| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // +build fuchsia |
| |
| package fidl_test |
| |
| import ( |
| "bytes" |
| "fmt" |
| "reflect" |
| "testing" |
| |
| "syscall/zx" |
| "syscall/zx/fidl" |
| ) |
| |
| func makeDefault(typ reflect.Type) fidl.Message { |
| if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct { |
| panic(fmt.Sprintf("expecting *struct, actual %v", typ)) |
| } |
| return reflect.New(typ.Elem()).Interface().(fidl.Message) |
| } |
| |
| // Note: fidl.Marshal uses the provided buffers to encode and works positionally to allow |
| // repeated encoding in the same buffer. This function doesn't do that and always starts |
| // with an empty buffer so tests do not cover the repeated encoding cases. |
| func encode(input fidl.Message) ([]byte, []zx.Handle, error) { |
| var respb [zx.ChannelMaxMessageBytes]byte |
| var resph [zx.ChannelMaxMessageHandles]zx.Handle |
| nb, nh, err := fidl.Marshal(input, respb[:], resph[:]) |
| if err != nil { |
| return nil, nil, err |
| } |
| return respb[:nb], resph[:nh], nil |
| } |
| |
| func decode(typ reflect.Type, b []byte, h []zx.Handle) (nb int, nh int, msg fidl.Message, err error) { |
| msg = makeDefault(typ) |
| nb, nh, err = fidl.Unmarshal(b, h, msg) |
| return nb, nh, msg, err |
| } |
| |
| type encodeSuccessCase struct { |
| name string |
| input fidl.Message |
| bytes []byte |
| handles []zx.Handle |
| } |
| |
| func (ex encodeSuccessCase) check(t *testing.T) { |
| t.Run(ex.name+" encode", func(t *testing.T) { |
| defer handlePanic(t) |
| b, h, err := encode(ex.input) |
| if err != nil { |
| t.Fatalf("failed: %s", err) |
| } |
| if !bytes.Equal(ex.bytes, b) { |
| t.Fatalf("expected %x, got %x", ex.bytes, b) |
| } |
| if len(ex.handles) == 0 { |
| if len(h) != 0 { |
| t.Fatalf("no handles expected, got %d", len(h)) |
| } |
| } else { |
| if !reflect.DeepEqual(ex.handles, h) { |
| t.Fatalf("expected %v, got %v", ex.handles, h) |
| } |
| } |
| }) |
| } |
| |
| type decodeSuccessCase struct { |
| name string |
| input fidl.Message |
| bytes []byte |
| handles []zx.Handle |
| } |
| |
| func (ex decodeSuccessCase) check(t *testing.T) { |
| t.Run(ex.name+" decode", func(t *testing.T) { |
| defer handlePanic(t) |
| nbActual, nhActual, output, err := decode(reflect.TypeOf(ex.input), ex.bytes, ex.handles) |
| if err != nil { |
| t.Fatalf("failed: %s", err) |
| } |
| if !reflect.DeepEqual(ex.input, output) { |
| t.Fatalf("expected: %v, got: %v", ex.input, output) |
| } |
| if len(ex.bytes) != nbActual { |
| t.Fatalf("num bytes, expected: %d, got: %d", len(ex.bytes), nbActual) |
| } |
| if len(ex.handles) != nhActual { |
| t.Fatalf("num handles, expected: %d, got: %d", len(ex.handles), nhActual) |
| } |
| }) |
| } |
| |
| // Represents a success case where both encode and decode should succeed. |
| type successCase struct { |
| name string |
| input fidl.Message |
| bytes []byte |
| handles []zx.Handle |
| } |
| |
| func (ex successCase) check(t *testing.T) { |
| encodeSuccessCase{ |
| name: ex.name, |
| input: ex.input, |
| bytes: ex.bytes, |
| handles: ex.handles, |
| }.check(t) |
| decodeSuccessCase{ |
| name: ex.name, |
| input: ex.input, |
| bytes: ex.bytes, |
| handles: ex.handles, |
| }.check(t) |
| } |
| |
| // TODO(FIDL-625) Support handles. |
| type encodeFailureCase struct { |
| name string |
| input fidl.Message |
| code fidl.ErrorCode |
| } |
| |
| func (ex encodeFailureCase) check(t *testing.T) { |
| t.Run(ex.name, func(t *testing.T) { |
| defer handlePanic(t) |
| _, _, err := encode(ex.input) |
| if err == nil { |
| t.Fatalf("encoding failure %s expected, but no errors were found", ex.code.String()) |
| } |
| fidlError, ok := err.(fidl.ValidationError) |
| if !ok { |
| t.Fatalf("wrong error type: got %T, want fidl.ValidationError", err) |
| } |
| if fidlError.Code() != ex.code { |
| t.Fatalf("wrong error code: got %s, want %s", fidlError.Code().String(), ex.code.String()) |
| } |
| }) |
| } |
| |
| // TODO(FIDL-625) Support handles. |
| type decodeFailureCase struct { |
| name string |
| valTyp reflect.Type |
| bytes []byte |
| code fidl.ErrorCode |
| } |
| |
| func (ex decodeFailureCase) check(t *testing.T) { |
| t.Run(ex.name, func(t *testing.T) { |
| defer handlePanic(t) |
| _, _, _, err := decode(ex.valTyp, ex.bytes, nil) |
| if err == nil { |
| t.Fatalf("decoding failure %s expected, but no errors were found", ex.code.String()) |
| } |
| fidlError, ok := err.(fidl.ValidationError) |
| if !ok { |
| t.Fatalf("wrong error type: got %T, want fidl.ValidationError", err) |
| } |
| if fidlError.Code() != ex.code { |
| t.Fatalf("wrong error code: got %s, want %s", fidlError.Code().String(), ex.code.String()) |
| } |
| }) |
| } |
| |
| func handlePanic(t *testing.T) { |
| // When running tests on device, this bubbles up the error on the |
| // console launching the tests, rather than having to look at the |
| // device's kernel logs. |
| if r := recover(); r != nil { |
| t.Fatalf("panic: %s", r) |
| panic(r) |
| } |
| } |