Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / mojo / public / tools / bindings / pylib / mojom_tests / parse / parser_unittest.py
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 if __name__ == "__main__":
1157   unittest.main()