blob: eb481789b712a4c29090ff74dacb05185be8a84d [file] [log] [blame]
// Copyright 2015 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.
package parser
import (
"fmt"
"mojom/mojom_tool/lexer"
"mojom/mojom_tool/mojom"
"strings"
"testing"
)
// 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(mojom.NewModuleNamespace(moduleNameSpace, nil))
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(lexer.Token{})
expectedFile.Attributes.List = append(expectedFile.Attributes.List,
mojom.NewMojomAttribute("cool", nil, mojom.MakeBoolLiteralValue(true, nil)))
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (import statements only)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `import "a.file";`
expectedFile.AddImport(mojom.NewImportedFile("a.file", nil))
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (module and import statements only)
////////////////////////////////////////////////////////////
startTestCase("mojom.test")
cases[testCaseNum].mojomContents = `
module mojom.test;
import "a.file";`
{
expectedFile.AddImport(mojom.NewImportedFile("a.file", nil))
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(lexer.Token{})
expectedFile.Attributes.List = append(expectedFile.Attributes.List,
mojom.NewMojomAttribute("cool", nil, mojom.MakeBoolLiteralValue(true, nil)))
expectedFile.AddImport(mojom.NewImportedFile("a.file", nil))
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, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x8"),
mojom.SimpleTypeInt8, mojom.MakeInt8LiteralValue(-0x7F, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu16"),
mojom.SimpleTypeUInt16, mojom.MakeUint16LiteralValue(65535, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x16"),
mojom.SimpleTypeInt16, mojom.MakeInt16LiteralValue(-32767, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu32"),
mojom.SimpleTypeUInt32, mojom.MakeUint32LiteralValue(0xFFFFFFFF, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x32"),
mojom.SimpleTypeInt32, mojom.MakeInt32LiteralValue(-0x7FFFFFFF, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("xu64"),
mojom.SimpleTypeUInt64, mojom.MakeUint64LiteralValue(18446744073709551615, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("x64"),
mojom.SimpleTypeInt64, mojom.MakeInt64LiteralValue(-9223372036854775807, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("manyNines"),
mojom.SimpleTypeUInt64, mojom.MakeUint64LiteralValue(9999999999999999999, nil)))
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, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("y"),
mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(123456789.123456789, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("z"),
mojom.SimpleTypeFloat, mojom.MakeDoubleLiteralValue(-0.01, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("w"),
mojom.SimpleTypeDouble, mojom.MakeDoubleLiteralValue(-0.01, nil)))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("r"),
mojom.SimpleTypeDouble, mojom.MakeDoubleLiteralValue(3.14159e+40, nil)))
}
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@0;
};`
{
expectedFile.AddImport(mojom.NewImportedFile("another.file", nil))
expectedFile.AddImport(mojom.NewImportedFile("and.another.file", nil))
structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
attributes := mojom.NewAttributes(lexer.Token{})
attributes.List = append(attributes.List, mojom.NewMojomAttribute("happy", nil, mojom.MakeBoolLiteralValue(true, nil)))
structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataAWithOrdinal("x", attributes, 0), 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@0 = 42;
[age=7, level="high"] string y = "Howdy!";
string? z;
bool w@3 = false;
};`
{
expectedFile.AddImport(mojom.NewImportedFile("another.file", nil))
expectedFile.AddImport(mojom.NewImportedFile("and.another.file", nil))
structFoo := mojom.NewMojomStruct(mojom.DeclTestData("Foo"))
structFoo.InitAsScope(mojom.NewTestFileScope("test.scope"))
structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("x", 0), mojom.SimpleTypeInt32, mojom.MakeInt8LiteralValue(42, nil)))
attributes := mojom.NewAttributes(lexer.Token{})
attributes.List = append(attributes.List, mojom.NewMojomAttribute("age", nil, mojom.MakeInt8LiteralValue(7, nil)))
attributes.List = append(attributes.List, mojom.NewMojomAttribute("level", nil, mojom.MakeStringLiteralValue("high", nil)))
structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataA("y", attributes), mojom.BuiltInType("string"), mojom.MakeStringLiteralValue("Howdy!", nil)))
structFoo.AddField(mojom.NewStructField(mojom.DeclTestData("z"), mojom.BuiltInType("string?"), nil))
structFoo.AddField(mojom.NewStructField(mojom.DeclTestDataWithOrdinal("w", 3), mojom.BuiltInType("bool"), mojom.MakeBoolLiteralValue(false, 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(mojom.NewImportedFile("another.file", nil))
expectedFile.AddImport(mojom.NewImportedFile("and.another.file", nil))
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(mojom.NewImportedFile("gpu/interfaces/command_buffer.mojom", nil))
attributes := mojom.NewAttributes(lexer.Token{})
attributes.List = append(attributes.List, mojom.NewMojomAttribute("ServiceName", nil, mojom.MakeStringLiteralValue("mojo::Gpu", nil)))
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(lexer.Token{})
expectedFile.Attributes.List = append(expectedFile.Attributes.List,
mojom.NewMojomAttribute("php_namespace", nil, mojom.MakeStringLiteralValue("mojom.test.php", nil)))
expectedFile.AddImport(mojom.NewImportedFile("another.file", nil))
expectedFile.AddImport(mojom.NewImportedFile("and.another.file", nil))
expectedFile.AddConstant(mojom.NewUserDefinedConstant(mojom.DeclTestData("TOO_SMALL_VALUE"),
mojom.SimpleTypeInt8, mojom.MakeInt8LiteralValue(6, nil)))
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, nil))
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)
}
}
}
}
// TestErrorParsing contains a series of test cases in which we
// run the parser on invalid mojom input string and compare the resulting
// error message to an expected one.
func TestErrorParsing(t *testing.T) {
type testCase struct {
fileName string
mojomContents string
expectedErrors []string
}
cases := make([]testCase, 0)
testCaseNum := 0
startTestCase := func(moduleNameSpace string) {
fileName := fmt.Sprintf("file%d", testCaseNum)
cases = append(cases, testCase{fileName: fileName})
}
expectError := func(expectedError string) {
cases[testCaseNum].expectedErrors = append(cases[testCaseNum].expectedErrors, expectedError)
}
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 (naked attributes)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = "[cool=true]"
expectError("The .mojom file contains an attributes section but nothing else.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (attributes directly before an import)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
[cool=true]
import "another.file";
`
expectError("Attributes are not allowed before an import statement.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (two sets of initial naked attributes)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
[cool=true]
[not-cool=false]
`
expectError("Unexpected ")
expectError("'['")
expectError("Expecting module, import, interface, struct, union, enum or constant.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (two sets of initial attributes with a module)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
[cool=true]
[not-cool=false]
module mojom.test;
`
expectError("Unexpected ")
expectError("'['")
expectError("Expecting module, import, interface, struct, union, enum or constant.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (import before module)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
import "another.file";
module mojom.test;
`
expectError("The module declaration must come before the import statements.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: empty string
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x@;
};
`
expectError("field \"x\": Invalid ordinal string following '@'")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: Not a number
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x@happy;
};
`
expectError("field \"x\": Invalid ordinal string following '@'")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: Negative
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x@-500;
};
`
expectError("field \"x\": Invalid ordinal string following '@'")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: too big for uint32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x@4294967295;
};
`
expectError("field \"x\": Invalid ordinal string following '@'")
expectError("4294967295")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: too big for uint64)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x@999999999999999999999999999999999999999;
};
`
expectError("field \"x\": Invalid ordinal string following '@'")
expectError("999999999999999999999999999999999999999")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: too big for size of struct)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x;
int32 y@2;
};
`
expectError("Invalid ordinal for field y: 2.")
expectError("A struct field ordinal must be a non-negative integer value less than the number of fields in the struct.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: implicit next value too big for size of struct)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x;
int32 y@2;
int32 z;
};
`
expectError("Invalid ordinal for field z: 3.")
expectError("A struct field ordinal must be a non-negative integer value less than the number of fields in the struct.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: Duplicate explicit)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x@0;
int32 y;
int32 z@0;
};
`
expectError("Invalid ordinal for field z: 0.")
expectError("There is already a field in struct MyStruct with that ordinal: x")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid struct field ordinal: Duplicate implicit)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
int32 x;
int32 y;
int32 z@1;
};
`
expectError("Invalid ordinal for field z: 1.")
expectError("There is already a field in struct MyStruct with that ordinal: y")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid union field ordinal: too big for uint32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
union MyUnion {
int8 x;
int16 y@4294967295;
int32 z;
};
`
expectError("union field \"y\": Invalid ordinal string following '@'")
expectError("4294967295")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid union field ordinal: implicit next value too big for uint32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
union MyUnion {
int8 x;
int16 y@4294967294;
int32 z;
};
`
expectError("Invalid tag for field z")
expectError("4294967295")
expectError("A union field tag must be between 0 and 4,294,967,294.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid union field ordinal: Duplicate explicit)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
union MyUnion {
int8 x@0;
int16 y;
int32 z@0;
};
`
expectError("Invalid tag for field z: 0.")
expectError("There is already a field in union MyUnion with that tag: x")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid union field ordinal: Duplicate implicit)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
union MyUnion {
int8 x;
int16 y;
int32 z@1;
};
`
expectError("Invalid tag for field z: 1.")
expectError("There is already a field in union MyUnion with that tag: y")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid method ordinal: too big for uint32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
MethodA@4294967295();
};
`
expectError("MethodA")
expectError("4294967295")
expectError("Invalid ordinal string")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid method ordinal: too big for int64)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
MethodA@999999999999999999999999999999999999999();
};
`
expectError("MethodA")
expectError("999999999999999999999999999999999999999")
expectError("Invalid ordinal string")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Invalid method ordinal: negative)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
MethodA@-1();
};
`
expectError("MethodA")
// Note that the lexer return "@" as the ordinal token, stopping when
// it sees the non-digit "-".
expectError("Invalid ordinal string")
expectError("Ordinals must be decimal integers between 0 and 4294967294")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Constant integer too big for uint64)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
const uint64 manyNines = 99999999999999999999;
`
expectError("Integer literal value out of range")
expectError("99999999999999999999")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Constant float too big for double)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
const uint64 veryBig = 3.14159E400;
`
expectError("Float literal value out of range: 3.14159E400")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Use array as constant type)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
const array<uint64> Foo = 0;
`
expectError("The type array<uint64> is not allowed as the type of a declared constant.")
expectError("Only simple types, strings and enum types may be the types of constants.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Identifier ends with a dot)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
const array<my.Foo.Type.> Foo = 0;
`
expectError("Invalid identifier")
expectError("\"my.Foo.Type.\"")
expectError("Identifiers may not end with a dot")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Unrecognized type of handle)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
handle<drawer> x;
};
`
expectError("Unrecognized type of handle: handle<drawer>")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Nullable bool)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
bool? x;
};
`
expectError("The type bool? is invalid because the type bool may not be made nullable")
endTestCase()
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range cases {
descriptor := mojom.NewMojomDescriptor()
parser := MakeParser(c.fileName, c.fileName, c.mojomContents, descriptor, nil)
parser.Parse()
if parser.OK() {
t.Errorf("Parsing was supposed to fail but did not for test case %d", i)
} else {
got := parser.GetError().Error()
for _, expected := range c.expectedErrors {
if !strings.Contains(got, expected) {
t.Errorf("%s:\n*****expected to contain:\n%s\n****actual\n%s", c.fileName, expected, got)
}
}
}
}
}
// TestInvalidAssignmentDuringParsing contains a series of test cases in which we
// run the parser on invalid mojom input string and compare the resulting
// error message to an expected one. The particular type of error we are testing
// here is invalid assignments of values to variables that may be detected during
// parsing.
func TestInvalidAssignmentDuringParsing(t *testing.T) {
type testCase struct {
fileName string
mojomContents string
expectedErrors []string
}
cases := make([]testCase, 0)
testCaseNum := 0
startTestCase := func(moduleNameSpace string) {
fileName := fmt.Sprintf("file%d", testCaseNum)
cases = append(cases, testCase{fileName: fileName})
}
expectError := func(expectedError string) {
cases[testCaseNum].expectedErrors = append(cases[testCaseNum].expectedErrors, expectedError)
}
endTestCase := func() {
testCaseNum += 1
}
////////////////////////////////////////////////////////////
// Group 1: Assign to struct field default value.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case (Assign string to int32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
int32 x = "hello";
};
`
expectError("Illegal assignment")
expectError("Field x of type int32 may not be assigned the value \"hello\" of type string.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign int32 to string)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
string? x = 42;
};
`
expectError("Illegal assignment")
expectError("Field x of type string? may not be assigned the value 42 of type int8.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign negative number to unit8)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
uint8 x = -1;
};
`
expectError("Illegal assignment")
expectError("Field x of type uint8 may not be assigned the value -1 of type int8.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign large integer to unit8)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
uint8 x = 9999999999;
};
`
expectError("Illegal assignment")
expectError("Field x of type uint8 may not be assigned the value 9999999999 of type int64.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign large float to float32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
float x = 3.14159E40;
};
`
expectError("Illegal assignment")
expectError("Field x of type float may not be assigned the value 3.14159e+40 of type double.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign default keyword to string)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
string x = default;
};
`
expectError("Illegal assignment")
expectError("The 'default' keyword may not be used with the field x of type string")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign integer to array field)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
};
struct Bar {
array<Foo?, 8>? x = 7;
};
`
expectError("Illegal assignment")
expectError("Field x of type array<Foo?, 8>? may not be assigned the value 7 of type int8.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign default keyword to map field)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
};
struct Bar {
map<bool, Foo?>? x = default;
};
`
expectError("Illegal assignment")
expectError("The 'default' keyword may not be used with the field x of type map<bool, Foo?>?.")
endTestCase()
////////////////////////////////////////////////////////////
// Group 2: Assign to constant.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case (Assign boolean to int32)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
const int32 Foo = true;
`
expectError("Illegal assignment")
expectError("Constant Foo of type int32 may not be assigned the value true of type bool.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (Assign string to bool)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
const bool Foo = "true";
`
expectError("Illegal assignment")
expectError("Constant Foo of type bool may not be assigned the value \"true\" of type string.")
endTestCase()
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range cases {
descriptor := mojom.NewMojomDescriptor()
parser := MakeParser(c.fileName, c.fileName, c.mojomContents, descriptor, nil)
parser.Parse()
if parser.OK() {
t.Errorf("Parsing was supposed to fail but did not for test case %d", i)
} else {
got := parser.GetError().Error()
for _, expected := range c.expectedErrors {
if !strings.Contains(got, expected) {
t.Errorf("%s:\n*****expected to contain:\n%s\n****actual\n%s", c.fileName, expected, got)
}
}
}
}
}
// TestLexerErrors contains a series of test cases in which we
// run the parser on invalid mojom input string and compare the resulting
// error message to an expected one. The particular type of error we are testing
// here are cases in which the lexer detects an error and returns one of
// its error tokens.
func TestLexerErrors(t *testing.T) {
type testCase struct {
fileName string
mojomContents string
expectedErrors []string
}
cases := make([]testCase, 0)
testCaseNum := 0
startTestCase := func(moduleNameSpace string) {
fileName := fmt.Sprintf("file%d", testCaseNum)
cases = append(cases, testCase{fileName: fileName})
}
expectError := func(expectedError string) {
cases[testCaseNum].expectedErrors = append(cases[testCaseNum].expectedErrors, expectedError)
}
endTestCase := func() {
testCaseNum += 1
}
////////////////////////////////////////////////////////////
// Group 1: Unterminated comment
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Unterminated comment at start of file.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
/*
* The woods are lovely
* dark and deep
* but I have promises to keep.
struct Foo {
int32 x = "hello";
};
`
expectError("Error")
expectError("unterminated comment")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case: Unterminated comment at end of file.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
int32 x ;
};
/*
* The woods are lovely
* dark and deep
* but I have promises to keep.
`
expectError("Error")
expectError("unterminated comment")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case: Unterminated comment in the middle.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
int32 x ;
/*
* The woods are lovely
* dark and deep
* but I have promises to keep.
};`
expectError("Error")
expectError("unterminated comment")
endTestCase()
////////////////////////////////////////////////////////////
// Group 2: Unterminated string literal
////////////////////////////////////////////////////////////
/// ////////////////////////////////////////////////////////////
// Test Case: Unterminated string literal in import.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
import "foo.bar
struct Foo {
int32 x = 42;
};
`
expectError("Error")
expectError("unterminated string literal")
endTestCase()
/// ////////////////////////////////////////////////////////////
// Test Case: Unterminated string literal in assignment.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
import "foo.bar";
struct Foo {
string x = "hello;
};
`
expectError("Error")
expectError("unterminated string literal")
endTestCase()
////////////////////////////////////////////////////////////
// Group 3: ErrorIllegalChar
////////////////////////////////////////////////////////////
/// ////////////////////////////////////////////////////////////
// Test Case: ErrorIllegalChar at the beginning of a file.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
/? What the ?/
import "foo.bar"
struct Foo {
int32 x = 42;
};
`
expectError("Error:")
expectError("Unexpected \"/\"")
endTestCase()
/// ////////////////////////////////////////////////////////////
// Test Case: ErrorIllegalChar in the middle.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
import "foo.bar";
struct Foo {
int32 x = %42;
};
`
expectError("Error:")
expectError("Unexpected \"%\"")
endTestCase()
/// ////////////////////////////////////////////////////////////
// Test Case: ErrorIllegalChar at the end
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
import "foo.bar";
struct Foo {
int32 x = 42;
};
*
`
expectError("Error:")
expectError("Unexpected \"*\"")
endTestCase()
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range cases {
descriptor := mojom.NewMojomDescriptor()
parser := MakeParser(c.fileName, c.fileName, c.mojomContents, descriptor, nil)
parser.Parse()
if parser.OK() {
t.Errorf("Parsing was supposed to fail but did not for test case %d", i)
} else {
got := parser.GetError().Error()
for _, expected := range c.expectedErrors {
if !strings.Contains(got, expected) {
t.Errorf("%s:\n*****expected to contain:\n%s\n****actual\n%s", c.fileName, expected, got)
}
}
}
}
}
func TestDuplicateNameErrorsSingleFile(t *testing.T) {
type testCase struct {
fileName string
mojomContents string
expectedErrors []string
}
cases := make([]testCase, 0)
testCaseNum := 0
startTestCase := func(moduleNameSpace string) {
fileName := fmt.Sprintf("file%d", testCaseNum)
cases = append(cases, testCase{fileName: fileName})
}
expectError := func(expectedError string) {
cases[testCaseNum].expectedErrors = append(cases[testCaseNum].expectedErrors, expectedError)
}
fileName := func() string {
return cases[testCaseNum].fileName
}
endTestCase := func() {
testCaseNum += 1
}
////////////////////////////////////////////////////////////
// Test Case: Duplicate struct field name.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct Foo {
int32 x = 3;
string y = "hello";
float x = 1.7;
};
`
expectError("Error")
expectError("Duplicate definition of 'x'.")
expectError("There is already a field with that name in struct Foo.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case: Duplicate union field name.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
union Foo {
int32 x;
string y;
float x ;
};
`
expectError("Error")
expectError("Duplicate definition of 'x'.")
expectError("There is already a field with that name in union Foo.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case: Duplicate method request parameter name.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface Foo {
DoIt(int32 x, string y, float x);
};
`
expectError("Error")
expectError("Duplicate definition of 'x'.")
expectError("There is already a request parameter with that name in method DoIt.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case: Duplicate method response parameter name.
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface Foo {
DoIt() => (int32 x, string y, float x);
};
`
expectError("Error")
expectError("Duplicate definition of 'x'.")
expectError("There is already a response parameter with that name in method DoIt.")
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (duplicate method names)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
MethodA();
MethodB();
MethodC();
MethodB();
MethodD();
};
`
expectError("Duplicate definition for \"MethodB\". Previous definition with the same fully-qualified name:")
expectError("method MyInterface.MethodB at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (A method and an enum with the same name)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
Foo();
Bar();
enum Foo{
PLAID = 1,
CHECKERED = 2
};
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("method MyInterface.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (An enum and a method with the same name)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
Bar();
enum Foo{
PLAID = 1,
CHECKERED = 2
};
Foo();
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("enum MyInterface.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (A method and a constant with the same name)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
Foo();
Bar();
const int32 Foo = 7;
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("method MyInterface.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (A constant and a method with the same name)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
interface MyInterface {
const int32 Foo = 7;
Foo();
Bar();
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("const MyInterface.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (A constant and a field with the same name)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
const int32 Bar = 7;
string Foo;
int32 Bar;
};
`
expectError("Duplicate definition for \"Bar\". Previous definition with the same fully-qualified name:")
expectError("const MyStruct.Bar at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (A field and a constant with the same name)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
struct MyStruct {
string Foo;
int32 Bar;
const int32 Bar = 7;
};
`
expectError("Duplicate definition for \"Bar\". Previous definition with the same fully-qualified name:")
expectError("field MyStruct.Bar at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (two types with the same name in file scope)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
module a.b.c;
interface Foo {
};
struct Bar {
};
struct Foo {
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("interface a.b.c.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (two values with the same name in file scope)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
module a.b.c;
const int32 NUM_HATS = 6;
const string NUM_HATS = "6";
`
expectError("Duplicate definition for \"NUM_HATS\". Previous definition with the same fully-qualified name:")
expectError("const a.b.c.NUM_HATS at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (a value with the same name as a type at file scope)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
module a.b.c;
union Foo {
};
const int32 Foo = 42;
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("union a.b.c.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (a type with the same name as a value)
////////////////////////////////////////////////////////////
startTestCase("")
cases[testCaseNum].mojomContents = `
module a.b.c;
const int32 Foo = 42;
union Foo {
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("const a.b.c.Foo at " + fileName())
endTestCase()
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range cases {
descriptor := mojom.NewMojomDescriptor()
parser := MakeParser(c.fileName, c.fileName, c.mojomContents, descriptor, nil)
parser.Parse()
if parser.OK() {
t.Errorf("Parsing was supposed to fail but did not for test case %d", i)
} else {
got := parser.GetError().Error()
for _, expected := range c.expectedErrors {
if !strings.Contains(got, expected) {
t.Errorf("%s:\n*****expected to contain:\n%s\n****actual\n%s", c.fileName, expected, got)
}
}
}
}
}
func TestDuplicateNameErrorsTwoFiles(t *testing.T) {
type testFile struct {
fileName string
mojomContents string
}
type testCase struct {
file1, file2 testFile
expectedErrors []string
}
cases := make([]testCase, 0)
testCaseNum := 0
startTestCase := func() {
fileNameA := fmt.Sprintf("file%dA", testCaseNum)
fileNameB := fmt.Sprintf("file%dB", testCaseNum)
cases = append(cases, testCase{
file1: testFile{fileName: fileNameA},
file2: testFile{fileName: fileNameB}})
}
expectError := func(expectedError string) {
cases[testCaseNum].expectedErrors = append(cases[testCaseNum].expectedErrors, expectedError)
}
fileName1 := func() string {
return cases[testCaseNum].file1.fileName
}
endTestCase := func() {
testCaseNum += 1
}
////////////////////////////////////////////////////////////
// Test Case (two types with the same name in different scopes)
////////////////////////////////////////////////////////////
startTestCase()
cases[testCaseNum].file1.mojomContents = `
module a.b.c;
struct Foo {
};
`
cases[testCaseNum].file2.mojomContents = `
module a.b;
struct c {
enum Foo{};
};
`
expectError("Duplicate definition for \"Foo\". Previous definition with the same fully-qualified name:")
expectError("struct a.b.c.Foo at " + fileName1())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (two vales with the same name in different scopes)
////////////////////////////////////////////////////////////
startTestCase()
cases[testCaseNum].file1.mojomContents = `
module a.b.c;
enum Hats {
COWBOY = 1,
TOP
};
`
cases[testCaseNum].file2.mojomContents = `
module a.b.c.Hats;
const double TOP = 4.9;
`
expectError("Duplicate definition for \"TOP\". Previous definition with the same fully-qualified name:")
expectError("enum value a.b.c.Hats.TOP at " + fileName1())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (a value with the same name as a type in a different scope)
////////////////////////////////////////////////////////////
startTestCase()
cases[testCaseNum].file1.mojomContents = `
module a.b.c;
interface Hats{};
`
cases[testCaseNum].file2.mojomContents = `
module a.b.c;
const double Hats = 4.9;
`
expectError("Duplicate definition for \"Hats\". Previous definition with the same fully-qualified name:")
expectError("interface a.b.c.Hats at " + fileName1())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (another value with the same name as a type in a different scope)
////////////////////////////////////////////////////////////
startTestCase()
cases[testCaseNum].file1.mojomContents = `
module a.b.c.Hats;
interface COWBOY{};
`
cases[testCaseNum].file2.mojomContents = `
module a.b.c;
enum Hats {
COWBOY = 1,
TOP
};
`
expectError("Duplicate definition for \"COWBOY\". Previous definition with the same fully-qualified name:")
expectError("interface a.b.c.Hats.COWBOY at " + fileName1())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (a type with the same name as a value in a different scope)
////////////////////////////////////////////////////////////
startTestCase()
cases[testCaseNum].file1.mojomContents = `
module a.b.c;
enum Hats {
COWBOY = 1,
TOP
};
`
cases[testCaseNum].file2.mojomContents = `
module a.b.c.Hats;
interface COWBOY{};
`
expectError("Duplicate definition for \"COWBOY\". Previous definition with the same fully-qualified name:")
expectError("enum value a.b.c.Hats.COWBOY at " + fileName1())
endTestCase()
////////////////////////////////////////////////////////////
// Test Case (a value with the same name as a method in a different scope)
////////////////////////////////////////////////////////////
startTestCase()
cases[testCaseNum].file1.mojomContents = `
module a.b;
interface Big {
Run();
};
`
cases[testCaseNum].file2.mojomContents = `
module a.b.Big;
const int32 Run = 0;
`
expectError("Duplicate definition for \"Run\". Previous definition with the same fully-qualified name:")
expectError("method a.b.Big.Run at " + fileName1())
endTestCase()
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range cases {
descriptor := mojom.NewMojomDescriptor()
parser := MakeParser(c.file1.fileName, c.file1.fileName, c.file1.mojomContents, descriptor, nil)
parser.Parse()
if !parser.OK() {
t.Errorf("Parsing was supposed to succeed for file1 but did not for test case %d: %s", i, parser.GetError().Error())
}
parser = MakeParser(c.file2.fileName, c.file2.fileName, c.file2.mojomContents, descriptor, nil)
parser.Parse()
if parser.OK() {
t.Errorf("Parsing was supposed to fail for file2 but did not for test case %d", i)
} else {
got := parser.GetError().Error()
for _, expected := range c.expectedErrors {
if !strings.Contains(got, expected) {
t.Errorf("%s:\n*****expected to contain:\n%s\n****actual\n%s", c.file2.fileName, expected, got)
}
}
}
}
}
// TestLexicalOrdering tests that the DeclaredObjects fields of the
// components of the mojom file are in order of occurrence in the source.
func TestLexicalOrdering(t *testing.T) {
source := `
[Key1="SomeModule",
Key2=10]
module hello.world;
import "import1";
import "import2";
interface InterfaceFoo {
Method1();
enum InnerEnum { };
Method2();
const int8 inner_const = 10;
Method3();
};
struct StructFoo {
int8 field1;
enum InnerEnum { };
int8 field2;
const int8 inner_const = 10;
int8 field3;
};
union UnionFoo {
int8 field1;
int8 field2;
};
enum EnumFoo {
VALUE1,
VALUE2,
};
const int8 const_foo = 10;
`
descriptor := mojom.NewMojomDescriptor()
parser := MakeParser("filename", "filename", source, descriptor, nil)
parser.Parse()
if !parser.OK() {
t.Errorf("Parser was not supposed to fail: %v", parser.GetError().Error())
}
checkEq := func(expected, actual interface{}) {
if expected != actual {
t.Fatalf("Failed check: Expected (%v), Actual (%v)", expected, actual)
}
}
mojomFile := parser.GetMojomFile()
checkEq("Key1", mojomFile.Attributes.List[0].Key)
checkEq("Key1", mojomFile.Attributes.List[0].KeyToken.Text)
checkEq("Key2", mojomFile.Attributes.List[1].Key)
checkEq("Key2", mojomFile.Attributes.List[1].KeyToken.Text)
checkEq("import1", mojomFile.Imports[0].SpecifiedName)
checkEq("import2", mojomFile.Imports[1].SpecifiedName)
mojomInterface := mojomFile.DeclaredObjects[0].(*mojom.MojomInterface)
checkEq("InterfaceFoo", mojomInterface.SimpleName())
checkEq("Method1", mojomInterface.DeclaredObjects[0].(*mojom.MojomMethod).SimpleName())
checkEq("InnerEnum", mojomInterface.DeclaredObjects[1].(*mojom.MojomEnum).SimpleName())
checkEq("Method2", mojomInterface.DeclaredObjects[2].(*mojom.MojomMethod).SimpleName())
checkEq("inner_const", mojomInterface.DeclaredObjects[3].(*mojom.UserDefinedConstant).SimpleName())
checkEq("Method3", mojomInterface.DeclaredObjects[4].(*mojom.MojomMethod).SimpleName())
mojomStruct := mojomFile.DeclaredObjects[1].(*mojom.MojomStruct)
checkEq("StructFoo", mojomStruct.SimpleName())
checkEq("field1", mojomStruct.DeclaredObjects[0].(*mojom.StructField).SimpleName())
checkEq("InnerEnum", mojomStruct.DeclaredObjects[1].(*mojom.MojomEnum).SimpleName())
checkEq("field2", mojomStruct.DeclaredObjects[2].(*mojom.StructField).SimpleName())
checkEq("inner_const", mojomStruct.DeclaredObjects[3].(*mojom.UserDefinedConstant).SimpleName())
checkEq("field3", mojomStruct.DeclaredObjects[4].(*mojom.StructField).SimpleName())
mojomUnion := mojomFile.DeclaredObjects[2].(*mojom.MojomUnion)
checkEq("UnionFoo", mojomUnion.SimpleName())
checkEq("field1", mojomUnion.DeclaredObjects[0].(*mojom.UnionField).SimpleName())
checkEq("field2", mojomUnion.DeclaredObjects[1].(*mojom.UnionField).SimpleName())
mojomEnum := mojomFile.DeclaredObjects[3].(*mojom.MojomEnum)
checkEq("EnumFoo", mojomEnum.SimpleName())
checkEq("VALUE1", mojomEnum.DeclaredObjects[0].(*mojom.EnumValue).SimpleName())
checkEq("VALUE2", mojomEnum.DeclaredObjects[1].(*mojom.EnumValue).SimpleName())
checkEq("const_foo", mojomFile.DeclaredObjects[4].(*mojom.UserDefinedConstant).SimpleName())
}