mirror of https://github.com/fluffle/goirc
Merge branch 'release'
Conflicts: client/connection.go client/connection_test.go
This commit is contained in:
commit
9c67c42fa1
|
@ -5,14 +5,9 @@ GoIRC Client Framework
|
||||||
|
|
||||||
Pretty simple, really:
|
Pretty simple, really:
|
||||||
|
|
||||||
goinstall github.com/fluffle/goirc
|
go get github.com/fluffle/goirc/client
|
||||||
|
|
||||||
You can build the test client also with:
|
There is some example code that demonstrates usage of the library in `client.go`. This will connect to freenode and join `#go-nuts` by default, so be careful ;-)
|
||||||
|
|
||||||
make
|
|
||||||
./gobot
|
|
||||||
|
|
||||||
This will connect to freenode and join `#go-nuts` by default, so be careful ;-)
|
|
||||||
|
|
||||||
### Using the framework
|
### Using the framework
|
||||||
|
|
||||||
|
|
|
@ -3,20 +3,16 @@ package client
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"github.com/fluffle/goevent/event"
|
"errors"
|
||||||
"github.com/fluffle/golog/logging"
|
|
||||||
"github.com/fluffle/goirc/state"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fluffle/goevent/event"
|
||||||
|
"github.com/fluffle/goirc/state"
|
||||||
|
"github.com/fluffle/golog/logging"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
second = int64(1e9)
|
|
||||||
)
|
|
||||||
|
|
||||||
// An IRC connection is represented by this struct.
|
// An IRC connection is represented by this struct.
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
// Connection Hostname and Nickname
|
// Connection Hostname and Nickname
|
||||||
|
@ -57,14 +53,12 @@ type Conn struct {
|
||||||
// Client->server ping frequency, in seconds. Defaults to 3m.
|
// Client->server ping frequency, in seconds. Defaults to 3m.
|
||||||
PingFreq int64
|
PingFreq int64
|
||||||
|
|
||||||
// Socket timeout, in seconds. Default to 5m.
|
|
||||||
Timeout int64
|
|
||||||
|
|
||||||
// Set this to true to disable flood protection and false to re-enable
|
// Set this to true to disable flood protection and false to re-enable
|
||||||
Flood bool
|
Flood bool
|
||||||
|
|
||||||
// Internal counters for flood protection
|
// Internal counters for flood protection
|
||||||
badness, lastsent int64
|
badness time.Duration
|
||||||
|
lastsent time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new IRC connection object, but doesn't connect to anything so
|
// Creates a new IRC connection object, but doesn't connect to anything so
|
||||||
|
@ -102,10 +96,9 @@ func Client(nick, ident, name string,
|
||||||
SSL: false,
|
SSL: false,
|
||||||
SSLConfig: nil,
|
SSLConfig: nil,
|
||||||
PingFreq: 180,
|
PingFreq: 180,
|
||||||
Timeout: 300,
|
|
||||||
Flood: false,
|
Flood: false,
|
||||||
badness: 0,
|
badness: 0,
|
||||||
lastsent: 0,
|
lastsent: time.Now(),
|
||||||
}
|
}
|
||||||
conn.addIntHandlers()
|
conn.addIntHandlers()
|
||||||
conn.Me = state.NewNick(nick, l)
|
conn.Me = state.NewNick(nick, l)
|
||||||
|
@ -151,9 +144,9 @@ func (conn *Conn) initialise() {
|
||||||
// on the connection to the IRC server, set Conn.SSL to true before calling
|
// on the connection to the IRC server, set Conn.SSL to true before calling
|
||||||
// Connect(). The port will default to 6697 if ssl is enabled, and 6667
|
// Connect(). The port will default to 6697 if ssl is enabled, and 6667
|
||||||
// otherwise. You can also provide an optional connect password.
|
// otherwise. You can also provide an optional connect password.
|
||||||
func (conn *Conn) Connect(host string, pass ...string) os.Error {
|
func (conn *Conn) Connect(host string, pass ...string) error {
|
||||||
if conn.Connected {
|
if conn.Connected {
|
||||||
return os.NewError(fmt.Sprintf(
|
return errors.New(fmt.Sprintf(
|
||||||
"irc.Connect(): already connected to %s, cannot connect to %s",
|
"irc.Connect(): already connected to %s, cannot connect to %s",
|
||||||
conn.Host, host))
|
conn.Host, host))
|
||||||
}
|
}
|
||||||
|
@ -196,7 +189,6 @@ func (conn *Conn) postConnect() {
|
||||||
conn.io = bufio.NewReadWriter(
|
conn.io = bufio.NewReadWriter(
|
||||||
bufio.NewReader(conn.sock),
|
bufio.NewReader(conn.sock),
|
||||||
bufio.NewWriter(conn.sock))
|
bufio.NewWriter(conn.sock))
|
||||||
conn.sock.SetTimeout(conn.Timeout * second)
|
|
||||||
go conn.send()
|
go conn.send()
|
||||||
go conn.recv()
|
go conn.recv()
|
||||||
if conn.PingFreq > 0 {
|
if conn.PingFreq > 0 {
|
||||||
|
@ -231,7 +223,7 @@ func (conn *Conn) recv() {
|
||||||
for {
|
for {
|
||||||
s, err := conn.io.ReadString('\n')
|
s, err := conn.io.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.l.Error("irc.recv(): %s", err.String())
|
conn.l.Error("irc.recv(): %s", err.Error())
|
||||||
conn.shutdown()
|
conn.shutdown()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -239,7 +231,7 @@ func (conn *Conn) recv() {
|
||||||
conn.l.Debug("<- %s", s)
|
conn.l.Debug("<- %s", s)
|
||||||
|
|
||||||
if line := parseLine(s); line != nil {
|
if line := parseLine(s); line != nil {
|
||||||
line.Time = time.LocalTime()
|
line.Time = time.Now()
|
||||||
conn.in <- line
|
conn.in <- line
|
||||||
} else {
|
} else {
|
||||||
conn.l.Warn("irc.recv(): problems parsing line:\n %s", s)
|
conn.l.Warn("irc.recv(): problems parsing line:\n %s", s)
|
||||||
|
@ -278,21 +270,21 @@ func (conn *Conn) runLoop() {
|
||||||
// using Hybrid's algorithm to rate limit if conn.Flood is false.
|
// using Hybrid's algorithm to rate limit if conn.Flood is false.
|
||||||
func (conn *Conn) write(line string) {
|
func (conn *Conn) write(line string) {
|
||||||
if !conn.Flood {
|
if !conn.Flood {
|
||||||
if t := conn.rateLimit(int64(len(line))); t != 0 {
|
if t := conn.rateLimit(len(line)); t != 0 {
|
||||||
// sleep for the current line's time value before sending it
|
// sleep for the current line's time value before sending it
|
||||||
conn.l.Debug("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
|
conn.l.Debug("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
|
||||||
float64(t)/float64(second))
|
t.Seconds())
|
||||||
<-time.After(t)
|
<-time.After(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
|
if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
|
||||||
conn.l.Error("irc.send(): %s", err.String())
|
conn.l.Error("irc.send(): %s", err.Error())
|
||||||
conn.shutdown()
|
conn.shutdown()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := conn.io.Flush(); err != nil {
|
if err := conn.io.Flush(); err != nil {
|
||||||
conn.l.Error("irc.send(): %s", err.String())
|
conn.l.Error("irc.send(): %s", err.Error())
|
||||||
conn.shutdown()
|
conn.shutdown()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -300,19 +292,19 @@ func (conn *Conn) write(line string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement Hybrid's flood control algorithm to rate-limit outgoing lines.
|
// Implement Hybrid's flood control algorithm to rate-limit outgoing lines.
|
||||||
func (conn *Conn) rateLimit(chars int64) int64 {
|
func (conn *Conn) rateLimit(chars int) time.Duration {
|
||||||
// Hybrid's algorithm allows for 2 seconds per line and an additional
|
// Hybrid's algorithm allows for 2 seconds per line and an additional
|
||||||
// 1/120 of a second per character on that line.
|
// 1/120 of a second per character on that line.
|
||||||
linetime := 2*second + chars*second/120
|
linetime := 2*time.Second + time.Duration(chars)*time.Second/120
|
||||||
elapsed := time.Nanoseconds() - conn.lastsent
|
elapsed := time.Now().Sub(conn.lastsent)
|
||||||
if conn.badness += linetime - elapsed; conn.badness < 0 {
|
if conn.badness += linetime - elapsed; conn.badness < 0 {
|
||||||
// negative badness times are badness...
|
// negative badness times are badness...
|
||||||
conn.badness = int64(0)
|
conn.badness = 0
|
||||||
}
|
}
|
||||||
conn.lastsent = time.Nanoseconds()
|
conn.lastsent = time.Now()
|
||||||
// If we've sent more than 10 second's worth of lines according to the
|
// If we've sent more than 10 second's worth of lines according to the
|
||||||
// calculation above, then we're at risk of "Excess Flood".
|
// calculation above, then we're at risk of "Excess Flood".
|
||||||
if conn.badness > 10*second {
|
if conn.badness > 10*time.Second {
|
||||||
return linetime
|
return linetime
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"github.com/fluffle/goevent/event"
|
"github.com/fluffle/goevent/event"
|
||||||
"github.com/fluffle/golog/logging"
|
"github.com/fluffle/golog/logging"
|
||||||
"github.com/fluffle/goirc/state"
|
"github.com/fluffle/goirc/state"
|
||||||
"gomock.googlecode.com/hg/gomock"
|
gomock "github.com/dsymonds/gomock/gomock"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -92,8 +92,12 @@ func TestClientAndStateTracking(t *testing.T) {
|
||||||
l := logging.NewMockLogger(ctrl)
|
l := logging.NewMockLogger(ctrl)
|
||||||
st := state.NewMockStateTracker(ctrl)
|
st := state.NewMockStateTracker(ctrl)
|
||||||
|
|
||||||
for n, h := range intHandlers {
|
for n, _ := range intHandlers {
|
||||||
r.EXPECT().AddHandler(h, n)
|
// We can't use EXPECT() here as comparisons of functions are
|
||||||
|
// no longer valid in Go, which causes reflect.DeepEqual to bail.
|
||||||
|
// Instead, ignore the function arg and just ensure that all the
|
||||||
|
// handler names are correctly passed to AddHandler.
|
||||||
|
ctrl.RecordCall(r, "AddHandler", gomock.Any(), []string{n})
|
||||||
}
|
}
|
||||||
c := Client("test", "test", "Testing IRC", r, l)
|
c := Client("test", "test", "Testing IRC", r, l)
|
||||||
|
|
||||||
|
@ -110,8 +114,9 @@ func TestClientAndStateTracking(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OK, while we're here with a mock event registry...
|
// OK, while we're here with a mock event registry...
|
||||||
for n, h := range stHandlers {
|
for n, _ := range stHandlers {
|
||||||
r.EXPECT().AddHandler(h, n)
|
// See above.
|
||||||
|
ctrl.RecordCall(r, "AddHandler", gomock.Any(), []string{n})
|
||||||
}
|
}
|
||||||
c.EnableStateTracking()
|
c.EnableStateTracking()
|
||||||
|
|
||||||
|
@ -128,8 +133,9 @@ func TestClientAndStateTracking(t *testing.T) {
|
||||||
me := c.Me
|
me := c.Me
|
||||||
c.ST = st
|
c.ST = st
|
||||||
st.EXPECT().Wipe()
|
st.EXPECT().Wipe()
|
||||||
for n, h := range stHandlers {
|
for n, _ := range stHandlers {
|
||||||
r.EXPECT().DelHandler(h, n)
|
// See above.
|
||||||
|
ctrl.RecordCall(r, "DelHandler", gomock.Any(), []string{n})
|
||||||
}
|
}
|
||||||
c.DisableStateTracking()
|
c.DisableStateTracking()
|
||||||
if c.st || c.ST != nil || c.Me != me {
|
if c.st || c.ST != nil || c.Me != me {
|
||||||
|
@ -412,8 +418,8 @@ func TestWrite(t *testing.T) {
|
||||||
s.nc.Expect("yo momma")
|
s.nc.Expect("yo momma")
|
||||||
|
|
||||||
// Flood control is disabled -- setUp sets c.Flood = true -- so we should
|
// Flood control is disabled -- setUp sets c.Flood = true -- so we should
|
||||||
// not have set c.badness or c.lastsent at this point.
|
// not have set c.badness at this point.
|
||||||
if c.badness != 0 || c.lastsent != 0 {
|
if c.badness != 0 {
|
||||||
t.Errorf("Flood control used when Flood = true.")
|
t.Errorf("Flood control used when Flood = true.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,8 +427,8 @@ func TestWrite(t *testing.T) {
|
||||||
c.write("she so useless")
|
c.write("she so useless")
|
||||||
s.nc.Expect("she so useless")
|
s.nc.Expect("she so useless")
|
||||||
|
|
||||||
// The lastsent time should have been updated now.
|
// The lastsent time should have been updated very recently...
|
||||||
if c.lastsent == 0 {
|
if time.Now().Sub(c.lastsent) > time.Millisecond {
|
||||||
t.Errorf("Flood control not used when Flood = false.")
|
t.Errorf("Flood control not used when Flood = false.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,24 +455,40 @@ func TestRateLimit(t *testing.T) {
|
||||||
c, s := setUp(t)
|
c, s := setUp(t)
|
||||||
defer s.tearDown()
|
defer s.tearDown()
|
||||||
|
|
||||||
if c.badness != 0 || c.lastsent != 0 {
|
if c.badness != 0 {
|
||||||
t.Errorf("Bad initial values for rate limit variables.")
|
t.Errorf("Bad initial values for rate limit variables.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// badness will still be 0 because lastsent was 0 before rateLimit.
|
// We'll be needing this later...
|
||||||
if l := c.rateLimit(60); l != 0 || c.badness != 0 || c.lastsent == 0 {
|
abs := func(i time.Duration) time.Duration {
|
||||||
t.Errorf("Rate limit variables not updated correctly after rateLimit.")
|
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
|
// 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
|
// 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
|
// 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 1us as a fuzz.
|
// be minimal but it's guaranteed that it won't be zero. Use 10us as a fuzz.
|
||||||
// This seems to be the minimum timer resolution, on my laptop at least...
|
if l := c.rateLimit(60); l != 0 || abs(c.badness - 25*1e8) > 10 * time.Microsecond {
|
||||||
if l := c.rateLimit(60); l != 0 || c.badness - int64(25*1e8) > 1e3 {
|
|
||||||
t.Errorf("Rate limit calculating badness incorrectly.")
|
t.Errorf("Rate limit calculating badness incorrectly.")
|
||||||
}
|
}
|
||||||
// At this point, we can tip over the badness scale, with a bit of help.
|
// At this point, we can tip over the badness scale, with a bit of help.
|
||||||
if l := c.rateLimit(360); l == 80*1e8 || c.badness - int64(105*1e8) > 1e3 {
|
// 720 chars => +8 seconds of badness => 10.5 seconds => ratelimit
|
||||||
|
if l := c.rateLimit(720); l != 8 * time.Second ||
|
||||||
|
abs(c.badness - 105*1e8) > 10 * time.Microsecond {
|
||||||
t.Errorf("Rate limit failed to return correct limiting values.")
|
t.Errorf("Rate limit failed to return correct limiting values.")
|
||||||
|
t.Errorf("l=%d, badness=%d", l, c.badness)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fluffle/goirc/state"
|
"github.com/fluffle/goirc/state"
|
||||||
"gomock.googlecode.com/hg/gomock"
|
gomock "github.com/dsymonds/gomock/gomock"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ type Line struct {
|
||||||
Nick, Ident, Host, Src string
|
Nick, Ident, Host, Src string
|
||||||
Cmd, Raw string
|
Cmd, Raw string
|
||||||
Args []string
|
Args []string
|
||||||
Time *time.Time
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this doesn't copy l.Time (this should be read-only anyway)
|
// NOTE: this doesn't copy l.Time (this should be read-only anyway)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -23,7 +24,7 @@ type mockNetConn struct {
|
||||||
rc chan bool
|
rc chan bool
|
||||||
|
|
||||||
closed bool
|
closed bool
|
||||||
rt, wt int64
|
rt, wt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func MockNetConn(t *testing.T) *mockNetConn {
|
func MockNetConn(t *testing.T) *mockNetConn {
|
||||||
|
@ -96,9 +97,9 @@ func (m *mockNetConn) ExpectNothing() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement net.Conn interface
|
// Implement net.Conn interface
|
||||||
func (m *mockNetConn) Read(b []byte) (int, os.Error) {
|
func (m *mockNetConn) Read(b []byte) (int, error) {
|
||||||
if m.closed {
|
if m.closed {
|
||||||
return 0, os.EINVAL
|
return 0, os.ErrInvalid
|
||||||
}
|
}
|
||||||
l := 0
|
l := 0
|
||||||
select {
|
select {
|
||||||
|
@ -106,14 +107,14 @@ func (m *mockNetConn) Read(b []byte) (int, os.Error) {
|
||||||
l = len(s)
|
l = len(s)
|
||||||
copy(b, s)
|
copy(b, s)
|
||||||
case <-m.closers[mockReadCloser]:
|
case <-m.closers[mockReadCloser]:
|
||||||
return 0, os.EOF
|
return 0, io.EOF
|
||||||
}
|
}
|
||||||
return l, nil
|
return l, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetConn) Write(s []byte) (int, os.Error) {
|
func (m *mockNetConn) Write(s []byte) (int, error) {
|
||||||
if m.closed {
|
if m.closed {
|
||||||
return 0, os.EINVAL
|
return 0, os.ErrInvalid
|
||||||
}
|
}
|
||||||
b := make([]byte, len(s))
|
b := make([]byte, len(s))
|
||||||
copy(b, s)
|
copy(b, s)
|
||||||
|
@ -121,9 +122,9 @@ func (m *mockNetConn) Write(s []byte) (int, os.Error) {
|
||||||
return len(s), nil
|
return len(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetConn) Close() os.Error {
|
func (m *mockNetConn) Close() error {
|
||||||
if m.closed {
|
if m.closed {
|
||||||
return os.EINVAL
|
return os.ErrInvalid
|
||||||
}
|
}
|
||||||
// Shut down *ALL* the goroutines!
|
// Shut down *ALL* the goroutines!
|
||||||
// This will trigger an EOF event in Read() too
|
// This will trigger an EOF event in Read() too
|
||||||
|
@ -142,18 +143,18 @@ func (m *mockNetConn) RemoteAddr() net.Addr {
|
||||||
return &net.IPAddr{net.IPv4(127, 0, 0, 1)}
|
return &net.IPAddr{net.IPv4(127, 0, 0, 1)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetConn) SetTimeout(ns int64) os.Error {
|
func (m *mockNetConn) SetDeadline(t time.Time) error {
|
||||||
m.rt = ns
|
m.rt = t
|
||||||
m.wt = ns
|
m.wt = t
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetConn) SetReadTimeout(ns int64) os.Error {
|
func (m *mockNetConn) SetReadDeadline(t time.Time) error {
|
||||||
m.rt = ns
|
m.rt = t
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockNetConn) SetWriteTimeout(ns int64) os.Error {
|
func (m *mockNetConn) SetWriteDeadline(t time.Time) error {
|
||||||
m.wt = ns
|
m.wt = t
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,8 +125,8 @@ func (ch *Channel) addNick(nk *Nick, cp *ChanPrivs) {
|
||||||
// Disassociates a Nick from a Channel.
|
// Disassociates a Nick from a Channel.
|
||||||
func (ch *Channel) delNick(nk *Nick) {
|
func (ch *Channel) delNick(nk *Nick) {
|
||||||
if _, ok := ch.nicks[nk]; ok {
|
if _, ok := ch.nicks[nk]; ok {
|
||||||
ch.nicks[nk] = nil, false
|
delete(ch.nicks, nk)
|
||||||
ch.lookup[nk.Nick] = nil, false
|
delete(ch.lookup, nk.Nick)
|
||||||
} else {
|
} else {
|
||||||
ch.l.Warn("Channel.delNick(): %s not on %s.", nk.Nick, ch.Name)
|
ch.l.Warn("Channel.delNick(): %s not on %s.", nk.Nick, ch.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gomock "gomock.googlecode.com/hg/gomock"
|
gomock "github.com/dsymonds/gomock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mock of StateTracker interface
|
// Mock of StateTracker interface
|
||||||
|
@ -24,127 +24,127 @@ func NewMockStateTracker(ctrl *gomock.Controller) *MockStateTracker {
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) EXPECT() *_MockStateTrackerRecorder {
|
func (_m *MockStateTracker) EXPECT() *_MockStateTrackerRecorder {
|
||||||
return m.recorder
|
return _m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) NewNick(nick string) *Nick {
|
func (_m *MockStateTracker) NewNick(nick string) *Nick {
|
||||||
ret := m.ctrl.Call(m, "NewNick", nick)
|
ret := _m.ctrl.Call(_m, "NewNick", nick)
|
||||||
ret0, _ := ret[0].(*Nick)
|
ret0, _ := ret[0].(*Nick)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) NewNick(arg0 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) NewNick(arg0 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "NewNick", arg0)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "NewNick", arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) GetNick(nick string) *Nick {
|
func (_m *MockStateTracker) GetNick(nick string) *Nick {
|
||||||
ret := m.ctrl.Call(m, "GetNick", nick)
|
ret := _m.ctrl.Call(_m, "GetNick", nick)
|
||||||
ret0, _ := ret[0].(*Nick)
|
ret0, _ := ret[0].(*Nick)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) GetNick(arg0 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) GetNick(arg0 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "GetNick", arg0)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetNick", arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) ReNick(old string, neu string) {
|
func (_m *MockStateTracker) ReNick(old string, neu string) {
|
||||||
m.ctrl.Call(m, "ReNick", old, neu)
|
_m.ctrl.Call(_m, "ReNick", old, neu)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) ReNick(arg0, arg1 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) ReNick(arg0, arg1 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "ReNick", arg0, arg1)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "ReNick", arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) DelNick(nick string) {
|
func (_m *MockStateTracker) DelNick(nick string) {
|
||||||
m.ctrl.Call(m, "DelNick", nick)
|
_m.ctrl.Call(_m, "DelNick", nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) DelNick(arg0 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) DelNick(arg0 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "DelNick", arg0)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "DelNick", arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) NewChannel(channel string) *Channel {
|
func (_m *MockStateTracker) NewChannel(channel string) *Channel {
|
||||||
ret := m.ctrl.Call(m, "NewChannel", channel)
|
ret := _m.ctrl.Call(_m, "NewChannel", channel)
|
||||||
ret0, _ := ret[0].(*Channel)
|
ret0, _ := ret[0].(*Channel)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) NewChannel(arg0 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) NewChannel(arg0 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "NewChannel", arg0)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "NewChannel", arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) GetChannel(channel string) *Channel {
|
func (_m *MockStateTracker) GetChannel(channel string) *Channel {
|
||||||
ret := m.ctrl.Call(m, "GetChannel", channel)
|
ret := _m.ctrl.Call(_m, "GetChannel", channel)
|
||||||
ret0, _ := ret[0].(*Channel)
|
ret0, _ := ret[0].(*Channel)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) GetChannel(arg0 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) GetChannel(arg0 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "GetChannel", arg0)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "GetChannel", arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) DelChannel(channel string) {
|
func (_m *MockStateTracker) DelChannel(channel string) {
|
||||||
m.ctrl.Call(m, "DelChannel", channel)
|
_m.ctrl.Call(_m, "DelChannel", channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) DelChannel(arg0 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) DelChannel(arg0 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "DelChannel", arg0)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "DelChannel", arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) Me() *Nick {
|
func (_m *MockStateTracker) Me() *Nick {
|
||||||
ret := m.ctrl.Call(m, "Me")
|
ret := _m.ctrl.Call(_m, "Me")
|
||||||
ret0, _ := ret[0].(*Nick)
|
ret0, _ := ret[0].(*Nick)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) Me() *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) Me() *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "Me")
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "Me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) IsOn(channel string, nick string) (*ChanPrivs, bool) {
|
func (_m *MockStateTracker) IsOn(channel string, nick string) (*ChanPrivs, bool) {
|
||||||
ret := m.ctrl.Call(m, "IsOn", channel, nick)
|
ret := _m.ctrl.Call(_m, "IsOn", channel, nick)
|
||||||
ret0, _ := ret[0].(*ChanPrivs)
|
ret0, _ := ret[0].(*ChanPrivs)
|
||||||
ret1, _ := ret[1].(bool)
|
ret1, _ := ret[1].(bool)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) IsOn(arg0, arg1 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) IsOn(arg0, arg1 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "IsOn", arg0, arg1)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "IsOn", arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) Associate(channel *Channel, nick *Nick) *ChanPrivs {
|
func (_m *MockStateTracker) Associate(channel *Channel, nick *Nick) *ChanPrivs {
|
||||||
ret := m.ctrl.Call(m, "Associate", channel, nick)
|
ret := _m.ctrl.Call(_m, "Associate", channel, nick)
|
||||||
ret0, _ := ret[0].(*ChanPrivs)
|
ret0, _ := ret[0].(*ChanPrivs)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) Associate(arg0, arg1 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) Associate(arg0, arg1 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "Associate", arg0, arg1)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "Associate", arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) Dissociate(channel *Channel, nick *Nick) {
|
func (_m *MockStateTracker) Dissociate(channel *Channel, nick *Nick) {
|
||||||
m.ctrl.Call(m, "Dissociate", channel, nick)
|
_m.ctrl.Call(_m, "Dissociate", channel, nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) Dissociate(arg0, arg1 interface{}) *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) Dissociate(arg0, arg1 interface{}) *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "Dissociate", arg0, arg1)
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "Dissociate", arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) Wipe() {
|
func (_m *MockStateTracker) Wipe() {
|
||||||
m.ctrl.Call(m, "Wipe")
|
_m.ctrl.Call(_m, "Wipe")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) Wipe() *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) Wipe() *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "Wipe")
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "Wipe")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockStateTracker) String() string {
|
func (_m *MockStateTracker) String() string {
|
||||||
ret := m.ctrl.Call(m, "String")
|
ret := _m.ctrl.Call(_m, "String")
|
||||||
ret0, _ := ret[0].(string)
|
ret0, _ := ret[0].(string)
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mr *_MockStateTrackerRecorder) String() *gomock.Call {
|
func (_mr *_MockStateTrackerRecorder) String() *gomock.Call {
|
||||||
return mr.mock.ctrl.RecordCall(mr.mock, "String")
|
return _mr.mock.ctrl.RecordCall(_mr.mock, "String")
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,8 +78,8 @@ func (nk *Nick) addChannel(ch *Channel, cp *ChanPrivs) {
|
||||||
// Disassociates a Channel from a Nick.
|
// Disassociates a Channel from a Nick.
|
||||||
func (nk *Nick) delChannel(ch *Channel) {
|
func (nk *Nick) delChannel(ch *Channel) {
|
||||||
if _, ok := nk.chans[ch]; ok {
|
if _, ok := nk.chans[ch]; ok {
|
||||||
nk.chans[ch] = nil, false
|
delete(nk.chans, ch)
|
||||||
nk.lookup[ch.Name] = nil, false
|
delete(nk.lookup, ch.Name)
|
||||||
} else {
|
} else {
|
||||||
nk.l.Warn("Nick.delChannel(): %s not on %s.", nk.Nick, ch.Name)
|
nk.l.Warn("Nick.delChannel(): %s not on %s.", nk.Nick, ch.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,12 +88,12 @@ func (st *stateTracker) ReNick(old, neu string) {
|
||||||
if nk, ok := st.nicks[old]; ok {
|
if nk, ok := st.nicks[old]; ok {
|
||||||
if _, ok := st.nicks[neu]; !ok {
|
if _, ok := st.nicks[neu]; !ok {
|
||||||
nk.Nick = neu
|
nk.Nick = neu
|
||||||
st.nicks[old] = nil, false
|
delete(st.nicks, old)
|
||||||
st.nicks[neu] = nk
|
st.nicks[neu] = nk
|
||||||
for ch, _ := range nk.chans {
|
for ch, _ := range nk.chans {
|
||||||
// We also need to update the lookup maps of all the channels
|
// We also need to update the lookup maps of all the channels
|
||||||
// the nick is on, to keep things in sync.
|
// the nick is on, to keep things in sync.
|
||||||
ch.lookup[old] = nil, false
|
delete(ch.lookup, old)
|
||||||
ch.lookup[neu] = nk
|
ch.lookup[neu] = nk
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,7 +123,7 @@ func (st *stateTracker) delNick(nk *Nick) {
|
||||||
st.l.Error("StateTracker.DelNick(): TRYING TO DELETE ME :-(")
|
st.l.Error("StateTracker.DelNick(): TRYING TO DELETE ME :-(")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
st.nicks[nk.Nick] = nil, false
|
delete(st.nicks, nk.Nick)
|
||||||
for ch, _ := range nk.chans {
|
for ch, _ := range nk.chans {
|
||||||
nk.delChannel(ch)
|
nk.delChannel(ch)
|
||||||
ch.delNick(nk)
|
ch.delNick(nk)
|
||||||
|
@ -165,7 +165,7 @@ func (st *stateTracker) DelChannel(c string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *stateTracker) delChannel(ch *Channel) {
|
func (st *stateTracker) delChannel(ch *Channel) {
|
||||||
st.chans[ch.Name] = nil, false
|
delete(st.chans, ch.Name)
|
||||||
for nk, _ := range ch.nicks {
|
for nk, _ := range ch.nicks {
|
||||||
ch.delNick(nk)
|
ch.delNick(nk)
|
||||||
nk.delChannel(ch)
|
nk.delChannel(ch)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fluffle/golog/logging"
|
"github.com/fluffle/golog/logging"
|
||||||
"gomock.googlecode.com/hg/gomock"
|
gomock "github.com/dsymonds/gomock/gomock"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue