Home | History | Annotate | Download | only in parse
      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 # Disable lint check for finding modules:
     11 # pylint: disable=F0401
     12 
     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__)
     17   while True:
     18     path, tail = os.path.split(path)
     19     assert tail
     20     if tail == dirname:
     21       return path
     22 
     23 try:
     24   imp.find_module("mojom")
     25 except ImportError:
     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
     30 
     31 
     32 class ParserTest(unittest.TestCase):
     33   """Tests |parser.Parse()|."""
     34 
     35   def testTrivialValidSource(self):
     36     """Tests a trivial, but valid, .mojom source."""
     37     source = """\
     38 // This is a comment.
     39 
     40 module my_module {
     41 }
     42 """
     43     self.assertEquals(parser.Parse(source, "my_file.mojom"),
     44                       [("MODULE", "my_module", None, None)])
     45 
     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)])
     51 
     52   def testUnexpectedEOF(self):
     53     """Tests a "truncated" .mojom source."""
     54     source = """\
     55 // This is a comment.
     56 
     57 module my_module {
     58 """
     59     with self.assertRaisesRegexp(
     60         parser.ParseError,
     61         r"^my_file\.mojom: Error: Unexpected end of file$"):
     62       parser.Parse(source, "my_file.mojom")
     63 
     64   def testCommentLineNumbers(self):
     65     """Tests that line numbers are correctly tracked when comments are
     66     present."""
     67     source1 = """\
     68 // Isolated C++-style comments.
     69 
     70 // Foo.
     71 asdf1
     72 """
     73     with self.assertRaisesRegexp(
     74         parser.ParseError,
     75         r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\nasdf1$"):
     76       parser.Parse(source1, "my_file.mojom")
     77 
     78     source2 = """\
     79 // Consecutive C++-style comments.
     80 // Foo.
     81   // Bar.
     82 
     83 struct Yada {  // Baz.
     84 // Quux.
     85   int32 x;
     86 };
     87 
     88 asdf2
     89 """
     90     with self.assertRaisesRegexp(
     91         parser.ParseError,
     92         r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\nasdf2$"):
     93       parser.Parse(source2, "my_file.mojom")
     94 
     95     source3 = """\
     96 /* Single-line C-style comments. */
     97 /* Foobar. */
     98 
     99 /* Baz. */
    100 asdf3
    101 """
    102     with self.assertRaisesRegexp(
    103         parser.ParseError,
    104         r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\nasdf3$"):
    105       parser.Parse(source3, "my_file.mojom")
    106 
    107     source4 = """\
    108 /* Multi-line C-style comments.
    109 */
    110 /*
    111 Foo.
    112 Bar.
    113 */
    114 
    115 /* Baz
    116    Quux. */
    117 asdf4
    118 """
    119     with self.assertRaisesRegexp(
    120         parser.ParseError,
    121         r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\nasdf4$"):
    122       parser.Parse(source4, "my_file.mojom")
    123 
    124 
    125   def testSimpleStruct(self):
    126     """Tests a simple .mojom source that just defines a struct."""
    127     source = """\
    128 module my_module {
    129 
    130 struct MyStruct {
    131   int32 a;
    132   double b;
    133 };
    134 
    135 }  // module my_module
    136 """
    137     expected = \
    138 [('MODULE',
    139   'my_module',
    140   None,
    141   [('STRUCT',
    142     'MyStruct',
    143     None,
    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)
    147 
    148   def testSimpleStructWithoutModule(self):
    149     """Tests a simple struct without an enclosing module."""
    150     source = """\
    151 struct MyStruct {
    152   int32 a;
    153   double b;
    154 };
    155 """
    156     expected = \
    157 [('MODULE',
    158   '',
    159   None,
    160   [('STRUCT',
    161     'MyStruct',
    162     None,
    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)
    166 
    167   def testMissingModuleName(self):
    168     """Tests an (invalid) .mojom with a missing module name."""
    169     source1 = """\
    170 // Missing module name.
    171 module {
    172 struct MyStruct {
    173   int32 a;
    174 };
    175 }
    176 """
    177     with self.assertRaisesRegexp(
    178         parser.ParseError,
    179         r"^my_file\.mojom:2: Error: Unexpected '{':\nmodule {$"):
    180       parser.Parse(source1, "my_file.mojom")
    181 
    182     # Another similar case, but make sure that line-number tracking/reporting
    183     # is correct.
    184     source2 = """\
    185 module
    186 // This line intentionally left unblank.
    187 
    188 {
    189 }
    190 """
    191     with self.assertRaisesRegexp(
    192         parser.ParseError,
    193         r"^my_file\.mojom:4: Error: Unexpected '{':\n{$"):
    194       parser.Parse(source2, "my_file.mojom")
    195 
    196   def testEnumInitializers(self):
    197     """Tests an enum with simple initialized values."""
    198     source = """\
    199 module my_module {
    200 
    201 enum MyEnum {
    202   MY_ENUM_NEG1 = -1,
    203   MY_ENUM_ZERO = 0,
    204   MY_ENUM_1 = +1,
    205   MY_ENUM_2,
    206 };
    207 
    208 }  // my_module
    209 """
    210     expected = \
    211 [('MODULE',
    212   'my_module',
    213   None,
    214   [('ENUM',
    215     'MyEnum',
    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)
    221 
    222   def testConst(self):
    223     """Tests some constants and struct memebers initialized with them."""
    224     source = """\
    225 module my_module {
    226 
    227 struct MyStruct {
    228   const int8 kNumber = -1;
    229   int8 number@0 = kNumber;
    230 };
    231 
    232 }  // my_module
    233 """
    234     expected = \
    235 [('MODULE',
    236   'my_module',
    237   None,
    238   [('STRUCT',
    239     'MyStruct', None,
    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)
    244 
    245   def testNoConditionals(self):
    246     """Tests that ?: is not allowed."""
    247     source = """\
    248 module my_module {
    249 
    250 enum MyEnum {
    251   MY_ENUM_1 = 1 ? 2 : 3
    252 };
    253 
    254 }  // my_module
    255 """
    256     with self.assertRaisesRegexp(
    257         lexer.LexError,
    258         r"^my_file\.mojom:4: Error: Illegal character '\?'$"):
    259       parser.Parse(source, "my_file.mojom")
    260 
    261   def testSimpleOrdinals(self):
    262     """Tests that (valid) ordinal values are scanned correctly."""
    263     source = """\
    264 module my_module {
    265 
    266 // This isn't actually valid .mojom, but the problem (missing ordinals) should
    267 // be handled at a different level.
    268 struct MyStruct {
    269   int32 a0@0;
    270   int32 a1@1;
    271   int32 a2@2;
    272   int32 a9@9;
    273   int32 a10 @10;
    274   int32 a11 @11;
    275   int32 a29 @29;
    276   int32 a1234567890 @1234567890;
    277 };
    278 
    279 }  // module my_module
    280 """
    281     expected = \
    282 [('MODULE',
    283   'my_module',
    284   None,
    285   [('STRUCT',
    286     'MyStruct',
    287     None,
    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)
    297 
    298   def testInvalidOrdinals(self):
    299     """Tests that (lexically) invalid ordinals are correctly detected."""
    300     source1 = """\
    301 module my_module {
    302 
    303 struct MyStruct {
    304   int32 a_missing@;
    305 };
    306 
    307 }  // module my_module
    308 """
    309     with self.assertRaisesRegexp(
    310         lexer.LexError,
    311         r"^my_file\.mojom:4: Error: Missing ordinal value$"):
    312       parser.Parse(source1, "my_file.mojom")
    313 
    314     source2 = """\
    315 module my_module {
    316 
    317 struct MyStruct {
    318   int32 a_octal@01;
    319 };
    320 
    321 }  // module my_module
    322 """
    323     with self.assertRaisesRegexp(
    324         lexer.LexError,
    325         r"^my_file\.mojom:4: Error: "
    326             r"Octal and hexadecimal ordinal values not allowed$"):
    327       parser.Parse(source2, "my_file.mojom")
    328 
    329     source3 = """\
    330 module my_module { struct MyStruct { int32 a_invalid_octal@08; }; }
    331 """
    332     with self.assertRaisesRegexp(
    333         lexer.LexError,
    334         r"^my_file\.mojom:1: Error: "
    335             r"Octal and hexadecimal ordinal values not allowed$"):
    336       parser.Parse(source3, "my_file.mojom")
    337 
    338     source4 = """\
    339 module my_module { struct MyStruct { int32 a_hex@0x1aB9; }; }
    340 """
    341     with self.assertRaisesRegexp(
    342         lexer.LexError,
    343         r"^my_file\.mojom:1: Error: "
    344             r"Octal and hexadecimal ordinal values not allowed$"):
    345       parser.Parse(source4, "my_file.mojom")
    346 
    347     source5 = """\
    348 module my_module { struct MyStruct { int32 a_hex@0X0; }; }
    349 """
    350     with self.assertRaisesRegexp(
    351         lexer.LexError,
    352         r"^my_file\.mojom:1: Error: "
    353             r"Octal and hexadecimal ordinal values not allowed$"):
    354       parser.Parse(source5, "my_file.mojom")
    355 
    356     source6 = """\
    357 struct MyStruct {
    358   int32 a_too_big@999999999999;
    359 };
    360 """
    361     with self.assertRaisesRegexp(
    362         parser.ParseError,
    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")
    367 
    368   def testNestedNamespace(self):
    369     """Tests that "nested" namespaces work."""
    370     source = """\
    371 module my.mod {
    372 
    373 struct MyStruct {
    374   int32 a;
    375 };
    376 
    377 }  // module my.mod
    378 """
    379     expected = \
    380 [('MODULE',
    381   'my.mod',
    382   None,
    383   [('STRUCT',
    384     'MyStruct',
    385     None,
    386     [('FIELD', 'int32', 'a', ast.Ordinal(None), None)])])]
    387     self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
    388 
    389   def testValidHandleTypes(self):
    390     """Tests (valid) handle types."""
    391     source = """\
    392 struct MyStruct {
    393   handle a;
    394   handle<data_pipe_consumer> b;
    395   handle <data_pipe_producer> c;
    396   handle < message_pipe > d;
    397   handle
    398     < shared_buffer
    399     > e;
    400 };
    401 """
    402     expected = \
    403 [('MODULE',
    404   '',
    405   None,
    406   [('STRUCT',
    407     'MyStruct',
    408     None,
    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)
    415 
    416   def testInvalidHandleType(self):
    417     """Tests an invalid (unknown) handle type."""
    418     source = """\
    419 struct MyStruct {
    420   handle<wtf_is_this> foo;
    421 };
    422 """
    423     with self.assertRaisesRegexp(
    424         parser.ParseError,
    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")
    429 
    430   def testValidDefaultValues(self):
    431     """Tests default values that are valid (to the parser)."""
    432     source = """\
    433 struct MyStruct {
    434   int16 a0 = 0;
    435   uint16 a1 = 0x0;
    436   uint16 a2 = 0x00;
    437   uint16 a3 = 0x01;
    438   uint16 a4 = 0xcd;
    439   int32 a5 = 12345;
    440   int64 a6 = -12345;
    441   int64 a7 = +12345;
    442   uint32 a8 = 0x12cd3;
    443   uint32 a9 = -0x12cD3;
    444   uint32 a10 = +0x12CD3;
    445   bool a11 = true;
    446   bool a12 = false;
    447   float a13 = 1.2345;
    448   float a14 = -1.2345;
    449   float a15 = +1.2345;
    450   float a16 = 123.;
    451   float a17 = .123;
    452   double a18 = 1.23E10;
    453   double a19 = 1.E-10;
    454   double a20 = .5E+10;
    455   double a21 = -1.23E10;
    456   double a22 = +.123E10;
    457 };
    458 """
    459     expected = \
    460 [('MODULE',
    461   '',
    462   None,
    463   [('STRUCT',
    464     'MyStruct',
    465     None,
    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)
    490 
    491 
    492 if __name__ == "__main__":
    493   unittest.main()
    494