exercism/go/paasio/paasio_test.go

224 lines
4.8 KiB
Go

package paasio
import (
"bytes"
"crypto/rand"
"io"
"runtime"
"strings"
"sync"
"testing"
"time"
)
// testVersion identifies the API tested by the test program.
const targetTestVersion = 3
func TestMultiThreaded(t *testing.T) {
if testVersion != targetTestVersion {
t.Errorf("Found testVersion = %v, want %v.", testVersion, targetTestVersion)
}
mincpu := 2
minproc := 2
ncpu := runtime.NumCPU()
if ncpu < mincpu {
t.Fatalf("at least %d cpu cores are required", mincpu)
}
nproc := runtime.GOMAXPROCS(0)
if nproc < minproc {
t.Errorf("at least %d threads are required; rerun the tests", minproc)
t.Errorf("")
t.Errorf("\tgo test -cpu %d ...", minproc)
}
}
// this test could be improved to test that error conditions are preserved.
func testWrite(t *testing.T, writer func(io.Writer) WriteCounter) {
for i, test := range []struct {
writes []string
}{
{nil},
{[]string{""}},
{[]string{"I", " ", "never met ", "", "a gohper"}},
} {
var buf bytes.Buffer
buft := writer(&buf)
for _, s := range test.writes {
n, err := buft.Write([]byte(s))
if err != nil {
t.Errorf("test %d: Write(%q) unexpected error: %v", i, s, err)
continue
}
if n != len(s) {
t.Errorf("test %d: Write(%q) unexpected number of bytes written: %v", i, s, n)
continue
}
}
out := buf.String()
if out != strings.Join(test.writes, "") {
t.Errorf("test %d: unexpected content in underlying writer: %q", i, out)
}
}
}
func TestWriteWriter(t *testing.T) {
testWrite(t, NewWriteCounter)
}
func TestWriteReadWriter(t *testing.T) {
testWrite(t, func(w io.Writer) WriteCounter {
var r nopReader
return NewReadWriteCounter(readWriter{r, w})
})
}
// this test could be improved to test exact number of operations as well as
// ensure that error conditions are preserved.
func testRead(t *testing.T, reader func(io.Reader) ReadCounter) {
chunkLen := 10 << 20 // 10MB
orig := make([]byte, 10<<20)
_, err := rand.Read(orig)
if err != nil {
t.Fatalf("error reading random data")
}
buf := bytes.NewBuffer(orig)
rc := reader(buf)
var obuf bytes.Buffer
ncopy, err := io.Copy(&obuf, rc)
if err != nil {
t.Fatalf("error reading: %v", err)
}
if ncopy != int64(chunkLen) {
t.Fatalf("copied %d bytes instead of %d", ncopy, chunkLen)
}
if string(orig) != obuf.String() {
t.Fatalf("unexpected output from Read()")
}
n, nops := rc.ReadCount()
if n != int64(chunkLen) {
t.Fatalf("reported %d bytes read instead of %d", n, chunkLen)
}
if nops < 2 {
t.Fatalf("unexpected number of reads: %v", nops)
}
}
func TestReadReader(t *testing.T) {
testRead(t, NewReadCounter)
}
func TestReadReadWriter(t *testing.T) {
testRead(t, func(r io.Reader) ReadCounter {
var w nopWriter
return NewReadWriteCounter(readWriter{r, w})
})
}
func testReadTotal(t *testing.T, rc ReadCounter) {
numGo := 8000
numBytes := 50
totalBytes := int64(numGo) * int64(numBytes)
p := make([]byte, numBytes)
t.Logf("Calling Read() for %d*%d=%d bytes", numGo, numBytes, totalBytes)
wg := new(sync.WaitGroup)
wg.Add(numGo)
start := make(chan struct{})
for i := 0; i < numGo; i++ {
go func() {
<-start
rc.Read(p)
wg.Done()
}()
}
close(start)
wg.Wait()
n, nops := rc.ReadCount()
if n != totalBytes {
t.Errorf("expected %d bytes read; %d bytes reported", totalBytes, n)
}
if nops != numGo {
t.Errorf("expected %d read operations; %d operations reported", numGo, nops)
}
}
func TestReadTotalReader(t *testing.T) {
var r nopReader
testReadTotal(t, NewReadCounter(r))
}
func TestReadTotalReadWriter(t *testing.T) {
var rw nopReadWriter
testReadTotal(t, NewReadWriteCounter(rw))
}
func testWriteTotal(t *testing.T, wt WriteCounter) {
numGo := 8000
numBytes := 50
totalBytes := int64(numGo) * int64(numBytes)
p := make([]byte, numBytes)
t.Logf("Calling Write() with %d*%d=%d bytes", numGo, numBytes, totalBytes)
wg := new(sync.WaitGroup)
wg.Add(numGo)
start := make(chan struct{})
for i := 0; i < numGo; i++ {
go func() {
<-start
wt.Write(p)
wg.Done()
}()
}
close(start)
wg.Wait()
n, nops := wt.WriteCount()
if n != totalBytes {
t.Errorf("expected %d bytes written; %d bytes reported", totalBytes, n)
}
if nops != numGo {
t.Errorf("expected %d write operations; %d operations reported", numGo, nops)
}
}
func TestWriteTotalWriter(t *testing.T) {
var w nopWriter
testWriteTotal(t, NewWriteCounter(w))
}
func TestWriteTotalReadWriter(t *testing.T) {
var rw nopReadWriter
testWriteTotal(t, NewReadWriteCounter(rw))
}
type nopWriter struct{ error }
func (w nopWriter) Write(p []byte) (int, error) {
time.Sleep(1)
if w.error != nil {
return 0, w.error
}
return len(p), nil
}
type nopReader struct{ error }
func (r nopReader) Read(p []byte) (int, error) {
time.Sleep(1)
if r.error != nil {
return 0, r.error
}
return len(p), nil
}
type nopReadWriter struct {
nopReader
nopWriter
}
type readWriter struct {
io.Reader
io.Writer
}