| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package parser |
| |
| import ( |
| "fmt" |
| "mojom/mojom_tool/mojom" |
| "mojom/mojom_tool/parser" |
| "strings" |
| "testing" |
| ) |
| |
| // TestStructFieldMinVersionErrors test the method MojomStruct.computeVersionInfo() which |
| // is invoked by ComputeFinalData. This phase occurs after resolution |
| // and type validation. We test that different types of errors related to |
| // the MinVersion attribute are correctly detected. |
| func TestStructFieldMinVersionErrors(t *testing.T) { |
| test := singleFileTest{} |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Float value for MinVersion |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = 1.1] |
| array<int32>? z; |
| |
| [MinVersion = 2] |
| array<int32>? w; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for field z: 1.1. ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: string value for MinVersion |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = "1"] |
| array<int32>? z; |
| |
| [MinVersion = 2] |
| array<int32>? w; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for field z: \"1\". ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: MinVersion is negative |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = -1] |
| array<int32>? z; |
| |
| [MinVersion = 2] |
| array<int32>? w; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for field z: -1. ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: MinVersion is to big for 32 bits |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = 1234567890123] |
| array<int32>? z; |
| |
| [MinVersion = 2] |
| array<int32>? w; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for field z: 1234567890123. ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = 2] |
| array<int32>? z; |
| |
| [MinVersion = 1] |
| array<int32>? w; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for field w: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This field's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing: ordinals are used. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = 1] |
| array<int32>? z@3; |
| |
| [MinVersion = 2] |
| array<int32>? w@2; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for field z: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This field's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Non-nullable type used. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| struct Foo{ |
| int32 x; |
| int32 y; |
| |
| [MinVersion = 1] |
| array<int32> z; |
| |
| [MinVersion = 2] |
| array<int32>? w; |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid type for field z: array<int32>.", |
| "Non-nullable reference fields are only allowed in version 0 of of a struct.", |
| "This field's MinVersion is 1."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // 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 |
| } |
| |
| err = descriptor.ComputeFinalData() |
| |
| if err == nil { |
| t.Errorf("Data computation 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) |
| } |
| } |
| |
| } |
| } |
| |
| // TestMethoddMinVersionErrors test the method MojomInterface.computeInterfaceVersion() which |
| // is invoked by ComputeFinalData. This phase occurs after resolution |
| // and type validation. We test that different types of errors related to |
| // the MinVersion attribute are correctly detected. |
| func TestMethodMinVersionErrors(t *testing.T) { |
| test := singleFileTest{} |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Float value for MinVersion on method. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| [MinVersion=1.1] |
| DoIt(); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for method DoIt: 1.1. ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Float value for MinVersion on request parameter. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt([MinVersion=1.1] int32 x); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for parameter x: 1.1. ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Float value for MinVersion on response parameter. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt([MinVersion=1] int32 x) => ([MinVersion=1.1] int32 y); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for response parameter y: 1.1. ", |
| "The value must be a non-negative 32-bit integer value."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing for mehtods. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| [MinVersion=2] |
| DoIt(); |
| |
| [MinVersion=1] |
| DoItAgain(); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for method DoItAgain: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This method's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing for request parameters. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt([MinVersion=2] int32 x, [MinVersion=1] int32 y); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for parameter y: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This parameter's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing for response parameters. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt() => ([MinVersion=2] int32 x, [MinVersion=1] int32 y); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for response parameter y: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This response parameter's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing for methods: ordinals are used. |
| //////////////////////////////////////////////////////////// |
| { |
| |
| contents := ` |
| interface MyInteface { |
| DoIt(); |
| |
| [MinVersion=1] |
| AndAgain@2(); |
| |
| [MinVersion=2] |
| DoItAgain@1(); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for method AndAgain: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This method's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing for request parameters: ordinals are used |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt([MinVersion=1] int32 y@1, [MinVersion=2] int32 x@0); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for parameter y: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This parameter's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Min Versions must be increasing for response parameters: ordinals are used |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt() => ([MinVersion=1] int32 y@1, [MinVersion=2] int32 x@0); |
| };` |
| test.addTestCase(contents, []string{ |
| "Invalid MinVersion attribute for response parameter y: 1. ", |
| "The MinVersion must be non-decreasing as a function of the ordinal.", |
| " This response parameter's MinVersion must be at least 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Non-nullable type used in request parameters. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt(int32 x, [MinVersion = 1] array<int32>? y, [MinVersion = 2] array<int32> z); |
| };` |
| |
| test.addTestCase(contents, []string{ |
| "Invalid type for parameter z: array<int32>.", |
| "Non-nullable reference parameters are only allowed in version 0 of of a struct.", |
| "This parameter's MinVersion is 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Non-nullable type used in response parameters. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInteface { |
| DoIt() => (int32 x, [MinVersion = 1] array<int32>? y, [MinVersion = 2] array<int32> z); |
| };` |
| |
| test.addTestCase(contents, []string{ |
| "Invalid type for response parameter z: array<int32>.", |
| "Non-nullable reference response parameters are only allowed in version 0 of of a struct.", |
| "This response parameter's MinVersion is 2."}) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // 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 |
| } |
| |
| err = descriptor.ComputeFinalData() |
| |
| if err == nil { |
| t.Errorf("Data computation 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) |
| } |
| } |
| |
| } |
| } |
| |
| 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 := 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 |
| } |
| } |
| } |
| } |
| |
| // TestInterfaceComputedData() 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 methods |
| // MojomInterface.ComputeFinalData() and MojomInterface.computeInterfaceVersion() |
| // produced the desired result. |
| func TestInterfaceComputedData(t *testing.T) { |
| test := singleFileSuccessTest{} |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Test that an empty interface still gets a version. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInterface { |
| };` |
| |
| testFunc := func(descriptor *mojom.MojomDescriptor) error { |
| myInterfaceType := descriptor.TypesByKey["TYPE_KEY:MyInterface"].(*mojom.MojomInterface) |
| if myInterfaceType.Version() != 0 { |
| return fmt.Errorf("myInterfaceType.Version()=%d", myInterfaceType.Version()) |
| } |
| return nil |
| } |
| test.addTestCase("", contents, testFunc) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Interface version comes from MinVersion on a method |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInterface { |
| Method1(); |
| Method2(); |
| [MinVersion=7] |
| Method3(); |
| };` |
| |
| testFunc := func(descriptor *mojom.MojomDescriptor) error { |
| myInterfaceType := descriptor.TypesByKey["TYPE_KEY:MyInterface"].(*mojom.MojomInterface) |
| if myInterfaceType.Version() != 7 { |
| return fmt.Errorf("myInterfaceType.Version()=%d", myInterfaceType.Version()) |
| } |
| return nil |
| } |
| test.addTestCase("", contents, testFunc) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Interface version comes from MinVersion on a parameter |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInterface { |
| Method1([MinVersion=1] int8 x, [MinVersion=8] int8 y); |
| Method2(); |
| [MinVersion=7] |
| Method3(); |
| };` |
| |
| testFunc := func(descriptor *mojom.MojomDescriptor) error { |
| myInterfaceType := descriptor.TypesByKey["TYPE_KEY:MyInterface"].(*mojom.MojomInterface) |
| if myInterfaceType.Version() != 8 { |
| return fmt.Errorf("myInterfaceType.Version()=%d", myInterfaceType.Version()) |
| } |
| return nil |
| } |
| test.addTestCase("", contents, testFunc) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Test Case: Interface version comes from MinVersion on a response parameter. |
| //////////////////////////////////////////////////////////// |
| { |
| contents := ` |
| interface MyInterface { |
| Method1([MinVersion=1] int8 x, [MinVersion=8] int8 y); |
| Method2() => ([MinVersion=1] int8 x, [MinVersion=9] int8 y); |
| [MinVersion=7] |
| Method3(); |
| };` |
| |
| testFunc := func(descriptor *mojom.MojomDescriptor) error { |
| myInterfaceType := descriptor.TypesByKey["TYPE_KEY:MyInterface"].(*mojom.MojomInterface) |
| if myInterfaceType.Version() != 9 { |
| return fmt.Errorf("myInterfaceType.Version()=%d", myInterfaceType.Version()) |
| } |
| return nil |
| } |
| test.addTestCase("", contents, testFunc) |
| } |
| |
| //////////////////////////////////////////////////////////// |
| // Execute all of the test cases. |
| //////////////////////////////////////////////////////////// |
| for i, c := range test.cases { |
| // Parse and resolve the mojom input. |
| descriptor := mojom.NewMojomDescriptor() |
| fileName := fmt.Sprintf("file%d", i) |
| parser := parser.MakeParser(fileName, fileName, c.mojomContents, descriptor, nil) |
| parser.Parse() |
| if !parser.OK() { |
| t.Errorf("Parsing error for %s: %s", fileName, parser.GetError().Error()) |
| continue |
| } |
| err := descriptor.Resolve() |
| if err != nil { |
| t.Errorf("Resolution failed for test case %d: %s", i, err.Error()) |
| continue |
| } |
| |
| if 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 |
| } |
| } |
| } |
| } |