package io import ( "errors" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // mocks io.Reader and io.Writer, always fails to Write() or Read(). type badRW struct{} func (w *badRW) Write(p []byte) (int, error) { return 0, errors.New("it always fails") } func (w *badRW) Read(p []byte) (int, error) { return w.Write(p) } func TestWriteU64LE(t *testing.T) { var ( val uint64 = 0xbadc0de15a11dead readval uint64 bin = []byte{0xad, 0xde, 0x11, 0x5a, 0xe1, 0x0d, 0xdc, 0xba} ) bw := NewBufBinWriter() bw.WriteU64LE(val) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() assert.Equal(t, wrotebin, bin) br := NewBinReaderFromBuf(bin) readval = br.ReadU64LE() assert.Nil(t, br.Err) assert.Equal(t, val, readval) } func TestWriteU32LE(t *testing.T) { var ( val uint32 = 0xdeadbeef readval uint32 bin = []byte{0xef, 0xbe, 0xad, 0xde} ) bw := NewBufBinWriter() bw.WriteU32LE(val) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() assert.Equal(t, wrotebin, bin) br := NewBinReaderFromBuf(bin) readval = br.ReadU32LE() assert.Nil(t, br.Err) assert.Equal(t, val, readval) } func TestWriteU16LE(t *testing.T) { var ( val uint16 = 0xbabe readval uint16 bin = []byte{0xbe, 0xba} ) bw := NewBufBinWriter() bw.WriteU16LE(val) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() assert.Equal(t, wrotebin, bin) br := NewBinReaderFromBuf(bin) readval = br.ReadU16LE() assert.Nil(t, br.Err) assert.Equal(t, val, readval) } func TestWriteU16BE(t *testing.T) { var ( val uint16 = 0xbabe readval uint16 bin = []byte{0xba, 0xbe} ) bw := NewBufBinWriter() bw.WriteU16BE(val) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() assert.Equal(t, wrotebin, bin) br := NewBinReaderFromBuf(bin) readval = br.ReadU16BE() assert.Nil(t, br.Err) assert.Equal(t, val, readval) } func TestWriteByte(t *testing.T) { var ( val byte = 0xa5 readval byte bin = []byte{0xa5} ) bw := NewBufBinWriter() bw.WriteB(val) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() assert.Equal(t, wrotebin, bin) br := NewBinReaderFromBuf(bin) readval = br.ReadB() assert.Nil(t, br.Err) assert.Equal(t, val, readval) } func TestWriteBool(t *testing.T) { var ( bin = []byte{0x01, 0x00} ) bw := NewBufBinWriter() bw.WriteBool(true) bw.WriteBool(false) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() assert.Equal(t, wrotebin, bin) br := NewBinReaderFromBuf(bin) assert.Equal(t, true, br.ReadBool()) assert.Equal(t, false, br.ReadBool()) assert.Nil(t, br.Err) } func TestReadLEErrors(t *testing.T) { bin := []byte{0xad, 0xde, 0x11, 0x5a, 0xe1, 0x0d, 0xdc, 0xba} br := NewBinReaderFromBuf(bin) // Prime the buffers with something. _ = br.ReadU64LE() assert.Nil(t, br.Err) assert.Equal(t, uint64(0), br.ReadU64LE()) assert.Equal(t, uint32(0), br.ReadU32LE()) assert.Equal(t, uint16(0), br.ReadU16LE()) assert.Equal(t, uint16(0), br.ReadU16BE()) assert.Equal(t, byte(0), br.ReadB()) assert.Equal(t, false, br.ReadBool()) assert.NotNil(t, br.Err) } func TestBufBinWriter_Len(t *testing.T) { val := []byte{0xde} bw := NewBufBinWriter() bw.WriteBytes(val) require.Equal(t, 1, bw.Len()) } func TestBinReader_ReadVarBytes(t *testing.T) { buf := make([]byte, 11) for i := range buf { buf[i] = byte(i) } w := NewBufBinWriter() w.WriteVarBytes(buf) require.NoError(t, w.Err) data := w.Bytes() t.Run("NoArguments", func(t *testing.T) { r := NewBinReaderFromBuf(data) actual := r.ReadVarBytes() require.NoError(t, r.Err) require.Equal(t, buf, actual) }) t.Run("Good", func(t *testing.T) { r := NewBinReaderFromBuf(data) actual := r.ReadVarBytes(11) require.NoError(t, r.Err) require.Equal(t, buf, actual) }) t.Run("Bad", func(t *testing.T) { r := NewBinReaderFromBuf(data) r.ReadVarBytes(10) require.Error(t, r.Err) }) } func TestWriterErrHandling(t *testing.T) { var badio = &badRW{} bw := NewBinWriterFromIO(badio) bw.WriteU32LE(uint32(0)) assert.NotNil(t, bw.Err) // these should work (without panic), preserving the Err bw.WriteU32LE(uint32(0)) bw.WriteU16BE(uint16(0)) bw.WriteVarUint(0) bw.WriteVarBytes([]byte{0x55, 0xaa}) bw.WriteString("neo") assert.NotNil(t, bw.Err) } func TestReaderErrHandling(t *testing.T) { var ( badio = &badRW{} ) br := NewBinReaderFromIO(badio) br.ReadU32LE() assert.NotNil(t, br.Err) // these should work (without panic), preserving the Err br.ReadU32LE() br.ReadU16BE() val := br.ReadVarUint() assert.Equal(t, val, uint64(0)) b := br.ReadVarBytes() assert.Equal(t, b, []byte{}) s := br.ReadString() assert.Equal(t, s, "") assert.NotNil(t, br.Err) } func TestBufBinWriterErr(t *testing.T) { bw := NewBufBinWriter() bw.WriteU32LE(uint32(0)) assert.Nil(t, bw.Err) // inject error bw.Err = errors.New("oopsie") res := bw.Bytes() assert.NotNil(t, bw.Err) assert.Nil(t, res) } func TestBufBinWriterReset(t *testing.T) { bw := NewBufBinWriter() for i := 0; i < 3; i++ { bw.WriteU32LE(uint32(i)) assert.Nil(t, bw.Err) _ = bw.Bytes() assert.NotNil(t, bw.Err) bw.Reset() assert.Nil(t, bw.Err) } } func TestWriteString(t *testing.T) { var ( str = "teststring" ) bw := NewBufBinWriter() bw.WriteString(str) assert.Nil(t, bw.Err) wrotebin := bw.Bytes() // +1 byte for length assert.Equal(t, len(wrotebin), len(str)+1) br := NewBinReaderFromBuf(wrotebin) readstr := br.ReadString() assert.Nil(t, br.Err) assert.Equal(t, str, readstr) } func TestWriteVarUint1(t *testing.T) { var ( val = uint64(1) ) bw := NewBufBinWriter() bw.WriteVarUint(val) assert.Nil(t, bw.Err) buf := bw.Bytes() assert.Equal(t, 1, len(buf)) br := NewBinReaderFromBuf(buf) res := br.ReadVarUint() assert.Nil(t, br.Err) assert.Equal(t, val, res) } func TestWriteVarUint1000(t *testing.T) { var ( val = uint64(1000) ) bw := NewBufBinWriter() bw.WriteVarUint(val) assert.Nil(t, bw.Err) buf := bw.Bytes() assert.Equal(t, 3, len(buf)) assert.Equal(t, byte(0xfd), buf[0]) br := NewBinReaderFromBuf(buf) res := br.ReadVarUint() assert.Nil(t, br.Err) assert.Equal(t, val, res) } func TestWriteVarUint100000(t *testing.T) { var ( val = uint64(100000) ) bw := NewBufBinWriter() bw.WriteVarUint(val) assert.Nil(t, bw.Err) buf := bw.Bytes() assert.Equal(t, 5, len(buf)) assert.Equal(t, byte(0xfe), buf[0]) br := NewBinReaderFromBuf(buf) res := br.ReadVarUint() assert.Nil(t, br.Err) assert.Equal(t, val, res) } func TestWriteVarUint100000000000(t *testing.T) { var ( val = uint64(1000000000000) ) bw := NewBufBinWriter() bw.WriteVarUint(val) assert.Nil(t, bw.Err) buf := bw.Bytes() assert.Equal(t, 9, len(buf)) assert.Equal(t, byte(0xff), buf[0]) br := NewBinReaderFromBuf(buf) res := br.ReadVarUint() assert.Nil(t, br.Err) assert.Equal(t, val, res) } func TestWriteBytes(t *testing.T) { var ( bin = []byte{0xde, 0xad, 0xbe, 0xef} ) bw := NewBufBinWriter() bw.WriteBytes(bin) assert.Nil(t, bw.Err) buf := bw.Bytes() assert.Equal(t, 4, len(buf)) assert.Equal(t, byte(0xde), buf[0]) bw = NewBufBinWriter() bw.Err = errors.New("smth bad") bw.WriteBytes(bin) assert.Equal(t, 0, bw.Len()) } type testSerializable uint16 // EncodeBinary implements the io.Serializable interface. func (t testSerializable) EncodeBinary(w *BinWriter) { w.WriteU16LE(uint16(t)) } // DecodeBinary implements the io.Serializable interface. func (t *testSerializable) DecodeBinary(r *BinReader) { *t = testSerializable(r.ReadU16LE()) } type testPtrSerializable uint16 // EncodeBinary implements the io.Serializable interface. func (t *testPtrSerializable) EncodeBinary(w *BinWriter) { w.WriteU16LE(uint16(*t)) } // DecodeBinary implements the io.Serializable interface. func (t *testPtrSerializable) DecodeBinary(r *BinReader) { *t = testPtrSerializable(r.ReadU16LE()) } func TestBinWriter_WriteArray(t *testing.T) { var arr [3]testSerializable for i := range arr { arr[i] = testSerializable(i) } expected := []byte{3, 0, 0, 1, 0, 2, 0} w := NewBufBinWriter() w.WriteArray(arr) require.NoError(t, w.Err) require.Equal(t, expected, w.Bytes()) w.Reset() w.WriteArray(arr[:]) require.NoError(t, w.Err) require.Equal(t, expected, w.Bytes()) arrS := make([]Serializable, len(arr)) for i := range arrS { arrS[i] = &arr[i] } w.Reset() w.WriteArray(arr) require.NoError(t, w.Err) require.Equal(t, expected, w.Bytes()) w.Reset() require.Panics(t, func() { w.WriteArray(1) }) w.Reset() w.Err = errors.New("error") w.WriteArray(arr[:]) require.Error(t, w.Err) require.Equal(t, w.Bytes(), []byte(nil)) w.Reset() require.Panics(t, func() { w.WriteArray([]int{1}) }) w.Reset() w.Err = errors.New("error") require.Panics(t, func() { w.WriteArray(make(chan testSerializable)) }) // Ptr receiver test var arrPtr [3]testPtrSerializable for i := range arrPtr { arrPtr[i] = testPtrSerializable(i) } w.Reset() w.WriteArray(arr[:]) require.NoError(t, w.Err) require.Equal(t, expected, w.Bytes()) } func TestBinReader_ReadArray(t *testing.T) { data := []byte{3, 0, 0, 1, 0, 2, 0} elems := []testSerializable{0, 1, 2} r := NewBinReaderFromBuf(data) arrPtr := []*testSerializable{} r.ReadArray(&arrPtr) require.Equal(t, []*testSerializable{&elems[0], &elems[1], &elems[2]}, arrPtr) r = NewBinReaderFromBuf(data) arrVal := []testSerializable{} r.ReadArray(&arrVal) require.NoError(t, r.Err) require.Equal(t, elems, arrVal) r = NewBinReaderFromBuf(data) arrVal = []testSerializable{} r.ReadArray(&arrVal, 3) require.NoError(t, r.Err) require.Equal(t, elems, arrVal) r = NewBinReaderFromBuf(data) arrVal = []testSerializable{} r.ReadArray(&arrVal, 2) require.Error(t, r.Err) r = NewBinReaderFromBuf([]byte{0}) r.ReadArray(&arrVal) require.NoError(t, r.Err) require.Equal(t, []testSerializable{}, arrVal) r = NewBinReaderFromBuf([]byte{0}) r.Err = errors.New("error") arrVal = ([]testSerializable)(nil) r.ReadArray(&arrVal) require.Error(t, r.Err) require.Equal(t, ([]testSerializable)(nil), arrVal) r = NewBinReaderFromBuf([]byte{0}) r.Err = errors.New("error") arrPtr = ([]*testSerializable)(nil) r.ReadArray(&arrVal) require.Error(t, r.Err) require.Equal(t, ([]*testSerializable)(nil), arrPtr) r = NewBinReaderFromBuf([]byte{0}) arrVal = []testSerializable{1, 2} r.ReadArray(&arrVal) require.NoError(t, r.Err) require.Equal(t, []testSerializable{}, arrVal) r = NewBinReaderFromBuf([]byte{1}) require.Panics(t, func() { r.ReadArray(&[]int{1}) }) r = NewBinReaderFromBuf([]byte{0}) r.Err = errors.New("error") require.Panics(t, func() { r.ReadArray(1) }) } func TestBinReader_ReadBytes(t *testing.T) { data := []byte{0, 1, 2, 3, 4, 5, 6, 7} r := NewBinReaderFromBuf(data) buf := make([]byte, 4) r.ReadBytes(buf) require.NoError(t, r.Err) require.Equal(t, data[:4], buf) r.ReadBytes([]byte{}) require.NoError(t, r.Err) buf = make([]byte, 3) r.ReadBytes(buf) require.NoError(t, r.Err) require.Equal(t, data[4:7], buf) buf = make([]byte, 2) r.ReadBytes(buf) require.Error(t, r.Err) r.ReadBytes([]byte{}) require.Error(t, r.Err) }