261 lines
7.4 KiB
Go
261 lines
7.4 KiB
Go
package byteio_test
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"math"
|
|
"testing"
|
|
"unicode/utf8"
|
|
|
|
"src.lwithers.me.uk/go/byteio"
|
|
)
|
|
|
|
// TestWriteUint ensures correct operation of the WriteUint variants by
|
|
// verifying written data integrity against encoding/binary.
|
|
func TestWriteUint(t *testing.T) {
|
|
checkErr := func(err error) {
|
|
if err != nil {
|
|
t.Fatalf("unexpected I/O error: %v", err)
|
|
}
|
|
}
|
|
|
|
// prepare a buffer of test data
|
|
vals := []uint64{0, 1, 0x8000, 0xFFFF, 0x81828384858687}
|
|
buf := bytes.NewBuffer(nil)
|
|
for _, val := range vals {
|
|
checkErr(byteio.WriteUint16BE(buf, uint16(val)))
|
|
checkErr(byteio.WriteUint32BE(buf, uint32(val)))
|
|
checkErr(byteio.WriteUint64BE(buf, val))
|
|
checkErr(byteio.WriteUint16LE(buf, uint16(val)))
|
|
checkErr(byteio.WriteUint32LE(buf, uint32(val)))
|
|
checkErr(byteio.WriteUint64LE(buf, val))
|
|
}
|
|
|
|
// verify its integrity
|
|
for _, exp64 := range vals {
|
|
var (
|
|
act16, exp16 uint16 = 0, uint16(exp64)
|
|
act32, exp32 uint32 = 0, uint32(exp64)
|
|
act64 uint64
|
|
)
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act16))
|
|
if act16 != exp16 {
|
|
t.Errorf("WriteUint16BE: act %X ≠ exp %X", act16, exp16)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act32))
|
|
if act32 != exp32 {
|
|
t.Errorf("WriteUint32BE: act %X ≠ exp %X", act32, exp32)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act64))
|
|
if act64 != exp64 {
|
|
t.Errorf("WriteUint64BE: act %X ≠ exp %X", act64, exp64)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act16))
|
|
if act16 != exp16 {
|
|
t.Errorf("WriteUint16LE: act %X ≠ exp %X", act16, exp16)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act32))
|
|
if act32 != exp32 {
|
|
t.Errorf("WriteUint32LE: act %X ≠ exp %X", act32, exp32)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act64))
|
|
if act64 != exp64 {
|
|
t.Errorf("WriteUint64LE: act %X ≠ exp %X", act64, exp64)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestWriteInt ensures correct operation of the WriteInt variants by verifying
|
|
// written data integrity against encoding/binary.
|
|
func TestWriteInt(t *testing.T) {
|
|
checkErr := func(err error) {
|
|
if err != nil {
|
|
t.Fatalf("unexpected I/O error: %v", err)
|
|
}
|
|
}
|
|
|
|
// prepare a buffer of test data
|
|
vals := []int64{-1, 0, 1, -0x8000, 0xFFFF, 0x01020304050607}
|
|
buf := bytes.NewBuffer(nil)
|
|
for _, val := range vals {
|
|
checkErr(byteio.WriteInt16BE(buf, int16(val)))
|
|
checkErr(byteio.WriteInt32BE(buf, int32(val)))
|
|
checkErr(byteio.WriteInt64BE(buf, val))
|
|
checkErr(byteio.WriteInt16LE(buf, int16(val)))
|
|
checkErr(byteio.WriteInt32LE(buf, int32(val)))
|
|
checkErr(byteio.WriteInt64LE(buf, val))
|
|
}
|
|
|
|
// verify its integrity
|
|
for _, exp64 := range vals {
|
|
var (
|
|
act16, exp16 int16 = 0, int16(exp64)
|
|
act32, exp32 int32 = 0, int32(exp64)
|
|
act64 int64
|
|
)
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act16))
|
|
if act16 != exp16 {
|
|
t.Errorf("WriteInt16BE: act %X ≠ exp %X", act16, exp16)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act32))
|
|
if act32 != exp32 {
|
|
t.Errorf("WriteInt32BE: act %X ≠ exp %X", act32, exp32)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act64))
|
|
if act64 != exp64 {
|
|
t.Errorf("WriteInt64BE: act %X ≠ exp %X", act64, exp64)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act16))
|
|
if act16 != exp16 {
|
|
t.Errorf("WriteInt16LE: act %X ≠ exp %X", act16, exp16)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act32))
|
|
if act32 != exp32 {
|
|
t.Errorf("WriteInt32LE: act %X ≠ exp %X", act32, exp32)
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act64))
|
|
if act64 != exp64 {
|
|
t.Errorf("WriteInt64LE: act %X ≠ exp %X", act64, exp64)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestWriteFloat verifies that byteio-written floating point numbers can be
|
|
// read back correctly using encoding/binary.
|
|
func TestWriteFloat(t *testing.T) {
|
|
vals := []float64{
|
|
0, 1, -1,
|
|
math.MaxFloat32, math.SmallestNonzeroFloat32,
|
|
math.Inf(1), math.Inf(-1), math.NaN(),
|
|
}
|
|
checkErr := func(err error) {
|
|
if err != nil {
|
|
t.Fatalf("unexpected I/O error: %v", err)
|
|
}
|
|
}
|
|
|
|
// prepare buffer of encoded data
|
|
buf := bytes.NewBuffer(nil)
|
|
for _, val := range vals {
|
|
checkErr(byteio.WriteFloat32BE(buf, float32(val)))
|
|
checkErr(byteio.WriteFloat32LE(buf, float32(val)))
|
|
checkErr(byteio.WriteFloat64BE(buf, val))
|
|
checkErr(byteio.WriteFloat64LE(buf, val))
|
|
}
|
|
|
|
// verify integrity using encoding/binary
|
|
for _, exp64 := range vals {
|
|
var (
|
|
act32, exp32 float32 = 0, float32(exp64)
|
|
act64 float64
|
|
)
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act32))
|
|
if act32 != exp32 && !math.IsNaN(float64(act32)) && !math.IsNaN(exp64) {
|
|
t.Errorf("act %f (0x%X) ≠ exp %f (0x%X)",
|
|
act32, math.Float32bits(act32),
|
|
exp32, math.Float32bits(exp32))
|
|
}
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act32))
|
|
if act32 != exp32 && !math.IsNaN(float64(act32)) && !math.IsNaN(exp64) {
|
|
t.Errorf("act %f (0x%X) ≠ exp %f (0x%X)",
|
|
act32, math.Float32bits(act32),
|
|
exp32, math.Float32bits(exp32))
|
|
}
|
|
|
|
checkErr(binary.Read(buf, binary.BigEndian, &act64))
|
|
if act64 != exp64 && !math.IsNaN(act64) && !math.IsNaN(exp64) {
|
|
t.Errorf("act %f (0x%X) ≠ exp %f (0x%X)",
|
|
act64, math.Float64bits(act64),
|
|
exp64, math.Float64bits(exp64))
|
|
}
|
|
checkErr(binary.Read(buf, binary.LittleEndian, &act64))
|
|
if act64 != exp64 && !math.IsNaN(act64) && !math.IsNaN(exp64) {
|
|
t.Errorf("act %f (0x%X) ≠ exp %f (0x%X)",
|
|
act64, math.Float64bits(act64),
|
|
exp64, math.Float64bits(exp64))
|
|
}
|
|
}
|
|
}
|
|
|
|
// ErrAbortWriter is returned by AbortWriter when the limit is reached.
|
|
var ErrAbortWriter = errors.New("aborted write")
|
|
|
|
// AbortWriter will abort after a certain number of bytes have been written. It
|
|
// implements byteio.Writer since we do not want it to be wrapped by a
|
|
// bufio.Writer, which would defer errors until a flush operation.
|
|
type AbortWriter struct {
|
|
when, cur int
|
|
}
|
|
|
|
func (aw *AbortWriter) WriteByte(b byte) error {
|
|
if aw.when == aw.cur {
|
|
return ErrAbortWriter
|
|
}
|
|
aw.cur++
|
|
return nil
|
|
}
|
|
|
|
func (aw *AbortWriter) WriteRune(r rune) (int, error) {
|
|
len := utf8.RuneLen(r)
|
|
for i := 0; i < len; i++ {
|
|
if err := aw.WriteByte(' '); err != nil {
|
|
return i, err
|
|
}
|
|
}
|
|
return len, nil
|
|
}
|
|
|
|
func (aw *AbortWriter) Write(buf []byte) (int, error) {
|
|
for i, b := range buf {
|
|
if err := aw.WriteByte(b); err != nil {
|
|
return i, err
|
|
}
|
|
}
|
|
return len(buf), nil
|
|
}
|
|
|
|
// TestWriteIntErr ensures that an error is correctly returned when writing an
|
|
// integer and the underlying writer reports an error.
|
|
func TestWriteIntErr(t *testing.T) {
|
|
check := func(name string, size int, fn func(bout byteio.Writer) error) {
|
|
for i := 0; i <= size; i++ {
|
|
err := fn(&AbortWriter{when: i})
|
|
switch err {
|
|
case nil:
|
|
if i < size {
|
|
t.Errorf("%s: expected err after %d "+
|
|
"bytes", name, i)
|
|
}
|
|
case ErrAbortWriter:
|
|
if i == size {
|
|
t.Errorf("%s: unexpected err after %d "+
|
|
"bytes", name, i)
|
|
}
|
|
default:
|
|
t.Errorf("%s: unexpected err %v", name, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
check("WriteUint16BE", 2, func(bout byteio.Writer) error { return byteio.WriteUint16BE(bout, 0) })
|
|
check("WriteUint32BE", 4, func(bout byteio.Writer) error { return byteio.WriteUint32BE(bout, 0) })
|
|
check("WriteUint64BE", 8, func(bout byteio.Writer) error { return byteio.WriteUint64BE(bout, 0) })
|
|
check("WriteUint16LE", 2, func(bout byteio.Writer) error { return byteio.WriteUint16LE(bout, 0) })
|
|
check("WriteUint32LE", 4, func(bout byteio.Writer) error { return byteio.WriteUint32LE(bout, 0) })
|
|
check("WriteUint64LE", 8, func(bout byteio.Writer) error { return byteio.WriteUint64LE(bout, 0) })
|
|
}
|