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)])
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)])
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 testSimpleStruct(self):
65 """Tests a simple .mojom source that just defines a struct."""
82 [('FIELD', 'int32', 'a', ast.Ordinal(None), None),
83 ('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
84 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
86 def testSimpleStructWithoutModule(self):
87 """Tests a simple struct without an enclosing module."""
100 [('FIELD', 'int32', 'a', ast.Ordinal(None), None),
101 ('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
102 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
104 def testMissingModuleName(self):
105 """Tests an (invalid) .mojom with a missing module name."""
107 // Missing module name.
114 with self.assertRaisesRegexp(
116 r"^my_file\.mojom:2: Error: Unexpected '{':\nmodule {$"):
117 parser.Parse(source1, "my_file.mojom")
119 # Another similar case, but make sure that line-number tracking/reporting
123 // This line intentionally left unblank.
128 with self.assertRaisesRegexp(
130 r"^my_file\.mojom:4: Error: Unexpected '{':\n{$"):
131 parser.Parse(source2, "my_file.mojom")
133 def testEnumExpressions(self):
134 """Tests an enum with values calculated using simple expressions."""
142 MY_ENUM_4 = 2 * (1 + 1),
143 MY_ENUM_5 = 1 + 2 * 2,
145 MY_ENUM_7 = 3 | (1 << 2),
147 MY_ENUM_9 = 1 ^ 15 & 8,
148 MY_ENUM_10 = 110 % 100,
159 [('ENUM_FIELD', 'MY_ENUM_1', ('EXPRESSION', ['1'])),
160 ('ENUM_FIELD', 'MY_ENUM_2', ('EXPRESSION', ['1', '+', '1'])),
161 ('ENUM_FIELD', 'MY_ENUM_3', ('EXPRESSION', ['1', '*', '3'])),
165 ['2', '*', '(', ('EXPRESSION', ['1', '+', '1']), ')'])),
168 ('EXPRESSION', ['1', '+', '2', '*', '2'])),
172 ['-', ('EXPRESSION', ['6', '/', '-', ('EXPRESSION', ['2'])])])),
176 ['3', '|', '(', ('EXPRESSION', ['1', '<<', '2']), ')'])),
177 ('ENUM_FIELD', 'MY_ENUM_8', ('EXPRESSION', ['16', '>>', '1'])),
180 ('EXPRESSION', ['1', '^', '15', '&', '8'])),
181 ('ENUM_FIELD', 'MY_ENUM_10', ('EXPRESSION', ['110', '%', '100'])),
184 ('EXPRESSION', ['~', ('EXPRESSION', ['0'])]))])])]
185 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
187 def testNoConditionals(self):
188 """Tests that ?: is not allowed."""
193 MY_ENUM_1 = 1 ? 2 : 3
198 with self.assertRaisesRegexp(
200 r"^my_file\.mojom:4: Error: Illegal character '\?'$"):
201 parser.Parse(source, "my_file.mojom")
203 def testSimpleOrdinals(self):
204 """Tests that (valid) ordinal values are scanned correctly."""
208 // This isn't actually valid .mojom, but the problem (missing ordinals) should
209 // be handled at a different level.
218 int32 a1234567890 @1234567890;
221 } // module my_module
229 [('FIELD', 'int32', 'a0', ast.Ordinal(0), None),
230 ('FIELD', 'int32', 'a1', ast.Ordinal(1), None),
231 ('FIELD', 'int32', 'a2', ast.Ordinal(2), None),
232 ('FIELD', 'int32', 'a9', ast.Ordinal(9), None),
233 ('FIELD', 'int32', 'a10', ast.Ordinal(10), None),
234 ('FIELD', 'int32', 'a11', ast.Ordinal(11), None),
235 ('FIELD', 'int32', 'a29', ast.Ordinal(29), None),
236 ('FIELD', 'int32', 'a1234567890', ast.Ordinal(1234567890), None)])])]
237 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
239 def testInvalidOrdinals(self):
240 """Tests that (lexically) invalid ordinals are correctly detected."""
248 } // module my_module
250 with self.assertRaisesRegexp(
252 r"^my_file\.mojom:4: Error: Missing ordinal value$"):
253 parser.Parse(source1, "my_file.mojom")
262 } // module my_module
264 with self.assertRaisesRegexp(
266 r"^my_file\.mojom:4: Error: "
267 r"Octal and hexadecimal ordinal values not allowed$"):
268 parser.Parse(source2, "my_file.mojom")
271 module my_module { struct MyStruct { int32 a_invalid_octal @08; }; }
273 with self.assertRaisesRegexp(
275 r"^my_file\.mojom:1: Error: "
276 r"Octal and hexadecimal ordinal values not allowed$"):
277 parser.Parse(source3, "my_file.mojom")
280 module my_module { struct MyStruct { int32 a_hex @0x1aB9; }; }
282 with self.assertRaisesRegexp(
284 r"^my_file\.mojom:1: Error: "
285 r"Octal and hexadecimal ordinal values not allowed$"):
286 parser.Parse(source4, "my_file.mojom")
289 module my_module { struct MyStruct { int32 a_hex @0X0; }; }
291 with self.assertRaisesRegexp(
293 r"^my_file\.mojom:1: Error: "
294 r"Octal and hexadecimal ordinal values not allowed$"):
295 parser.Parse(source5, "my_file.mojom")
299 int32 a_too_big @999999999999;
302 with self.assertRaisesRegexp(
304 r"^my_file\.mojom:2: Error: "
305 r"Ordinal value 999999999999 too large:\n"
306 r" int32 a_too_big @999999999999;$"):
307 parser.Parse(source6, "my_file.mojom")
309 def testNestedNamespace(self):
310 """Tests nested namespaces work."""
326 [('FIELD', 'int32', 'a', ast.Ordinal(None), None)])])]
327 self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
330 if __name__ == "__main__":