blob: 1f8b43b8ca71bf9a1eafa57be033ee0a6614aad1 [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 (
"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)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(fidl.ValidationError).Code()
if errCode != fidl.ErrPayloadTooSmall {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
// 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)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(fidl.ValidationError).Code()
if errCode != fidl.ErrTooManyHandles {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
// 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)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(fidl.ValidationError).Code()
if errCode != fidl.ErrBadRefEncoding {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
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: %v", err))
}
defer vmo.Close()
h0, h1, err := zx.NewChannel(0)
defer h0.Close()
defer h1.Close()
if err != nil {
panic(fmt.Sprintf("failed to create vmo: %v", 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},
}
}
func check(t *testing.T, input fidl.Message, expectSize int) {
t.Helper()
defer func() {
if r := recover(); r != nil {
// 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.
t.Fatalf("panic: %s", r)
panic(r)
}
}()
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: %v", 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 simpleTableWithXY = []byte{
5, 0, 0, 0, 0, 0, 0, 0, // max ordinal
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
8, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
0, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 3: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 4: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
8, 0, 0, 0, 0, 0, 0, 0, // envelope 5: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
42, 0, 0, 0, 0, 0, 0, 0, // field X
67, 0, 0, 0, 0, 0, 0, 0, // field Y
}
var simpleTableWithY = []byte{
5, 0, 0, 0, 0, 0, 0, 0, // max ordinal
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
0, 0, 0, 0, 0, 0, 0, 0, // envelope 1: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 2: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 3: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
0, 0, 0, 0, 0, 0, 0, 0, // envelope 4: num bytes / num handles
0, 0, 0, 0, 0, 0, 0, 0, // no alloc
8, 0, 0, 0, 0, 0, 0, 0, // envelope 5: num bytes / num handles
255, 255, 255, 255, 255, 255, 255, 255, // alloc present
67, 0, 0, 0, 0, 0, 0, 0, // field Y
}
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 %v, got %v", input.H, handleDispositions[0].Handle)
}
if handleDispositions[0].Rights != 3 {
t.Fatalf("incorrect rights: want %v, got %v", 3, handleDispositions[0].Rights)
}
if handleDispositions[0].Type != 3 {
t.Fatalf("incorrect subtype: want %v, got %v", 3, 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 %v, got %v", input.H, output.H)
}
// missing required rights
handleInfos = []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Rights: 0x1,
Type: handleDispositions[0].Type,
},
}
output = &bindingstest.HandleRightsSubtypeTestStruct{}
if _, _, err = fidl.Unmarshal(data, handleInfos, output); err == nil {
t.Fatalf("expected error due to missing required rights")
} else if err != nil && err.(fidl.ValidationError).Code() != fidl.ErrMissingRequiredHandleRights {
t.Fatalf("unexpected error code %v", err)
}
// incorrect handle type
handleInfos = []zx.HandleInfo{
{
Handle: handleDispositions[0].Handle,
Rights: handleDispositions[0].Rights,
Type: 1,
},
}
output = &bindingstest.HandleRightsSubtypeTestStruct{}
if _, _, err = fidl.Unmarshal(data, handleInfos, output); err == nil {
t.Fatalf("expected error due to incorrect handle type")
} else if err != nil && err.(fidl.ValidationError).Code() != fidl.ErrIncorrectHandleType {
t.Fatalf("unexpected error code %v", 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 %v, got %v", input.H, handleDispositions[0].Handle)
}
if handleDispositions[0].Type != 0 {
t.Fatalf("incorrect subtype: want %v, got %v", 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 %v, got %v", 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 %v, got %v", 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[:])
validationErr, ok := err.(fidl.ValidationError)
if !ok {
t.Fatalf("expected ValidationError, was %v", err)
}
if validationErr.Code() != ex.errorCode {
t.Fatalf("expected %s, was %s", ex.errorCode, validationErr.Code())
}
})
}
}
// 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)
validationErr, ok := err.(fidl.ValidationError)
if !ok {
t.Fatalf("expected ValidationError, was %v", err)
}
if validationErr.Code() != ex.errorCode {
t.Fatalf("expected %s, was %s", ex.errorCode, validationErr.Code())
}
})
}
}
// 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.InvertKnownBits() != 0b110110 {
t.Errorf(
"Got wrong InvertKnownBits: expected %b, got %b", 0b110110, unknownStrict.InvertKnownBits())
}
if unknownStrict != unknownStrict.InvertKnownBits().InvertKnownBits() {
t.Errorf(
"Got wrong double InvertKnownBits: expected %b, got %b", unknownStrict, unknownStrict.InvertKnownBits().InvertKnownBits())
}
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.InvertKnownBits() != 0b110110 {
t.Errorf(
"Got wrong InvertKnownBits: expected %b, got %b", 0b110110, unknownFlexible.InvertKnownBits())
}
if unknownFlexible != unknownFlexible.InvertKnownBits().InvertKnownBits() {
t.Errorf(
"Got wrong double InvertKnownBits: expected %b, got %b", unknownFlexible, unknownFlexible.InvertKnownBits().InvertKnownBits())
}
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 len(data.Bytes) != 1 || data.Bytes[0] != 51 {
t.Errorf("Expected unknown data to be [51], got: %v", data.Bytes)
}
if len(data.Handles) != 0 {
t.Errorf("Expected empty unknown handles")
}
}
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 len(data.Bytes) != 1 || data.Bytes[0] != 51 {
t.Errorf("Expected unknown data to be [51], got: %v", data.Bytes)
}
if len(data.Handles) != 0 {
t.Errorf("Expected empty unknown handles")
}
}