blob: b5a265f041ad5352bcbb2343d73337fa8dd4a2f3 [file] [log] [blame]
// Copyright 2018 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.
package fidl_test
import (
"bytes"
"fmt"
"reflect"
"strings"
"syscall/zx"
"syscall/zx/fidl"
"syscall/zx/fidl/internal/bindingstest"
"testing"
)
var testCtx fidl.MarshalerContext
func TestMarshalMessageHeader(t *testing.T) {
data := []byte{
0x12, 0x34, 0x56, 0x78, // txid
0xAB, 0xCD, 0xEF, // flags
0x01, // magic number
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // method ordinal
}
var header fidl.MessageHeader
hnb, _, err := fidl.Unmarshal(data, nil, &header)
if err != nil {
t.Fatalf("unmarshal failed: %s", err)
}
if hnb != 16 {
t.Fatalf("expected 16 bytes read, was %d", hnb)
}
if header.Magic != 0x01 {
t.Fatalf("expected header txid of 0x01, was %x", header.Magic)
}
}
// TODO(https://fxbug.dev/7832): do we purposefully provide buffers too big in
// conformance tests to ensure the returned size is properly calculated? If
// not, we should keep this sort of testing, or augment the conformance tests.
func TestCheckUnmarshalReadSize(t *testing.T) {
examples := [][]byte{
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // these will go unread
},
}
for _, data := range examples {
var message bindingstest.EmptyStruct
hnb, _, err := fidl.Unmarshal(data, nil, &message)
if err != nil {
t.Fatalf("unmarshal failed: %s", err)
}
if hnb != 8 {
t.Fatalf("expected 8 bytes read, was %d", hnb)
}
}
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
func TestEnvelopeByteCountTooLarge(t *testing.T) {
input := []byte{
0x21, 0xEB, 0x7E, 0x76, 0x00, 0x00, 0x00, 0x00, // ordinal + padding
0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, // byte + handle count
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // envelope present
0xef, 0xbe, 0xad, 0xde, 0x11, 0xba, 0x5e, 0xba, // data
}
var message bindingstest.XUnion1Struct
_, _, err := fidl.Unmarshal(input, nil, &message)
want := fidl.ErrPayloadTooSmall
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == want {
return
}
}
t.Fatalf("got fidl.Unmarshal(...) = (_, _, %v), want %s", err, want)
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
func TestEnvelopeHandleCountTooLarge(t *testing.T) {
input := []byte{
0x21, 0xEB, 0x7E, 0x76, 0x00, 0x00, 0x00, 0x00, // ordinal + padding
0x08, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, // byte + invalid handle count
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // envelope present
0xef, 0xbe, 0xad, 0xde, 0x11, 0xba, 0x5e, 0xba, // data
}
var message bindingstest.XUnion1Struct
_, _, err := fidl.Unmarshal(input, nil, &message)
want := fidl.ErrTooManyHandles
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == want {
return
}
}
t.Fatalf("got fidl.Unmarshal(...) = (_, _, %v), want %s", err, want)
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
func TestEnvelopeInvalidPresence(t *testing.T) {
input := []byte{
0x21, 0xEB, 0x7E, 0x76, 0x00, 0x00, 0x00, 0x00, // ordinal + padding
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // byte + handle count
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // invalid envelope presence
0xef, 0xbe, 0xad, 0xde, 0x11, 0xba, 0x5e, 0xba, // data
}
var message bindingstest.XUnion1Struct
_, _, err := fidl.Unmarshal(input, nil, &message)
want := fidl.ErrBadRefEncoding
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == want {
return
}
}
t.Fatalf("got fidl.Unmarshal(...) = (_, _, %v), want %s", err, want)
}
type example struct {
name string
input fidl.Message
expectSize int
}
// general provides test cases used to verify correctness, and
// benchmark against.
//
// Keep these as a slice to preserve consistent ordering when running.
func general() []example {
vmo, err := zx.NewVMO(10, 0)
if err != nil {
panic(fmt.Sprintf("failed to create vmo: %s", err))
}
defer func() {
if err := vmo.Close(); err != nil {
panic(fmt.Sprintf("failed to close vmo: %s", err))
}
}()
h0, h1, err := zx.NewChannel(0)
if err != nil {
panic(fmt.Sprintf("failed to create channel: %s", err))
}
defer func() {
if err := h0.Close(); err != nil {
panic(fmt.Sprintf("failed to close channel: %s", err))
}
if err := h1.Close(); err != nil {
panic(fmt.Sprintf("failed to close channel: %s", err))
}
}()
st1 := bindingstest.SimpleTable{}
st1.SetX(42)
st1.SetY(67)
return []example{
// TODO(https://fxbug.dev/7832): We lack coverage of floats in the conformance test suite.
{"float1", &bindingstest.TestFloat1{A: -36.0}, 8},
{"float2", &bindingstest.TestFloat2{A: -1254918271.0}, 8},
{"float3", &bindingstest.TestFloat3{A: 1241.1, B: 0.2141, C: 20, D: 0.0}, 32},
// TODO(https://fxbug.dev/7832): We just recently added handles in conformance suite. Are these covered?
{"handle1", &bindingstest.TestHandle1{
A: zx.Handle(22),
B: zx.HandleInvalid,
C: vmo,
D: zx.VMO(zx.HandleInvalid),
}, 16},
{"handle2", &bindingstest.TestHandle2{
A: []zx.Handle{zx.Handle(vmo)},
B: []zx.VMO{zx.VMO(zx.HandleInvalid)},
}, 48},
// TODO(https://fxbug.dev/7832): Here we're checking the special logic adapting channels to
// `request<P>` or `P` representations. This is not supported by GIDL yet.
{"interface1", &bindingstest.TestInterface1{
A: bindingstest.Test1WithCtxInterface(fidl.ChannelProxy{Channel: h0}),
B: bindingstest.Test1WithCtxInterface(fidl.ChannelProxy{Channel: zx.Channel(zx.HandleInvalid)}),
C: bindingstest.Test1WithCtxInterfaceRequest(fidl.InterfaceRequest{Channel: h1}),
D: bindingstest.Test1WithCtxInterfaceRequest(fidl.InterfaceRequest{
Channel: zx.Channel(zx.HandleInvalid),
}),
}, 16},
{"recursive-struct", &bindingstest.TestRecursiveStruct{
S: &bindingstest.TestRecursiveStruct{
S: &bindingstest.TestRecursiveStruct{},
},
}, 24},
}
}
func check(t *testing.T, input fidl.Message, expectSize int) {
t.Helper()
var respb [zx.ChannelMaxMessageBytes]byte
var resphd [zx.ChannelMaxMessageHandles]zx.HandleDisposition
nb, nh, err := fidl.MarshalWithContext(testCtx, input, respb[:], resphd[:])
if err != nil {
t.Fatalf("marshal: failed: %s", err)
}
if nb != expectSize {
t.Fatalf("marshal: expected size %d but got %d: %s", expectSize, nb, respb[:nb])
}
var resphi [zx.ChannelMaxMessageHandles]zx.HandleInfo
for i := 0; i < nh; i++ {
resphi[i] = zx.HandleInfo{
Handle: resphd[i].Handle,
Type: resphd[i].Type,
Rights: resphd[i].Rights,
}
}
output := makeDefault(reflect.TypeOf(input))
nbActual, nhActual, err := fidl.UnmarshalWithContext2(testCtx, respb[:nb], resphi[:nh], output)
if err != nil {
t.Fatalf("unmarshal: failed: %s", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("unmarshal: expected: %#v, got: %#v", input, output)
}
if nb != nbActual {
t.Fatalf("unmarshal: num bytes, expected: %d, got: %d", nb, nbActual)
}
if nh != nhActual {
t.Fatalf("unmarshal: num handles, expected: %d, got: %d", nh, nhActual)
}
}
func TestCorrectness(t *testing.T) {
for _, ex := range general() {
t.Run(ex.name, func(t *testing.T) {
check(t, ex.input, ex.expectSize)
})
}
}
type errorCaseUnmarshal struct {
name string
message fidl.Message
input []byte
errorCode fidl.ErrorCode
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
var baseErrorCasesUnmarshal = []errorCaseUnmarshal{
{"empty-array-bindingstest.TestSimple", &bindingstest.TestSimple{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestSimple", &bindingstest.TestSimple{}, nil, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestSimple", &bindingstest.TestSimple{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestSimpleBool", &bindingstest.TestSimpleBool{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestSimpleBool", &bindingstest.TestSimpleBool{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestAlignment1", &bindingstest.TestAlignment1{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestAlignment1", &bindingstest.TestAlignment1{}, nil, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestAlignment1", &bindingstest.TestAlignment1{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestAlignment2", &bindingstest.TestAlignment2{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestAlignment2", &bindingstest.TestAlignment2{}, nil, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestAlignment2", &bindingstest.TestAlignment2{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestAlignment2-2", &bindingstest.TestAlignment2{}, make([]byte, 10), fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestFloat1", &bindingstest.TestFloat1{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestFloat1", &bindingstest.TestFloat1{}, nil, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestFloat1", &bindingstest.TestFloat1{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestFloat2", &bindingstest.TestFloat2{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestFloat2", &bindingstest.TestFloat2{}, nil, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestFloat2", &bindingstest.TestFloat2{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestFloat3", &bindingstest.TestFloat3{}, []byte{}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestFloat3", &bindingstest.TestFloat3{}, nil, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestFloat3", &bindingstest.TestFloat3{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"small-array-bindingstest.TestFloat3-2", &bindingstest.TestFloat3{}, make([]byte, 6), fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestArray1", &bindingstest.TestArray1{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestArray1", &bindingstest.TestArray1{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-bindingstest.TestArray1", &bindingstest.TestArray1{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestArray2", &bindingstest.TestArray2{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestArray2", &bindingstest.TestArray2{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-bindingstest.TestArray2", &bindingstest.TestArray2{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"six-bytes-array-bindingstest.TestArray2", &bindingstest.TestArray2{}, make([]byte, 6), fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestArray3", &bindingstest.TestArray3{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestArray3", &bindingstest.TestArray3{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-bindingstest.TestArray3", &bindingstest.TestArray3{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"six-bytes-array-bindingstest.TestArray3", &bindingstest.TestArray3{}, make([]byte, 6), fidl.ErrPayloadTooSmall},
{"thirteen-bytes-array-bindingstest.TestArray3", &bindingstest.TestArray3{}, make([]byte, 13), fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestArray4", &bindingstest.TestArray4{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestArray4", &bindingstest.TestArray4{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-bindingstest.TestArray4", &bindingstest.TestArray4{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestString1", &bindingstest.TestString1{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestString1", &bindingstest.TestString1{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-bindingstest.TestString1", &bindingstest.TestString1{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"nil-array-bindingstest.TestString2", &bindingstest.TestString2{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-bindingstest.TestString2", &bindingstest.TestString2{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-bindingstest.TestString2", &bindingstest.TestString2{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"nil-array-TestStruct2", &bindingstest.TestStruct2{}, nil, fidl.ErrPayloadTooSmall},
{"empty-array-TestStruct2", &bindingstest.TestStruct2{}, []byte{}, fidl.ErrPayloadTooSmall},
{"two-bytes-array-TestStruct2", &bindingstest.TestStruct2{}, []byte{0x00, 0x0}, fidl.ErrPayloadTooSmall},
{"six-bytes-array-TestStruct2", &bindingstest.TestStruct2{}, make([]byte, 6), fidl.ErrPayloadTooSmall},
{"thirteen-bytes-array-TestStruct2", &bindingstest.TestStruct2{}, make([]byte, 13), fidl.ErrPayloadTooSmall},
{"empty-struct-non-zero", &bindingstest.EmptyStruct{}, emptyStructWithOneInsteadOfZero, fidl.ErrInvalidEmptyStruct},
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
var allErrorCasesUnmarshal = append(baseErrorCasesUnmarshal, []errorCaseUnmarshal{
{"string-wrong-ptr-no-alloc", &bindingstest.TestStringWithBound{}, []byte{
3, 0, 0, 0, 0, 0, 0, 0, // length
0, 0, 0, 0, 0, 0, 0, 0, // ptr (no alloc)
// no data, unmarshal should fail before
}, fidl.ErrUnexpectedNullRef},
{"string-wrong-ptr-incorrect", &bindingstest.TestStringWithBound{}, []byte{
3, 0, 0, 0, 0, 0, 0, 0, // length
0, 0, 0, 0, 0, 0, 0, 1, // ptr (no alloc)
// no data, unmarshal should fail before
}, fidl.ErrBadRefEncoding},
{"string-too-long", &bindingstest.TestStringWithBound{}, []byte{
9, 0, 0, 0, 0, 0, 0, 0, // length (too long)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ptr
// no data, unmarshal should fail before
}, fidl.ErrStringTooLong},
{"string-has-truncated-data", &bindingstest.TestStringWithBound{}, []byte{
8, 0, 0, 0, 0, 0, 0, 0, // length
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ptr
0x41, 0x72, 0x67, 0x68, 0x68, 0x68, 0x68, // only 7 bytes
}, fidl.ErrPayloadTooSmall},
{"opt-string-wrong-ptr-incorrect", &bindingstest.TestOptStringWithBound{}, []byte{
3, 0, 0, 0, 0, 0, 0, 0, // length
0, 0, 0, 0, 0, 0, 0, 1, // ptr (no alloc)
// no data, unmarshal should fail before
}, fidl.ErrBadRefEncoding},
{"opt-string-too-long", &bindingstest.TestOptStringWithBound{}, []byte{
9, 0, 0, 0, 0, 0, 0, 0, // length (too long)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ptr
// no data, unmarshal should fail before
}, fidl.ErrStringTooLong},
}...)
var emptyStructWithOneInsteadOfZero = []byte{
1, // empty struct with invalid value of 1 (instead of zero)
0, 0, 0, 0, 0, 0, 0, // 7 bytes of padding after empty struct, to align to 64 bits
}
// TODO(https://fxbug.dev/7832): These should move into the conformance suite too as they captured
// actual regressions that occurred, which none of the other tests captured.
func TestAllManualSuccessCases(t *testing.T) {
// TODO(FIDL-635): Complete conversion.
var ipAddressConfig bindingstest.IpAddressConfig
ipAddressConfig.SetDhcp(true)
successCase{
name: "add-ethernet-device-request",
context: testCtx,
input: &bindingstest.TestAddEthernetDeviceRequest{
TopologicalPath: "@/dev/sys/pci/00:03.0/e1000/ethernet",
Config: bindingstest.InterfaceConfig{
Name: "ethp0003",
IpAddressConfig: ipAddressConfig,
},
Device: bindingstest.EthernetDeviceWithCtxInterface{Channel: zx.Channel(0xdeadbeef)},
},
bytes: []byte{
0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // topological_path
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // topological_path
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // name
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // name
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subnet (dhcp variant)
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subnet envelope (num bytes/handles)
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // subnet envelope (PRESENT)
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, // device (handle present)
0x40, 0x2f, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x79, // @/dev/sy
0x73, 0x2f, 0x70, 0x63, 0x69, 0x2f, 0x30, 0x30, // s/pci/00
0x3a, 0x30, 0x33, 0x2e, 0x30, 0x2f, 0x65, 0x31, // :03.0/e1
0x30, 0x30, 0x30, 0x2f, 0x65, 0x74, 0x68, 0x65, // 000/ethe
0x72, 0x6e, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, // rnet
0x65, 0x74, 0x68, 0x70, 0x30, 0x30, 0x30, 0x33, // ethp0003
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // subnet (dhcp data)
},
handleInfos: []zx.HandleInfo{
{
Handle: zx.Handle(0xdeadbeef),
Rights: fidl.ProtocolRights,
Type: zx.ObjectTypeChannel,
},
},
checkRights: true,
}.check(t)
successCase{
name: "package-resolver-resolve-request",
context: testCtx,
input: &bindingstest.TestPackageResolverResolveRequest{
PackageUrl: "a",
Selectors: []string{"a"},
UpdatePolicy: bindingstest.UpdatePolicy{
FetchIfAbsent: true,
AllowOldVersions: true,
},
Dir: bindingstest.EthernetDeviceWithCtxInterfaceRequest{Channel: zx.Channel(0xdeadbeef)},
},
bytes: []byte{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // package url size
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // package url ptr
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // selectors size
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // selectors ptr
0x01, 0x01, 0x00, 0x00, // policy struct + padding
0xFF, 0xFF, 0xFF, 0xFF, // request handle
// out of line data
0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
handleInfos: []zx.HandleInfo{
{
Handle: zx.Handle(0xdeadbeef),
Rights: fidl.ProtocolRights,
Type: zx.ObjectTypeChannel,
},
},
checkRights: true,
}.check(t)
}
// Handle rights is not in conformance testing.
func TestHandleRightsMarshalUnmarshal(t *testing.T) {
data := make([]byte, 1024)
handleDispositions := make([]zx.HandleDisposition, 1)
input := &bindingstest.HandleRightsSubtypeTestStruct{H: []zx.VMO{5}}
_, nh, err := fidl.Marshal(input, data, handleDispositions)
if err != nil {
t.Fatal(err)
}
if nh != 1 {
t.Fatalf("unexpected number of handles: want %d, got %d", 1, nh)
}
if handleDispositions[0].Handle != *input.H[0].Handle() {
t.Fatalf("expected handle to match: want %d, got %d", input.H, handleDispositions[0].Handle)
}
if want := zx.RightDuplicate | zx.RightTransfer; handleDispositions[0].Rights != want {
t.Fatalf("incorrect rights: want %d, got %d", want, handleDispositions[0].Rights)
}
if want := zx.ObjectTypeVmo; handleDispositions[0].Type != want {
t.Fatalf("incorrect subtype: want %d, got %d", want, handleDispositions[0].Type)
}
// normal unmarshaling
{
handleInfos := []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Rights: handleDispositions[0].Rights,
Type: handleDispositions[0].Type,
},
}
output := &bindingstest.HandleRightsSubtypeTestStruct{}
if _, _, err := fidl.Unmarshal(data, handleInfos, output); err != nil {
t.Fatal(err)
}
if output.H[0] != input.H[0] {
t.Fatalf("incorrect handle: want %d, got %d", input.H, output.H)
}
}
// missing required rights
{
handleInfos := []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Rights: 0x1,
Type: handleDispositions[0].Type,
},
}
output := &bindingstest.HandleRightsSubtypeTestStruct{}
func() {
_, _, err := fidl.Unmarshal(data, handleInfos, output)
want := fidl.ErrMissingRequiredHandleRights
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == want {
return
}
}
t.Fatalf("got fidl.Unmarshal(...) = (_, _, %v), want %s", want, err)
}()
}
// incorrect handle type
{
handleInfos := []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Rights: handleDispositions[0].Rights,
Type: 1,
},
}
output := &bindingstest.HandleRightsSubtypeTestStruct{}
func() {
_, _, err := fidl.Unmarshal(data, handleInfos, output)
want := fidl.ErrIncorrectHandleType
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == want {
return
}
}
t.Fatalf("got fidl.Unmarshal(...) = (_, _, %v), want %s", want, err)
}()
}
}
// Handle rights is not in conformance testing.
func TestHandleUncheckedSubtypeMarshalUnmarshal(t *testing.T) {
data := make([]byte, 1024)
handleDispositions := make([]zx.HandleDisposition, 1)
input := &bindingstest.PlainHandleTestStruct{H: zx.Handle(5)}
_, nh, err := fidl.Marshal(input, data, handleDispositions)
if err != nil {
t.Fatal(err)
}
if nh != 1 {
t.Fatalf("unexpected number of handles: want %d, got %d", 1, nh)
}
if handleDispositions[0].Handle != input.H {
t.Fatalf("expected handle to match: want %d, got %d", input.H, handleDispositions[0].Handle)
}
if handleDispositions[0].Type != 0 {
t.Fatalf("incorrect subtype: want %d, got %d", 0, handleDispositions[0].Type)
}
// normal unmarshaling
{
handleInfos := []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Type: handleDispositions[0].Type,
},
}
output := &bindingstest.PlainHandleTestStruct{}
if _, _, err := fidl.Unmarshal(data, handleInfos, output); err != nil {
t.Fatal(err)
}
if output.H != input.H {
t.Fatalf("incorrect handle: want %d, got %d", input.H, output.H)
}
}
// unchecked handle type
{
handleInfos := []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Type: 1,
},
}
output := &bindingstest.PlainHandleTestStruct{}
if _, _, err = fidl.Unmarshal(data, handleInfos, output); err != nil {
t.Fatalf("expected handle of type ZX_OBJ_TYPE_NONE to be unchecked")
}
if output.H != input.H {
t.Fatalf("incorrect handle: want %d, got %d", input.H, output.H)
}
}
}
// This is a specific test checking that the marshaler construction properly
// fails with a clear error message if fed incorrect generated code. Hence,
// this has to stay a hand written test.
func TestFailureNullableTable(t *testing.T) {
type TestNullableTable struct {
_ struct{} `fidl:"s"`
A *bindingstest.SimpleTable `fidl:"0" fidl_offset_v1:"0"`
}
_, err := fidl.CreateMarshaler(TestNullableTable{})
if err == nil {
t.Fatalf("expected error creating marshaler for nullable table")
}
if !strings.Contains(err.Error(), "optional field marshaler") {
t.Fatalf("unexpected error: %s", err.Error())
}
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
func TestFailuresMarshal(t *testing.T) {
v1 := []int64{1, 2, 3}
cases := []struct {
name string
input fidl.Message
errorCode fidl.ErrorCode
}{
{"string3-string-too-long", &bindingstest.TestString3{
A: [2]string{
"too long!", // limit is 4, provided is longer(tm)
"g",
},
B: [2]*string{nil, nil},
}, fidl.ErrStringTooLong},
{"vector1-C-vector-too-long", &bindingstest.TestVector1{
A: []int8{1, 2, 3, 4},
B: nil,
C: []int32{99, 100, 101}, // limit is 2, provided is 3
D: nil,
}, fidl.ErrVectorTooLong},
{"vector1-D-vector-too-long", &bindingstest.TestVector1{
A: []int8{1, 2, 3, 4},
B: nil,
C: []int32{99},
D: &v1, // limit is 2, provided is 3
}, fidl.ErrVectorTooLong},
}
for _, ex := range cases {
t.Run(ex.name, func(t *testing.T) {
var respb [zx.ChannelMaxMessageBytes]byte
var resphd [zx.ChannelMaxMessageHandles]zx.HandleDisposition
_, _, err := fidl.MarshalWithContext(testCtx, ex.input, respb[:], resphd[:])
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == ex.errorCode {
return
}
}
t.Fatalf("got fidl.MarshalWithContext(...) = (_, _, %v), want %s", ex.errorCode, err)
})
}
}
// TODO(https://fxbug.dev/7832): We are lacking in negative tests in the conformance suite.
func TestFailuresUnmarshalNoHandles(t *testing.T) {
for _, ex := range allErrorCasesUnmarshal {
t.Run(ex.name, func(t *testing.T) {
_, _, err := fidl.Unmarshal(ex.input, nil, ex.message)
switch err := err.(type) {
case fidl.ValidationError:
if err.Code() == ex.errorCode {
return
}
}
t.Fatalf("got fidl.Unmarshal(...) = (_, _, %v), want %s", ex.errorCode, err)
})
}
}
// TODO(https://fxbug.dev/62647): Move to GIDL once we support protocol handles.
func TestConformanceClientEnd(t *testing.T) {
successCase{
name: "handles-clientend",
context: testCtx,
input: &bindingstest.HasClientEnd{
ClientEnd: bindingstest.ExampleProtocolWithCtxInterface(fidl.ChannelProxy{
Channel: zx.Channel(0x44332211),
}),
},
bytes: []byte{
0xff, 0xff, 0xff, 0xff, // handle present
0x00, 0x00, 0x00, 0x00, // padding
},
handleInfos: []zx.HandleInfo{
{
Handle: zx.Handle(0x44332211),
Rights: fidl.ProtocolRights,
Type: zx.ObjectTypeChannel,
},
},
checkRights: true,
}.check(t)
}
// TODO(https://fxbug.dev/62647): Move to GIDL once we support protocol handles.
func TestConformanceServerEnd(t *testing.T) {
successCase{
name: "handles-serverend",
context: testCtx,
input: &bindingstest.HasServerEnd{
ServerEnd: bindingstest.ExampleProtocolWithCtxInterfaceRequest(fidl.InterfaceRequest{
Channel: zx.Channel(0x44332211),
}),
},
bytes: []byte{
0xff, 0xff, 0xff, 0xff, // handle present
0x00, 0x00, 0x00, 0x00, // padding
},
handleInfos: []zx.HandleInfo{
{
Handle: zx.Handle(0x44332211),
Rights: fidl.ProtocolRights,
Type: zx.ObjectTypeChannel,
},
},
checkRights: true,
}.check(t)
}
func TestBitsApi(t *testing.T) {
raw := uint32(0b110101)
unknownStrict := bindingstest.StrictBits(raw)
if !unknownStrict.HasUnknownBits() {
t.Error("unknown bits returned false for HasUnknownBits")
}
if unknownStrict.GetUnknownBits() != 0b110100 {
t.Errorf(
"Got wrong unknown bits: expected %b, was %b", 0b110100, unknownStrict.GetUnknownBits())
}
if unknownStrict.InvertBits() != 0b10 {
t.Errorf(
"Got wrong InvertBits: expected %b, got %b", 0b10, unknownStrict.InvertBits())
}
if unknownStrict.InvertBits().InvertBits() != 0b1 {
t.Errorf(
"Got wrong double InvertBits: expected %b, got %b", 0b1, unknownStrict.InvertBits().InvertBits())
}
if !unknownStrict.HasBits(0b1) {
t.Errorf("Failed to match on known bits for HasBits")
}
if !unknownStrict.HasBits(0b110000) {
t.Errorf("Failed to match on unknown bits for HasBits")
}
if unknownStrict.HasBits(0b10) {
t.Errorf("Matched on known bits for HasBits when it should not")
}
if unknownStrict.HasBits(0b1000) {
t.Errorf("Matched on unknown bits for HasBits when it should not")
}
if unknownStrict.ClearBits(0b1) != bindingstest.StrictBits(0b110100) {
t.Errorf("Failed to clear known bits for ClearBits")
}
if unknownStrict.ClearBits(0b10000) != bindingstest.StrictBits(0b100101) {
t.Errorf("Failed to clear unknown bits for ClearBits")
}
if unknownStrict != unknownStrict.ClearBits(0b10) {
t.Errorf("Cleared known bits when it should not")
}
if unknownStrict != unknownStrict.ClearBits(0b1000) {
t.Errorf("Cleared unknown bits when it should not")
}
knownStrict := unknownStrict & bindingstest.StrictBits_Mask
if knownStrict != bindingstest.StrictBitsOne {
t.Errorf("Expected masked value: %b, got %b", bindingstest.StrictBitsOne, knownStrict)
}
unknownFlexible := bindingstest.FlexibleBits(raw)
if !unknownFlexible.HasUnknownBits() {
t.Error("unknown bits returned false for HasUnknownBits")
}
if unknownFlexible.GetUnknownBits() != 0b110100 {
t.Errorf(
"Got wrong unknown bits: expected %b, was %b", 0b110100, unknownFlexible.GetUnknownBits())
}
if unknownFlexible.InvertBits() != 0b10 {
t.Errorf(
"Got wrong InvertBits: expected %b, got %b", 0b10, unknownFlexible.InvertBits())
}
if unknownFlexible.InvertBits().InvertBits() != 0b1 {
t.Errorf(
"Got wrong double InvertBits: expected %b, got %b", 0b1, unknownFlexible.InvertBits().InvertBits())
}
if !unknownFlexible.HasBits(0b1) {
t.Errorf("Failed to match on known bits for HasBits")
}
if !unknownFlexible.HasBits(0b110000) {
t.Errorf("Failed to match on unknown bits for HasBits")
}
if unknownFlexible.HasBits(0b10) {
t.Errorf("Matched on known bits for HasBits when it should not")
}
if unknownFlexible.HasBits(0b1000) {
t.Errorf("Matched on unknown bits for HasBits when it should not")
}
if unknownFlexible.ClearBits(0b1) != bindingstest.FlexibleBits(0b110100) {
t.Errorf("Failed to clear known bits for ClearBits")
}
if unknownFlexible.ClearBits(0b10000) != bindingstest.FlexibleBits(0b100101) {
t.Errorf("Failed to clear unknown bits for ClearBits")
}
if unknownFlexible != unknownFlexible.ClearBits(0b10) {
t.Errorf("Cleared known bits when it should not")
}
if unknownFlexible != unknownFlexible.ClearBits(0b1000) {
t.Errorf("Cleared unknown bits when it should not")
}
knownFlexible := unknownFlexible & bindingstest.FlexibleBits_Mask
if knownFlexible != bindingstest.FlexibleBitsOne {
t.Errorf("Expected masked value: %b, got %b", bindingstest.FlexibleBitsOne, knownFlexible)
}
}
func TestUnionApi(t *testing.T) {
union := bindingstest.XUnion1{
I_xUnion1Tag: 4,
I_unknownData: fidl.UnknownData{
Bytes: []byte{51},
},
}
if union.Which() != bindingstest.XUnion1_unknownData {
t.Errorf("Expected unknown union to have unknown tag")
}
data := union.GetUnknownData()
if want := []byte{51}; !bytes.Equal(data.Bytes, want) {
t.Errorf("Expected unknown data to be %s, got: %s", want, data.Bytes)
}
if len(data.Handles) != 0 {
t.Errorf("Expected empty unknown handles, got %d", data.Handles)
}
union.SetD(1.0)
if union != bindingstest.XUnion1WithD(1.0) {
t.Errorf("Expected modified-in-place union modified from unknown variant and constructed union to be equal")
}
union.SetA([3]int8{0, 1, 2})
if union != bindingstest.XUnion1WithA([3]int8{0, 1, 2}) {
t.Errorf("Expected modified-in-place union modified from known variant and constructed union to be equal")
}
}
func TestTableApi(t *testing.T) {
var emptyTable bindingstest.SimpleTable
if emptyTable.HasUnknownData() {
t.Errorf("Expected new table to have empty unknown data")
}
table := bindingstest.SimpleTable{
I_unknownData: map[uint64]fidl.UnknownData{
3: {
Bytes: []byte{51},
},
},
}
table.SetX(5)
if !table.HasX() {
t.Errorf("Expected X to be set")
}
if table.GetX() != 5 {
t.Errorf("Expected X value 5, got: %d", table.GetX())
}
if table.HasY() {
t.Errorf("Expected Y to be unset")
}
y := table.GetYWithDefault(13)
if y != 13 {
t.Errorf("Expected default Y value 13, got: %d", y)
}
if !table.HasUnknownData() {
t.Errorf("Expected table to have unknown data")
}
unknownData := table.GetUnknownData()
data, ok := unknownData[3]
if !ok {
t.Errorf("Expected table to have unknown ordinal 3")
}
if want := []byte{51}; !bytes.Equal(data.Bytes, want) {
t.Errorf("Expected unknown data to be %s, got: %s", want, data.Bytes)
}
if len(data.Handles) != 0 {
t.Errorf("Expected empty unknown handles")
}
}
func TestRecursiveVectors(t *testing.T) {
successCase{
name: "recursive-vector-struct",
context: testCtx,
input: &bindingstest.TestRecursiveVectorStruct{
V: []*bindingstest.TestRecursiveVectorStruct{
{
V: []*bindingstest.TestRecursiveVectorStruct{},
},
},
},
bytes: []byte{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
}.check(t)
a := bindingstest.TestRecursiveVectorUnionWithA(1.0)
b := bindingstest.TestRecursiveVectorUnionWithB([]*bindingstest.TestRecursiveVectorUnion{
&a,
})
successCase{
name: "recursive-vector-union",
context: testCtx,
input: &bindingstest.TestRecursiveUnionStruct{
U: &b,
},
bytes: []byte{
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00,
},
}.check(t)
}