From 6e4239649a213f198bc7fe1dabf097f77cf0d592 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Thu, 28 Apr 2016 11:37:26 -0700 Subject: [PATCH] Import testio package. --- testio/LICENSE | 13 ++ testio/README.md | 15 +++ testio/git-hist.tar.xz | Bin 0 -> 15580 bytes testio/testio.go | 261 +++++++++++++++++++++++++++++++++++++++++ testio/testio_test.go | 220 ++++++++++++++++++++++++++++++++++ 5 files changed, 509 insertions(+) create mode 100644 testio/LICENSE create mode 100644 testio/README.md create mode 100644 testio/git-hist.tar.xz create mode 100644 testio/testio.go create mode 100644 testio/testio_test.go diff --git a/testio/LICENSE b/testio/LICENSE new file mode 100644 index 0000000..b228ca4 --- /dev/null +++ b/testio/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2014 Kyle Isom + +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. diff --git a/testio/README.md b/testio/README.md new file mode 100644 index 0000000..c4d28a4 --- /dev/null +++ b/testio/README.md @@ -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. diff --git a/testio/git-hist.tar.xz b/testio/git-hist.tar.xz new file mode 100644 index 0000000000000000000000000000000000000000..2800f5e7bba848f91c6270716b2f8d122909a6e9 GIT binary patch literal 15580 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 literal 0 HcmV?d00001 diff --git a/testio/testio.go b/testio/testio.go new file mode 100644 index 0000000..ce90562 --- /dev/null +++ b/testio/testio.go @@ -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 +} diff --git a/testio/testio_test.go b/testio/testio_test.go new file mode 100644 index 0000000..d819e62 --- /dev/null +++ b/testio/testio_test.go @@ -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) + } +}