diff --git a/irc/ircconn_test.go b/irc/ircconn_test.go new file mode 100644 index 00000000..13a9930c --- /dev/null +++ b/irc/ircconn_test.go @@ -0,0 +1,135 @@ +// Copyright (c) 2020 Shivaram Lingamneni +// released under the MIT license + +package irc + +import ( + "io" + "math/rand" + "net" + "reflect" + "testing" + "time" + + "github.com/oragono/oragono/irc/utils" +) + +// mockConn is a fake net.Conn / io.Reader that yields len(counts) lines, +// each consisting of counts[i] 'a' characters and a terminating '\n' +type mockConn struct { + counts []int +} + +func min(i, j int) (m int) { + if i < j { + return i + } else { + return j + } +} + +func (c *mockConn) Read(b []byte) (n int, err error) { + for len(b) > 0 { + if len(c.counts) == 0 { + return n, io.EOF + } + if c.counts[0] == 0 { + b[0] = '\n' + c.counts = c.counts[1:] + b = b[1:] + n += 1 + continue + } + size := min(c.counts[0], len(b)) + for i := 0; i < size; i++ { + b[i] = 'a' + } + c.counts[0] -= size + b = b[size:] + n += size + } + return n, nil +} + +func (c *mockConn) Write(b []byte) (n int, err error) { + return +} + +func (c *mockConn) Close() error { + c.counts = nil + return nil +} + +func (c *mockConn) LocalAddr() net.Addr { + return nil +} + +func (c *mockConn) RemoteAddr() net.Addr { + return nil +} + +func (c *mockConn) SetDeadline(t time.Time) error { + return nil +} + +func (c *mockConn) SetReadDeadline(t time.Time) error { + return nil +} + +func (c *mockConn) SetWriteDeadline(t time.Time) error { + return nil +} + +func newMockConn(counts []int) *utils.WrappedConn { + cpCounts := make([]int, len(counts)) + copy(cpCounts, counts) + c := &mockConn{ + counts: cpCounts, + } + return &utils.WrappedConn{ + Conn: c, + } +} + +// construct a mock reader with some number of \n-terminated lines, +// verify that IRCStreamConn can read and split them as expected +func doLineReaderTest(counts []int, t *testing.T) { + c := newMockConn(counts) + r := NewIRCStreamConn(c) + var readCounts []int + for { + line, err := r.ReadLine() + if err == nil { + readCounts = append(readCounts, len(line)) + } else if err == io.EOF { + break + } else { + panic(err) + } + } + + if !reflect.DeepEqual(counts, readCounts) { + t.Errorf("expected %#v, got %#v", counts, readCounts) + } +} + +const ( + maxMockReaderLen = 100 + maxMockReaderLineLen = 4096 + 511 +) + +func TestLineReader(t *testing.T) { + counts := []int{44, 428, 3, 0, 200, 2000, 0, 4044, 33, 3, 2, 1, 0, 1, 2, 3, 48, 555} + doLineReaderTest(counts, t) + + // fuzz + r := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < 1000; i++ { + countsLen := r.Intn(maxMockReaderLen) + 1 + counts := make([]int, countsLen) + for i := 0; i < countsLen; i++ { + counts[i] = r.Intn(maxMockReaderLineLen) + } + doLineReaderTest(counts, t) + } +}