byteio/val_write_test.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) })
}