blob: 6c7edadf0bb7c84fdd9290c47db9a5da10c2ee31 [file] [log] [blame]
// Copyright 2022 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 <fstream>
#include <gtest/gtest.h>
#include "tools/fidl/fidlc/tests/test_library.h"
// This file tests the temporal decomposition algorithm by comparing the JSON IR
// resulting from a versioned library and its manually decomposed equivalents.
namespace fidlc {
namespace {
// Returns true if str starts with prefix.
bool StartsWith(std::string_view str, std::string_view prefix) {
return str.substr(0, prefix.size()) == prefix;
}
// If the line starts with whitespace followed by str, returns the whitespace.
std::optional<std::string> GetSpaceBefore(std::string_view line, std::string_view str) {
size_t i = 0;
while (i < line.size() && line[i] == ' ') {
i++;
}
if (StartsWith(line.substr(i), str)) {
return std::string(line.substr(0, i));
}
return std::nullopt;
}
// Erases fields from a JSON IR string that manual decomposition can change:
//
// * "platform": changes in DecompositionTests.UnversionedLibrary.
// * "location": decomposing changes all locations.
// * "maybe_attributes": decomposing changes @available attributes.
// * "declaration_order": decomposing can change the DFS order.
//
// Also removes all end-of-line commas since these can cause spurious diffs.
// Note that this means the returned string is not valid JSON.
std::string ScrubJson(const std::string& json) {
// We scan the JSON line by line, filtering out the undesired lines. To do
// this, we rely on JsonWriter emitting correct indentation and newlines.
std::istringstream input(json);
std::ostringstream output;
std::string line;
std::optional<std::string> skip_until;
while (std::getline(input, line)) {
if (skip_until) {
if (StartsWith(line, skip_until.value())) {
skip_until = std::nullopt;
}
continue;
}
if (GetSpaceBefore(line, "\"platform\": \"")) {
// Skip platform line.
} else if (auto indent = GetSpaceBefore(line, "\"location\": {")) {
skip_until = indent.value() + '}';
} else if (auto indent = GetSpaceBefore(line, "\"maybe_attributes\": [")) {
skip_until = indent.value() + ']';
} else if (auto indent = GetSpaceBefore(line, "\"declaration_order\": [")) {
skip_until = indent.value() + ']';
} else {
if (line.back() == ',') {
line.pop_back();
}
output << line << '\n';
}
}
return output.str();
}
// Platform name and library name for all test libraries in this file.
const char* kLibraryName = "example";
// Helper function to implement ASSERT_EQUIVALENT.
void AssertEquivalent(const std::string& left_fidl, const std::string& right_fidl,
std::string_view version) {
TestLibrary left_lib(left_fidl);
left_lib.SelectVersion(kLibraryName, version);
ASSERT_COMPILED(left_lib);
ASSERT_EQ(left_lib.name(), kLibraryName);
TestLibrary right_lib(right_fidl);
right_lib.SelectVersion(kLibraryName, version);
ASSERT_COMPILED(right_lib);
ASSERT_EQ(right_lib.name(), kLibraryName);
auto left_json = ScrubJson(left_lib.GenerateJSON());
auto right_json = ScrubJson(right_lib.GenerateJSON());
if (left_json != right_json) {
std::ofstream output_left("decomposition_tests_left.txt");
output_left << left_json;
output_left.close();
std::ofstream output_right("decomposition_tests_right.txt");
output_right << right_json;
output_right.close();
}
ASSERT_EQ(left_json, right_json)
<< "To compare results, run:\n\n"
"diff $(cat $FUCHSIA_DIR/.fx-build-dir)/decomposition_tests_{left,right}.txt\n";
}
// Asserts that left_fidl and right_fidl compile to JSON IR that is identical
// after scrubbbing (see ScrubJson) for the given version.
#define ASSERT_EQUIVALENT(left_fidl, right_fidl, version) \
{ \
SCOPED_TRACE("ASSERT_EQUIVALENT failed for version " version); \
AssertEquivalent(left_fidl, right_fidl, version); \
}
TEST(VersioningDecompositionTests, EquivalentToSelf) {
auto fidl = R"FIDL(
@available(added=1)
library example;
)FIDL";
ASSERT_EQUIVALENT(fidl, fidl, "1");
ASSERT_EQUIVALENT(fidl, fidl, "2");
ASSERT_EQUIVALENT(fidl, fidl, "HEAD");
ASSERT_EQUIVALENT(fidl, fidl, "LEGACY");
}
// An unversioned library behaves the same as an unchanging versioned library.
TEST(VersioningDecompositionTests, UnversionedLibrary) {
auto unversioned = R"FIDL(
library example;
type Foo = struct {};
)FIDL";
auto versioned = R"FIDL(
@available(added=1)
library example;
type Foo = struct {};
)FIDL";
ASSERT_EQUIVALENT(unversioned, versioned, "1");
ASSERT_EQUIVALENT(unversioned, versioned, "2");
ASSERT_EQUIVALENT(unversioned, versioned, "HEAD");
ASSERT_EQUIVALENT(unversioned, versioned, "LEGACY");
}
TEST(VersioningDecompositionTests, AbsentLibraryIsEmpty) {
auto fidl = R"FIDL(
@available(added=2, removed=3)
library example;
type Foo = struct {};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type Foo = struct {};
)FIDL";
auto v3_onward = R"FIDL(
@available(added=3)
library example;
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3_onward, "3");
ASSERT_EQUIVALENT(fidl, v3_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v3_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, SplitByMembership) {
auto fidl = R"FIDL(
@available(added=1)
library example;
type TopLevel = struct {
@available(added=2)
first uint32;
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type TopLevel = struct {};
)FIDL";
auto v2_onward = R"FIDL(
@available(added=2)
library example;
type TopLevel = struct {
first uint32;
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2_onward, "2");
ASSERT_EQUIVALENT(fidl, v2_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v2_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, SplitByReference) {
auto fidl = R"FIDL(
@available(added=1)
library example;
type This = struct {
this_member That;
};
type That = struct {
@available(added=2)
that_member uint32;
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type This = struct {
this_member That;
};
type That = struct {};
)FIDL";
auto v2_onward = R"FIDL(
@available(added=2)
library example;
type This = struct {
this_member That;
};
type That = struct {
that_member uint32;
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2_onward, "2");
ASSERT_EQUIVALENT(fidl, v2_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v2_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, SplitByTwoMembers) {
auto fidl = R"FIDL(
@available(added=1)
library example;
type This = struct {
@available(added=2)
first That;
@available(added=3)
second That;
};
type That = struct {};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type This = struct {};
type That = struct {};
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type This = struct {
first That;
};
type That = struct {};
)FIDL";
auto v3_onward = R"FIDL(
@available(added=3)
library example;
type This = struct {
first That;
second That;
};
type That = struct {};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3_onward, "3");
ASSERT_EQUIVALENT(fidl, v3_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v3_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, Recursion) {
auto fidl = R"FIDL(
@available(added=1)
library example;
type Expr = flexible union {
1: num int64;
@available(removed=3)
2: add struct {
left Expr:optional;
right Expr:optional;
};
@available(added=2, removed=3)
3: mul struct {
left Expr:optional;
right Expr:optional;
};
@available(added=3)
4: bin struct {
kind flexible enum {
ADD = 1;
MUL = 2;
DIV = 3;
@available(added=4)
MOD = 4;
};
left Expr:optional;
right Expr:optional;
};
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type Expr = flexible union {
1: num int64;
2: add struct {
left Expr:optional;
right Expr:optional;
};
};
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type Expr = flexible union {
1: num int64;
2: add struct {
left Expr:optional;
right Expr:optional;
};
3: mul struct {
left Expr:optional;
right Expr:optional;
};
};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
type Expr = flexible union {
1: num int64;
4: bin struct {
kind flexible enum {
ADD = 1;
MUL = 2;
DIV = 3;
};
left Expr:optional;
right Expr:optional;
};
};
)FIDL";
auto v4_onward = R"FIDL(
@available(added=4)
library example;
type Expr = flexible union {
1: num int64;
4: bin struct {
kind flexible enum {
ADD = 1;
MUL = 2;
DIV = 3;
MOD = 4;
};
left Expr:optional;
right Expr:optional;
};
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4_onward, "4");
ASSERT_EQUIVALENT(fidl, v4_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v4_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, MutualRecursion) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(added=2)
type Foo = struct {
str string;
@available(added=3)
bars vector<box<Bar>>;
};
@available(added=2)
type Bar = struct {
@available(removed=5)
foo box<Foo>;
@available(added=4)
str string;
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type Foo = struct {
str string;
};
type Bar = struct {
foo box<Foo>;
};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
type Foo = struct {
str string;
bars vector<box<Bar>>;
};
type Bar = struct {
foo box<Foo>;
};
)FIDL";
auto v4 = R"FIDL(
@available(added=4, removed=5)
library example;
type Foo = struct {
str string;
bars vector<box<Bar>>;
};
type Bar = struct {
foo box<Foo>;
str string;
};
)FIDL";
auto v5_onward = R"FIDL(
@available(added=5)
library example;
type Foo = struct {
str string;
bars vector<box<Bar>>;
};
type Bar = struct {
str string;
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4, "4");
ASSERT_EQUIVALENT(fidl, v5_onward, "5");
ASSERT_EQUIVALENT(fidl, v5_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v5_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, MisalignedSwapping) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(replaced=4)
const LEN uint64 = 16;
@available(added=4)
const LEN uint64 = 32;
@available(added=2)
type Foo = table {
@available(replaced=3)
1: bar string;
@available(added=3)
1: bar string:LEN;
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
const LEN uint64 = 16;
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
const LEN uint64 = 16;
type Foo = table {
1: bar string;
};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
const LEN uint64 = 16;
type Foo = table {
1: bar string:LEN;
};
)FIDL";
auto v4_onward = R"FIDL(
@available(added=4)
library example;
const LEN uint64 = 32;
type Foo = table {
1: bar string:LEN;
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4_onward, "4");
ASSERT_EQUIVALENT(fidl, v4_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v4_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, StrictToFlexible) {
auto fidl = R"FIDL(
@available(added=1)
library example;
type X = struct {
@available(added=2, removed=4)
y Y;
};
@available(added=2, replaced=3)
type Y = strict enum { A = 1; };
@available(added=3)
type Y = flexible enum { A = 1; };
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type X = struct {};
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type X = struct {
y Y;
};
type Y = strict enum { A = 1; };
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
type X = struct {
y Y;
};
type Y = flexible enum { A = 1; };
)FIDL";
auto v4_onward = R"FIDL(
@available(added=4)
library example;
type X = struct {};
type Y = flexible enum { A = 1; };
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4_onward, "4");
ASSERT_EQUIVALENT(fidl, v4_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v4_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, NameReuse) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(added=2, removed=3)
type Foo = struct {
bar Bar;
};
@available(added=1, replaced=4)
type Bar = struct {};
@available(added=4, removed=7)
type Foo = struct {};
@available(added=4, removed=6)
type Bar = struct {
foo Foo;
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type Bar = struct {};
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type Foo = struct {
bar Bar;
};
type Bar = struct {};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
type Bar = struct {};
)FIDL";
auto v4_to_5 = R"FIDL(
@available(added=4, removed=6)
library example;
type Foo = struct {};
type Bar = struct {
foo Foo;
};
)FIDL";
auto v6 = R"FIDL(
@available(added=6, removed=7)
library example;
type Foo = struct {};
)FIDL";
auto v7_onward = R"FIDL(
@available(added=7)
library example;
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4_to_5, "4");
ASSERT_EQUIVALENT(fidl, v4_to_5, "5");
ASSERT_EQUIVALENT(fidl, v6, "6");
ASSERT_EQUIVALENT(fidl, v7_onward, "7");
ASSERT_EQUIVALENT(fidl, v7_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v7_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, ConstsAndConstraints) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(removed=4)
const LEN uint64 = 10;
type Foo = table {
@available(replaced=3)
1: bar Bar;
@available(added=3, replaced=4)
1: bar string:LEN;
@available(added=4, removed=5)
1: bar Bar;
};
@available(replaced=2)
type Bar = struct {};
@available(added=2)
type Bar = table {};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
const LEN uint64 = 10;
type Foo = table {
1: bar Bar;
};
type Bar = struct {};
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
const LEN uint64 = 10;
type Foo = table {
1: bar Bar;
};
type Bar = table {};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
const LEN uint64 = 10;
type Foo = table {
1: bar string:LEN;
};
type Bar = table {};
)FIDL";
auto v4 = R"FIDL(
@available(added=4, removed=5)
library example;
type Foo = table {
1: bar Bar;
};
type Bar = table {};
)FIDL";
auto v5_onward = R"FIDL(
@available(added=5)
library example;
type Foo = table {};
type Bar = table {};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4, "4");
ASSERT_EQUIVALENT(fidl, v5_onward, "5");
ASSERT_EQUIVALENT(fidl, v5_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v5_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, AllElementsSplitByMembership) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(added=2, removed=5)
type Bits = bits {
FIRST = 1;
@available(added=3, removed=4)
SECOND = 2;
};
@available(added=2, removed=5)
type Enum = enum {
FIRST = 1;
@available(added=3, removed=4)
SECOND = 2;
};
@available(added=2, removed=5)
type Struct = struct {
first string;
@available(added=3, removed=4)
second string;
};
@available(added=2, removed=5)
type Table = table {
1: first string;
@available(added=3, removed=4)
2: second string;
};
@available(added=2, removed=5)
type Union = union {
1: first string;
@available(added=3, removed=4)
2: second string;
};
@available(added=2, removed=5)
protocol TargetProtocol {};
@available(added=2, removed=5)
protocol ProtocolComposition {
@available(added=3, removed=4)
compose TargetProtocol;
};
@available(added=2, removed=5)
protocol ProtocolMethods {
@available(added=3, removed=4)
Method() -> ();
};
@available(added=2, removed=5)
service Service {
first client_end:TargetProtocol;
@available(added=3, removed=4)
second client_end:TargetProtocol;
};
@available(added=2, removed=5)
resource_definition Resource : uint32 {
properties {
first uint32;
@available(added=3, removed=4)
second uint32;
// This property is required for compilation, but is not otherwise under test.
subtype flexible enum : uint32 {};
};
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
type Bits = bits {
FIRST = 1;
};
type Enum = enum {
FIRST = 1;
};
type Struct = struct {
first string;
};
type Table = table {
1: first string;
};
type Union = union {
1: first string;
};
protocol TargetProtocol {};
protocol ProtocolComposition {};
protocol ProtocolMethods {};
service Service {
first client_end:TargetProtocol;
};
resource_definition Resource : uint32 {
properties {
first uint32;
// This property is required for compilation, but is not otherwise under test.
subtype flexible enum : uint32 {};
};
};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
type Bits = bits {
FIRST = 1;
SECOND = 2;
};
type Enum = enum {
FIRST = 1;
SECOND = 2;
};
type Struct = struct {
first string;
second string;
};
type Table = table {
1: first string;
2: second string;
};
type Union = union {
1: first string;
2: second string;
};
protocol TargetProtocol {};
protocol ProtocolComposition {
compose TargetProtocol;
};
protocol ProtocolMethods {
Method() -> ();
};
service Service {
first client_end:TargetProtocol;
second client_end:TargetProtocol;
};
resource_definition Resource : uint32 {
properties {
first uint32;
second uint32;
// This property is required for compilation, but is not otherwise under test.
subtype flexible enum : uint32 {};
};
};
)FIDL";
auto v4 = R"FIDL(
@available(added=4, removed=5)
library example;
type Bits = bits {
FIRST = 1;
};
type Enum = enum {
FIRST = 1;
};
type Struct = struct {
first string;
};
type Table = table {
1: first string;
};
type Union = union {
1: first string;
};
protocol TargetProtocol {};
protocol ProtocolComposition {};
protocol ProtocolMethods {};
service Service {
first client_end:TargetProtocol;
};
resource_definition Resource : uint32 {
properties {
first uint32;
// This property is required for compilation, but is not otherwise under test.
subtype flexible enum : uint32 {};
};
};
)FIDL";
auto v5_onward = R"FIDL(
@available(added=5)
library example;
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4, "4");
ASSERT_EQUIVALENT(fidl, v5_onward, "5");
ASSERT_EQUIVALENT(fidl, v5_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v5_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, AllElementsSplitByReference) {
auto fidl_prefix = R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
const VALUE uint32 = 1;
@available(added=2)
const VALUE uint32 = 2;
@available(replaced=2)
type Type = struct {
value bool;
};
@available(added=2)
type Type = table {
1: value bool;
};
// Need unsigned integers for bits underlying type.
@available(replaced=2)
alias IntegerType = uint32;
@available(added=2)
alias IntegerType = uint64;
// Need uint32/int32 for error type.
@available(replaced=2)
alias ErrorIntegerType = uint32;
@available(added=2)
alias ErrorIntegerType = int32;
@available(replaced=2)
protocol TargetProtocol {};
@available(added=2)
protocol TargetProtocol {
Method();
};
)FIDL";
auto v1_prefix = R"FIDL(
@available(added=1, removed=2)
library example;
const VALUE uint32 = 1;
type Type = struct {
value bool;
};
alias IntegerType = uint32;
alias ErrorIntegerType = uint32;
protocol TargetProtocol {};
)FIDL";
auto v2_onward_prefix = R"FIDL(
@available(added=2)
library example;
const VALUE uint32 = 2;
type Type = table {
1: value bool;
};
alias IntegerType = uint64;
alias ErrorIntegerType = int32;
protocol TargetProtocol { Method(); };
)FIDL";
auto common_suffix = R"FIDL(
const CONST uint32 = VALUE;
alias Alias = Type;
// TODO(https://fxbug.dev/42158155): Uncomment.
// type Newtype = Type;
type BitsUnderlying = bits : IntegerType {
MEMBER = 1;
};
type BitsMemberValue = bits {
MEMBER = VALUE;
};
type EnumUnderlying = enum : IntegerType {
MEMBER = 1;
};
type EnumMemberValue = enum {
MEMBER = VALUE;
};
type StructMemberType = struct {
member Type;
};
type StructMemberDefault = struct {
@allow_deprecated_struct_defaults
member uint32 = VALUE;
};
type Table = table {
1: member Type;
};
type Union = union {
1: member Type;
};
protocol ProtocolComposition {
compose TargetProtocol;
};
protocol ProtocolMethodRequest {
Method(Type);
};
protocol ProtocolMethodResponse {
Method() -> (Type);
};
protocol ProtocolEvent {
-> Event(Type);
};
protocol ProtocolSuccess {
Method() -> (Type) error uint32;
};
protocol ProtocolError {
Method() -> () error ErrorIntegerType;
};
service Service {
member client_end:TargetProtocol;
};
resource_definition Resource : uint32 {
properties {
first IntegerType;
// This property is required for compilation, but is not otherwise under test.
subtype flexible enum : uint32 {};
};
};
type NestedTypes = struct {
first vector<Type>;
second vector<array<Type, 3>>;
};
type LayoutParameters = struct {
member array<bool, VALUE>;
};
type Constraints = struct {
member vector<bool>:VALUE;
};
type AnonymousLayouts = struct {
first_member table {
1: second_member union {
1: third_member Type;
};
};
};
protocol AnonymousLayoutsInProtocol {
Request(struct { member Type; });
Response() -> (struct { member Type; });
-> Event(struct { member Type; });
Success() -> (struct { member Type; }) error uint32;
Error() -> () error ErrorIntegerType;
};
)FIDL";
auto fidl = std::string(fidl_prefix) + common_suffix;
auto v1 = std::string(v1_prefix) + common_suffix;
auto v2_onward = std::string(v2_onward_prefix) + common_suffix;
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2_onward, "2");
ASSERT_EQUIVALENT(fidl, v2_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v2_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, Complicated) {
auto fidl = R"FIDL(
@available(added=1)
library example;
type X = resource struct {
@available(removed=7)
x1 bool;
@available(added=3)
x2 Y;
@available(added=4)
x3 Z;
};
@available(added=3)
type Y = resource union {
1: y1 client_end:A;
@available(added=4, removed=5)
2: y2 client_end:B;
};
@available(added=3)
type Z = resource struct {
z1 Y:optional;
z2 vector<W>:optional;
};
@available(added=3)
type W = resource table {
1: w1 X;
};
protocol A {
A1(X);
@available(added=7)
A2(resource struct { y Y; });
};
@available(added=3)
protocol B {
@available(removed=5)
B1(X);
@available(added=5)
B2(resource struct {
x X;
y Y;
});
};
@available(removed=6)
protocol AB {
compose A;
@available(added=4)
compose B;
};
)FIDL";
auto v1_to_2 = R"FIDL(
@available(added=1, removed=3)
library example;
type X = resource struct {
x1 bool;
};
protocol A {
A1(X);
};
protocol AB {
compose A;
};
)FIDL";
auto v3 = R"FIDL(
@available(added=3, removed=4)
library example;
type X = resource struct {
x1 bool;
x2 Y;
};
type Y = resource union {
1: y1 client_end:A;
};
type Z = resource struct {
z1 Y:optional;
z2 vector<W>:optional;
};
type W = resource table {
1: w1 X;
};
protocol A {
A1(X);
};
protocol B {
B1(X);
};
protocol AB {
compose A;
};
)FIDL";
auto v4 = R"FIDL(
@available(added=4, removed=5)
library example;
type X = resource struct {
x1 bool;
x2 Y;
x3 Z;
};
type Y = resource union {
1: y1 client_end:A;
2: y2 client_end:B;
};
type Z = resource struct {
z1 Y:optional;
z2 vector<W>:optional;
};
type W = resource table {
1: w1 X;
};
protocol A {
A1(X);
};
protocol B {
B1(X);
};
protocol AB {
compose A;
compose B;
};
)FIDL";
auto v5 = R"FIDL(
@available(added=5, removed=6)
library example;
type X = resource struct {
x1 bool;
x2 Y;
x3 Z;
};
type Y = resource union {
1: y1 client_end:A;
};
type Z = resource struct {
z1 Y:optional;
z2 vector<W>:optional;
};
type W = resource table {
1: w1 X;
};
protocol A {
A1(X);
};
protocol B {
B2(resource struct {
x X;
y Y;
});
};
protocol AB {
compose A;
compose B;
};
)FIDL";
auto v6 = R"FIDL(
@available(added=6, removed=7)
library example;
type X = resource struct {
x1 bool;
x2 Y;
x3 Z;
};
type Y = resource union {
1: y1 client_end:A;
};
type Z = resource struct {
z1 Y:optional;
z2 vector<W>:optional;
};
type W = resource table {
1: w1 X;
};
protocol A {
A1(X);
};
protocol B {
B2(resource struct {
x X;
y Y;
});
};
)FIDL";
auto v7_onward = R"FIDL(
@available(added=7)
library example;
type X = resource struct {
x2 Y;
x3 Z;
};
type Y = resource union {
1: y1 client_end:A;
};
type Z = resource struct {
z1 Y:optional;
z2 vector<W>:optional;
};
type W = resource table {
1: w1 X;
};
protocol A {
A1(X);
A2(resource struct { y Y; });
};
protocol B {
B2(resource struct {
x X;
y Y;
});
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1_to_2, "1");
ASSERT_EQUIVALENT(fidl, v1_to_2, "2");
ASSERT_EQUIVALENT(fidl, v3, "3");
ASSERT_EQUIVALENT(fidl, v4, "4");
ASSERT_EQUIVALENT(fidl, v5, "5");
ASSERT_EQUIVALENT(fidl, v6, "6");
ASSERT_EQUIVALENT(fidl, v7_onward, "7");
ASSERT_EQUIVALENT(fidl, v7_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v7_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, Legacy) {
auto fidl = R"FIDL(
@available(added=1)
library example;
protocol NeverRemoved {
@available(removed=3)
RemovedAt3();
@available(removed=3, legacy=false)
RemovedAt3LegacyFalse();
@available(removed=3, legacy=true)
RemovedAt3LegacyTrue();
@available(replaced=2)
ReplacedAt2();
@available(added=2)
ReplacedAt2(struct { b bool; });
};
@available(removed=3)
protocol RemovedAt3 {
Default();
@available(legacy=false)
LegacyFalse();
@available(removed=2)
RemovedAt2();
@available(replaced=2)
ReplacedAt2();
@available(added=2)
ReplacedAt2(struct { b bool; });
};
@available(removed=3, legacy=false)
protocol RemovedAt3LegacyFalse {
Default();
@available(legacy=false)
LegacyFalse();
@available(removed=2)
RemovedAt2();
@available(replaced=2)
ReplacedAt2();
@available(added=2)
ReplacedAt2(struct { b bool; });
};
@available(removed=3, legacy=true)
protocol RemovedAt3LegacyTrue {
Default();
@available(legacy=false)
LegacyFalse();
@available(legacy=true)
LegacyTrue();
@available(removed=2)
RemovedAt2();
@available(replaced=2)
ReplacedAt2();
@available(added=2)
ReplacedAt2(struct { b bool; });
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
protocol NeverRemoved {
RemovedAt3();
RemovedAt3LegacyFalse();
RemovedAt3LegacyTrue();
ReplacedAt2();
};
protocol RemovedAt3 {
Default();
LegacyFalse();
RemovedAt2();
ReplacedAt2();
};
protocol RemovedAt3LegacyFalse {
Default();
LegacyFalse();
RemovedAt2();
ReplacedAt2();
};
protocol RemovedAt3LegacyTrue {
Default();
LegacyFalse();
LegacyTrue();
RemovedAt2();
ReplacedAt2();
};
)FIDL";
auto v2 = R"FIDL(
@available(added=2, removed=3)
library example;
protocol NeverRemoved {
RemovedAt3();
RemovedAt3LegacyFalse();
RemovedAt3LegacyTrue();
ReplacedAt2(struct { b bool; });
};
protocol RemovedAt3 {
Default();
LegacyFalse();
ReplacedAt2(struct { b bool; });
};
protocol RemovedAt3LegacyFalse {
Default();
LegacyFalse();
ReplacedAt2(struct { b bool; });
};
protocol RemovedAt3LegacyTrue {
Default();
LegacyFalse();
LegacyTrue();
ReplacedAt2(struct { b bool; });
};
)FIDL";
auto v3_to_head = R"FIDL(
@available(added=3)
library example;
protocol NeverRemoved {
ReplacedAt2(struct { b bool; });
};
)FIDL";
auto legacy = R"FIDL(
// This is the closest we can get to making the library only available at LEGACY.
@available(added=1, removed=2, legacy=true)
library example;
protocol NeverRemoved {
RemovedAt3LegacyTrue();
ReplacedAt2(struct { b bool; });
};
protocol RemovedAt3LegacyTrue {
Default();
LegacyTrue();
ReplacedAt2(struct { b bool; });
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2, "2");
ASSERT_EQUIVALENT(fidl, v3_to_head, "3");
ASSERT_EQUIVALENT(fidl, v3_to_head, "HEAD");
ASSERT_EQUIVALENT(fidl, legacy, "LEGACY");
}
TEST(VersioningDecompositionTests, ConvertNamedToAnonymous) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
type Foo = struct {
member Bar;
};
@available(replaced=2)
type Bar = struct {};
@available(added=2)
type Foo = struct {
member @generated_name("Bar") struct {};
};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type Foo = struct {
member Bar;
};
type Bar = struct {};
)FIDL";
auto v2_onward = R"FIDL(
@available(added=2)
library example;
type Foo = struct {
member @generated_name("Bar") struct {};
};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2_onward, "2");
ASSERT_EQUIVALENT(fidl, v2_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v2_onward, "LEGACY");
}
TEST(VersioningDecompositionTests, ConvertAnonymousToNamed) {
auto fidl = R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
type Foo = struct {
member @generated_name("Bar") struct {};
};
@available(added=2)
type Foo = struct {
member Bar;
};
@available(added=2)
type Bar = struct {};
)FIDL";
auto v1 = R"FIDL(
@available(added=1, removed=2)
library example;
type Foo = struct {
member @generated_name("Bar") struct {};
};
)FIDL";
auto v2_onward = R"FIDL(
@available(added=2)
library example;
type Foo = struct {
member Bar;
};
type Bar = struct {};
)FIDL";
ASSERT_EQUIVALENT(fidl, v1, "1");
ASSERT_EQUIVALENT(fidl, v2_onward, "2");
ASSERT_EQUIVALENT(fidl, v2_onward, "HEAD");
ASSERT_EQUIVALENT(fidl, v2_onward, "LEGACY");
}
} // namespace
} // namespace fidlc