From 228cb144914ff9a059c4f6bc40554dd333b49f44 Mon Sep 17 00:00:00 2001 From: Mickael Remond Date: Thu, 5 Oct 2017 20:11:28 -0400 Subject: [PATCH] Test refactoring --- xmpp/client_test.go | 101 +++++----------------------------------- xmpp/tcp_server_mock.go | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 90 deletions(-) create mode 100644 xmpp/tcp_server_mock.go diff --git a/xmpp/client_test.go b/xmpp/client_test.go index 2728ff1..5a3c2cc 100644 --- a/xmpp/client_test.go +++ b/xmpp/client_test.go @@ -16,8 +16,8 @@ const ( func TestClient_Connect(t *testing.T) { // Setup Mock server - mock := XMPPServerMock{} - mock.Start(t, handlerConnackSuccess) + mock := ServerMock{} + mock.Start(t, testXMPPAddress, handlerConnectSuccess) // Test / Check result options := Options{Address: testXMPPAddress, Jid: "test@localhost", Password: "test"} @@ -28,13 +28,10 @@ func TestClient_Connect(t *testing.T) { t.Errorf("connect create XMPP client: %s", err) } - var session *Session - if session, err = client.Connect(); err != nil { + if _, err = client.Connect(); err != nil { t.Errorf("XMPP connection failed: %s", err) } - fmt.Println("Stream opened, we have streamID = ", session.StreamId) - mock.Stop() } @@ -43,30 +40,20 @@ func TestClient_Connect(t *testing.T) { const serverStreamOpen = "" -func handlerConnackSuccess(t *testing.T, c net.Conn) { +func handlerConnectSuccess(t *testing.T, c net.Conn) { decoder := xml.NewDecoder(c) - checkOpenStream(t, decoder) + checkOpenStream(t, c, decoder) - if _, err := fmt.Fprintf(c, serverStreamOpen, "localhost", "streamid1", NSClient, NSStream); err != nil { - t.Errorf("cannot write server stream open: %s", err) - } - fmt.Println("Sent stream Open") sendStreamFeatures(t, c, decoder) - fmt.Println("Sent stream feature") readAuth(t, decoder) fmt.Fprintln(c, "") - checkOpenStream(t, decoder) - if _, err := fmt.Fprintf(c, serverStreamOpen, "localhost", "streamid1", NSClient, NSStream); err != nil { - t.Errorf("cannot write server stream open: %s", err) - } + checkOpenStream(t, c, decoder) sendBindFeature(t, c, decoder) - bind(t, c, decoder) - session(t, c, decoder) } -func checkOpenStream(t *testing.T, decoder *xml.Decoder) { +func checkOpenStream(t *testing.T, c net.Conn, decoder *xml.Decoder) { for { var token xml.Token token, err := decoder.Token() @@ -79,11 +66,12 @@ func checkOpenStream(t *testing.T, decoder *xml.Decoder) { case xml.StartElement: if elem.Name.Space != NSStream || elem.Name.Local != "stream" { err = errors.New("xmpp: expected but got <" + elem.Name.Local + "> in " + elem.Name.Space) + return + } + if _, err := fmt.Fprintf(c, serverStreamOpen, "localhost", "streamid1", NSClient, NSStream); err != nil { + t.Errorf("cannot write server stream open: %s", err) } - fmt.Printf("Received: %v\n", elem.Name.Local) return - case xml.ProcInst: - fmt.Printf("Received: %v\n", elem.Inst) } } } @@ -112,7 +100,6 @@ func readAuth(t *testing.T, decoder *xml.Decoder) string { nv = &auth{} // Decode element into pointer storage if err = decoder.DecodeElement(nv, &se); err != nil { - fmt.Println(err) t.Errorf("cannot decode auth: %s", err) return "" } @@ -144,7 +131,6 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) { iq := &ClientIQ{} // Decode element into pointer storage if err = decoder.DecodeElement(&iq, &se); err != nil { - fmt.Println(err) t.Errorf("cannot decode bind iq: %s", err) return } @@ -160,68 +146,3 @@ func bind(t *testing.T, c net.Conn, decoder *xml.Decoder) { ` fmt.Fprintf(c, result, iq.Id, "test@localhost/test") // TODO use real JID } - -func session(t *testing.T, c net.Conn, decoder *xml.Decoder) { - -} - -type testHandler func(t *testing.T, conn net.Conn) - -type XMPPServerMock struct { - t *testing.T - handler testHandler - listener net.Listener - connections []net.Conn - done chan struct{} -} - -func (mock *XMPPServerMock) Start(t *testing.T, handler testHandler) { - mock.t = t - mock.handler = handler - if err := mock.init(); err != nil { - return - } - go mock.loop() -} - -func (mock *XMPPServerMock) Stop() { - close(mock.done) - if mock.listener != nil { - mock.listener.Close() - } - // Close all existing connections - for _, c := range mock.connections { - c.Close() - } -} - -func (mock *XMPPServerMock) init() error { - mock.done = make(chan struct{}) - - l, err := net.Listen("tcp", testXMPPAddress) - if err != nil { - mock.t.Errorf("TCPServerMock cannot listen on address: %q", testXMPPAddress) - return err - } - mock.listener = l - return nil -} - -func (mock *XMPPServerMock) loop() { - listener := mock.listener - for { - conn, err := listener.Accept() - if err != nil { - select { - case <-mock.done: - return - default: - mock.t.Error("TCPServerMock accept error:", err.Error()) - } - return - } - mock.connections = append(mock.connections, conn) - // TODO Create and pass a context to cancel the handler if they are still around = avoid possible leak on complex handlers - go mock.handler(mock.t, conn) - } -} diff --git a/xmpp/tcp_server_mock.go b/xmpp/tcp_server_mock.go new file mode 100644 index 0000000..bdc4397 --- /dev/null +++ b/xmpp/tcp_server_mock.go @@ -0,0 +1,83 @@ +package xmpp + +import ( + "net" + "testing" +) + +//============================================================================= +// TCP Server Mock + +// ClientHandler is passed by the test client to provide custom behaviour to +// the TCP server mock. This allows customizing the server behaviour to allow +// testing clients under various scenarii. +type ClientHandler func(t *testing.T, conn net.Conn) + +// ServerMock is a simple TCP server that can be use to mock basic server +// behaviour to test clients. +type ServerMock struct { + t *testing.T + handler ClientHandler + listener net.Listener + connections []net.Conn + done chan struct{} +} + +// Start launches the mock TCP server, listening to an actual address / port. +func (mock *ServerMock) Start(t *testing.T, addr string, handler ClientHandler) { + mock.t = t + mock.handler = handler + if err := mock.init(addr); err != nil { + return + } + go mock.loop() +} + +func (mock *ServerMock) Stop() { + close(mock.done) + if mock.listener != nil { + mock.listener.Close() + } + // Close all existing connections + for _, c := range mock.connections { + c.Close() + } +} + +//============================================================================= +// Mock Server internals + +// init starts listener on the provided address. +func (mock *ServerMock) init(addr string) error { + mock.done = make(chan struct{}) + + l, err := net.Listen("tcp", addr) + if err != nil { + mock.t.Errorf("TCPServerMock cannot listen on address: %q", addr) + return err + } + mock.listener = l + return nil +} + +// loop accepts connections and creates a go routine per connection. +// The go routine is running the client handler, that is used to provide the +// real TCP server behaviour. +func (mock *ServerMock) loop() { + listener := mock.listener + for { + conn, err := listener.Accept() + if err != nil { + select { + case <-mock.done: + return + default: + mock.t.Error("TCPServerMock accept error:", err.Error()) + } + return + } + mock.connections = append(mock.connections, conn) + // TODO Create and pass a context to cancel the handler if they are still around = avoid possible leak on complex handlers + go mock.handler(mock.t, conn) + } +}