| // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <regex> | 
 |  | 
 | #include <fidl/flat_ast.h> | 
 | #include <fidl/formatter.h> | 
 | #include <fidl/lexer.h> | 
 | #include <fidl/parser.h> | 
 | #include <fidl/source_file.h> | 
 | #include <fidl/tree_visitor.h> | 
 | #include <zxtest/zxtest.h> | 
 |  | 
 | #include "examples.h" | 
 | #include "test_library.h" | 
 |  | 
 | namespace { | 
 |  | 
 | std::map<std::string, std::string> formatted_output_; | 
 | const fidl::ExperimentalFlags FLAGS(fidl::ExperimentalFlags::Flag::kEnableHandleRights); | 
 |  | 
 | void InitializeContents() { | 
 |   for (auto element : Examples::map()) { | 
 |     TestLibrary library(element.first, element.second, FLAGS); | 
 |     std::unique_ptr<fidl::raw::File> ast; | 
 |     library.Parse(&ast); | 
 |  | 
 |     fidl::raw::FormattingTreeVisitor visitor; | 
 |     visitor.OnFile(ast); | 
 |  | 
 |     formatted_output_[element.first] = *visitor.formatted_output(); | 
 |   } | 
 | } | 
 |  | 
 | // Tests that repeatedly applying the formatter results in no change. | 
 | TEST(FormatterTests, GoodIdempotenceTest) { | 
 |   InitializeContents(); | 
 |  | 
 |   for (auto element : formatted_output_) { | 
 |     TestLibrary library(element.first, element.second, FLAGS); | 
 |     std::unique_ptr<fidl::raw::File> ast; | 
 |     EXPECT_TRUE(library.Parse(&ast)); | 
 |  | 
 |     fidl::raw::FormattingTreeVisitor visitor; | 
 |     visitor.OnFile(ast); | 
 |  | 
 |     EXPECT_STR_EQ(element.second.c_str(), visitor.formatted_output()->c_str(), | 
 |                   "Applying formatting multiple times produces different results"); | 
 |   } | 
 | } | 
 |  | 
 | TEST(FormatterTests, GoodBasicFormattingRulesTest) { | 
 |   InitializeContents(); | 
 |  | 
 |   std::regex trailing_ws(".*\\s+$"); | 
 |   std::regex top_level_decl("^\\s*(?:struct|enum|union)\\s+.*"); | 
 |   std::regex attribute("\\s*\\[[A-Za-z,=\" ]+\\]\\s*"); | 
 |   std::regex comment("\\s*//.*"); | 
 |  | 
 |   // Break the output into lines | 
 |   for (auto element : formatted_output_) { | 
 |     printf("testing %s\n", element.first.c_str()); | 
 |     std::stringstream ss(element.second); | 
 |     std::string line; | 
 |     std::vector<std::string> lines; | 
 |  | 
 |     while (std::getline(ss, line, '\n')) { | 
 |       lines.push_back(line); | 
 |  | 
 |       // RULE: No trailing whitespace | 
 |       ASSERT_FALSE(std::regex_search(line, trailing_ws), "Trailing whitespace found"); | 
 |  | 
 |       // RULE: No tab characters | 
 |       ASSERT_TRUE(line.find_first_of('\t') == std::string::npos, "Tab character found"); | 
 |  | 
 |       // RULE: 4 space indents (at least) | 
 |       ASSERT_TRUE(!isspace(line[0]) || line.find_first_of("    ") == 0, "<4 space indent found"); | 
 |     } | 
 |  | 
 |     // RULE: Separate top-level declarations for struct, enum, and union with one | 
 |     // blank line. | 
 |     for (size_t i = 0; i < lines.size(); i++) { | 
 |       if (std::regex_search(lines[i], top_level_decl)) { | 
 |         // Just means there is a top-level decl on the first line. | 
 |         if (i == 0) { | 
 |           continue; | 
 |         } | 
 |  | 
 |         size_t line_to_check = i - 1; | 
 |         // Back up to before attributes and comments. | 
 |         while (std::regex_search(lines[line_to_check], attribute) || | 
 |                std::regex_search(lines[line_to_check], comment)) { | 
 |           line_to_check--; | 
 |           ASSERT_GE(line_to_check, 0); | 
 |         } | 
 |  | 
 |         std::string full = lines[i - 1] + lines[i]; | 
 |         ASSERT_EQ(lines[line_to_check].size(), 0, "No blank line found before top level decl"); | 
 |       } | 
 |     } | 
 |  | 
 |     // RULE: End the file with exactly one newline (no blank lines at the end). | 
 |     ASSERT_NE(lines[lines.size() - 1].size(), 0, "No newline at EOF"); | 
 |   } | 
 | } | 
 |  | 
 | TEST(FormatterTests, GoodGoldenFileTest) { | 
 |   InitializeContents(); | 
 |  | 
 |   std::string good_output; | 
 |   std::string formatted_bad_output; | 
 |  | 
 |   for (auto element : Examples::map()) { | 
 |     if (element.first.find("testdata/goodformat.test.fidl") != std::string::npos) { | 
 |       good_output = Examples::map()[element.first]; | 
 |     } else if (element.first.find("testdata/badformat.fidl") != std::string::npos) { | 
 |       formatted_bad_output = formatted_output_[element.first]; | 
 |     } | 
 |   } | 
 |  | 
 |   ASSERT_GT(good_output.size(), 0); | 
 |   ASSERT_GT(formatted_bad_output.size(), 0); | 
 |  | 
 |   ASSERT_STR_EQ(good_output.c_str(), formatted_bad_output.c_str(), | 
 |                 "Formatting for badformat.fidl looks weird"); | 
 | } | 
 |  | 
 | }  // namespace |