// ATTRIBUTES -> lbracket ATTR_ASSIGNMENT { comma, ATTR_ASSIGNMENT} // ATTR_ASSIGNMENT -> name equals name | name equals literal func (p *Parser) parseAttributes() (attributes *mojom.Attributes) { if !p.OK() { return } if !p.tryMatch(lexer.LBracket) { // There is no attributes section here return } p.pushChildNode("attributes") defer p.popNode() attributes = mojom.NewAttributes() nextToken := p.lastConsumed for nextToken.Kind != lexer.RBracket { key := p.readName() p.attachToken() if !p.OK() { return } if !p.match(lexer.Equals) { return } var value mojom.ConcreteValue if p.peekNextToken("Expecting to find an attribute value.").Kind == lexer.Name { text := p.readName() // TODO(rudominer) Do not throw away the distinction between a name and a string literal. value = mojom.MakeStringLiteralValue(text) } else { value = p.parseLiteral() } p.attachToken() if !p.OK() { return } attributes.List = append(attributes.List, mojom.MojomAttribute{key, value}) nextToken = p.peekNextToken("I was reading an attributes section.") if !p.OK() { return } p.consumeNextToken() if nextToken.Kind != lexer.RBracket && nextToken.Kind != lexer.Comma { var message string switch nextToken.Kind { case lexer.Module, lexer.Interface, lexer.Struct, lexer.Union, lexer.Enum: message = fmt.Sprintf("The attribute section is missing a closing ] before %v at %s.", nextToken, nextToken.LongLocationString()) default: message = p.unexpectedTokenError(nextToken, "comma or ]") } p.err = &ParseError{ParserErrorCodeUnexpectedToken, message} return } } return }
// TestSuccessfulParsing contains a series of test cases in which we // run the parser on a valid mojom input string and compare the resulting // MojomFile to an expected one. func TestSuccessfulParsing(t *testing.T) { type testCase struct { fileName string mojomContents string expectedFile *mojom.MojomFile } cases := make([]testCase, 0) testCaseNum := 0 var expectedFile *mojom.MojomFile startTestCase := func(moduleNameSpace string) { descriptor := mojom.NewMojomDescriptor() fileName := fmt.Sprintf("file%d", testCaseNum) expectedFile = descriptor.AddMojomFile(fileName) expectedFile.InitializeFileScope(moduleNameSpace) cases = append(cases, testCase{fileName, "", expectedFile}) } endTestCase := func() { testCaseNum += 1 } // Note(rudominer) The structure of this method is designed to allow // test cases to be rearranged and new test cases to be inserted at // arbitrary locations. Do not hard-code anything that refers to the // position of a test case in the list. //////////////////////////////////////////////////////////// // Test Case (empty file) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = "" endTestCase() //////////////////////////////////////////////////////////// // Test Case (module statement only) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = `module mojom.test;` endTestCase() //////////////////////////////////////////////////////////// // Test Case (module statement with attributes) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = `[cool=true]module mojom.test;` expectedFile.Attributes = mojom.NewAttributes() expectedFile.Attributes.List = append(expectedFile.Attributes.List, mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)}) endTestCase() //////////////////////////////////////////////////////////// // Test Case (import statements only) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = `import "a.file";` expectedFile.AddImport("a.file") endTestCase() //////////////////////////////////////////////////////////// // Test Case (module and import statements only) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "a.file";` { expectedFile.AddImport("a.file") endTestCase() } //////////////////////////////////////////////////////////// // Test Case (module with attributes and import statements only) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` [cool=true] module mojom.test; import "a.file";` { expectedFile.Attributes = mojom.NewAttributes() expectedFile.Attributes.List = append(expectedFile.Attributes.List, mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)}) expectedFile.AddImport("a.file") endTestCase() } //////////////////////////////////////////////////////////// // Test Case (one empty sruct) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = `struct Foo{};` expectedFile.AddStruct(mojom.NewMojomStruct(mojom.DeclTestData("Foo"))) endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; struct Foo{ int32 x; };` { structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil)) expectedFile.AddStruct(structFoo) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "another.file"; import "and.another.file"; struct Foo{ [happy=true] int32 x@4; };` { expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) attributes := mojom.NewAttributes() attributes.List = append(attributes.List, mojom.MojomAttribute{"happy", mojom.MakeBoolLiteralValue(true)}) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataAWithOrdinal("x", attributes, 4), mojom.SimpleTypeInt32, nil)) expectedFile.AddStruct(structFoo) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "another.file"; import "and.another.file"; struct Foo{ int32 x@4 = 42; [age=7, level="high"] string y = "Howdy!"; string? z; };` { expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("x", 4), mojom.SimpleTypeInt32, mojom.MakeInt64LiteralValue(42))) attributes := mojom.NewAttributes() attributes.List = append(attributes.List, mojom.MojomAttribute{"age", mojom.MakeInt64LiteralValue(7)}) attributes.List = append(attributes.List, mojom.MojomAttribute{"level", mojom.MakeStringLiteralValue("high")}) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataA("y", attributes), mojom.BuiltInType("string"), mojom.MakeStringLiteralValue("Howdy!"))) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil)) expectedFile.AddStruct(structFoo) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "another.file"; import "and.another.file"; struct Foo{ int32 x; string y; string? z; }; interface Doer { DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo); }; ` { expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil)) expectedFile.AddStruct(structFoo) interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer")) // The first reference to Foo inside of interface Doer fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{}) // The second reference to Foo inside of interface Doer. nullable=true fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{}) params := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil)) params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil)) responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil)) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil)) interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams)) expectedFile.AddInterface(interfaceDoer) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` [php_namespace="mojom.test.php"] module mojom.test; import "another.file"; import "and.another.file"; const int8 TOO_SMALL_VALUE = 6; enum ErrorCodes { TOO_BIG = 5, TOO_SMALL = TOO_SMALL_VALUE, JUST_RIGHT, }; struct Foo{ int32 x; string y; string? z; }; interface Doer { DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo); }; ` { expectedFile.Attributes = mojom.NewAttributes() expectedFile.Attributes.List = append(expectedFile.Attributes.List, mojom.MojomAttribute{"php_namespace", mojom.MakeStringLiteralValue("mojom.test.php")}) expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("TOO_SMALL_VALUE"), mojom.SimpleTypeInt8, mojom.MakeInt64LiteralValue(6))) errorCodeEnum := mojom.NewMojomEnum(mojom.DeclTestData("ErrorCodes")) errorCodeEnum.InitAsScope(expectedFile.FileScope) // The reference to TOO_SMALL_VALUE from within the ErrorCodes enum. assigneeType := mojom.NewResolvedUserTypeRef("ErrorCodes", errorCodeEnum) tooSmallValueRef := mojom.NewUserValueRef(assigneeType, "TOO_SMALL_VALUE", expectedFile.FileScope, lexer.Token{}) errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_BIG"), mojom.MakeInt64LiteralValue(5)) errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_SMALL"), tooSmallValueRef) errorCodeEnum.AddEnumValue(mojom.DeclTestData("JUST_RIGHT"), nil) expectedFile.AddEnum(errorCodeEnum) structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil)) expectedFile.AddStruct(structFoo) interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer")) // The first reference to Foo inside of interface Doer fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{}) // The second reference to Foo inside of interface Doer. nullable=true fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{}) params := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil)) params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil)) responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil)) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil)) interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams)) expectedFile.AddInterface(interfaceDoer) } endTestCase() //////////////////////////////////////////////////////////// // Execute all of the test cases. //////////////////////////////////////////////////////////// for _, c := range cases { descriptor := mojom.NewMojomDescriptor() parser := MakeParser(c.fileName, c.mojomContents, descriptor) parser.Parse() if !parser.OK() { t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error()) } else { got := parser.GetMojomFile().String() expected := c.expectedFile.String() if got != expected { t.Errorf("%s:\n*****expected:\n%s\n****actual\n%s", c.fileName, expected, got) } } } }
// ATTRIBUTES -> lbracket ATTR_ASSIGNMENT { comma, ATTR_ASSIGNMENT} // ATTR_ASSIGNMENT -> name equals name | name equals literal func (p *Parser) parseAttributes() (attributes *mojom.Attributes) { if !p.OK() { return } if !p.tryMatch(lexer.LBRACKET) { // There is no attributes section here return } p.pushChildNode("attributes") defer p.popNode() attributes = mojom.NewAttributes() nextToken := p.lastConsumed for nextToken.Kind != lexer.RBRACKET { key := p.readName() p.attachToken() if !p.OK() { return } if !p.match(lexer.EQUALS) { return } var value mojom.ConcreteValue if p.peekNextToken("Expecting to find an attribute value.").Kind == lexer.NAME { text := p.readName() value = mojom.MakeStringLiteralValue(text) } else { value = p.parseLiteral() } p.attachToken() if !p.OK() { return } attributes.List = append(attributes.List, mojom.MojomAttribute{key, value}) nextToken = p.peekNextToken("I was reading an attributes section.") if !p.OK() { return } p.consumeNextToken() if nextToken.Kind != lexer.RBRACKET && nextToken.Kind != lexer.COMMA { var message string switch nextToken.Kind { case lexer.MODULE, lexer.INTERFACE, lexer.STRUCT, lexer.UNION, lexer.ENUM: message = fmt.Sprintf("The attribute section is missing a closing ] before %v at %s.", nextToken, nextToken.LongLocationString()) default: message = p.unexpectedTokenError(nextToken, "comma or ]") } p.err = &ParseError{E_UNEXPECTED_TOKEN, message} return } } return }
// TestSuccessfulParsing contains a series of test cases in which we // run the parser on a valid mojom input string and compare the resulting // MojomFile to an expected one. func TestSuccessfulParsing(t *testing.T) { type testCase struct { fileName string mojomContents string expectedFile *mojom.MojomFile } cases := make([]testCase, 0) testCaseNum := 0 var expectedFile *mojom.MojomFile startTestCase := func(moduleNameSpace string) { descriptor := mojom.NewMojomDescriptor() fileName := fmt.Sprintf("file%d", testCaseNum) expectedFile = descriptor.AddMojomFile(fileName, fileName, nil, "") expectedFile.InitializeFileScope(moduleNameSpace) cases = append(cases, testCase{fileName, "", expectedFile}) } endTestCase := func() { testCaseNum += 1 } // Note(rudominer) The structure of this method is designed to allow // test cases to be rearranged and new test cases to be inserted at // arbitrary locations. Do not hard-code anything that refers to the // position of a test case in the list. //////////////////////////////////////////////////////////// // Test Case (empty file) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = "" endTestCase() //////////////////////////////////////////////////////////// // Test Case (module statement only) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = `module mojom.test;` endTestCase() //////////////////////////////////////////////////////////// // Test Case (module statement with attributes) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = `[cool=true]module mojom.test;` expectedFile.Attributes = mojom.NewAttributes() expectedFile.Attributes.List = append(expectedFile.Attributes.List, mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)}) endTestCase() //////////////////////////////////////////////////////////// // Test Case (import statements only) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = `import "a.file";` expectedFile.AddImport("a.file") endTestCase() //////////////////////////////////////////////////////////// // Test Case (module and import statements only) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "a.file";` { expectedFile.AddImport("a.file") endTestCase() } //////////////////////////////////////////////////////////// // Test Case (module with attributes and import statements only) //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` [cool=true] module mojom.test; import "a.file";` { expectedFile.Attributes = mojom.NewAttributes() expectedFile.Attributes.List = append(expectedFile.Attributes.List, mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)}) expectedFile.AddImport("a.file") endTestCase() } //////////////////////////////////////////////////////////// // Test Case (one empty sruct) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = `struct Foo{};` expectedFile.AddStruct(mojom.NewMojomStruct(mojom.DeclTestData("Foo"))) endTestCase() //////////////////////////////////////////////////////////// // Test Case (Integer constants) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = ` const uint8 xu8 = 255; const int8 x8 = -127; const uint16 xu16 = 0xFFFF; const int16 x16 = -0x7FFF; const uint32 xu32 = 4294967295; const int32 x32 = -2147483647; const uint64 xu64 = 0xFFFFFFFFFFFFFFFF; const int64 x64 = -0x7FFFFFFFFFFFFFFF; const uint64 manyNines = 9999999999999999999; ` expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu8"), mojom.SimpleTypeUInt8, mojom.MakeUint8LiteralValue(0xFF))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x8"), mojom.SimpleTypeInt8, mojom.MakeInt8LiteralValue(-0x7F))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu16"), mojom.SimpleTypeUInt16, mojom.MakeUint16LiteralValue(65535))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x16"), mojom.SimpleTypeInt16, mojom.MakeInt16LiteralValue(-32767))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu32"), mojom.SimpleTypeUInt32, mojom.MakeUint32LiteralValue(0xFFFFFFFF))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x32"), mojom.SimpleTypeInt32, mojom.MakeInt32LiteralValue(-0x7FFFFFFF))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu64"), mojom.SimpleTypeUInt64, mojom.MakeUint64LiteralValue(18446744073709551615))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x64"), mojom.SimpleTypeInt64, mojom.MakeInt64LiteralValue(-9223372036854775807))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("manyNines"), mojom.SimpleTypeUInt64, mojom.MakeUint64LiteralValue(9999999999999999999))) endTestCase() //////////////////////////////////////////////////////////// // Test Case (float and double constants) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = ` const float x = 123.456E7; const float y = 123456789.123456789; const float z = -0.01; const double w = -0.01; const double r = 3.14159E40; ` { expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x"), mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(1234560000))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("y"), mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(123456789.123456789))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("z"), mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(-0.01))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("w"), mojom.SimpleTypeDouble, mojom.MakeDoubleLiteralValue(-0.01))) expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("r"), mojom.SimpleTypeDouble, mojom.MakeDoubleLiteralValue(3.14159e+40))) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; struct Foo{ int32 x; };` { structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.InitAsScope(mojom.NewTestFileScope("test.scope")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil)) expectedFile.AddStruct(structFoo) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "another.file"; import "and.another.file"; struct Foo{ [happy=true] int32 x@4; };` { expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.InitAsScope(mojom.NewTestFileScope("test.scope")) attributes := mojom.NewAttributes() attributes.List = append(attributes.List, mojom.MojomAttribute{"happy", mojom.MakeBoolLiteralValue(true)}) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataAWithOrdinal("x", attributes, 4), mojom.SimpleTypeInt32, nil)) expectedFile.AddStruct(structFoo) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "another.file"; import "and.another.file"; struct Foo{ int32 x@4 = 42; [age=7, level="high"] string y = "Howdy!"; string? z; bool w@6 = false; };` { expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.InitAsScope(mojom.NewTestFileScope("test.scope")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("x", 4), mojom.SimpleTypeInt32, mojom.MakeInt8LiteralValue(42))) attributes := mojom.NewAttributes() attributes.List = append(attributes.List, mojom.MojomAttribute{"age", mojom.MakeInt8LiteralValue(7)}) attributes.List = append(attributes.List, mojom.MojomAttribute{"level", mojom.MakeStringLiteralValue("high")}) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataA("y", attributes), mojom.BuiltInType("string"), mojom.MakeStringLiteralValue("Howdy!"))) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("w", 6), mojom.BuiltInType("bool"), mojom.MakeBoolLiteralValue(false))) expectedFile.AddStruct(structFoo) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` module mojom.test; import "another.file"; import "and.another.file"; struct Foo{ int32 x; string y; string? z; }; interface Doer { DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo); }; ` { expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.InitAsScope(mojom.NewTestFileScope("test.scope")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil)) expectedFile.AddStruct(structFoo) interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer")) interfaceDoer.InitAsScope(mojom.NewTestFileScope("test.scope")) // The first reference to Foo inside of interface Doer fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{}) // The second reference to Foo inside of interface Doer. nullable=true fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{}) params := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) params.InitAsScope(mojom.NewTestFileScope("test.scope")) params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil)) params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil)) responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) responseParams.InitAsScope(mojom.NewTestFileScope("test.scope")) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil)) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil)) interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams)) expectedFile.AddInterface(interfaceDoer) } endTestCase() //////////////////////////////////////////////////////////// // Test Case (Annotation right after imports) //////////////////////////////////////////////////////////// startTestCase("") cases[testCaseNum].mojomContents = ` import "gpu/interfaces/command_buffer.mojom"; [ServiceName="mojo::Gpu"] interface Gpu { }; ` { expectedFile.AddImport("gpu/interfaces/command_buffer.mojom") attributes := mojom.NewAttributes() attributes.List = append(attributes.List, mojom.MojomAttribute{"ServiceName", mojom.MakeStringLiteralValue("mojo::Gpu")}) interfaceGpu := mojom.NewMojomInterface(mojom.DeclTestDataA("Gpu", attributes)) expectedFile.AddInterface(interfaceGpu) } endTestCase() //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// startTestCase("mojom.test") cases[testCaseNum].mojomContents = ` [php_namespace="mojom.test.php"] module mojom.test; import "another.file"; import "and.another.file"; const int8 TOO_SMALL_VALUE = 6; enum ErrorCodes { TOO_BIG = 5, TOO_SMALL = TOO_SMALL_VALUE, JUST_RIGHT, }; struct Foo{ int32 x; string y; string? z; }; interface Doer { DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo); }; ` { expectedFile.Attributes = mojom.NewAttributes() expectedFile.Attributes.List = append(expectedFile.Attributes.List, mojom.MojomAttribute{"php_namespace", mojom.MakeStringLiteralValue("mojom.test.php")}) expectedFile.AddImport("another.file") expectedFile.AddImport("and.another.file") expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("TOO_SMALL_VALUE"), mojom.SimpleTypeInt8, mojom.MakeInt8LiteralValue(6))) errorCodeEnum := mojom.NewMojomEnum(mojom.DeclTestData("ErrorCodes")) errorCodeEnum.InitAsScope(expectedFile.FileScope) // The reference to TOO_SMALL_VALUE from within the ErrorCodes enum. assigneeType := mojom.NewResolvedUserTypeRef("ErrorCodes", errorCodeEnum) tooSmallValueRef := mojom.NewUserValueRef(mojom.AssigneeSpec{"assignee", assigneeType}, "TOO_SMALL_VALUE", expectedFile.FileScope, lexer.Token{}) errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_BIG"), mojom.MakeInt8LiteralValue(5)) errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_SMALL"), tooSmallValueRef) errorCodeEnum.AddEnumValue(mojom.DeclTestData("JUST_RIGHT"), nil) expectedFile.AddEnum(errorCodeEnum) structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo")) structFoo.InitAsScope(mojom.NewTestFileScope("test.scope")) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil)) structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil)) expectedFile.AddStruct(structFoo) interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer")) interfaceDoer.InitAsScope(mojom.NewTestFileScope("test.scope")) // The first reference to Foo inside of interface Doer fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{}) // The second reference to Foo inside of interface Doer. nullable=true fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{}) params := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) params.InitAsScope(mojom.NewTestFileScope("test.scope")) params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil)) params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil)) responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy")) responseParams.InitAsScope(mojom.NewTestFileScope("test.scope")) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil)) responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil)) interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams)) expectedFile.AddInterface(interfaceDoer) } endTestCase() //////////////////////////////////////////////////////////// // Execute all of the test cases. //////////////////////////////////////////////////////////// for _, c := range cases { descriptor := mojom.NewMojomDescriptor() parser := MakeParser(c.fileName, c.fileName, c.mojomContents, descriptor, nil) parser.Parse() if !parser.OK() { t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error()) } else { got := parser.GetMojomFile().String() expected := c.expectedFile.String() if got != expected { t.Errorf("%s:\n*****expected:\n%s\n****actual\n%s", c.fileName, expected, got) } } } }