blob: 18ef0dc1af5f5369909d9f7e8cadd18d743629e9 [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 "test_library.h"
namespace {
static bool Compiles(const std::string& source_code,
std::vector<std::string>* out_errors = nullptr) {
auto library = TestLibrary("test.fidl", source_code);
const bool success = library.Compile();
if (out_errors) {
*out_errors = library.errors();
}
return success;
}
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(), 1u);
ASSERT_STR_STR(library.errors().at(0).c_str(), "missing ordinal before type");
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 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_STR_STR(library.errors().at(0).c_str(), "Multiple union fields with the same ordinal");
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_STR_STR(library.errors().at(0).c_str(), "missing ordinal before type");
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_STR_STR(library.errors().at(0).c_str(), "ordinals must start at 1");
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_STR_STR(library.errors().at(0).c_str(), "unexpected token Equal");
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_STR_STR(library.errors().at(0).c_str(),
"missing ordinal 2 (ordinals must be dense); consider marking it reserved");
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_STR_STR(library.errors().at(0).c_str(), "must have at least one non reserved member");
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());
auto errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(), "union members cannot be nullable");
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());
auto errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(), "There is an includes-cycle in declarations");
END_TEST;
}
bool invalid_empty_unions() {
BEGIN_TEST;
TestLibrary library(R"FIDL(
library example;
union Foo {};
)FIDL");
ASSERT_FALSE(library.Compile());
auto errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(), "must have at least one non reserved member");
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());
auto errors = library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(), "placement of attribute 'Selector' disallowed here");
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());
auto errors = xunion_library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(), "xunion is deprecated, please use `flexible union` instead");
TestLibrary flexible_xunion_library(R"FIDL(
library test;
flexible xunion FlexibleFoo {
1: string foo;
};
)FIDL");
ASSERT_FALSE(flexible_xunion_library.Compile());
errors = flexible_xunion_library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(), "xunion is deprecated, please use `flexible union` instead");
TestLibrary strict_xunion_library(R"FIDL(
library test;
strict xunion StrictFoo {
1: string foo;
};
)FIDL");
ASSERT_FALSE(strict_xunion_library.Compile());
errors = strict_xunion_library.errors();
ASSERT_EQ(errors.size(), 1);
ASSERT_STR_STR(errors[0].c_str(),
"strict xunion is deprecated, please use `strict union` instead");
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(ordinals_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)