From 7d9b8c3099d1e56a410254f0f04e2f435a732043 Mon Sep 17 00:00:00 2001 From: Alex Bramley Date: Tue, 23 Aug 2011 10:53:52 +0100 Subject: [PATCH] Add tests for explicit and implicit (via EOF) shutdown; fix bug ;-) --- client/connection.go | 2 +- client/connection_test.go | 84 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/client/connection.go b/client/connection.go index ddac627..6b239a0 100644 --- a/client/connection.go +++ b/client/connection.go @@ -265,11 +265,11 @@ func (conn *Conn) shutdown() { // Guard against double-call of shutdown() if we get an error in send() // as calling sock.Close() will cause recv() to recieve EOF in readstring() if conn.Connected { + conn.Dispatcher.Dispatch("disconnected", conn, &Line{}) conn.Connected = false conn.sock.Close() conn.cSend <- true conn.cLoop <- true - conn.Dispatcher.Dispatch("disconnected", conn, &Line{}) // reinit datastructures ready for next connection // do this here rather than after runLoop()'s for due to race conn.initialise() diff --git a/client/connection_test.go b/client/connection_test.go index 3b14be2..7a2e8ba 100644 --- a/client/connection_test.go +++ b/client/connection_test.go @@ -3,6 +3,7 @@ package client import ( "strings" "testing" + "time" ) func setUp(t *testing.T) (*mockNetConn, *Conn) { @@ -14,6 +15,89 @@ func setUp(t *testing.T) (*mockNetConn, *Conn) { return m, c } +func tearDown(m *mockNetConn, c *Conn) { + // This is enough to cause all the associated goroutines in m and c stop + // (tested below in TestShutdown to make sure this is the case) + m.Close() +} + +func TestShutdown(t *testing.T) { + _, c := setUp(t) + + // Shutdown won't clean things up unless we're "connected" already + c.Connected = true + + // Setup a mock event dispatcher to test correct triggering of "disconnected" + flag := false + c.Dispatcher = WasEventDispatched("disconnected", &flag) + + // Call shutdown manually + c.shutdown() + + // Check that we get an EOF from Read() + timer := time.NewTimer(5e6) + select { + case <-timer.C: + t.Errorf("No error received for shutdown.") + case err := <-c.Err: + timer.Stop() + if err.String() != "irc.recv(): EOF" { + t.Errorf("Expected EOF, got: %s", err) + } + } + + // Verify that the connection no longer thinks it's connected + if c.Connected { + t.Errorf("Conn still thinks it's connected to the server.") + } + + // Verify that the "disconnected" event fired correctly + if !flag { + t.Errorf("Calling Close() didn't result in dispatch of disconnected event.") + } + + // TODO(fluffle): Try to work out a way of testing that the background + // goroutines were *actually* stopped? Test m a bit more? +} + +// Practically the same as the above test, but shutdown is called implicitly +// by recv() getting an EOF from the mock connection. +func TestEOF(t *testing.T) { + m, c := setUp(t) + + // Shutdown won't clean things up unless we're "connected" already + c.Connected = true + + // Setup a mock event dispatcher to test correct triggering of "disconnected" + flag := false + c.Dispatcher = WasEventDispatched("disconnected", &flag) + + // Simulate EOF from server + m.Close() + + // Check that we get an EOF from Read() + timer := time.NewTimer(5e6) + select { + case <-timer.C: + t.Errorf("No error received for shutdown.") + case err := <-c.Err: + timer.Stop() + if err.String() != "irc.recv(): EOF" { + t.Errorf("Expected EOF, got: %s", err) + } + } + + // Verify that the connection no longer thinks it's connected + if c.Connected { + t.Errorf("Conn still thinks it's connected to the server.") + } + + // Verify that the "disconnected" event fired correctly + if !flag { + t.Errorf("Calling Close() didn't result in dispatch of disconnected event.") + } +} + // Mock dispatcher to verify that events are triggered successfully type mockDispatcher func(string, ...interface{})