Chromium Code Reviews| 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..e67f8d24a56ab63b9503dea1faa4d3da03a117b7 |
| --- /dev/null |
| +++ b/mojom/mojom_parser/serialization/serialization_test.go |
| @@ -0,0 +1,274 @@ |
| +// 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" |
| +) |
| + |
| +// 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) { |
| + |
| + type testCase struct { |
| + fileName string |
| + mojomContents string |
| + lineAndcolumnNumbers bool |
| + expectedGraph *mojom_files.MojomFileGraph |
| + } |
| + cases := make([]testCase, 0) |
| + testCaseNum := 0 |
| + var expectedGraph *mojom_files.MojomFileGraph |
| + var expectedFile *mojom_files.MojomFile |
| + var fileName string |
| + |
| + startTestCase := func(moduleNameSpace string) { |
|
mattr
2015/11/10 19:26:49
I found this structure a bit confusing. You could
rudominer
2015/11/10 23:29:36
I have refactored the test to try to make it more
|
| + fileName = fmt.Sprintf("file%d", testCaseNum) |
| + expectedGraph = new(mojom_files.MojomFileGraph) |
| + expectedGraph.Files = make(map[string]mojom_files.MojomFile) |
| + expectedFile = new(mojom_files.MojomFile) |
| + expectedFile.FileName = fileName |
| + expectedFile.ModuleNamespace = new(string) |
| + *expectedFile.ModuleNamespace = moduleNameSpace |
| + expectedGraph.ResolvedTypes = make(map[string]mojom_types.UserDefinedType) |
| + expectedGraph.ResolvedValues = make(map[string]mojom_types.UserDefinedValue) |
| + // We don't want to bother with line and column numbers which are brittle. |
| + lineAndcolumnNumbers := false |
| + cases = append(cases, testCase{fileName, "", lineAndcolumnNumbers, expectedGraph}) |
| + } |
| + |
| + endTestCase := func() { |
| + expectedGraph.Files[fileName] = *expectedFile |
| + testCaseNum += 1 |
| + } |
| + |
| + newDeclData := func(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: fileName, |
| + }} |
| + } |
| + |
| + //////////////////////////////////////////////////////////// |
| + // Test Case |
| + //////////////////////////////////////////////////////////// |
| + startTestCase("mojom.test") |
| + cases[testCaseNum].mojomContents = ` |
| + [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 |
| + }; |
| + };` |
| + { |
| + // Attributes |
| + expectedFile.Attributes = &[]mojom_types.Attribute{ |
| + {"go_namespace", "go.test"}, {"lucky", "true"}, {"planet", "EARTH"}, |
| + } |
| + |
| + // Imports |
| + expectedFile.Imports = &[]string{ |
| + "another.file.canonical", "and.another.file.canonical", |
| + } |
| + |
| + // DeclaredMojomObjects |
| + expectedFile.DeclaredMojomObjects.Structs = &[]string{"ooF.tset.mojom"} |
| + expectedFile.DeclaredMojomObjects.TopLevelConstants = &[]string{"IGAM_MUN.tset.mojom"} |
| + |
| + // Resolved Values |
| + |
| + // NUM_MAGI |
| + expectedGraph.ResolvedValues["IGAM_MUN.tset.mojom"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{ |
| + DeclData: *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 |
| + expectedGraph.ResolvedValues["POT.staH.ooF.tset.mojom"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ |
| + DeclData: newDeclData("TOP", "mojom.test.Foo.Hats.TOP"), |
| + EnumTypeKey: "staH.ooF.tset.mojom", |
| + IntValue: -1, |
| + }} |
| + |
| + // Hats.COWBOY |
| + expectedGraph.ResolvedValues["YOBWOC.staH.ooF.tset.mojom"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ |
| + DeclData: newDeclData("COWBOY", "mojom.test.Foo.Hats.COWBOY"), |
| + EnumTypeKey: "staH.ooF.tset.mojom", |
| + IntValue: 3, |
| + InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{ |
| + Identifier: "NUM_MAGI", |
| + ValueKey: newString("IGAM_MUN.tset.mojom"), |
| + ResolvedConcreteValue: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{3}}, |
| + }}, |
| + }} |
| + |
| + // ResolvedTypes |
| + |
| + // struct Foo |
| + expectedGraph.ResolvedTypes["ooF.tset.mojom"] = &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: fileName, |
| + }, |
| + ContainedDeclarations: &mojom_types.ContainedDeclarations{ |
| + Enums: &[]string{"staH.ooF.tset.mojom"}}, |
| + }, |
| + Fields: []mojom_types.StructField{ |
| + // field x |
| + { |
| + DeclData: newDeclData("x", ""), |
| + Type: &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}, |
| + }, |
| + // field y |
| + { |
| + DeclData: newDeclData("y", ""), |
| + Type: &mojom_types.TypeStringType{mojom_types.StringType{false}}, |
| + DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueLiteralValue{&mojom_types.LiteralValueStringValue{"hello"}}}, |
| + }, |
| + // field z |
| + { |
| + DeclData: newDeclData("z", ""), |
| + Type: &mojom_types.TypeStringType{mojom_types.StringType{true}}, |
| + }, |
| + }, |
| + }} |
| + |
| + // enum Hats |
| + expectedGraph.ResolvedTypes["staH.ooF.tset.mojom"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ |
| + DeclData: 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 |
| + expectedGraph.ResolvedValues["POT.staH.ooF.tset.mojom"].(*mojom_types.UserDefinedValueEnumValue).Value, |
| + // value COWBOY |
| + expectedGraph.ResolvedValues["YOBWOC.staH.ooF.tset.mojom"].(*mojom_types.UserDefinedValueEnumValue).Value, |
| + }, |
| + }} |
| + } |
| + |
| + endTestCase() |
| + |
| + //////////////////////////////////////////////////////////// |
| + // Execute all of the test cases. |
| + //////////////////////////////////////////////////////////// |
| + for _, c := range 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 |
| +} |