summaryrefslogtreecommitdiff
path: root/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py')
-rw-r--r--utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py1390
1 files changed, 1390 insertions, 0 deletions
diff --git a/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
new file mode 100644
index 00000000..6d6b7153
--- /dev/null
+++ b/utils/ipc/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
@@ -0,0 +1,1390 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import imp
+import os.path
+import sys
+import unittest
+
+from mojom.parse import ast
+from mojom.parse import lexer
+from mojom.parse import parser
+
+
+class ParserTest(unittest.TestCase):
+ """Tests |parser.Parse()|."""
+
+ def testTrivialValidSource(self):
+ """Tests a trivial, but valid, .mojom source."""
+
+ source = """\
+ // This is a comment.
+
+ module my_module;
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testSourceWithCrLfs(self):
+ """Tests a .mojom source with CR-LFs instead of LFs."""
+
+ source = "// This is a comment.\r\n\r\nmodule my_module;\r\n"
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testUnexpectedEOF(self):
+ """Tests a "truncated" .mojom source."""
+
+ source = """\
+ // This is a comment.
+
+ module my_module
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom: Error: Unexpected end of file$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testCommentLineNumbers(self):
+ """Tests that line numbers are correctly tracked when comments are
+ present."""
+
+ source1 = """\
+ // Isolated C++-style comments.
+
+ // Foo.
+ asdf1
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'asdf1':\n *asdf1$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ // Consecutive C++-style comments.
+ // Foo.
+ // Bar.
+
+ struct Yada { // Baz.
+ // Quux.
+ int32 x;
+ };
+
+ asdf2
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:10: Error: Unexpected 'asdf2':\n *asdf2$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ /* Single-line C-style comments. */
+ /* Foobar. */
+
+ /* Baz. */
+ asdf3
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:5: Error: Unexpected 'asdf3':\n *asdf3$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ source4 = """\
+ /* Multi-line C-style comments.
+ */
+ /*
+ Foo.
+ Bar.
+ */
+
+ /* Baz
+ Quux. */
+ asdf4
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:10: Error: Unexpected 'asdf4':\n *asdf4$"):
+ parser.Parse(source4, "my_file.mojom")
+
+ def testSimpleStruct(self):
+ """Tests a simple .mojom source that just defines a struct."""
+
+ source = """\
+ module my_module;
+
+ struct MyStruct {
+ int32 a;
+ double b;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a', None, None, 'int32', None),
+ ast.StructField('b', None, None, 'double', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testSimpleStructWithoutModule(self):
+ """Tests a simple struct without an explict module statement."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ double b;
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a', None, None, 'int32', None),
+ ast.StructField('b', None, None, 'double', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidStructDefinitions(self):
+ """Tests all types of definitions that can occur in a struct."""
+
+ source = """\
+ struct MyStruct {
+ enum MyEnum { VALUE };
+ const double kMyConst = 1.23;
+ int32 a;
+ SomeOtherStruct b; // Invalidity detected at another stage.
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.Enum('MyEnum', None,
+ ast.EnumValueList(ast.EnumValue('VALUE', None, None))),
+ ast.Const('kMyConst', None, 'double', '1.23'),
+ ast.StructField('a', None, None, 'int32', None),
+ ast.StructField('b', None, None, 'SomeOtherStruct', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidStructDefinitions(self):
+ """Tests that definitions that aren't allowed in a struct are correctly
+ detected."""
+
+ source1 = """\
+ struct MyStruct {
+ MyMethod(int32 a);
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '\(':\n"
+ r" *MyMethod\(int32 a\);$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ struct MyInnerStruct {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyInnerStruct {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ interface MyInterface {
+ MyMethod(int32 a);
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'interface':\n"
+ r" *interface MyInterface {$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testMissingModuleName(self):
+ """Tests an (invalid) .mojom with a missing module name."""
+
+ source1 = """\
+ // Missing module name.
+ module ;
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected ';':\n *module ;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # Another similar case, but make sure that line-number tracking/reporting
+ # is correct.
+ source2 = """\
+ module
+ // This line intentionally left unblank.
+
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:4: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testMultipleModuleStatements(self):
+ """Tests an (invalid) .mojom with multiple module statements."""
+
+ source = """\
+ module foo;
+ module bar;
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Multiple \"module\" statements not "
+ r"allowed:\n *module bar;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testModuleStatementAfterImport(self):
+ """Tests an (invalid) .mojom with a module statement after an import."""
+
+ source = """\
+ import "foo.mojom";
+ module foo;
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: \"module\" statements must precede imports "
+ r"and definitions:\n *module foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testModuleStatementAfterDefinition(self):
+ """Tests an (invalid) .mojom with a module statement after a definition."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ };
+ module foo;
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: \"module\" statements must precede imports "
+ r"and definitions:\n *module foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testImportStatementAfterDefinition(self):
+ """Tests an (invalid) .mojom with an import statement after a definition."""
+
+ source = """\
+ struct MyStruct {
+ int32 a;
+ };
+ import "foo.mojom";
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: \"import\" statements must precede "
+ r"definitions:\n *import \"foo.mojom\";$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testEnums(self):
+ """Tests that enum statements are correctly parsed."""
+
+ source = """\
+ module my_module;
+ enum MyEnum1 { VALUE1, VALUE2 }; // No trailing comma.
+ enum MyEnum2 {
+ VALUE1 = -1,
+ VALUE2 = 0,
+ VALUE3 = + 987, // Check that space is allowed.
+ VALUE4 = 0xAF12,
+ VALUE5 = -0x09bcd,
+ VALUE6 = VALUE5,
+ VALUE7, // Leave trailing comma.
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Enum(
+ 'MyEnum1', None,
+ ast.EnumValueList([
+ ast.EnumValue('VALUE1', None, None),
+ ast.EnumValue('VALUE2', None, None)
+ ])),
+ ast.Enum(
+ 'MyEnum2', None,
+ ast.EnumValueList([
+ ast.EnumValue('VALUE1', None, '-1'),
+ ast.EnumValue('VALUE2', None, '0'),
+ ast.EnumValue('VALUE3', None, '+987'),
+ ast.EnumValue('VALUE4', None, '0xAF12'),
+ ast.EnumValue('VALUE5', None, '-0x09bcd'),
+ ast.EnumValue('VALUE6', None, ('IDENTIFIER', 'VALUE5')),
+ ast.EnumValue('VALUE7', None, None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidEnumInitializers(self):
+ """Tests that invalid enum initializers are correctly detected."""
+
+ # Floating point value.
+ source2 = "enum MyEnum { VALUE = 0.123 };"
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:1: Error: Unexpected '0\.123':\n"
+ r"enum MyEnum { VALUE = 0\.123 };$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ # Boolean value.
+ source2 = "enum MyEnum { VALUE = true };"
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:1: Error: Unexpected 'true':\n"
+ r"enum MyEnum { VALUE = true };$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testConsts(self):
+ """Tests some constants and struct members initialized with them."""
+
+ source = """\
+ module my_module;
+
+ struct MyStruct {
+ const int8 kNumber = -1;
+ int8 number@0 = kNumber;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.Const('kNumber', None, 'int8', '-1'),
+ ast.StructField('number', None, ast.Ordinal(0), 'int8',
+ ('IDENTIFIER', 'kNumber'))
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testNoConditionals(self):
+ """Tests that ?: is not allowed."""
+
+ source = """\
+ module my_module;
+
+ enum MyEnum {
+ MY_ENUM_1 = 1 ? 2 : 3
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:4: Error: Unexpected '\?':\n"
+ r" *MY_ENUM_1 = 1 \? 2 : 3$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testSimpleOrdinals(self):
+ """Tests that (valid) ordinal values are scanned correctly."""
+
+ source = """\
+ module my_module;
+
+ // This isn't actually valid .mojom, but the problem (missing ordinals)
+ // should be handled at a different level.
+ struct MyStruct {
+ int32 a0@0;
+ int32 a1@1;
+ int32 a2@2;
+ int32 a9@9;
+ int32 a10 @10;
+ int32 a11 @11;
+ int32 a29 @29;
+ int32 a1234567890 @1234567890;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a0', None, ast.Ordinal(0), 'int32', None),
+ ast.StructField('a1', None, ast.Ordinal(1), 'int32', None),
+ ast.StructField('a2', None, ast.Ordinal(2), 'int32', None),
+ ast.StructField('a9', None, ast.Ordinal(9), 'int32', None),
+ ast.StructField('a10', None, ast.Ordinal(10), 'int32',
+ None),
+ ast.StructField('a11', None, ast.Ordinal(11), 'int32',
+ None),
+ ast.StructField('a29', None, ast.Ordinal(29), 'int32',
+ None),
+ ast.StructField('a1234567890', None,
+ ast.Ordinal(1234567890), 'int32', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidOrdinals(self):
+ """Tests that (lexically) invalid ordinals are correctly detected."""
+
+ source1 = """\
+ module my_module;
+
+ struct MyStruct {
+ int32 a_missing@;
+ };
+ """
+ with self.assertRaisesRegexp(
+ lexer.LexError, r"^my_file\.mojom:4: Error: Missing ordinal value$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ module my_module;
+
+ struct MyStruct {
+ int32 a_octal@01;
+ };
+ """
+ with self.assertRaisesRegexp(
+ lexer.LexError, r"^my_file\.mojom:4: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ module my_module; struct MyStruct { int32 a_invalid_octal@08; };
+ """
+ with self.assertRaisesRegexp(
+ lexer.LexError, r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ source4 = "module my_module; struct MyStruct { int32 a_hex@0x1aB9; };"
+ with self.assertRaisesRegexp(
+ lexer.LexError, r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source4, "my_file.mojom")
+
+ source5 = "module my_module; struct MyStruct { int32 a_hex@0X0; };"
+ with self.assertRaisesRegexp(
+ lexer.LexError, r"^my_file\.mojom:1: Error: "
+ r"Octal and hexadecimal ordinal values not allowed$"):
+ parser.Parse(source5, "my_file.mojom")
+
+ source6 = """\
+ struct MyStruct {
+ int32 a_too_big@999999999999;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: "
+ r"Ordinal value 999999999999 too large:\n"
+ r" *int32 a_too_big@999999999999;$"):
+ parser.Parse(source6, "my_file.mojom")
+
+ def testNestedNamespace(self):
+ """Tests that "nested" namespaces work."""
+
+ source = """\
+ module my.mod;
+
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my.mod'), None), ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody(ast.StructField('a', None, None, 'int32', None)))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidHandleTypes(self):
+ """Tests (valid) handle types."""
+
+ source = """\
+ struct MyStruct {
+ handle a;
+ handle<data_pipe_consumer> b;
+ handle <data_pipe_producer> c;
+ handle < message_pipe > d;
+ handle
+ < shared_buffer
+ > e;
+ handle
+ <platform
+
+ > f;
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a', None, None, 'handle', None),
+ ast.StructField('b', None, None, 'handle<data_pipe_consumer>',
+ None),
+ ast.StructField('c', None, None, 'handle<data_pipe_producer>',
+ None),
+ ast.StructField('d', None, None, 'handle<message_pipe>', None),
+ ast.StructField('e', None, None, 'handle<shared_buffer>', None),
+ ast.StructField('f', None, None, 'handle<platform>', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidHandleType(self):
+ """Tests an invalid (unknown) handle type."""
+
+ source = """\
+ struct MyStruct {
+ handle<wtf_is_this> foo;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: "
+ r"Invalid handle type 'wtf_is_this':\n"
+ r" *handle<wtf_is_this> foo;$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testValidDefaultValues(self):
+ """Tests default values that are valid (to the parser)."""
+
+ source = """\
+ struct MyStruct {
+ int16 a0 = 0;
+ uint16 a1 = 0x0;
+ uint16 a2 = 0x00;
+ uint16 a3 = 0x01;
+ uint16 a4 = 0xcd;
+ int32 a5 = 12345;
+ int64 a6 = -12345;
+ int64 a7 = +12345;
+ uint32 a8 = 0x12cd3;
+ uint32 a9 = -0x12cD3;
+ uint32 a10 = +0x12CD3;
+ bool a11 = true;
+ bool a12 = false;
+ float a13 = 1.2345;
+ float a14 = -1.2345;
+ float a15 = +1.2345;
+ float a16 = 123.;
+ float a17 = .123;
+ double a18 = 1.23E10;
+ double a19 = 1.E-10;
+ double a20 = .5E+10;
+ double a21 = -1.23E10;
+ double a22 = +.123E10;
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a0', None, None, 'int16', '0'),
+ ast.StructField('a1', None, None, 'uint16', '0x0'),
+ ast.StructField('a2', None, None, 'uint16', '0x00'),
+ ast.StructField('a3', None, None, 'uint16', '0x01'),
+ ast.StructField('a4', None, None, 'uint16', '0xcd'),
+ ast.StructField('a5', None, None, 'int32', '12345'),
+ ast.StructField('a6', None, None, 'int64', '-12345'),
+ ast.StructField('a7', None, None, 'int64', '+12345'),
+ ast.StructField('a8', None, None, 'uint32', '0x12cd3'),
+ ast.StructField('a9', None, None, 'uint32', '-0x12cD3'),
+ ast.StructField('a10', None, None, 'uint32', '+0x12CD3'),
+ ast.StructField('a11', None, None, 'bool', 'true'),
+ ast.StructField('a12', None, None, 'bool', 'false'),
+ ast.StructField('a13', None, None, 'float', '1.2345'),
+ ast.StructField('a14', None, None, 'float', '-1.2345'),
+ ast.StructField('a15', None, None, 'float', '+1.2345'),
+ ast.StructField('a16', None, None, 'float', '123.'),
+ ast.StructField('a17', None, None, 'float', '.123'),
+ ast.StructField('a18', None, None, 'double', '1.23E10'),
+ ast.StructField('a19', None, None, 'double', '1.E-10'),
+ ast.StructField('a20', None, None, 'double', '.5E+10'),
+ ast.StructField('a21', None, None, 'double', '-1.23E10'),
+ ast.StructField('a22', None, None, 'double', '+.123E10')
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidFixedSizeArray(self):
+ """Tests parsing a fixed size array."""
+
+ source = """\
+ struct MyStruct {
+ array<int32> normal_array;
+ array<int32, 1> fixed_size_array_one_entry;
+ array<int32, 10> fixed_size_array_ten_entries;
+ array<array<array<int32, 1>>, 2> nested_arrays;
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('normal_array', None, None, 'int32[]', None),
+ ast.StructField('fixed_size_array_one_entry', None, None,
+ 'int32[1]', None),
+ ast.StructField('fixed_size_array_ten_entries', None, None,
+ 'int32[10]', None),
+ ast.StructField('nested_arrays', None, None, 'int32[1][][2]',
+ None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testValidNestedArray(self):
+ """Tests parsing a nested array."""
+
+ source = "struct MyStruct { array<array<int32>> nested_array; };"
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody(
+ ast.StructField('nested_array', None, None, 'int32[][]', None)))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidFixedArraySize(self):
+ """Tests that invalid fixed array bounds are correctly detected."""
+
+ source1 = """\
+ struct MyStruct {
+ array<int32, 0> zero_size_array;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Fixed array size 0 invalid:\n"
+ r" *array<int32, 0> zero_size_array;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ array<int32, 999999999999> too_big_array;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Fixed array size 999999999999 invalid:\n"
+ r" *array<int32, 999999999999> too_big_array;$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ array<int32, abcdefg> not_a_number;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'abcdefg':\n"
+ r" *array<int32, abcdefg> not_a_number;"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidAssociativeArrays(self):
+ """Tests that we can parse valid associative array structures."""
+
+ source1 = "struct MyStruct { map<string, uint8> data; };"
+ expected1 = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody(
+ [ast.StructField('data', None, None, 'uint8{string}', None)]))
+ ])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ source2 = "interface MyInterface { MyMethod(map<string, uint8> a); };"
+ expected2 = ast.Mojom(None, ast.ImportList(), [
+ ast.Interface(
+ 'MyInterface', None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod', None, None,
+ ast.ParameterList(
+ ast.Parameter('a', None, None, 'uint8{string}')),
+ None)))
+ ])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ source3 = "struct MyStruct { map<string, array<uint8>> data; };"
+ expected3 = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody(
+ [ast.StructField('data', None, None, 'uint8[]{string}', None)]))
+ ])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ def testValidMethod(self):
+ """Tests parsing method declarations."""
+
+ source1 = "interface MyInterface { MyMethod(int32 a); };"
+ expected1 = ast.Mojom(None, ast.ImportList(), [
+ ast.Interface(
+ 'MyInterface', None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod', None, None,
+ ast.ParameterList(ast.Parameter('a', None, None, 'int32')),
+ None)))
+ ])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ source2 = """\
+ interface MyInterface {
+ MyMethod1@0(int32 a@0, int64 b@1);
+ MyMethod2@1() => ();
+ };
+ """
+ expected2 = ast.Mojom(None, ast.ImportList(), [
+ ast.Interface(
+ 'MyInterface', None,
+ ast.InterfaceBody([
+ ast.Method(
+ 'MyMethod1', None, ast.Ordinal(0),
+ ast.ParameterList([
+ ast.Parameter('a', None, ast.Ordinal(0), 'int32'),
+ ast.Parameter('b', None, ast.Ordinal(1), 'int64')
+ ]), None),
+ ast.Method('MyMethod2', None, ast.Ordinal(1),
+ ast.ParameterList(), ast.ParameterList())
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ source3 = """\
+ interface MyInterface {
+ MyMethod(string a) => (int32 a, bool b);
+ };
+ """
+ expected3 = ast.Mojom(None, ast.ImportList(), [
+ ast.Interface(
+ 'MyInterface', None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod', None, None,
+ ast.ParameterList(ast.Parameter('a', None, None, 'string')),
+ ast.ParameterList([
+ ast.Parameter('a', None, None, 'int32'),
+ ast.Parameter('b', None, None, 'bool')
+ ]))))
+ ])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ def testInvalidMethods(self):
+ """Tests that invalid method declarations are correctly detected."""
+
+ # No trailing commas.
+ source1 = """\
+ interface MyInterface {
+ MyMethod(string a,);
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '\)':\n"
+ r" *MyMethod\(string a,\);$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # No leading commas.
+ source2 = """\
+ interface MyInterface {
+ MyMethod(, string a);
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected ',':\n"
+ r" *MyMethod\(, string a\);$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ def testValidInterfaceDefinitions(self):
+ """Tests all types of definitions that can occur in an interface."""
+
+ source = """\
+ interface MyInterface {
+ enum MyEnum { VALUE };
+ const int32 kMyConst = 123;
+ MyMethod(int32 x) => (MyEnum y);
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Interface(
+ 'MyInterface', None,
+ ast.InterfaceBody([
+ ast.Enum('MyEnum', None,
+ ast.EnumValueList(ast.EnumValue('VALUE', None, None))),
+ ast.Const('kMyConst', None, 'int32', '123'),
+ ast.Method(
+ 'MyMethod', None, None,
+ ast.ParameterList(ast.Parameter('x', None, None, 'int32')),
+ ast.ParameterList(ast.Parameter('y', None, None, 'MyEnum')))
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidInterfaceDefinitions(self):
+ """Tests that definitions that aren't allowed in an interface are correctly
+ detected."""
+
+ source1 = """\
+ interface MyInterface {
+ struct MyStruct {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ interface MyInterface {
+ interface MyInnerInterface {
+ MyMethod(int32 x);
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'interface':\n"
+ r" *interface MyInnerInterface {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ interface MyInterface {
+ int32 my_field;
+ };
+ """
+ # The parser thinks that "int32" is a plausible name for a method, so it's
+ # "my_field" that gives it away.
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'my_field':\n"
+ r" *int32 my_field;$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidAttributes(self):
+ """Tests parsing attributes (and attribute lists)."""
+
+ # Note: We use structs because they have (optional) attribute lists.
+
+ # Empty attribute list.
+ source1 = "[] struct MyStruct {};"
+ expected1 = ast.Mojom(
+ None, ast.ImportList(),
+ [ast.Struct('MyStruct', ast.AttributeList(), ast.StructBody())])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ # One-element attribute list, with name value.
+ source2 = "[MyAttribute=MyName] struct MyStruct {};"
+ expected2 = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct('MyStruct',
+ ast.AttributeList(ast.Attribute("MyAttribute", "MyName")),
+ ast.StructBody())
+ ])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ # Two-element attribute list, with one string value and one integer value.
+ source3 = "[MyAttribute1 = \"hello\", MyAttribute2 = 5] struct MyStruct {};"
+ expected3 = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct',
+ ast.AttributeList([
+ ast.Attribute("MyAttribute1", "hello"),
+ ast.Attribute("MyAttribute2", 5)
+ ]), ast.StructBody())
+ ])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ # Various places that attribute list is allowed.
+ source4 = """\
+ [Attr0=0] module my_module;
+
+ [Attr1=1] import "my_import";
+
+ [Attr2=2] struct MyStruct {
+ [Attr3=3] int32 a;
+ };
+ [Attr4=4] union MyUnion {
+ [Attr5=5] int32 a;
+ };
+ [Attr6=6] enum MyEnum {
+ [Attr7=7] a
+ };
+ [Attr8=8] interface MyInterface {
+ [Attr9=9] MyMethod([Attr10=10] int32 a) => ([Attr11=11] bool b);
+ };
+ [Attr12=12] const double kMyConst = 1.23;
+ """
+ expected4 = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'),
+ ast.AttributeList([ast.Attribute("Attr0", 0)])),
+ ast.ImportList(
+ ast.Import(
+ ast.AttributeList([ast.Attribute("Attr1", 1)]), "my_import")),
+ [
+ ast.Struct(
+ 'MyStruct', ast.AttributeList(ast.Attribute("Attr2", 2)),
+ ast.StructBody(
+ ast.StructField(
+ 'a', ast.AttributeList([ast.Attribute("Attr3", 3)]),
+ None, 'int32', None))),
+ ast.Union(
+ 'MyUnion', ast.AttributeList(ast.Attribute("Attr4", 4)),
+ ast.UnionBody(
+ ast.UnionField(
+ 'a', ast.AttributeList([ast.Attribute("Attr5", 5)]),
+ None, 'int32'))),
+ ast.Enum(
+ 'MyEnum', ast.AttributeList(ast.Attribute("Attr6", 6)),
+ ast.EnumValueList(
+ ast.EnumValue(
+ 'VALUE', ast.AttributeList([ast.Attribute("Attr7", 7)]),
+ None))),
+ ast.Interface(
+ 'MyInterface', ast.AttributeList(ast.Attribute("Attr8", 8)),
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod', ast.AttributeList(
+ ast.Attribute("Attr9", 9)), None,
+ ast.ParameterList(
+ ast.Parameter(
+ 'a',
+ ast.AttributeList([ast.Attribute("Attr10", 10)
+ ]), None, 'int32')),
+ ast.ParameterList(
+ ast.Parameter(
+ 'b',
+ ast.AttributeList([ast.Attribute("Attr11", 11)
+ ]), None, 'bool'))))),
+ ast.Const('kMyConst', ast.AttributeList(
+ ast.Attribute("Attr12", 12)), 'double', '1.23')
+ ])
+ self.assertEquals(parser.Parse(source4, "my_file.mojom"), expected4)
+
+ # TODO(vtl): Boolean attributes don't work yet. (In fact, we just |eval()|
+ # literal (non-name) values, which is extremely dubious.)
+
+ def testInvalidAttributes(self):
+ """Tests that invalid attributes and attribute lists are correctly
+ detected."""
+
+ # Trailing commas not allowed.
+ source1 = "[MyAttribute=MyName,] struct MyStruct {};"
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:1: Error: Unexpected '\]':\n"
+ r"\[MyAttribute=MyName,\] struct MyStruct {};$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ # Missing value.
+ source2 = "[MyAttribute=] struct MyStruct {};"
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:1: Error: Unexpected '\]':\n"
+ r"\[MyAttribute=\] struct MyStruct {};$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ # Missing key.
+ source3 = "[=MyName] struct MyStruct {};"
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:1: Error: Unexpected '=':\n"
+ r"\[=MyName\] struct MyStruct {};$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidImports(self):
+ """Tests parsing import statements."""
+
+ # One import (no module statement).
+ source1 = "import \"somedir/my.mojom\";"
+ expected1 = ast.Mojom(None,
+ ast.ImportList(ast.Import(None, "somedir/my.mojom")),
+ [])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ # Two imports (no module statement).
+ source2 = """\
+ import "somedir/my1.mojom";
+ import "somedir/my2.mojom";
+ """
+ expected2 = ast.Mojom(
+ None,
+ ast.ImportList([
+ ast.Import(None, "somedir/my1.mojom"),
+ ast.Import(None, "somedir/my2.mojom")
+ ]), [])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ # Imports with module statement.
+ source3 = """\
+ module my_module;
+ import "somedir/my1.mojom";
+ import "somedir/my2.mojom";
+ """
+ expected3 = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None),
+ ast.ImportList([
+ ast.Import(None, "somedir/my1.mojom"),
+ ast.Import(None, "somedir/my2.mojom")
+ ]), [])
+ self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)
+
+ def testInvalidImports(self):
+ """Tests that invalid import statements are correctly detected."""
+
+ source1 = """\
+ // Make the error occur on line 2.
+ import invalid
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'invalid':\n"
+ r" *import invalid$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ import // Missing string.
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ import "foo.mojom" // Missing semicolon.
+ struct MyStruct {
+ int32 a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testValidNullableTypes(self):
+ """Tests parsing nullable types."""
+
+ source = """\
+ struct MyStruct {
+ int32? a; // This is actually invalid, but handled at a different
+ // level.
+ string? b;
+ array<int32> ? c;
+ array<string ? > ? d;
+ array<array<int32>?>? e;
+ array<int32, 1>? f;
+ array<string?, 1>? g;
+ some_struct? h;
+ handle? i;
+ handle<data_pipe_consumer>? j;
+ handle<data_pipe_producer>? k;
+ handle<message_pipe>? l;
+ handle<shared_buffer>? m;
+ some_interface&? n;
+ handle<platform>? o;
+ };
+ """
+ expected = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a', None, None, 'int32?', None),
+ ast.StructField('b', None, None, 'string?', None),
+ ast.StructField('c', None, None, 'int32[]?', None),
+ ast.StructField('d', None, None, 'string?[]?', None),
+ ast.StructField('e', None, None, 'int32[]?[]?', None),
+ ast.StructField('f', None, None, 'int32[1]?', None),
+ ast.StructField('g', None, None, 'string?[1]?', None),
+ ast.StructField('h', None, None, 'some_struct?', None),
+ ast.StructField('i', None, None, 'handle?', None),
+ ast.StructField('j', None, None, 'handle<data_pipe_consumer>?',
+ None),
+ ast.StructField('k', None, None, 'handle<data_pipe_producer>?',
+ None),
+ ast.StructField('l', None, None, 'handle<message_pipe>?', None),
+ ast.StructField('m', None, None, 'handle<shared_buffer>?',
+ None),
+ ast.StructField('n', None, None, 'some_interface&?', None),
+ ast.StructField('o', None, None, 'handle<platform>?', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
+
+ def testInvalidNullableTypes(self):
+ """Tests that invalid nullable types are correctly detected."""
+ source1 = """\
+ struct MyStruct {
+ string?? a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '\?':\n"
+ r" *string\?\? a;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ handle?<data_pipe_consumer> a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '<':\n"
+ r" *handle\?<data_pipe_consumer> a;$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ some_interface?& a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '&':\n"
+ r" *some_interface\?& a;$"):
+ parser.Parse(source3, "my_file.mojom")
+
+ def testSimpleUnion(self):
+ """Tests a simple .mojom source that just defines a union."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ int32 a;
+ double b;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Union(
+ 'MyUnion', None,
+ ast.UnionBody([
+ ast.UnionField('a', None, None, 'int32'),
+ ast.UnionField('b', None, None, 'double')
+ ]))
+ ])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithOrdinals(self):
+ """Test that ordinals are assigned to fields."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ int32 a @10;
+ double b @30;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Union(
+ 'MyUnion', None,
+ ast.UnionBody([
+ ast.UnionField('a', None, ast.Ordinal(10), 'int32'),
+ ast.UnionField('b', None, ast.Ordinal(30), 'double')
+ ]))
+ ])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithStructMembers(self):
+ """Test that struct members are accepted."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ SomeStruct s;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Union(
+ 'MyUnion', None,
+ ast.UnionBody([ast.UnionField('s', None, None, 'SomeStruct')]))
+ ])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithArrayMember(self):
+ """Test that array members are accepted."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ array<int32> a;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Union(
+ 'MyUnion', None,
+ ast.UnionBody([ast.UnionField('a', None, None, 'int32[]')]))
+ ])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionWithMapMember(self):
+ """Test that map members are accepted."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ map<int32, string> m;
+ };
+ """
+ expected = ast.Mojom(
+ ast.Module(('IDENTIFIER', 'my_module'), None), ast.ImportList(), [
+ ast.Union(
+ 'MyUnion', None,
+ ast.UnionBody(
+ [ast.UnionField('m', None, None, 'string{int32}')]))
+ ])
+ actual = parser.Parse(source, "my_file.mojom")
+ self.assertEquals(actual, expected)
+
+ def testUnionDisallowNestedStruct(self):
+ """Tests that structs cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ struct MyStruct {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:4: Error: Unexpected 'struct':\n"
+ r" *struct MyStruct {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testUnionDisallowNestedInterfaces(self):
+ """Tests that interfaces cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ interface MyInterface {
+ MyMethod(int32 a);
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:4: Error: Unexpected 'interface':\n"
+ r" *interface MyInterface {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testUnionDisallowNestedUnion(self):
+ """Tests that unions cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ union MyOtherUnion {
+ int32 a;
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:4: Error: Unexpected 'union':\n"
+ r" *union MyOtherUnion {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testUnionDisallowNestedEnum(self):
+ """Tests that enums cannot be nested in unions."""
+ source = """\
+ module my_module;
+
+ union MyUnion {
+ enum MyEnum {
+ A,
+ };
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:4: Error: Unexpected 'enum':\n"
+ r" *enum MyEnum {$"):
+ parser.Parse(source, "my_file.mojom")
+
+ def testValidAssociatedKinds(self):
+ """Tests parsing associated interfaces and requests."""
+ source1 = """\
+ struct MyStruct {
+ associated MyInterface a;
+ associated MyInterface& b;
+ associated MyInterface? c;
+ associated MyInterface&? d;
+ };
+ """
+ expected1 = ast.Mojom(None, ast.ImportList(), [
+ ast.Struct(
+ 'MyStruct', None,
+ ast.StructBody([
+ ast.StructField('a', None, None, 'asso<MyInterface>', None),
+ ast.StructField('b', None, None, 'asso<MyInterface&>', None),
+ ast.StructField('c', None, None, 'asso<MyInterface>?', None),
+ ast.StructField('d', None, None, 'asso<MyInterface&>?', None)
+ ]))
+ ])
+ self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
+
+ source2 = """\
+ interface MyInterface {
+ MyMethod(associated A a) =>(associated B& b);
+ };"""
+ expected2 = ast.Mojom(None, ast.ImportList(), [
+ ast.Interface(
+ 'MyInterface', None,
+ ast.InterfaceBody(
+ ast.Method(
+ 'MyMethod', None, None,
+ ast.ParameterList(
+ ast.Parameter('a', None, None, 'asso<A>')),
+ ast.ParameterList(
+ ast.Parameter('b', None, None, 'asso<B&>')))))
+ ])
+ self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
+
+ def testInvalidAssociatedKinds(self):
+ """Tests that invalid associated interfaces and requests are correctly
+ detected."""
+ source1 = """\
+ struct MyStruct {
+ associated associated SomeInterface a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError,
+ r"^my_file\.mojom:2: Error: Unexpected 'associated':\n"
+ r" *associated associated SomeInterface a;$"):
+ parser.Parse(source1, "my_file.mojom")
+
+ source2 = """\
+ struct MyStruct {
+ associated handle a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected 'handle':\n"
+ r" *associated handle a;$"):
+ parser.Parse(source2, "my_file.mojom")
+
+ source3 = """\
+ struct MyStruct {
+ associated? MyInterface& a;
+ };
+ """
+ with self.assertRaisesRegexp(
+ parser.ParseError, r"^my_file\.mojom:2: Error: Unexpected '\?':\n"
+ r" *associated\? MyInterface& a;$"):
+ parser.Parse(source3, "my_file.mojom")
+
+
+if __name__ == "__main__":
+ unittest.main()