| Index: mojom/mojom_tool/integration_tests/illfounded_types_test.go
|
| diff --git a/mojom/mojom_tool/integration_tests/illfounded_types_test.go b/mojom/mojom_tool/integration_tests/illfounded_types_test.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..859bc55f21775b88b6310a2a306e991f457c6219
|
| --- /dev/null
|
| +++ b/mojom/mojom_tool/integration_tests/illfounded_types_test.go
|
| @@ -0,0 +1,474 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package parser
|
| +
|
| +import (
|
| + "fmt"
|
| + "mojom/mojom_tool/mojom"
|
| + "mojom/mojom_tool/parser"
|
| + "strings"
|
| + "testing"
|
| +)
|
| +
|
| +// TestIllegalPatterns tests cases where DetectIllFoundedTypes() should return an error.
|
| +func TestIllegalPatterns(t *testing.T) {
|
| + test := singleFileTest{}
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + Foo x;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type Foo is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: Foo.x -> Foo",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, fixed-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<Foo, 1> x;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type Foo is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: Foo.x -> Foo",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, fixed-length array of
|
| + // non-nullable fixed-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<array<Foo, 1>, 1> x;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type Foo is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: Foo.x -> Foo",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Union field is non-nullable pointer to union itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + union Foo{
|
| + Foo x;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type Foo is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: Foo.x -> Foo",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Union field is non-nullable, fixed-length array of non-nullable pointer to union itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + union Foo{
|
| + array<Foo, 1> x;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type Foo is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: Foo.x -> Foo",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: A cycle of length 3.
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct MyStruct1 {
|
| + MyStruct2 x;
|
| + };
|
| +
|
| + struct MyStruct2 {
|
| + MyStruct3 x;
|
| + };
|
| +
|
| + struct MyStruct3 {
|
| + MyStruct1 x;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type MyStruct1 is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: MyStruct1.x -> MyStruct2.x -> MyStruct3.x -> MyStruct1",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Two structs and a union
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct MyStruct1 {
|
| + MyStruct2 x;
|
| + int32 y;
|
| + };
|
| +
|
| + struct MyStruct2 {
|
| + MyUnion x;
|
| + int32 y;
|
| + };
|
| +
|
| + union MyUnion {
|
| + MyStruct1 x;
|
| + MyStruct2 y;
|
| + };`
|
| + test.addTestCase(contents, []string{
|
| + "The type MyStruct1 is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: MyStruct1.x -> MyStruct2.x -> MyUnion.x -> MyStruct1",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Ill-foundedness not found until second pass of algorithm.
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct AEmptyStruct {
|
| + };
|
| +
|
| + struct BMyUnion {
|
| + AEmptyStruct x;
|
| + CBadStruct y;
|
| + };
|
| +
|
| + struct CBadStruct {
|
| + CBadStruct x;
|
| + };`
|
| +
|
| + test.addTestCase(contents, []string{
|
| + "The type CBadStruct is unserializable: Every instance of this type would include a cycle.",
|
| + "Example cycle: CBadStruct.x -> CBadStruct",
|
| + "One way to break this cycle is to make the field \"x\" nullable.",
|
| + })
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Execute all of the test cases.
|
| + ////////////////////////////////////////////////////////////
|
| + for i, c := range test.cases {
|
| + // Parse anresolve the mojom input.
|
| + descriptor := mojom.NewMojomDescriptor()
|
| + specifiedName := ""
|
| + if c.importedFrom == nil {
|
| + specifiedName = c.fileName
|
| + }
|
| + parser := parser.MakeParser(c.fileName, specifiedName, c.mojomContents, descriptor, c.importedFrom)
|
| + parser.Parse()
|
| + if !parser.OK() {
|
| + t.Errorf("Parsing error for %s: %s", c.fileName, parser.GetError().Error())
|
| + continue
|
| + }
|
| + err := descriptor.Resolve()
|
| + if err != nil {
|
| + t.Errorf("Resolution error for %s: %s", c.fileName, err)
|
| + continue
|
| + }
|
| + if err = descriptor.ComputeFinalData(); err != nil {
|
| + t.Errorf("ComputeFinalData error for %s: %s", c.fileName, err)
|
| + continue
|
| + }
|
| +
|
| + descriptor.SetTestingMode(true)
|
| + err = descriptor.DetectIllFoundedTypes()
|
| +
|
| + if err == nil {
|
| + t.Errorf("No cycles were detected 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)
|
| + }
|
| + }
|
| +
|
| + }
|
| +}
|
| +
|
| +// TestLegalPatterns tests cases where DetectIllFoundedTypes() should not return an error.
|
| +func TestLegalPatterns(t *testing.T) {
|
| + test := singleFileSuccessTest{}
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is nullable, pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + Foo? x;
|
| + };`
|
| +
|
| + testFunc := func(descriptor *mojom.MojomDescriptor) error {
|
| + return nil
|
| + }
|
| + test.addTestCase("", contents, testFunc)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, variable-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| +
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<Foo> x;
|
| + };`
|
| +
|
| + testFunc := func(descriptor *mojom.MojomDescriptor) error {
|
| + return nil
|
| + }
|
| + test.addTestCase("", contents, testFunc)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is nullable, fixed-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| +
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<Foo, 1>? x;
|
| + };`
|
| +
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, fixed-length array of nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| +
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<Foo?, 1> x;
|
| + };`
|
| +
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, variable-length array of
|
| + // non-nullable fixed-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<array<Foo, 1>> x;
|
| + };`
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, fixed-length array of
|
| + // non-nullable variable-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<array<Foo>, 1> x;
|
| + };`
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is non-nullable, fixed-length array of
|
| + // non-nullable fixed-length array of nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<array<Foo?, 1>, 1> x;
|
| + };`
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Struct field is nullable, fixed-length array of
|
| + // non-nullable fixed-length array of non-nullable pointer to struct itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + array<array<Foo, 1>, 1>? x;
|
| + };`
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Field of struct Foo is a map from a string to a Foo
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct Foo{
|
| + map<string, Foo> x;
|
| + };`
|
| +
|
| + testFunc := func(descriptor *mojom.MojomDescriptor) error {
|
| + return nil
|
| + }
|
| + test.addTestCase("", contents, testFunc)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: union field is nullable, pointer to union itself
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + union Foo{
|
| + Foo? x;
|
| + };`
|
| +
|
| + testFunc := func(descriptor *mojom.MojomDescriptor) error {
|
| + return nil
|
| + }
|
| + test.addTestCase("", contents, testFunc)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: union field is non-nullable, variable-length array of non-nullable pointer to union itself
|
| + ////////////////////////////////////////////////////////////
|
| +
|
| + {
|
| + contents := `
|
| + union Foo{
|
| + array<Foo> x;
|
| + };`
|
| +
|
| + testFunc := func(descriptor *mojom.MojomDescriptor) error {
|
| + return nil
|
| + }
|
| + test.addTestCase("", contents, testFunc)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: union field is nullable, fixed-length array of non-nullable pointer to union itself
|
| + ////////////////////////////////////////////////////////////
|
| +
|
| + {
|
| + contents := `
|
| + union Foo{
|
| + array<Foo, 1>? x;
|
| + };`
|
| +
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: union field is non-nullable, fixed-length array of nullable pointer to union itself
|
| + ////////////////////////////////////////////////////////////
|
| +
|
| + {
|
| + contents := `
|
| + union Foo{
|
| + array<Foo?, 1> x;
|
| + };`
|
| +
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: A cycle of length 3 with 1 nullable.
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + struct MyStruct1 {
|
| + MyStruct2 x;
|
| + };
|
| +
|
| + struct MyStruct2 {
|
| + MyStruct3? x;
|
| + };
|
| +
|
| + struct MyStruct3 {
|
| + MyStruct1 x;
|
| + };`
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Test Case: Well-founded because of CStruct2
|
| + ////////////////////////////////////////////////////////////
|
| + {
|
| + contents := `
|
| + union AUnion0 {
|
| + BStruct1 x;
|
| + CStruct2 y;
|
| + };
|
| +
|
| + struct BStruct1 {
|
| + int32 x;
|
| + DUnion3 y;
|
| + };
|
| +
|
| + struct CStruct2 {
|
| + string x;
|
| + };
|
| +
|
| + union DUnion3 {
|
| + AUnion0 x;
|
| + BStruct1 y;
|
| + };`
|
| + test.addTestCase("", contents, nil)
|
| + }
|
| +
|
| + ////////////////////////////////////////////////////////////
|
| + // Execute all of the test cases.
|
| + ////////////////////////////////////////////////////////////
|
| + for i, c := range test.cases {
|
| + // Parse and resolve the mojom input.
|
| + descriptor := mojom.NewMojomDescriptor()
|
| + fileName := fmt.Sprintf("file%d", i)
|
| + parser := parser.MakeParser(fileName, fileName, c.mojomContents, descriptor, nil)
|
| + parser.Parse()
|
| + if !parser.OK() {
|
| + t.Errorf("Parsing error for %s: %s", fileName, parser.GetError().Error())
|
| + continue
|
| + }
|
| + err := descriptor.Resolve()
|
| + if err != nil {
|
| + t.Errorf("Resolution failed for test case %d: %s", i, err.Error())
|
| + continue
|
| + }
|
| +
|
| + if err := descriptor.ComputeFinalData(); err != nil {
|
| + t.Errorf("ComputeFinalData error for test case %d: %s", i, err.Error())
|
| + continue
|
| + }
|
| +
|
| + if err := descriptor.DetectIllFoundedTypes(); err != nil {
|
| + t.Errorf("DetectIllFoundedTypes error 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
|
| + }
|
| + }
|
| + }
|
| +}
|
|
|