Resync
This commit is contained in:
32
go/paasio/README.md
Normal file
32
go/paasio/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Paasio
|
||||
|
||||
Write a program that reports network IO statistics
|
||||
|
||||
You are writing a [PaaS][], and you need a way to bill customers based
|
||||
on network and filesystem usage.
|
||||
|
||||
Create a wrapper for network connections and files that can report IO
|
||||
statistics. The wrapper must report:
|
||||
|
||||
- The total number of bytes read/written.
|
||||
- The total number of read/write operations.
|
||||
|
||||
[PaaS]: http://en.wikipedia.org/wiki/Platform_as_a_service
|
||||
|
||||
To run the tests simply run the command `go test` in the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||
flag:
|
||||
|
||||
go test -bench .
|
||||
|
||||
For more detailed info about the Go track see the [help
|
||||
page](http://exercism.io/languages/go).
|
||||
|
||||
## Source
|
||||
|
||||
Brian Matsuo [https://github.com/bmatsuo](https://github.com/bmatsuo)
|
||||
|
||||
## Submitting Incomplete Problems
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||
|
40
go/paasio/interface.go
Normal file
40
go/paasio/interface.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package paasio
|
||||
|
||||
import "io"
|
||||
|
||||
// ReadCounter is an interface describing objects that can be read from,
|
||||
// and that can count the number of times they have been read from.
|
||||
//
|
||||
// If multiple goroutines concurrently call Read, implementations are not
|
||||
// required to provide any guarantees about interleaving of the Read calls.
|
||||
// However, implementations MUST guarantee that calls to ReadCount always return
|
||||
// correct results even in the presence of concurrent Read calls.
|
||||
type ReadCounter interface {
|
||||
io.Reader
|
||||
// ReadCount returns the total number of bytes successfully read along
|
||||
// with the total number of calls to Read().
|
||||
ReadCount() (n int64, nops int)
|
||||
}
|
||||
|
||||
// WriteCounter is an interface describing objects that can be written to,
|
||||
// and that can count the number of times they have been written to.
|
||||
//
|
||||
// If multiple goroutines concurrently call Write, implementations are not
|
||||
// required to provide any guarantees about interleaving of the Write calls.
|
||||
// However, implementations MUST guarantee that calls to WriteCount always return
|
||||
// correct results even in the presence of concurrent Write calls.
|
||||
type WriteCounter interface {
|
||||
io.Writer
|
||||
// WriteCount returns the total number of bytes successfully written along
|
||||
// with the total number of calls to Write().
|
||||
WriteCount() (n int64, nops int)
|
||||
}
|
||||
|
||||
// ReadWriteCounter is the union of ReadCounter and WriteCounter.
|
||||
//
|
||||
// All guarantees that apply to either of ReadCounter or WriteCounter
|
||||
// also apply to ReadWriteCounter.
|
||||
type ReadWriteCounter interface {
|
||||
ReadCounter
|
||||
WriteCounter
|
||||
}
|
223
go/paasio/paasio_test.go
Normal file
223
go/paasio/paasio_test.go
Normal file
@@ -0,0 +1,223 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user