| // 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 tests |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "reflect" |
| "strings" |
| "testing" |
| |
| "mojo/public/go/bindings" |
| "mojo/public/go/system" |
| test "mojo/public/interfaces/bindings/tests/validation_test_interfaces" |
| ) |
| |
| func getTestPath(name string) string { |
| // TODO(rogulenko): try to get a better solution. |
| // This should be .../out/name{Debug|Release}/obj/mojo/go. |
| dir, err := filepath.Abs(filepath.Dir(os.Args[0])) |
| if err != nil { |
| panic(err) |
| } |
| // Go 5 folders up. |
| for i := 0; i < 5; i++ { |
| dir = filepath.Dir(dir) |
| } |
| testsFolder := filepath.Join("mojo", "public", "interfaces", "bindings", "tests", "data", "validation") |
| if name != "" { |
| return filepath.Join(dir, testsFolder, name) |
| } else { |
| return filepath.Join(dir, testsFolder) |
| } |
| } |
| |
| func listTestFiles() []string { |
| files, err := ioutil.ReadDir(getTestPath("")) |
| if err != nil { |
| panic(err) |
| } |
| var fileNames []string |
| for _, file := range files { |
| if file.Mode().IsRegular() { |
| fileNames = append(fileNames, file.Name()) |
| } |
| } |
| return fileNames |
| } |
| |
| func getMatchingTests(fileNames []string, prefix string) []string { |
| var result []string |
| extension := ".data" |
| for _, fileName := range fileNames { |
| if strings.HasPrefix(fileName, prefix) && strings.HasSuffix(fileName, extension) { |
| result = append(result, strings.TrimSuffix(fileName, extension)) |
| } |
| } |
| if len(result) == 0 { |
| panic("empty test list") |
| } |
| return result |
| } |
| |
| func readTest(testName string) ([]byte, []system.UntypedHandle) { |
| content, err := ioutil.ReadFile(getTestPath(testName + ".data")) |
| if err != nil { |
| panic(err) |
| } |
| lines := strings.Split(strings.Replace(string(content), "\r", "\n", -1), "\n") |
| for i, _ := range lines { |
| lines[i] = strings.Split(lines[i], "//")[0] |
| } |
| parser := &inputParser{} |
| bytes, handles := parser.Parse(strings.Join(lines, " ")) |
| return bytes, handles |
| } |
| |
| func readAnswer(testName string) string { |
| content, err := ioutil.ReadFile(getTestPath(testName + ".expected")) |
| if err != nil { |
| panic(err) |
| } |
| return strings.TrimSpace(string(content)) |
| } |
| |
| func pipeOwner(h system.MessagePipeHandle) bindings.MessagePipeHandleOwner { |
| return bindings.NewMessagePipeHandleOwner(h) |
| } |
| |
| type rawMessage struct { |
| Bytes []byte |
| Handles []system.UntypedHandle |
| } |
| |
| type mockMessagePipeHandle struct { |
| bindings.InvalidHandle |
| messages chan rawMessage |
| } |
| |
| func NewMockMessagePipeHandle() *mockMessagePipeHandle { |
| h := &mockMessagePipeHandle{} |
| h.messages = make(chan rawMessage, 10) |
| return h |
| } |
| |
| func (h *mockMessagePipeHandle) reset() { |
| h.messages = make(chan rawMessage, 10) |
| } |
| |
| func (h *mockMessagePipeHandle) IsValid() bool { |
| return true |
| } |
| |
| func (h *mockMessagePipeHandle) ToUntypedHandle() system.UntypedHandle { |
| return h |
| } |
| |
| func (h *mockMessagePipeHandle) ToMessagePipeHandle() system.MessagePipeHandle { |
| return h |
| } |
| |
| func (h *mockMessagePipeHandle) ReadMessage(flags system.MojoReadMessageFlags) (system.MojoResult, []byte, []system.UntypedHandle) { |
| message := <-h.messages |
| return system.MOJO_RESULT_OK, message.Bytes, message.Handles |
| } |
| |
| func (h *mockMessagePipeHandle) WriteMessage(bytes []byte, handles []system.UntypedHandle, flags system.MojoWriteMessageFlags) system.MojoResult { |
| h.messages <- rawMessage{bytes, handles} |
| return system.MOJO_RESULT_OK |
| } |
| |
| type conformanceValidator struct { |
| CheckArgs bool |
| Proxy test.ConformanceTestInterface |
| } |
| |
| func (v *conformanceValidator) Method0(inParam0 float32) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := float32(-1) |
| if inParam0 != param0 { |
| return fmt.Errorf("unexpected value (Method0, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method0(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method1(inParam0 test.StructA) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := test.StructA{1234} |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method1, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method1(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method2(inParam0 test.StructB, inParam1 test.StructA) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := test.StructB{test.StructA{12345}} |
| param1 := test.StructA{67890} |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method2, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| if !reflect.DeepEqual(inParam1, param1) { |
| return fmt.Errorf("unexpected value (Method2, inParam1): expected %v, got %v", param1, inParam1) |
| } |
| return v.Proxy.Method2(inParam0, inParam1) |
| } |
| |
| func (v *conformanceValidator) Method3(inParam0 []bool) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := []bool{true, false, true, false, true, false, true, false, true, true, true, true} |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method3, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method3(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method4(inParam0 test.StructC, inParam1 []uint8) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := test.StructC{[]uint8{0, 1, 2}} |
| param1 := []uint8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method4, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| if !reflect.DeepEqual(inParam1, param1) { |
| return fmt.Errorf("unexpected value (Method4, inParam1): expected %v, got %v", param1, inParam1) |
| } |
| return v.Proxy.Method4(inParam0, inParam1) |
| } |
| |
| func (v *conformanceValidator) Method5(inParam0 test.StructE, inParam1 system.ProducerHandle) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := test.StructE{ |
| test.StructD{[]system.MessagePipeHandle{ |
| &mockHandle{handle: 1}, |
| &mockHandle{handle: 2}, |
| }}, |
| &mockHandle{handle: 4}, |
| } |
| param1 := &mockHandle{handle: 5} |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method5, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| if !reflect.DeepEqual(inParam1, param1) { |
| return fmt.Errorf("unexpected value (Method5, inParam1): expected %v, got %v", param1, inParam1) |
| } |
| return v.Proxy.Method5(inParam0, inParam1) |
| } |
| |
| func (v *conformanceValidator) Method6(inParam0 [][]uint8) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := [][]uint8{[]uint8{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}} |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method6, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method6(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method7(inParam0 test.StructF, inParam1 [2]*[3]uint8) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := test.StructF{[3]uint8{0, 1, 2}} |
| param1 := [2]*[3]uint8{ |
| nil, |
| &[3]uint8{0, 1, 2}, |
| } |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method7, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| if !reflect.DeepEqual(inParam1, param1) { |
| return fmt.Errorf("unexpected value (Method7, inParam1): expected %v, got %v", param1, inParam1) |
| } |
| return v.Proxy.Method7(inParam0, inParam1) |
| } |
| |
| func (v *conformanceValidator) Method8(inParam0 []*[]string) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := []*[]string{ |
| nil, |
| &[]string{string([]byte{0, 1, 2, 3, 4})}, |
| nil, |
| } |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method8, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method8(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method9(inParam0 *[][]*system.Handle) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| handles := []system.Handle{ |
| &mockHandle{handle: 1}, |
| &mockHandle{handle: 3}, |
| &mockHandle{handle: 4}, |
| } |
| param0 := &[][]*system.Handle{ |
| []*system.Handle{&handles[0], nil}, |
| []*system.Handle{&handles[1], nil, &handles[2]}, |
| } |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method9, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method9(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method10(inParam0 map[string]uint8) error { |
| if !v.CheckArgs { |
| return nil |
| } |
| param0 := map[string]uint8{ |
| string([]byte{0, 1, 2, 3, 4}): 1, |
| string([]byte{5, 6, 7, 8, 9}): 2, |
| } |
| if !reflect.DeepEqual(inParam0, param0) { |
| return fmt.Errorf("unexpected value (Method10, inParam0): expected %v, got %v", param0, inParam0) |
| } |
| return v.Proxy.Method10(inParam0) |
| } |
| |
| func (v *conformanceValidator) Method11(test.StructG) error { |
| return nil |
| } |
| |
| func (v *conformanceValidator) Method12(float32) (float32, error) { |
| return 0, nil |
| } |
| |
| func (v *conformanceValidator) Method13(*test.InterfaceA_Pointer, uint32, *test.InterfaceA_Pointer) error { |
| return nil |
| } |
| |
| func verifyValidationError(t *testing.T, test string, err error, answer string) { |
| if (err == nil) != (answer == "PASS") { |
| t.Fatalf("unexpected result for test %v: %v", test, err) |
| } |
| if answer != "PASS" { |
| validationError, ok := err.(*bindings.ValidationError) |
| if !ok { |
| t.Fatalf("can't convert err '%v' to ValidationError in test %v", err, test) |
| } |
| code := validationError.ErrorCode |
| if code != answer { |
| t.Fatalf("unexpected error code in test %v: got %v(%v), want %v", test, code, err, answer) |
| } |
| } |
| } |
| |
| func TestConformanceValidation(t *testing.T) { |
| tests := getMatchingTests(listTestFiles(), "conformance_") |
| |
| h := NewMockMessagePipeHandle() |
| proxyIn, proxyOut := h, h |
| interfacePointer := test.ConformanceTestInterface_Pointer{pipeOwner(proxyIn)} |
| impl := &conformanceValidator{false, test.NewConformanceTestInterfaceProxy(interfacePointer, waiter)} |
| |
| h = NewMockMessagePipeHandle() |
| stubIn, stubOut := h, h |
| interfaceRequest := test.ConformanceTestInterface_Request{pipeOwner(stubOut)} |
| stub := test.NewConformanceTestInterfaceStub(interfaceRequest, impl, waiter) |
| for _, test := range tests { |
| bytes, handles := readTest(test) |
| answer := readAnswer(test) |
| impl.CheckArgs = strings.HasSuffix(test, "_good") |
| stubIn.WriteMessage(bytes, handles, system.MOJO_WRITE_MESSAGE_FLAG_NONE) |
| err := stub.ServeRequest() |
| verifyValidationError(t, test, err, answer) |
| |
| if !impl.CheckArgs { |
| continue |
| } |
| // Decode again to verify correctness of encoding. |
| _, bytes, handles = proxyOut.ReadMessage(system.MOJO_READ_MESSAGE_FLAG_NONE) |
| stubIn.WriteMessage(bytes, handles, system.MOJO_WRITE_MESSAGE_FLAG_NONE) |
| if err := stub.ServeRequest(); err != nil { |
| t.Fatalf("error processing encoded data for test %v: %v", test, err) |
| } |
| proxyOut.ReadMessage(system.MOJO_READ_MESSAGE_FLAG_NONE) |
| // Do not compare encoded bytes, as some tests contain maps, that |
| // can be encoded randomly. |
| } |
| } |
| |
| func runTests(t *testing.T, prefix string, in *mockMessagePipeHandle, check func() error) { |
| tests := getMatchingTests(listTestFiles(), prefix) |
| for _, test := range tests { |
| bytes, handles := readTest(test) |
| answer := readAnswer(test) |
| // Replace request ID to match proxy numbers. |
| if bytes[0] == 24 { |
| bytes[16] = 1 |
| } |
| in.WriteMessage(bytes, handles, system.MOJO_WRITE_MESSAGE_FLAG_NONE) |
| verifyValidationError(t, test, check(), answer) |
| } |
| } |
| |
| type integrationStubImpl struct{} |
| |
| func (s *integrationStubImpl) Method0(test.BasicStruct) ([]uint8, error) { |
| return nil, nil |
| } |
| |
| func TestIntegrationTests(t *testing.T) { |
| h := NewMockMessagePipeHandle() |
| checkStub := func() error { |
| interfaceRequest := test.IntegrationTestInterface_Request{pipeOwner(h)} |
| stub := test.NewIntegrationTestInterfaceStub(interfaceRequest, &integrationStubImpl{}, waiter) |
| err := stub.ServeRequest() |
| stub.Close() |
| h.reset() |
| return err |
| } |
| runTests(t, "integration_intf_rqst_", h, checkStub) |
| runTests(t, "integration_msghdr_", h, checkStub) |
| |
| checkProxy := func() error { |
| interfacePointer := test.IntegrationTestInterface_Pointer{pipeOwner(h)} |
| proxy := test.NewIntegrationTestInterfaceProxy(interfacePointer, waiter) |
| _, err := proxy.Method0(test.BasicStruct{}) |
| proxy.Close_Proxy() |
| h.reset() |
| return err |
| } |
| runTests(t, "integration_intf_resp_", h, checkProxy) |
| runTests(t, "integration_msghdr_", h, checkProxy) |
| } |
| |
| type boundsCheckStubImpl struct{} |
| |
| func (s *boundsCheckStubImpl) Method0(uint8) (uint8, error) { |
| return 0, nil |
| } |
| |
| func (s *boundsCheckStubImpl) Method1(uint8) error { |
| return nil |
| } |
| |
| func TestBoundsCheck(t *testing.T) { |
| h := NewMockMessagePipeHandle() |
| checkStub := func() error { |
| interfaceRequest := test.BoundsCheckTestInterface_Request{pipeOwner(h)} |
| stub := test.NewBoundsCheckTestInterfaceStub(interfaceRequest, &boundsCheckStubImpl{}, waiter) |
| err := stub.ServeRequest() |
| stub.Close() |
| return err |
| } |
| runTests(t, "boundscheck_", h, checkStub) |
| |
| checkProxy := func() error { |
| interfacePointer := test.BoundsCheckTestInterface_Pointer{pipeOwner(h)} |
| proxy := test.NewBoundsCheckTestInterfaceProxy(interfacePointer, waiter) |
| _, err := proxy.Method0(0) |
| proxy.Close_Proxy() |
| h.reset() |
| return err |
| } |
| runTests(t, "resp_boundscheck_", h, checkProxy) |
| } |
| |
| func TestConformanceResponse(t *testing.T) { |
| h := NewMockMessagePipeHandle() |
| checkProxy := func() error { |
| interfacePointer := test.ConformanceTestInterface_Pointer{pipeOwner(h)} |
| proxy := test.NewConformanceTestInterfaceProxy(interfacePointer, waiter) |
| _, err := proxy.Method12(0) |
| proxy.Close_Proxy() |
| h.reset() |
| return err |
| } |
| runTests(t, "resp_conformance_", h, checkProxy) |
| } |