blob: 06bff3728277930938a7ece998fabcf7aad5e173 [file] [log] [blame]
// Copyright 2019 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/names.h>
#include <fidl/parser.h>
#include <fidl/source_file.h>
#include <unittest/unittest.h>
#include "error_test.h"
#include "test_library.h"
namespace {
static bool Compiles(const std::string& source_code,
std::vector<std::unique_ptr<fidl::BaseError>>* out_errors = nullptr) {
auto library = TestLibrary("test.fidl", source_code);
return library.Compile();
}
bool compiling() {
BEGIN_TEST;
// Keywords as field names.
EXPECT_TRUE(Compiles(R"FIDL(
library test;
struct struct {
bool field;
};
union Foo {
1: int64 union;
2: bool library;
3: uint32 uint32;
4: struct member;
};
)FIDL"));
// Recursion is allowed.
EXPECT_TRUE(Compiles(R"FIDL(
library test;
union Value {
1: bool bool_value;
2: vector<Value?> list_value;
};
)FIDL"));
// Mutual recursion is allowed.
EXPECT_TRUE(Compiles(R"FIDL(
library test;
union Foo {
1: Bar bar;
};
struct Bar {
Foo? foo;
};
)FIDL"));
// Specifying flexible is allowed.
EXPECT_TRUE(Compiles(R"FIDL(
library test;
flexible union Foo {
1: string bar;
};
)FIDL"));
// Specifying strict is allowed.
EXPECT_TRUE(Compiles(R"FIDL(
library test;
strict union Foo {
1: string bar;
};
)FIDL"));
END_TEST;
}
bool must_have_explicit_ordinals() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
int64 foo;
vector<uint32>:10 bar;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 2u);
ASSERT_ERR(library.errors().at(0), fidl::ErrMissingOrdinalBeforeType);
ASSERT_ERR(library.errors().at(1), fidl::ErrMissingOrdinalBeforeType);
END_TEST;
}
bool explicit_ordinals() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
1: int64 foo;
2: vector<uint32>:10 bar;
};
)FIDL");
ASSERT_TRUE(library.Compile());
auto fidl_union = library.LookupUnion("Foo");
ASSERT_NONNULL(fidl_union);
ASSERT_EQ(fidl_union->members.size(), 2);
auto& member0 = fidl_union->members[0];
EXPECT_NONNULL(member0.maybe_used);
EXPECT_EQ(member0.ordinal->value, 1);
auto& member1 = fidl_union->members[1];
EXPECT_NONNULL(member1.maybe_used);
EXPECT_EQ(member1.ordinal->value, 2);
END_TEST;
}
bool explicit_ordinals_with_reserved() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
1: reserved;
2: int64 foo;
3: reserved;
4: vector<uint32>:10 bar;
5: reserved;
};
)FIDL");
ASSERT_TRUE(library.Compile());
auto fidl_union = library.LookupUnion("Foo");
ASSERT_NONNULL(fidl_union);
ASSERT_EQ(fidl_union->members.size(), 5);
auto& member0 = fidl_union->members[0];
EXPECT_NULL(member0.maybe_used);
EXPECT_EQ(member0.ordinal->value, 1);
auto& member1 = fidl_union->members[1];
EXPECT_NONNULL(member1.maybe_used);
EXPECT_EQ(member1.ordinal->value, 2);
auto& member2 = fidl_union->members[2];
EXPECT_NULL(member2.maybe_used);
EXPECT_EQ(member2.ordinal->value, 3);
auto& member3 = fidl_union->members[3];
EXPECT_NONNULL(member3.maybe_used);
EXPECT_EQ(member3.ordinal->value, 4);
auto& member4 = fidl_union->members[4];
EXPECT_NULL(member4.maybe_used);
EXPECT_EQ(member4.ordinal->value, 5);
END_TEST;
}
bool explicit_ordinals_out_of_order() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
5: int64 foo;
2: vector<uint32>:10 bar;
3: reserved;
1: reserved;
4: uint32 baz;
};
)FIDL");
ASSERT_TRUE(library.Compile());
auto fidl_union = library.LookupUnion("Foo");
ASSERT_NONNULL(fidl_union);
ASSERT_EQ(fidl_union->members.size(), 5);
auto& member0 = fidl_union->members[0];
EXPECT_NONNULL(member0.maybe_used);
EXPECT_EQ(member0.ordinal->value, 5);
auto& member1 = fidl_union->members[1];
EXPECT_NONNULL(member1.maybe_used);
EXPECT_EQ(member1.ordinal->value, 2);
auto& member2 = fidl_union->members[2];
EXPECT_NULL(member2.maybe_used);
EXPECT_EQ(member2.ordinal->value, 3);
auto& member3 = fidl_union->members[3];
EXPECT_NULL(member3.maybe_used);
EXPECT_EQ(member3.ordinal->value, 1);
auto& member4 = fidl_union->members[4];
EXPECT_NONNULL(member4.maybe_used);
EXPECT_EQ(member4.ordinal->value, 4);
END_TEST;
}
bool ordinal_out_of_bounds() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
-1: uint32 foo;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrOrdinalOutOfBound);
END_TEST;
}
bool ordinals_must_be_unique() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
1: reserved;
1: uint64 x;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrDuplicateUnionMemberOrdinal);
END_TEST;
}
bool member_names_must_be_unique() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Duplicates {
1: string s;
2: int32 s;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrDuplicateUnionMemberName);
END_TEST;
}
bool cannot_mix_explicit_and_hashed_ordinals() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
1: int64 foo;
vector<uint32>:10 bar;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrMissingOrdinalBeforeType);
END_TEST;
}
bool cannot_start_at_zero() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
0: uint32 foo;
1: uint64 bar;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrOrdinalsMustStartAtOne);
END_TEST;
}
bool default_not_allowed() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library test;
union Foo {
1: int64 t = 1;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrDefaultsOnUnionsNotSupported);
END_TEST;
}
bool must_be_dense() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Example {
1: int64 first;
3: int64 third;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrNonDenseOrdinal);
ASSERT_STR_STR(library.errors().at(0)->msg.c_str(), "2");
END_TEST;
}
bool must_have_at_least_one_non_reserved() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Foo {
2: reserved;
1: reserved;
};
)FIDL");
ASSERT_FALSE(library.Compile());
ASSERT_EQ(library.errors().size(), 1u);
ASSERT_ERR(library.errors().at(0), fidl::ErrMustHaveNonReservedMember);
END_TEST;
}
bool no_nullable_members_in_unions() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Foo {
1: string? bar;
};
)FIDL");
ASSERT_FALSE(library.Compile());
const auto& errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrNullableUnionMember);
END_TEST;
}
bool no_directly_recursive_unions() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Value {
1: Value value;
};
)FIDL");
ASSERT_FALSE(library.Compile());
const auto& errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrIncludeCycle);
END_TEST;
}
bool invalid_empty_unions() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Foo {};
)FIDL");
ASSERT_FALSE(library.Compile());
const auto& errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrMustHaveNonReservedMember);
END_TEST;
}
bool error_syntax_explicit_ordinals() {
BEGIN_TEST;
TestLibrary error_library(R"FIDL(
library example;
protocol Example {
Method() -> () error int32;
};
)FIDL");
ASSERT_TRUE(error_library.Compile());
const fidl::flat::Union* error_union = error_library.LookupUnion("Example_Method_Result");
ASSERT_NOT_NULL(error_union);
ASSERT_EQ(error_union->members.front().ordinal->value, 1);
ASSERT_EQ(error_union->members.back().ordinal->value, 2);
END_TEST;
}
bool no_selector() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Foo {
[Selector = "v2"] 1: string v;
};
)FIDL");
ASSERT_FALSE(library.Compile());
const auto& errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrInvalidAttributePlacement);
ASSERT_STR_STR(errors[0]->msg.c_str(), "Selector");
END_TEST;
}
bool deprecated_xunion_error() {
BEGIN_TEST;
{
TestLibrary xunion_library(R"FIDL(
library test;
xunion Foo {
1: string foo;
};
)FIDL");
ASSERT_FALSE(xunion_library.Compile());
const auto& errors = xunion_library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrXunionDeprecated);
}
{
TestLibrary flexible_xunion_library(R"FIDL(
library test;
flexible xunion FlexibleFoo {
1: string foo;
};
)FIDL");
ASSERT_FALSE(flexible_xunion_library.Compile());
const auto& errors = flexible_xunion_library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrXunionDeprecated);
}
{
TestLibrary strict_xunion_library(R"FIDL(
library test;
strict xunion StrictFoo {
1: string foo;
};
)FIDL");
ASSERT_FALSE(strict_xunion_library.Compile());
const auto& errors = strict_xunion_library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_ERR(errors[0], fidl::ErrStrictXunionDeprecated);
}
END_TEST;
}
} // namespace
BEGIN_TEST_CASE(union_tests)
RUN_TEST(compiling)
RUN_TEST(must_have_explicit_ordinals)
RUN_TEST(explicit_ordinals)
RUN_TEST(explicit_ordinals_with_reserved)
RUN_TEST(explicit_ordinals_out_of_order)
RUN_TEST(ordinal_out_of_bounds)
RUN_TEST(ordinals_must_be_unique)
RUN_TEST(member_names_must_be_unique)
RUN_TEST(cannot_mix_explicit_and_hashed_ordinals)
RUN_TEST(cannot_start_at_zero)
RUN_TEST(default_not_allowed)
RUN_TEST(must_be_dense)
RUN_TEST(must_have_at_least_one_non_reserved)
RUN_TEST(no_nullable_members_in_unions)
RUN_TEST(no_directly_recursive_unions)
RUN_TEST(invalid_empty_unions)
RUN_TEST(error_syntax_explicit_ordinals)
RUN_TEST(no_selector)
RUN_TEST(deprecated_xunion_error)
END_TEST_CASE(union_tests)