blob: e184e6b83b3a46423e9ea73ebcbc4319e34b87c2 [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"
"syscall/zx/fidl/conformance"
)
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 conformance.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 TestEnvelopeBadPadding(t *testing.T) {
input := []byte{
0x21, 0xEB, 0x7E, 0x76, 0x55, 0x55, 0x55, 0x55, // ordinal + invalid 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("Unmarshal() returned nil error")
}
errCode := err.(ValidationError).Code()
if errCode != ErrNonZeroPadding {
t.Fatalf("Unmarshal() returned %d", errCode)
}
}
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{
0x01, 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}
u1 = Union1{}
xu1 = XUnion1{}
sxu1 = StrictXUnion1{}
)
u1.SetB(TestSimple{X: 555})
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},
// unions
{"union1", &TestUnion1{
A: u1,
B: nil,
}, 24}, // A=8(tag+padding)+8(data), B=8(ptr)
{"union1-bis", &TestUnion1{
A: u1,
B: &u1,
}, 40}, // A=8(tag+padding)+8(data), B=8(ptr), out-of-line A=16
{"union2", &TestUnion2{
A: []Union1{u1, u1, u1},
B: []*Union1{&u1, nil, nil},
}, 120}, // A=8(size)+8(ptr), B=8(size)+8(ptr), OOL A=3*16, OOL B=3*8(ptr), OOL B[0]=16
// 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: Test1Interface(ChannelProxy{Channel: h0}),
B: Test1Interface(ChannelProxy{Channel: zx.Channel(zx.HandleInvalid)}),
C: Test1InterfaceRequest(InterfaceRequest{Channel: h1}),
D: Test1InterfaceRequest(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 (
bytes16 = randBytes(16)
bytes64 = randBytes(64)
bytes256 = randBytes(256)
bytes1024 = randBytes(1024)
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},
}
}
// 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 resph [zx.ChannelMaxMessageHandles]zx.Handle
nb, nh, err := Marshal(input, respb[:], resph[:])
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])
}
output := makeDefault(reflect.TypeOf(input))
nbActual, nhActual, err := Unmarshal(respb[:nb], resph[: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},
{"empty-array-TestUnion1", &TestUnion1{}, []byte{}, ErrPayloadTooSmall},
{"nil-array-TestUnion1", &TestUnion1{}, nil, ErrPayloadTooSmall},
{"small-array-TestUnion1", &TestUnion1{}, []byte{0x00, 0x0}, ErrPayloadTooSmall},
{"non-zero-or-one-bool", &TestSimpleBool{}, []byte{2}, ErrInvalidBoolValue},
{"wrong-tag", &TestUnion1{}, []byte{0, 0, 0, 4}, ErrInvalidUnionTag},
{"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",
input: &TestAddEthernetDeviceRequest{
TopologicalPath: "@/dev/sys/pci/00:03.0/e1000/ethernet",
Config: InterfaceConfig{
Name: "ethp0003",
IpAddressConfig: ipAddressConfig,
},
Device: EthernetDeviceInterface{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
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // subnet (dhcp variant)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // padding
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
},
handles: []zx.Handle{
zx.Handle(0xdeadbeef),
},
}.check(t)
}
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{} `fidl2:"s,123,456"`
A *SimpleTable
}
_, 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},
{"union1-A-uninitialized", &TestUnion1{
A: Union1{}, // Intentionally don't set any members of the union.
B: nil,
}, ErrInvalidUnionTag},
{"union1-A-tag-out-of-bounds", &TestUnion1{
A: Union1{I_union1Tag: Union1D + 1},
B: nil,
}, ErrInvalidUnionTag},
}
for _, ex := range cases {
t.Run(ex.name, func(t *testing.T) {
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
_, _, err := Marshal(ex.input, respb[:], resph[:])
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())
}
})
}
}
func TestUnmarshalXunionIntoUnion(t *testing.T) {
const expectedUnionTag = 3 // float32 field
const expectedFieldValue = 1.2
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
input := XUnion1Struct{Xu: XUnion1WithD(expectedFieldValue)}
_, _, err := Marshal(&input, respb[:], resph[:])
if err != nil {
t.Fatalf("error unmarshaling: %v\n", err)
}
// Set the ordinal value to the value in XUnion1AsUnionStruct, since it is
// a different type.
// This value comes from the generated JSON for test.test.fidl.
respb[0] = 0x24
respb[1] = 0x94
respb[2] = 0x6a
respb[3] = 0x3e
ctx := MarshalerContext{DecodeUnionsFromXunionBytes: true}
var union XUnion1AsUnionStruct
_, _, err = UnmarshalWithContext(ctx, respb[:], nil, &union)
if err != nil {
t.Fatalf("error marshaling: %v", err)
}
if union.Xuau.I_xUnion1AsUnionTag != expectedUnionTag {
t.Fatalf("want %v, got %v", expectedUnionTag, union.Xuau.I_xUnion1AsUnionTag)
}
if union.Xuau.D != expectedFieldValue {
t.Fatalf("want %v, got %v", expectedFieldValue, union.Xuau.D)
}
}
func benchmarkMarshal(b *testing.B, ex example) {
b.Run(ex.name, func(b *testing.B) {
b.StopTimer()
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
b.StartTimer()
for n := 0; n < b.N; n++ {
if _, _, err := Marshal(ex.input, respb[:], resph[:]); err != nil {
b.Fail()
}
}
})
}
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) {
b.Run(ex.name, func(b *testing.B) {
b.StopTimer()
var respb [zx.ChannelMaxMessageBytes]byte
var resph [zx.ChannelMaxMessageHandles]zx.Handle
nb, nh, err := Marshal(ex.input, respb[:], resph[:])
if err != nil {
b.Fail()
}
output := makeDefault(reflect.TypeOf(ex.input))
b.StartTimer()
for n := 0; n < b.N; n++ {
if _, _, err := Unmarshal(respb[:nb], resph[:nh], output); err != nil {
b.Fail()
}
}
})
}
func BenchmarkUnmarshal(b *testing.B) {
for _, ex := range general() {
benchmarkUnmarshal(b, ex)
}
for _, ex := range fuchsia() {
benchmarkUnmarshal(b, ex)
}
}