blob: 35e00530b33abda721afe695831773f2ded0a556 [file] [log] [blame]
// 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)
}
}