MockNetConn: Wrap all channel reads in selects; send EOF on Close(); kill goroutines correctly.

This commit is contained in:
Alex Bramley 2011-08-23 10:42:54 +01:00
parent b04196327a
commit 020730aca1
1 changed files with 53 additions and 11 deletions

View File

@ -4,6 +4,7 @@ import (
"net" "net"
"os" "os"
"testing" "testing"
"time"
) )
type mockNetConn struct { type mockNetConn struct {
@ -11,6 +12,8 @@ type mockNetConn struct {
In, Out chan string In, Out chan string
in, out chan []byte in, out chan []byte
closers []chan bool
rc chan bool
closed bool closed bool
rt, wt int64 rt, wt int64
@ -19,6 +22,7 @@ type mockNetConn struct {
func MockNetConn(t *testing.T) (*mockNetConn) { func MockNetConn(t *testing.T) (*mockNetConn) {
// Our mock connection is a testing object // Our mock connection is a testing object
m := &mockNetConn{T: t} m := &mockNetConn{T: t}
m.closers = make([]chan bool, 0, 3)
// set known values for conn info // set known values for conn info
m.closed = false m.closed = false
@ -28,20 +32,39 @@ func MockNetConn(t *testing.T) (*mockNetConn) {
// buffer input // buffer input
m.In = make(chan string, 20) m.In = make(chan string, 20)
m.in = make(chan []byte) m.in = make(chan []byte)
ic := make(chan bool)
m.closers = append(m.closers, ic)
go func() { go func() {
for !m.closed { for {
m.in <- []byte(<-m.In) select {
case <-ic:
return
case s := <-m.In:
m.in <- []byte(s)
}
} }
}() }()
// buffer output // buffer output
m.Out = make(chan string) m.Out = make(chan string)
m.out = make(chan []byte, 20) m.out = make(chan []byte, 20)
oc := make(chan bool)
m.closers = append(m.closers, oc)
go func() { go func() {
for !m.closed { for {
m.Out <- string(<-m.out) select {
case <-oc:
return
case b := <-m.out:
m.Out <- string(b)
}
} }
}() }()
// Set up channel to force EOF to Read() on close.
m.rc = make(chan bool)
m.closers = append(m.closers, m.rc)
return m return m
} }
@ -51,10 +74,17 @@ func (m *mockNetConn) Send(s string) {
} }
func (m *mockNetConn) Expect(e string) { func (m *mockNetConn) Expect(e string) {
s := <-m.Out t := time.NewTimer(5e6)
if e + "\r\n" != s { select {
m.Errorf("Mock connection received unexpected value.\n\t" + case <-t.C:
"Expected: %s\n\tGot: %s", e, s) m.Errorf("Mock connection did not receive expected output.\n\t" +
"Expected: '%s', got nothing.", e)
case s := <-m.Out:
t.Stop()
if e + "\r\n" != s {
m.Errorf("Mock connection received unexpected value.\n\t" +
"Expected: '%s'\n\tGot: '%s'", e, s)
}
} }
} }
@ -63,9 +93,13 @@ func (m *mockNetConn) Read(b []byte) (int, os.Error) {
if m.closed { if m.closed {
return 0, os.NewError("EOF") return 0, os.NewError("EOF")
} }
s := <-m.in select {
copy(b, s) case s := <-m.in:
return len(s), nil copy(b, s)
case <-m.rc:
return 0, os.EOF
}
return len(b), nil
} }
func (m *mockNetConn) Write(s []byte) (int, os.Error) { func (m *mockNetConn) Write(s []byte) (int, os.Error) {
@ -79,6 +113,14 @@ func (m *mockNetConn) Write(s []byte) (int, os.Error) {
} }
func (m *mockNetConn) Close() os.Error { func (m *mockNetConn) Close() os.Error {
if m.closed {
return os.EINVAL
}
// Shut down *ALL* the goroutines!
// This will trigger an EOF event in Read() too
for _, c := range m.closers {
c <- true
}
m.closed = true m.closed = true
return nil return nil
} }