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