OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package serialization |
| 6 |
| 7 import ( |
| 8 "fmt" |
| 9 "mojo/public/go/bindings" |
| 10 "mojom/mojom_parser/generated/mojom_files" |
| 11 "mojom/mojom_parser/generated/mojom_types" |
| 12 "mojom/mojom_parser/mojom" |
| 13 "mojom/mojom_parser/parser" |
| 14 "reflect" |
| 15 "testing" |
| 16 myfmt "third_party/golang/src/fmt" |
| 17 ) |
| 18 |
| 19 // singleFileTestCase stores the data for one serialization test case |
| 20 // in which only a single file is added to the file graph. |
| 21 type singleFileTestCase struct { |
| 22 fileName string |
| 23 mojomContents string |
| 24 lineAndcolumnNumbers bool |
| 25 expectedFile *mojom_files.MojomFile |
| 26 expectedGraph *mojom_files.MojomFileGraph |
| 27 } |
| 28 |
| 29 // singleFileTest contains a series of singleFileTestCase and a current |
| 30 // testCaseNum. |
| 31 type singleFileTest struct { |
| 32 cases []singleFileTestCase |
| 33 testCaseNum int |
| 34 } |
| 35 |
| 36 // expectedFile() returns the expectedFile of the current test case. |
| 37 func (t *singleFileTest) expectedFile() *mojom_files.MojomFile { |
| 38 return t.cases[t.testCaseNum].expectedFile |
| 39 } |
| 40 |
| 41 // expectedGraph() returns the expectedGraph of the current test case. |
| 42 func (t *singleFileTest) expectedGraph() *mojom_files.MojomFileGraph { |
| 43 return t.cases[t.testCaseNum].expectedGraph |
| 44 } |
| 45 |
| 46 // fileName() returns the fileName of the current test case |
| 47 func (t *singleFileTest) fileName() string { |
| 48 return t.cases[t.testCaseNum].fileName |
| 49 } |
| 50 |
| 51 // addTestCase() should be invoked at the start of a case in |
| 52 // TestSingleFileSerialization. |
| 53 func (test *singleFileTest) addTestCase(moduleNameSpace, contents string) { |
| 54 fileName := fmt.Sprintf("file%d", test.testCaseNum) |
| 55 test.cases = append(test.cases, singleFileTestCase{fileName, contents, f
alse, |
| 56 new(mojom_files.MojomFile), new(mojom_files.MojomFileGraph)}) |
| 57 |
| 58 test.expectedFile().FileName = fileName |
| 59 test.expectedFile().ModuleNamespace = newString(moduleNameSpace) |
| 60 |
| 61 test.expectedGraph().ResolvedTypes = make(map[string]mojom_types.UserDef
inedType) |
| 62 test.expectedGraph().ResolvedValues = make(map[string]mojom_types.UserDe
finedValue) |
| 63 } |
| 64 |
| 65 // endTestCase() should be invoked at the end of a case in |
| 66 // TestSingleFileSerialization. |
| 67 func (test *singleFileTest) endTestCase() { |
| 68 test.expectedGraph().Files = make(map[string]mojom_files.MojomFile) |
| 69 test.expectedGraph().Files[test.fileName()] = *test.expectedFile() |
| 70 test.testCaseNum += 1 |
| 71 } |
| 72 |
| 73 // newDeclData constructs a new DeclarationData with the given data. |
| 74 func (test *singleFileTest) newDeclData(shortName, fullIdentifier string) *mojom
_types.DeclarationData { |
| 75 return &mojom_types.DeclarationData{ |
| 76 ShortName: newString(shortName), |
| 77 FullIdentifier: newString(fullIdentifier), |
| 78 DeclaredOrdinal: -1, |
| 79 DeclarationOrder: -1, |
| 80 SourceFileInfo: &mojom_types.SourceFileInfo{ |
| 81 FileName: test.fileName(), |
| 82 }} |
| 83 } |
| 84 |
| 85 // TestSingleFileSerialization uses a series of test cases in which the text of
a .mojom |
| 86 // file is specified and the expected MojomFileGraph is specified using Go struc
t literals. |
| 87 func TestSingleFileSerialization(t *testing.T) { |
| 88 test := singleFileTest{} |
| 89 |
| 90 //////////////////////////////////////////////////////////// |
| 91 // Test Case |
| 92 //////////////////////////////////////////////////////////// |
| 93 { |
| 94 |
| 95 contents := ` |
| 96 [go_namespace="go.test", |
| 97 lucky=true, |
| 98 planet=EARTH] |
| 99 module mojom.test; |
| 100 |
| 101 import "another.file"; |
| 102 import "and.another.file"; |
| 103 |
| 104 const uint16 NUM_MAGI = 3; |
| 105 |
| 106 struct Foo{ |
| 107 int32 x; |
| 108 string y = "hello"; |
| 109 string? z; |
| 110 |
| 111 enum Hats { |
| 112 TOP, |
| 113 COWBOY = NUM_MAGI |
| 114 }; |
| 115 };` |
| 116 |
| 117 test.addTestCase("mojom.test", contents) |
| 118 |
| 119 // Attributes |
| 120 test.expectedFile().Attributes = &[]mojom_types.Attribute{ |
| 121 {"go_namespace", "go.test"}, {"lucky", "true"}, {"planet
", "EARTH"}, |
| 122 } |
| 123 |
| 124 // Imports |
| 125 test.expectedFile().Imports = &[]string{ |
| 126 "another.file.canonical", "and.another.file.canonical", |
| 127 } |
| 128 |
| 129 // DeclaredMojomObjects |
| 130 test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TY
PE_KEY:mojom.test.Foo"} |
| 131 test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]
string{"TYPE_KEY:mojom.test.NUM_MAGI"} |
| 132 |
| 133 // Resolved Values |
| 134 |
| 135 // NUM_MAGI |
| 136 test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.NUM_MAG
I"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant
{ |
| 137 DeclData: *test.newDeclData("NUM_MAGI", "mojom.test.NUM_
MAGI"), |
| 138 Type: &mojom_types.TypeSimpleType{mojom_types.Simple
Type_UinT16}, |
| 139 Value: &mojom_types.ValueLiteralValue{&mojom_types.Li
teralValueInt64Value{3}}, |
| 140 }} |
| 141 |
| 142 // Hats.TOP |
| 143 test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hat
s.TOP"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ |
| 144 DeclData: test.newDeclData("TOP", "mojom.test.Foo.Hat
s.TOP"), |
| 145 EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", |
| 146 IntValue: -1, |
| 147 }} |
| 148 |
| 149 // Hats.COWBOY |
| 150 test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hat
s.COWBOY"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{ |
| 151 DeclData: test.newDeclData("COWBOY", "mojom.test.Foo.
Hats.COWBOY"), |
| 152 EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats", |
| 153 IntValue: 3, |
| 154 InitializerValue: &mojom_types.ValueUserValueReference{m
ojom_types.UserValueReference{ |
| 155 Identifier: "NUM_MAGI", |
| 156 ValueKey: newString("TYPE_KEY:mojom
.test.NUM_MAGI"), |
| 157 ResolvedConcreteValue: &mojom_types.ValueLiteral
Value{&mojom_types.LiteralValueInt64Value{3}}, |
| 158 }}, |
| 159 }} |
| 160 |
| 161 // ResolvedTypes |
| 162 |
| 163 // struct Foo |
| 164 test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo"] =
&mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{ |
| 165 DeclData: &mojom_types.DeclarationData{ |
| 166 ShortName: newString("Foo"), |
| 167 FullIdentifier: newString("mojom.test.Foo"), |
| 168 DeclaredOrdinal: -1, |
| 169 DeclarationOrder: -1, |
| 170 SourceFileInfo: &mojom_types.SourceFileInfo{ |
| 171 FileName: test.fileName(), |
| 172 }, |
| 173 ContainedDeclarations: &mojom_types.ContainedDec
larations{ |
| 174 Enums: &[]string{"TYPE_KEY:mojom.test.Fo
o.Hats"}}, |
| 175 }, |
| 176 Fields: []mojom_types.StructField{ |
| 177 // field x |
| 178 { |
| 179 DeclData: test.newDeclData("x", ""), |
| 180 Type: &mojom_types.TypeSimpleType{mo
jom_types.SimpleType_InT32}, |
| 181 }, |
| 182 // field y |
| 183 { |
| 184 DeclData: test.newDeclData("y", ""), |
| 185 Type: &mojom_types.TypeStringTyp
e{mojom_types.StringType{false}}, |
| 186 DefaultValue: &mojom_types.DefaultFieldV
alueValue{&mojom_types.ValueLiteralValue{&mojom_types.LiteralValueStringValue{"h
ello"}}}, |
| 187 }, |
| 188 // field z |
| 189 { |
| 190 DeclData: test.newDeclData("z", ""), |
| 191 Type: &mojom_types.TypeStringType{mo
jom_types.StringType{true}}, |
| 192 }, |
| 193 }, |
| 194 }} |
| 195 |
| 196 // enum Hats |
| 197 test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo.Hats
"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{ |
| 198 DeclData: test.newDeclData("Hats", "mojom.test.Foo.Hats"
), |
| 199 Values: []mojom_types.EnumValue{ |
| 200 // Note(rudominer) It is a bug that we need to c
opy the enum values here. |
| 201 // See https://github.com/domokit/mojo/issues/51
3. |
| 202 // value TOP |
| 203 test.expectedGraph().ResolvedValues["TYPE_KEY:mo
jom.test.Foo.Hats.TOP"].(*mojom_types.UserDefinedValueEnumValue).Value, |
| 204 // value COWBOY |
| 205 test.expectedGraph().ResolvedValues["TYPE_KEY:mo
jom.test.Foo.Hats.COWBOY"].(*mojom_types.UserDefinedValueEnumValue).Value, |
| 206 }, |
| 207 }} |
| 208 |
| 209 test.endTestCase() |
| 210 } |
| 211 |
| 212 //////////////////////////////////////////////////////////// |
| 213 // Execute all of the test cases. |
| 214 //////////////////////////////////////////////////////////// |
| 215 for _, c := range test.cases { |
| 216 // Parse and resolve the mojom input. |
| 217 descriptor := mojom.NewMojomDescriptor() |
| 218 parser := parser.MakeParser(c.fileName, c.mojomContents, descrip
tor) |
| 219 parser.Parse() |
| 220 if !parser.OK() { |
| 221 t.Errorf("Parsing error for %s: %s", c.fileName, parser.
GetError().Error()) |
| 222 continue |
| 223 } |
| 224 if err := descriptor.Resolve(); err != nil { |
| 225 t.Errorf("Resolve error for %s: %s", c.fileName, err.Err
or()) |
| 226 continue |
| 227 } |
| 228 if err := descriptor.ComputeEnumValueIntegers(); err != nil { |
| 229 t.Errorf("ComputeEnumValueIntegers error for %s: %s", c.
fileName, err.Error()) |
| 230 continue |
| 231 } |
| 232 if err := descriptor.ComputeDataForGenerators(); err != nil { |
| 233 t.Errorf("ComputeDataForGenerators error for %s: %s", c.
fileName, err.Error()) |
| 234 continue |
| 235 } |
| 236 |
| 237 // Simulate setting the canonical file name for the imported fil
es. In real operation |
| 238 // this step is done in parser_driver.go when each of the import
ed files are parsed. |
| 239 mojomFile := parser.GetMojomFile() |
| 240 if mojomFile.Imports != nil { |
| 241 for _, imp := range mojomFile.Imports { |
| 242 imp.CanonicalFileName = fmt.Sprintf("%s.canonica
l", imp.SpecifiedName) |
| 243 } |
| 244 } |
| 245 |
| 246 // Serialize |
| 247 EmitLineAndColumnNumbers = c.lineAndcolumnNumbers |
| 248 bytes, err := Serialize(descriptor) |
| 249 if err != nil { |
| 250 t.Errorf("Serialization error for %s: %s", c.fileName, e
rr.Error()) |
| 251 continue |
| 252 } |
| 253 |
| 254 // Deserialize |
| 255 decoder := bindings.NewDecoder(bytes, nil) |
| 256 fileGraph := mojom_files.MojomFileGraph{} |
| 257 fileGraph.Decode(decoder) |
| 258 |
| 259 // Compare |
| 260 if err := compareFileGraphs(c.expectedGraph, &fileGraph); err !=
nil { |
| 261 t.Errorf("%s:\n%s", c.fileName, err.Error()) |
| 262 continue |
| 263 } |
| 264 } |
| 265 } |
| 266 |
| 267 // compareFileGraphs compares |expected| and |actual| and returns a non-nil |
| 268 // error if they are not deeply equal. The error message contains a human-readab
le |
| 269 // string containing a deep-print of expected and actual along with the substrin
gs |
| 270 // starting from the first character where they differ. |
| 271 func compareFileGraphs(expected *mojom_files.MojomFileGraph, actual *mojom_files
.MojomFileGraph) error { |
| 272 if !reflect.DeepEqual(expected, actual) { |
| 273 // Note(rudominer) The myfmt package is a local modification of
the fmt package |
| 274 // that does a deep printing that follows pointers for up to 50
levels. |
| 275 // Thus expectedString and actualString should contain enough in
formation to |
| 276 // precisely capture the structure of expected and actual. |
| 277 expectedString := myfmt.Sprintf("%+v", expected) |
| 278 actualString := myfmt.Sprintf("%+v", actual) |
| 279 if expectedString != actualString { |
| 280 diffPos := -1 |
| 281 for i := 0; i < len(expectedString) && i < len(actualStr
ing); i++ { |
| 282 if expectedString[i] != actualString[i] { |
| 283 diffPos = i |
| 284 break |
| 285 } |
| 286 } |
| 287 mismatchExpected := "" |
| 288 mismatchActual := "" |
| 289 if diffPos > -1 { |
| 290 mismatchExpected = expectedString[diffPos:] |
| 291 mismatchActual = actualString[diffPos:] |
| 292 } |
| 293 return fmt.Errorf("*****\nexpected=\n*****\n%q\n*****\na
ctual=\n*****\n%q\n*****\n"+ |
| 294 "match failed at position %d: expected=\n*****\n
%s\n******\nactual=\n*****\n%s\n******\n", |
| 295 expectedString, actualString, diffPos, mismatchE
xpected, mismatchActual) |
| 296 } else { |
| 297 return fmt.Errorf("expected != actual but the two printe
d equal.") |
| 298 } |
| 299 } |
| 300 return nil |
| 301 } |
OLD | NEW |