1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <unittest/unittest.h>
6 
7 #include <fidl/flat_ast.h>
8 #include <fidl/lexer.h>
9 #include <fidl/parser.h>
10 #include <fidl/source_file.h>
11 #include <fidl/tree_visitor.h>
12 
13 #include "examples.h"
14 #include "test_library.h"
15 
16 namespace {
17 
18 // A TreeVisitor that reads in a file and spits back out the same file
19 class NoopTreeVisitor : public fidl::raw::DeclarationOrderTreeVisitor {
20 public:
NoopTreeVisitor()21     NoopTreeVisitor()
22         : last_source_location_(nullptr) {}
23 
OnSourceElementStart(const fidl::raw::SourceElement & element)24     virtual void OnSourceElementStart(const fidl::raw::SourceElement& element) override {
25         OnSourceElementShared(element.start_);
26     }
27 
OnSourceElementEnd(const fidl::raw::SourceElement & element)28     virtual void OnSourceElementEnd(const fidl::raw::SourceElement& element) override {
29         OnSourceElementShared(element.end_);
30     }
OnSourceElementShared(const fidl::Token & current_token)31     void OnSourceElementShared(const fidl::Token& current_token) {
32         const char* ws_location = current_token.previous_end().data().data();
33         // Printed code must increase in monotonic order, for two reasons.
34         // First of all, we don't reorder anything.  Second of all, the start
35         // token for an identifier list (for example) is the same as the start
36         // token for the first identifier, so we need to make sure we don't
37         // print that token twice.
38         if (ws_location > last_source_location_) {
39             int size = (int)(current_token.data().data() - current_token.previous_end().data().data());
40             std::string gap(ws_location, size);
41             std::string content(current_token.data().data(), current_token.data().size());
42             output_ += gap + content;
43             last_source_location_ = ws_location;
44         }
45     }
output()46     std::string& output() { return output_; }
47 
48 private:
49     std::string output_;
50     const char* last_source_location_;
51 };
52 
53 // Provides more useful context for string diff than EXPECT_STR_EQ, which shows
54 // a limited prefix.  When the string is long, and the difference is buried
55 // past the limited prefix, the limited prefix doesn't give useful information.
targeted_diff(const char * expected,const char * actual,int size)56 std::string targeted_diff(const char* expected, const char* actual, int size) {
57     // We want two lines of useful context:
58     int i = 0;
59     int last_nl = 0;
60     int last_last_nl = 0;
61     while (i <= size && expected[i] == actual[i]) {
62         if (expected[i] == '\n') {
63             last_last_nl = last_nl;
64             last_nl = i;
65         }
66         i++;
67     }
68 
69     int start = last_last_nl;
70     int expected_end = (i + 10 < strlen(expected)) ? i + 10 : strlen(expected) - 1;
71     int actual_end = (i + 10 < strlen(actual)) ? i + 10 : strlen(actual) - 1;
72     std::string s("Expected contains \"");
73     s.append(std::string(expected + start, expected_end - start));
74     s.append("\" and actual contains \"");
75     s.append(std::string(actual + start, actual_end - start));
76     s.append("\"");
77     return s;
78 }
79 
80 // Test that the AST visitor works: ensure that if you visit a file, you can
81 // reconstruct its original contents.
read_and_write_direct_test()82 bool read_and_write_direct_test() {
83     BEGIN_TEST;
84 
85     for (auto element : Examples::map()) {
86         TestLibrary library(element.first, element.second);
87         std::unique_ptr<fidl::raw::File> ast;
88         EXPECT_TRUE(library.Parse(ast));
89 
90         NoopTreeVisitor visitor;
91         visitor.OnFile(ast);
92         std::string expected(library.source_file().data());
93         std::string output = visitor.output();
94         const char* actual = output.c_str();
95         std::string d = targeted_diff(expected.c_str(), actual, output.size());
96         d = element.first + ": " + d;
97 
98         EXPECT_STR_EQ(expected.c_str(), actual, d.c_str());
99     }
100 
101     END_TEST;
102 }
103 
104 } // namespace
105 
106 BEGIN_TEST_CASE(visitor_tests);
107 RUN_TEST(read_and_write_direct_test);
108 END_TEST_CASE(visitor_tests);
109