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.
10 # Disable lint check for finding modules:
11 # pylint: disable=F0401
13 def _GetDirAbove(dirname):
14 """Returns the directory "above" this file containing |dirname| (which must
15 also be "above" this file)."""
16 path = os.path.abspath(__file__)
18 path, tail = os.path.split(path)
24 imp.find_module("mojom")
26 sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
27 import mojom.parse.ast as ast
28 import mojom.parse.lexer as lexer
29 import mojom.parse.parser as parser
32 class ParserTest(unittest.TestCase):
33 """Tests |parser.Parse()|."""
35 def testTrivialValidSource(self):
36 """Tests a trivial, but valid, .mojom source."""
43 self.assertEquals(parser.Parse(source, "my_file.mojom"),
44 [("MODULE", "my_module", None, None)])
46 def testSourceWithCrLfs(self):
47 """Tests a .mojom source with CR-LFs instead of LFs."""
48 source = "// This is a comment.\r\n\r\nmodule my_module {\r\n}\r\n"
49 self.assertEquals(parser.Parse(source, "my_file.mojom"),
50 [("MODULE", "my_module", None, None)])
52 def testUnexpectedEOF(self):
53 """Tests a "truncated" .mojom source."""
59 with self.assertRaisesRegexp(
61 r"^my_file\.mojom: Error: Unexpected end of file$"):
62 parser.Parse(source, "my_file.mojom")
64 def testCommentLineNumbers(self):
65 """Tests that line numbers are correctly tracked when comments are
68 // Isolated C++-style comments.
73 with self.assertRaisesRegexp(
75 r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\nasdf1$"):
76 parser.Parse(source1, "my_file.mojom")
79 // Consecutive C++-style comments.
90 with self.assertRaisesRegexp(
92 r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\nasdf2$"):
93 parser.Parse(source2, "my_file.mojom")
96 /* Single-line C-style comments. */
102 with self.assertRaisesRegexp(
104 r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\nasdf3$"):
105 parser.Parse(source3, "my_file.mojom")
108 /* Multi-line C-style comments.
119 with self.assertRaisesRegexp(
121 r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\nasdf4$"):
122 parser.Parse(source4, "my_file.mojom")
125 def testSimpleStruct(self):
126 """Tests a simple .mojom source that just defines a struct."""
135 } // module my_module
144 [('FIELD', 'int32', 'a', ast.Ordinal(None), None),
145 ('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
146 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
148 def testSimpleStructWithoutModule(self):
149 """Tests a simple struct without an enclosing module."""
163 [('FIELD', 'int32', 'a', ast.Ordinal(None), None),
164 ('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
165 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
167 def testMissingModuleName(self):
168 """Tests an (invalid) .mojom with a missing module name."""
170 // Missing module name.
177 with self.assertRaisesRegexp(
179 r"^my_file\.mojom:2: Error: Unexpected '{':\nmodule {$"):
180 parser.Parse(source1, "my_file.mojom")
182 # Another similar case, but make sure that line-number tracking/reporting
186 // This line intentionally left unblank.
191 with self.assertRaisesRegexp(
193 r"^my_file\.mojom:4: Error: Unexpected '{':\n{$"):
194 parser.Parse(source2, "my_file.mojom")
196 def testEnumInitializers(self):
197 """Tests an enum with simple initialized values."""
216 [('ENUM_FIELD', 'MY_ENUM_NEG1', '-1'),
217 ('ENUM_FIELD', 'MY_ENUM_ZERO', '0'),
218 ('ENUM_FIELD', 'MY_ENUM_1', '+1'),
219 ('ENUM_FIELD', 'MY_ENUM_2', None)])])]
220 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
223 """Tests some constants and struct memebers initialized with them."""
228 const int8 kNumber = -1;
229 int8 number@0 = kNumber;
240 [('CONST', 'int8', 'kNumber', '-1'),
241 ('FIELD', 'int8', 'number',
242 ast.Ordinal(0), ('IDENTIFIER', 'kNumber'))])])]
243 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
245 def testNoConditionals(self):
246 """Tests that ?: is not allowed."""
251 MY_ENUM_1 = 1 ? 2 : 3
256 with self.assertRaisesRegexp(
258 r"^my_file\.mojom:4: Error: Illegal character '\?'$"):
259 parser.Parse(source, "my_file.mojom")
261 def testSimpleOrdinals(self):
262 """Tests that (valid) ordinal values are scanned correctly."""
266 // This isn't actually valid .mojom, but the problem (missing ordinals) should
267 // be handled at a different level.
276 int32 a1234567890 @1234567890;
279 } // module my_module
288 [('FIELD', 'int32', 'a0', ast.Ordinal(0), None),
289 ('FIELD', 'int32', 'a1', ast.Ordinal(1), None),
290 ('FIELD', 'int32', 'a2', ast.Ordinal(2), None),
291 ('FIELD', 'int32', 'a9', ast.Ordinal(9), None),
292 ('FIELD', 'int32', 'a10', ast.Ordinal(10), None),
293 ('FIELD', 'int32', 'a11', ast.Ordinal(11), None),
294 ('FIELD', 'int32', 'a29', ast.Ordinal(29), None),
295 ('FIELD', 'int32', 'a1234567890', ast.Ordinal(1234567890), None)])])]
296 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
298 def testInvalidOrdinals(self):
299 """Tests that (lexically) invalid ordinals are correctly detected."""
307 } // module my_module
309 with self.assertRaisesRegexp(
311 r"^my_file\.mojom:4: Error: Missing ordinal value$"):
312 parser.Parse(source1, "my_file.mojom")
321 } // module my_module
323 with self.assertRaisesRegexp(
325 r"^my_file\.mojom:4: Error: "
326 r"Octal and hexadecimal ordinal values not allowed$"):
327 parser.Parse(source2, "my_file.mojom")
330 module my_module { struct MyStruct { int32 a_invalid_octal@08; }; }
332 with self.assertRaisesRegexp(
334 r"^my_file\.mojom:1: Error: "
335 r"Octal and hexadecimal ordinal values not allowed$"):
336 parser.Parse(source3, "my_file.mojom")
339 module my_module { struct MyStruct { int32 a_hex@0x1aB9; }; }
341 with self.assertRaisesRegexp(
343 r"^my_file\.mojom:1: Error: "
344 r"Octal and hexadecimal ordinal values not allowed$"):
345 parser.Parse(source4, "my_file.mojom")
348 module my_module { struct MyStruct { int32 a_hex@0X0; }; }
350 with self.assertRaisesRegexp(
352 r"^my_file\.mojom:1: Error: "
353 r"Octal and hexadecimal ordinal values not allowed$"):
354 parser.Parse(source5, "my_file.mojom")
358 int32 a_too_big@999999999999;
361 with self.assertRaisesRegexp(
363 r"^my_file\.mojom:2: Error: "
364 r"Ordinal value 999999999999 too large:\n"
365 r" int32 a_too_big@999999999999;$"):
366 parser.Parse(source6, "my_file.mojom")
368 def testNestedNamespace(self):
369 """Tests that "nested" namespaces work."""
386 [('FIELD', 'int32', 'a', ast.Ordinal(None), None)])])]
387 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
389 def testValidHandleTypes(self):
390 """Tests (valid) handle types."""
394 handle<data_pipe_consumer> b;
395 handle <data_pipe_producer> c;
396 handle < message_pipe > d;
409 [('FIELD', 'handle', 'a', ast.Ordinal(None), None),
410 ('FIELD', 'handle<data_pipe_consumer>', 'b', ast.Ordinal(None), None),
411 ('FIELD', 'handle<data_pipe_producer>', 'c', ast.Ordinal(None), None),
412 ('FIELD', 'handle<message_pipe>', 'd', ast.Ordinal(None), None),
413 ('FIELD', 'handle<shared_buffer>', 'e', ast.Ordinal(None), None)])])]
414 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
416 def testInvalidHandleType(self):
417 """Tests an invalid (unknown) handle type."""
420 handle<wtf_is_this> foo;
423 with self.assertRaisesRegexp(
425 r"^my_file\.mojom:2: Error: "
426 r"Invalid handle type 'wtf_is_this':\n"
427 r" handle<wtf_is_this> foo;$"):
428 parser.Parse(source, "my_file.mojom")
430 def testValidDefaultValues(self):
431 """Tests default values that are valid (to the parser)."""
443 uint32 a9 = -0x12cD3;
444 uint32 a10 = +0x12CD3;
452 double a18 = 1.23E10;
455 double a21 = -1.23E10;
456 double a22 = +.123E10;
466 [('FIELD', 'int16', 'a0', ast.Ordinal(None), '0'),
467 ('FIELD', 'uint16', 'a1', ast.Ordinal(None), '0x0'),
468 ('FIELD', 'uint16', 'a2', ast.Ordinal(None), '0x00'),
469 ('FIELD', 'uint16', 'a3', ast.Ordinal(None), '0x01'),
470 ('FIELD', 'uint16', 'a4', ast.Ordinal(None), '0xcd'),
471 ('FIELD', 'int32', 'a5' , ast.Ordinal(None), '12345'),
472 ('FIELD', 'int64', 'a6', ast.Ordinal(None), '-12345'),
473 ('FIELD', 'int64', 'a7', ast.Ordinal(None), '+12345'),
474 ('FIELD', 'uint32', 'a8', ast.Ordinal(None), '0x12cd3'),
475 ('FIELD', 'uint32', 'a9', ast.Ordinal(None), '-0x12cD3'),
476 ('FIELD', 'uint32', 'a10', ast.Ordinal(None), '+0x12CD3'),
477 ('FIELD', 'bool', 'a11', ast.Ordinal(None), 'true'),
478 ('FIELD', 'bool', 'a12', ast.Ordinal(None), 'false'),
479 ('FIELD', 'float', 'a13', ast.Ordinal(None), '1.2345'),
480 ('FIELD', 'float', 'a14', ast.Ordinal(None), '-1.2345'),
481 ('FIELD', 'float', 'a15', ast.Ordinal(None), '+1.2345'),
482 ('FIELD', 'float', 'a16', ast.Ordinal(None), '123.'),
483 ('FIELD', 'float', 'a17', ast.Ordinal(None), '.123'),
484 ('FIELD', 'double', 'a18', ast.Ordinal(None), '1.23E10'),
485 ('FIELD', 'double', 'a19', ast.Ordinal(None), '1.E-10'),
486 ('FIELD', 'double', 'a20', ast.Ordinal(None), '.5E+10'),
487 ('FIELD', 'double', 'a21', ast.Ordinal(None), '-1.23E10'),
488 ('FIELD', 'double', 'a22', ast.Ordinal(None), '+.123E10')])])]
489 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
492 if __name__ == "__main__":