示例#1
0
// ATTRIBUTES      -> lbracket ATTR_ASSIGNMENT { comma, ATTR_ASSIGNMENT}
// ATTR_ASSIGNMENT -> name equals name | name equals literal
func (p *Parser) parseAttributes() (attributes *mojom.Attributes) {
	if !p.OK() {
		return
	}

	if !p.tryMatch(lexer.LBracket) {
		// There is no attributes section here
		return
	}

	p.pushChildNode("attributes")
	defer p.popNode()

	attributes = mojom.NewAttributes()

	nextToken := p.lastConsumed
	for nextToken.Kind != lexer.RBracket {
		key := p.readName()
		p.attachToken()
		if !p.OK() {
			return
		}
		if !p.match(lexer.Equals) {
			return
		}

		var value mojom.ConcreteValue
		if p.peekNextToken("Expecting to find an attribute value.").Kind == lexer.Name {
			text := p.readName()
			// TODO(rudominer) Do not throw away the distinction between a name and a string literal.
			value = mojom.MakeStringLiteralValue(text)
		} else {
			value = p.parseLiteral()
		}
		p.attachToken()

		if !p.OK() {
			return
		}
		attributes.List = append(attributes.List, mojom.MojomAttribute{key, value})

		nextToken = p.peekNextToken("I was reading an attributes section.")
		if !p.OK() {
			return
		}
		p.consumeNextToken()
		if nextToken.Kind != lexer.RBracket && nextToken.Kind != lexer.Comma {
			var message string
			switch nextToken.Kind {
			case lexer.Module, lexer.Interface, lexer.Struct, lexer.Union, lexer.Enum:
				message = fmt.Sprintf("The attribute section is missing a closing ] before %v at %s.",
					nextToken, nextToken.LongLocationString())
			default:
				message = p.unexpectedTokenError(nextToken, "comma or ]")
			}
			p.err = &ParseError{ParserErrorCodeUnexpectedToken, message}
			return
		}
	}

	return
}
示例#2
0
// TestSuccessfulParsing contains a series of test cases in which we
// run the parser on a valid mojom input string and compare the resulting
// MojomFile to an expected one.
func TestSuccessfulParsing(t *testing.T) {
	type testCase struct {
		fileName      string
		mojomContents string
		expectedFile  *mojom.MojomFile
	}
	cases := make([]testCase, 0)
	testCaseNum := 0
	var expectedFile *mojom.MojomFile

	startTestCase := func(moduleNameSpace string) {
		descriptor := mojom.NewMojomDescriptor()
		fileName := fmt.Sprintf("file%d", testCaseNum)
		expectedFile = descriptor.AddMojomFile(fileName)
		expectedFile.InitializeFileScope(moduleNameSpace)
		cases = append(cases, testCase{fileName, "", expectedFile})
	}

	endTestCase := func() {
		testCaseNum += 1
	}

	// Note(rudominer) The structure of this method is designed to allow
	// test cases to be rearranged and new test cases to be inserted at
	// arbitrary locations. Do not hard-code anything that refers to the
	// position of a test case in the list.

	////////////////////////////////////////////////////////////
	// Test Case (empty file)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = ""
	endTestCase()
	////////////////////////////////////////////////////////////
	// Test Case (module statement only)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `module mojom.test;`
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (module statement with attributes)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `[cool=true]module mojom.test;`
	expectedFile.Attributes = mojom.NewAttributes()
	expectedFile.Attributes.List = append(expectedFile.Attributes.List,
		mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)})
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (import statements only)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `import "a.file";`
	expectedFile.AddImport("a.file")
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (module and import statements only)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "a.file";`
	{
		expectedFile.AddImport("a.file")
		endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case (module with attributes and import statements only)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	[cool=true]
	module mojom.test;

	import "a.file";`
	{
		expectedFile.Attributes = mojom.NewAttributes()
		expectedFile.Attributes.List = append(expectedFile.Attributes.List,
			mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)})
		expectedFile.AddImport("a.file")
		endTestCase()
	}
	////////////////////////////////////////////////////////////
	// Test Case (one empty sruct)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `struct Foo{};`
	expectedFile.AddStruct(mojom.NewMojomStruct(mojom.DeclTestData("Foo")))
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	struct Foo{
		int32 x;
	};`
	{
		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil))
		expectedFile.AddStruct(structFoo)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "another.file";
	import "and.another.file";

	struct Foo{
		[happy=true] int32 x@4;
	};`
	{
		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		attributes := mojom.NewAttributes()
		attributes.List = append(attributes.List, mojom.MojomAttribute{"happy", mojom.MakeBoolLiteralValue(true)})
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataAWithOrdinal("x", attributes, 4), mojom.SimpleTypeInt32, nil))
		expectedFile.AddStruct(structFoo)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "another.file";
	import "and.another.file";

	struct Foo{
		int32 x@4 = 42;
		[age=7, level="high"] string  y = "Howdy!";
		string? z;
	};`
	{
		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("x", 4), mojom.SimpleTypeInt32, mojom.MakeInt64LiteralValue(42)))
		attributes := mojom.NewAttributes()
		attributes.List = append(attributes.List, mojom.MojomAttribute{"age", mojom.MakeInt64LiteralValue(7)})
		attributes.List = append(attributes.List, mojom.MojomAttribute{"level", mojom.MakeStringLiteralValue("high")})
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataA("y", attributes), mojom.BuiltInType("string"), mojom.MakeStringLiteralValue("Howdy!")))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
		expectedFile.AddStruct(structFoo)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "another.file";
	import "and.another.file";

	struct Foo{
		int32 x;
		string  y;
		string? z;
	};

	interface Doer {
		DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo);
	};

	`
	{
		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
		expectedFile.AddStruct(structFoo)

		interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer"))

		// The first reference to Foo inside of interface Doer
		fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{})

		// The second reference to Foo inside of interface Doer. nullable=true
		fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{})

		params := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil))
		responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil))

		interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams))
		expectedFile.AddInterface(interfaceDoer)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	[php_namespace="mojom.test.php"]
	module mojom.test;

	import "another.file";
	import "and.another.file";

	const int8 TOO_SMALL_VALUE = 6;

	enum ErrorCodes {
		TOO_BIG = 5,
		TOO_SMALL = TOO_SMALL_VALUE,
		JUST_RIGHT,
	};

	struct Foo{
		int32 x;
		string  y;
		string? z;
	};

	interface Doer {
		DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo);
	};

	`
	{
		expectedFile.Attributes = mojom.NewAttributes()
		expectedFile.Attributes.List = append(expectedFile.Attributes.List,
			mojom.MojomAttribute{"php_namespace", mojom.MakeStringLiteralValue("mojom.test.php")})

		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("TOO_SMALL_VALUE"),
			mojom.SimpleTypeInt8, mojom.MakeInt64LiteralValue(6)))

		errorCodeEnum := mojom.NewMojomEnum(mojom.DeclTestData("ErrorCodes"))
		errorCodeEnum.InitAsScope(expectedFile.FileScope)

		// The reference to TOO_SMALL_VALUE from within the ErrorCodes enum.
		assigneeType := mojom.NewResolvedUserTypeRef("ErrorCodes", errorCodeEnum)
		tooSmallValueRef := mojom.NewUserValueRef(assigneeType, "TOO_SMALL_VALUE",
			expectedFile.FileScope, lexer.Token{})

		errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_BIG"), mojom.MakeInt64LiteralValue(5))
		errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_SMALL"), tooSmallValueRef)
		errorCodeEnum.AddEnumValue(mojom.DeclTestData("JUST_RIGHT"), nil)
		expectedFile.AddEnum(errorCodeEnum)

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
		expectedFile.AddStruct(structFoo)

		interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer"))

		// The first reference to Foo inside of interface Doer
		fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{})

		// The second reference to Foo inside of interface Doer. nullable=true
		fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{})

		params := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil))
		responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil))

		interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams))
		expectedFile.AddInterface(interfaceDoer)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Execute all of the test cases.
	////////////////////////////////////////////////////////////
	for _, c := range cases {
		descriptor := mojom.NewMojomDescriptor()
		parser := MakeParser(c.fileName, c.mojomContents, descriptor)
		parser.Parse()
		if !parser.OK() {
			t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
		} else {
			got := parser.GetMojomFile().String()
			expected := c.expectedFile.String()
			if got != expected {
				t.Errorf("%s:\n*****expected:\n%s\n****actual\n%s", c.fileName, expected, got)
			}
		}
	}
}
示例#3
0
// ATTRIBUTES      -> lbracket ATTR_ASSIGNMENT { comma, ATTR_ASSIGNMENT}
// ATTR_ASSIGNMENT -> name equals name | name equals literal
func (p *Parser) parseAttributes() (attributes *mojom.Attributes) {
	if !p.OK() {
		return
	}

	if !p.tryMatch(lexer.LBRACKET) {
		// There is no attributes section here
		return
	}

	p.pushChildNode("attributes")
	defer p.popNode()

	attributes = mojom.NewAttributes()

	nextToken := p.lastConsumed
	for nextToken.Kind != lexer.RBRACKET {
		key := p.readName()
		p.attachToken()
		if !p.OK() {
			return
		}
		if !p.match(lexer.EQUALS) {
			return
		}

		var value mojom.ConcreteValue
		if p.peekNextToken("Expecting to find an attribute value.").Kind == lexer.NAME {
			text := p.readName()
			value = mojom.MakeStringLiteralValue(text)
		} else {
			value = p.parseLiteral()
		}
		p.attachToken()

		if !p.OK() {
			return
		}
		attributes.List = append(attributes.List, mojom.MojomAttribute{key, value})

		nextToken = p.peekNextToken("I was reading an attributes section.")
		if !p.OK() {
			return
		}
		p.consumeNextToken()
		if nextToken.Kind != lexer.RBRACKET && nextToken.Kind != lexer.COMMA {
			var message string
			switch nextToken.Kind {
			case lexer.MODULE, lexer.INTERFACE, lexer.STRUCT, lexer.UNION, lexer.ENUM:
				message = fmt.Sprintf("The attribute section is missing a closing ] before %v at %s.",
					nextToken, nextToken.LongLocationString())
			default:
				message = p.unexpectedTokenError(nextToken, "comma or ]")
			}
			p.err = &ParseError{E_UNEXPECTED_TOKEN, message}
			return
		}
	}

	return
}
示例#4
0
// TestSuccessfulParsing contains a series of test cases in which we
// run the parser on a valid mojom input string and compare the resulting
// MojomFile to an expected one.
func TestSuccessfulParsing(t *testing.T) {
	type testCase struct {
		fileName      string
		mojomContents string
		expectedFile  *mojom.MojomFile
	}
	cases := make([]testCase, 0)
	testCaseNum := 0
	var expectedFile *mojom.MojomFile

	startTestCase := func(moduleNameSpace string) {
		descriptor := mojom.NewMojomDescriptor()
		fileName := fmt.Sprintf("file%d", testCaseNum)
		expectedFile = descriptor.AddMojomFile(fileName, fileName, nil, "")
		expectedFile.InitializeFileScope(moduleNameSpace)
		cases = append(cases, testCase{fileName, "", expectedFile})
	}

	endTestCase := func() {
		testCaseNum += 1
	}

	// Note(rudominer) The structure of this method is designed to allow
	// test cases to be rearranged and new test cases to be inserted at
	// arbitrary locations. Do not hard-code anything that refers to the
	// position of a test case in the list.

	////////////////////////////////////////////////////////////
	// Test Case (empty file)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = ""
	endTestCase()
	////////////////////////////////////////////////////////////
	// Test Case (module statement only)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `module mojom.test;`
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (module statement with attributes)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `[cool=true]module mojom.test;`
	expectedFile.Attributes = mojom.NewAttributes()
	expectedFile.Attributes.List = append(expectedFile.Attributes.List,
		mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)})
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (import statements only)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `import "a.file";`
	expectedFile.AddImport("a.file")
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (module and import statements only)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "a.file";`
	{
		expectedFile.AddImport("a.file")
		endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case (module with attributes and import statements only)
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	[cool=true]
	module mojom.test;

	import "a.file";`
	{
		expectedFile.Attributes = mojom.NewAttributes()
		expectedFile.Attributes.List = append(expectedFile.Attributes.List,
			mojom.MojomAttribute{"cool", mojom.MakeBoolLiteralValue(true)})
		expectedFile.AddImport("a.file")
		endTestCase()
	}
	////////////////////////////////////////////////////////////
	// Test Case (one empty sruct)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `struct Foo{};`
	expectedFile.AddStruct(mojom.NewMojomStruct(mojom.DeclTestData("Foo")))
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (Integer constants)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `
	const uint8 xu8 = 255;
	const int8 x8 = -127;
	const uint16 xu16 = 0xFFFF;
	const int16 x16 = -0x7FFF;
	const uint32 xu32 = 4294967295;
	const int32 x32 = -2147483647;
	const uint64 xu64 = 0xFFFFFFFFFFFFFFFF;
	const int64 x64 = -0x7FFFFFFFFFFFFFFF;
	const uint64 manyNines = 9999999999999999999;
	`
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu8"),
		mojom.SimpleTypeUInt8, mojom.MakeUint8LiteralValue(0xFF)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x8"),
		mojom.SimpleTypeInt8, mojom.MakeInt8LiteralValue(-0x7F)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu16"),
		mojom.SimpleTypeUInt16, mojom.MakeUint16LiteralValue(65535)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x16"),
		mojom.SimpleTypeInt16, mojom.MakeInt16LiteralValue(-32767)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu32"),
		mojom.SimpleTypeUInt32, mojom.MakeUint32LiteralValue(0xFFFFFFFF)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x32"),
		mojom.SimpleTypeInt32, mojom.MakeInt32LiteralValue(-0x7FFFFFFF)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu64"),
		mojom.SimpleTypeUInt64, mojom.MakeUint64LiteralValue(18446744073709551615)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x64"),
		mojom.SimpleTypeInt64, mojom.MakeInt64LiteralValue(-9223372036854775807)))
	expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("manyNines"),
		mojom.SimpleTypeUInt64, mojom.MakeUint64LiteralValue(9999999999999999999)))
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (float and double constants)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `
	const float x = 123.456E7;
	const float y = 123456789.123456789;
	const float z = -0.01;
	const double w = -0.01;
	const double r = 3.14159E40;
	`
	{
		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x"),
			mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(1234560000)))
		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("y"),
			mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(123456789.123456789)))
		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("z"),
			mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(-0.01)))
		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("w"),
			mojom.SimpleTypeDouble, mojom.MakeDoubleLiteralValue(-0.01)))
		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("r"),
			mojom.SimpleTypeDouble, mojom.MakeDoubleLiteralValue(3.14159e+40)))
	}
	endTestCase()
	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	struct Foo{
		int32 x;
	};`
	{
		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil))
		expectedFile.AddStruct(structFoo)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "another.file";
	import "and.another.file";

	struct Foo{
		[happy=true] int32 x@4;
	};`
	{
		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
		attributes := mojom.NewAttributes()
		attributes.List = append(attributes.List, mojom.MojomAttribute{"happy", mojom.MakeBoolLiteralValue(true)})
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataAWithOrdinal("x", attributes, 4), mojom.SimpleTypeInt32, nil))
		expectedFile.AddStruct(structFoo)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "another.file";
	import "and.another.file";

	struct Foo{
		int32 x@4 = 42;
		[age=7, level="high"] string  y = "Howdy!";
		string? z;
		bool w@6 = false;
	};`
	{
		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("x", 4), mojom.SimpleTypeInt32, mojom.MakeInt8LiteralValue(42)))
		attributes := mojom.NewAttributes()
		attributes.List = append(attributes.List, mojom.MojomAttribute{"age", mojom.MakeInt8LiteralValue(7)})
		attributes.List = append(attributes.List, mojom.MojomAttribute{"level", mojom.MakeStringLiteralValue("high")})
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataA("y", attributes), mojom.BuiltInType("string"), mojom.MakeStringLiteralValue("Howdy!")))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("w", 6), mojom.BuiltInType("bool"), mojom.MakeBoolLiteralValue(false)))
		expectedFile.AddStruct(structFoo)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	module mojom.test;

	import "another.file";
	import "and.another.file";

	struct Foo{
		int32 x;
		string  y;
		string? z;
	};

	interface Doer {
		DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo);
	};

	`
	{
		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
		expectedFile.AddStruct(structFoo)

		interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer"))
		interfaceDoer.InitAsScope(mojom.NewTestFileScope("test.scope"))

		// The first reference to Foo inside of interface Doer
		fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{})

		// The second reference to Foo inside of interface Doer. nullable=true
		fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{})

		params := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		params.InitAsScope(mojom.NewTestFileScope("test.scope"))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil))
		responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		responseParams.InitAsScope(mojom.NewTestFileScope("test.scope"))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil))

		interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams))
		expectedFile.AddInterface(interfaceDoer)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case (Annotation right after imports)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `
    import "gpu/interfaces/command_buffer.mojom";

    [ServiceName="mojo::Gpu"]
    interface Gpu {
    };

	`
	{
		expectedFile.AddImport("gpu/interfaces/command_buffer.mojom")

		attributes := mojom.NewAttributes()
		attributes.List = append(attributes.List, mojom.MojomAttribute{"ServiceName", mojom.MakeStringLiteralValue("mojo::Gpu")})
		interfaceGpu := mojom.NewMojomInterface(mojom.DeclTestDataA("Gpu", attributes))
		expectedFile.AddInterface(interfaceGpu)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	startTestCase("mojom.test")
	cases[testCaseNum].mojomContents = `
	[php_namespace="mojom.test.php"]
	module mojom.test;

	import "another.file";
	import "and.another.file";

	const int8 TOO_SMALL_VALUE = 6;

	enum ErrorCodes {
		TOO_BIG = 5,
		TOO_SMALL = TOO_SMALL_VALUE,
		JUST_RIGHT,
	};

	struct Foo{
		int32 x;
		string  y;
		string? z;
	};

	interface Doer {
		DoIt(int8 lemon, handle<message_pipe> pipe) => (array<Foo> someFoos, Foo? anotherFoo);
	};

	`
	{
		expectedFile.Attributes = mojom.NewAttributes()
		expectedFile.Attributes.List = append(expectedFile.Attributes.List,
			mojom.MojomAttribute{"php_namespace", mojom.MakeStringLiteralValue("mojom.test.php")})

		expectedFile.AddImport("another.file")
		expectedFile.AddImport("and.another.file")

		expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("TOO_SMALL_VALUE"),
			mojom.SimpleTypeInt8, mojom.MakeInt8LiteralValue(6)))

		errorCodeEnum := mojom.NewMojomEnum(mojom.DeclTestData("ErrorCodes"))
		errorCodeEnum.InitAsScope(expectedFile.FileScope)

		// The reference to TOO_SMALL_VALUE from within the ErrorCodes enum.
		assigneeType := mojom.NewResolvedUserTypeRef("ErrorCodes", errorCodeEnum)
		tooSmallValueRef := mojom.NewUserValueRef(mojom.AssigneeSpec{"assignee", assigneeType}, "TOO_SMALL_VALUE",
			expectedFile.FileScope, lexer.Token{})

		errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_BIG"), mojom.MakeInt8LiteralValue(5))
		errorCodeEnum.AddEnumValue(mojom.DeclTestData("TOO_SMALL"), tooSmallValueRef)
		errorCodeEnum.AddEnumValue(mojom.DeclTestData("JUST_RIGHT"), nil)
		expectedFile.AddEnum(errorCodeEnum)

		structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
		structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("x"), mojom.SimpleTypeInt32, nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("y"), mojom.BuiltInType("string"), nil))
		structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
		expectedFile.AddStruct(structFoo)

		interfaceDoer := mojom.NewMojomInterface(mojom.DeclTestData("Doer"))
		interfaceDoer.InitAsScope(mojom.NewTestFileScope("test.scope"))

		// The first reference to Foo inside of interface Doer
		fooRef1 := mojom.NewUserTypeRef("Foo", false, false, interfaceDoer.Scope(), lexer.Token{})

		// The second reference to Foo inside of interface Doer. nullable=true
		fooRef2 := mojom.NewUserTypeRef("Foo", true, false, interfaceDoer.Scope(), lexer.Token{})

		params := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		params.InitAsScope(mojom.NewTestFileScope("test.scope"))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("lemon"), mojom.SimpleTypeInt8, nil))
		params.AddField(mojom.NewStructField(mojom.DeclTestData("pipe"), mojom.BuiltInType("handle<message_pipe>"), nil))
		responseParams := mojom.NewMojomStruct(mojom.DeclTestData("dummy"))
		responseParams.InitAsScope(mojom.NewTestFileScope("test.scope"))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("someFoos"), mojom.NewArrayTypeRef(fooRef1, -1, false), nil))
		responseParams.AddField(mojom.NewStructField(mojom.DeclTestData("anotherFoo"), fooRef2, nil))

		interfaceDoer.AddMethod(mojom.NewMojomMethod(mojom.DeclTestData("DoIt"), params, responseParams))
		expectedFile.AddInterface(interfaceDoer)
	}
	endTestCase()

	////////////////////////////////////////////////////////////
	// Execute all of the test cases.
	////////////////////////////////////////////////////////////
	for _, c := range cases {
		descriptor := mojom.NewMojomDescriptor()
		parser := MakeParser(c.fileName, c.fileName, c.mojomContents, descriptor, nil)
		parser.Parse()
		if !parser.OK() {
			t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
		} else {
			got := parser.GetMojomFile().String()
			expected := c.expectedFile.String()
			if got != expected {
				t.Errorf("%s:\n*****expected:\n%s\n****actual\n%s", c.fileName, expected, got)
			}
		}
	}
}