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