|  | // 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 <fidl/flat_ast.h> | 
|  | #include <fidl/lexer.h> | 
|  | #include <fidl/parser.h> | 
|  | #include <fidl/source_file.h> | 
|  | #include <fidl/tree_visitor.h> | 
|  | #include <unittest/unittest.h> | 
|  |  | 
|  | #include "examples.h" | 
|  | #include "test_library.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // A TreeVisitor that reads in a file and spits back out the same file | 
|  | class NoopTreeVisitor : public fidl::raw::DeclarationOrderTreeVisitor { | 
|  | public: | 
|  | NoopTreeVisitor() : last_location_(nullptr) {} | 
|  |  | 
|  | virtual void OnSourceElementStart(const fidl::raw::SourceElement& element) override { | 
|  | OnSourceElementShared(element.start_); | 
|  | } | 
|  |  | 
|  | virtual void OnSourceElementEnd(const fidl::raw::SourceElement& element) override { | 
|  | OnSourceElementShared(element.end_); | 
|  | } | 
|  | void OnSourceElementShared(const fidl::Token& current_token) { | 
|  | const char* ws_location = current_token.previous_end().data().data(); | 
|  | // Printed code must increase in monotonic order, for two reasons. | 
|  | // First of all, we don't reorder anything.  Second of all, the start | 
|  | // token for an identifier list (for example) is the same as the start | 
|  | // token for the first identifier, so we need to make sure we don't | 
|  | // print that token twice. | 
|  | if (ws_location > last_location_) { | 
|  | int size = (int)(current_token.data().data() - current_token.previous_end().data().data()); | 
|  | std::string gap(ws_location, size); | 
|  | std::string content(current_token.data().data(), current_token.data().size()); | 
|  | output_ += gap + content; | 
|  | last_location_ = ws_location; | 
|  | } | 
|  | } | 
|  | std::string& output() { return output_; } | 
|  |  | 
|  | private: | 
|  | std::string output_; | 
|  | const char* last_location_; | 
|  | }; | 
|  |  | 
|  | // Provides more useful context for string diff than EXPECT_STR_EQ, which shows | 
|  | // a limited prefix.  When the string is long, and the difference is buried | 
|  | // past the limited prefix, the limited prefix doesn't give useful information. | 
|  | std::string targeted_diff(const char* expected, const char* actual, size_t size) { | 
|  | // We want two lines of useful context: | 
|  | size_t i = 0; | 
|  | size_t last_nl = 0; | 
|  | size_t last_last_nl = 0; | 
|  | while (i <= size && expected[i] == actual[i]) { | 
|  | if (expected[i] == '\n') { | 
|  | last_last_nl = last_nl; | 
|  | last_nl = i; | 
|  | } | 
|  | i++; | 
|  | } | 
|  |  | 
|  | size_t start = last_last_nl; | 
|  | size_t expected_end = (i + 10 < strlen(expected)) ? i + 10 : strlen(expected) - 1; | 
|  | size_t actual_end = (i + 10 < strlen(actual)) ? i + 10 : strlen(actual) - 1; | 
|  | std::string s("Expected contains \""); | 
|  | s.append(std::string(expected + start, expected_end - start)); | 
|  | s.append("\" and actual contains \""); | 
|  | s.append(std::string(actual + start, actual_end - start)); | 
|  | s.append("\""); | 
|  | return s; | 
|  | } | 
|  |  | 
|  | // Test that the AST visitor works: ensure that if you visit a file, you can | 
|  | // reconstruct its original contents. | 
|  | bool read_and_write_direct_test() { | 
|  | BEGIN_TEST; | 
|  |  | 
|  | for (auto element : Examples::map()) { | 
|  | TestLibrary library(element.first, element.second); | 
|  | std::unique_ptr<fidl::raw::File> ast; | 
|  | bool is_parse_success = library.Parse(&ast); | 
|  | EXPECT_TRUE(is_parse_success); | 
|  |  | 
|  | if (is_parse_success) { | 
|  | NoopTreeVisitor visitor; | 
|  | visitor.OnFile(ast); | 
|  | std::string expected(library.source_file().data()); | 
|  | std::string output = visitor.output(); | 
|  | const char* actual = output.c_str(); | 
|  | std::string d = targeted_diff(expected.c_str(), actual, output.size()); | 
|  | d = element.first + ": " + d; | 
|  |  | 
|  | EXPECT_STR_EQ(expected.c_str(), actual, d.c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | END_TEST; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | BEGIN_TEST_CASE(visitor_tests) | 
|  | RUN_TEST(read_and_write_direct_test) | 
|  | END_TEST_CASE(visitor_tests) |