From 473acc61c8392dc7ae303d91568e179c4f105a76 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 2 Jul 2019 12:12:53 +0200 Subject: add black list --- .../fluffle/goirc/client/connection_test.go | 585 --------------------- 1 file changed, 585 deletions(-) delete mode 100644 vendor/github.com/fluffle/goirc/client/connection_test.go (limited to 'vendor/github.com/fluffle/goirc/client/connection_test.go') diff --git a/vendor/github.com/fluffle/goirc/client/connection_test.go b/vendor/github.com/fluffle/goirc/client/connection_test.go deleted file mode 100644 index acf4713..0000000 --- a/vendor/github.com/fluffle/goirc/client/connection_test.go +++ /dev/null @@ -1,585 +0,0 @@ -package client - -import ( - "runtime" - "strings" - "testing" - "time" - - "github.com/fluffle/goirc/state" - "github.com/golang/mock/gomock" -) - -type checker struct { - t *testing.T - c chan struct{} -} - -func callCheck(t *testing.T) checker { - return checker{t: t, c: make(chan struct{})} -} - -func (c checker) call() { - c.c <- struct{}{} -} - -func (c checker) assertNotCalled(fmt string, args ...interface{}) { - select { - case <-c.c: - c.t.Errorf(fmt, args...) - default: - } -} - -func (c checker) assertWasCalled(fmt string, args ...interface{}) { - select { - case <-c.c: - case <-time.After(time.Millisecond): - // Usually need to wait for goroutines to settle :-/ - c.t.Errorf(fmt, args...) - } -} - -type testState struct { - ctrl *gomock.Controller - st *state.MockTracker - nc *mockNetConn - c *Conn -} - -// NOTE: including a second argument at all prevents calling c.postConnect() -func setUp(t *testing.T, start ...bool) (*Conn, *testState) { - ctrl := gomock.NewController(t) - st := state.NewMockTracker(ctrl) - nc := MockNetConn(t) - c := SimpleClient("test", "test", "Testing IRC") - c.initialise() - - c.st = st - c.sock = nc - c.cfg.Flood = true // Tests can take a while otherwise - c.connected = true - // If a second argument is passed to setUp, we tell postConnect not to - // start the various goroutines that shuttle data around. - c.postConnect(len(start) == 0) - // Sleep 1ms to allow background routines to start. - <-time.After(time.Millisecond) - - return c, &testState{ctrl, st, nc, c} -} - -func (s *testState) tearDown() { - s.nc.ExpectNothing() - s.c.Close() - s.ctrl.Finish() -} - -// Practically the same as the above test, but Close is called implicitly -// by recv() getting an EOF from the mock connection. -func TestEOF(t *testing.T) { - c, s := setUp(t) - // Since we're not using tearDown() here, manually call Finish() - defer s.ctrl.Finish() - - // Set up a handler to detect whether disconnected handlers are called - dcon := callCheck(t) - c.HandleFunc(DISCONNECTED, func(conn *Conn, line *Line) { - dcon.call() - }) - - // Simulate EOF from server - s.nc.Close() - - // Verify that disconnected handler was called - dcon.assertWasCalled("Conn did not call disconnected handlers.") - - // Verify that the connection no longer thinks it's connected - if c.Connected() { - t.Errorf("Conn still thinks it's connected to the server.") - } -} - -func TestClientAndStateTracking(t *testing.T) { - ctrl := gomock.NewController(t) - st := state.NewMockTracker(ctrl) - c := SimpleClient("test", "test", "Testing IRC") - - // Assert some basic things about the initial state of the Conn struct - me := c.cfg.Me - if me.Nick != "test" || me.Ident != "test" || - me.Name != "Testing IRC" || me.Host != "" { - t.Errorf("Conn.cfg.Me not correctly initialised.") - } - // Check that the internal handlers are correctly set up - for k, _ := range intHandlers { - if _, ok := c.intHandlers.set[strings.ToLower(k)]; !ok { - t.Errorf("Missing internal handler for '%s'.", k) - } - } - - // Now enable the state tracking code and check its handlers - c.EnableStateTracking() - for k, _ := range stHandlers { - if _, ok := c.intHandlers.set[strings.ToLower(k)]; !ok { - t.Errorf("Missing state handler for '%s'.", k) - } - } - if len(c.stRemovers) != len(stHandlers) { - t.Errorf("Incorrect number of Removers (%d != %d) when adding state handlers.", - len(c.stRemovers), len(stHandlers)) - } - if neu := c.Me(); neu.Nick != me.Nick || neu.Ident != me.Ident || - neu.Name != me.Name || neu.Host != me.Host { - t.Errorf("Enabling state tracking erased information about me!") - } - - // We're expecting the untracked me to be replaced by a tracked one - if c.st == nil { - t.Errorf("State tracker not enabled correctly.") - } - if me = c.cfg.Me; me.Nick != "test" || me.Ident != "test" || - me.Name != "Testing IRC" || me.Host != "" { - t.Errorf("Enabling state tracking did not replace Me correctly.") - } - - // Now, shim in the mock state tracker and test disabling state tracking - c.st = st - gomock.InOrder( - st.EXPECT().Me().Return(me), - st.EXPECT().Wipe(), - ) - c.DisableStateTracking() - if c.st != nil || !c.cfg.Me.Equals(me) { - t.Errorf("State tracker not disabled correctly.") - } - - // Finally, check state tracking handlers were all removed correctly - for k, _ := range stHandlers { - if _, ok := c.intHandlers.set[strings.ToLower(k)]; ok && k != "NICK" { - // A bit leaky, because intHandlers adds a NICK handler. - t.Errorf("State handler for '%s' not removed correctly.", k) - } - } - if len(c.stRemovers) != 0 { - t.Errorf("stRemovers not zeroed correctly when removing state handlers.") - } - ctrl.Finish() -} - -func TestSendExitsOnDie(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - defer s.tearDown() - - // Assert that before send is running, nothing should be sent to the socket - // but writes to the buffered channel "out" should not block. - c.out <- "SENT BEFORE START" - s.nc.ExpectNothing() - - // We want to test that the a goroutine calling send will exit correctly. - exited := callCheck(t) - // send() will decrement the WaitGroup, so we must increment it. - c.wg.Add(1) - go func() { - c.send() - exited.call() - }() - - // send is now running in the background as if started by postConnect. - // This should read the line previously buffered in c.out, and write it - // to the socket connection. - s.nc.Expect("SENT BEFORE START") - - // Send another line, just to be sure :-) - c.out <- "SENT AFTER START" - s.nc.Expect("SENT AFTER START") - - // Now, use the control channel to exit send and kill the goroutine. - // This sneakily uses the fact that the other two goroutines that would - // normally be waiting for die to close are not running, so we only send - // to the goroutine started above. Normally Close() closes c.die and - // signals to all three goroutines (send, ping, runLoop) to exit. - exited.assertNotCalled("Exited before signal sent.") - c.die <- struct{}{} - exited.assertWasCalled("Didn't exit after signal.") - s.nc.ExpectNothing() - - // Sending more on c.out shouldn't reach the network. - c.out <- "SENT AFTER END" - s.nc.ExpectNothing() -} - -func TestSendExitsOnWriteError(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - // We can't use tearDown here because we're testing shutdown conditions - // (and so need to EXPECT() a call to st.Wipe() in the right place) - defer s.ctrl.Finish() - - // We want to test that the a goroutine calling send will exit correctly. - exited := callCheck(t) - // send() will decrement the WaitGroup, so we must increment it. - c.wg.Add(1) - go func() { - c.send() - exited.call() - }() - - // Send a line to be sure things are good. - c.out <- "SENT AFTER START" - s.nc.Expect("SENT AFTER START") - - // Now, close the underlying socket to cause write() to return an error. - // This will call Close() => a call to st.Wipe() will happen. - exited.assertNotCalled("Exited before signal sent.") - s.nc.Close() - // Sending more on c.out shouldn't reach the network, but we need to send - // *something* to trigger a call to write() that will fail. - c.out <- "SENT AFTER END" - exited.assertWasCalled("Didn't exit after signal.") - s.nc.ExpectNothing() -} - -func TestSendDeadlockOnFullBuffer(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - // We can't use tearDown here because we're testing a deadlock condition - // and if tearDown tries to call Close() it will deadlock some more - // because send() is holding the conn mutex via Close() already. - defer s.ctrl.Finish() - - // We want to test that the a goroutine calling send will exit correctly. - loopExit := callCheck(t) - sendExit := callCheck(t) - // send() and runLoop() will decrement the WaitGroup, so we must increment it. - c.wg.Add(2) - - // The deadlock arises when a handler being called from conn.dispatch() in - // runLoop() tries to write to conn.out to send a message back to the IRC - // server, but the buffer is full. If at the same time send() is - // calling conn.Close() and waiting in there for runLoop() to call - // conn.wg.Done(), it will not empty the buffer of conn.out => deadlock. - // - // We simulate this by artifically filling conn.out. We must use a - // goroutine to put in one more line than the buffer can hold, because - // send() will read a line from conn.out on its first loop iteration: - go func() { - for i := 0; i < 33; i++ { - c.out <- "FILL BUFFER WITH CRAP" - } - }() - // Then we add a handler that tries to write a line to conn.out: - c.HandleFunc(PRIVMSG, func(conn *Conn, line *Line) { - conn.Raw(line.Raw) - }) - // And trigger it by starting runLoop and inserting a line into conn.in: - go func() { - c.runLoop() - loopExit.call() - }() - c.in <- &Line{Cmd: PRIVMSG, Raw: "WRITE THAT CAUSES DEADLOCK"} - - // At this point the handler should be blocked on a write to conn.out, - // preventing runLoop from looping and thus noticing conn.die is closed. - // - // The next part is to force send() to call conn.Close(), which can - // be done by closing the fake net.Conn so that it returns an error on - // calls to Write(): - s.nc.ExpectNothing() - s.nc.Close() - - // Now when send is started it will read one line from conn.out and try - // to write it to the socket. It should immediately receive an error and - // call conn.Close(), triggering the deadlock as it waits forever for - // runLoop to call conn.wg.Done. - go func() { - c.send() - sendExit.call() - }() - - // Make sure that things are definitely deadlocked. - <-time.After(time.Millisecond) - - // Verify that the connection no longer thinks it's connected, i.e. - // conn.Close() has definitely been called. We can't call - // conn.Connected() here because conn.Close() holds the mutex. - if c.connected { - t.Errorf("Conn still thinks it's connected to the server.") - } - - // We expect both loops to terminate cleanly. If either of them don't - // then we have successfully deadlocked :-( - loopExit.assertWasCalled("runLoop did not exit cleanly.") - sendExit.assertWasCalled("send did not exit cleanly.") -} - -func TestRecv(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - // We can't use tearDown here because we're testing shutdown conditions - // (and so need to EXPECT() a call to st.Wipe() in the right place) - defer s.ctrl.Finish() - - // Send a line before recv is started up, to verify nothing appears on c.in - s.nc.Send(":irc.server.org 001 test :First test line.") - - // reader is a helper to do a "non-blocking" read of c.in - reader := func() *Line { - select { - case <-time.After(time.Millisecond): - case l := <-c.in: - return l - } - return nil - } - if l := reader(); l != nil { - t.Errorf("Line parsed before recv started.") - } - - // We want to test that the a goroutine calling recv will exit correctly. - exited := callCheck(t) - // recv() will decrement the WaitGroup, so we must increment it. - c.wg.Add(1) - go func() { - c.recv() - exited.call() - }() - - // Now, this should mean that we'll receive our parsed line on c.in - if l := reader(); l == nil || l.Cmd != "001" { - t.Errorf("Bad first line received on input channel") - } - - // Send a second line, just to be sure. - s.nc.Send(":irc.server.org 002 test :Second test line.") - if l := reader(); l == nil || l.Cmd != "002" { - t.Errorf("Bad second line received on input channel.") - } - - // Test that recv does something useful with a line it can't parse - // (not that there are many, ParseLine is forgiving). - s.nc.Send(":textwithnospaces") - if l := reader(); l != nil { - t.Errorf("Bad line still caused receive on input channel.") - } - - // The only way recv() exits is when the socket closes. - exited.assertNotCalled("Exited before socket close.") - s.nc.Close() - exited.assertWasCalled("Didn't exit on socket close.") - - // Since s.nc is closed we can't attempt another send on it... - if l := reader(); l != nil { - t.Errorf("Line received on input channel after socket close.") - } -} - -func TestPing(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - defer s.tearDown() - - res := time.Millisecond - - // Windows has a timer resolution of 15.625ms by default. - // This means the test will be slower on windows, but - // should at least stop most of the flakiness... - // https://github.com/fluffle/goirc/issues/88 - if runtime.GOOS == "windows" { - res = 15625 * time.Microsecond - } - - // Set a low ping frequency for testing. - c.cfg.PingFreq = 10 * res - - // reader is a helper to do a "non-blocking" read of c.out - reader := func() string { - select { - case <-time.After(res): - case s := <-c.out: - return s - } - return "" - } - if s := reader(); s != "" { - t.Errorf("Line output before ping started.") - } - - // Start ping loop. - exited := callCheck(t) - // ping() will decrement the WaitGroup, so we must increment it. - c.wg.Add(1) - go func() { - c.ping() - exited.call() - }() - - // The first ping should be after 10*res ms, - // so we don't expect anything now on c.in - if s := reader(); s != "" { - t.Errorf("Line output directly after ping started.") - } - - <-time.After(c.cfg.PingFreq) - if s := reader(); s == "" || !strings.HasPrefix(s, "PING :") { - t.Errorf("Line not output after %s.", c.cfg.PingFreq) - } - - // Reader waits for res ms and we call it a few times above. - <-time.After(7 * res) - if s := reader(); s != "" { - t.Errorf("Line output <%s after last ping.", 7*res) - } - - // This is a short window in which the ping should happen - // This may result in flaky tests; sorry (and file a bug) if so. - <-time.After(2 * res) - if s := reader(); s == "" || !strings.HasPrefix(s, "PING :") { - t.Errorf("Line not output after another %s.", 2*res) - } - - // Now kill the ping loop. - // This sneakily uses the fact that the other two goroutines that would - // normally be waiting for die to close are not running, so we only send - // to the goroutine started above. Normally Close() closes c.die and - // signals to all three goroutines (send, ping, runLoop) to exit. - exited.assertNotCalled("Exited before signal sent.") - c.die <- struct{}{} - exited.assertWasCalled("Didn't exit after signal.") - // Make sure we're no longer pinging by waiting >2x PingFreq - <-time.After(2*c.cfg.PingFreq + res) - if s := reader(); s != "" { - t.Errorf("Line output after ping stopped.") - } -} - -func TestRunLoop(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - defer s.tearDown() - - // Set up a handler to detect whether 001 handler is called - h001 := callCheck(t) - c.HandleFunc("001", func(conn *Conn, line *Line) { - h001.call() - }) - h002 := callCheck(t) - // Set up a handler to detect whether 002 handler is called - c.HandleFunc("002", func(conn *Conn, line *Line) { - h002.call() - }) - - l1 := ParseLine(":irc.server.org 001 test :First test line.") - c.in <- l1 - h001.assertNotCalled("001 handler called before runLoop started.") - - // We want to test that the a goroutine calling runLoop will exit correctly. - // Now, we can expect the call to Dispatch to take place as runLoop starts. - exited := callCheck(t) - // runLoop() will decrement the WaitGroup, so we must increment it. - c.wg.Add(1) - go func() { - c.runLoop() - exited.call() - }() - h001.assertWasCalled("001 handler not called after runLoop started.") - - // Send another line, just to be sure :-) - h002.assertNotCalled("002 handler called before expected.") - l2 := ParseLine(":irc.server.org 002 test :Second test line.") - c.in <- l2 - h002.assertWasCalled("002 handler not called while runLoop started.") - - // Now, use the control channel to exit send and kill the goroutine. - // This sneakily uses the fact that the other two goroutines that would - // normally be waiting for die to close are not running, so we only send - // to the goroutine started above. Normally Close() closes c.die and - // signals to all three goroutines (send, ping, runLoop) to exit. - exited.assertNotCalled("Exited before signal sent.") - c.die <- struct{}{} - exited.assertWasCalled("Didn't exit after signal.") - - // Sending more on c.in shouldn't dispatch any further events - c.in <- l1 - h001.assertNotCalled("001 handler called after runLoop ended.") -} - -func TestWrite(t *testing.T) { - // Passing a second value to setUp stops goroutines from starting - c, s := setUp(t, false) - // We can't use tearDown here because we're testing shutdown conditions - // (and so need to EXPECT() a call to st.Wipe() in the right place) - defer s.ctrl.Finish() - - // Write should just write a line to the socket. - if err := c.write("yo momma"); err != nil { - t.Errorf("Write returned unexpected error %v", err) - } - s.nc.Expect("yo momma") - - // Flood control is disabled -- setUp sets c.cfg.Flood = true -- so we should - // not have set c.badness at this point. - if c.badness != 0 { - t.Errorf("Flood control used when Flood = true.") - } - - c.cfg.Flood = false - if err := c.write("she so useless"); err != nil { - t.Errorf("Write returned unexpected error %v", err) - } - s.nc.Expect("she so useless") - - // The lastsent time should have been updated very recently... - if time.Now().Sub(c.lastsent) > time.Millisecond { - t.Errorf("Flood control not used when Flood = false.") - } - - // Finally, test the error state by closing the socket then writing. - s.nc.Close() - if err := c.write("she can't pass unit tests"); err == nil { - t.Errorf("Expected write to return error after socket close.") - } -} - -func TestRateLimit(t *testing.T) { - c, s := setUp(t) - defer s.tearDown() - - if c.badness != 0 { - t.Errorf("Bad initial values for rate limit variables.") - } - - // We'll be needing this later... - abs := func(i time.Duration) time.Duration { - if i < 0 { - return -i - } - return i - } - - // Since the changes to the time module, c.lastsent is now a time.Time. - // It's initialised on client creation to time.Now() which for the purposes - // of this test was probably around 1.2 ms ago. This is inconvenient. - // Making it >10s ago effectively clears out the inconsistency, as this - // makes elapsed > linetime and thus zeros c.badness and resets c.lastsent. - c.lastsent = time.Now().Add(-10 * time.Second) - if l := c.rateLimit(60); l != 0 || c.badness != 0 { - t.Errorf("Rate limit got non-zero badness from long-ago lastsent.") - } - - // So, time at the nanosecond resolution is a bit of a bitch. Choosing 60 - // characters as the line length means we should be increasing badness by - // 2.5 seconds minus the delta between the two ratelimit calls. This should - // be minimal but it's guaranteed that it won't be zero. Use 20us as a fuzz. - if l := c.rateLimit(60); l != 0 || - abs(c.badness-2500*time.Millisecond) > 20*time.Microsecond { - t.Errorf("Rate limit calculating badness incorrectly.") - } - // At this point, we can tip over the badness scale, with a bit of help. - // 720 chars => +8 seconds of badness => 10.5 seconds => ratelimit - if l := c.rateLimit(720); l != 8*time.Second || - abs(c.badness-10500*time.Millisecond) > 20*time.Microsecond { - t.Errorf("Rate limit failed to return correct limiting values.") - t.Errorf("l=%d, badness=%d", l, c.badness) - } -} -- cgit v1.2.3