blob: fdce26c18ff2ad84b6b8edd3742c8a813de0c11b [file] [log] [blame]
# 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 unittest
import ast
import lexer
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 {
}
"""
self.assertEquals(parser.Parse(source, "my_file.mojom"),
[("MODULE", "my_module", None)])
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}\r\n";
self.assertEquals(parser.Parse(source, "my_file.mojom"),
[("MODULE", "my_module", None)])
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 testSimpleStruct(self):
"""Tests a simple .mojom source that just defines a struct."""
source = """\
module my_module {
struct MyStruct {
int32 a;
double b;
};
} // module my_module
"""
expected = \
[('MODULE',
'my_module',
[('STRUCT',
'MyStruct',
None,
[('FIELD', 'int32', 'a', ast.Ordinal(None), None),
('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
def testSimpleStructWithoutModule(self):
"""Tests a simple struct without an enclosing module."""
source = """\
struct MyStruct {
int32 a;
double b;
};
"""
expected = \
[('MODULE',
'',
[('STRUCT',
'MyStruct',
None,
[('FIELD', 'int32', 'a', ast.Ordinal(None), None),
('FIELD', 'double', 'b', ast.Ordinal(None), None)])])]
self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
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 '{':\nmodule {$"):
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.
{
}
"""
with self.assertRaisesRegexp(
parser.ParseError,
r"^my_file\.mojom:4: Error: Unexpected '{':\n{$"):
parser.Parse(source2, "my_file.mojom")
def testEnumExpressions(self):
"""Tests an enum with values calculated using simple expressions."""
source = """\
module my_module {
enum MyEnum {
MY_ENUM_1 = 1,
MY_ENUM_2 = 1 + 1,
MY_ENUM_3 = 1 * 3,
MY_ENUM_4 = 2 * (1 + 1),
MY_ENUM_5 = 1 + 2 * 2,
MY_ENUM_6 = -6 / -2,
MY_ENUM_7 = 3 | (1 << 2),
MY_ENUM_8 = 16 >> 1,
MY_ENUM_9 = 1 ^ 15 & 8,
MY_ENUM_10 = 110 % 100,
MY_ENUM_MINUS_1 = ~0
};
} // my_module
"""
expected = \
[('MODULE',
'my_module',
[('ENUM',
'MyEnum',
[('ENUM_FIELD', 'MY_ENUM_1', ('EXPRESSION', ['1'])),
('ENUM_FIELD', 'MY_ENUM_2', ('EXPRESSION', ['1', '+', '1'])),
('ENUM_FIELD', 'MY_ENUM_3', ('EXPRESSION', ['1', '*', '3'])),
('ENUM_FIELD',
'MY_ENUM_4',
('EXPRESSION',
['2', '*', '(', ('EXPRESSION', ['1', '+', '1']), ')'])),
('ENUM_FIELD',
'MY_ENUM_5',
('EXPRESSION', ['1', '+', '2', '*', '2'])),
('ENUM_FIELD',
'MY_ENUM_6',
('EXPRESSION',
['-', ('EXPRESSION', ['6', '/', '-', ('EXPRESSION', ['2'])])])),
('ENUM_FIELD',
'MY_ENUM_7',
('EXPRESSION',
['3', '|', '(', ('EXPRESSION', ['1', '<<', '2']), ')'])),
('ENUM_FIELD', 'MY_ENUM_8', ('EXPRESSION', ['16', '>>', '1'])),
('ENUM_FIELD',
'MY_ENUM_9',
('EXPRESSION', ['1', '^', '15', '&', '8'])),
('ENUM_FIELD', 'MY_ENUM_10', ('EXPRESSION', ['110', '%', '100'])),
('ENUM_FIELD',
'MY_ENUM_MINUS_1',
('EXPRESSION', ['~', ('EXPRESSION', ['0'])]))])])]
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
};
} // my_module
"""
with self.assertRaisesRegexp(
lexer.LexError,
r"^my_file\.mojom:4: Error: Illegal character '\?'$"):
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;
};
} // module my_module
"""
expected = \
[('MODULE',
'my_module',
[('STRUCT',
'MyStruct',
None,
[('FIELD', 'int32', 'a0', ast.Ordinal(0), None),
('FIELD', 'int32', 'a1', ast.Ordinal(1), None),
('FIELD', 'int32', 'a2', ast.Ordinal(2), None),
('FIELD', 'int32', 'a9', ast.Ordinal(9), None),
('FIELD', 'int32', 'a10', ast.Ordinal(10), None),
('FIELD', 'int32', 'a11', ast.Ordinal(11), None),
('FIELD', 'int32', 'a29', ast.Ordinal(29), None),
('FIELD', 'int32', 'a1234567890', ast.Ordinal(1234567890), 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 @;
};
} // module my_module
"""
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;
};
} // module my_module
"""
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 nested namespaces work."""
source = """\
module my.mod {
struct MyStruct {
int32 a;
};
} // module my.mod
"""
expected = \
[('MODULE',
'my.mod',
[('STRUCT',
'MyStruct',
None,
[('FIELD', 'int32', 'a', ast.Ordinal(None), None)])])]
self.assertEquals(parser.Parse(source, "my_file.mojom"), expected)
if __name__ == "__main__":
unittest.main()