| // Copyright 2018 The Go 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 ( |
| "fmt" |
| "math/rand" |
| "reflect" |
| "strings" |
| "syscall/zx" |
| "testing" |
| |
| . "syscall/zx/fidl" |
| . "syscall/zx/fidl/bindingstest" |
| ) |
| |
| type marshalFunc struct { |
| name string |
| fn func(Message, []byte, []zx.Handle) (int, int, error) |
| } |
| |
| var marshalFuncs = []marshalFunc{ |
| {"Marshal", func(input Message, respb []byte, resph []zx.Handle) (int, int, error) { |
| return Marshal(input, respb, resph) |
| }}, |
| {"MarshalNew", MarshalNew}, |
| } |
| |
| type unmarshalFunc struct { |
| name string |
| fn func([]byte, []zx.Handle, Message) error |
| } |
| |
| var unmarshalFuncs = []unmarshalFunc{ |
| {"Unmarshal", func(respb []byte, resph []zx.Handle, output Message) error { |
| return Unmarshal(respb, resph, output) |
| }}, |
| {"UnmarshalNew", UnmarshalNew}, |
| } |
| |
| type example struct { |
| name string |
| input Message |
| expectSize int |
| output Message |
| } |
| |
| // 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{} |
| ) |
| u1.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, &TestSimple{}}, |
| {"simplebool", &TestSimpleBool{X: true}, 8, &TestSimpleBool{}}, |
| |
| // alignement |
| {"align1", &TestAlignment1{X: -36, Y: -10, Z: 51}, 8, &TestAlignment1{}}, |
| {"align2", &TestAlignment2{ |
| A: 1212141, |
| B: 908935, |
| C: -1, |
| D: 125, |
| E: -22, |
| F: 111, |
| G: 1515, |
| H: 65535, |
| I: 1515, |
| }, 24, &TestAlignment2{}}, |
| |
| // floats |
| {"float1", &TestFloat1{A: -36.0}, 8, &TestFloat1{}}, |
| {"float2", &TestFloat2{A: -1254918271.0}, 8, &TestFloat2{}}, |
| {"float3", &TestFloat3{A: 1241.1, B: 0.2141, C: 20, D: 0.0}, 32, &TestFloat3{}}, |
| |
| // arrays |
| {"array1", &TestArray1{A: [5]int8{1, 77, 2, 4, 89}}, 8, &TestArray1{}}, |
| {"array2", &TestArray2{A: -1.0, B: [1]float32{0.2}}, 16, &TestArray2{}}, |
| {"array3", &TestArray3{ |
| A: -999, |
| B: [3]uint16{11, 12, 13}, |
| C: 1021, |
| }, 24, &TestArray3{}}, |
| {"array4", &TestArray4{ |
| A: [9]bool{true, false, false, true, false, true, true, true, true}, |
| }, 16, &TestArray4{}}, |
| |
| // strings |
| {"string1", &TestString1{A: "str", B: nil}, 40, &TestString1{}}, |
| {"string1-longer256", &TestString1{A: s256, B: &s256}, 1056, &TestString1{}}, |
| {"string1-longer8192", &TestString1{A: s8192, B: &s8192}, 16416, &TestString1{}}, |
| {"string2", &TestString2{A: [2]string{"hello", "g"}}, 48, &TestString2{}}, |
| {"string3", &TestString3{ |
| A: [2]string{"boop", "g"}, |
| B: [2]*string{&s, nil}, |
| }, 88, &TestString3{}}, |
| |
| // vectors |
| {"vector1", &TestVector1{ |
| A: []int8{1, 2, 3, 4}, |
| B: nil, |
| C: []int32{99}, |
| D: &v1, |
| }, 88, &TestVector1{}}, |
| {"vector2", &TestVector2{ |
| A: [2][]int8{{9, -1}, {}}, |
| B: [][]int8{{-111, 41}, {-1, -1, -1, -1}}, |
| C: []*[]string{nil, &v2}, |
| }, 200, &TestVector2{}}, |
| |
| // structs |
| {"struct1", &TestStruct1{ |
| A: TestSimple{ |
| X: -9999, |
| }, |
| B: &TestSimple{ |
| X: 1254125, |
| }, |
| }, 24, &TestStruct1{}}, |
| {"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, &TestStruct2{}}, |
| |
| // unions |
| {"union1", &TestUnion1{ |
| A: u1, |
| B: nil, |
| }, 24, &TestUnion1{}}, |
| {"union1-bis", &TestUnion1{ |
| A: u1, |
| B: &u1, |
| }, 40, &TestUnion1{}}, |
| {"union2", &TestUnion2{ |
| A: []Union1{u1, u1, u1}, |
| B: []*Union1{&u1, nil, nil}, |
| }, 120, &TestUnion2{}}, |
| |
| // handles |
| {"handle1", &TestHandle1{ |
| A: zx.Handle(22), |
| B: zx.HandleInvalid, |
| C: vmo, |
| D: zx.VMO(zx.HandleInvalid), |
| }, 16, &TestHandle1{}}, |
| {"handle2", &TestHandle2{ |
| A: []zx.Handle{zx.Handle(vmo)}, |
| B: []zx.VMO{zx.VMO(zx.HandleInvalid)}, |
| }, 48, &TestHandle2{}}, |
| |
| // interfaces |
| {"interface1", &TestInterface1{ |
| A: Test1Interface(Proxy{Channel: h0}), |
| B: Test1Interface(Proxy{Channel: zx.Channel(zx.HandleInvalid)}), |
| C: Test1InterfaceRequest(InterfaceRequest{Channel: h1}), |
| D: Test1InterfaceRequest(InterfaceRequest{ |
| Channel: zx.Channel(zx.HandleInvalid), |
| }), |
| }, 16, &TestInterface1{}}, |
| |
| // tables |
| {"table1", &TestSimpleTable{ |
| Table: st1, |
| }, 112, &TestSimpleTable{}}, |
| {"opt-table1", &TestOptSimpleTable{ |
| OptTable: &st1, |
| }, 120, &TestOptSimpleTable{}}, |
| } |
| } |
| |
| // 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, &TestFuchsiaIoReadAtResponse{}}, |
| {"fuchsia.io-readAt-response16", &TestFuchsiaIoReadAtResponse{ |
| S: 5, |
| Data: bytes16, |
| }, 24 + 16, &TestFuchsiaIoReadAtResponse{}}, |
| {"fuchsia.io-readAt-response64", &TestFuchsiaIoReadAtResponse{ |
| S: 5, |
| Data: bytes64, |
| }, 24 + 64, &TestFuchsiaIoReadAtResponse{}}, |
| {"fuchsia.io-readAt-response256", &TestFuchsiaIoReadAtResponse{ |
| S: 5, |
| Data: bytes256, |
| }, 24 + 256, &TestFuchsiaIoReadAtResponse{}}, |
| {"fuchsia.io-readAt-response1024", &TestFuchsiaIoReadAtResponse{ |
| S: 5, |
| Data: bytes1024, |
| }, 24 + 1024, &TestFuchsiaIoReadAtResponse{}}, |
| {"fuchsia.io-readAt-response4096", &TestFuchsiaIoReadAtResponse{ |
| S: 5, |
| Data: bytes4096, |
| }, 24 + 4096, &TestFuchsiaIoReadAtResponse{}}, |
| {"fuchsia.io-readAt-response8192", &TestFuchsiaIoReadAtResponse{ |
| S: 5, |
| Data: bytes8192, |
| }, 24 + 8192, &TestFuchsiaIoReadAtResponse{}}, |
| |
| // fuchsia.io, WriteAt request |
| {"fuchsia.io-writeAt-request0", &TestFuchsiaIoWriteAtRequest{ |
| Data: []byte{}, |
| Offset: 5, |
| }, 24 + 0, &TestFuchsiaIoWriteAtRequest{}}, |
| {"fuchsia.io-writeAt-request16", &TestFuchsiaIoWriteAtRequest{ |
| Data: bytes16, |
| Offset: 5, |
| }, 24 + 16, &TestFuchsiaIoWriteAtRequest{}}, |
| {"fuchsia.io-writeAt-request64", &TestFuchsiaIoWriteAtRequest{ |
| Data: bytes64, |
| Offset: 5, |
| }, 24 + 64, &TestFuchsiaIoWriteAtRequest{}}, |
| {"fuchsia.io-writeAt-request256", &TestFuchsiaIoWriteAtRequest{ |
| Data: bytes256, |
| Offset: 5, |
| }, 24 + 256, &TestFuchsiaIoWriteAtRequest{}}, |
| {"fuchsia.io-writeAt-request1024", &TestFuchsiaIoWriteAtRequest{ |
| Data: bytes1024, |
| Offset: 5, |
| }, 24 + 1024, &TestFuchsiaIoWriteAtRequest{}}, |
| {"fuchsia.io-writeAt-request4096", &TestFuchsiaIoWriteAtRequest{ |
| Data: bytes4096, |
| Offset: 5, |
| }, 24 + 4096, &TestFuchsiaIoWriteAtRequest{}}, |
| {"fuchsia.io-writeAt-request8192", &TestFuchsiaIoWriteAtRequest{ |
| Data: bytes8192, |
| Offset: 5, |
| }, 24 + 8192, &TestFuchsiaIoWriteAtRequest{}}, |
| } |
| } |
| |
| type checker struct { |
| marshalFunc |
| unmarshalFunc |
| } |
| |
| func (c checker) check(t *testing.T, input Message, expectSize int, output Message) { |
| t.Helper() |
| var respb [zx.ChannelMaxMessageBytes]byte |
| var resph [zx.ChannelMaxMessageHandles]zx.Handle |
| nb, nh, err := c.marshalFunc.fn(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]) |
| } |
| if err := c.unmarshalFunc.fn(respb[:nb], resph[:nh], output); err != nil { |
| t.Fatalf("unmarshal: failed: %s", err) |
| } |
| if !reflect.DeepEqual(input, output) { |
| t.Fatalf("unmarshal: expected: %v, got: %v", input, output) |
| } |
| } |
| |
| func TestCorrectness(t *testing.T) { |
| for _, m := range marshalFuncs { |
| for _, u := range unmarshalFuncs { |
| c := checker{m, u} |
| for _, ex := range general() { |
| t.Run(fmt.Sprintf("%s-%s-%s", m.name, u.name, ex.name), func(t *testing.T) { |
| c.check(t, ex.input, ex.expectSize, ex.output) |
| }) |
| } |
| for _, ex := range fuchsia() { |
| t.Run(fmt.Sprintf("%s-%s-%s", m.name, u.name, ex.name), func(t *testing.T) { |
| c.check(t, ex.input, ex.expectSize, ex.output) |
| }) |
| } |
| } |
| } |
| } |
| |
| func makeDefault(msg Message) Message { |
| typ := reflect.TypeOf(msg) |
| if typ.Kind() != reflect.Ptr && typ.Elem().Kind() != reflect.Struct { |
| panic("expecting *struct") |
| } |
| return reflect.New(typ.Elem()).Interface().(Message) |
| } |
| |
| // successCase represents a golden test for a success case, where encoding |
| // and decoding should succceed. |
| type successCase struct { |
| name string |
| input Message |
| bytes []byte |
| } |
| |
| func (ex successCase) check(t *testing.T) { |
| for _, mFn := range marshalFuncs { |
| for _, uFn := range unmarshalFuncs { |
| t.Run(ex.name+"_"+mFn.name+"_"+uFn.name, func(t *testing.T) { |
| var respb [zx.ChannelMaxMessageBytes]byte |
| var resph [zx.ChannelMaxMessageHandles]zx.Handle |
| nb, nh, err := mFn.fn(ex.input, respb[:], resph[:]) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if nh != 0 { |
| t.Fatalf("no handles expected, got %d", nh) |
| } |
| if !reflect.DeepEqual(ex.bytes, respb[:nb]) { |
| t.Fatalf("expected %v, got %v", ex.bytes, respb[:nb]) |
| } |
| output := makeDefault(ex.input) |
| if err := uFn.fn(respb[:nb], resph[:nh], output); err != nil { |
| t.Fatalf("unmarshal: failed: %s", err) |
| } |
| if !reflect.DeepEqual(ex.input, output) { |
| t.Fatalf("unmarshal: expected: %v, got: %v", ex.input, output) |
| } |
| }) |
| } |
| } |
| } |
| |
| 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 |
| } |
| |
| func TestAllSuccessCases(t *testing.T) { |
| st1 := SimpleTable{} |
| st1.SetX(42) |
| st1.SetY(67) |
| successCase{ |
| name: "simpletable-x-and-y", |
| input: &TestSimpleTable{ |
| Table: st1, |
| }, |
| bytes: simpleTableWithXY, |
| }.check(t) |
| |
| st2 := SimpleTable{} |
| st2.SetY(67) |
| successCase{ |
| name: "simpletable-just-y", |
| input: &TestSimpleTable{ |
| Table: st2, |
| }, |
| bytes: simpleTableWithY, |
| }.check(t) |
| |
| st3 := SimpleTable{} |
| st3.SetX(42) |
| st3.SetY(67) |
| successCase{ |
| name: "opt-simpletable-x-and-y", |
| input: &TestOptSimpleTable{ |
| OptTable: &st3, |
| }, |
| bytes: append([]byte{ |
| 255, 255, 255, 255, 255, 255, 255, 255, // alloc present, |
| }, |
| simpleTableWithXY...), |
| }.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 { |
| for _, u := range unmarshalFuncs { |
| output := makeDefault(expected) |
| u.fn(simpleTableWithXY, nil, output) |
| 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 { |
| for _, u := range unmarshalFuncs { |
| output := makeDefault(expected) |
| u.fn(simpleTableWithY, nil, output) |
| if !reflect.DeepEqual(expected, output) { |
| t.Fatalf("unmarshal: expected: %v, got: %v", expected, output) |
| } |
| } |
| } |
| } |
| |
| 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{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 := MarshalNew(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) { |
| cases := []struct { |
| name string |
| input []byte |
| message Message |
| errorCode ErrorCode |
| }{ |
| {"zero-bytes", []byte{}, &TestSimple{}, ErrPayloadTooSmall}, |
| {"non-zero-or-one-bool", []byte{2}, &TestSimpleBool{}, ErrInvalidBoolValue}, |
| {"wrong-tag", []byte{0, 0, 0, 4}, &TestUnion1{}, ErrInvalidUnionTag}, |
| } |
| for _, ex := range cases { |
| t.Run(ex.name, func(t *testing.T) { |
| err := UnmarshalNew(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) { |
| for _, m := range marshalFuncs { |
| b.Run(fmt.Sprintf("%s-%s", m.name, 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 := m.fn(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) { |
| for _, u := range unmarshalFuncs { |
| b.Run(fmt.Sprintf("%s-%s", u.name, 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() |
| } |
| b.StartTimer() |
| for n := 0; n < b.N; n++ { |
| if err := u.fn(respb[:nb], resph[:nh], ex.output); err != nil { |
| b.Fail() |
| } |
| } |
| }) |
| } |
| } |
| |
| func BenchmarkUnmarshal(b *testing.B) { |
| for _, ex := range general() { |
| benchmarkUnmarshal(b, ex) |
| } |
| for _, ex := range fuchsia() { |
| benchmarkUnmarshal(b, ex) |
| } |
| } |