blob: 79688c0ccb2b31deaf0ad891aec30c16eaac4daf [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.
// +build fuchsia
package fidl_test
import (
"bytes"
"fmt"
"math/rand"
"reflect"
"strings"
"syscall/zx"
"testing"
. "syscall/zx/fidl"
. "syscall/zx/fidl/bindingstest"
)
var testCtx = func() MarshalerContext {
var hdr MessageHeader
ctx := hdr.NewCtx()
// The write wire format and read wire format will be the same within all
// tests, so set the decode field based on the encode field instead of the
// MessageHeader
ctx.DecodeUnionsFromXUnionBytes = ctx.EncodeUnionsAsXUnionBytes
return ctx
}()
var isV1 = testCtx.EncodeUnionsAsXUnionBytes
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 MessageHeader
hnb, _, err := 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)
}
}
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 EmptyStruct
hnb, _, err := 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)
}
}
}
func Test64bitOrdinal(t *testing.T) {
input := []byte{
0x21, 0xEB, 0x7E, 0x76, 0x55, 0x55, 0x55, 0x55, // 64bit ordinal
0x08, 0x00, 0x00, 0x00, 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 XUnion1Struct
_, _, err := Unmarshal(input, nil, &message)
if err != nil {
t.Fatalf("Unmarshal() failed: %s", err)
}
}
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 XUnion1Struct
_, _, err := Unmarshal(input, nil, &message)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(ValidationError).Code()
if errCode != ErrEnvelopeTooLong {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
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 XUnion1Struct
_, _, err := Unmarshal(input, nil, &message)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(ValidationError).Code()
if errCode != ErrTooManyHandles {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
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 XUnion1Struct
_, _, err := Unmarshal(input, nil, &message)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(ValidationError).Code()
if errCode != ErrBadRefEncoding {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
func TestStrictXUnionWithUnknownData(t *testing.T) {
input := []byte{
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ordinal + padding
0x08, 0x00, 0x00, 0x00, 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 StrictXUnion1Struct
_, _, err := Unmarshal(input, nil, &message)
if err == nil {
t.Fatalf("Unmarshal() returned nil error")
}
errCode := err.(ValidationError).Code()
if errCode != ErrInvalidXUnionTag {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
func TestFlexibleXUnionWithUnknownData(t *testing.T) {
input := []byte{
0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ordinal + padding
0x08, 0x00, 0x00, 0x00, 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 XUnion1Struct
_, _, err := Unmarshal(input, nil, &message)
if err != nil {
t.Fatalf("TestXUnionWithUnknownData failed: %s", err)
}
if int(message.Xu.Ordinal()) != int(input[0]) {
t.Fatalf("XUnion1Tag expected=%d actual=%d", input[0], message.Xu.Ordinal())
}
if !bytes.Equal(message.Xu.I_unknownData, input[24:32]) {
t.Fatalf("I_unknownData expected=%v, actual=%v", input[24:32], message.Xu.I_unknownData)
}
}
type example struct {
name string
input 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 {
var (
s = "bye"
s256 = strings.Repeat("hello!!!", 64)
s8192 = strings.Repeat("hello!!!", 1024)
v1 = []int64{-1}
v2 = []string{"x", "hello"}
v3 = []int64{101010}
xu1 = XUnion1{}
sxu1 = StrictXUnion1{}
)
xu1.SetB(TestSimple{X: 555})
sxu1.SetB(TestSimple{X: 555})
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 := SimpleTable{}
st1.SetX(42)
st1.SetY(67)
return []example{
// simple (to get started)
{"simple", &TestSimple{X: 124}, 8},
{"simplebool", &TestSimpleBool{X: true}, 8},
// alignment
{"align1", &TestAlignment1{X: -36, Y: -10, Z: 51}, 8},
{"align2", &TestAlignment2{
A: 1212141,
B: 908935,
C: -1,
D: 125,
E: -22,
F: 111,
G: 1515,
H: 65535,
I: 1515,
}, 24},
// floats
{"float1", &TestFloat1{A: -36.0}, 8},
{"float2", &TestFloat2{A: -1254918271.0}, 8},
{"float3", &TestFloat3{A: 1241.1, B: 0.2141, C: 20, D: 0.0}, 32},
// arrays
{"array1", &TestArray1{A: [5]int8{1, 77, 2, 4, 89}}, 8},
{"array2", &TestArray2{A: -1.0, B: [1]float32{0.2}}, 16},
{"array3", &TestArray3{
A: -999,
B: [3]uint16{11, 12, 13},
C: 1021,
}, 24},
{"array4", &TestArray4{
A: [9]bool{true, false, false, true, false, true, true, true, true},
}, 16},
// strings
{"string1", &TestString1{A: "str", B: nil}, 40},
{"string1-longer256", &TestString1{A: s256, B: &s256}, 1056},
{"string1-longer8192", &TestString1{A: s8192, B: &s8192}, 16416},
{"string2", &TestString2{A: [2]string{"hello", "g"}}, 48},
{"string3", &TestString3{
A: [2]string{"boop", "g"},
B: [2]*string{&s, nil},
}, 88},
{"string-with-bound", &TestStringWithBound{A: "str"}, 24},
{"opt-string-with-bound", &TestOptStringWithBound{A: &s}, 24},
// vectors
{"vector1", &TestVector1{
A: []int8{1, 2, 3, 4},
B: nil,
C: []int32{99},
D: &v1,
}, 88},
{"vector2", &TestVector2{
A: [2][]int8{{9, -1}, {}},
B: [][]int8{{-111, 41}, {-1, -1, -1, -1}},
C: []*[]string{nil, &v2},
}, 200},
// structs
{"struct1", &TestStruct1{
A: TestSimple{
X: -9999,
},
B: &TestSimple{
X: 1254125,
},
}, 24},
{"struct2", &TestStruct2{
A: TestArray1{
A: [5]int8{1, 77, 2, 4, 5},
},
B: TestFloat1{
A: 2.81212,
},
C: TestVector1{
A: []int8{1, 2, 3, 4},
B: nil,
C: []int32{99},
D: &v3,
},
D: &TestString1{
A: "str",
B: nil,
},
}, 152},
// xunions
{"xunion1", &TestXUnion1{
A: xu1,
B: nil,
}, 56}, // A=24(xunion_header), B=24(zero_xunion_header), OOL A=8(data)
{"xunion1-bis", &TestXUnion1{
A: xu1,
B: &xu1,
}, 64}, // A=24(xunion_header), B=24(xunion_header), OOL A=8(data), OOL B=8(data)
{"xunion2", &TestXUnion2{
A: []XUnion1{xu1, xu1, xu1},
B: []*XUnion1{&xu1, nil, nil},
}, 208}, // A=8(size)+8(ptr), B=8(size)+8(ptr), OOL A.vector_contents=3*24(xunion_header), OOL B.vector_contents=3*24, OOL A[0,1,2]=3*8, OOL B[0]=8
// strict xunions
{"strict-xunion1", &TestStrictXUnion1{
A: sxu1,
B: nil,
}, 56}, // A=24(xunion_header), B=24(zero_xunion_header), OOL A=8(data)
{"strict-xunion1-bis", &TestStrictXUnion1{
A: sxu1,
B: &sxu1,
}, 64}, // A=24(xunion_header), B=24(xunion_header), OOL A=8(data), OOL B=8(data)
{"strict-xunion2", &TestStrictXUnion2{
A: []StrictXUnion1{sxu1, sxu1, sxu1},
B: []*StrictXUnion1{&sxu1, nil, nil},
}, 208}, // A=8(size)+8(ptr), B=8(size)+8(ptr), OOL A.vector_contents=3*24(xunion_header), OOL B.vector_contents=3*24, OOL A[0,1,2]=3*8, OOL B[0]=8
// handles
{"handle1", &TestHandle1{
A: zx.Handle(22),
B: zx.HandleInvalid,
C: vmo,
D: zx.VMO(zx.HandleInvalid),
}, 16},
{"handle2", &TestHandle2{
A: []zx.Handle{zx.Handle(vmo)},
B: []zx.VMO{zx.VMO(zx.HandleInvalid)},
}, 48},
// interfaces
{"interface1", &TestInterface1{
A: Test1WithCtxInterface(ChannelProxy{Channel: h0}),
B: Test1WithCtxInterface(ChannelProxy{Channel: zx.Channel(zx.HandleInvalid)}),
C: Test1WithCtxInterfaceRequest(InterfaceRequest{Channel: h1}),
D: Test1WithCtxInterfaceRequest(InterfaceRequest{
Channel: zx.Channel(zx.HandleInvalid),
}),
}, 16},
// tables
{"table1", &TestSimpleTable{
Table: st1,
}, 112},
}
}
// Using a fixed seed for the randomizer for deterministic behavior of the
// tests, all the while using 'random looking' data.
var r = rand.New(rand.NewSource(524287))
func randBytes(num int) []byte {
bytes := make([]byte, num, num)
if _, err := r.Read(bytes); err != nil {
panic(err)
}
return bytes
}
// fuchsia provides test cases from fuchsia verify correctness, and
// benchmark against.
//
// Keep these as a slice to preserve consistent ordering when running.
func fuchsia() []example {
var (
bytes0 = []byte{}
bytes16 = randBytes(16)
bytes64 = randBytes(64)
bytes128 = randBytes(128)
bytes256 = randBytes(256)
bytes1024 = randBytes(1024)
bytes2048 = randBytes(2048)
bytes4096 = randBytes(4096)
bytes8192 = randBytes(8192)
)
return []example{
// fuchsia.io, ReadAt response
{"fuchsia.io-readAt-response0", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: []byte{},
}, 24 + 0},
{"fuchsia.io-readAt-response16", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: bytes16,
}, 24 + 16},
{"fuchsia.io-readAt-response64", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: bytes64,
}, 24 + 64},
{"fuchsia.io-readAt-response256", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: bytes256,
}, 24 + 256},
{"fuchsia.io-readAt-response1024", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: bytes1024,
}, 24 + 1024},
{"fuchsia.io-readAt-response4096", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: bytes4096,
}, 24 + 4096},
{"fuchsia.io-readAt-response8192", &TestFuchsiaIoReadAtResponse{
S: 5,
Data: bytes8192,
}, 24 + 8192},
// fuchsia.io, WriteAt request
{"fuchsia.io-writeAt-request0", &TestFuchsiaIoWriteAtRequest{
Data: []byte{},
Offset: 5,
}, 24 + 0},
{"fuchsia.io-writeAt-request16", &TestFuchsiaIoWriteAtRequest{
Data: bytes16,
Offset: 5,
}, 24 + 16},
{"fuchsia.io-writeAt-request64", &TestFuchsiaIoWriteAtRequest{
Data: bytes64,
Offset: 5,
}, 24 + 64},
{"fuchsia.io-writeAt-request256", &TestFuchsiaIoWriteAtRequest{
Data: bytes256,
Offset: 5,
}, 24 + 256},
{"fuchsia.io-writeAt-request1024", &TestFuchsiaIoWriteAtRequest{
Data: bytes1024,
Offset: 5,
}, 24 + 1024},
{"fuchsia.io-writeAt-request4096", &TestFuchsiaIoWriteAtRequest{
Data: bytes4096,
Offset: 5,
}, 24 + 4096},
{"fuchsia.io-writeAt-request8192", &TestFuchsiaIoWriteAtRequest{
Data: bytes8192,
Offset: 5,
}, 24 + 8192},
// fuchsia.posix.socket, SendMsg request
{"fuchsia.posix.socket-SendMsg-request0", &TestFuchsiaPosixSocketSendMsgRequest{
Sockaddr: bytes128,
Data: [][]byte{bytes0},
Control: bytes0,
Flags: 0,
}, 128 + 24 + 24 + 24 + 0},
{"fuchsia.posix.socket-SendMsg-request16", &TestFuchsiaPosixSocketSendMsgRequest{
Sockaddr: bytes128,
Data: [][]byte{bytes16},
Control: bytes0,
Flags: 0,
}, 128 + 24 + 24 + 24 + 16},
{"fuchsia.posix.socket-SendMsg-request64", &TestFuchsiaPosixSocketSendMsgRequest{
Sockaddr: bytes128,
Data: [][]byte{bytes64},
Control: bytes0,
Flags: 0,
}, 128 + 24 + 24 + 24 + 64},
{"fuchsia.posix.socket-SendMsg-request256", &TestFuchsiaPosixSocketSendMsgRequest{
Sockaddr: bytes128,
Data: [][]byte{bytes256},
Control: bytes0,
Flags: 0,
}, 128 + 24 + 24 + 24 + 256},
{"fuchsia.posix.socket-SendMsg-request1024", &TestFuchsiaPosixSocketSendMsgRequest{
Sockaddr: bytes128,
Data: [][]byte{bytes1024},
Control: bytes0,
Flags: 0,
}, 128 + 24 + 24 + 24 + 1024},
{"fuchsia.posix.socket-SendMsg-request2048", &TestFuchsiaPosixSocketSendMsgRequest{
Sockaddr: bytes128,
Data: [][]byte{bytes2048},
Control: bytes0,
Flags: 0,
}, 128 + 24 + 24 + 24 + 2048},
}
}
// TODO(FIDL-508): Convert all these tests to be conformance tests, using GIDL.
func check(t *testing.T, input 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 := 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 := 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)
})
}
for _, ex := range fuchsia() {
t.Run(ex.name, func(t *testing.T) {
check(t, ex.input, ex.expectSize)
})
}
}
type errorCaseUnmarshal struct {
name string
message Message
input []byte
errorCode ErrorCode
}
var baseErrorCasesUnmarshal = []errorCaseUnmarshal{
{"empty-array-TestSimple", &TestSimple{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestSimple", &TestSimple{}, nil, ErrPayloadTooSmall},
{"small-array-TestSimple", &TestSimple{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"empty-array-TestSimpleBool", &TestSimpleBool{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestSimpleBool", &TestSimpleBool{}, nil, ErrPayloadTooSmall},
{"empty-array-TestAlignment1", &TestAlignment1{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestAlignment1", &TestAlignment1{}, nil, ErrPayloadTooSmall},
{"small-array-TestAlignment1", &TestAlignment1{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"empty-array-TestAlignment2", &TestAlignment2{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestAlignment2", &TestAlignment2{}, nil, ErrPayloadTooSmall},
{"small-array-TestAlignment2", &TestAlignment2{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"small-array-TestAlignment2-2", &TestAlignment2{}, make([]byte, 10), ErrPayloadTooSmall},
{"empty-array-TestFloat1", &TestFloat1{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestFloat1", &TestFloat1{}, nil, ErrPayloadTooSmall},
{"small-array-TestFloat1", &TestFloat1{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"empty-array-TestFloat2", &TestFloat2{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestFloat2", &TestFloat2{}, nil, ErrPayloadTooSmall},
{"small-array-TestFloat2", &TestFloat2{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"empty-array-TestFloat3", &TestFloat3{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestFloat3", &TestFloat3{}, nil, ErrPayloadTooSmall},
{"small-array-TestFloat3", &TestFloat3{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"small-array-TestFloat3-2", &TestFloat3{}, make([]byte, 6), ErrPayloadTooSmall},
{"nil-array-TestArray1", &TestArray1{}, nil, ErrPayloadTooSmall},
{"empty-array-TestArray1", &TestArray1{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestArray1", &TestArray1{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"nil-array-TestArray2", &TestArray2{}, nil, ErrPayloadTooSmall},
{"empty-array-TestArray2", &TestArray2{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestArray2", &TestArray2{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"six-bytes-array-TestArray2", &TestArray2{}, make([]byte, 6), ErrPayloadTooSmall},
{"nil-array-TestArray3", &TestArray3{}, nil, ErrPayloadTooSmall},
{"empty-array-TestArray3", &TestArray3{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestArray3", &TestArray3{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"six-bytes-array-TestArray3", &TestArray3{}, make([]byte, 6), ErrPayloadTooSmall},
{"thirteen-bytes-array-TestArray3", &TestArray3{}, make([]byte, 13), ErrPayloadTooSmall},
{"nil-array-TestArray4", &TestArray4{}, nil, ErrPayloadTooSmall},
{"empty-array-TestArray4", &TestArray4{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestArray4", &TestArray4{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"nil-array-TestString1", &TestString1{}, nil, ErrPayloadTooSmall},
{"empty-array-TestString1", &TestString1{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestString1", &TestString1{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"nil-array-TestString2", &TestString2{}, nil, ErrPayloadTooSmall},
{"empty-array-TestString2", &TestString2{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestString2", &TestString2{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"nil-array-TestStruct2", &TestStruct2{}, nil, ErrPayloadTooSmall},
{"empty-array-TestStruct2", &TestStruct2{}, []byte{}, ErrPayloadTooSmall},
{"two-bytes-array-TestStruct2", &TestStruct2{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"six-bytes-array-TestStruct2", &TestStruct2{}, make([]byte, 6), ErrPayloadTooSmall},
{"thirteen-bytes-array-TestStruct2", &TestStruct2{}, make([]byte, 13), ErrPayloadTooSmall},
{"non-zero-or-one-bool", &TestSimpleBool{}, []byte{2}, ErrInvalidBoolValue},
{"empty-struct-non-zero", &EmptyStruct{}, emptyStructWithOneInsteadOfZero, ErrInvalidEmptyStruct},
}
var allErrorCasesUnmarshal = append(baseErrorCasesUnmarshal, []errorCaseUnmarshal{
{"string-wrong-ptr-no-alloc", &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
}, ErrUnexpectedNullRef},
{"string-wrong-ptr-incorrect", &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
}, ErrBadRefEncoding},
{"string-too-long", &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
}, ErrStringTooLong},
{"string-has-truncated-data", &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
}, ErrMessageTooSmall},
{"string-is-not-valid-utf8", &TestStringWithBound{}, []byte{
2, 0, 0, 0, 0, 0, 0, 0, // length
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // ptr
0xc3, 0x28, // invalid 2 octet sequence
}, ErrStringNotUTF8},
{"opt-string-wrong-ptr-incorrect", &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
}, ErrBadRefEncoding},
{"opt-string-too-long", &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
}, 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
}
func TestAllManualSuccessCases(t *testing.T) {
// TODO(FIDL-635): Complete conversion.
var ipAddressConfig IpAddressConfig
ipAddressConfig.SetDhcp(true)
successCase{
name: "add-ethernet-device-request",
context: MarshalerContext{
DecodeUnionsFromXUnionBytes: false,
EncodeUnionsAsXUnionBytes: false,
},
input: &TestAddEthernetDeviceRequest{
TopologicalPath: "@/dev/sys/pci/00:03.0/e1000/ethernet",
Config: InterfaceConfig{
Name: "ethp0003",
IpAddressConfig: ipAddressConfig,
},
Device: 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: ProtocolRights,
Type: zx.ObjectTypeChannel,
},
},
}.check(t)
successCase{
name: "package-resolver-resolve-request",
context: MarshalerContext{
DecodeUnionsFromXUnionBytes: false,
EncodeUnionsAsXUnionBytes: false,
},
input: &TestPackageResolverResolveRequest{
PackageUrl: "a",
Selectors: []string{"a"},
UpdatePolicy: UpdatePolicy{
FetchIfAbsent: true,
AllowOldVersions: true,
},
Dir: 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: ProtocolRights,
Type: zx.ObjectTypeChannel,
},
},
}.check(t)
}
func TestHandleRightsMarshalUnmarshal(t *testing.T) {
data := make([]byte, 1024)
handleDispositions := make([]zx.HandleDisposition, 1)
input := &HandleRightsSubtypeTestStruct{H: []zx.VMO{5}}
_, nh, err := 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 := &HandleRightsSubtypeTestStruct{}
if _, _, err = 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 = &HandleRightsSubtypeTestStruct{}
if _, _, err = Unmarshal(data, handleInfos, output); err == nil {
t.Fatalf("expected error due to missing required rights")
} else if err != nil && err.(ValidationError).Code() != 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 = &HandleRightsSubtypeTestStruct{}
if _, _, err = Unmarshal(data, handleInfos, output); err == nil {
t.Fatalf("expected error due to incorrect handle type")
} else if err != nil && err.(ValidationError).Code() != ErrIncorrectHandleType {
t.Fatalf("unexpected error code %v", err)
}
}
func TestHandleUncheckedSubtypeMarshalUnmarshal(t *testing.T) {
data := make([]byte, 1024)
handleDispositions := make([]zx.HandleDisposition, 1)
input := &PlainHandleTestStruct{H: zx.Handle(5)}
_, nh, err := 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 := &PlainHandleTestStruct{}
if _, _, err = 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 = &PlainHandleTestStruct{}
if _, _, err = 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)
}
}
func TestTableCompatibilityWithXY(t *testing.T) {
ost := OlderSimpleTable{}
ost.SetX(42)
st := SimpleTable{}
st.SetX(42)
st.SetY(67)
nst := NewerSimpleTable{}
nst.SetX(42)
nst.SetY(67)
cases := []Message{
&TestOlderSimpleTable{Table: ost},
&TestSimpleTable{Table: st},
&TestNewerSimpleTable{Table: nst},
}
for _, expected := range cases {
output := makeDefault(reflect.TypeOf(expected))
if _, _, err := Unmarshal(simpleTableWithXY, nil, output); err != nil {
t.Fatalf("unmarshal: failed: %s", err)
}
if !reflect.DeepEqual(expected, output) {
t.Fatalf("unmarshal: expected: %v, got: %v", expected, output)
}
}
}
func TestTableCompatibilityWithY(t *testing.T) {
ost := OlderSimpleTable{}
st := SimpleTable{}
st.SetY(67)
nst := NewerSimpleTable{}
nst.SetY(67)
cases := []Message{
&TestOlderSimpleTable{Table: ost},
&TestSimpleTable{Table: st},
&TestNewerSimpleTable{Table: nst},
}
for _, expected := range cases {
output := makeDefault(reflect.TypeOf(expected))
_, _, err := Unmarshal(simpleTableWithY, nil, output)
if err != nil {
t.Fatalf("unmarshal: failed: %s", err)
}
if !reflect.DeepEqual(expected, output) {
t.Fatalf("unmarshal: expected: %v, got: %v", expected, output)
}
}
}
func TestFailureNullableTable(t *testing.T) {
type TestNullableTable struct {
_ struct{} `fidl:"s"`
A *SimpleTable `fidl:"0" fidl_offset_v1:"0"`
}
_, err := 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())
}
}
func TestFailuresMarshal(t *testing.T) {
v1 := []int64{1, 2, 3}
cases := []struct {
name string
input Message
errorCode ErrorCode
}{
{"string3-string-too-long", &TestString3{
A: [2]string{
"too long!", // limit is 4, provided is longer(tm)
"g",
},
B: [2]*string{nil, nil},
}, ErrStringTooLong},
{"vector1-C-vector-too-long", &TestVector1{
A: []int8{1, 2, 3, 4},
B: nil,
C: []int32{99, 100, 101}, // limit is 2, provided is 3
D: nil,
}, ErrVectorTooLong},
{"vector1-D-vector-too-long", &TestVector1{
A: []int8{1, 2, 3, 4},
B: nil,
C: []int32{99},
D: &v1, // limit is 2, provided is 3
}, 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 := MarshalWithContext(testCtx, ex.input, respb[:], resphd[:])
validationErr, ok := err.(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())
}
})
}
}
func TestFailuresUnmarshalNoHandles(t *testing.T) {
for _, ex := range allErrorCasesUnmarshal {
t.Run(ex.name, func(t *testing.T) {
_, _, err := Unmarshal(ex.input, nil, ex.message)
validationErr, ok := err.(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(pascallouis): Move to GIDL
func TestMarshalUnmarshalUcharEnumVec(t *testing.T) {
m := TestUcharEnumVec{Values: []TestUcharEnum{TestUcharEnumUcharEnum1, TestUcharEnumUcharEnum2}}
var mbytes [zx.ChannelMaxMessageBytes]byte
nb, _, err := MarshalWithContext(testCtx, &m, mbytes[:], nil)
if err != nil {
t.Fatalf("Marshal vec failed: %v", err)
}
m = TestUcharEnumVec{}
_, _, err = UnmarshalWithContext2(testCtx, mbytes[:nb], nil, &m)
if err != nil {
t.Fatalf("Unmarshal vec failed: %v", err)
}
if len(m.Values) != 2 {
t.Fatalf("Unmarshalled values do not match")
}
if m.Values[0] != TestUcharEnumUcharEnum1 {
t.Errorf("First unmarshalled value doesn't match")
}
if m.Values[1] != TestUcharEnumUcharEnum2 {
t.Errorf("Second unmarshalled value doesn't match")
}
}
func benchmarkMarshal(b *testing.B, ex example) {
var respb [zx.ChannelMaxMessageBytes]byte
var resphd [zx.ChannelMaxMessageHandles]zx.HandleDisposition
b.Run(ex.name, func(b *testing.B) {
for n := 0; n < b.N; n++ {
if _, _, err := Marshal(ex.input, respb[:], resphd[:]); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkMarshal(b *testing.B) {
for _, ex := range general() {
benchmarkMarshal(b, ex)
}
for _, ex := range fuchsia() {
benchmarkMarshal(b, ex)
}
}
func benchmarkUnmarshal(b *testing.B, ex example) {
var respb [zx.ChannelMaxMessageBytes]byte
var resphd [zx.ChannelMaxMessageHandles]zx.HandleDisposition
b.Run(ex.name, func(b *testing.B) {
nb, nh, err := Marshal(ex.input, respb[:], resphd[:])
if err != nil {
b.Fatal(err)
}
var resphi [zx.ChannelMaxMessageHandles]zx.HandleInfo
for i := 0; i < nh; i++ {
resphi[i] = zx.HandleInfo{
Handle: resphd[i].Handle,
Type: zx.ObjectTypeNone,
Rights: zx.RightSameRights,
}
}
output := makeDefault(reflect.TypeOf(ex.input))
b.ResetTimer()
for n := 0; n < b.N; n++ {
if _, _, err := Unmarshal(respb[:nb], resphi[:nh], output); err != nil {
b.Fatal(err)
}
}
})
}
func BenchmarkUnmarshal(b *testing.B) {
for _, ex := range general() {
benchmarkUnmarshal(b, ex)
}
for _, ex := range fuchsia() {
benchmarkUnmarshal(b, ex)
}
}