blob: 2bc9d8da4253f33df83fc6a17fe12705a688043d [file] [log] [blame]
// Copyright 2024 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 <gtest/gtest.h>
#include "tools/fidl/fidlc/tests/test_library.h"
// This file tests how FIDL versioning distinguishes "removal" from
// "replacement", and validation that replacement is done correctly.
// Tests are run for all the versions given in INSTANTIATE_TEST_SUITE_P.
namespace fidlc {
namespace {
class VersioningReplacementTest : public testing::TestWithParam<Version> {};
const Version V1 = Version::From(1).value();
const Version V2 = Version::From(2).value();
const Version V3 = Version::From(3).value();
INSTANTIATE_TEST_SUITE_P(VersioningReplacementTests, VersioningReplacementTest,
testing::Values(V1, V2, V3),
[](auto info) { return info.param.ToString(); });
TEST_P(VersioningReplacementTest, GoodDeclRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(removed=2)
type Foo = struct {};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
}
TEST_P(VersioningReplacementTest, BadDeclRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(removed=2)
type Foo = struct {};
@available(added=2)
type Foo = resource struct {};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrRemovedWithReplacement, "Foo", V2, "example.fidl:9:6");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodDeclReplaced) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
type Foo = struct {};
@available(added=2)
type Foo = resource struct {};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupStruct("Foo")->resourceness,
GetParam() == V1 ? Resourceness::kValue : Resourceness::kResource);
}
TEST_P(VersioningReplacementTest, BadDeclReplaced) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
type Foo = struct {};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrReplacedWithoutReplacement, "Foo", V2);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodMemberRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Foo = table {
@available(removed=2)
1: bar string;
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupTable("Foo")->members.size(), GetParam() == V1 ? 1u : 0u);
}
TEST_P(VersioningReplacementTest, BadMemberRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Foo = table {
@available(removed=2)
1: bar string;
@available(added=2)
1: bar uint32;
};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrRemovedWithReplacement, "bar", V2, "example.fidl:9:8");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodMemberReplaced) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Foo = table {
@available(replaced=2)
1: bar string;
@available(added=2)
1: bar uint32;
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupTable("Foo")->members.front().type_ctor->type->kind,
GetParam() == V1 ? Type::Kind::kString : Type::Kind::kPrimitive);
}
TEST_P(VersioningReplacementTest, BadMemberReplaced) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Foo = table {
@available(replaced=2)
1: bar string;
};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrReplacedWithoutReplacement, "bar", V2);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodMethodRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
protocol Foo {
@available(removed=2)
Bar();
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupProtocol("Foo")->methods.size(), GetParam() == V1 ? 1u : 0u);
}
TEST_P(VersioningReplacementTest, BadMethodRemoved) {
TestLibrary library;
library.SelectVersion("test", GetParam());
library.AddFile("bad/fi-0205.test.fidl");
library.ExpectFail(ErrRemovedWithReplacement, "Bar", V2, "bad/fi-0205.test.fidl:11:14");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodMethodReplaced) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupProtocol("Foo")->methods[0].strictness,
GetParam() == V1 ? Strictness::kStrict : Strictness::kFlexible);
}
TEST_P(VersioningReplacementTest, BadMethodReplaced) {
TestLibrary library;
library.AddFile("bad/fi-0206.test.fidl");
library.SelectVersion("test", GetParam());
library.ExpectFail(ErrReplacedWithoutReplacement, "Bar", V2);
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, BadMethodRemovedComposeNew) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
protocol Protocol {
@available(removed=2)
Method();
@available(added=2)
compose Base;
};
protocol Base {
@selector("example/Protocol.Method")
Method();
};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrRemovedWithReplacement, "Method", V2, "example.fidl:15:3");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, BadMethodRemovedComposeExisting) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
protocol Protocol {
@available(removed=2)
Method();
compose Base;
};
protocol Base {
@available(added=2)
@selector("example/Protocol.Method")
Method();
};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrRemovedWithReplacement, "Method", V2, "example.fidl:15:3");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodMethodReplacedComposeNew) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
protocol Protocol {
@available(replaced=2)
Method();
@available(added=2)
compose Base;
};
protocol Base {
@selector("example/Protocol.Method")
Method();
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupProtocol("Protocol")->all_methods[0].is_composed, GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodMethodReplacedComposeExisting) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
protocol Protocol {
@available(replaced=2)
Method();
compose Base;
};
protocol Base {
@available(added=2)
@selector("example/Protocol.Method")
Method();
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.LookupProtocol("Protocol")->all_methods[0].is_composed, GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodReplacedTwice) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
type Foo = struct {};
@available(added=2, replaced=3)
type Foo = struct {};
@available(added=3)
type Foo = struct {};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
}
TEST_P(VersioningReplacementTest, GoodAllDeclsReplacedAndRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
const CONST uint32 = 1;
@available(added=2, removed=3)
const CONST uint32 = 1;
@available(replaced=2)
alias Alias = string;
@available(added=2, removed=3)
alias Alias = string;
// TODO(https://fxbug.dev/42158155): Uncomment.
// @available(replaced=2)
// type Type = string;
// @available(added=2, removed=2)
// type Type = string;
@available(replaced=2)
type Bits = bits {};
@available(added=2, removed=3)
type Bits = bits {};
@available(replaced=2)
type Enum = enum {};
@available(added=2, removed=3)
type Enum = enum {};
@available(replaced=2)
type Struct = struct {};
@available(added=2, removed=3)
type Struct = struct {};
@available(replaced=2)
type Table = table {};
@available(added=2, removed=3)
type Table = table {};
@available(replaced=2)
type Union = union {};
@available(added=2, removed=3)
type Union = union {};
@available(replaced=2)
protocol Protocol {};
@available(added=2, removed=3)
protocol Protocol {};
@available(replaced=2)
service Service {};
@available(added=2, removed=3)
service Service {};
@available(replaced=2)
resource_definition Resource : uint32 { properties { subtype flexible enum : uint32 {}; }; };
@available(added=2, removed=3)
resource_definition Resource : uint32 { properties { subtype flexible enum : uint32 {}; }; };
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.declaration_order().size(), GetParam() <= V2 ? 11u : 0u);
}
TEST_P(VersioningReplacementTest, GoodAllMembersReplacedAndRemoved) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Bits = bits {
@available(replaced=2)
MEMBER = 1;
@available(added=2, removed=3)
MEMBER = 1;
};
type Enum = enum {
@available(replaced=2)
MEMBER = 1;
@available(added=2, removed=3)
MEMBER = 1;
};
type Struct = struct {
@available(replaced=2)
member uint32;
@available(added=2, removed=3)
member uint32;
};
type Table = table {
@available(replaced=2)
1: member uint32;
@available(added=2, removed=3)
1: member uint32;
};
type Union = union {
@available(replaced=2)
1: member uint32;
@available(added=2, removed=3)
1: member uint32;
};
protocol Protocol {
@available(replaced=2)
Member();
@available(added=2, removed=3)
Member();
};
service Service {
@available(replaced=2)
member client_end:Protocol;
@available(added=2, removed=3)
member client_end:Protocol;
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
auto num_members = GetParam() <= V2 ? 1u : 0u;
EXPECT_EQ(library.LookupBits("Bits")->members.size(), num_members);
EXPECT_EQ(library.LookupEnum("Enum")->members.size(), num_members);
EXPECT_EQ(library.LookupStruct("Struct")->members.size(), num_members);
EXPECT_EQ(library.LookupTable("Table")->members.size(), num_members);
EXPECT_EQ(library.LookupUnion("Union")->members.size(), num_members);
EXPECT_EQ(library.LookupProtocol("Protocol")->methods.size(), num_members);
EXPECT_EQ(library.LookupService("Service")->members.size(), num_members);
}
TEST_P(VersioningReplacementTest, BadRemovedNamedToAnonymous) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(removed=2)
type Foo = struct {};
type Bar = struct {
@available(added=2)
foo struct {};
};
)FIDL");
library.SelectVersion("example", GetParam());
library.ExpectFail(ErrRemovedWithReplacement, "Foo", V2, "example.fidl:10:9");
ASSERT_COMPILER_DIAGNOSTICS(library);
}
TEST_P(VersioningReplacementTest, GoodRemovedAnonymousToNamed) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Bar = struct {
// The anonymous type "Foo" inherits removed=2, but removed/replaced
// does not apply to inherited availabilities.
@available(removed=2)
foo struct {};
};
@available(added=2)
type Foo = table {};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
EXPECT_EQ(library.HasTable("Foo"), GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodRemovedAnonymousToAnonymous) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Bar1 = struct {
// The anonymous type "Foo" inherits removed=2, but removed/replaced
// does not apply to inherited availabilities.
@available(removed=2)
foo struct {};
};
type Bar2 = struct {
@available(added=2)
foo table {};
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
EXPECT_EQ(library.HasTable("Foo"), GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodReplacedNamedToAnonymous) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
@available(replaced=2)
type Foo = struct {};
type Bar = struct {
@available(added=2)
foo table {};
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
EXPECT_EQ(library.HasTable("Foo"), GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodReplacedAnonymousToNamed) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Bar = struct {
@available(replaced=2)
foo struct {};
@available(added=2)
foo string;
};
@available(added=2)
type Foo = table {};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
EXPECT_EQ(library.HasTable("Foo"), GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodReplacedAnonymousToAnonymous) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Bar1 = struct {
@available(replaced=2)
foo struct {};
@available(added=2)
foo string;
};
type Bar2 = struct {
@available(added=2)
foo table {};
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
EXPECT_EQ(library.HasTable("Foo"), GetParam() >= V2);
}
TEST_P(VersioningReplacementTest, GoodReplacedAnonymousToNothing) {
TestLibrary library(R"FIDL(
@available(added=1)
library example;
type Bar = struct {
// The anonymous type "Foo" inherits replaced=2, but removed/replaced
// validation does not apply to inherited availabilities.
@available(replaced=2)
foo struct {};
@available(added=2)
foo string;
};
)FIDL");
library.SelectVersion("example", GetParam());
ASSERT_COMPILED(library);
EXPECT_EQ(library.HasStruct("Foo"), GetParam() == V1);
}
} // namespace
} // namespace fidlc