blob: 416cca8f7e09f2d01b4a7a36a080694bb1b6e05a [file] [log] [blame]
// 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 <zxtest/zxtest.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_STREQ, 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.
TEST(VisitorTests, ReadAndWriteDirectTest) {
// ---------------40---------------- |
std::string contents = R"FIDL(
/// C1
library foo.bar; // C2
using baz.qux; // C3
/// C4
type MyEnum = enum { // C5
/// C6
MY_VALUE = 1; // C7
};
/// C8
type MyTable = table { // C9
/// C10
1: field thing; // C11
};
/// C12
alias MyAlias = MyStruct; // C13
/// C14
protocol MyProtocol { // C15
/// C16
MyMethod(struct { // C17
/// C18
data MyTable; // C19
}) -> () error MyEnum; // C20
};
)FIDL";
TestLibrary library(contents);
std::unique_ptr<fidl::raw::File> ast;
bool is_parse_success = library.Parse(&ast);
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 = "example.fidl: " + d;
EXPECT_STREQ(expected.c_str(), actual, "%s", d.c_str());
}
}
} // namespace