| OLD | NEW |
| (Empty) |
| 1 # Copyright 2014 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 import imp | |
| 6 import os.path | |
| 7 import sys | |
| 8 import unittest | |
| 9 | |
| 10 def _GetDirAbove(dirname): | |
| 11 """Returns the directory "above" this file containing |dirname| (which must | |
| 12 also be "above" this file).""" | |
| 13 path = os.path.abspath(__file__) | |
| 14 while True: | |
| 15 path, tail = os.path.split(path) | |
| 16 assert tail | |
| 17 if tail == dirname: | |
| 18 return path | |
| 19 | |
| 20 try: | |
| 21 imp.find_module("mojom") | |
| 22 except ImportError: | |
| 23 sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib")) | |
| 24 import mojom.parse.ast as ast | |
| 25 import mojom.parse.lexer as lexer | |
| 26 import mojom.parse.parser as parser | |
| 27 | |
| 28 | |
| 29 class ParserTest(unittest.TestCase): | |
| 30 """Tests |parser.Parse()|.""" | |
| 31 | |
| 32 def testTrivialValidSource(self): | |
| 33 """Tests a trivial, but valid, .mojom source.""" | |
| 34 | |
| 35 source = """\ | |
| 36 // This is a comment. | |
| 37 | |
| 38 module my_module; | |
| 39 """ | |
| 40 expected = ast.Mojom( | |
| 41 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 42 ast.ImportList(), | |
| 43 []) | |
| 44 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 45 | |
| 46 def testSourceWithCrLfs(self): | |
| 47 """Tests a .mojom source with CR-LFs instead of LFs.""" | |
| 48 | |
| 49 source = "// This is a comment.\r\n\r\nmodule my_module;\r\n" | |
| 50 expected = ast.Mojom( | |
| 51 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 52 ast.ImportList(), | |
| 53 []) | |
| 54 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 55 | |
| 56 def testUnexpectedEOF(self): | |
| 57 """Tests a "truncated" .mojom source.""" | |
| 58 | |
| 59 source = """\ | |
| 60 // This is a comment. | |
| 61 | |
| 62 module my_module | |
| 63 """ | |
| 64 with self.assertRaisesRegexp( | |
| 65 parser.ParseError, | |
| 66 r"^my_file\.mojom: Error: Unexpected end of file$"): | |
| 67 parser.Parse(source, "my_file.mojom") | |
| 68 | |
| 69 def testCommentLineNumbers(self): | |
| 70 """Tests that line numbers are correctly tracked when comments are | |
| 71 present.""" | |
| 72 | |
| 73 source1 = """\ | |
| 74 // Isolated C++-style comments. | |
| 75 | |
| 76 // Foo. | |
| 77 asdf1 | |
| 78 """ | |
| 79 with self.assertRaisesRegexp( | |
| 80 parser.ParseError, | |
| 81 r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\n *asdf1$"): | |
| 82 parser.Parse(source1, "my_file.mojom") | |
| 83 | |
| 84 source2 = """\ | |
| 85 // Consecutive C++-style comments. | |
| 86 // Foo. | |
| 87 // Bar. | |
| 88 | |
| 89 struct Yada { // Baz. | |
| 90 // Quux. | |
| 91 int32 x; | |
| 92 }; | |
| 93 | |
| 94 asdf2 | |
| 95 """ | |
| 96 with self.assertRaisesRegexp( | |
| 97 parser.ParseError, | |
| 98 r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\n *asdf2$"): | |
| 99 parser.Parse(source2, "my_file.mojom") | |
| 100 | |
| 101 source3 = """\ | |
| 102 /* Single-line C-style comments. */ | |
| 103 /* Foobar. */ | |
| 104 | |
| 105 /* Baz. */ | |
| 106 asdf3 | |
| 107 """ | |
| 108 with self.assertRaisesRegexp( | |
| 109 parser.ParseError, | |
| 110 r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\n *asdf3$"): | |
| 111 parser.Parse(source3, "my_file.mojom") | |
| 112 | |
| 113 source4 = """\ | |
| 114 /* Multi-line C-style comments. | |
| 115 */ | |
| 116 /* | |
| 117 Foo. | |
| 118 Bar. | |
| 119 */ | |
| 120 | |
| 121 /* Baz | |
| 122 Quux. */ | |
| 123 asdf4 | |
| 124 """ | |
| 125 with self.assertRaisesRegexp( | |
| 126 parser.ParseError, | |
| 127 r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\n *asdf4$"): | |
| 128 parser.Parse(source4, "my_file.mojom") | |
| 129 | |
| 130 | |
| 131 def testSimpleStruct(self): | |
| 132 """Tests a simple .mojom source that just defines a struct.""" | |
| 133 | |
| 134 source = """\ | |
| 135 module my_module; | |
| 136 | |
| 137 struct MyStruct { | |
| 138 int32 a; | |
| 139 double b; | |
| 140 }; | |
| 141 """ | |
| 142 expected = ast.Mojom( | |
| 143 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 144 ast.ImportList(), | |
| 145 [ast.Struct( | |
| 146 'MyStruct', | |
| 147 None, | |
| 148 ast.StructBody( | |
| 149 [ast.StructField('a', None, 'int32', None), | |
| 150 ast.StructField('b', None, 'double', None)]))]) | |
| 151 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 152 | |
| 153 def testSimpleStructWithoutModule(self): | |
| 154 """Tests a simple struct without an explict module statement.""" | |
| 155 | |
| 156 source = """\ | |
| 157 struct MyStruct { | |
| 158 int32 a; | |
| 159 double b; | |
| 160 }; | |
| 161 """ | |
| 162 expected = ast.Mojom( | |
| 163 None, | |
| 164 ast.ImportList(), | |
| 165 [ast.Struct( | |
| 166 'MyStruct', | |
| 167 None, | |
| 168 ast.StructBody( | |
| 169 [ast.StructField('a', None, 'int32', None), | |
| 170 ast.StructField('b', None, 'double', None)]))]) | |
| 171 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 172 | |
| 173 def testValidStructDefinitions(self): | |
| 174 """Tests all types of definitions that can occur in a struct.""" | |
| 175 | |
| 176 source = """\ | |
| 177 struct MyStruct { | |
| 178 enum MyEnum { VALUE }; | |
| 179 const double kMyConst = 1.23; | |
| 180 int32 a; | |
| 181 SomeOtherStruct b; // Invalidity detected at another stage. | |
| 182 }; | |
| 183 """ | |
| 184 expected = ast.Mojom( | |
| 185 None, | |
| 186 ast.ImportList(), | |
| 187 [ast.Struct( | |
| 188 'MyStruct', | |
| 189 None, | |
| 190 ast.StructBody( | |
| 191 [ast.Enum('MyEnum', | |
| 192 ast.EnumValueList(ast.EnumValue('VALUE', None))), | |
| 193 ast.Const('kMyConst', 'double', '1.23'), | |
| 194 ast.StructField('a', None, 'int32', None), | |
| 195 ast.StructField('b', None, 'SomeOtherStruct', None)]))]) | |
| 196 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 197 | |
| 198 def testInvalidStructDefinitions(self): | |
| 199 """Tests that definitions that aren't allowed in a struct are correctly | |
| 200 detected.""" | |
| 201 | |
| 202 source1 = """\ | |
| 203 struct MyStruct { | |
| 204 MyMethod(int32 a); | |
| 205 }; | |
| 206 """ | |
| 207 with self.assertRaisesRegexp( | |
| 208 parser.ParseError, | |
| 209 r"^my_file\.mojom:2: Error: Unexpected '\(':\n" | |
| 210 r" *MyMethod\(int32 a\);$"): | |
| 211 parser.Parse(source1, "my_file.mojom") | |
| 212 | |
| 213 source2 = """\ | |
| 214 struct MyStruct { | |
| 215 struct MyInnerStruct { | |
| 216 int32 a; | |
| 217 }; | |
| 218 }; | |
| 219 """ | |
| 220 with self.assertRaisesRegexp( | |
| 221 parser.ParseError, | |
| 222 r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" | |
| 223 r" *struct MyInnerStruct {$"): | |
| 224 parser.Parse(source2, "my_file.mojom") | |
| 225 | |
| 226 source3 = """\ | |
| 227 struct MyStruct { | |
| 228 interface MyInterface { | |
| 229 MyMethod(int32 a); | |
| 230 }; | |
| 231 }; | |
| 232 """ | |
| 233 with self.assertRaisesRegexp( | |
| 234 parser.ParseError, | |
| 235 r"^my_file\.mojom:2: Error: Unexpected 'interface':\n" | |
| 236 r" *interface MyInterface {$"): | |
| 237 parser.Parse(source3, "my_file.mojom") | |
| 238 | |
| 239 def testMissingModuleName(self): | |
| 240 """Tests an (invalid) .mojom with a missing module name.""" | |
| 241 | |
| 242 source1 = """\ | |
| 243 // Missing module name. | |
| 244 module ; | |
| 245 struct MyStruct { | |
| 246 int32 a; | |
| 247 }; | |
| 248 """ | |
| 249 with self.assertRaisesRegexp( | |
| 250 parser.ParseError, | |
| 251 r"^my_file\.mojom:2: Error: Unexpected ';':\n *module ;$"): | |
| 252 parser.Parse(source1, "my_file.mojom") | |
| 253 | |
| 254 # Another similar case, but make sure that line-number tracking/reporting | |
| 255 # is correct. | |
| 256 source2 = """\ | |
| 257 module | |
| 258 // This line intentionally left unblank. | |
| 259 | |
| 260 struct MyStruct { | |
| 261 int32 a; | |
| 262 }; | |
| 263 """ | |
| 264 with self.assertRaisesRegexp( | |
| 265 parser.ParseError, | |
| 266 r"^my_file\.mojom:4: Error: Unexpected 'struct':\n" | |
| 267 r" *struct MyStruct {$"): | |
| 268 parser.Parse(source2, "my_file.mojom") | |
| 269 | |
| 270 def testMultipleModuleStatements(self): | |
| 271 """Tests an (invalid) .mojom with multiple module statements.""" | |
| 272 | |
| 273 source = """\ | |
| 274 module foo; | |
| 275 module bar; | |
| 276 """ | |
| 277 with self.assertRaisesRegexp( | |
| 278 parser.ParseError, | |
| 279 r"^my_file\.mojom:2: Error: Multiple \"module\" statements not " | |
| 280 r"allowed:\n *module bar;$"): | |
| 281 parser.Parse(source, "my_file.mojom") | |
| 282 | |
| 283 def testModuleStatementAfterImport(self): | |
| 284 """Tests an (invalid) .mojom with a module statement after an import.""" | |
| 285 | |
| 286 source = """\ | |
| 287 import "foo.mojom"; | |
| 288 module foo; | |
| 289 """ | |
| 290 with self.assertRaisesRegexp( | |
| 291 parser.ParseError, | |
| 292 r"^my_file\.mojom:2: Error: \"module\" statements must precede imports " | |
| 293 r"and definitions:\n *module foo;$"): | |
| 294 parser.Parse(source, "my_file.mojom") | |
| 295 | |
| 296 def testModuleStatementAfterDefinition(self): | |
| 297 """Tests an (invalid) .mojom with a module statement after a definition.""" | |
| 298 | |
| 299 source = """\ | |
| 300 struct MyStruct { | |
| 301 int32 a; | |
| 302 }; | |
| 303 module foo; | |
| 304 """ | |
| 305 with self.assertRaisesRegexp( | |
| 306 parser.ParseError, | |
| 307 r"^my_file\.mojom:4: Error: \"module\" statements must precede imports " | |
| 308 r"and definitions:\n *module foo;$"): | |
| 309 parser.Parse(source, "my_file.mojom") | |
| 310 | |
| 311 def testImportStatementAfterDefinition(self): | |
| 312 """Tests an (invalid) .mojom with an import statement after a definition.""" | |
| 313 | |
| 314 source = """\ | |
| 315 struct MyStruct { | |
| 316 int32 a; | |
| 317 }; | |
| 318 import "foo.mojom"; | |
| 319 """ | |
| 320 with self.assertRaisesRegexp( | |
| 321 parser.ParseError, | |
| 322 r"^my_file\.mojom:4: Error: \"import\" statements must precede " | |
| 323 r"definitions:\n *import \"foo.mojom\";$"): | |
| 324 parser.Parse(source, "my_file.mojom") | |
| 325 | |
| 326 def testEnums(self): | |
| 327 """Tests that enum statements are correctly parsed.""" | |
| 328 | |
| 329 source = """\ | |
| 330 module my_module; | |
| 331 enum MyEnum1 { VALUE1, VALUE2 }; // No trailing comma. | |
| 332 enum MyEnum2 { | |
| 333 VALUE1 = -1, | |
| 334 VALUE2 = 0, | |
| 335 VALUE3 = + 987, // Check that space is allowed. | |
| 336 VALUE4 = 0xAF12, | |
| 337 VALUE5 = -0x09bcd, | |
| 338 VALUE6 = VALUE5, | |
| 339 VALUE7, // Leave trailing comma. | |
| 340 }; | |
| 341 """ | |
| 342 expected = ast.Mojom( | |
| 343 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 344 ast.ImportList(), | |
| 345 [ast.Enum( | |
| 346 'MyEnum1', | |
| 347 ast.EnumValueList([ast.EnumValue('VALUE1', None), | |
| 348 ast.EnumValue('VALUE2', None)])), | |
| 349 ast.Enum( | |
| 350 'MyEnum2', | |
| 351 ast.EnumValueList([ast.EnumValue('VALUE1', '-1'), | |
| 352 ast.EnumValue('VALUE2', '0'), | |
| 353 ast.EnumValue('VALUE3', '+987'), | |
| 354 ast.EnumValue('VALUE4', '0xAF12'), | |
| 355 ast.EnumValue('VALUE5', '-0x09bcd'), | |
| 356 ast.EnumValue('VALUE6', ('IDENTIFIER', | |
| 357 'VALUE5')), | |
| 358 ast.EnumValue('VALUE7', None)]))]) | |
| 359 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 360 | |
| 361 def testInvalidEnumInitializers(self): | |
| 362 """Tests that invalid enum initializers are correctly detected.""" | |
| 363 | |
| 364 # No values. | |
| 365 source1 = """\ | |
| 366 enum MyEnum { | |
| 367 }; | |
| 368 """ | |
| 369 with self.assertRaisesRegexp( | |
| 370 parser.ParseError, | |
| 371 r"^my_file\.mojom:2: Error: Unexpected '}':\n" | |
| 372 r" *};$"): | |
| 373 parser.Parse(source1, "my_file.mojom") | |
| 374 | |
| 375 # Floating point value. | |
| 376 source2 = "enum MyEnum { VALUE = 0.123 };" | |
| 377 with self.assertRaisesRegexp( | |
| 378 parser.ParseError, | |
| 379 r"^my_file\.mojom:1: Error: Unexpected '0\.123':\n" | |
| 380 r"enum MyEnum { VALUE = 0\.123 };$"): | |
| 381 parser.Parse(source2, "my_file.mojom") | |
| 382 | |
| 383 # Boolean value. | |
| 384 source2 = "enum MyEnum { VALUE = true };" | |
| 385 with self.assertRaisesRegexp( | |
| 386 parser.ParseError, | |
| 387 r"^my_file\.mojom:1: Error: Unexpected 'true':\n" | |
| 388 r"enum MyEnum { VALUE = true };$"): | |
| 389 parser.Parse(source2, "my_file.mojom") | |
| 390 | |
| 391 def testConsts(self): | |
| 392 """Tests some constants and struct members initialized with them.""" | |
| 393 | |
| 394 source = """\ | |
| 395 module my_module; | |
| 396 | |
| 397 struct MyStruct { | |
| 398 const int8 kNumber = -1; | |
| 399 int8 number@0 = kNumber; | |
| 400 }; | |
| 401 """ | |
| 402 expected = ast.Mojom( | |
| 403 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 404 ast.ImportList(), | |
| 405 [ast.Struct( | |
| 406 'MyStruct', None, | |
| 407 ast.StructBody( | |
| 408 [ast.Const('kNumber', 'int8', '-1'), | |
| 409 ast.StructField('number', ast.Ordinal(0), 'int8', | |
| 410 ('IDENTIFIER', 'kNumber'))]))]) | |
| 411 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 412 | |
| 413 def testNoConditionals(self): | |
| 414 """Tests that ?: is not allowed.""" | |
| 415 | |
| 416 source = """\ | |
| 417 module my_module; | |
| 418 | |
| 419 enum MyEnum { | |
| 420 MY_ENUM_1 = 1 ? 2 : 3 | |
| 421 }; | |
| 422 """ | |
| 423 with self.assertRaisesRegexp( | |
| 424 parser.ParseError, | |
| 425 r"^my_file\.mojom:4: Error: Unexpected '\?':\n" | |
| 426 r" *MY_ENUM_1 = 1 \? 2 : 3$"): | |
| 427 parser.Parse(source, "my_file.mojom") | |
| 428 | |
| 429 def testSimpleOrdinals(self): | |
| 430 """Tests that (valid) ordinal values are scanned correctly.""" | |
| 431 | |
| 432 source = """\ | |
| 433 module my_module; | |
| 434 | |
| 435 // This isn't actually valid .mojom, but the problem (missing ordinals) | |
| 436 // should be handled at a different level. | |
| 437 struct MyStruct { | |
| 438 int32 a0@0; | |
| 439 int32 a1@1; | |
| 440 int32 a2@2; | |
| 441 int32 a9@9; | |
| 442 int32 a10 @10; | |
| 443 int32 a11 @11; | |
| 444 int32 a29 @29; | |
| 445 int32 a1234567890 @1234567890; | |
| 446 }; | |
| 447 """ | |
| 448 expected = ast.Mojom( | |
| 449 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 450 ast.ImportList(), | |
| 451 [ast.Struct( | |
| 452 'MyStruct', | |
| 453 None, | |
| 454 ast.StructBody( | |
| 455 [ast.StructField('a0', ast.Ordinal(0), 'int32', None), | |
| 456 ast.StructField('a1', ast.Ordinal(1), 'int32', None), | |
| 457 ast.StructField('a2', ast.Ordinal(2), 'int32', None), | |
| 458 ast.StructField('a9', ast.Ordinal(9), 'int32', None), | |
| 459 ast.StructField('a10', ast.Ordinal(10), 'int32', None), | |
| 460 ast.StructField('a11', ast.Ordinal(11), 'int32', None), | |
| 461 ast.StructField('a29', ast.Ordinal(29), 'int32', None), | |
| 462 ast.StructField('a1234567890', ast.Ordinal(1234567890), | |
| 463 'int32', None)]))]) | |
| 464 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 465 | |
| 466 def testInvalidOrdinals(self): | |
| 467 """Tests that (lexically) invalid ordinals are correctly detected.""" | |
| 468 | |
| 469 source1 = """\ | |
| 470 module my_module; | |
| 471 | |
| 472 struct MyStruct { | |
| 473 int32 a_missing@; | |
| 474 }; | |
| 475 """ | |
| 476 with self.assertRaisesRegexp( | |
| 477 lexer.LexError, | |
| 478 r"^my_file\.mojom:4: Error: Missing ordinal value$"): | |
| 479 parser.Parse(source1, "my_file.mojom") | |
| 480 | |
| 481 source2 = """\ | |
| 482 module my_module; | |
| 483 | |
| 484 struct MyStruct { | |
| 485 int32 a_octal@01; | |
| 486 }; | |
| 487 """ | |
| 488 with self.assertRaisesRegexp( | |
| 489 lexer.LexError, | |
| 490 r"^my_file\.mojom:4: Error: " | |
| 491 r"Octal and hexadecimal ordinal values not allowed$"): | |
| 492 parser.Parse(source2, "my_file.mojom") | |
| 493 | |
| 494 source3 = """\ | |
| 495 module my_module; struct MyStruct { int32 a_invalid_octal@08; }; | |
| 496 """ | |
| 497 with self.assertRaisesRegexp( | |
| 498 lexer.LexError, | |
| 499 r"^my_file\.mojom:1: Error: " | |
| 500 r"Octal and hexadecimal ordinal values not allowed$"): | |
| 501 parser.Parse(source3, "my_file.mojom") | |
| 502 | |
| 503 source4 = "module my_module; struct MyStruct { int32 a_hex@0x1aB9; };" | |
| 504 with self.assertRaisesRegexp( | |
| 505 lexer.LexError, | |
| 506 r"^my_file\.mojom:1: Error: " | |
| 507 r"Octal and hexadecimal ordinal values not allowed$"): | |
| 508 parser.Parse(source4, "my_file.mojom") | |
| 509 | |
| 510 source5 = "module my_module; struct MyStruct { int32 a_hex@0X0; };" | |
| 511 with self.assertRaisesRegexp( | |
| 512 lexer.LexError, | |
| 513 r"^my_file\.mojom:1: Error: " | |
| 514 r"Octal and hexadecimal ordinal values not allowed$"): | |
| 515 parser.Parse(source5, "my_file.mojom") | |
| 516 | |
| 517 source6 = """\ | |
| 518 struct MyStruct { | |
| 519 int32 a_too_big@999999999999; | |
| 520 }; | |
| 521 """ | |
| 522 with self.assertRaisesRegexp( | |
| 523 parser.ParseError, | |
| 524 r"^my_file\.mojom:2: Error: " | |
| 525 r"Ordinal value 999999999999 too large:\n" | |
| 526 r" *int32 a_too_big@999999999999;$"): | |
| 527 parser.Parse(source6, "my_file.mojom") | |
| 528 | |
| 529 def testNestedNamespace(self): | |
| 530 """Tests that "nested" namespaces work.""" | |
| 531 | |
| 532 source = """\ | |
| 533 module my.mod; | |
| 534 | |
| 535 struct MyStruct { | |
| 536 int32 a; | |
| 537 }; | |
| 538 """ | |
| 539 expected = ast.Mojom( | |
| 540 ast.Module(('IDENTIFIER', 'my.mod'), None), | |
| 541 ast.ImportList(), | |
| 542 [ast.Struct( | |
| 543 'MyStruct', | |
| 544 None, | |
| 545 ast.StructBody(ast.StructField('a', None, 'int32', None)))]) | |
| 546 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 547 | |
| 548 def testValidHandleTypes(self): | |
| 549 """Tests (valid) handle types.""" | |
| 550 | |
| 551 source = """\ | |
| 552 struct MyStruct { | |
| 553 handle a; | |
| 554 handle<data_pipe_consumer> b; | |
| 555 handle <data_pipe_producer> c; | |
| 556 handle < message_pipe > d; | |
| 557 handle | |
| 558 < shared_buffer | |
| 559 > e; | |
| 560 }; | |
| 561 """ | |
| 562 expected = ast.Mojom( | |
| 563 None, | |
| 564 ast.ImportList(), | |
| 565 [ast.Struct( | |
| 566 'MyStruct', | |
| 567 None, | |
| 568 ast.StructBody( | |
| 569 [ast.StructField('a', None, 'handle', None), | |
| 570 ast.StructField('b', None, 'handle<data_pipe_consumer>', None), | |
| 571 ast.StructField('c', None, 'handle<data_pipe_producer>', None), | |
| 572 ast.StructField('d', None, 'handle<message_pipe>', None), | |
| 573 ast.StructField('e', None, 'handle<shared_buffer>', None)]))]) | |
| 574 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 575 | |
| 576 def testInvalidHandleType(self): | |
| 577 """Tests an invalid (unknown) handle type.""" | |
| 578 | |
| 579 source = """\ | |
| 580 struct MyStruct { | |
| 581 handle<wtf_is_this> foo; | |
| 582 }; | |
| 583 """ | |
| 584 with self.assertRaisesRegexp( | |
| 585 parser.ParseError, | |
| 586 r"^my_file\.mojom:2: Error: " | |
| 587 r"Invalid handle type 'wtf_is_this':\n" | |
| 588 r" *handle<wtf_is_this> foo;$"): | |
| 589 parser.Parse(source, "my_file.mojom") | |
| 590 | |
| 591 def testValidDefaultValues(self): | |
| 592 """Tests default values that are valid (to the parser).""" | |
| 593 | |
| 594 source = """\ | |
| 595 struct MyStruct { | |
| 596 int16 a0 = 0; | |
| 597 uint16 a1 = 0x0; | |
| 598 uint16 a2 = 0x00; | |
| 599 uint16 a3 = 0x01; | |
| 600 uint16 a4 = 0xcd; | |
| 601 int32 a5 = 12345; | |
| 602 int64 a6 = -12345; | |
| 603 int64 a7 = +12345; | |
| 604 uint32 a8 = 0x12cd3; | |
| 605 uint32 a9 = -0x12cD3; | |
| 606 uint32 a10 = +0x12CD3; | |
| 607 bool a11 = true; | |
| 608 bool a12 = false; | |
| 609 float a13 = 1.2345; | |
| 610 float a14 = -1.2345; | |
| 611 float a15 = +1.2345; | |
| 612 float a16 = 123.; | |
| 613 float a17 = .123; | |
| 614 double a18 = 1.23E10; | |
| 615 double a19 = 1.E-10; | |
| 616 double a20 = .5E+10; | |
| 617 double a21 = -1.23E10; | |
| 618 double a22 = +.123E10; | |
| 619 }; | |
| 620 """ | |
| 621 expected = ast.Mojom( | |
| 622 None, | |
| 623 ast.ImportList(), | |
| 624 [ast.Struct( | |
| 625 'MyStruct', | |
| 626 None, | |
| 627 ast.StructBody( | |
| 628 [ast.StructField('a0', None, 'int16', '0'), | |
| 629 ast.StructField('a1', None, 'uint16', '0x0'), | |
| 630 ast.StructField('a2', None, 'uint16', '0x00'), | |
| 631 ast.StructField('a3', None, 'uint16', '0x01'), | |
| 632 ast.StructField('a4', None, 'uint16', '0xcd'), | |
| 633 ast.StructField('a5' , None, 'int32', '12345'), | |
| 634 ast.StructField('a6', None, 'int64', '-12345'), | |
| 635 ast.StructField('a7', None, 'int64', '+12345'), | |
| 636 ast.StructField('a8', None, 'uint32', '0x12cd3'), | |
| 637 ast.StructField('a9', None, 'uint32', '-0x12cD3'), | |
| 638 ast.StructField('a10', None, 'uint32', '+0x12CD3'), | |
| 639 ast.StructField('a11', None, 'bool', 'true'), | |
| 640 ast.StructField('a12', None, 'bool', 'false'), | |
| 641 ast.StructField('a13', None, 'float', '1.2345'), | |
| 642 ast.StructField('a14', None, 'float', '-1.2345'), | |
| 643 ast.StructField('a15', None, 'float', '+1.2345'), | |
| 644 ast.StructField('a16', None, 'float', '123.'), | |
| 645 ast.StructField('a17', None, 'float', '.123'), | |
| 646 ast.StructField('a18', None, 'double', '1.23E10'), | |
| 647 ast.StructField('a19', None, 'double', '1.E-10'), | |
| 648 ast.StructField('a20', None, 'double', '.5E+10'), | |
| 649 ast.StructField('a21', None, 'double', '-1.23E10'), | |
| 650 ast.StructField('a22', None, 'double', '+.123E10')]))]) | |
| 651 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 652 | |
| 653 def testValidFixedSizeArray(self): | |
| 654 """Tests parsing a fixed size array.""" | |
| 655 | |
| 656 source = """\ | |
| 657 struct MyStruct { | |
| 658 array<int32> normal_array; | |
| 659 array<int32, 1> fixed_size_array_one_entry; | |
| 660 array<int32, 10> fixed_size_array_ten_entries; | |
| 661 array<array<array<int32, 1>>, 2> nested_arrays; | |
| 662 }; | |
| 663 """ | |
| 664 expected = ast.Mojom( | |
| 665 None, | |
| 666 ast.ImportList(), | |
| 667 [ast.Struct( | |
| 668 'MyStruct', | |
| 669 None, | |
| 670 ast.StructBody( | |
| 671 [ast.StructField('normal_array', None, 'int32[]', None), | |
| 672 ast.StructField('fixed_size_array_one_entry', None, 'int32[1]', | |
| 673 None), | |
| 674 ast.StructField('fixed_size_array_ten_entries', None, | |
| 675 'int32[10]', None), | |
| 676 ast.StructField('nested_arrays', None, | |
| 677 'int32[1][][2]', None)]))]) | |
| 678 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 679 | |
| 680 def testValidNestedArray(self): | |
| 681 """Tests parsing a nested array.""" | |
| 682 | |
| 683 source = "struct MyStruct { array<array<int32>> nested_array; };" | |
| 684 expected = ast.Mojom( | |
| 685 None, | |
| 686 ast.ImportList(), | |
| 687 [ast.Struct( | |
| 688 'MyStruct', | |
| 689 None, | |
| 690 ast.StructBody( | |
| 691 ast.StructField('nested_array', None, 'int32[][]', None)))]) | |
| 692 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 693 | |
| 694 def testInvalidFixedArraySize(self): | |
| 695 """Tests that invalid fixed array bounds are correctly detected.""" | |
| 696 | |
| 697 source1 = """\ | |
| 698 struct MyStruct { | |
| 699 array<int32, 0> zero_size_array; | |
| 700 }; | |
| 701 """ | |
| 702 with self.assertRaisesRegexp( | |
| 703 parser.ParseError, | |
| 704 r"^my_file\.mojom:2: Error: Fixed array size 0 invalid:\n" | |
| 705 r" *array<int32, 0> zero_size_array;$"): | |
| 706 parser.Parse(source1, "my_file.mojom") | |
| 707 | |
| 708 source2 = """\ | |
| 709 struct MyStruct { | |
| 710 array<int32, 999999999999> too_big_array; | |
| 711 }; | |
| 712 """ | |
| 713 with self.assertRaisesRegexp( | |
| 714 parser.ParseError, | |
| 715 r"^my_file\.mojom:2: Error: Fixed array size 999999999999 invalid:\n" | |
| 716 r" *array<int32, 999999999999> too_big_array;$"): | |
| 717 parser.Parse(source2, "my_file.mojom") | |
| 718 | |
| 719 source3 = """\ | |
| 720 struct MyStruct { | |
| 721 array<int32, abcdefg> not_a_number; | |
| 722 }; | |
| 723 """ | |
| 724 with self.assertRaisesRegexp( | |
| 725 parser.ParseError, | |
| 726 r"^my_file\.mojom:2: Error: Unexpected 'abcdefg':\n" | |
| 727 r" *array<int32, abcdefg> not_a_number;"): | |
| 728 parser.Parse(source3, "my_file.mojom") | |
| 729 | |
| 730 def testValidAssociativeArrays(self): | |
| 731 """Tests that we can parse valid associative array structures.""" | |
| 732 | |
| 733 source1 = "struct MyStruct { map<string, uint8> data; };" | |
| 734 expected1 = ast.Mojom( | |
| 735 None, | |
| 736 ast.ImportList(), | |
| 737 [ast.Struct( | |
| 738 'MyStruct', | |
| 739 None, | |
| 740 ast.StructBody( | |
| 741 [ast.StructField('data', None, 'uint8{string}', None)]))]) | |
| 742 self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) | |
| 743 | |
| 744 source2 = "interface MyInterface { MyMethod(map<string, uint8> a); };" | |
| 745 expected2 = ast.Mojom( | |
| 746 None, | |
| 747 ast.ImportList(), | |
| 748 [ast.Interface( | |
| 749 'MyInterface', | |
| 750 None, | |
| 751 ast.InterfaceBody( | |
| 752 ast.Method( | |
| 753 'MyMethod', | |
| 754 None, | |
| 755 ast.ParameterList( | |
| 756 ast.Parameter('a', None, 'uint8{string}')), | |
| 757 None)))]) | |
| 758 self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) | |
| 759 | |
| 760 source3 = "struct MyStruct { map<string, array<uint8>> data; };" | |
| 761 expected3 = ast.Mojom( | |
| 762 None, | |
| 763 ast.ImportList(), | |
| 764 [ast.Struct( | |
| 765 'MyStruct', | |
| 766 None, | |
| 767 ast.StructBody( | |
| 768 [ast.StructField('data', None, 'uint8[]{string}', None)]))]) | |
| 769 self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) | |
| 770 | |
| 771 def testValidMethod(self): | |
| 772 """Tests parsing method declarations.""" | |
| 773 | |
| 774 source1 = "interface MyInterface { MyMethod(int32 a); };" | |
| 775 expected1 = ast.Mojom( | |
| 776 None, | |
| 777 ast.ImportList(), | |
| 778 [ast.Interface( | |
| 779 'MyInterface', | |
| 780 None, | |
| 781 ast.InterfaceBody( | |
| 782 ast.Method( | |
| 783 'MyMethod', | |
| 784 None, | |
| 785 ast.ParameterList(ast.Parameter('a', None, 'int32')), | |
| 786 None)))]) | |
| 787 self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) | |
| 788 | |
| 789 source2 = """\ | |
| 790 interface MyInterface { | |
| 791 MyMethod1@0(int32 a@0, int64 b@1); | |
| 792 MyMethod2@1() => (); | |
| 793 }; | |
| 794 """ | |
| 795 expected2 = ast.Mojom( | |
| 796 None, | |
| 797 ast.ImportList(), | |
| 798 [ast.Interface( | |
| 799 'MyInterface', | |
| 800 None, | |
| 801 ast.InterfaceBody( | |
| 802 [ast.Method( | |
| 803 'MyMethod1', | |
| 804 ast.Ordinal(0), | |
| 805 ast.ParameterList([ast.Parameter('a', ast.Ordinal(0), | |
| 806 'int32'), | |
| 807 ast.Parameter('b', ast.Ordinal(1), | |
| 808 'int64')]), | |
| 809 None), | |
| 810 ast.Method( | |
| 811 'MyMethod2', | |
| 812 ast.Ordinal(1), | |
| 813 ast.ParameterList(), | |
| 814 ast.ParameterList())]))]) | |
| 815 self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) | |
| 816 | |
| 817 source3 = """\ | |
| 818 interface MyInterface { | |
| 819 MyMethod(string a) => (int32 a, bool b); | |
| 820 }; | |
| 821 """ | |
| 822 expected3 = ast.Mojom( | |
| 823 None, | |
| 824 ast.ImportList(), | |
| 825 [ast.Interface( | |
| 826 'MyInterface', | |
| 827 None, | |
| 828 ast.InterfaceBody( | |
| 829 ast.Method( | |
| 830 'MyMethod', | |
| 831 None, | |
| 832 ast.ParameterList(ast.Parameter('a', None, 'string')), | |
| 833 ast.ParameterList([ast.Parameter('a', None, 'int32'), | |
| 834 ast.Parameter('b', None, 'bool')]))))]) | |
| 835 self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) | |
| 836 | |
| 837 def testInvalidMethods(self): | |
| 838 """Tests that invalid method declarations are correctly detected.""" | |
| 839 | |
| 840 # No trailing commas. | |
| 841 source1 = """\ | |
| 842 interface MyInterface { | |
| 843 MyMethod(string a,); | |
| 844 }; | |
| 845 """ | |
| 846 with self.assertRaisesRegexp( | |
| 847 parser.ParseError, | |
| 848 r"^my_file\.mojom:2: Error: Unexpected '\)':\n" | |
| 849 r" *MyMethod\(string a,\);$"): | |
| 850 parser.Parse(source1, "my_file.mojom") | |
| 851 | |
| 852 # No leading commas. | |
| 853 source2 = """\ | |
| 854 interface MyInterface { | |
| 855 MyMethod(, string a); | |
| 856 }; | |
| 857 """ | |
| 858 with self.assertRaisesRegexp( | |
| 859 parser.ParseError, | |
| 860 r"^my_file\.mojom:2: Error: Unexpected ',':\n" | |
| 861 r" *MyMethod\(, string a\);$"): | |
| 862 parser.Parse(source2, "my_file.mojom") | |
| 863 | |
| 864 def testValidInterfaceDefinitions(self): | |
| 865 """Tests all types of definitions that can occur in an interface.""" | |
| 866 | |
| 867 source = """\ | |
| 868 interface MyInterface { | |
| 869 enum MyEnum { VALUE }; | |
| 870 const int32 kMyConst = 123; | |
| 871 MyMethod(int32 x) => (MyEnum y); | |
| 872 }; | |
| 873 """ | |
| 874 expected = ast.Mojom( | |
| 875 None, | |
| 876 ast.ImportList(), | |
| 877 [ast.Interface( | |
| 878 'MyInterface', | |
| 879 None, | |
| 880 ast.InterfaceBody( | |
| 881 [ast.Enum('MyEnum', | |
| 882 ast.EnumValueList(ast.EnumValue('VALUE', None))), | |
| 883 ast.Const('kMyConst', 'int32', '123'), | |
| 884 ast.Method( | |
| 885 'MyMethod', | |
| 886 None, | |
| 887 ast.ParameterList(ast.Parameter('x', None, 'int32')), | |
| 888 ast.ParameterList(ast.Parameter('y', None, 'MyEnum')))]))]) | |
| 889 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 890 | |
| 891 def testInvalidInterfaceDefinitions(self): | |
| 892 """Tests that definitions that aren't allowed in an interface are correctly | |
| 893 detected.""" | |
| 894 | |
| 895 source1 = """\ | |
| 896 interface MyInterface { | |
| 897 struct MyStruct { | |
| 898 int32 a; | |
| 899 }; | |
| 900 }; | |
| 901 """ | |
| 902 with self.assertRaisesRegexp( | |
| 903 parser.ParseError, | |
| 904 r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" | |
| 905 r" *struct MyStruct {$"): | |
| 906 parser.Parse(source1, "my_file.mojom") | |
| 907 | |
| 908 source2 = """\ | |
| 909 interface MyInterface { | |
| 910 interface MyInnerInterface { | |
| 911 MyMethod(int32 x); | |
| 912 }; | |
| 913 }; | |
| 914 """ | |
| 915 with self.assertRaisesRegexp( | |
| 916 parser.ParseError, | |
| 917 r"^my_file\.mojom:2: Error: Unexpected 'interface':\n" | |
| 918 r" *interface MyInnerInterface {$"): | |
| 919 parser.Parse(source2, "my_file.mojom") | |
| 920 | |
| 921 source3 = """\ | |
| 922 interface MyInterface { | |
| 923 int32 my_field; | |
| 924 }; | |
| 925 """ | |
| 926 # The parser thinks that "int32" is a plausible name for a method, so it's | |
| 927 # "my_field" that gives it away. | |
| 928 with self.assertRaisesRegexp( | |
| 929 parser.ParseError, | |
| 930 r"^my_file\.mojom:2: Error: Unexpected 'my_field':\n" | |
| 931 r" *int32 my_field;$"): | |
| 932 parser.Parse(source3, "my_file.mojom") | |
| 933 | |
| 934 def testValidAttributes(self): | |
| 935 """Tests parsing attributes (and attribute lists).""" | |
| 936 | |
| 937 # Note: We use structs because they have (optional) attribute lists. | |
| 938 | |
| 939 # Empty attribute list. | |
| 940 source1 = "[] struct MyStruct {};" | |
| 941 expected1 = ast.Mojom( | |
| 942 None, | |
| 943 ast.ImportList(), | |
| 944 [ast.Struct('MyStruct', ast.AttributeList(), ast.StructBody())]) | |
| 945 self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) | |
| 946 | |
| 947 # One-element attribute list, with name value. | |
| 948 source2 = "[MyAttribute=MyName] struct MyStruct {};" | |
| 949 expected2 = ast.Mojom( | |
| 950 None, | |
| 951 ast.ImportList(), | |
| 952 [ast.Struct( | |
| 953 'MyStruct', | |
| 954 ast.AttributeList(ast.Attribute("MyAttribute", "MyName")), | |
| 955 ast.StructBody())]) | |
| 956 self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) | |
| 957 | |
| 958 # Two-element attribute list, with one string value and one integer value. | |
| 959 source3 = "[MyAttribute1 = \"hello\", MyAttribute2 = 5] struct MyStruct {};" | |
| 960 expected3 = ast.Mojom( | |
| 961 None, | |
| 962 ast.ImportList(), | |
| 963 [ast.Struct( | |
| 964 'MyStruct', | |
| 965 ast.AttributeList([ast.Attribute("MyAttribute1", "hello"), | |
| 966 ast.Attribute("MyAttribute2", 5)]), | |
| 967 ast.StructBody())]) | |
| 968 self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) | |
| 969 | |
| 970 # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()| | |
| 971 # literal (non-name) values, which is extremely dubious.) | |
| 972 | |
| 973 def testInvalidAttributes(self): | |
| 974 """Tests that invalid attributes and attribute lists are correctly | |
| 975 detected.""" | |
| 976 | |
| 977 # Trailing commas not allowed. | |
| 978 source1 = "[MyAttribute=MyName,] struct MyStruct {};" | |
| 979 with self.assertRaisesRegexp( | |
| 980 parser.ParseError, | |
| 981 r"^my_file\.mojom:1: Error: Unexpected '\]':\n" | |
| 982 r"\[MyAttribute=MyName,\] struct MyStruct {};$"): | |
| 983 parser.Parse(source1, "my_file.mojom") | |
| 984 | |
| 985 # Missing value. | |
| 986 source2 = "[MyAttribute=] struct MyStruct {};" | |
| 987 with self.assertRaisesRegexp( | |
| 988 parser.ParseError, | |
| 989 r"^my_file\.mojom:1: Error: Unexpected '\]':\n" | |
| 990 r"\[MyAttribute=\] struct MyStruct {};$"): | |
| 991 parser.Parse(source2, "my_file.mojom") | |
| 992 | |
| 993 # Missing key. | |
| 994 source3 = "[=MyName] struct MyStruct {};" | |
| 995 with self.assertRaisesRegexp( | |
| 996 parser.ParseError, | |
| 997 r"^my_file\.mojom:1: Error: Unexpected '=':\n" | |
| 998 r"\[=MyName\] struct MyStruct {};$"): | |
| 999 parser.Parse(source3, "my_file.mojom") | |
| 1000 | |
| 1001 def testValidImports(self): | |
| 1002 """Tests parsing import statements.""" | |
| 1003 | |
| 1004 # One import (no module statement). | |
| 1005 source1 = "import \"somedir/my.mojom\";" | |
| 1006 expected1 = ast.Mojom( | |
| 1007 None, | |
| 1008 ast.ImportList(ast.Import("somedir/my.mojom")), | |
| 1009 []) | |
| 1010 self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1) | |
| 1011 | |
| 1012 # Two imports (no module statement). | |
| 1013 source2 = """\ | |
| 1014 import "somedir/my1.mojom"; | |
| 1015 import "somedir/my2.mojom"; | |
| 1016 """ | |
| 1017 expected2 = ast.Mojom( | |
| 1018 None, | |
| 1019 ast.ImportList([ast.Import("somedir/my1.mojom"), | |
| 1020 ast.Import("somedir/my2.mojom")]), | |
| 1021 []) | |
| 1022 self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2) | |
| 1023 | |
| 1024 # Imports with module statement. | |
| 1025 source3 = """\ | |
| 1026 module my_module; | |
| 1027 import "somedir/my1.mojom"; | |
| 1028 import "somedir/my2.mojom"; | |
| 1029 """ | |
| 1030 expected3 = ast.Mojom( | |
| 1031 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 1032 ast.ImportList([ast.Import("somedir/my1.mojom"), | |
| 1033 ast.Import("somedir/my2.mojom")]), | |
| 1034 []) | |
| 1035 self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3) | |
| 1036 | |
| 1037 def testInvalidImports(self): | |
| 1038 """Tests that invalid import statements are correctly detected.""" | |
| 1039 | |
| 1040 source1 = """\ | |
| 1041 // Make the error occur on line 2. | |
| 1042 import invalid | |
| 1043 """ | |
| 1044 with self.assertRaisesRegexp( | |
| 1045 parser.ParseError, | |
| 1046 r"^my_file\.mojom:2: Error: Unexpected 'invalid':\n" | |
| 1047 r" *import invalid$"): | |
| 1048 parser.Parse(source1, "my_file.mojom") | |
| 1049 | |
| 1050 source2 = """\ | |
| 1051 import // Missing string. | |
| 1052 struct MyStruct { | |
| 1053 int32 a; | |
| 1054 }; | |
| 1055 """ | |
| 1056 with self.assertRaisesRegexp( | |
| 1057 parser.ParseError, | |
| 1058 r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" | |
| 1059 r" *struct MyStruct {$"): | |
| 1060 parser.Parse(source2, "my_file.mojom") | |
| 1061 | |
| 1062 source3 = """\ | |
| 1063 import "foo.mojom" // Missing semicolon. | |
| 1064 struct MyStruct { | |
| 1065 int32 a; | |
| 1066 }; | |
| 1067 """ | |
| 1068 with self.assertRaisesRegexp( | |
| 1069 parser.ParseError, | |
| 1070 r"^my_file\.mojom:2: Error: Unexpected 'struct':\n" | |
| 1071 r" *struct MyStruct {$"): | |
| 1072 parser.Parse(source3, "my_file.mojom") | |
| 1073 | |
| 1074 def testValidNullableTypes(self): | |
| 1075 """Tests parsing nullable types.""" | |
| 1076 | |
| 1077 source = """\ | |
| 1078 struct MyStruct { | |
| 1079 int32? a; // This is actually invalid, but handled at a different | |
| 1080 // level. | |
| 1081 string? b; | |
| 1082 array<int32> ? c; | |
| 1083 array<string ? > ? d; | |
| 1084 array<array<int32>?>? e; | |
| 1085 array<int32, 1>? f; | |
| 1086 array<string?, 1>? g; | |
| 1087 some_struct? h; | |
| 1088 handle? i; | |
| 1089 handle<data_pipe_consumer>? j; | |
| 1090 handle<data_pipe_producer>? k; | |
| 1091 handle<message_pipe>? l; | |
| 1092 handle<shared_buffer>? m; | |
| 1093 some_interface&? n; | |
| 1094 }; | |
| 1095 """ | |
| 1096 expected = ast.Mojom( | |
| 1097 None, | |
| 1098 ast.ImportList(), | |
| 1099 [ast.Struct( | |
| 1100 'MyStruct', | |
| 1101 None, | |
| 1102 ast.StructBody( | |
| 1103 [ast.StructField('a', None, 'int32?', None), | |
| 1104 ast.StructField('b', None, 'string?', None), | |
| 1105 ast.StructField('c', None, 'int32[]?', None), | |
| 1106 ast.StructField('d', None, 'string?[]?', None), | |
| 1107 ast.StructField('e', None, 'int32[]?[]?', None), | |
| 1108 ast.StructField('f', None, 'int32[1]?', None), | |
| 1109 ast.StructField('g', None, 'string?[1]?', None), | |
| 1110 ast.StructField('h', None, 'some_struct?', None), | |
| 1111 ast.StructField('i', None, 'handle?', None), | |
| 1112 ast.StructField('j', None, 'handle<data_pipe_consumer>?', | |
| 1113 None), | |
| 1114 ast.StructField('k', None, 'handle<data_pipe_producer>?', | |
| 1115 None), | |
| 1116 ast.StructField('l', None, 'handle<message_pipe>?', None), | |
| 1117 ast.StructField('m', None, 'handle<shared_buffer>?', None), | |
| 1118 ast.StructField('n', None, 'some_interface&?', None)]))]) | |
| 1119 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected) | |
| 1120 | |
| 1121 def testInvalidNullableTypes(self): | |
| 1122 """Tests that invalid nullable types are correctly detected.""" | |
| 1123 source1 = """\ | |
| 1124 struct MyStruct { | |
| 1125 string?? a; | |
| 1126 }; | |
| 1127 """ | |
| 1128 with self.assertRaisesRegexp( | |
| 1129 parser.ParseError, | |
| 1130 r"^my_file\.mojom:2: Error: Unexpected '\?':\n" | |
| 1131 r" *string\?\? a;$"): | |
| 1132 parser.Parse(source1, "my_file.mojom") | |
| 1133 | |
| 1134 source2 = """\ | |
| 1135 struct MyStruct { | |
| 1136 handle?<data_pipe_consumer> a; | |
| 1137 }; | |
| 1138 """ | |
| 1139 with self.assertRaisesRegexp( | |
| 1140 parser.ParseError, | |
| 1141 r"^my_file\.mojom:2: Error: Unexpected '<':\n" | |
| 1142 r" *handle\?<data_pipe_consumer> a;$"): | |
| 1143 parser.Parse(source2, "my_file.mojom") | |
| 1144 | |
| 1145 source3 = """\ | |
| 1146 struct MyStruct { | |
| 1147 some_interface?& a; | |
| 1148 }; | |
| 1149 """ | |
| 1150 with self.assertRaisesRegexp( | |
| 1151 parser.ParseError, | |
| 1152 r"^my_file\.mojom:2: Error: Unexpected '&':\n" | |
| 1153 r" *some_interface\?& a;$"): | |
| 1154 parser.Parse(source3, "my_file.mojom") | |
| 1155 | |
| 1156 def testSimpleUnion(self): | |
| 1157 """Tests a simple .mojom source that just defines a union.""" | |
| 1158 source = """\ | |
| 1159 module my_module; | |
| 1160 | |
| 1161 union MyUnion { | |
| 1162 int32 a; | |
| 1163 double b; | |
| 1164 }; | |
| 1165 """ | |
| 1166 expected = ast.Mojom( | |
| 1167 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 1168 ast.ImportList(), | |
| 1169 [ast.Union( | |
| 1170 'MyUnion', | |
| 1171 ast.UnionBody([ | |
| 1172 ast.UnionField('a', None, 'int32'), | |
| 1173 ast.UnionField('b', None, 'double') | |
| 1174 ]))]) | |
| 1175 actual = parser.Parse(source, "my_file.mojom") | |
| 1176 self.assertEquals(actual, expected) | |
| 1177 | |
| 1178 def testUnionWithOrdinals(self): | |
| 1179 """Test that ordinals are assigned to fields.""" | |
| 1180 source = """\ | |
| 1181 module my_module; | |
| 1182 | |
| 1183 union MyUnion { | |
| 1184 int32 a @10; | |
| 1185 double b @30; | |
| 1186 }; | |
| 1187 """ | |
| 1188 expected = ast.Mojom( | |
| 1189 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 1190 ast.ImportList(), | |
| 1191 [ast.Union( | |
| 1192 'MyUnion', | |
| 1193 ast.UnionBody([ | |
| 1194 ast.UnionField('a', ast.Ordinal(10), 'int32'), | |
| 1195 ast.UnionField('b', ast.Ordinal(30), 'double') | |
| 1196 ]))]) | |
| 1197 actual = parser.Parse(source, "my_file.mojom") | |
| 1198 self.assertEquals(actual, expected) | |
| 1199 | |
| 1200 def testUnionWithStructMembers(self): | |
| 1201 """Test that struct members are accepted.""" | |
| 1202 source = """\ | |
| 1203 module my_module; | |
| 1204 | |
| 1205 union MyUnion { | |
| 1206 SomeStruct s; | |
| 1207 }; | |
| 1208 """ | |
| 1209 expected = ast.Mojom( | |
| 1210 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 1211 ast.ImportList(), | |
| 1212 [ast.Union( | |
| 1213 'MyUnion', | |
| 1214 ast.UnionBody([ | |
| 1215 ast.UnionField('s', None, 'SomeStruct') | |
| 1216 ]))]) | |
| 1217 actual = parser.Parse(source, "my_file.mojom") | |
| 1218 self.assertEquals(actual, expected) | |
| 1219 | |
| 1220 def testUnionWithArrayMember(self): | |
| 1221 """Test that array members are accepted.""" | |
| 1222 source = """\ | |
| 1223 module my_module; | |
| 1224 | |
| 1225 union MyUnion { | |
| 1226 array<int32> a; | |
| 1227 }; | |
| 1228 """ | |
| 1229 expected = ast.Mojom( | |
| 1230 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 1231 ast.ImportList(), | |
| 1232 [ast.Union( | |
| 1233 'MyUnion', | |
| 1234 ast.UnionBody([ | |
| 1235 ast.UnionField('a', None, 'int32[]') | |
| 1236 ]))]) | |
| 1237 actual = parser.Parse(source, "my_file.mojom") | |
| 1238 self.assertEquals(actual, expected) | |
| 1239 | |
| 1240 def testUnionWithMapMember(self): | |
| 1241 """Test that map members are accepted.""" | |
| 1242 source = """\ | |
| 1243 module my_module; | |
| 1244 | |
| 1245 union MyUnion { | |
| 1246 map<int32, string> m; | |
| 1247 }; | |
| 1248 """ | |
| 1249 expected = ast.Mojom( | |
| 1250 ast.Module(('IDENTIFIER', 'my_module'), None), | |
| 1251 ast.ImportList(), | |
| 1252 [ast.Union( | |
| 1253 'MyUnion', | |
| 1254 ast.UnionBody([ | |
| 1255 ast.UnionField('m', None, 'string{int32}') | |
| 1256 ]))]) | |
| 1257 actual = parser.Parse(source, "my_file.mojom") | |
| 1258 self.assertEquals(actual, expected) | |
| 1259 | |
| 1260 def testUnionDisallowNestedStruct(self): | |
| 1261 """Tests that structs cannot be nested in unions.""" | |
| 1262 source = """\ | |
| 1263 module my_module; | |
| 1264 | |
| 1265 union MyUnion { | |
| 1266 struct MyStruct { | |
| 1267 int32 a; | |
| 1268 }; | |
| 1269 }; | |
| 1270 """ | |
| 1271 with self.assertRaisesRegexp( | |
| 1272 parser.ParseError, | |
| 1273 r"^my_file\.mojom:4: Error: Unexpected 'struct':\n" | |
| 1274 r" *struct MyStruct {$"): | |
| 1275 parser.Parse(source, "my_file.mojom") | |
| 1276 | |
| 1277 def testUnionDisallowNestedInterfaces(self): | |
| 1278 """Tests that interfaces cannot be nested in unions.""" | |
| 1279 source = """\ | |
| 1280 module my_module; | |
| 1281 | |
| 1282 union MyUnion { | |
| 1283 interface MyInterface { | |
| 1284 MyMethod(int32 a); | |
| 1285 }; | |
| 1286 }; | |
| 1287 """ | |
| 1288 with self.assertRaisesRegexp( | |
| 1289 parser.ParseError, | |
| 1290 r"^my_file\.mojom:4: Error: Unexpected 'interface':\n" | |
| 1291 r" *interface MyInterface {$"): | |
| 1292 parser.Parse(source, "my_file.mojom") | |
| 1293 | |
| 1294 def testUnionDisallowNestedUnion(self): | |
| 1295 """Tests that unions cannot be nested in unions.""" | |
| 1296 source = """\ | |
| 1297 module my_module; | |
| 1298 | |
| 1299 union MyUnion { | |
| 1300 union MyOtherUnion { | |
| 1301 int32 a; | |
| 1302 }; | |
| 1303 }; | |
| 1304 """ | |
| 1305 with self.assertRaisesRegexp( | |
| 1306 parser.ParseError, | |
| 1307 r"^my_file\.mojom:4: Error: Unexpected 'union':\n" | |
| 1308 r" *union MyOtherUnion {$"): | |
| 1309 parser.Parse(source, "my_file.mojom") | |
| 1310 | |
| 1311 def testUnionDisallowNestedEnum(self): | |
| 1312 """Tests that enums cannot be nested in unions.""" | |
| 1313 source = """\ | |
| 1314 module my_module; | |
| 1315 | |
| 1316 union MyUnion { | |
| 1317 enum MyEnum { | |
| 1318 A, | |
| 1319 }; | |
| 1320 }; | |
| 1321 """ | |
| 1322 with self.assertRaisesRegexp( | |
| 1323 parser.ParseError, | |
| 1324 r"^my_file\.mojom:4: Error: Unexpected 'enum':\n" | |
| 1325 r" *enum MyEnum {$"): | |
| 1326 parser.Parse(source, "my_file.mojom") | |
| 1327 | |
| 1328 | |
| 1329 if __name__ == "__main__": | |
| 1330 unittest.main() | |
| OLD | NEW |