| // Copyright 2018 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/parser.h> |
| #include <fidl/source_file.h> |
| |
| #include <zxtest/zxtest.h> |
| |
| #include "error_test.h" |
| #include "fidl/diagnostics.h" |
| #include "fidl/flat/types.h" |
| #include "test_library.h" |
| |
| namespace { |
| |
| TEST(ProtocolTests, GoodValidEmptyProtocol) { |
| TestLibrary library(R"FIDL(library example; |
| |
| protocol Empty {}; |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_COMPILED(library); |
| |
| auto protocol = library.LookupProtocol("Empty"); |
| ASSERT_NOT_NULL(protocol); |
| |
| EXPECT_EQ(protocol->methods.size(), 0); |
| EXPECT_EQ(protocol->openness, fidl::types::Openness::kAjar); |
| EXPECT_EQ(protocol->all_methods.size(), 0); |
| } |
| |
| TEST(ProtocolTests, GoodValidEmptyOpenProtocol) { |
| TestLibrary library(R"FIDL(library example; |
| |
| open protocol Empty {}; |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_COMPILED(library); |
| |
| auto protocol = library.LookupProtocol("Empty"); |
| ASSERT_NOT_NULL(protocol); |
| |
| EXPECT_EQ(protocol->methods.size(), 0); |
| EXPECT_EQ(protocol->openness, fidl::types::Openness::kOpen); |
| EXPECT_EQ(protocol->all_methods.size(), 0); |
| } |
| |
| TEST(ProtocolTests, GoodValidEmptyAjarProtocol) { |
| TestLibrary library(R"FIDL(library example; |
| |
| ajar protocol Empty {}; |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_COMPILED(library); |
| |
| auto protocol = library.LookupProtocol("Empty"); |
| ASSERT_NOT_NULL(protocol); |
| |
| EXPECT_EQ(protocol->methods.size(), 0); |
| EXPECT_EQ(protocol->openness, fidl::types::Openness::kAjar); |
| EXPECT_EQ(protocol->all_methods.size(), 0); |
| } |
| |
| TEST(ProtocolTests, GoodValidEmptyClosedProtocol) { |
| TestLibrary library(R"FIDL(library example; |
| |
| closed protocol Empty {}; |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_COMPILED(library); |
| |
| auto protocol = library.LookupProtocol("Empty"); |
| ASSERT_NOT_NULL(protocol); |
| |
| EXPECT_EQ(protocol->methods.size(), 0); |
| EXPECT_EQ(protocol->openness, fidl::types::Openness::kClosed); |
| EXPECT_EQ(protocol->all_methods.size(), 0); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, GoodValidEmptyProtocolWithoutUnknownInteractions) { |
| TestLibrary library(R"FIDL(library example; |
| |
| protocol Empty {}; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| |
| auto protocol = library.LookupProtocol("Empty"); |
| ASSERT_NOT_NULL(protocol); |
| |
| EXPECT_EQ(protocol->methods.size(), 0); |
| EXPECT_EQ(protocol->openness, fidl::types::Openness::kAjar); |
| EXPECT_EQ(protocol->all_methods.size(), 0); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadOpenProtocolWithoutUnknownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| open protocol Empty {}; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedDeclaration); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadAjarProtocolWithoutUnknownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| ajar protocol Empty {}; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedDeclaration); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadClosedProtocolWithoutUnknownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| closed protocol Empty {}; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedDeclaration); |
| } |
| |
| TEST(ProtocolTests, BadEmptyStrictProtocol) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| strict protocol Empty {}; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedDeclaration); |
| } |
| |
| TEST(ProtocolTests, BadEmptyFlexibleProtocol) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| flexible protocol Empty {}; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedDeclaration); |
| } |
| |
| TEST(ProtocolTests, BadOpenMissingProtocolToken) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| open Empty {}; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnexpectedIdentifier); |
| } |
| |
| TEST(ProtocolTests, BadAjarMissingProtocolToken) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| ajar Empty {}; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnexpectedIdentifier); |
| } |
| |
| TEST(ProtocolTests, BadClosedMissingProtocolToken) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| closed Empty {}; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnexpectedIdentifier); |
| } |
| |
| TEST(ProtocolTests, BadEmptyProtocolMember) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Example { |
| ; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, GoodValidProtocolComposition) { |
| TestLibrary library(R"FIDL(library example; |
| |
| protocol A { |
| MethodA(); |
| }; |
| |
| protocol B { |
| compose A; |
| MethodB(); |
| }; |
| |
| protocol C { |
| compose A; |
| MethodC(); |
| }; |
| |
| protocol D { |
| compose B; |
| compose C; |
| MethodD(); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| |
| auto protocol_a = library.LookupProtocol("A"); |
| ASSERT_NOT_NULL(protocol_a); |
| EXPECT_EQ(protocol_a->methods.size(), 1); |
| EXPECT_EQ(protocol_a->all_methods.size(), 1); |
| |
| auto protocol_b = library.LookupProtocol("B"); |
| ASSERT_NOT_NULL(protocol_b); |
| EXPECT_EQ(protocol_b->methods.size(), 1); |
| EXPECT_EQ(protocol_b->all_methods.size(), 2); |
| |
| auto protocol_c = library.LookupProtocol("C"); |
| ASSERT_NOT_NULL(protocol_c); |
| EXPECT_EQ(protocol_c->methods.size(), 1); |
| EXPECT_EQ(protocol_c->all_methods.size(), 2); |
| |
| auto protocol_d = library.LookupProtocol("D"); |
| ASSERT_NOT_NULL(protocol_d); |
| EXPECT_EQ(protocol_d->methods.size(), 1); |
| EXPECT_EQ(protocol_d->all_methods.size(), 4); |
| } |
| |
| TEST(ProtocolTests, GoodValidOpenClosedProtocolComposition) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| closed protocol Closed {}; |
| ajar protocol Ajar {}; |
| open protocol Open {}; |
| |
| closed protocol ComposeInClosed { |
| compose Closed; |
| }; |
| |
| ajar protocol ComposeInAjar { |
| compose Closed; |
| compose Ajar; |
| }; |
| |
| open protocol ComposeInOpen { |
| compose Closed; |
| compose Ajar; |
| compose Open; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_COMPILED(library); |
| |
| auto compose_in_closed = library.LookupProtocol("ComposeInClosed"); |
| ASSERT_NOT_NULL(compose_in_closed); |
| EXPECT_EQ(compose_in_closed->composed_protocols.size(), 1); |
| |
| auto compose_in_ajar = library.LookupProtocol("ComposeInAjar"); |
| ASSERT_NOT_NULL(compose_in_ajar); |
| EXPECT_EQ(compose_in_ajar->composed_protocols.size(), 2); |
| |
| auto compose_in_open = library.LookupProtocol("ComposeInOpen"); |
| ASSERT_NOT_NULL(compose_in_open); |
| EXPECT_EQ(compose_in_open->composed_protocols.size(), 3); |
| } |
| |
| TEST(ProtocolTests, BadInvalidComposeOpenInClosed) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| open protocol Composed {}; |
| |
| closed protocol Composing { |
| compose Composed; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrComposedProtocolTooOpen); |
| } |
| |
| TEST(ProtocolTests, BadInvalidComposeAjarInClosed) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| ajar protocol Composed {}; |
| |
| closed protocol Composing { |
| compose Composed; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrComposedProtocolTooOpen); |
| } |
| |
| TEST(ProtocolTests, BadInvalidComposeOpenInAjar) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| open protocol Composed {}; |
| |
| ajar protocol Composing { |
| compose Composed; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrComposedProtocolTooOpen); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadModifierStrictOnComposeWithoutUnkownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol A {}; |
| |
| protocol B { |
| strict compose A; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadModifierFlexibleOnComposeWithoutUnkownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol A {}; |
| |
| protocol B { |
| flexible compose A; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadModifierStrictOnInvalidMemberWithoutUnkownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Example { |
| strict; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| // TODO(fxb/88366): remove checks for behavior with unknown interactions turned |
| // off when unknown interactions are always-on. |
| TEST(ProtocolTests, BadModifierFlexibleOnInvalidMemberWithoutUnkownInteractions) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Example { |
| flexible; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadModifierStrictOnCompose) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol A {}; |
| |
| protocol B { |
| strict compose A; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadModifierFlexibleOnCompose) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol A {}; |
| |
| protocol B { |
| flexible compose A; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadModifierStrictOnInvalidMember) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Example { |
| strict; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadModifierFlexibleOnInvalidMember) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Example { |
| flexible; |
| }; |
| |
| )FIDL"); |
| library.EnableFlag(fidl::ExperimentalFlags::Flag::kUnknownInteractions); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadColonNotSupported) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Parent {}; |
| protocol Child : Parent {}; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnexpectedTokenOfKind); |
| } |
| |
| TEST(ProtocolTests, BadDocCommentOutsideAttributelist) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol WellDocumented { |
| Method(); |
| /// Misplaced doc comment |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, GoodAttachAttributesToCompose) { |
| TestLibrary library(R"FIDL(library example; |
| |
| protocol ParentA { |
| ParentMethodA(); |
| }; |
| |
| protocol ParentB { |
| ParentMethodB(); |
| }; |
| |
| protocol Child { |
| @this_is_allowed |
| compose ParentA; |
| /// This is also allowed. |
| compose ParentB; |
| ChildMethod(); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| |
| auto child_protocol = library.LookupProtocol("Child"); |
| ASSERT_NOT_NULL(child_protocol); |
| EXPECT_EQ(child_protocol->methods.size(), 1); |
| EXPECT_EQ(child_protocol->all_methods.size(), 3); |
| ASSERT_EQ(child_protocol->composed_protocols.size(), 2); |
| EXPECT_EQ(child_protocol->composed_protocols.front().attributes->attributes.size(), 1); |
| EXPECT_EQ(child_protocol->composed_protocols.front().attributes->attributes.front()->name.data(), |
| "this_is_allowed"); |
| EXPECT_EQ(child_protocol->composed_protocols.back().attributes->attributes.size(), 1); |
| EXPECT_EQ(child_protocol->composed_protocols.back().attributes->attributes.front()->name.data(), |
| "doc"); |
| EXPECT_EQ(child_protocol->composed_protocols.back().attributes->attributes.front()->span.data(), |
| "/// This is also allowed."); |
| ASSERT_EQ(child_protocol->composed_protocols.back().attributes->attributes.front()->args.size(), |
| 1); |
| EXPECT_TRUE(child_protocol->composed_protocols.back() |
| .attributes->attributes.front() |
| ->args.front() |
| ->value->IsResolved()); |
| } |
| |
| TEST(ProtocolTests, BadCannotComposeYourself) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Narcisse { |
| compose Narcisse; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrIncludeCycle); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "protocol 'Narcisse' -> protocol 'Narcisse'"); |
| } |
| |
| TEST(ProtocolTests, BadCannotMutuallyCompose) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Yin { |
| compose Yang; |
| }; |
| |
| protocol Yang { |
| compose Yin; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrIncludeCycle); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), |
| "protocol 'Yang' -> protocol 'Yin' -> protocol 'Yang'"); |
| } |
| |
| TEST(ProtocolTests, BadCannotComposeSameProtocolTwice) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Parent { |
| Method(); |
| }; |
| |
| protocol Child { |
| compose Parent; |
| compose Parent; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrProtocolComposedMultipleTimes); |
| } |
| |
| TEST(ProtocolTests, BadCannotComposeMissingProtocol) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Child { |
| compose MissingParent; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrNameNotFound); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "MissingParent"); |
| } |
| |
| TEST(ProtocolTests, BadCannotComposeNonProtocol) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type S = struct {}; |
| protocol P { |
| compose S; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrComposingNonProtocol); |
| } |
| |
| TEST(ProtocolTests, BadCannotUseOrdinalsInProtocolDeclaration) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol NoMoreOrdinals { |
| 42: NiceTry(); |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrExpectedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadEmptyNamedItem) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol NoMoreOrdinals { |
| NotActuallyAMethod; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadNoOtherPragmaThanCompose) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol Wrong { |
| not_compose Something; |
| }; |
| |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnrecognizedProtocolMember); |
| } |
| |
| TEST(ProtocolTests, BadComposedProtocolsHaveClashingNames) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol A { |
| MethodA(); |
| }; |
| |
| protocol B { |
| compose A; |
| MethodB(); |
| }; |
| |
| protocol C { |
| compose A; |
| MethodC(); |
| }; |
| |
| protocol D { |
| compose B; |
| compose C; |
| MethodD(); |
| MethodA(); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrDuplicateMethodName); |
| } |
| |
| // See GetGeneratedOrdinal64ForTesting in test_library.h |
| // See GetGeneratedOrdinal64ForTesting in test_library.h |
| TEST(ProtocolTests, BadComposedProtocolsHaveClashingOrdinals) { |
| TestLibrary library(R"FIDL( |
| library methodhasher; |
| |
| protocol SpecialComposed { |
| ClashOne(); |
| }; |
| |
| protocol Special { |
| compose SpecialComposed; |
| ClashTwo(); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrDuplicateMethodOrdinal); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "ClashTwo_"); |
| } |
| |
| TEST(ProtocolTests, BadSimpleConstraintAppliesToComposedMethodsToo) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol NotSimple { |
| Complex(struct { arg vector<uint64>; }); |
| }; |
| |
| @for_deprecated_c_bindings |
| protocol YearningForSimplicity { |
| compose NotSimple; |
| Simple(); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrMemberMustBeSimple); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "arg"); |
| EXPECT_SUBSTR(library.errors()[0]->msg.c_str(), "for_deprecated_c_bindings"); |
| } |
| |
| TEST(ProtocolTests, BadRequestMustBeProtocol) { |
| // TODO(fxbug.dev/75112): currently need to specify second constraint to get |
| // the more specific error |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type S = struct {}; |
| protocol P { |
| Method(struct { r server_end:<S, optional>; }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrMustBeAProtocol); |
| } |
| |
| TEST(ProtocolTests, BadRequestMustBeParameterized) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol P { |
| Method(struct { r server_end; }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrProtocolConstraintRequired); |
| } |
| |
| TEST(ProtocolTests, BadRequestCannotHaveSize) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol P {}; |
| type S = struct { |
| p server_end:<P,0>; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnexpectedConstraint); |
| } |
| |
| TEST(ProtocolTests, BadDuplicateParameterName) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol P { |
| MethodWithDuplicateParams(struct {foo uint8; foo uint8; }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrDuplicateStructMemberName); |
| } |
| |
| TEST(ProtocolTests, BadParameterizedTypedChannel) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol {}; |
| |
| type Foo = resource struct { |
| foo client_end<MyProtocol>; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrWrongNumberOfLayoutParameters); |
| } |
| |
| TEST(ProtocolTests, BadTooManyConstraintsTypedChannel) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol {}; |
| |
| type Foo = resource struct { |
| foo client_end:<MyProtocol, optional, 1, 2>; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrTooManyConstraints); |
| } |
| |
| TEST(ProtocolTests, GoodTypedChannels) { |
| TestLibrary library(R"FIDL(library example; |
| |
| protocol MyProtocol {}; |
| |
| type Foo = resource struct { |
| a client_end:MyProtocol; |
| b client_end:<MyProtocol, optional>; |
| c server_end:MyProtocol; |
| d server_end:<MyProtocol, optional>; |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| |
| auto container = library.LookupStruct("Foo"); |
| ASSERT_NOT_NULL(container); |
| ASSERT_EQ(container->members.size(), 4); |
| |
| size_t i = 0; |
| |
| auto a_type_base = container->members[i++].type_ctor->type; |
| ASSERT_EQ(a_type_base->kind, fidl::flat::Type::Kind::kTransportSide); |
| const auto* a_type = static_cast<const fidl::flat::TransportSideType*>(a_type_base); |
| EXPECT_EQ(a_type->end, fidl::flat::TransportSide::kClient); |
| EXPECT_EQ(a_type->nullability, fidl::types::Nullability::kNonnullable); |
| |
| auto b_type_base = container->members[i++].type_ctor->type; |
| ASSERT_EQ(b_type_base->kind, fidl::flat::Type::Kind::kTransportSide); |
| const auto* b_type = static_cast<const fidl::flat::TransportSideType*>(b_type_base); |
| EXPECT_EQ(b_type->end, fidl::flat::TransportSide::kClient); |
| EXPECT_EQ(b_type->nullability, fidl::types::Nullability::kNullable); |
| |
| auto c_type_base = container->members[i++].type_ctor->type; |
| ASSERT_EQ(c_type_base->kind, fidl::flat::Type::Kind::kTransportSide); |
| const auto* c_type = static_cast<const fidl::flat::TransportSideType*>(c_type_base); |
| EXPECT_EQ(c_type->end, fidl::flat::TransportSide::kServer); |
| EXPECT_EQ(c_type->nullability, fidl::types::Nullability::kNonnullable); |
| |
| auto d_type_base = container->members[i++].type_ctor->type; |
| ASSERT_EQ(d_type_base->kind, fidl::flat::Type::Kind::kTransportSide); |
| const auto* d_type = static_cast<const fidl::flat::TransportSideType*>(d_type_base); |
| EXPECT_EQ(d_type->end, fidl::flat::TransportSide::kServer); |
| EXPECT_EQ(d_type->nullability, fidl::types::Nullability::kNullable); |
| } |
| |
| TEST(ProtocolTests, GoodPartialTypedChannelConstraints) { |
| TestLibrary library(R"FIDL(library example; |
| |
| protocol MyProtocol {}; |
| |
| alias ClientEnd = client_end:MyProtocol; |
| alias ServerEnd = server_end:MyProtocol; |
| |
| type Foo = resource struct { |
| a ClientEnd; |
| b ClientEnd:optional; |
| c ServerEnd; |
| d ServerEnd:optional; |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodStructSimpleLayout) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| @for_deprecated_c_bindings |
| protocol MyProtocol { |
| -> OnMyEvent(struct { |
| b bool; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, BadMethodStructSimpleLayout) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| @for_deprecated_c_bindings |
| protocol MyProtocol { |
| -> OnMyEvent(struct { |
| b vector<bool>; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrMemberMustBeSimple); |
| EXPECT_SUBSTR(library.errors()[0]->msg.c_str(), "for_deprecated_c_bindings"); |
| } |
| |
| TEST(ProtocolTests, BadMethodStructSizeConstraints) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyStruct = resource struct { |
| a client_end:<MyProtocol>; |
| }; |
| |
| @max_handles("0") @max_bytes("1") |
| protocol MyProtocol { |
| MyMethod(MyStruct) -> (MyStruct) error uint32; |
| -> OnMyEvent(struct { b uint16; }); |
| }; |
| )FIDL"); |
| ASSERT_FALSE(library.Compile()); |
| |
| // Both uses of "MyStruct" use too many handles. |
| EXPECT_ERR(library.errors()[0], fidl::ErrTooManyHandles); |
| EXPECT_ERR(library.errors()[1], fidl::ErrTooManyHandles); |
| |
| // Both uses of "MyStruct," as well as the anonymous layout, use too many bytes. |
| EXPECT_ERR(library.errors()[2], fidl::ErrTooManyBytes); |
| EXPECT_ERR(library.errors()[3], fidl::ErrTooManyBytes); |
| EXPECT_ERR(library.errors()[4], fidl::ErrTooManyBytes); |
| } |
| |
| TEST(ProtocolTests, BadMethodStructLayoutDefaultMember) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod(struct { |
| @allow_deprecated_struct_defaults |
| foo uint8 = 1; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrPayloadStructHasDefaultMembers); |
| } |
| |
| TEST(ProtocolTests, BadMethodEmptyPayloadStruct) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod(struct {}) -> (struct {}); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_TWICE_DURING_COMPILE(library, fidl::ErrEmptyPayloadStructs, |
| fidl::ErrEmptyPayloadStructs); |
| } |
| |
| TEST(ProtocolTests, BadMethodEnumLayout) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod(enum { |
| FOO = 1; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrInvalidParameterListKind); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "enum"); |
| } |
| |
| TEST(ProtocolTests, BadMethodEmptyResponseWithError) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod() -> () error uint32; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrResponsesWithErrorsMustNotBeEmpty); |
| } |
| |
| TEST(ProtocolTests, GoodMethodNamedTypeRequest) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type MyStruct = struct{ |
| a bool; |
| }; |
| |
| protocol MyProtocol { |
| MyMethodOneWay(MyStruct); |
| MyMethodTwoWay(MyStruct) -> (); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodNamedTypeResponse) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type MyStruct = struct{ |
| a bool; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod() -> (MyStruct); |
| -> OnMyEvent(MyStruct); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodNamedTypeResultPayload) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type MyStruct = struct{ |
| a bool; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod() -> (MyStruct) error uint32; |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodNamedAlias) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type MyStruct = struct { |
| a bool; |
| }; |
| |
| alias MyStructAlias = MyStruct; |
| alias MyAliasAlias = MyStructAlias; |
| |
| protocol MyProtocol { |
| MyMethod(MyStructAlias) -> (MyAliasAlias); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, BadMethodNamedEmptyPayloadStruct) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type MyStruct = struct{}; |
| |
| protocol MyProtocol { |
| MyMethod(MyStruct) -> (MyStruct); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_TWICE_DURING_COMPILE(library, fidl::ErrEmptyPayloadStructs, |
| fidl::ErrEmptyPayloadStructs); |
| } |
| |
| TEST(ProtocolTests, BadMethodNamedDefaultValueStruct) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type MyStruct = struct{ |
| @allow_deprecated_struct_defaults |
| a bool = false; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod(MyStruct) -> (MyStruct); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_TWICE_DURING_COMPILE(library, fidl::ErrPayloadStructHasDefaultMembers, |
| fidl::ErrPayloadStructHasDefaultMembers); |
| } |
| |
| TEST(ProtocolTests, BadMethodNamedInvalidHandle) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type obj_type = strict enum : uint32 { |
| NONE = 0; |
| VMO = 3; |
| }; |
| |
| type rights = strict bits : uint32 { |
| TRANSFER = 1; |
| }; |
| |
| resource_definition handle : uint32 { |
| properties { |
| subtype obj_type; |
| rights rights; |
| }; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod(handle); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrInvalidParameterListType); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "handle"); |
| } |
| |
| TEST(ProtocolTests, BadMethodNamedInvalidAlias) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| type obj_type = strict enum : uint32 { |
| NONE = 0; |
| VMO = 3; |
| }; |
| |
| type rights = strict bits : uint32 { |
| TRANSFER = 1; |
| }; |
| |
| resource_definition handle : uint32 { |
| properties { |
| subtype obj_type; |
| rights rights; |
| }; |
| }; |
| |
| alias MyPrimAlias = bool; |
| alias MyHandleAlias = handle; |
| alias MyVectorAlias = vector<MyPrimAlias>; |
| alias MyAliasAlias = MyVectorAlias:optional; |
| |
| protocol MyProtocol { |
| MyMethod(MyPrimAlias) -> (MyHandleAlias); |
| MyOtherMethod(MyVectorAlias) -> (MyAliasAlias); |
| }; |
| )FIDL"); |
| ASSERT_FALSE(library.Compile()); |
| |
| ASSERT_ERR(library.errors()[0], fidl::ErrInvalidParameterListType); |
| ASSERT_SUBSTR(library.errors()[0]->msg.c_str(), "bool"); |
| ASSERT_ERR(library.errors()[1], fidl::ErrInvalidParameterListType); |
| ASSERT_SUBSTR(library.errors()[1]->msg.c_str(), "example/handle"); |
| |
| ASSERT_ERR(library.errors()[2], fidl::ErrInvalidParameterListType); |
| ASSERT_SUBSTR(library.errors()[2]->msg.c_str(), "vector<bool>"); |
| ASSERT_ERR(library.errors()[3], fidl::ErrInvalidParameterListType); |
| // TODO(fxbug.dev/93999): Should be "vector<bool>:optional". |
| ASSERT_SUBSTR(library.errors()[3]->msg.c_str(), "vector<bool>?"); |
| } |
| |
| TEST(ProtocolTests, BadMethodNamedInvalidKind) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol { |
| MyOtherMethod(); |
| }; |
| |
| service MyService { |
| my_other_protocol client_end:MyOtherProtocol; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod(MyOtherProtocol) -> (MyService); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_TWICE_DURING_COMPILE(library, fidl::ErrExpectedType, fidl::ErrExpectedType); |
| } |
| |
| TEST(ProtocolTests, BadMethodTableSizeConstraints) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyTable = resource table { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| @max_handles("0") @max_bytes("1") |
| protocol MyProtocol { |
| MyMethod(MyTable) -> (MyTable) error uint32; |
| -> OnMyEvent(table { |
| 1: b bool; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_FALSE(library.Compile()); |
| |
| // Both uses of "MyTable" use too many handles. |
| EXPECT_ERR(library.errors()[0], fidl::ErrTooManyHandles); |
| EXPECT_ERR(library.errors()[1], fidl::ErrTooManyHandles); |
| |
| // Both uses of "MyTable," as well as the anonymous layout, use too many bytes. |
| EXPECT_ERR(library.errors()[2], fidl::ErrTooManyBytes); |
| EXPECT_ERR(library.errors()[3], fidl::ErrTooManyBytes); |
| EXPECT_ERR(library.errors()[4], fidl::ErrTooManyBytes); |
| } |
| |
| TEST(ProtocolTests, BadMethodTableSimpleLayout) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| @for_deprecated_c_bindings |
| protocol MyProtocol { |
| -> OnMyEvent(table { |
| 1: b bool; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrTableCannotBeSimple); |
| EXPECT_SUBSTR(library.errors()[0]->msg.c_str(), "for_deprecated_c_bindings"); |
| } |
| |
| TEST(ProtocolTests, GoodMethodTableRequest) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyTable = resource table { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| protocol MyProtocol { |
| MyMethodOneWay(table { |
| 1: b bool; |
| }); |
| MyMethodTwoWay(MyTable) -> (); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodTableResponse) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyTable = resource table { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod() -> (table { |
| 1: b bool; |
| }); |
| -> OnMyEvent(MyTable); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodTableResultPayload) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyTable = resource table { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod() -> (MyTable) error uint32; |
| MyAnonResponseMethod() -> (table { |
| 1: b bool; |
| }) error uint32; |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodUnionRequest) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyUnion = strict resource union { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| protocol MyProtocol { |
| MyMethodOneWay(flexible union { |
| 1: b bool; |
| }); |
| MyMethodTwoWay(MyUnion) -> (); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodUnionResponse) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyUnion = strict resource union { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod() -> (flexible union { |
| 1: b bool; |
| }); |
| -> OnMyEvent(MyUnion); |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, GoodMethodUnionResultPayload) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyUnion = strict resource union { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| protocol MyProtocol { |
| MyMethod() -> (MyUnion) error uint32; |
| MyAnonResponseMethod() -> (flexible union { |
| 1: b bool; |
| }) error uint32; |
| }; |
| )FIDL"); |
| ASSERT_COMPILED(library); |
| } |
| |
| TEST(ProtocolTests, BadMethodUnionSizeConstraints) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyOtherProtocol {}; |
| |
| type MyUnion = strict resource union { |
| 1: a client_end:<MyProtocol>; |
| }; |
| |
| @max_handles("0") @max_bytes("1") |
| protocol MyProtocol { |
| MyMethod(MyUnion) -> (MyUnion) error uint32; |
| -> OnMyEvent(flexible union { 1: b bool; }); |
| }; |
| )FIDL"); |
| ASSERT_FALSE(library.Compile()); |
| |
| // Both uses of "MyUnion" use too many handles. |
| EXPECT_ERR(library.errors()[0], fidl::ErrTooManyHandles); |
| EXPECT_ERR(library.errors()[1], fidl::ErrTooManyHandles); |
| |
| // Both uses of "MyUnion," as well as the anonymous layout, use too many bytes. |
| EXPECT_ERR(library.errors()[2], fidl::ErrTooManyBytes); |
| EXPECT_ERR(library.errors()[3], fidl::ErrTooManyBytes); |
| EXPECT_ERR(library.errors()[4], fidl::ErrTooManyBytes); |
| } |
| |
| TEST(ProtocolTests, BadMethodUnionSimpleLayout) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| @for_deprecated_c_bindings |
| protocol MyProtocol { |
| -> OnMyEvent(flexible union { |
| 1: b bool; |
| }); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrUnionCannotBeSimple); |
| EXPECT_SUBSTR(library.errors()[0]->msg.c_str(), "for_deprecated_c_bindings"); |
| } |
| |
| TEST(ProtocolTests, BadEventErrorSyntax) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| -> OnMyEvent(struct {}) error int32; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrEventErrorSyntaxDeprecated); |
| } |
| |
| TEST(ProtocolTests, BadDisallowedRequestType) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod(uint32); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrInvalidParameterListType); |
| } |
| |
| TEST(ProtocolTests, BadInvalidRequestType) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod(box); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrWrongNumberOfLayoutParameters); |
| } |
| |
| TEST(ProtocolTests, BadDisallowedResponseType) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod() -> (uint32); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrInvalidParameterListType); |
| } |
| |
| TEST(ProtocolTests, BadInvalidResponseType) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod() -> (box); |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrWrongNumberOfLayoutParameters); |
| } |
| |
| TEST(ProtocolTests, BadDisallowedSuccessType) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod() -> (uint32) error uint32; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrInvalidParameterListType); |
| } |
| |
| TEST(ProtocolTests, BadInvalidSuccessType) { |
| TestLibrary library(R"FIDL( |
| library example; |
| |
| protocol MyProtocol { |
| MyMethod() -> (box) error uint32; |
| }; |
| )FIDL"); |
| ASSERT_ERRORED_DURING_COMPILE(library, fidl::ErrWrongNumberOfLayoutParameters); |
| } |
| |
| // TODO(fxbug.dev/93542): add bad `:optional` message body tests here. |
| |
| } // namespace |