Index: mojom/mojom_parser/parser/computed_data_test.go |
diff --git a/mojom/mojom_parser/parser/computed_data_test.go b/mojom/mojom_parser/parser/computed_data_test.go |
index 2f4bb1ff8ce3521b880b0298c0a557679d4bf84a..c00e12a8bee7e20034b4e9630f790bbf3abdb4e8 100644 |
--- a/mojom/mojom_parser/parser/computed_data_test.go |
+++ b/mojom/mojom_parser/parser/computed_data_test.go |
@@ -5,6 +5,7 @@ |
package parser |
import ( |
+ "fmt" |
"mojom/mojom_parser/mojom" |
"strings" |
"testing" |
@@ -198,3 +199,380 @@ func TestMinVersionErrors(t *testing.T) { |
} |
} |
+ |
+func checkStructVersion(description string, structVersion *mojom.StructVersion, |
+ expectedVersionNumber, expectedNumFields, expectedNumBytes uint32) error { |
+ if structVersion.VersionNumber != expectedVersionNumber { |
+ return fmt.Errorf("%s: VersionNumber %d != %d", description, structVersion.VersionNumber, expectedVersionNumber) |
+ } |
+ if structVersion.NumFields != expectedNumFields { |
+ return fmt.Errorf("%s: for version %d: NumFields= %d != %d", description, |
+ structVersion.VersionNumber, structVersion.NumFields, expectedNumFields) |
+ } |
+ if structVersion.NumBytes != expectedNumBytes { |
+ return fmt.Errorf("%s: for version %d: NumBytes=%d != %d", description, |
+ structVersion.VersionNumber, structVersion.NumBytes, expectedNumBytes) |
+ } |
+ return nil |
+} |
+ |
+func checkStructFieldOffsets(description string, mojomStruct *mojom.MojomStruct, |
+ expectedOrdinal, expectedOffset []uint32) error { |
+ if len(mojomStruct.FieldsInOrdinalOrder()) != len(expectedOrdinal) { |
+ return fmt.Errorf("%s: len(FieldsInOrdinalOrder())=%d != %d", description, |
+ len(mojomStruct.FieldsInOrdinalOrder()), len(expectedOrdinal)) |
+ } |
+ for i, field := range mojomStruct.FieldsInLexicalOrder { |
+ if field != mojomStruct.FieldsInOrdinalOrder()[expectedOrdinal[i]] { |
+ return fmt.Errorf("%s: i=%d wrong ordinal", description, i) |
+ } |
+ if field.Offset() != expectedOffset[i] { |
+ return fmt.Errorf("%s: i=%d wrong offset %d != %d", description, i, |
+ field.Offset(), expectedOffset[i]) |
+ } |
+ } |
+ return nil |
+} |
+ |
+// TestStructsComputedData() iterates through a series of test cases. |
+// For each case we expect for parsing, resolution and final data computation to succeed. |
+// Then we execute a given callback test function to test that the functions |
+// computeFieldOffsets and computeVersionData produced the desired result. |
+func TestStructsComputedData(t *testing.T) { |
+ test := singleFileSuccessTest{} |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test computeVersionInfo empty struct |
+ // Tests that computerVersionInfo never produces an empty |
+ // list of versions, even for empty structs. |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ struct MyStruct { |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if len(myStructType.FieldsInOrdinalOrder()) != 0 { |
+ return fmt.Errorf("len(myStructType.FieldsInOrdinalOrder())=%d", len(myStructType.FieldsInOrdinalOrder())) |
+ } |
+ if len(myStructType.VersionInfo()) != 1 { |
+ return fmt.Errorf("len(myStructType.VersionInfo() = %d", len(myStructType.VersionInfo())) |
+ } |
+ if err := checkStructVersion("EmptyStruct", &myStructType.VersionInfo()[0], 0, 0, 8); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test One field |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ struct MyStruct { |
+ int8 x; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("OneField", myStructType, []uint32{0}, []uint32{0}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("OneField", &myStructType.VersionInfo()[0], 0, 1, 16); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test Padding, In Order |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ struct MyStruct { |
+ int8 x; |
+ uint8 y; |
+ int32 z; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("InOrder", myStructType, []uint32{0, 1, 2}, []uint32{0, 1, 4}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("In order", &myStructType.VersionInfo()[0], 0, 3, 16); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test Padding, Out of order |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ struct MyStruct { |
+ int8 x; |
+ int32 y; |
+ uint8 z; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("OutOfOrder", myStructType, []uint32{0, 1, 2}, []uint32{0, 4, 1}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("OutOfOrder", &myStructType.VersionInfo()[0], 0, 3, 16); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test Padding, Overflow |
+ // 2 bytes should be packed together first, followed by short, then by int. |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ struct MyStruct { |
+ int8 f1; |
+ int32 f2; |
+ int16 f3; |
+ int8 f4; |
+ int8 f5; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("Overflow", myStructType, []uint32{0, 1, 2, 3, 4}, []uint32{0, 4, 2, 1, 8}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("Overflow", &myStructType.VersionInfo()[0], 0, 5, 24); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Nullable Types |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ interface MyInterface{}; |
+ |
+ struct MyStruct { |
+ string? f1; |
+ handle? f2; |
+ MyStruct? f3; |
+ handle<data_pipe_consumer>? f4; |
+ array<int8>? f5; |
+ handle<data_pipe_producer>? f6; |
+ array<int8, 5>? f7; |
+ handle<message_pipe>? f8; |
+ MyInterface? f9; |
+ handle<shared_buffer>? f10; |
+ MyInterface&? f11; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("Nullable Types", myStructType, |
+ []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, |
+ []uint32{0, 8, 16, 12, 24, 32, 40, 36, 48, 56, 60}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("Nullable Types", &myStructType.VersionInfo()[0], 0, 11, 72); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: All Types |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ interface MyStruct2{}; |
+ |
+ struct MyStruct { |
+ bool f1; |
+ int8 f2; |
+ string f3; |
+ uint8 f4; |
+ int16 f5; |
+ double f6; |
+ uint16 f7; |
+ int32 f8; |
+ uint32 f9; |
+ int64 f10; |
+ float f11; |
+ string f12; |
+ handle f13; |
+ uint64 f14; |
+ MyStruct2 f15; |
+ array<int32> f16; |
+ string? f17; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("All Types", myStructType, |
+ []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, |
+ []uint32{0, 1, 8, 2, 4, 16, 6, 24, 28, 32, 40, 48, 44, 56, 64, 72, 80}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("All Types", &myStructType.VersionInfo()[0], 0, 17, 96); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Bools |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ interface MyStruct2{}; |
+ |
+ struct MyStruct { |
+ bool f1; |
+ bool f2; |
+ int32 f3; |
+ bool f4; |
+ bool f5; |
+ bool f6; |
+ bool f7; |
+ bool f8; |
+ bool f9; |
+ bool f10; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("Bools", myStructType, |
+ []uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, |
+ []uint32{0, 0, 4, 0, 0, 0, 0, 0, 0, 1}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("Bools", &myStructType.VersionInfo()[0], 0, 10, 16); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test computeVersionInfo complex order |
+ // Tests computerVersionInfo using a struct wose definition order, |
+ // ordinal order and pack order for fields are all different. |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ struct MyStruct { |
+ [MinVersion = 3] |
+ bool field3@3; |
+ |
+ int32 field_0@0; |
+ |
+ [MinVersion = 2] |
+ int64 field_1@1; |
+ |
+ [MinVersion = 3] |
+ int64 field_2@2; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("ComplexOrder", myStructType, []uint32{3, 0, 1, 2}, []uint32{4, 0, 8, 16}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("ComplexOrder", &myStructType.VersionInfo()[0], 0, 1, 16); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("ComplexOrder", &myStructType.VersionInfo()[1], 2, 2, 24); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("ComplexOrder", &myStructType.VersionInfo()[2], 3, 4, 32); err != nil { |
+ return err |
+ } |
+ return nil |
+ } |
+ test.addTestCase("", contents, testFunc) |
+ } |
+ |
+ //////////////////////////////////////////////////////////// |
+ // Test Case: Test Interface Alignment |
+ // Tests that interfaces are aligned on 4-byte boundaries, |
+ // although the size of an interface is 8 bytes. |
+ //////////////////////////////////////////////////////////// |
+ { |
+ contents := ` |
+ interface MyInterface{}; |
+ |
+ struct MyStruct { |
+ int32 x; |
+ MyInterface y; |
+ };` |
+ |
+ testFunc := func(descriptor *mojom.MojomDescriptor) error { |
+ myStructType := descriptor.TypesByKey["TYPE_KEY:MyStruct"].(*mojom.MojomStruct) |
+ if err := checkStructFieldOffsets("InterfaceAlignment", myStructType, []uint32{0, 1}, []uint32{0, 4}); err != nil { |
+ return err |
+ } |
+ if err := checkStructVersion("InterfaceAlignment", |
+ &myStructType.VersionInfo()[0], 0, 2, 24); err != nil { |
+ return err |
+ } |
+ 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 err := descriptor.ComputeFinalData(); err != nil { |
+ t.Errorf("ComputeFinalData 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 |
+ } |
+ } |
+ } |
+} |