// TestSingleFileSerialization uses a series of test cases in which the text of a .mojom // file is specified and the expected MojomFileGraph is specified using Go struct literals. func TestSingleFileSerialization(t *testing.T) { test := singleFileTest{} //////////////////////////////////////////////////////////// // Test Case: array of int32 //////////////////////////////////////////////////////////// { contents := ` struct Foo{ array<int32> bar1; array<int32, 7> bar2; array<int32>? bar3; array<int32, 8>? bar4; };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"} // ResolvedTypes // struct Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: test.newDeclData("Foo", "Foo"), Fields: []mojom_types.StructField{ // field bar1 is not nullable and not fixed length { DeclData: test.newShortDeclData("bar1"), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ false, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar2 is not nullable and fixed length of 7 { DeclData: test.newShortDeclData("bar2"), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ false, 7, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar3 is nullable and not fixed length { DeclData: test.newShortDeclData("bar3"), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ true, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar4 is nullable and fixed length of 8 { DeclData: test.newShortDeclData("bar4"), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ true, 8, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: map string to int32 //////////////////////////////////////////////////////////// { contents := ` struct Foo{ map<string, int32> bar1; map<string?, int32> bar2; map<string, int32>? bar3; map<string?, int32>? bar4; };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"} // ResolvedTypes // struct Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: test.newDeclData("Foo", "Foo"), Fields: []mojom_types.StructField{ // field bar1 is non-nullable with a non-nullable key. { DeclData: test.newShortDeclData("bar1"), Type: &mojom_types.TypeMapType{mojom_types.MapType{ false, &mojom_types.TypeStringType{mojom_types.StringType{false}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar2 is non-nullable with a nullable key. { DeclData: test.newShortDeclData("bar2"), Type: &mojom_types.TypeMapType{mojom_types.MapType{ false, &mojom_types.TypeStringType{mojom_types.StringType{true}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar3 is nullable with a non-nullable key. { DeclData: test.newShortDeclData("bar3"), Type: &mojom_types.TypeMapType{mojom_types.MapType{ true, &mojom_types.TypeStringType{mojom_types.StringType{false}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar4 is nullable with a nullable key. { DeclData: test.newShortDeclData("bar4"), Type: &mojom_types.TypeMapType{mojom_types.MapType{ true, &mojom_types.TypeStringType{mojom_types.StringType{true}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: enum value initializer //////////////////////////////////////////////////////////// { contents := ` enum Foo{ X0, X1 = 42, X2 = X1 };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.TopLevelEnums = &[]string{"TYPE_KEY:Foo"} // Resolved Values // Foo.X0 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X0"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("X0", "Foo.X0"), EnumTypeKey: "TYPE_KEY:Foo", IntValue: 0, }} // Foo.X1 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("X1", "Foo.X1"), EnumTypeKey: "TYPE_KEY:Foo", InitializerValue: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt8Value{42}}, IntValue: 42, }} // Foo.X2 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("X2", "Foo.X2"), EnumTypeKey: "TYPE_KEY:Foo", InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{ Identifier: "X1", ValueKey: stringPointer("TYPE_KEY:Foo.X1"), }}, IntValue: 42, }} // ResolvedTypes // enum Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ DeclData: test.newDeclData("Foo", "Foo"), Values: []mojom_types.EnumValue{ // Note(rudominer) It is a bug that we need to copy the enum values here. // See https://github.com/domokit/mojo/issues/513. // value X1 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X0"].(*mojom_types.UserDefinedValueEnumValue).Value, // value X1 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"].(*mojom_types.UserDefinedValueEnumValue).Value, // value X2 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"].(*mojom_types.UserDefinedValueEnumValue).Value, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: enum value name is shadowed by local constant declaration. //////////////////////////////////////////////////////////// { contents := ` enum Color{ RED, BLUE }; struct MyStruct { const Color RED = BLUE; Color a_color = RED; // This should resolve to the local constant RED. };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.TopLevelEnums = &[]string{"TYPE_KEY:Color"} test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:MyStruct"} // Resolved Values // Color.RED test.expectedGraph().ResolvedValues["TYPE_KEY:Color.RED"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("RED", "Color.RED"), EnumTypeKey: "TYPE_KEY:Color", IntValue: 0, }} // Color.BLUE test.expectedGraph().ResolvedValues["TYPE_KEY:Color.BLUE"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("BLUE", "Color.BLUE"), EnumTypeKey: "TYPE_KEY:Color", IntValue: 1, }} // MyStruct.RED test.expectedGraph().ResolvedValues["TYPE_KEY:MyStruct.RED"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newContainedDeclData("RED", "MyStruct.RED", stringPointer("TYPE_KEY:MyStruct")), Type: &mojom_types.TypeTypeReference{mojom_types.TypeReference{ false, false, stringPointer("Color"), stringPointer("TYPE_KEY:Color")}}, Value: &mojom_types.ValueUserValueReference{ mojom_types.UserValueReference{ Identifier: "BLUE", ValueKey: stringPointer("TYPE_KEY:Color.BLUE")}}, }} // ResolvedTypes // enum Color test.expectedGraph().ResolvedTypes["TYPE_KEY:Color"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ DeclData: test.newDeclData("Color", "Color"), Values: []mojom_types.EnumValue{ // Note(rudominer) It is a bug that we need to copy the enum values here. // See https://github.com/domokit/mojo/issues/513. // value RED test.expectedGraph().ResolvedValues["TYPE_KEY:Color.RED"].(*mojom_types.UserDefinedValueEnumValue).Value, // value BLUE test.expectedGraph().ResolvedValues["TYPE_KEY:Color.BLUE"].(*mojom_types.UserDefinedValueEnumValue).Value, }, }} // struct MyStruct test.expectedGraph().ResolvedTypes["TYPE_KEY:MyStruct"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: &mojom_types.DeclarationData{ ShortName: stringPointer("MyStruct"), FullIdentifier: stringPointer("MyStruct"), DeclaredOrdinal: -1, DeclarationOrder: -1, SourceFileInfo: &mojom_types.SourceFileInfo{ FileName: test.fileName(), }, ContainedDeclarations: &mojom_types.ContainedDeclarations{ Constants: &[]string{"TYPE_KEY:MyStruct.RED"}}, }, Fields: []mojom_types.StructField{ // field a_color { DeclData: test.newShortDeclData("a_color"), Type: &mojom_types.TypeTypeReference{mojom_types.TypeReference{ false, false, stringPointer("Color"), stringPointer("TYPE_KEY:Color")}}, DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueUserValueReference{ mojom_types.UserValueReference{ Identifier: "RED", ValueKey: stringPointer("TYPE_KEY:MyStruct.RED")}}}, // Note this refers to MyStruct.RED and not Color.RED. }, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: In-Out method parameters with the same name. //////////////////////////////////////////////////////////// { contents := ` module test; interface EchoService { EchoString(string? value) => (string? value); DelayedEchoString(string? value, int32 millis) => (string? value); };` test.addTestCase("test", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Interfaces = &[]string{"TYPE_KEY:test.EchoService"} // ResolvedTypes // interface EchoService test.expectedGraph().ResolvedTypes["TYPE_KEY:test.EchoService"] = &mojom_types.UserDefinedTypeInterfaceType{mojom_types.MojomInterface{ DeclData: test.newDeclData("EchoService", "test.EchoService"), InterfaceName: "EchoService", Methods: map[uint32]mojom_types.MojomMethod{ 0: mojom_types.MojomMethod{ DeclData: test.newDeclData("EchoString", ""), Parameters: mojom_types.MojomStruct{ DeclData: test.newDeclData("EchoString-request", ""), Fields: []mojom_types.StructField{ mojom_types.StructField{ DeclData: test.newDeclData("value", ""), Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, }, }, }, ResponseParams: &mojom_types.MojomStruct{ DeclData: test.newDeclData("EchoString-response", ""), Fields: []mojom_types.StructField{ mojom_types.StructField{ DeclData: test.newDeclData("value", ""), Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, }, }, }, }, 1: mojom_types.MojomMethod{ DeclData: test.newDeclData("DelayedEchoString", ""), Parameters: mojom_types.MojomStruct{ DeclData: test.newDeclData("DelayedEchoString-request", ""), Fields: []mojom_types.StructField{ mojom_types.StructField{ DeclData: test.newDeclData("value", ""), Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, }, mojom_types.StructField{ DeclData: test.newDeclData("millis", ""), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}, }, }, }, ResponseParams: &mojom_types.MojomStruct{ DeclData: test.newDeclData("DelayedEchoString-response", ""), Fields: []mojom_types.StructField{ mojom_types.StructField{ DeclData: test.newDeclData("value", ""), Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, }, }, }, Ordinal: 1, }, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: Integer constants //////////////////////////////////////////////////////////// { contents := ` 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; ` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{ "TYPE_KEY:xu8", "TYPE_KEY:x8", "TYPE_KEY:xu16", "TYPE_KEY:x16", "TYPE_KEY:xu32", "TYPE_KEY:x32", "TYPE_KEY:xu64", "TYPE_KEY:x64"} // Resolved Values // xu8 test.expectedGraph().ResolvedValues["TYPE_KEY:xu8"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("xu8", "xu8"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT8}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint8Value{255}}, }} // x8 test.expectedGraph().ResolvedValues["TYPE_KEY:x8"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("x8", "x8"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT8}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt8Value{-127}}, }} // xu16 test.expectedGraph().ResolvedValues["TYPE_KEY:xu16"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("xu16", "xu16"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint16Value{0xFFFF}}, }} // x16 test.expectedGraph().ResolvedValues["TYPE_KEY:x16"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("x16", "x16"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT16}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt16Value{-0x7FFF}}, }} // xu32 test.expectedGraph().ResolvedValues["TYPE_KEY:xu32"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("xu32", "xu32"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint32Value{4294967295}}, }} // x32 test.expectedGraph().ResolvedValues["TYPE_KEY:x32"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("x32", "x32"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt32Value{-2147483647}}, }} // xu64 test.expectedGraph().ResolvedValues["TYPE_KEY:xu64"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("xu64", "xu64"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT64}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint64Value{0xFFFFFFFFFFFFFFFF}}, }} // x64 test.expectedGraph().ResolvedValues["TYPE_KEY:x64"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("x64", "x64"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT64}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{-0x7FFFFFFFFFFFFFFF}}, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: Builtin Floating-Point Constants //////////////////////////////////////////////////////////// { contents := ` const float f1 = float.INFINITY; const float f2 = float.NEGATIVE_INFINITY; const float f3 = float.NAN; const double d1 = double.INFINITY; const double d2 = double.NEGATIVE_INFINITY; const double d3 = double.NAN; ` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{ "TYPE_KEY:f1", "TYPE_KEY:f2", "TYPE_KEY:f3", "TYPE_KEY:d1", "TYPE_KEY:d2", "TYPE_KEY:d3"} // Resolved Values // f1 test.expectedGraph().ResolvedValues["TYPE_KEY:f1"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("f1", "f1"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_Float}, Value: &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_FloatInfinity}, }} // f2 test.expectedGraph().ResolvedValues["TYPE_KEY:f2"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("f2", "f2"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_Float}, Value: &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_FloatNegativeInfinity}, }} // f3 test.expectedGraph().ResolvedValues["TYPE_KEY:f3"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("f3", "f3"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_Float}, Value: &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_FloatNan}, }} // d1 test.expectedGraph().ResolvedValues["TYPE_KEY:d1"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("d1", "d1"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_Double}, Value: &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_DoubleInfinity}, }} // d2 test.expectedGraph().ResolvedValues["TYPE_KEY:d2"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("d2", "d2"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_Double}, Value: &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_DoubleNegativeInfinity}, }} // d3 test.expectedGraph().ResolvedValues["TYPE_KEY:d3"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("d3", "d3"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_Double}, Value: &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_DoubleNan}, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// { contents := ` [go_namespace="go.test", lucky=true, planet=EARTH] module mojom.test; import "another.file"; import "and.another.file"; const uint16 NUM_MAGI = 3; struct Foo{ int32 x; [min_version=2] string y = "hello"; string? z; enum Hats { TOP, COWBOY = NUM_MAGI, HARD, }; };` test.addTestCase("mojom.test", contents) // Attributes test.expectedFile().Attributes = &[]mojom_types.Attribute{ {"go_namespace", &mojom_types.LiteralValueStringValue{"go.test"}}, {"lucky", &mojom_types.LiteralValueBoolValue{true}}, {"planet", &mojom_types.LiteralValueStringValue{"EARTH"}}, } // Imports test.expectedFile().Imports = &[]string{ "another.file.canonical", "and.another.file.canonical", } // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:mojom.test.Foo"} test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{"TYPE_KEY:mojom.test.NUM_MAGI"} // Resolved Values // NUM_MAGI test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.NUM_MAGI"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("NUM_MAGI", "mojom.test.NUM_MAGI"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt8Value{3}}, }} // Hats.TOP test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("TOP", "mojom.test.Foo.Hats.TOP"), EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", IntValue: 0, }} // Hats.COWBOY test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("COWBOY", "mojom.test.Foo.Hats.COWBOY"), EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", IntValue: 3, InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{ Identifier: "NUM_MAGI", ValueKey: stringPointer("TYPE_KEY:mojom.test.NUM_MAGI"), }}, }} // Hats.HARD test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.HARD"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("HARD", "mojom.test.Foo.Hats.HARD"), EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", IntValue: 4, }} // ResolvedTypes // struct Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: &mojom_types.DeclarationData{ ShortName: stringPointer("Foo"), FullIdentifier: stringPointer("mojom.test.Foo"), DeclaredOrdinal: -1, DeclarationOrder: -1, SourceFileInfo: &mojom_types.SourceFileInfo{ FileName: test.fileName(), }, ContainedDeclarations: &mojom_types.ContainedDeclarations{ Enums: &[]string{"TYPE_KEY:mojom.test.Foo.Hats"}}, }, Fields: []mojom_types.StructField{ // field x { DeclData: test.newShortDeclData("x"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}, }, // field y { DeclData: test.newShortDeclDataA("y", &[]mojom_types.Attribute{{"min_version", &mojom_types.LiteralValueInt8Value{2}}}), Type: &mojom_types.TypeStringType{mojom_types.StringType{false}}, DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueLiteralValue{&mojom_types.LiteralValueStringValue{"hello"}}}, }, // field z { DeclData: test.newShortDeclData("z"), Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, }, }, }} // enum Hats test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo.Hats"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ DeclData: test.newContainedDeclData("Hats", "mojom.test.Foo.Hats", stringPointer("TYPE_KEY:mojom.test.Foo")), Values: []mojom_types.EnumValue{ // Note(rudominer) It is a bug that we need to copy the enum values here. // See https://github.com/domokit/mojo/issues/513. // value TOP test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"].(*mojom_types.UserDefinedValueEnumValue).Value, // value COWBOY test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"].(*mojom_types.UserDefinedValueEnumValue).Value, // value HARD test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.HARD"].(*mojom_types.UserDefinedValueEnumValue).Value, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Execute all of the test cases. //////////////////////////////////////////////////////////// for _, c := range test.cases { // Parse and resolve the mojom input. descriptor := mojom.NewMojomDescriptor() parser := 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()) continue } if err := descriptor.Resolve(); err != nil { t.Errorf("Resolve error for %s: %s", c.fileName, err.Error()) continue } if err := descriptor.ComputeEnumValueIntegers(); err != nil { t.Errorf("ComputeEnumValueIntegers error for %s: %s", c.fileName, err.Error()) continue } if err := descriptor.ComputeDataForGenerators(); err != nil { t.Errorf("ComputeDataForGenerators error for %s: %s", c.fileName, err.Error()) continue } // Simulate setting the canonical file name for the imported files. In real operation // this step is done in parser_driver.go when each of the imported files are parsed. mojomFile := parser.GetMojomFile() if mojomFile.Imports != nil { for _, imp := range mojomFile.Imports { imp.CanonicalFileName = fmt.Sprintf("%s.canonical", imp.SpecifiedName) } } // Serialize EmitLineAndColumnNumbers = c.lineAndcolumnNumbers bytes, _, err := Serialize(descriptor, false) if err != nil { t.Errorf("Serialization error for %s: %s", c.fileName, err.Error()) continue } // Deserialize decoder := bindings.NewDecoder(bytes, nil) fileGraph := mojom_files.MojomFileGraph{} fileGraph.Decode(decoder) // Compare if err := compareFileGraphs(c.expectedGraph, &fileGraph); err != nil { t.Errorf("%s:\n%s", c.fileName, err.Error()) continue } } }
// TestTwoFileSerialization uses a series of test cases in which the text of two .mojom // files is specified and the expected MojomFileGraph is specified using Go struct literals. func TestTwoFileSerialization(t *testing.T) { test := twoFileTest{} ///////////////////////////////////////////////////////////// // Test Case: Two top-level files with no import relationship ///////////////////////////////////////////////////////////// { contentsA := ` module a.b.c; struct FooA{ };` contentsB := ` module a.b.c; struct FooB{ };` importingName := "" // File A is not importing file B. topLevel := true // Simulate that File B is a top-level file. test.addTestCase("a.b.c", contentsA, contentsB, topLevel, importingName) // DeclaredMojomObjects test.expectedFileA().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooA"} test.expectedFileB().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooB"} // struct FooA test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooA"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: newDeclData(test.expectedFileA().FileName, "FooA", "a.b.c.FooA"), Fields: []mojom_types.StructField{}}} // struct FooB test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooB"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: newDeclData(test.expectedFileB().FileName, "FooB", "a.b.c.FooB"), Fields: []mojom_types.StructField{}}} test.endTestCase() } ///////////////////////////////////////////////////////////// // Test Case: Two top-level files where the first imports the second. ///////////////////////////////////////////////////////////// { contentsA := ` module a.b.c; import "myLittleFriend"; struct FooA{ };` contentsB := ` module a.b.c; struct FooB{ };` importingName := "myLittleFriend" // File A is importing File B using this name. topLevel := true // Simulate the File B is a top-level file. test.addTestCase("a.b.c", contentsA, contentsB, topLevel, importingName) // DeclaredMojomObjects test.expectedFileA().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooA"} test.expectedFileB().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooB"} // struct FooA test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooA"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: newDeclData(test.expectedFileA().FileName, "FooA", "a.b.c.FooA"), Fields: []mojom_types.StructField{}}} // struct FooB test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooB"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: newDeclData(test.expectedFileB().FileName, "FooB", "a.b.c.FooB"), Fields: []mojom_types.StructField{}}} test.endTestCase() } ///////////////////////////////////////////////////////////// // Test Case: A top-level file that imports a non-top-level file. ///////////////////////////////////////////////////////////// { contentsA := ` module a.b.c; import "myLittleFriend"; struct FooA{ };` contentsB := ` module a.b.c; struct FooB{ };` importingName := "myLittleFriend" // File A is importing File B using this name. topLevel := false // Simulate the File B is not a top-level file. test.addTestCase("a.b.c", contentsA, contentsB, topLevel, importingName) // DeclaredMojomObjects test.expectedFileA().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooA"} test.expectedFileB().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooB"} // struct FooA test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooA"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: newDeclData(test.expectedFileA().FileName, "FooA", "a.b.c.FooA"), Fields: []mojom_types.StructField{}}} // struct FooB test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooB"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: newDeclData(test.expectedFileB().FileName, "FooB", "a.b.c.FooB"), Fields: []mojom_types.StructField{}}} test.endTestCase() } //////////////////////////////////////////////////////////// // Execute all of the test cases. //////////////////////////////////////////////////////////// for i, c := range test.cases { descriptor := mojom.NewMojomDescriptor() // Parse file A. parserA := parser.MakeParser(c.expectedFileA.FileName, *c.expectedFileA.SpecifiedFileName, c.mojomContentsA, descriptor, nil) parserA.Parse() if !parserA.OK() { t.Errorf("Parsing error for %s: %s", c.expectedFileA.FileName, parserA.GetError().Error()) continue } mojomFileA := parserA.GetMojomFile() // Parse file B. var importedFrom *mojom.MojomFile if !c.topLevel { // If file B is not a top-level file then when the parser for it is constructed we give it a non-nil |importedFrom|. importedFrom = mojomFileA } parserB := parser.MakeParser(c.expectedFileB.FileName, *c.expectedFileB.SpecifiedFileName, c.mojomContentsB, descriptor, importedFrom) parserB.Parse() if !parserB.OK() { t.Errorf("Parsing error for %s: %s", c.expectedFileB.FileName, parserB.GetError().Error()) continue } mojomFileB := parserB.GetMojomFile() // Set the canonical file name for the imported files. In real operation // this step is done in parser_driver.go when each of the imported files are parsed. if c.importingName != "" { // The call to SetCanonicalImportName does a lookup in a map for a key corresponding to the // first argument. Thus here we are also testing that |importingName| is in fact string // that was parsed from the .mojom file. mojomFileA.SetCanonicalImportName(c.importingName, mojomFileB.CanonicalFileName) } // Resolve if err := descriptor.Resolve(); err != nil { t.Errorf("Resolve error for case %d: %s", i, err.Error()) continue } if err := descriptor.ComputeEnumValueIntegers(); err != nil { t.Errorf("ComputeEnumValueIntegers error for case %d: %s", i, err.Error()) continue } if err := descriptor.ComputeDataForGenerators(); err != nil { t.Errorf("ComputeDataForGenerators error for case %d: %s", i, err.Error()) continue } // Serialize EmitLineAndColumnNumbers = c.lineAndcolumnNumbers bytes, _, err := Serialize(descriptor, false) if err != nil { t.Errorf("Serialization error for case %d: %s", i, err.Error()) continue } // Deserialize decoder := bindings.NewDecoder(bytes, nil) fileGraph := mojom_files.MojomFileGraph{} fileGraph.Decode(decoder) // Compare if err := compareFileGraphs(c.expectedGraph, &fileGraph); err != nil { t.Errorf("case %d:\n%s", i, err.Error()) continue } } }
// TestSingleFileSerialization uses a series of test cases in which the text of a .mojom // file is specified and the expected MojomFileGraph is specified using Go struct literals. func TestSingleFileSerialization(t *testing.T) { test := singleFileTest{} //////////////////////////////////////////////////////////// // Test Case: array of int32 //////////////////////////////////////////////////////////// { contents := ` struct Foo{ array<int32> bar1; array<int32, 7> bar2; array<int32>? bar3; array<int32, 8>? bar4; };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"} // ResolvedTypes // struct Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: test.newDeclData("Foo", "Foo"), Fields: []mojom_types.StructField{ // field bar1 is not nullable and not fixed length { DeclData: test.newDeclData("bar1", ""), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ false, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar2 is not nullable and fixed length of 7 { DeclData: test.newDeclData("bar2", ""), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ false, 7, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar3 is nullable and not fixed length { DeclData: test.newDeclData("bar3", ""), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ true, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar4 is nullable and fixed length of 8 { DeclData: test.newDeclData("bar4", ""), Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{ true, 8, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: map string to int32 //////////////////////////////////////////////////////////// { contents := ` struct Foo{ map<string, int32> bar1; map<string?, int32> bar2; map<string, int32>? bar3; map<string?, int32>? bar4; };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"} // ResolvedTypes // struct Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: test.newDeclData("Foo", "Foo"), Fields: []mojom_types.StructField{ // field bar1 is non-nullable with a non-nullable key. { DeclData: test.newDeclData("bar1", ""), Type: &mojom_types.TypeMapType{mojom_types.MapType{ false, &mojom_types.TypeStringType{mojom_types.StringType{false}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar2 is non-nullable with a nullable key. { DeclData: test.newDeclData("bar2", ""), Type: &mojom_types.TypeMapType{mojom_types.MapType{ false, &mojom_types.TypeStringType{mojom_types.StringType{true}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar3 is nullable with a non-nullable key. { DeclData: test.newDeclData("bar3", ""), Type: &mojom_types.TypeMapType{mojom_types.MapType{ true, &mojom_types.TypeStringType{mojom_types.StringType{false}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, // field bar4 is nullable with a nullable key. { DeclData: test.newDeclData("bar4", ""), Type: &mojom_types.TypeMapType{mojom_types.MapType{ true, &mojom_types.TypeStringType{mojom_types.StringType{true}}, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}}, }, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case: enum value initializer //////////////////////////////////////////////////////////// { contents := ` enum Foo{ X1 = 42, X2 = X1 };` test.addTestCase("", contents) // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.TopLevelEnums = &[]string{"TYPE_KEY:Foo"} // Resolved Values // Foo.X1 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("X1", "Foo.X1"), EnumTypeKey: "TYPE_KEY:Foo", InitializerValue: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{42}}, IntValue: -1, }} // Foo.X2 test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("X2", "Foo.X2"), EnumTypeKey: "TYPE_KEY:Foo", InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{ Identifier: "X1", ValueKey: stringPointer("TYPE_KEY:Foo.X1"), }}, IntValue: -1, }} // ResolvedTypes // enum Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ DeclData: test.newDeclData("Foo", "Foo"), Values: []mojom_types.EnumValue{ // Note(rudominer) It is a bug that we need to copy the enum values here. // See https://github.com/domokit/mojo/issues/513. // value TOP test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"].(*mojom_types.UserDefinedValueEnumValue).Value, // value COWBOY test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"].(*mojom_types.UserDefinedValueEnumValue).Value, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Test Case //////////////////////////////////////////////////////////// { contents := ` [go_namespace="go.test", lucky=true, planet=EARTH] module mojom.test; import "another.file"; import "and.another.file"; const uint16 NUM_MAGI = 3; struct Foo{ int32 x; string y = "hello"; string? z; enum Hats { TOP, COWBOY = NUM_MAGI }; };` test.addTestCase("mojom.test", contents) // Attributes test.expectedFile().Attributes = &[]mojom_types.Attribute{ {"go_namespace", "go.test"}, {"lucky", "true"}, {"planet", "EARTH"}, } // Imports test.expectedFile().Imports = &[]string{ "another.file.canonical", "and.another.file.canonical", } // DeclaredMojomObjects test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:mojom.test.Foo"} test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{"TYPE_KEY:mojom.test.NUM_MAGI"} // Resolved Values // NUM_MAGI test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.NUM_MAGI"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ DeclData: *test.newDeclData("NUM_MAGI", "mojom.test.NUM_MAGI"), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16}, Value: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{3}}, }} // Hats.TOP test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("TOP", "mojom.test.Foo.Hats.TOP"), EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", IntValue: -1, }} // Hats.COWBOY test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ DeclData: test.newDeclData("COWBOY", "mojom.test.Foo.Hats.COWBOY"), EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", IntValue: -1, InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{ Identifier: "NUM_MAGI", ValueKey: stringPointer("TYPE_KEY:mojom.test.NUM_MAGI"), }}, }} // ResolvedTypes // struct Foo test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ DeclData: &mojom_types.DeclarationData{ ShortName: stringPointer("Foo"), FullIdentifier: stringPointer("mojom.test.Foo"), DeclaredOrdinal: -1, DeclarationOrder: -1, SourceFileInfo: &mojom_types.SourceFileInfo{ FileName: test.fileName(), }, ContainedDeclarations: &mojom_types.ContainedDeclarations{ Enums: &[]string{"TYPE_KEY:mojom.test.Foo.Hats"}}, }, Fields: []mojom_types.StructField{ // field x { DeclData: test.newDeclData("x", ""), Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}, }, // field y { DeclData: test.newDeclData("y", ""), Type: &mojom_types.TypeStringType{mojom_types.StringType{false}}, DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueLiteralValue{&mojom_types.LiteralValueStringValue{"hello"}}}, }, // field z { DeclData: test.newDeclData("z", ""), Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, }, }, }} // enum Hats test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo.Hats"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ DeclData: test.newDeclData("Hats", "mojom.test.Foo.Hats"), Values: []mojom_types.EnumValue{ // Note(rudominer) It is a bug that we need to copy the enum values here. // See https://github.com/domokit/mojo/issues/513. // value TOP test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"].(*mojom_types.UserDefinedValueEnumValue).Value, // value COWBOY test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"].(*mojom_types.UserDefinedValueEnumValue).Value, }, }} test.endTestCase() } //////////////////////////////////////////////////////////// // Execute all of the test cases. //////////////////////////////////////////////////////////// for _, c := range test.cases { // Parse and resolve the mojom input. descriptor := mojom.NewMojomDescriptor() parser := parser.MakeParser(c.fileName, c.mojomContents, descriptor) parser.Parse() if !parser.OK() { t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error()) continue } if err := descriptor.Resolve(); err != nil { t.Errorf("Resolve error for %s: %s", c.fileName, err.Error()) continue } if err := descriptor.ComputeEnumValueIntegers(); err != nil { t.Errorf("ComputeEnumValueIntegers error for %s: %s", c.fileName, err.Error()) continue } if err := descriptor.ComputeDataForGenerators(); err != nil { t.Errorf("ComputeDataForGenerators error for %s: %s", c.fileName, err.Error()) continue } // Simulate setting the canonical file name for the imported files. In real operation // this step is done in parser_driver.go when each of the imported files are parsed. mojomFile := parser.GetMojomFile() if mojomFile.Imports != nil { for _, imp := range mojomFile.Imports { imp.CanonicalFileName = fmt.Sprintf("%s.canonical", imp.SpecifiedName) } } // Serialize EmitLineAndColumnNumbers = c.lineAndcolumnNumbers bytes, err := Serialize(descriptor) if err != nil { t.Errorf("Serialization error for %s: %s", c.fileName, err.Error()) continue } // Deserialize decoder := bindings.NewDecoder(bytes, nil) fileGraph := mojom_files.MojomFileGraph{} fileGraph.Decode(decoder) // Compare if err := compareFileGraphs(c.expectedGraph, &fileGraph); err != nil { t.Errorf("%s:\n%s", c.fileName, err.Error()) continue } } }