| // 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 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 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) |
| } |
| } |