blob: f1434e22eac07226dff573a8ccd95ee0755b9b72 [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/mojom"
"mojom/mojom_tool/parser"
"strings"
"testing"
)
type singleFileTestCase struct {
fileName string
mojomContents string
importedFrom *mojom.MojomFile
expectedErrors []string
}
type singleFileTest struct {
cases []singleFileTestCase
testCaseNum int
}
func (test *singleFileTest) addTestCase(contents string, expectedErrors []string) {
fileName := fmt.Sprintf("file%d", test.testCaseNum)
var importedFrom *mojom.MojomFile = nil
test.cases = append(test.cases, singleFileTestCase{fileName, contents, importedFrom, expectedErrors})
test.testCaseNum += 1
}
// TestSingleFileResolutionErrors() tests that appropriate error messages are generated
// when a .mojom file contains unresolved references.
func TestSingleFileResolutionErrors(t *testing.T) {
test := singleFileTest{}
////////////////////////////////////////////////////////////
// Test Case: One unresolved value reference
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
int32 x = Bar;
};`
test.addTestCase(contents, []string{
"Undefined value: \"Bar\"",
"int32 x = Bar;",
"^^^"})
}
////////////////////////////////////////////////////////////
// Test Case:Two unresolved value references
////////////////////////////////////////////////////////////
{
contents := `
const bool F = Baz;
struct Foo{
int32 x = Bar;
};`
test.addTestCase(contents, []string{
"Undefined value: \"Baz\"", "Undefined value: \"Bar\""})
}
////////////////////////////////////////////////////////////
// Test Case: One unresolved type reference
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
Bar x;
};`
test.addTestCase(contents, []string{"Undefined type: \"Bar\""})
}
////////////////////////////////////////////////////////////
// Test Case: Multiple unresolved types and values.
////////////////////////////////////////////////////////////
{
contents := `
const int32 X = Boom;
struct Foo{
Bar x;
Baz y = Bing;
int32 z = X;
};
struct Foo2 {
Foo f = default;
};`
test.addTestCase(contents, []string{
"Undefined type: \"Bar\"", "Undefined type: \"Baz\"",
"Undefined value: \"Boom\"", "Undefined value: \"Bing\"",
"Use of unresolved value: \"X\"",
"^^^^"}) // There will be four carets in the snippet because of "Boom"
}
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range test.cases {
// Parse and resolve the mojom input.
descriptor := mojom.NewMojomDescriptor()
specifiedName := ""
if c.importedFrom == nil {
specifiedName = c.fileName
}
parser := parser.MakeParser(c.fileName, specifiedName, c.mojomContents, descriptor, c.importedFrom)
parser.Parse()
if !parser.OK() {
t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
continue
}
err := descriptor.Resolve()
if err == nil {
t.Errorf("Resolution unexpectedly succeeded for test case %d.", i)
continue
}
got := err.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)
}
}
}
}
// TestSingleFileValueValidationErrors() test the function
// UserValueRef.validateAfterResolution(). It tests that appropriate error messages are generated
// when a .mojom file contains validation errors. In particular we are testing errors
// that are not detected during parsing but are only detected after all names have
// been resolved. Thus we are not testing here, for example, an attempt to assign
// an int32 literal value to a string variable, because that type of error can be
// detected during parsing. But we are testing the case of a constant whose value is
// an int32 literal being assigned to a string variable.
func TestSingleFileValueValidationErrors(t *testing.T) {
test := singleFileTest{}
////////////////////////////////////////////////////////////
// Group 1: The left-hand-side is an int32 variable.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Assign constant of bool type to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
const bool Bar = true;
struct Foo{
int32 x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value true of type bool may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant of string type to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
const string Bar = "Hi Bar";
struct Foo{
int32 x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value \"Hi Bar\" of type string may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant with value double.INFINITY to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
const double Bar = double.INFINITY;
struct Foo{
int32 x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value double.INFINITY may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign double.INFINITY directly to an int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
int32 x = double.INFINITY;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"double.INFINITY may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant of enum value to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = TWO;
struct Foo{
int32 x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value MyEnum.TWO may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign enum value directly to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const int32 Bar = MyEnum.TWO;
`
test.addTestCase(contents, []string{
"Illegal assignment",
"The enum value MyEnum.TWO of type MyEnum may not be assigned to Bar of type int32"})
}
////////////////////////////////////////////////////////////
// Group 2: The left-hand-side is a string variable.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Assign constant of int32 type to string variable.
////////////////////////////////////////////////////////////
{
contents := `
const int32 Bar = 42;
struct Foo{
string? x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value 42 of type int8 may not be assigned to x of type string?"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant with value double.INFINITY to a string variable.
////////////////////////////////////////////////////////////
{
contents := `
const double Bar = double.INFINITY;
struct Foo{
string x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value double.INFINITY may not be assigned to x of type string"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign double.INFINITY directly to a string variable.
////////////////////////////////////////////////////////////
{
contents := `
const string Bar = double.INFINITY;
`
test.addTestCase(contents, []string{
"Illegal assignment",
"double.INFINITY may not be assigned to Bar of type string"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant of enum value to string variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = TWO;
struct Foo{
string x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value MyEnum.TWO may not be assigned to x of type string"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign enum value directly to a string variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const string Bar = MyEnum.TWO;
`
test.addTestCase(contents, []string{
"Illegal assignment",
"The enum value MyEnum.TWO of type MyEnum may not be assigned to Bar of type string"})
}
////////////////////////////////////////////////////////////
// Group 3: The left-hand-side is an enum variable.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Assign constant of bool type to an enum variable.
////////////////////////////////////////////////////////////
{
contents := `
const bool Bar = true;
enum MyEnum {
ONE,
TWO,
THREE
};
struct Foo{
MyEnum x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value true of type bool may not be assigned to x of type MyEnum"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant of value double.INFINITY to an enum variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const double Bar = double.INFINITY;
struct Foo{
MyEnum x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value double.INFINITY may not be assigned to x of type MyEnum"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign double.INFINITY directly to an enum variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = double.INFINITY;
`
test.addTestCase(contents, []string{
"Illegal assignment",
"double.INFINITY may not be assigned to Bar of type MyEnum"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant of enum value to a variable of a different enum type.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
enum MyOtherEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = TWO;
struct Foo{
MyOtherEnum x = Bar;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value MyEnum.TWO may not be assigned to x of type MyOtherEnum"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign enum value to a variable of a different enum type.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
enum MyOtherEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = MyOtherEnum.TWO;
`
test.addTestCase(contents, []string{
"Illegal assignment",
"The enum value MyOtherEnum.TWO of type MyOtherEnum may not be assigned to Bar of type MyEnum"})
}
////////////////////////////////////////////////////////////
// Test Case: Use an enum value from a different enum as an enum value initializer.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
enum MyOtherEnum {
ONE,
TWO = MyEnum.TWO,
THREE
};
`
test.addTestCase(contents, []string{
"Illegal assignment",
"The enum value MyEnum.TWO of type MyEnum may not be used as an initializer for TWO of type MyOtherEnum."})
}
////////////////////////////////////////////////////////////
// Test Case: Use a constant with a value of an enum value from a different enum as an enum value initializer.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = MyEnum.TWO;
enum MyOtherEnum {
ONE,
TWO = Bar,
THREE
};
`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar with the value MyEnum.TWO may not be used as an initializer for TWO of type MyOtherEnum."})
}
////////////////////////////////////////////////////////////
// Test Case: Assign the default keyword to a variable of enum type.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
};
struct Foo{
MyEnum x = default;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"The 'default' keyword may not be used with the field x of type MyEnum."})
}
////////////////////////////////////////////////////////////
// Group 4: The left-hand-side is an interface varialbe.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Assign an enum value to an interface
////////////////////////////////////////////////////////////
{
contents := `
interface Foo{
enum MyEnum {
ONE
};
};
struct Bar{
Foo&? x = Foo.MyEnum.ONE;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"The enum value Foo.MyEnum.ONE of type Foo.MyEnum may not be assigned to x of type Foo&?."})
}
////////////////////////////////////////////////////////////
// Test Case: Assign constant of value double.INFINITY to an enum variable.
////////////////////////////////////////////////////////////
{
contents := `
interface Foo{
const double BIG = double.INFINITY;
};
struct Bar{
Foo? x = Foo.BIG;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Foo.BIG with the value double.INFINITY may not be assigned to x of type Foo?."})
}
////////////////////////////////////////////////////////////
// Test Case: Assign double.INFINITY directly to an interface variable.
////////////////////////////////////////////////////////////
{
contents := `
interface Foo{
};
struct Bar{
Foo& x = double.INFINITY;
};
`
test.addTestCase(contents, []string{
"Illegal assignment",
"double.INFINITY may not be assigned to x of type Foo&."})
}
////////////////////////////////////////////////////////////
// Test Case: Assign the default keyword to a variable of interface type.
////////////////////////////////////////////////////////////
{
contents := `
interface Foo{
};
struct Bar{
Foo& x = default;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"The 'default' keyword may not be used with the field x of type Foo&."})
}
////////////////////////////////////////////////////////////
// Group 5: Multiple indirection
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Assign doubly-indirected constant of bool type to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
const bool Bar = true;
const bool Baz = Bar;
struct Foo{
int32 x = Baz;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Baz with the value true of type bool may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign doubly-indirected constant with value double.INFINITY to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
const double Bar = double.INFINITY;
const double Baz = Bar;
struct Foo{
int32 x = Baz;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Baz with the value double.INFINITY may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign doubly-indirected constant of enum value to int32 variable.
////////////////////////////////////////////////////////////
{
contents := `
enum MyEnum {
ONE,
TWO,
THREE
};
const MyEnum Bar = TWO;
const MyEnum Baz = Bar;
struct Foo{
int32 x = Baz;
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Baz with the value MyEnum.TWO may not be assigned to x of type int32"})
}
////////////////////////////////////////////////////////////
// Group 4: Invalid EnumValue initializers.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Use string as enum value initializer.
////////////////////////////////////////////////////////////
{
contents := `
const int32 Foo = 8;
const string Bar = "fly";
enum MyEnum {
ONE = Foo,
TWO = Bar,
THREE
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"Bar cannot be used as an enum value initializer because its value, \"fly\", is not a signed 32-bit integer"})
}
////////////////////////////////////////////////////////////
// Test Case: Use double.NAN as an enum value initializer.
////////////////////////////////////////////////////////////
{
contents := `
const int32 Foo = 8;
const int64 Bar = 9;
enum MyEnum {
ONE = Foo,
TWO = Bar,
THREE = double.NAN
};`
test.addTestCase(contents, []string{
"Illegal assignment",
"double.NAN cannot be used as an enum value initializer"})
}
////////////////////////////////////////////////////////////
// Group 5: Non-referent shadows referent
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Field name shadows constant name
////////////////////////////////////////////////////////////
{
contents := `
const int32 foo = 42;
struct MyStruct {
string foo = "hello";
int32 bar = foo;
};`
test.addTestCase(contents, []string{
"Error",
"\"foo\" does not refer to a value. It refers to the field MyStruct.foo at"})
}
////////////////////////////////////////////////////////////
// Test Case: Method name shadows constant name
////////////////////////////////////////////////////////////
{
contents := `
const int32 foo = 42;
interface MyInterface {
foo();
const int32 bar = foo;
};`
test.addTestCase(contents, []string{
"Error",
"\"foo\" does not refer to a value. It refers to the method MyInterface.foo at"})
}
////////////////////////////////////////////////////////////
// Test Case: Field name shadows type name
////////////////////////////////////////////////////////////
{
contents := `
struct SomethingGood{};
struct MySecondStruct {
int32 SomethingGood = 7;
SomethingGood x = default;
};`
test.addTestCase(contents, []string{
"Error",
"\"SomethingGood\" does not refer to a type. It refers to the field MySecondStruct.SomethingGood at"})
}
////////////////////////////////////////////////////////////
// Test Case: Method name shadows type name
////////////////////////////////////////////////////////////
{
contents := `
struct SomethingGood{};
interface MyInterface {
SomethingGood();
DoIt(SomethingGood x);
};`
test.addTestCase(contents, []string{
"Error",
"\"SomethingGood\" does not refer to a type. It refers to the method MyInterface.SomethingGood at"})
}
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range test.cases {
// Parse and resolve the mojom input.
descriptor := mojom.NewMojomDescriptor()
specifiedName := ""
if c.importedFrom == nil {
specifiedName = c.fileName
}
parser := parser.MakeParser(c.fileName, specifiedName, c.mojomContents, descriptor, c.importedFrom)
parser.Parse()
if !parser.OK() {
t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
continue
}
err := descriptor.Resolve()
if err == nil {
t.Errorf("Resolution unexpectedly succeeded for test case %d.", i)
continue
}
got := err.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)
}
}
}
}
// TestSingleFileTypeValidationErrors() test the method UserTypeRef.validateAfterResolution().
// It is similar to TestSingleFileValueValidationErrors()
// except that it tests the validation of types phase which occurs before the validation
// of values phase. This phase detects errors that may not be detected during parsing
// but that may be detected without resolving value references.
func TestSingleFileTypeValidationErrors(t *testing.T) {
test := singleFileTest{}
////////////////////////////////////////////////////////////
// Group 1: Left-hand-side is a struct
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Use struct as constant type
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
};
const Foo MyFoo = 3;
`
test.addTestCase(contents, []string{
"The type Foo is not allowed as the type of a declared constant.",
"Only simple types, strings and enum types may be used"})
}
////////////////////////////////////////////////////////////
// Test Case: Use struct as map key
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
};
struct Bar{
map<Foo, int32> x;
};
`
test.addTestCase(contents, []string{
"The type Foo is not allowed as the key type of a map.",
"Only simple types, strings and enum types may be map keys"})
}
////////////////////////////////////////////////////////////
// Test Case: Assign an integer to a struct variable.
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
};
struct Bar{
Foo x = 42;
};
`
test.addTestCase(contents, []string{
"Illegal assignment",
"Field x of type Foo may not be assigned the value 42 of type int8"})
}
////////////////////////////////////////////////////////////
// Group 2: Left-hand-side is an enum
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Assign an integer to an enum variable in a struct field.
////////////////////////////////////////////////////////////
{
contents := `
enum Hats {
COWBOY,
TOP
};
struct Bar{
Hats my_hat = 1;
};
`
test.addTestCase(contents, []string{
"Illegal assignment",
"Field my_hat of type Hats may not be assigned the value 1 of type int8."})
}
////////////////////////////////////////////////////////////
// Test Case: Assign an integer to an enum variable in a constant.
////////////////////////////////////////////////////////////
{
contents := `
enum Hats {
COWBOY,
TOP
};
const Hats MY_HAT = 1;
`
test.addTestCase(contents, []string{
"Illegal assignment",
"Const MY_HAT of type Hats may not be assigned the value 1 of type int8."})
}
////////////////////////////////////////////////////////////
// Group 3: Invalid use of interface request.
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Make an interface request out of a struct
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
};
struct Bar{
Foo& x;
};
`
test.addTestCase(contents, []string{
"Invalid interface request specification",
"Foo&. Foo is not an interface type"})
}
////////////////////////////////////////////////////////////
// Test Case: Make a nullable interface request out of a struct
////////////////////////////////////////////////////////////
{
contents := `
struct Foo{
};
struct Bar{
Foo&? x;
};
`
test.addTestCase(contents, []string{
"Invalid interface request specification",
"Foo&?. Foo is not an interface type"})
}
////////////////////////////////////////////////////////////
// Group 4: Invalid use of nullable
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// Test Case: Make nullable enum.
////////////////////////////////////////////////////////////
{
contents := `
enum Hats {
COWBOY,
TOP
};
struct Bar{
Hats? my_hat;
};
`
test.addTestCase(contents, []string{
"The type Hats? is invalid because Hats is an enum type and these may not be made nullable."})
}
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range test.cases {
// Parse and resolve the mojom input.
descriptor := mojom.NewMojomDescriptor()
specifiedName := ""
if c.importedFrom == nil {
specifiedName = c.fileName
}
parser := parser.MakeParser(c.fileName, specifiedName, c.mojomContents, descriptor, c.importedFrom)
parser.Parse()
if !parser.OK() {
t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
continue
}
err := descriptor.Resolve()
if err == nil {
t.Errorf("Resolution unexpectedly succeeded for test case %d.", i)
continue
}
got := err.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)
}
}
}
}
////////////////////////////////////////////////
/// Resolution Success Tests
////////////////////////////////////////////////
// testFuncType is a callback used to specify how to test each case.
type testFuncType func(*mojom.MojomDescriptor) error
// singleFileSuccessTestCase stores the data for one test case.
type singleFileSuccessTestCase struct {
mojomContents string
testFunc testFuncType
}
// singleFileSuccessTest contains a series of test cases.
type singleFileSuccessTest struct {
cases []singleFileSuccessTestCase
}
// addTestCase() should be invoked at the start of a case in
// TestSingleFileResolutionSuccess.
func (test *singleFileSuccessTest) addTestCase(moduleNameSpace, contents string, testFunc testFuncType) {
test.cases = append(test.cases, singleFileSuccessTestCase{contents, testFunc})
}
// TestSingleFileResolutionSuccess() iterates through a series of test cases.
// For each case we expect for parsing and resolution to succeed. Then we
// execute a given callback test function to test that resolution produced
// the desired result.
func TestSingleFileResolutionSuccess(t *testing.T) {
test := singleFileSuccessTest{}
////////////////////////////////////////////////////////////
// Test Case: A local constant name shadows an enum name.
////////////////////////////////////////////////////////////
{
contents := `
enum Color{
RED, BLUE
};
struct MyStruct {
const Color RED = BLUE;
Color a_color = RED; // This should resolve to the local constant RED,
// and therefore the concrete value should be BLUE.
};`
testFunc := func(descriptor *mojom.MojomDescriptor) error {
myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct)
aColorField := myStructType.FieldsInLexicalOrder[0]
concreteValue := aColorField.DefaultValue.ResolvedConcreteValue().(*mojom.EnumValue)
key := concreteValue.ValueKey()
if key != "TYPE_KEY:Color.BLUE" {
return fmt.Errorf("%s != TYPE_KEY:Color.BLUE", key)
}
return nil
}
test.addTestCase("", contents, testFunc)
}
////////////////////////////////////////////////////////////
// Execute all of the test cases.
////////////////////////////////////////////////////////////
for i, c := range test.cases {
// Parse and resolve the mojom input.
descriptor := mojom.NewMojomDescriptor()
fileName := fmt.Sprintf("file%d", i)
parser := parser.MakeParser(fileName, fileName, c.mojomContents, descriptor, nil)
parser.Parse()
if !parser.OK() {
t.Errorf("Parsing error for %s: %s", fileName, parser.GetError().Error())
continue
}
err := descriptor.Resolve()
if err != nil {
t.Errorf("Resolution failed for test case %d: %s", i, err.Error())
continue
}
if c.testFunc != nil {
if err := c.testFunc(descriptor); err != nil {
t.Errorf("%s:\n%s", fileName, err.Error())
continue
}
}
}
}