예제 #1
0
// 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.Fields[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 := 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
			}
		}
	}
}
예제 #2
0
파일: parse_driver.go 프로젝트: ukode/mojo
// Parses each of the given .mojom files and all of the files in the
// import graph rooted by each file. A single MojomDescriptor is created and
// populated with the result of parsing all of those files. If the parsing is
// successful then err will be nil.
//
// fileNames must not be nil or we will panic.
func (d *ParseDriver) ParseFiles(fileNames []string) (descriptor *mojom.MojomDescriptor, err error) {
	if fileNames == nil {
		// We panic instead of returning an error here because this would be a programming error
		// as opposed to an error in the input.
		panic("fileNames may not be nil.")
	}
	filesToProcess := make([]*FileReference, len(fileNames))
	descriptor = mojom.NewMojomDescriptor()
	for i, fileName := range fileNames {
		filesToProcess[i] = &FileReference{specifiedPath: fileName}
	}

	for len(filesToProcess) > 0 {
		currentFile := filesToProcess[0]
		filesToProcess = filesToProcess[1:]
		if err = d.fileProvider.findFile(currentFile); err != nil {
			return
		}

		if !descriptor.ContainsFile(currentFile.absolutePath) {
			contents, fileReadError := d.fileProvider.provideContents(currentFile)
			if fileReadError != nil {
				err = fileReadError
				return
			}
			parser := MakeParser(currentFile.absolutePath, contents, descriptor)
			parser.SetDebugMode(d.debugMode)
			parser.Parse()

			if d.debugMode {
				fmt.Printf("\nParseTree for %s:", currentFile)
				fmt.Println(parser.GetParseTree())
			}

			if !parser.OK() {
				err = fmt.Errorf("\nError while parsing %s: \n%s\n",
					currentFile, parser.GetError().Error())
				return
			}
			mojomFile := d.fileExtractor.extractMojomFile(&parser)
			for _, importedFile := range mojomFile.Imports {
				filesToProcess = append(filesToProcess,
					&FileReference{importedFrom: currentFile, specifiedPath: importedFile})
			}
		}
	}

	// Perform type and value resolution
	if err = descriptor.Resolve(); err != nil {
		return
	}

	// Compute data for generators.
	err = descriptor.ComputeDataForGenerators()
	return
}
예제 #3
0
// Parses each of the given .mojom files and all of the files in the
// import graph rooted by each file. A single MojomDescriptor is created and
// populated with the result of parsing all of those files. The returned
// ParseResult contains the populuted MojomDescriptor and any error that
// occurred.
func (d *ParseDriver) ParseFiles(fileNames []string) ParseResult {
	if fileNames == nil {
		panic("fileNames may not be nil.")
	}
	filesToProcess := make([]*FileReference, len(fileNames))
	descriptor := mojom.NewMojomDescriptor()
	for i, fileName := range fileNames {
		filesToProcess[i] = &FileReference{specifiedPath: fileName}
	}

	for len(filesToProcess) > 0 {
		currentFile := filesToProcess[0]
		filesToProcess = filesToProcess[1:]
		if err := d.fileProvider.FindFile(currentFile); err != nil {
			return ParseResult{Err: err, Descriptor: descriptor}
		}

		if !descriptor.ContainsFile(currentFile.absolutePath) {
			contents, fileReadError := d.fileProvider.ProvideContents(currentFile)
			if fileReadError != nil {
				return ParseResult{Err: fileReadError, Descriptor: descriptor}
			}
			parser := MakeParser(currentFile.absolutePath, contents, descriptor)
			parser.SetDebugMode(d.debugMode)
			parser.Parse()

			if d.debugMode {
				fmt.Printf("\nParseTree for %s:", currentFile)
				fmt.Println(parser.GetParseTree())
			}

			if !parser.OK() {
				parseError := fmt.Errorf("\nError while parsing %s: \n%s\n",
					currentFile, parser.GetError().Error())
				return ParseResult{Err: parseError, Descriptor: descriptor}
			}
			mojomFile := parser.GetMojomFile()
			for _, importedFile := range mojomFile.Imports {
				filesToProcess = append(filesToProcess,
					&FileReference{importedFrom: currentFile, specifiedPath: importedFile})
			}
		}
	}

	// Perform type and value resolution
	if resolutionError := descriptor.Resolve(); resolutionError != nil {
		return ParseResult{Err: resolutionError, Descriptor: descriptor}
	}

	// Compute data for generators.
	computationError := descriptor.ComputeDataForGenerators()
	return ParseResult{Err: computationError, Descriptor: descriptor}
}
예제 #4
0
// TestTwoFileSerialization uses a series of test cases in which the text of two .mojom
// files is specified and the expected MojomFileGraph is specified using Go struct literals.
func TestTwoFileSerialization(t *testing.T) {
	test := twoFileTest{}

	/////////////////////////////////////////////////////////////
	// Test Case: Two top-level files with no import relationship
	/////////////////////////////////////////////////////////////
	{

		contentsA := `
	module a.b.c;
	struct FooA{
	};`

		contentsB := `
	module a.b.c;
	struct FooB{
	};`

		importingName := "" // File A is not importing file B.
		topLevel := true    // Simulate that File B is a top-level file.
		test.addTestCase("a.b.c", contentsA, contentsB, topLevel, importingName)

		// DeclaredMojomObjects
		test.expectedFileA().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooA"}
		test.expectedFileB().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooB"}

		// struct FooA
		test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooA"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: newDeclData(test.expectedFileA().FileName, "FooA", "a.b.c.FooA"),
			Fields:   []mojom_types.StructField{}}}

		// struct FooB
		test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooB"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: newDeclData(test.expectedFileB().FileName, "FooB", "a.b.c.FooB"),
			Fields:   []mojom_types.StructField{}}}

		test.endTestCase()
	}

	/////////////////////////////////////////////////////////////
	// Test Case: Two top-level files where the first imports the second.
	/////////////////////////////////////////////////////////////
	{

		contentsA := `
	module a.b.c;
	import "myLittleFriend";
	struct FooA{
	};`

		contentsB := `
	module a.b.c;
	struct FooB{
	};`

		importingName := "myLittleFriend" // File A is importing File B using this name.
		topLevel := true                  // Simulate the File B is a top-level file.
		test.addTestCase("a.b.c", contentsA, contentsB, topLevel, importingName)

		// DeclaredMojomObjects
		test.expectedFileA().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooA"}
		test.expectedFileB().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooB"}

		// struct FooA
		test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooA"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: newDeclData(test.expectedFileA().FileName, "FooA", "a.b.c.FooA"),
			Fields:   []mojom_types.StructField{}}}

		// struct FooB
		test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooB"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: newDeclData(test.expectedFileB().FileName, "FooB", "a.b.c.FooB"),
			Fields:   []mojom_types.StructField{}}}

		test.endTestCase()
	}

	/////////////////////////////////////////////////////////////
	// Test Case: A top-level file that imports a non-top-level file.
	/////////////////////////////////////////////////////////////
	{

		contentsA := `
	module a.b.c;
	import "myLittleFriend";
	struct FooA{
	};`

		contentsB := `
	module a.b.c;
	struct FooB{
	};`

		importingName := "myLittleFriend" // File A is importing File B using this name.
		topLevel := false                 // Simulate the File B is not a top-level file.
		test.addTestCase("a.b.c", contentsA, contentsB, topLevel, importingName)

		// DeclaredMojomObjects
		test.expectedFileA().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooA"}
		test.expectedFileB().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:a.b.c.FooB"}

		// struct FooA
		test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooA"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: newDeclData(test.expectedFileA().FileName, "FooA", "a.b.c.FooA"),
			Fields:   []mojom_types.StructField{}}}

		// struct FooB
		test.expectedGraph().ResolvedTypes["TYPE_KEY:a.b.c.FooB"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: newDeclData(test.expectedFileB().FileName, "FooB", "a.b.c.FooB"),
			Fields:   []mojom_types.StructField{}}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Execute all of the test cases.
	////////////////////////////////////////////////////////////
	for i, c := range test.cases {
		descriptor := mojom.NewMojomDescriptor()

		// Parse file A.
		parserA := parser.MakeParser(c.expectedFileA.FileName, *c.expectedFileA.SpecifiedFileName, c.mojomContentsA, descriptor, nil)
		parserA.Parse()
		if !parserA.OK() {
			t.Errorf("Parsing error for %s: %s", c.expectedFileA.FileName, parserA.GetError().Error())
			continue
		}
		mojomFileA := parserA.GetMojomFile()

		// Parse file B.
		var importedFrom *mojom.MojomFile
		if !c.topLevel {
			// If file B is not a top-level file then when the parser for it is constructed we give it a non-nil |importedFrom|.
			importedFrom = mojomFileA
		}
		parserB := parser.MakeParser(c.expectedFileB.FileName, *c.expectedFileB.SpecifiedFileName, c.mojomContentsB, descriptor, importedFrom)
		parserB.Parse()
		if !parserB.OK() {
			t.Errorf("Parsing error for %s: %s", c.expectedFileB.FileName, parserB.GetError().Error())
			continue
		}
		mojomFileB := parserB.GetMojomFile()

		// Set the canonical file name for the imported files. In real operation
		// this step is done in parser_driver.go when each of the imported files are parsed.
		if c.importingName != "" {
			// The call to SetCanonicalImportName does a lookup in a map for a key corresponding to the
			// first argument. Thus here we are also testing that |importingName| is in fact string
			// that was parsed from the .mojom file.
			mojomFileA.SetCanonicalImportName(c.importingName, mojomFileB.CanonicalFileName)
		}

		// Resolve
		if err := descriptor.Resolve(); err != nil {
			t.Errorf("Resolve error for case %d: %s", i, err.Error())
			continue
		}
		if err := descriptor.ComputeEnumValueIntegers(); err != nil {
			t.Errorf("ComputeEnumValueIntegers error for case %d: %s", i, err.Error())
			continue
		}
		if err := descriptor.ComputeDataForGenerators(); err != nil {
			t.Errorf("ComputeDataForGenerators error for case %d: %s", i, err.Error())
			continue
		}

		// Serialize
		EmitLineAndColumnNumbers = c.lineAndcolumnNumbers
		bytes, _, err := Serialize(descriptor, false)
		if err != nil {
			t.Errorf("Serialization error for case %d: %s", i, err.Error())
			continue
		}

		// Deserialize
		decoder := bindings.NewDecoder(bytes, nil)
		fileGraph := mojom_files.MojomFileGraph{}
		fileGraph.Decode(decoder)

		// Compare
		if err := compareFileGraphs(c.expectedGraph, &fileGraph); err != nil {
			t.Errorf("case %d:\n%s", i, err.Error())
			continue
		}
	}
}
예제 #5
0
// TestSingleFileSerialization uses a series of test cases in which the text of a .mojom
// file is specified and the expected MojomFileGraph is specified using Go struct literals.
func TestSingleFileSerialization(t *testing.T) {
	test := singleFileTest{}

	////////////////////////////////////////////////////////////
	// Test Case: array of int32
	////////////////////////////////////////////////////////////
	{

		contents := `
	struct Foo{
	  array<int32> bar1;
	  array<int32, 7> bar2;
	  array<int32>? bar3;
	  array<int32, 8>? bar4;
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"}

		// ResolvedTypes

		// struct Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: test.newDeclData("Foo", "Foo"),
			Fields: []mojom_types.StructField{
				// field bar1 is not nullable and not fixed length
				{
					DeclData: test.newDeclData("bar1", ""),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						false, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar2 is not nullable and fixed length of 7
				{
					DeclData: test.newDeclData("bar2", ""),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						false, 7, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar3 is nullable and not fixed length
				{
					DeclData: test.newDeclData("bar3", ""),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						true, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar4 is nullable and fixed length of 8
				{
					DeclData: test.newDeclData("bar4", ""),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						true, 8, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: map string to int32
	////////////////////////////////////////////////////////////
	{

		contents := `
	struct Foo{
	  map<string,  int32>  bar1;
	  map<string?, int32>  bar2;
	  map<string,  int32>? bar3;
	  map<string?, int32>? bar4;
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"}

		// ResolvedTypes

		// struct Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: test.newDeclData("Foo", "Foo"),
			Fields: []mojom_types.StructField{
				// field bar1 is non-nullable with a non-nullable key.
				{
					DeclData: test.newDeclData("bar1", ""),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						false,
						&mojom_types.TypeStringType{mojom_types.StringType{false}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar2 is non-nullable with a nullable key.
				{
					DeclData: test.newDeclData("bar2", ""),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						false,
						&mojom_types.TypeStringType{mojom_types.StringType{true}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar3 is nullable with a non-nullable key.
				{
					DeclData: test.newDeclData("bar3", ""),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						true,
						&mojom_types.TypeStringType{mojom_types.StringType{false}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar4 is nullable with a nullable key.
				{
					DeclData: test.newDeclData("bar4", ""),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						true,
						&mojom_types.TypeStringType{mojom_types.StringType{true}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: enum value initializer
	////////////////////////////////////////////////////////////
	{

		contents := `
	enum Foo{
	  X1 = 42,
	  X2 = X1
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.TopLevelEnums = &[]string{"TYPE_KEY:Foo"}

		// Resolved Values

		// Foo.X1
		test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:         test.newDeclData("X1", "Foo.X1"),
			EnumTypeKey:      "TYPE_KEY:Foo",
			InitializerValue: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{42}},
			IntValue:         -1,
		}}

		// Foo.X2
		test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("X2", "Foo.X2"),
			EnumTypeKey: "TYPE_KEY:Foo",
			InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{
				Identifier: "X1",
				ValueKey:   stringPointer("TYPE_KEY:Foo.X1"),
			}},
			IntValue: -1,
		}}

		// ResolvedTypes

		// enum Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{
			DeclData: test.newDeclData("Foo", "Foo"),
			Values: []mojom_types.EnumValue{
				// Note(rudominer) It is a bug that we need to copy the enum values here.
				// See https://github.com/domokit/mojo/issues/513.
				// value TOP
				test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value COWBOY
				test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"].(*mojom_types.UserDefinedValueEnumValue).Value,
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	{

		contents := `
	[go_namespace="go.test",
	lucky=true,
	planet=EARTH]
	module mojom.test;

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

	const uint16 NUM_MAGI = 3;

	struct Foo{
		int32 x;
		string y = "hello";
		string? z;

		enum Hats {
			TOP,
			COWBOY = NUM_MAGI
		};
	};`

		test.addTestCase("mojom.test", contents)

		// Attributes
		test.expectedFile().Attributes = &[]mojom_types.Attribute{
			{"go_namespace", "go.test"}, {"lucky", "true"}, {"planet", "EARTH"},
		}

		// Imports
		test.expectedFile().Imports = &[]string{
			"another.file.canonical", "and.another.file.canonical",
		}

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:mojom.test.Foo"}
		test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{"TYPE_KEY:mojom.test.NUM_MAGI"}

		// Resolved Values

		// NUM_MAGI
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.NUM_MAGI"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("NUM_MAGI", "mojom.test.NUM_MAGI"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{3}},
		}}

		// Hats.TOP
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("TOP", "mojom.test.Foo.Hats.TOP"),
			EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats",
			IntValue:    -1,
		}}

		// Hats.COWBOY
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("COWBOY", "mojom.test.Foo.Hats.COWBOY"),
			EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats",
			IntValue:    -1,
			InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{
				Identifier: "NUM_MAGI",
				ValueKey:   stringPointer("TYPE_KEY:mojom.test.NUM_MAGI"),
			}},
		}}

		// ResolvedTypes

		// struct Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: &mojom_types.DeclarationData{
				ShortName:        stringPointer("Foo"),
				FullIdentifier:   stringPointer("mojom.test.Foo"),
				DeclaredOrdinal:  -1,
				DeclarationOrder: -1,
				SourceFileInfo: &mojom_types.SourceFileInfo{
					FileName: test.fileName(),
				},
				ContainedDeclarations: &mojom_types.ContainedDeclarations{
					Enums: &[]string{"TYPE_KEY:mojom.test.Foo.Hats"}},
			},
			Fields: []mojom_types.StructField{
				// field x
				{
					DeclData: test.newDeclData("x", ""),
					Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32},
				},
				// field y
				{
					DeclData:     test.newDeclData("y", ""),
					Type:         &mojom_types.TypeStringType{mojom_types.StringType{false}},
					DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueLiteralValue{&mojom_types.LiteralValueStringValue{"hello"}}},
				},
				// field z
				{
					DeclData: test.newDeclData("z", ""),
					Type:     &mojom_types.TypeStringType{mojom_types.StringType{true}},
				},
			},
		}}

		// enum Hats
		test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo.Hats"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{
			DeclData: test.newDeclData("Hats", "mojom.test.Foo.Hats"),
			Values: []mojom_types.EnumValue{
				// Note(rudominer) It is a bug that we need to copy the enum values here.
				// See https://github.com/domokit/mojo/issues/513.
				// value TOP
				test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value COWBOY
				test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"].(*mojom_types.UserDefinedValueEnumValue).Value,
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Execute all of the test cases.
	////////////////////////////////////////////////////////////
	for _, c := range test.cases {
		// Parse and resolve the mojom input.
		descriptor := mojom.NewMojomDescriptor()
		parser := parser.MakeParser(c.fileName, c.mojomContents, descriptor)
		parser.Parse()
		if !parser.OK() {
			t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
			continue
		}
		if err := descriptor.Resolve(); err != nil {
			t.Errorf("Resolve error for %s: %s", c.fileName, err.Error())
			continue
		}
		if err := descriptor.ComputeEnumValueIntegers(); err != nil {
			t.Errorf("ComputeEnumValueIntegers error for %s: %s", c.fileName, err.Error())
			continue
		}
		if err := descriptor.ComputeDataForGenerators(); err != nil {
			t.Errorf("ComputeDataForGenerators error for %s: %s", c.fileName, err.Error())
			continue
		}

		// Simulate setting the canonical file name for the imported files. In real operation
		// this step is done in parser_driver.go when each of the imported files are parsed.
		mojomFile := parser.GetMojomFile()
		if mojomFile.Imports != nil {
			for _, imp := range mojomFile.Imports {
				imp.CanonicalFileName = fmt.Sprintf("%s.canonical", imp.SpecifiedName)
			}
		}

		// Serialize
		EmitLineAndColumnNumbers = c.lineAndcolumnNumbers
		bytes, err := Serialize(descriptor)
		if err != nil {
			t.Errorf("Serialization error for %s: %s", c.fileName, err.Error())
			continue
		}

		// Deserialize
		decoder := bindings.NewDecoder(bytes, nil)
		fileGraph := mojom_files.MojomFileGraph{}
		fileGraph.Decode(decoder)

		// Compare
		if err := compareFileGraphs(c.expectedGraph, &fileGraph); err != nil {
			t.Errorf("%s:\n%s", c.fileName, err.Error())
			continue
		}
	}
}
예제 #6
0
// TestSingleFileSerialization uses a series of test cases in which the text of a .mojom
// file is specified and the expected MojomFileGraph is specified using Go struct literals.
func TestSingleFileSerialization(t *testing.T) {
	test := singleFileTest{}

	////////////////////////////////////////////////////////////
	// Test Case: array of int32
	////////////////////////////////////////////////////////////
	{

		contents := `
	struct Foo{
	  array<int32> bar1;
	  array<int32, 7> bar2;
	  array<int32>? bar3;
	  array<int32, 8>? bar4;
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"}

		// ResolvedTypes

		// struct Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: test.newDeclData("Foo", "Foo"),
			Fields: []mojom_types.StructField{
				// field bar1 is not nullable and not fixed length
				{
					DeclData: test.newShortDeclData("bar1"),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						false, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar2 is not nullable and fixed length of 7
				{
					DeclData: test.newShortDeclData("bar2"),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						false, 7, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar3 is nullable and not fixed length
				{
					DeclData: test.newShortDeclData("bar3"),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						true, -1, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar4 is nullable and fixed length of 8
				{
					DeclData: test.newShortDeclData("bar4"),
					Type: &mojom_types.TypeArrayType{mojom_types.ArrayType{
						true, 8, &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: map string to int32
	////////////////////////////////////////////////////////////
	{

		contents := `
	struct Foo{
	  map<string,  int32>  bar1;
	  map<string?, int32>  bar2;
	  map<string,  int32>? bar3;
	  map<string?, int32>? bar4;
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:Foo"}

		// ResolvedTypes

		// struct Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: test.newDeclData("Foo", "Foo"),
			Fields: []mojom_types.StructField{
				// field bar1 is non-nullable with a non-nullable key.
				{
					DeclData: test.newShortDeclData("bar1"),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						false,
						&mojom_types.TypeStringType{mojom_types.StringType{false}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar2 is non-nullable with a nullable key.
				{
					DeclData: test.newShortDeclData("bar2"),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						false,
						&mojom_types.TypeStringType{mojom_types.StringType{true}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar3 is nullable with a non-nullable key.
				{
					DeclData: test.newShortDeclData("bar3"),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						true,
						&mojom_types.TypeStringType{mojom_types.StringType{false}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
				// field bar4 is nullable with a nullable key.
				{
					DeclData: test.newShortDeclData("bar4"),
					Type: &mojom_types.TypeMapType{mojom_types.MapType{
						true,
						&mojom_types.TypeStringType{mojom_types.StringType{true}},
						&mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32}}},
				},
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: enum value initializer
	////////////////////////////////////////////////////////////
	{

		contents := `
	enum Foo{
	  X0,
	  X1 = 42,
	  X2 = X1
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.TopLevelEnums = &[]string{"TYPE_KEY:Foo"}

		// Resolved Values

		// Foo.X0
		test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X0"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("X0", "Foo.X0"),
			EnumTypeKey: "TYPE_KEY:Foo",
			IntValue:    0,
		}}

		// Foo.X1
		test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:         test.newDeclData("X1", "Foo.X1"),
			EnumTypeKey:      "TYPE_KEY:Foo",
			InitializerValue: &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt8Value{42}},
			IntValue:         42,
		}}

		// Foo.X2
		test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("X2", "Foo.X2"),
			EnumTypeKey: "TYPE_KEY:Foo",
			InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{
				Identifier: "X1",
				ValueKey:   stringPointer("TYPE_KEY:Foo.X1"),
			}},
			IntValue: 42,
		}}

		// ResolvedTypes

		// enum Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Foo"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{
			DeclData: test.newDeclData("Foo", "Foo"),
			Values: []mojom_types.EnumValue{
				// Note(rudominer) It is a bug that we need to copy the enum values here.
				// See https://github.com/domokit/mojo/issues/513.
				// value X1
				test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X0"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value X1
				test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X1"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value X2
				test.expectedGraph().ResolvedValues["TYPE_KEY:Foo.X2"].(*mojom_types.UserDefinedValueEnumValue).Value,
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: enum value name is shadowed by local constant declaration.
	////////////////////////////////////////////////////////////
	{

		contents := `
	enum Color{
	  RED, BLUE
	};

	struct MyStruct {
		const Color RED = BLUE;

        Color a_color = RED; // This should resolve to the local constant RED.
	};`

		test.addTestCase("", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.TopLevelEnums = &[]string{"TYPE_KEY:Color"}
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:MyStruct"}

		// Resolved Values

		// Color.RED
		test.expectedGraph().ResolvedValues["TYPE_KEY:Color.RED"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("RED", "Color.RED"),
			EnumTypeKey: "TYPE_KEY:Color",
			IntValue:    0,
		}}

		// Color.BLUE
		test.expectedGraph().ResolvedValues["TYPE_KEY:Color.BLUE"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("BLUE", "Color.BLUE"),
			EnumTypeKey: "TYPE_KEY:Color",
			IntValue:    1,
		}}

		// MyStruct.RED
		test.expectedGraph().ResolvedValues["TYPE_KEY:MyStruct.RED"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newContainedDeclData("RED", "MyStruct.RED", stringPointer("TYPE_KEY:MyStruct")),
			Type: &mojom_types.TypeTypeReference{mojom_types.TypeReference{
				false, false, stringPointer("Color"), stringPointer("TYPE_KEY:Color")}},
			Value: &mojom_types.ValueUserValueReference{
				mojom_types.UserValueReference{
					Identifier: "BLUE",
					ValueKey:   stringPointer("TYPE_KEY:Color.BLUE")}},
		}}

		// ResolvedTypes

		// enum Color
		test.expectedGraph().ResolvedTypes["TYPE_KEY:Color"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{
			DeclData: test.newDeclData("Color", "Color"),
			Values: []mojom_types.EnumValue{
				// Note(rudominer) It is a bug that we need to copy the enum values here.
				// See https://github.com/domokit/mojo/issues/513.
				// value RED
				test.expectedGraph().ResolvedValues["TYPE_KEY:Color.RED"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value BLUE
				test.expectedGraph().ResolvedValues["TYPE_KEY:Color.BLUE"].(*mojom_types.UserDefinedValueEnumValue).Value,
			},
		}}

		// struct MyStruct
		test.expectedGraph().ResolvedTypes["TYPE_KEY:MyStruct"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: &mojom_types.DeclarationData{
				ShortName:        stringPointer("MyStruct"),
				FullIdentifier:   stringPointer("MyStruct"),
				DeclaredOrdinal:  -1,
				DeclarationOrder: -1,
				SourceFileInfo: &mojom_types.SourceFileInfo{
					FileName: test.fileName(),
				},
				ContainedDeclarations: &mojom_types.ContainedDeclarations{
					Constants: &[]string{"TYPE_KEY:MyStruct.RED"}},
			},
			Fields: []mojom_types.StructField{
				// field a_color
				{
					DeclData: test.newShortDeclData("a_color"),
					Type: &mojom_types.TypeTypeReference{mojom_types.TypeReference{
						false, false, stringPointer("Color"), stringPointer("TYPE_KEY:Color")}},
					DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueUserValueReference{
						mojom_types.UserValueReference{
							Identifier: "RED",
							ValueKey:   stringPointer("TYPE_KEY:MyStruct.RED")}}}, // Note this refers to MyStruct.RED and not Color.RED.
				},
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: In-Out method parameters with the same name.
	////////////////////////////////////////////////////////////
	{

		contents := `
	module test;

	interface EchoService {
      EchoString(string? value) => (string? value);
      DelayedEchoString(string? value, int32 millis) => (string? value);
    };`

		test.addTestCase("test", contents)

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Interfaces = &[]string{"TYPE_KEY:test.EchoService"}

		// ResolvedTypes

		// interface EchoService
		test.expectedGraph().ResolvedTypes["TYPE_KEY:test.EchoService"] = &mojom_types.UserDefinedTypeInterfaceType{mojom_types.MojomInterface{
			DeclData:      test.newDeclData("EchoService", "test.EchoService"),
			InterfaceName: "EchoService",
			Methods: map[uint32]mojom_types.MojomMethod{
				0: mojom_types.MojomMethod{
					DeclData: test.newDeclData("EchoString", ""),
					Parameters: mojom_types.MojomStruct{
						DeclData: test.newDeclData("EchoString-request", ""),
						Fields: []mojom_types.StructField{
							mojom_types.StructField{
								DeclData: test.newDeclData("value", ""),
								Type:     &mojom_types.TypeStringType{mojom_types.StringType{true}},
							},
						},
					},
					ResponseParams: &mojom_types.MojomStruct{
						DeclData: test.newDeclData("EchoString-response", ""),
						Fields: []mojom_types.StructField{
							mojom_types.StructField{
								DeclData: test.newDeclData("value", ""),
								Type:     &mojom_types.TypeStringType{mojom_types.StringType{true}},
							},
						},
					},
				},
				1: mojom_types.MojomMethod{
					DeclData: test.newDeclData("DelayedEchoString", ""),
					Parameters: mojom_types.MojomStruct{
						DeclData: test.newDeclData("DelayedEchoString-request", ""),
						Fields: []mojom_types.StructField{
							mojom_types.StructField{
								DeclData: test.newDeclData("value", ""),
								Type:     &mojom_types.TypeStringType{mojom_types.StringType{true}},
							},
							mojom_types.StructField{
								DeclData: test.newDeclData("millis", ""),
								Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32},
							},
						},
					},
					ResponseParams: &mojom_types.MojomStruct{
						DeclData: test.newDeclData("DelayedEchoString-response", ""),
						Fields: []mojom_types.StructField{
							mojom_types.StructField{
								DeclData: test.newDeclData("value", ""),
								Type:     &mojom_types.TypeStringType{mojom_types.StringType{true}},
							},
						},
					},
					Ordinal: 1,
				},
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: Integer constants
	////////////////////////////////////////////////////////////
	{

		contents := `
	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;
	`
		test.addTestCase("", contents)

		// DeclaredMojomObjects

		test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{
			"TYPE_KEY:xu8", "TYPE_KEY:x8", "TYPE_KEY:xu16", "TYPE_KEY:x16",
			"TYPE_KEY:xu32", "TYPE_KEY:x32", "TYPE_KEY:xu64", "TYPE_KEY:x64"}

		// Resolved Values

		// xu8
		test.expectedGraph().ResolvedValues["TYPE_KEY:xu8"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("xu8", "xu8"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT8},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint8Value{255}},
		}}

		// x8
		test.expectedGraph().ResolvedValues["TYPE_KEY:x8"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("x8", "x8"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT8},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt8Value{-127}},
		}}

		// xu16
		test.expectedGraph().ResolvedValues["TYPE_KEY:xu16"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("xu16", "xu16"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint16Value{0xFFFF}},
		}}

		// x16
		test.expectedGraph().ResolvedValues["TYPE_KEY:x16"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("x16", "x16"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT16},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt16Value{-0x7FFF}},
		}}

		// xu32
		test.expectedGraph().ResolvedValues["TYPE_KEY:xu32"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("xu32", "xu32"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT32},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint32Value{4294967295}},
		}}

		// x32
		test.expectedGraph().ResolvedValues["TYPE_KEY:x32"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("x32", "x32"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt32Value{-2147483647}},
		}}

		// xu64
		test.expectedGraph().ResolvedValues["TYPE_KEY:xu64"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("xu64", "xu64"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT64},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueUint64Value{0xFFFFFFFFFFFFFFFF}},
		}}

		// x64
		test.expectedGraph().ResolvedValues["TYPE_KEY:x64"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("x64", "x64"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT64},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt64Value{-0x7FFFFFFFFFFFFFFF}},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case: Builtin Floating-Point Constants
	////////////////////////////////////////////////////////////
	{

		contents := `
	const float f1 = float.INFINITY;
	const float f2 = float.NEGATIVE_INFINITY;
	const float f3 = float.NAN;
	const double d1 = double.INFINITY;
	const double d2 = double.NEGATIVE_INFINITY;
	const double d3 = double.NAN;
	`
		test.addTestCase("", contents)

		// DeclaredMojomObjects

		test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{
			"TYPE_KEY:f1", "TYPE_KEY:f2", "TYPE_KEY:f3", "TYPE_KEY:d1", "TYPE_KEY:d2", "TYPE_KEY:d3"}

		// Resolved Values

		// f1
		test.expectedGraph().ResolvedValues["TYPE_KEY:f1"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("f1", "f1"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_Float},
			Value:    &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_FloatInfinity},
		}}

		// f2
		test.expectedGraph().ResolvedValues["TYPE_KEY:f2"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("f2", "f2"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_Float},
			Value:    &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_FloatNegativeInfinity},
		}}

		// f3
		test.expectedGraph().ResolvedValues["TYPE_KEY:f3"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("f3", "f3"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_Float},
			Value:    &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_FloatNan},
		}}

		// d1
		test.expectedGraph().ResolvedValues["TYPE_KEY:d1"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("d1", "d1"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_Double},
			Value:    &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_DoubleInfinity},
		}}

		// d2
		test.expectedGraph().ResolvedValues["TYPE_KEY:d2"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("d2", "d2"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_Double},
			Value:    &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_DoubleNegativeInfinity},
		}}

		// d3
		test.expectedGraph().ResolvedValues["TYPE_KEY:d3"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("d3", "d3"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_Double},
			Value:    &mojom_types.ValueBuiltinValue{mojom_types.BuiltinConstantValue_DoubleNan},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	{

		contents := `
	[go_namespace="go.test",
	lucky=true,
	planet=EARTH]
	module mojom.test;

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

	const uint16 NUM_MAGI = 3;

	struct Foo{
		int32 x;
		[min_version=2]
		string y = "hello";
		string? z;

		enum Hats {
			TOP,
			COWBOY = NUM_MAGI,
			HARD,
		};
	};`

		test.addTestCase("mojom.test", contents)

		// Attributes
		test.expectedFile().Attributes = &[]mojom_types.Attribute{
			{"go_namespace", &mojom_types.LiteralValueStringValue{"go.test"}},
			{"lucky", &mojom_types.LiteralValueBoolValue{true}},
			{"planet", &mojom_types.LiteralValueStringValue{"EARTH"}},
		}

		// Imports
		test.expectedFile().Imports = &[]string{
			"another.file.canonical", "and.another.file.canonical",
		}

		// DeclaredMojomObjects
		test.expectedFile().DeclaredMojomObjects.Structs = &[]string{"TYPE_KEY:mojom.test.Foo"}
		test.expectedFile().DeclaredMojomObjects.TopLevelConstants = &[]string{"TYPE_KEY:mojom.test.NUM_MAGI"}

		// Resolved Values

		// NUM_MAGI
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.NUM_MAGI"] = &mojom_types.UserDefinedValueDeclaredConstant{mojom_types.DeclaredConstant{
			DeclData: *test.newDeclData("NUM_MAGI", "mojom.test.NUM_MAGI"),
			Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_UinT16},
			Value:    &mojom_types.ValueLiteralValue{&mojom_types.LiteralValueInt8Value{3}},
		}}

		// Hats.TOP
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("TOP", "mojom.test.Foo.Hats.TOP"),
			EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats",
			IntValue:    0,
		}}

		// Hats.COWBOY
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("COWBOY", "mojom.test.Foo.Hats.COWBOY"),
			EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats",
			IntValue:    3,
			InitializerValue: &mojom_types.ValueUserValueReference{mojom_types.UserValueReference{
				Identifier: "NUM_MAGI",
				ValueKey:   stringPointer("TYPE_KEY:mojom.test.NUM_MAGI"),
			}},
		}}

		// Hats.HARD
		test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.HARD"] = &mojom_types.UserDefinedValueEnumValue{mojom_types.EnumValue{
			DeclData:    test.newDeclData("HARD", "mojom.test.Foo.Hats.HARD"),
			EnumTypeKey: "TYPE_KEY:mojom.test.Foo.Hats",
			IntValue:    4,
		}}

		// ResolvedTypes

		// struct Foo
		test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo"] = &mojom_types.UserDefinedTypeStructType{mojom_types.MojomStruct{
			DeclData: &mojom_types.DeclarationData{
				ShortName:        stringPointer("Foo"),
				FullIdentifier:   stringPointer("mojom.test.Foo"),
				DeclaredOrdinal:  -1,
				DeclarationOrder: -1,
				SourceFileInfo: &mojom_types.SourceFileInfo{
					FileName: test.fileName(),
				},
				ContainedDeclarations: &mojom_types.ContainedDeclarations{
					Enums: &[]string{"TYPE_KEY:mojom.test.Foo.Hats"}},
			},
			Fields: []mojom_types.StructField{
				// field x
				{
					DeclData: test.newShortDeclData("x"),
					Type:     &mojom_types.TypeSimpleType{mojom_types.SimpleType_InT32},
				},
				// field y
				{
					DeclData:     test.newShortDeclDataA("y", &[]mojom_types.Attribute{{"min_version", &mojom_types.LiteralValueInt8Value{2}}}),
					Type:         &mojom_types.TypeStringType{mojom_types.StringType{false}},
					DefaultValue: &mojom_types.DefaultFieldValueValue{&mojom_types.ValueLiteralValue{&mojom_types.LiteralValueStringValue{"hello"}}},
				},
				// field z
				{
					DeclData: test.newShortDeclData("z"),
					Type:     &mojom_types.TypeStringType{mojom_types.StringType{true}},
				},
			},
		}}

		// enum Hats
		test.expectedGraph().ResolvedTypes["TYPE_KEY:mojom.test.Foo.Hats"] = &mojom_types.UserDefinedTypeEnumType{mojom_types.MojomEnum{
			DeclData: test.newContainedDeclData("Hats", "mojom.test.Foo.Hats", stringPointer("TYPE_KEY:mojom.test.Foo")),
			Values: []mojom_types.EnumValue{
				// Note(rudominer) It is a bug that we need to copy the enum values here.
				// See https://github.com/domokit/mojo/issues/513.
				// value TOP
				test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.TOP"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value COWBOY
				test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.COWBOY"].(*mojom_types.UserDefinedValueEnumValue).Value,
				// value HARD
				test.expectedGraph().ResolvedValues["TYPE_KEY:mojom.test.Foo.Hats.HARD"].(*mojom_types.UserDefinedValueEnumValue).Value,
			},
		}}

		test.endTestCase()
	}

	////////////////////////////////////////////////////////////
	// Execute all of the test cases.
	////////////////////////////////////////////////////////////
	for _, c := range test.cases {
		// Parse and resolve the mojom input.
		descriptor := mojom.NewMojomDescriptor()
		parser := 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())
			continue
		}
		if err := descriptor.Resolve(); err != nil {
			t.Errorf("Resolve error for %s: %s", c.fileName, err.Error())
			continue
		}
		if err := descriptor.ComputeEnumValueIntegers(); err != nil {
			t.Errorf("ComputeEnumValueIntegers error for %s: %s", c.fileName, err.Error())
			continue
		}
		if err := descriptor.ComputeDataForGenerators(); err != nil {
			t.Errorf("ComputeDataForGenerators error for %s: %s", c.fileName, err.Error())
			continue
		}

		// Simulate setting the canonical file name for the imported files. In real operation
		// this step is done in parser_driver.go when each of the imported files are parsed.
		mojomFile := parser.GetMojomFile()
		if mojomFile.Imports != nil {
			for _, imp := range mojomFile.Imports {
				imp.CanonicalFileName = fmt.Sprintf("%s.canonical", imp.SpecifiedName)
			}
		}

		// Serialize
		EmitLineAndColumnNumbers = c.lineAndcolumnNumbers
		bytes, _, err := Serialize(descriptor, false)
		if err != nil {
			t.Errorf("Serialization error for %s: %s", c.fileName, err.Error())
			continue
		}

		// Deserialize
		decoder := bindings.NewDecoder(bytes, nil)
		fileGraph := mojom_files.MojomFileGraph{}
		fileGraph.Decode(decoder)

		// Compare
		if err := compareFileGraphs(c.expectedGraph, &fileGraph); err != nil {
			t.Errorf("%s:\n%s", c.fileName, err.Error())
			continue
		}
	}
}
예제 #7
0
// 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 := 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)
			}
		}

	}
}
예제 #8
0
// 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 := 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)
			}
		}

	}
}
예제 #9
0
// TestFindReachableTypes() iterates through a series of test cases.
// For each case we expect for parsing and resolution to succeed. Then we
// invoke FindReachableTypes() on |typeToSearch| and compare the result
// to |expectedReachableTypes|.
func TestFindReachableTypes(t *testing.T) {
	test := typeGraphTest{}

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	{
		contents := `
	struct Struct1{
	};

	struct Struct2{
		Struct1 x;
	};

    struct Struct3{
		Struct2 x;
	};

	struct Struct4{
		Struct3 x;
	};

	union Union1 {
		Struct3 x;
		Struct2 y;
	};

	union Union2 {
		Struct2 x;
	};
	`
		test.addTestCase(contents,
			"Struct4",
			[]string{"Struct1", "Struct2", "Struct3", "Struct4"},
		)

		test.addTestCase(contents,
			"Union1",
			[]string{"Union1", "Struct1", "Struct2", "Struct3"},
		)

		test.addTestCase(contents,
			"Union2",
			[]string{"Union2", "Struct1", "Struct2"},
		)
	}

	////////////////////////////////////////////////////////////
	// Test Case
	////////////////////////////////////////////////////////////
	{
		contents := `
	   	struct Struct1{
	   		Struct4 x;
	   	};

	   	struct Struct2{
	   		Struct1 x;
	   	};

	    struct Struct3{
	   		Struct2 x;
	   	};

	   	struct Struct4{
	   		Struct3 x;
	   	};

	   	union Union1 {
	   		Struct2 x;
	   	};
	   	`

		test.addTestCase(contents,
			"Union1",
			[]string{"Union1", "Struct1", "Struct2", "Struct3", "Struct4"},
		)
	}

	////////////////////////////////////////////////////////////
	// Test Cases
	////////////////////////////////////////////////////////////
	{
		contents := `

		enum Height {
			SHORT, TALL
		};

		struct Struct1{
		  enum Color {
			RED, BLUE
		  };
		};

		struct Struct2{
			const Struct1.Color FAVORITE = RED;
		};
		`

		test.addTestCase(contents,
			"Struct1",
			[]string{"Struct1", "Struct1.Color"},
		)

		test.addTestCase(contents,
			"Struct2",
			[]string{"Struct2", "Struct1.Color"},
		)
	}

	////////////////////////////////////////////////////////////
	// Test Cases
	////////////////////////////////////////////////////////////
	{
		contents := `
		enum Color {
			RED, BLUE
		};

		enum Height {
			SHORT, TALL
		};

		struct Struct1{};
		struct Struct2{};

		interface Interface1 {
			const Color FAVORITE_COLOR = RED;

			Foo(int32 x) => (string y);
		};

		interface Interface2 {
			Foo(int32 x) => (Struct1 y);
		};
		`

		test.addTestCase(contents,
			"Interface1",
			[]string{"Interface1", "Color"},
		)

		test.addTestCase(contents,
			"Interface2",
			[]string{"Interface2", "Struct1"},
		)
	}

	////////////////////////////////////////////////////////////
	// Test Cases
	////////////////////////////////////////////////////////////

	{
		contents := `
		enum Color {
			RED, BLUE
		};

		enum Height {
			SHORT, TALL
		};

		struct Struct1{};
		struct Struct2{};

		interface Interface1 {
			const Color FAVORITE_COLOR = RED;

			Foo(map<Height, int8> x) => (string y);
		};

		interface Interface2 {
			Foo(int32 x) => (array<Struct1?> y);
		};
		`

		test.addTestCase(contents,
			"Interface1",
			[]string{"Interface1", "Color", "Height"},
		)

		test.addTestCase(contents,
			"Interface2",
			[]string{"Interface2", "Struct1"},
		)
	}

	////////////////////////////////////////////////////////////
	// Test Cases
	////////////////////////////////////////////////////////////

	{
		contents := `
		enum Color {
			RED, BLUE
		};

		enum Height {
			SHORT, TALL
		};

		struct Struct1{};
		struct Struct2{};

		interface Interface1 {
			const Color FAVORITE_COLOR = RED;

			Foo(int32 x) => (string y);
			Bar(map<string, Height> z) => ();
		};

		interface Interface2 {
			Foo(int32 x) => (map<Height, Struct1?> y);
		};
		`

		test.addTestCase(contents,
			"Interface1",
			[]string{"Interface1", "Color", "Height"},
		)

		test.addTestCase(contents,
			"Interface2",
			[]string{"Interface2", "Height", "Struct1"},
		)
	}

	////////////////////////////////////////////////////////////
	// 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 := 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
		}

		userDefinedType := descriptor.TypesByKey[mojom.ComputeTypeKey(c.typeToSearch)]
		result := userDefinedType.FindReachableTypes()
		if err := compareTypeSets(descriptor, c.expectedReachableTypes, result); err != nil {
			t.Errorf("Case %d, unexpected typeset for %s: %s\n", i, c.typeToSearch, err.Error())
			continue
		}
	}
}
예제 #10
0
// 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 := 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)
			}
		}

	}
}
예제 #11
0
// Parses each of the given .mojom files and all of the files in the
// import graph rooted by each file. A single MojomDescriptor is created and
// populated with the result of parsing all of those files. If the parsing is
// successful then err will be nil.
//
// fileNames must not be nil or we will panic.
func (d *ParseDriver) ParseFiles(fileNames []string) (descriptor *mojom.MojomDescriptor, err error) {
	if fileNames == nil {
		// We panic instead of returning an error here because this would be a programming error
		// as opposed to an error in the input.
		panic("fileNames may not be nil.")
	}
	filesToProcess := make([]*FileReference, len(fileNames))
	descriptor = mojom.NewMojomDescriptor()
	for i, fileName := range fileNames {
		filesToProcess[i] = &FileReference{specifiedPath: fileName}
	}

	for len(filesToProcess) > 0 {
		currentFile := filesToProcess[0]
		filesToProcess = filesToProcess[1:]
		if err = d.fileProvider.findFile(currentFile); err != nil {
			return
		}

		if currentFile.importedFrom != nil {
			// Tell the importing file about the absolute path of the imported file.
			// Note that we must do this even if the imported file has already been processed
			// because a given file may be imported by multiple files and each of those need
			// to be told about the absolute path of the imported file.
			currentFile.importedFrom.mojomFile.SetCanonicalImportName(currentFile.specifiedPath, currentFile.absolutePath)
		}

		if !descriptor.ContainsFile(currentFile.absolutePath) {
			contents, fileReadError := d.fileProvider.provideContents(currentFile)
			if fileReadError != nil {
				err = fileReadError
				return
			}
			parser := MakeParser(currentFile.absolutePath, contents, descriptor)
			parser.SetDebugMode(d.debugMode)
			// Invoke parser.Parse() (but skip doing so in tests sometimes.)
			d.parseInvoker.invokeParse(&parser)

			if d.debugMode {
				fmt.Printf("\nParseTree for %s:", currentFile)
				fmt.Println(parser.GetParseTree())
			}

			if !parser.OK() {
				err = fmt.Errorf("\nError while parsing %s: \n%s\n",
					currentFile, parser.GetError().Error())
				return
			}
			currentFile.mojomFile = d.fileExtractor.extractMojomFile(&parser)
			for _, importedFile := range currentFile.mojomFile.Imports {
				// Note that it is important that we append all of the imported files here even
				// if some of them have already been processed. That is because when the imported
				// file is pulled from the queue it will be pre-processed during which time the
				// absolute path to the file will be discovered and this absolute path will be
				// set in |mojomFile| which is necessary for serializing mojomFile.
				filesToProcess = append(filesToProcess,
					&FileReference{importedFrom: currentFile, specifiedPath: importedFile.SpecifiedName})
			}
		}
	}

	// Perform type and value resolution
	if err = descriptor.Resolve(); err != nil {
		return
	}

	// Compute enum value integers.
	if err = descriptor.ComputeEnumValueIntegers(); err != nil {
		return
	}

	// Compute data for generators.
	err = descriptor.ComputeDataForGenerators()
	return
}
예제 #12
0
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)
				}
			}
		}
	}
}
예제 #13
0
// 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 token")
	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 token")
	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 (duplicate method names)
	////////////////////////////////////////////////////////////
	startTestCase("")
	cases[testCaseNum].mojomContents = `
	interface MyInterface {
		MethodA();
		MethodB();
		MethodC();
		MethodB();
		MethodD();
	};

	`
	expectError("Duplicate definition of method 'MethodB'. There is already a method with that name in interface MyInterface.")
	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()

	////////////////////////////////////////////////////////////
	// Execute all of the test cases.
	////////////////////////////////////////////////////////////
	for i, c := range cases {
		descriptor := mojom.NewMojomDescriptor()
		parser := MakeParser(c.fileName, c.mojomContents, descriptor)
		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)
				}
			}
		}
	}
}
예제 #14
0
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)
				}
			}
		}
	}
}
예제 #15
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)
			}
		}
	}
}
예제 #16
0
// 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)
				}
			}
		}
	}
}
예제 #17
0
// 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)
				}
			}
		}
	}
}
예제 #18
0
// 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 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)
				}
			}
		}
	}
}
예제 #19
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)
			}
		}
	}
}