blob: f4f761b26051c736322ba1a62a415335ca31c4e4 [file] [log] [blame] [edit]
// Copyright 2020 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 <sys/stat.h>
#include <zircon/types.h>
#include <iostream>
#include <zxtest/zxtest.h>
#include "tools/fidl/fidlc/fix/command_line_options.h"
#include "tools/fidl/fidlc/include/fidl/diagnostics.h"
#include "tools/fidl/fidlc/include/fidl/experimental_flags.h"
#include "tools/fidl/fidlc/include/fidl/fixes.h"
#include "tools/fidl/fidlc/include/fidl/reporter.h"
#include "tools/fidl/fidlc/include/fidl/source_file.h"
#include "tools/fidl/fidlc/include/fidl/source_manager.h"
#include "tools/fidl/fidlc/include/fidl/versioning_types.h"
#include "tools/fidl/fidlc/tests/error_test.h"
namespace {
using namespace fidl;
using namespace fidl::fix;
static const std::string kBadFileName = "bad.fidl";
static const std::string kGoodFileName = "good.fidl";
template <typename T, Fixable::Kind Fixing>
void GoodParsedFix(const ExperimentalFlags experimental_flags, const std::string& before,
const std::string& after) {
static_assert(std::is_base_of_v<ParsedFix, T>,
"Only classes derived from |ParsedFix| may be used by |GoodTransform()}");
Reporter reporter;
auto manager = std::make_unique<SourceManager>();
manager->AddSourceFile(std::make_unique<SourceFile>(kGoodFileName, before));
auto fix = T(std::move(manager), experimental_flags);
EXPECT_EQ(fix.ValidateFlags(), Status::kOk);
auto result = fix.Transform(&reporter);
ASSERT_TRUE(result.is_ok());
OutputMap outputs = result.value();
EXPECT_EQ(outputs.size(), 1);
auto transformed = outputs[kGoodFileName];
EXPECT_STREQ(transformed, after);
}
// This template assumes that all libraries and dependencies are in a single file.
template <typename T, Fixable::Kind Fixing>
void GoodCompiledFix(const ExperimentalFlags experimental_flags, const std::string& before,
const std::vector<std::string>& deps, const std::string& after) {
static_assert(std::is_base_of_v<CompiledFix, T>,
"Only classes derived from |CompiledFix| may be used by |GoodTransform()}");
Reporter reporter;
auto library_manager = std::make_unique<SourceManager>();
library_manager->AddSourceFile(std::make_unique<SourceFile>(kGoodFileName, before));
std::vector<std::unique_ptr<SourceManager>> deps_managers;
for (size_t i = 0; i < deps.size(); i++) {
deps_managers.emplace_back(std::make_unique<SourceManager>());
std::unique_ptr<SourceManager>& manager = deps_managers.back();
manager->AddSourceFile(
std::make_unique<SourceFile>("dep_" + std::to_string(i) + ".fidl", deps[i]));
}
VersionSelection version_selection;
version_selection.Insert(Platform::Parse("example").value(), Version::Head());
auto fix = T(std::move(library_manager), std::move(deps_managers), &version_selection,
experimental_flags);
EXPECT_EQ(fix.ValidateFlags(), Status::kOk);
auto result = fix.Transform(&reporter);
ASSERT_TRUE(result.is_ok());
OutputMap outputs = result.value();
ASSERT_EQ(outputs.size(), 1);
auto transformed = outputs[kGoodFileName];
EXPECT_STREQ(transformed, after);
}
// Immediately fail - used to test transform failure reporting.
class FailingTransformer final : public ParsedTransformer {
public:
FailingTransformer(const std::vector<const SourceFile*>& source_files,
const ExperimentalFlags& experimental_flags, Reporter* reporter)
: ParsedTransformer(source_files, experimental_flags, reporter) {}
void WhenFile(raw::File* el, TokenSlice& token_slice) override {
// Immediately fail, and do nothing else.
AddError("failure!");
}
};
// A fix that fails during transformation.
class FailingParsedFix final : public ParsedFix {
public:
FailingParsedFix(std::unique_ptr<SourceManager> library,
const ExperimentalFlags experimental_flags)
: ParsedFix(Fixable::Get(Fixable::Kind::kNoop), std::move(library), experimental_flags) {}
std::unique_ptr<ParsedTransformer> GetParsedTransformer(
const std::vector<const SourceFile*>& source_files,
const fidl::ExperimentalFlags& experimental_flags, Reporter* reporter) override {
return std::make_unique<FailingTransformer>(source_files, experimental_flags, reporter);
}
};
TEST(FixTests, BadCommandLineFlagsEmpty) {
fidl::fix::CommandLineOptions options;
std::vector<std::string> filepaths;
std::unique_ptr<fidl::fix::Fix> fix;
auto status = ProcessCommandLine(options, filepaths, fix);
EXPECT_EQ(status.has_error(), true);
}
TEST(FixTests, GoodCommandLineFlags) {
fidl::fix::CommandLineOptions options;
std::vector<std::string> filepaths;
std::unique_ptr<fidl::fix::Fix> fix;
options.fix = "noop";
struct stat buffer;
if (stat("host_x64/fidlc-tests/good/fi-0001.test.fidl", &buffer) == 0) {
filepaths.emplace_back("host_x64/fidlc-tests/good/fi-0001.test.fidl");
} else if (stat("host_arm64/fidlc-tests/good/fi-0001.test.fidl", &buffer) == 0) {
filepaths.emplace_back("host_arm64/fidlc-tests/good/fi-0001.test.fidl");
}
// If the path is something else, there will be an error.
auto status = ProcessCommandLine(options, filepaths, fix);
EXPECT_EQ(false, status.has_error());
}
TEST(FixTests, BadInvalidFlags) {
ExperimentalFlags experimental_flags;
auto manager = std::make_unique<SourceManager>();
manager->AddSourceFile(std::make_unique<SourceFile>("bad.fidl", "library;"));
auto fix = FailingParsedFix(std::move(manager), experimental_flags);
EXPECT_EQ(fix.ValidateFlags(), Status::kErrorOther);
}
TEST(FixTests, BadParsingFailure) {
Reporter reporter;
ExperimentalFlags experimental_flags;
experimental_flags.EnableFlag(ExperimentalFlags::Flag::kNoop);
auto manager = std::make_unique<SourceManager>();
manager->AddSourceFile(std::make_unique<SourceFile>("bad.fidl", "library;"));
auto fix = NoopParsedFix(std::move(manager), experimental_flags);
EXPECT_EQ(fix.ValidateFlags(), Status::kOk);
auto result = fix.Transform(&reporter);
ASSERT_FALSE(result.is_ok());
auto failure = result.error_value();
EXPECT_EQ(failure.status, Status::kErrorPreFix);
ASSERT_EQ(failure.errors.size(), 1);
ASSERT_EQ(reporter.errors().size(), 1);
EXPECT_EQ(failure.errors[0].step, fidl::fix::Step::kPreparing);
EXPECT_ERR(reporter.errors()[0], ErrUnexpectedTokenOfKind);
EXPECT_SUBSTR(failure.errors[0].msg, "bad.fidl");
}
TEST(FixTests, BadTransformFailure) {
Reporter reporter;
ExperimentalFlags experimental_flags;
experimental_flags.EnableFlag(ExperimentalFlags::Flag::kNoop);
auto manager = std::make_unique<SourceManager>();
manager->AddSourceFile(std::make_unique<SourceFile>("valid.fidl", "library example;"));
auto fix = FailingParsedFix(std::move(manager), experimental_flags);
EXPECT_EQ(fix.ValidateFlags(), Status::kOk);
auto result = fix.Transform(&reporter);
ASSERT_FALSE(result.is_ok());
auto failure = result.error_value();
EXPECT_EQ(failure.status, Status::kErrorDuringFix);
ASSERT_EQ(failure.errors.size(), 1);
ASSERT_TRUE(reporter.errors().empty());
EXPECT_EQ(failure.errors[0].step, fidl::fix::Step::kTransforming);
EXPECT_SUBSTR(failure.errors[0].msg, "failure!");
}
TEST(FixTests, BadProtocolModifierFixMissingExperiment) {
ExperimentalFlags experimental_flags;
auto manager = std::make_unique<SourceManager>();
manager->AddSourceFile(std::make_unique<SourceFile>("valid.fidl", "library example;"));
auto fix = ProtocolModifierFix(std::move(manager), experimental_flags);
EXPECT_EQ(fix.ValidateFlags(), Status::kErrorOther);
}
void GoodProtocolModifierFix(const std::string& before, const std::string& after) {
GoodParsedFix<ProtocolModifierFix, Fixable::Kind::kProtocolModifier>(
ExperimentalFlags(ExperimentalFlags::Flag::kUnknownInteractions), before, after);
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolAlreadyClosed) {
std::string noop = R"FIDL(library example;
closed protocol MyProtocol {};
)FIDL";
GoodProtocolModifierFix(noop, noop);
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolAlreadyAjar) {
std::string noop = R"FIDL(library example;
ajar protocol MyProtocol {};
)FIDL";
GoodProtocolModifierFix(noop, noop);
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolAlreadyOpen) {
std::string noop = R"FIDL(library example;
open protocol MyProtocol {};
)FIDL";
GoodProtocolModifierFix(noop, noop);
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolUnmodified) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolWithComment) {
GoodProtocolModifierFix(R"FIDL(library example;
// comment
protocol MyProtocol {};
)FIDL",
R"FIDL(library example;
// comment
closed protocol MyProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolWithDocComment) {
GoodProtocolModifierFix(R"FIDL(library example;
/// doc comment
protocol MyProtocol {};
)FIDL",
R"FIDL(library example;
/// doc comment
closed protocol MyProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolWithAttribute) {
GoodProtocolModifierFix(R"FIDL(library example;
@attr
protocol MyProtocol {};
)FIDL",
R"FIDL(library example;
@attr
closed protocol MyProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixEmptyProtocolWithAllPrefixes) {
GoodProtocolModifierFix(R"FIDL(library example;
// comment
/// doc comment
@attr
protocol MyProtocol {};
)FIDL",
R"FIDL(library example;
// comment
/// doc comment
@attr
closed protocol MyProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMultipleEmptyProtocols) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {};
protocol MyOtherProtocol {};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {};
closed protocol MyOtherProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsAlreadyStrict) {
std::string noop = R"FIDL(library example;
closed protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
)FIDL";
GoodProtocolModifierFix(noop, noop);
}
TEST(FixTests, GoodProtocolModifierFixMethodsAlreadyFlexible) {
std::string noop = R"FIDL(library example;
open protocol MyProtocol {
flexible OneWay();
flexible TwoWay() -> ();
flexible WithErr() -> () error uint32;
flexible -> OnEvent();
};
)FIDL";
GoodProtocolModifierFix(noop, noop);
}
TEST(FixTests, GoodProtocolModifierFixMethodsUnmodified) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
OneWay();
TwoWay() -> ();
WithErr() -> () error uint32;
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsWithComments) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
// comment
OneWay();
// comment
TwoWay() -> ();
// comment
WithErr() -> () error uint32;
// comment
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
// comment
strict OneWay();
// comment
strict TwoWay() -> ();
// comment
strict WithErr() -> () error uint32;
// comment
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsWithDocComments) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
/// doc comment
OneWay();
/// doc comment
TwoWay() -> ();
/// doc comment
WithErr() -> () error uint32;
/// doc comment
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
/// doc comment
strict OneWay();
/// doc comment
strict TwoWay() -> ();
/// doc comment
strict WithErr() -> () error uint32;
/// doc comment
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsWithAttributes) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
@attr
OneWay();
@attr
TwoWay() -> ();
@attr
WithErr() -> () error uint32;
@attr
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
@attr
strict OneWay();
@attr
strict TwoWay() -> ();
@attr
strict WithErr() -> () error uint32;
@attr
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsWithAllPrefixes) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
// comment
/// doc comment
@attr
OneWay();
// comment
/// doc comment
@attr
TwoWay() -> ();
// comment
/// doc comment
@attr
WithErr() -> () error uint32;
// comment
/// doc comment
@attr
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
// comment
/// doc comment
@attr
strict OneWay();
// comment
/// doc comment
@attr
strict TwoWay() -> ();
// comment
/// doc comment
@attr
strict WithErr() -> () error uint32;
// comment
/// doc comment
@attr
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMultiplePopulatedProtocols) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
OneWay();
TwoWay() -> ();
};
protocol MyOtherProtocol {
WithErr() -> () error uint32;
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
};
closed protocol MyOtherProtocol {
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixProtocolModifierOnly) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodModifiersOnly) {
GoodProtocolModifierFix(R"FIDL(library example;
closed protocol MyProtocol {
OneWay();
TwoWay() -> ();
WithErr() -> () error uint32;
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixSomeExistingModifiers) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
WithErr() -> () error uint32;
-> OnEvent();
};
open protocol MyOtherProtocol {
OneWay();
TwoWay() -> ();
strict WithErr() -> () error uint32;
flexible -> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
strict -> OnEvent();
};
open protocol MyOtherProtocol {
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
flexible -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixOtherProtocolIdentifiers) {
GoodProtocolModifierFix(R"FIDL(library example;
const protocol bool = true;
@attr(protocol)
protocol MyProtocol {};
@protocol(protocol=protocol)
protocol MyOtherProtocol {};
)FIDL",
R"FIDL(library example;
const protocol bool = true;
@attr(protocol)
closed protocol MyProtocol {};
@protocol(protocol=protocol)
closed protocol MyOtherProtocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixProtocolNamedProtocol) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol protocol {};
)FIDL",
R"FIDL(library example;
closed protocol protocol {};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsWithCompose) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
compose MyOtherProtocol;
OneWay();
TwoWay() -> ();
strict WithErr() -> () error uint32;
flexible -> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
compose MyOtherProtocol;
strict OneWay();
strict TwoWay() -> ();
strict WithErr() -> () error uint32;
flexible -> OnEvent();
};
)FIDL");
}
TEST(FixTests, GoodProtocolModifierFixMethodsWithComposeAndAllPrefixes) {
GoodProtocolModifierFix(R"FIDL(library example;
protocol MyProtocol {
compose MyOtherProtocol;
// comment
/// doc comment
@attr
OneWay();
// comment
/// doc comment
@attr
TwoWay() -> ();
// comment
/// doc comment
@attr
WithErr() -> () error uint32;
// comment
/// doc comment
@attr
-> OnEvent();
};
)FIDL",
R"FIDL(library example;
closed protocol MyProtocol {
compose MyOtherProtocol;
// comment
/// doc comment
@attr
strict OneWay();
// comment
/// doc comment
@attr
strict TwoWay() -> ();
// comment
/// doc comment
@attr
strict WithErr() -> () error uint32;
// comment
/// doc comment
@attr
strict -> OnEvent();
};
)FIDL");
}
} // namespace