Index: mojom/mojom_parser/serialization/serialization_test.go |
diff --git a/mojom/mojom_parser/serialization/serialization_test.go b/mojom/mojom_parser/serialization/serialization_test.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b8ccaf0fd2eda5738cbd84176c04557ff9e8511a |
--- /dev/null |
+++ b/mojom/mojom_parser/serialization/serialization_test.go |
@@ -0,0 +1,301 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package serialization |
+ |
+import ( |
+ "fmt" |
+ "mojo/public/go/bindings" |
+ "mojom/mojom_parser/generated/mojom_files" |
+ "mojom/mojom_parser/generated/mojom_types" |
+ "mojom/mojom_parser/mojom" |
+ "mojom/mojom_parser/parser" |
+ "reflect" |
+ "testing" |
+ myfmt "third_party/golang/src/fmt" |
+) |
+ |
+// singleFileTestCase stores the data for one serialization test case |
+// in which only a single file is added to the file graph. |
+type singleFileTestCase struct { |
+ fileName string |
+ mojomContents string |
+ lineAndcolumnNumbers bool |
+ expectedFile *mojom_files.MojomFile |
+ expectedGraph *mojom_files.MojomFileGraph |
+} |
+ |
+// singleFileTest contains a series of singleFileTestCase and a current |
+// testCaseNum. |
+type singleFileTest struct { |
+ cases []singleFileTestCase |
+ testCaseNum int |
+} |
+ |
+// expectedFile() returns the expectedFile of the current test case. |
+func (t *singleFileTest) expectedFile() *mojom_files.MojomFile { |
+ return t.cases[t.testCaseNum].expectedFile |
+} |
+ |
+// expectedGraph() returns the expectedGraph of the current test case. |
+func (t *singleFileTest) expectedGraph() *mojom_files.MojomFileGraph { |
+ return t.cases[t.testCaseNum].expectedGraph |
+} |
+ |
+// fileName() returns the fileName of the current test case |
+func (t *singleFileTest) fileName() string { |
+ return t.cases[t.testCaseNum].fileName |
+} |
+ |
+// addTestCase() should be invoked at the start of a case in |
+// TestSingleFileSerialization. |
+func (test *singleFileTest) addTestCase(moduleNameSpace, contents string) { |
+ fileName := fmt.Sprintf("file%d", test.testCaseNum) |
+ test.cases = append(test.cases, singleFileTestCase{fileName, contents, false, |
+ new(mojom_files.MojomFile), new(mojom_files.MojomFileGraph)}) |
+ |
+ test.expectedFile().FileName = fileName |
+ test.expectedFile().ModuleNamespace = newString(moduleNameSpace) |
+ |
+ test.expectedGraph().ResolvedTypes = make(map[string]mojom_types.UserDefinedType) |
+ test.expectedGraph().ResolvedValues = make(map[string]mojom_types.UserDefinedValue) |
+} |
+ |
+// endTestCase() should be invoked at the end of a case in |
+// TestSingleFileSerialization. |
+func (test *singleFileTest) endTestCase() { |
+ test.expectedGraph().Files = make(map[string]mojom_files.MojomFile) |
+ test.expectedGraph().Files[test.fileName()] = *test.expectedFile() |
+ test.testCaseNum += 1 |
+} |
+ |
+// newDeclData constructs a new DeclarationData with the given data. |
+func (test *singleFileTest) newDeclData(shortName, fullIdentifier string) *mojom_types.DeclarationData { |
+ return &mojom_types.DeclarationData{ |
+ ShortName: newString(shortName), |
+ FullIdentifier: newString(fullIdentifier), |
+ DeclaredOrdinal: -1, |
+ DeclarationOrder: -1, |
+ SourceFileInfo: &mojom_types.SourceFileInfo{ |
+ FileName: test.fileName(), |
+ }} |
+} |
+ |
+// 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 |
+ //////////////////////////////////////////////////////////// |
+ { |
+ |
+ 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: 3, |
+ InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{ |
+ Identifier: "NUM_MAGI", |
+ ValueKey: newString("TYPE_KEY:mojom.test.NUM_MAGI"), |
+ ResolvedConcreteValue: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{3}}, |
+ }}, |
+ }} |
+ |
+ // ResolvedTypes |
+ |
+ // struct Foo |
+ test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ |
+ DeclData: &mojom_types.DeclarationData{ |
+ ShortName: newString("Foo"), |
+ FullIdentifier: newString("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 |
+ } |
+ } |
+} |
+ |
+// compareFileGraphs compares |expected| and |actual| and returns a non-nil |
+// error if they are not deeply equal. The error message contains a human-readable |
+// string containing a deep-print of expected and actual along with the substrings |
+// starting from the first character where they differ. |
+func compareFileGraphs(expected *mojom_files.MojomFileGraph, actual *mojom_files.MojomFileGraph) error { |
+ if !reflect.DeepEqual(expected, actual) { |
+ // Note(rudominer) The myfmt package is a local modification of the fmt package |
+ // that does a deep printing that follows pointers for up to 50 levels. |
+ // Thus expectedString and actualString should contain enough information to |
+ // precisely capture the structure of expected and actual. |
+ expectedString := myfmt.Sprintf("%+v", expected) |
+ actualString := myfmt.Sprintf("%+v", actual) |
+ if expectedString != actualString { |
+ diffPos := -1 |
+ for i := 0; i < len(expectedString) && i < len(actualString); i++ { |
+ if expectedString[i] != actualString[i] { |
+ diffPos = i |
+ break |
+ } |
+ } |
+ mismatchExpected := "" |
+ mismatchActual := "" |
+ if diffPos > -1 { |
+ mismatchExpected = expectedString[diffPos:] |
+ mismatchActual = actualString[diffPos:] |
+ } |
+ return fmt.Errorf("*****\nexpected=\n*****\n%q\n*****\nactual=\n*****\n%q\n*****\n"+ |
+ "match failed at position %d: expected=\n*****\n%s\n******\nactual=\n*****\n%s\n******\n", |
+ expectedString, actualString, diffPos, mismatchExpected, mismatchActual) |
+ } else { |
+ return fmt.Errorf("expected != actual but the two printed equal.") |
+ } |
+ } |
+ return nil |
+} |