Chromium Code Reviews

Unified Diff: mojom/mojom_tool/integration_tests/illfounded_types_test.go

Issue 1915413002: Mojom frontend: Detect Ill-founded Types (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Fix comments as per code review. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
« no previous file with comments | « no previous file | mojom/mojom_tool/mojom/mojom_descriptor.go » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
+ }
+ }
+ }
+}
« no previous file with comments | « no previous file | mojom/mojom_tool/mojom/mojom_descriptor.go » ('j') | no next file with comments »

Powered by Google App Engine