// 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) } } } }
// Parses each of the given .mojom files and all of the files in the // import graph rooted by each file. A single MojomDescriptor is created and // populated with the result of parsing all of those files. If the parsing is // successful then err will be nil. // // fileNames must not be nil or we will panic. func (d *ParseDriver) ParseFiles(fileNames []string) (descriptor *mojom.MojomDescriptor, err error) { if fileNames == nil { // We panic instead of returning an error here because this would be a programming error // as opposed to an error in the input. panic("fileNames may not be nil.") } filesToProcess := make([]*FileReference, len(fileNames)) descriptor = mojom.NewMojomDescriptor() for i, fileName := range fileNames { filesToProcess[i] = &FileReference{specifiedPath: fileName} } for len(filesToProcess) > 0 { currentFile := filesToProcess[0] filesToProcess = filesToProcess[1:] if err = d.fileProvider.findFile(currentFile); err != nil { return } var importedFrom *mojom.MojomFile = nil if currentFile.importedFrom != nil { importedFrom = currentFile.importedFrom.mojomFile // Tell the importing file about the absolute path of the imported file. // Note that we must do this even if the imported file has already been processed // because a given file may be imported by multiple files and each of those need // to be told about the absolute path of the imported file. importedFrom.SetCanonicalImportName(currentFile.specifiedPath, currentFile.absolutePath) } if !descriptor.ContainsFile(currentFile.absolutePath) { contents, fileReadError := d.fileProvider.provideContents(currentFile) if fileReadError != nil { err = fileReadError return } // topLevelFileName should be non-empty if and only if the current file is a top-level file. topLevelFileName := "" if importedFrom == nil { topLevelFileName = currentFile.specifiedPath } parser := MakeParser(currentFile.absolutePath, topLevelFileName, contents, descriptor, importedFrom) parser.SetDebugMode(d.debugMode) // Invoke parser.Parse() (but skip doing so in tests sometimes.) d.parseInvoker.invokeParse(&parser) if d.debugMode { fmt.Printf("\nParseTree for %s:", currentFile) fmt.Println(parser.GetParseTree()) } if !parser.OK() { err = parser.GetError() return } currentFile.mojomFile = d.fileExtractor.extractMojomFile(&parser) for _, importedFile := range currentFile.mojomFile.Imports { // Note that it is important that we append all of the imported files here even // if some of them have already been processed. That is because when the imported // file is pulled from the queue it will be pre-processed during which time the // absolute path to the file will be discovered and this absolute path will be // set in |mojomFile| which is necessary for serializing mojomFile. filesToProcess = append(filesToProcess, &FileReference{importedFrom: currentFile, specifiedPath: importedFile.SpecifiedName}) } } } // Perform type and value resolution if err = descriptor.Resolve(); err != nil { return } // Compute enum value integers. if err = descriptor.ComputeEnumValueIntegers(); err != nil { return } // Compute data for generators. err = descriptor.ComputeDataForGenerators() 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) } } } }