Import testio package.
This commit is contained in:
parent
3b215bc8b2
commit
6e4239649a
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright (c) 2014 Kyle Isom <kyle@tyrfingr.is>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,15 @@
|
||||||
|
## testio
|
||||||
|
|
||||||
|
This is a collection of various utility io types:
|
||||||
|
|
||||||
|
* BrokenReadWriter
|
||||||
|
* BrokenWriter
|
||||||
|
* BufCloser
|
||||||
|
* BufferConn
|
||||||
|
* LoggingBuffer
|
||||||
|
|
||||||
|
You can check out the
|
||||||
|
[godoc](https://godoc.org/github.com/kisom/goutils/testio) for dtails.
|
||||||
|
|
||||||
|
It was imported from [kisom/testio](https://github.com/kisom/testio/). The
|
||||||
|
original Git directory is preserved in git-hist.tar.xz.
|
Binary file not shown.
|
@ -0,0 +1,261 @@
|
||||||
|
// Package testio implements various io utility types. Included are
|
||||||
|
// BrokenWriter, which fails after writing a certain number of bytes;
|
||||||
|
// a BufCloser, which wraps a bytes.Buffer in a Close method; a
|
||||||
|
// BrokenReadWriter, which fails after writing a certain number of
|
||||||
|
// bytes and/or reading a certain number of bytes; a LoggingBuffer
|
||||||
|
// that logs all reads and writes; and a BufferConn, that is designed
|
||||||
|
// to simulate net.Conn.
|
||||||
|
package testio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BrokenWriter implements an io.Writer that fails after a certain
|
||||||
|
// number of bytes. This can be used to simulate a network connection
|
||||||
|
// that breaks during write or a file on a filesystem that becomes
|
||||||
|
// full, for example. A BrokenWriter doesn't actually store any data.
|
||||||
|
type BrokenWriter struct {
|
||||||
|
current, limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBrokenWriter creates a new BrokenWriter that can store only
|
||||||
|
// limit bytes.
|
||||||
|
func NewBrokenWriter(limit int) *BrokenWriter {
|
||||||
|
return &BrokenWriter{limit: limit}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write will write the byte slice to the BrokenWriter, failing if the
|
||||||
|
// maximum number of bytes has been reached.
|
||||||
|
func (w *BrokenWriter) Write(p []byte) (int, error) {
|
||||||
|
if (len(p) + w.current) <= w.limit {
|
||||||
|
w.current += len(p)
|
||||||
|
} else {
|
||||||
|
spill := (len(p) + w.current) - w.limit
|
||||||
|
w.current = w.limit
|
||||||
|
return len(p) - spill, errors.New("testio: write failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend increases the byte limit to allow more data to be written.
|
||||||
|
func (w *BrokenWriter) Extend(n int) {
|
||||||
|
w.limit += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears the limit and bytes in the BrokenWriter. Extend needs
|
||||||
|
// to be called to allow data to be written.
|
||||||
|
func (w *BrokenWriter) Reset() {
|
||||||
|
w.limit = 0
|
||||||
|
w.current = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// BrokenReadWriter implements a broken reader and writer, backed by a
|
||||||
|
// bytes.Buffer.
|
||||||
|
type BrokenReadWriter struct {
|
||||||
|
rlimit, wlimit int
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBrokenReadWriter initialises a new BrokerReadWriter with an empty
|
||||||
|
// reader and the specified limits.
|
||||||
|
func NewBrokenReadWriter(wlimit, rlimit int) *BrokenReadWriter {
|
||||||
|
return &BrokenReadWriter{
|
||||||
|
wlimit: wlimit,
|
||||||
|
rlimit: rlimit,
|
||||||
|
buf: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write satisfies the Writer interface.
|
||||||
|
func (brw *BrokenReadWriter) Write(p []byte) (int, error) {
|
||||||
|
if (len(p) + brw.buf.Len()) > brw.wlimit {
|
||||||
|
remain := brw.wlimit - brw.buf.Len()
|
||||||
|
if remain > 0 {
|
||||||
|
brw.buf.Write(p[:remain])
|
||||||
|
return remain, errors.New("testio: write failed")
|
||||||
|
}
|
||||||
|
return 0, errors.New("testio: write failed")
|
||||||
|
}
|
||||||
|
return brw.buf.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read satisfies the Reader interface.
|
||||||
|
func (brw *BrokenReadWriter) Read(p []byte) (int, error) {
|
||||||
|
remain := brw.rlimit - brw.buf.Len()
|
||||||
|
if len(p) > remain {
|
||||||
|
tmp := make([]byte, len(p)-remain)
|
||||||
|
n, err := brw.buf.Read(tmp)
|
||||||
|
if err == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
copy(p, tmp)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
return brw.buf.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend increases the BrokenReadWriter limit.
|
||||||
|
func (brw *BrokenReadWriter) Extend(w, r int) {
|
||||||
|
brw.rlimit += r
|
||||||
|
brw.wlimit += w
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears the internal buffer. It retains its original limit.
|
||||||
|
func (brw *BrokenReadWriter) Reset() {
|
||||||
|
brw.buf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BufCloser is a buffer wrapped with a Close method.
|
||||||
|
type BufCloser struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the data to the BufCloser.
|
||||||
|
func (buf *BufCloser) Write(p []byte) (int, error) {
|
||||||
|
return buf.buf.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads data from the BufCloser.
|
||||||
|
func (buf *BufCloser) Read(p []byte) (int, error) {
|
||||||
|
return buf.buf.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is a stub function to satisfy the io.Closer interface.
|
||||||
|
func (buf *BufCloser) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears the internal buffer.
|
||||||
|
func (buf *BufCloser) Reset() {
|
||||||
|
buf.buf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the contents of the buffer as a byte slice.
|
||||||
|
func (buf *BufCloser) Bytes() []byte {
|
||||||
|
return buf.buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufCloser creates and initializes a new BufCloser using buf as
|
||||||
|
// its initial contents. It is intended to prepare a BufCloser to read
|
||||||
|
// existing data. It can also be used to size the internal buffer for
|
||||||
|
// writing. To do that, buf should have the desired capacity but a
|
||||||
|
// length of zero.
|
||||||
|
func NewBufCloser(buf []byte) *BufCloser {
|
||||||
|
bc := new(BufCloser)
|
||||||
|
bc.buf = bytes.NewBuffer(buf)
|
||||||
|
return bc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufCloserString creates and initializes a new Buffer using
|
||||||
|
// string s as its initial contents. It is intended to prepare a
|
||||||
|
// buffer to read an existing string.
|
||||||
|
func NewBufCloserString(s string) *BufCloser {
|
||||||
|
buf := new(BufCloser)
|
||||||
|
buf.buf = bytes.NewBufferString(s)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// A LoggingBuffer is an io.ReadWriter that prints the hex value of
|
||||||
|
// the data for all reads and writes.
|
||||||
|
type LoggingBuffer struct {
|
||||||
|
rw io.ReadWriter
|
||||||
|
w io.Writer
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLoggingBuffer creates a logging buffer from an existing
|
||||||
|
// io.ReadWriter. By default, it will log to standard error.
|
||||||
|
func NewLoggingBuffer(rw io.ReadWriter) *LoggingBuffer {
|
||||||
|
return &LoggingBuffer{
|
||||||
|
rw: rw,
|
||||||
|
w: os.Stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogTo sets the io.Writer that the buffer will write logs to.
|
||||||
|
func (lb *LoggingBuffer) LogTo(w io.Writer) {
|
||||||
|
lb.w = w
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetName gives a name to the logging buffer to help distinguish
|
||||||
|
// output from this buffer.
|
||||||
|
func (lb *LoggingBuffer) SetName(name string) {
|
||||||
|
lb.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes the data to the logging buffer and writes the data to
|
||||||
|
// the logging writer.
|
||||||
|
func (lb *LoggingBuffer) Write(p []byte) (int, error) {
|
||||||
|
if lb.name != "" {
|
||||||
|
fmt.Fprintf(lb.w, "[%s] ", lb.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(lb.w, "[WRITE] %x\n", p)
|
||||||
|
return lb.rw.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads the data from the logging buffer and writes the data to
|
||||||
|
// the logging writer.
|
||||||
|
func (lb *LoggingBuffer) Read(p []byte) (int, error) {
|
||||||
|
n, err := lb.rw.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
if lb.name != "" {
|
||||||
|
fmt.Fprintf(lb.w, "[%s] ", lb.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(lb.w, "[READ] %x\n", p)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// BufferConn is a type that can be used to simulate network
|
||||||
|
// connections between a "client" (the code that uses the BufferConn)
|
||||||
|
// and some simulated "peer". Writes go to a "client" buffer, which is
|
||||||
|
// used to record the data sent by the caller, which may be read with
|
||||||
|
// ReadPeer. The peer's responses may be simulated by calling
|
||||||
|
// WritePeer; when the client reads from the BufferConn, they will see
|
||||||
|
// this data.
|
||||||
|
type BufferConn struct {
|
||||||
|
client, peer *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufferConn initialises a new simulated network connection.
|
||||||
|
func NewBufferConn() *BufferConn {
|
||||||
|
return &BufferConn{
|
||||||
|
client: &bytes.Buffer{},
|
||||||
|
peer: &bytes.Buffer{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the client buffer.
|
||||||
|
func (bc *BufferConn) Write(p []byte) (int, error) {
|
||||||
|
return bc.client.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the peer buffer.
|
||||||
|
func (bc *BufferConn) Read(p []byte) (int, error) {
|
||||||
|
return bc.peer.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WritePeer writes data to the peer buffer.
|
||||||
|
func (bc *BufferConn) WritePeer(p []byte) (int, error) {
|
||||||
|
return bc.peer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadClient reads data from the client buffer.
|
||||||
|
func (bc *BufferConn) ReadClient(p []byte) (int, error) {
|
||||||
|
return bc.client.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is a dummy operation that allows the BufferConn to be used as
|
||||||
|
// an io.Closer.
|
||||||
|
func (bc *BufferConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
package testio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBrokenWriter(t *testing.T) {
|
||||||
|
buf := NewBrokenWriter(2)
|
||||||
|
data := []byte{1, 2}
|
||||||
|
|
||||||
|
n, err := buf.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
} else if n != 2 {
|
||||||
|
t.Fatalf("expected write size of 2, have %d", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a write failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
_, err = buf.Write(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected a write failure after reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Extend(2)
|
||||||
|
_, err = buf.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBufCloser(t *testing.T) {
|
||||||
|
var data = []byte{1, 2}
|
||||||
|
var read = make([]byte, 2)
|
||||||
|
|
||||||
|
buf := NewBufCloser(data)
|
||||||
|
_, err := buf.Read(read)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = buf.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Close()
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
s := "hi"
|
||||||
|
buf = NewBufCloserString(s)
|
||||||
|
|
||||||
|
read = buf.Bytes()
|
||||||
|
if string(read) != s {
|
||||||
|
t.Fatalf("expected %s, have %s", s, read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoggingBuffer(t *testing.T) {
|
||||||
|
src := &bytes.Buffer{}
|
||||||
|
data := []byte("AB")
|
||||||
|
lb := NewLoggingBuffer(src)
|
||||||
|
_, err := lb.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src.Reset()
|
||||||
|
lb.SetName("TEST")
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
lb.LogTo(out)
|
||||||
|
|
||||||
|
_, err = lb.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := "[TEST] [WRITE] 4142\n"
|
||||||
|
if string(out.Bytes()) != expected {
|
||||||
|
t.Fatalf("expected '%s', have '%s'", expected, string(out.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Reset()
|
||||||
|
src = bytes.NewBuffer(data)
|
||||||
|
read := make([]byte, 2)
|
||||||
|
|
||||||
|
_, err = lb.Read(read)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = "[TEST] [READ] 4142\n"
|
||||||
|
if string(out.Bytes()) != expected {
|
||||||
|
t.Fatalf("expected '%s', have '%s'", expected, string(out.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Reset()
|
||||||
|
lb.SetName("")
|
||||||
|
lb.LogTo(os.Stderr)
|
||||||
|
lb.Write([]byte("AB"))
|
||||||
|
|
||||||
|
lb.LogTo(out)
|
||||||
|
_, err = lb.Read(read)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = "[READ] 4142\n"
|
||||||
|
if string(out.Bytes()) != expected {
|
||||||
|
t.Fatalf("expected '%s', have '%s'", expected, string(out.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
src.Reset()
|
||||||
|
_, err = lb.Read(read)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a read failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBrokenReadWriter(t *testing.T) {
|
||||||
|
brw := NewBrokenReadWriter(0, 0)
|
||||||
|
lb := NewLoggingBuffer(brw)
|
||||||
|
|
||||||
|
var p = make([]byte, 2)
|
||||||
|
var data = []byte("HI")
|
||||||
|
_, err := lb.Write(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a write failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = lb.Read(p)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a read failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
brw.Extend(1, 0)
|
||||||
|
_, err = lb.Write(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a write failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
brw.Extend(2, 0)
|
||||||
|
_, err = lb.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
brw.Extend(4, 1)
|
||||||
|
_, err = lb.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = lb.Read(p)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected a read failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
brw.Reset()
|
||||||
|
brw.Extend(10, 2)
|
||||||
|
_, err = lb.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p = make([]byte, 1)
|
||||||
|
_, err = lb.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBufferConn(t *testing.T) {
|
||||||
|
bc := NewBufferConn()
|
||||||
|
|
||||||
|
client := []byte("AB")
|
||||||
|
peer := []byte("XY")
|
||||||
|
|
||||||
|
_, err := bc.WritePeer(peer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var p = make([]byte, 2)
|
||||||
|
_, err = bc.Write(client)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bc.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(p, peer) {
|
||||||
|
t.Fatalf("client should have read %x, but read %x",
|
||||||
|
peer, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bc.ReadClient(p)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(client, p) {
|
||||||
|
t.Fatalf("client should have sent %x, but sent %x",
|
||||||
|
client, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bc.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Close should always return nil, but it returned %v", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue