package graceful
|
|
|
|
import (
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
var b = make([]byte, 0)
|
|
|
|
func connify(c net.Conn) *conn {
|
|
switch c.(type) {
|
|
case (*conn):
|
|
return c.(*conn)
|
|
case (*sendfile):
|
|
return &c.(*sendfile).conn
|
|
default:
|
|
panic("IDK")
|
|
}
|
|
}
|
|
|
|
func assertState(t *testing.T, n net.Conn, st connstate) {
|
|
c := connify(n)
|
|
c.m.Lock()
|
|
defer c.m.Unlock()
|
|
if c.state != st {
|
|
t.Fatalf("conn was %v, but expected %v", c.state, st)
|
|
}
|
|
}
|
|
|
|
// Not super happy about making the tests dependent on the passing of time, but
|
|
// I'm not really sure what else to do.
|
|
|
|
func expectCall(t *testing.T, ch <-chan struct{}, name string) {
|
|
select {
|
|
case <-ch:
|
|
case <-time.After(5 * time.Millisecond):
|
|
t.Fatalf("Expected call to %s", name)
|
|
}
|
|
}
|
|
|
|
func TestCounting(t *testing.T) {
|
|
kill = make(chan struct{})
|
|
c := WrapConn(fakeConn{})
|
|
ch := make(chan struct{})
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
ch <- struct{}{}
|
|
}()
|
|
|
|
select {
|
|
case <-ch:
|
|
t.Fatal("Expected connection to keep us from quitting")
|
|
case <-time.After(5 * time.Millisecond):
|
|
}
|
|
|
|
c.Close()
|
|
expectCall(t, ch, "wg.Wait()")
|
|
}
|
|
|
|
func TestStateTransitions1(t *testing.T) {
|
|
kill = make(chan struct{})
|
|
ch := make(chan struct{})
|
|
|
|
onclose := make(chan struct{})
|
|
read := make(chan struct{})
|
|
deadline := make(chan struct{})
|
|
c := WrapConn(fakeConn{
|
|
onClose: func() {
|
|
onclose <- struct{}{}
|
|
},
|
|
onRead: func() {
|
|
read <- struct{}{}
|
|
},
|
|
onSetReadDeadline: func() {
|
|
deadline <- struct{}{}
|
|
},
|
|
})
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
ch <- struct{}{}
|
|
}()
|
|
|
|
assertState(t, c, csWaiting)
|
|
|
|
// Waiting + Read() = Working
|
|
go c.Read(b)
|
|
expectCall(t, read, "c.Read()")
|
|
assertState(t, c, csWorking)
|
|
|
|
// Working + SetReadDeadline() = Waiting
|
|
go c.SetReadDeadline(time.Now())
|
|
expectCall(t, deadline, "c.SetReadDeadline()")
|
|
assertState(t, c, csWaiting)
|
|
|
|
// Waiting + kill = Dead
|
|
close(kill)
|
|
expectCall(t, onclose, "c.Close()")
|
|
assertState(t, c, csDead)
|
|
|
|
expectCall(t, ch, "wg.Wait()")
|
|
}
|
|
|
|
func TestStateTransitions2(t *testing.T) {
|
|
kill = make(chan struct{})
|
|
ch := make(chan struct{})
|
|
onclose := make(chan struct{})
|
|
read := make(chan struct{})
|
|
deadline := make(chan struct{})
|
|
c := WrapConn(fakeConn{
|
|
onClose: func() {
|
|
onclose <- struct{}{}
|
|
},
|
|
onRead: func() {
|
|
read <- struct{}{}
|
|
},
|
|
onSetReadDeadline: func() {
|
|
deadline <- struct{}{}
|
|
},
|
|
})
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
ch <- struct{}{}
|
|
}()
|
|
|
|
assertState(t, c, csWaiting)
|
|
|
|
// Waiting + Read() = Working
|
|
go c.Read(b)
|
|
expectCall(t, read, "c.Read()")
|
|
assertState(t, c, csWorking)
|
|
|
|
// Working + Read() = Working
|
|
go c.Read(b)
|
|
expectCall(t, read, "c.Read()")
|
|
assertState(t, c, csWorking)
|
|
|
|
// Working + kill = Dying
|
|
close(kill)
|
|
time.Sleep(5 * time.Millisecond)
|
|
assertState(t, c, csDying)
|
|
|
|
// Dying + Read() = Dying
|
|
go c.Read(b)
|
|
expectCall(t, read, "c.Read()")
|
|
assertState(t, c, csDying)
|
|
|
|
// Dying + SetReadDeadline() = Dead
|
|
go c.SetReadDeadline(time.Now())
|
|
expectCall(t, deadline, "c.SetReadDeadline()")
|
|
assertState(t, c, csDead)
|
|
|
|
expectCall(t, ch, "wg.Wait()")
|
|
}
|
|
|
|
func TestHijack(t *testing.T) {
|
|
kill = make(chan struct{})
|
|
fake := fakeConn{}
|
|
c := WrapConn(fake)
|
|
ch := make(chan struct{})
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
ch <- struct{}{}
|
|
}()
|
|
|
|
cc := connify(c)
|
|
if _, ok := cc.hijack().(fakeConn); !ok {
|
|
t.Error("Expected original connection back out")
|
|
}
|
|
assertState(t, c, csDead)
|
|
expectCall(t, ch, "wg.Wait()")
|
|
}
|
|
|
|
type fakeSendfile struct {
|
|
fakeConn
|
|
}
|
|
|
|
func (f fakeSendfile) ReadFrom(r io.Reader) (int64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
func TestReadFrom(t *testing.T) {
|
|
kill = make(chan struct{})
|
|
c := WrapConn(fakeSendfile{})
|
|
r := strings.NewReader("Hello world")
|
|
|
|
if rf, ok := c.(io.ReaderFrom); ok {
|
|
rf.ReadFrom(r)
|
|
} else {
|
|
t.Fatal("Expected a ReaderFrom in return")
|
|
}
|
|
}
|