// Copyright 2018 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package binary import ( "bytes" "encoding/binary" "errors" "fmt" "io" "reflect" "testing" ) func newInt32(i int32) *int32 { return &i } func TestSize(t *testing.T) { if got, want := Size(uint32(10)), uintptr(4); got != want { t.Errorf("Got = %d, want = %d", got, want) } } func TestPanic(t *testing.T) { tests := []struct { name string f func([]byte, binary.ByteOrder, interface{}) data interface{} want string }{ {"Unmarshal int", Unmarshal, 5, "invalid type: int"}, {"Unmarshal []int", Unmarshal, []int{5}, "invalid type: int"}, {"Marshal int", func(_ []byte, bo binary.ByteOrder, d interface{}) { Marshal(nil, bo, d) }, 5, "invalid type: int"}, {"Marshal int[]", func(_ []byte, bo binary.ByteOrder, d interface{}) { Marshal(nil, bo, d) }, []int{5}, "invalid type: int"}, {"Unmarshal short buffer", Unmarshal, newInt32(5), "runtime error: index out of range"}, {"Unmarshal long buffer", func(_ []byte, bo binary.ByteOrder, d interface{}) { Unmarshal(make([]byte, 50), bo, d) }, newInt32(5), "buffer too long by 46 bytes"}, {"marshal int", func(_ []byte, bo binary.ByteOrder, d interface{}) { marshal(nil, bo, reflect.ValueOf(d)) }, 5, "invalid type: int"}, {"Size int", func(_ []byte, _ binary.ByteOrder, d interface{}) { Size(d) }, 5, "invalid type: int"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { defer func() { r := recover() if got := fmt.Sprint(r); got != test.want { t.Errorf("Got recover() = %q, want = %q", got, test.want) } }() test.f(nil, LittleEndian, test.data) }) } } type inner struct { Field int32 } type outer struct { Int8 int8 Int16 int16 Int32 int32 Int64 int64 Uint8 uint8 Uint16 uint16 Uint32 uint32 Uint64 uint64 Slice []int32 Array [5]int32 Struct inner } func TestMarshalUnmarshal(t *testing.T) { want := outer{ 1, 2, 3, 4, 5, 6, 7, 8, []int32{9, 10, 11}, [5]int32{12, 13, 14, 15, 16}, inner{17}, } buf := Marshal(nil, LittleEndian, want) got := outer{Slice: []int32{0, 0, 0}} Unmarshal(buf, LittleEndian, &got) if !reflect.DeepEqual(&got, &want) { t.Errorf("Got = %#v, want = %#v", got, want) } } type outerBenchmark struct { Int8 int8 Int16 int16 Int32 int32 Int64 int64 Uint8 uint8 Uint16 uint16 Uint32 uint32 Uint64 uint64 Array [5]int32 Struct inner } func BenchmarkMarshalUnmarshal(b *testing.B) { b.ReportAllocs() in := outerBenchmark{ 1, 2, 3, 4, 5, 6, 7, 8, [5]int32{9, 10, 11, 12, 13}, inner{14}, } buf := make([]byte, Size(&in)) out := outerBenchmark{} for i := 0; i < b.N; i++ { buf := Marshal(buf[:0], LittleEndian, &in) Unmarshal(buf, LittleEndian, &out) } } func BenchmarkReadWrite(b *testing.B) { b.ReportAllocs() in := outerBenchmark{ 1, 2, 3, 4, 5, 6, 7, 8, [5]int32{9, 10, 11, 12, 13}, inner{14}, } buf := bytes.NewBuffer(make([]byte, binary.Size(&in))) out := outerBenchmark{} for i := 0; i < b.N; i++ { buf.Reset() if err := binary.Write(buf, LittleEndian, &in); err != nil { b.Error("Write:", err) } if err := binary.Read(buf, LittleEndian, &out); err != nil { b.Error("Read:", err) } } } type outerPadding struct { _ int8 _ int16 _ int32 _ int64 _ uint8 _ uint16 _ uint32 _ uint64 _ []int32 _ [5]int32 _ inner } func TestMarshalUnmarshalPadding(t *testing.T) { var want outerPadding buf := Marshal(nil, LittleEndian, want) var got outerPadding Unmarshal(buf, LittleEndian, &got) if !reflect.DeepEqual(&got, &want) { t.Errorf("Got = %#v, want = %#v", got, want) } } // Numbers with bits in every byte that distinguishable in big and little endian. const ( want16 = 64<<8 | 128 want32 = 16<<24 | 32<<16 | want16 want64 = 1<<56 | 2<<48 | 4<<40 | 8<<32 | want32 ) func TestReadWriteUint16(t *testing.T) { const want = uint16(want16) var buf bytes.Buffer if err := WriteUint16(&buf, LittleEndian, want); err != nil { t.Error("WriteUint16:", err) } got, err := ReadUint16(&buf, LittleEndian) if err != nil { t.Error("ReadUint16:", err) } if got != want { t.Errorf("got = %d, want = %d", got, want) } } func TestReadWriteUint32(t *testing.T) { const want = uint32(want32) var buf bytes.Buffer if err := WriteUint32(&buf, LittleEndian, want); err != nil { t.Error("WriteUint32:", err) } got, err := ReadUint32(&buf, LittleEndian) if err != nil { t.Error("ReadUint32:", err) } if got != want { t.Errorf("got = %d, want = %d", got, want) } } func TestReadWriteUint64(t *testing.T) { const want = uint64(want64) var buf bytes.Buffer if err := WriteUint64(&buf, LittleEndian, want); err != nil { t.Error("WriteUint64:", err) } got, err := ReadUint64(&buf, LittleEndian) if err != nil { t.Error("ReadUint64:", err) } if got != want { t.Errorf("got = %d, want = %d", got, want) } } type readWriter struct { err error } func (rw *readWriter) Write([]byte) (int, error) { return 0, rw.err } func (rw *readWriter) Read([]byte) (int, error) { return 0, rw.err } func TestReadWriteError(t *testing.T) { tests := []struct { name string f func(rw io.ReadWriter) error }{ {"WriteUint16", func(rw io.ReadWriter) error { return WriteUint16(rw, LittleEndian, 0) }}, {"ReadUint16", func(rw io.ReadWriter) error { _, err := ReadUint16(rw, LittleEndian); return err }}, {"WriteUint32", func(rw io.ReadWriter) error { return WriteUint32(rw, LittleEndian, 0) }}, {"ReadUint32", func(rw io.ReadWriter) error { _, err := ReadUint32(rw, LittleEndian); return err }}, {"WriteUint64", func(rw io.ReadWriter) error { return WriteUint64(rw, LittleEndian, 0) }}, {"ReadUint64", func(rw io.ReadWriter) error { _, err := ReadUint64(rw, LittleEndian); return err }}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { want := errors.New("want") if got := test.f(&readWriter{want}); got != want { t.Errorf("got = %v, want = %v", got, want) } }) } }