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 |