574 lines
12 KiB
Go
574 lines
12 KiB
Go
package hbot
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func ExampleParseMessage() {
|
|
message := ParseMessage("JOIN #help")
|
|
|
|
fmt.Println(message.Params[0])
|
|
|
|
// Output: #help
|
|
}
|
|
|
|
func ExampleMessage_String() {
|
|
message := &Message{
|
|
Prefix: Prefix{
|
|
Name: "sorcix",
|
|
User: "sorcix",
|
|
Host: "myhostname",
|
|
},
|
|
Command: "PRIVMSG",
|
|
Params: []string{"This is an example!"},
|
|
}
|
|
|
|
fmt.Println(message.String())
|
|
|
|
// Output: :sorcix!sorcix@myhostname PRIVMSG :This is an example!
|
|
}
|
|
|
|
var messageTests = [...]*struct {
|
|
parsed *Message
|
|
rawMessage string
|
|
rawPrefix string
|
|
hostmask bool // Is it very clear that the prefix is a hostname?
|
|
server bool // Is the prefix a servername?
|
|
}{
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "syrk",
|
|
User: "kalt",
|
|
Host: "millennium.stealth.net",
|
|
},
|
|
Command: "QUIT",
|
|
Params: []string{"Gone to have lunch"},
|
|
},
|
|
rawMessage: ":syrk!kalt@millennium.stealth.net QUIT :Gone to have lunch",
|
|
rawPrefix: "syrk!kalt@millennium.stealth.net",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "Trillian",
|
|
},
|
|
Command: "SQUIT",
|
|
Params: []string{"cm22.eng.umd.edu", "Server out of control"},
|
|
},
|
|
rawMessage: ":Trillian SQUIT cm22.eng.umd.edu :Server out of control",
|
|
rawPrefix: "Trillian",
|
|
server: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "WiZ",
|
|
User: "jto",
|
|
Host: "tolsun.oulu.fi",
|
|
},
|
|
Command: "JOIN",
|
|
Params: []string{"#Twilight_zone"},
|
|
},
|
|
rawMessage: ":WiZ!jto@tolsun.oulu.fi JOIN #Twilight_zone",
|
|
rawPrefix: "WiZ!jto@tolsun.oulu.fi",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "WiZ",
|
|
User: "jto",
|
|
Host: "tolsun.oulu.fi",
|
|
},
|
|
Command: "PART",
|
|
Params: []string{"#playzone", "I lost"},
|
|
},
|
|
rawMessage: ":WiZ!jto@tolsun.oulu.fi PART #playzone :I lost",
|
|
rawPrefix: "WiZ!jto@tolsun.oulu.fi",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "WiZ",
|
|
User: "jto",
|
|
Host: "tolsun.oulu.fi",
|
|
},
|
|
Command: "MODE",
|
|
Params: []string{"#eu-opers", "-l"},
|
|
},
|
|
rawMessage: ":WiZ!jto@tolsun.oulu.fi MODE #eu-opers -l",
|
|
rawPrefix: "WiZ!jto@tolsun.oulu.fi",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Command: "MODE",
|
|
Params: []string{"&oulu", "+b", "*!*@*.edu", "+e", "*!*@*.bu.edu"},
|
|
},
|
|
rawMessage: "MODE &oulu +b *!*@*.edu +e *!*@*.bu.edu",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Command: "PRIVMSG",
|
|
Params: []string{"#channel", "Message with :colons!"},
|
|
},
|
|
rawMessage: "PRIVMSG #channel :Message with :colons!",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "irc.vives.lan",
|
|
},
|
|
Command: "251",
|
|
Params: []string{"test", "There are 2 users and 0 services on 1 servers"},
|
|
},
|
|
rawMessage: ":irc.vives.lan 251 test :There are 2 users and 0 services on 1 servers",
|
|
rawPrefix: "irc.vives.lan",
|
|
server: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "irc.vives.lan",
|
|
},
|
|
Command: "376",
|
|
Params: []string{"test", "End of MOTD command"},
|
|
},
|
|
rawMessage: ":irc.vives.lan 376 test :End of MOTD command",
|
|
rawPrefix: "irc.vives.lan",
|
|
server: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "irc.vives.lan",
|
|
},
|
|
Command: "250",
|
|
Params: []string{"test", "Highest connection count: 1 (1 connections received)"},
|
|
},
|
|
rawMessage: ":irc.vives.lan 250 test :Highest connection count: 1 (1 connections received)",
|
|
rawPrefix: "irc.vives.lan",
|
|
server: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "sorcix",
|
|
User: "~sorcix",
|
|
Host: "sorcix.users.quakenet.org",
|
|
},
|
|
Command: "PRIVMSG",
|
|
Params: []string{"#viveslan", "\001ACTION is testing CTCP messages!\001"},
|
|
},
|
|
rawMessage: ":sorcix!~sorcix@sorcix.users.quakenet.org PRIVMSG #viveslan :\001ACTION is testing CTCP messages!\001",
|
|
rawPrefix: "sorcix!~sorcix@sorcix.users.quakenet.org",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "sorcix",
|
|
User: "~sorcix",
|
|
Host: "sorcix.users.quakenet.org",
|
|
},
|
|
Command: "NOTICE",
|
|
Params: []string{"midnightfox", "\001PONG 1234567890\001"},
|
|
},
|
|
rawMessage: ":sorcix!~sorcix@sorcix.users.quakenet.org NOTICE midnightfox :\001PONG 1234567890\001",
|
|
rawPrefix: "sorcix!~sorcix@sorcix.users.quakenet.org",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "a",
|
|
User: "b",
|
|
Host: "c",
|
|
},
|
|
Command: "QUIT",
|
|
},
|
|
rawMessage: ":a!b@c QUIT",
|
|
rawPrefix: "a!b@c",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "a",
|
|
User: "b",
|
|
},
|
|
Command: "PRIVMSG",
|
|
Params: []string{"message"},
|
|
},
|
|
rawMessage: ":a!b PRIVMSG message",
|
|
rawPrefix: "a!b",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "a",
|
|
Host: "c",
|
|
},
|
|
Command: "NOTICE",
|
|
Params: []string{":::Hey!"},
|
|
},
|
|
rawMessage: ":a@c NOTICE ::::Hey!",
|
|
rawPrefix: "a@c",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "nick",
|
|
},
|
|
Command: "PRIVMSG",
|
|
Params: []string{"$@", "This message contains a\ttab!"},
|
|
},
|
|
rawMessage: ":nick PRIVMSG $@ :This message contains a\ttab!",
|
|
rawPrefix: "nick",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Command: "TEST",
|
|
Params: []string{"$@", "", "param", "Trailing"},
|
|
},
|
|
rawMessage: "TEST $@ param Trailing",
|
|
},
|
|
{
|
|
rawMessage: ": PRIVMSG test :Invalid message with empty prefix.",
|
|
rawPrefix: "",
|
|
},
|
|
{
|
|
rawMessage: ": PRIVMSG test :Invalid message with space prefix",
|
|
rawPrefix: " ",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Command: "TOPIC",
|
|
Params: []string{"#foo", ""},
|
|
},
|
|
rawMessage: "TOPIC #foo :",
|
|
rawPrefix: "",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Prefix: Prefix{
|
|
Name: "name",
|
|
User: "user",
|
|
Host: "example.org",
|
|
},
|
|
Command: "PRIVMSG",
|
|
Params: []string{"#test", "Message with spaces at the end! "},
|
|
},
|
|
rawMessage: ":name!user@example.org PRIVMSG #test :Message with spaces at the end! ",
|
|
rawPrefix: "name!user@example.org",
|
|
hostmask: true,
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Command: "PASS",
|
|
Params: []string{"oauth:token_goes_here"},
|
|
},
|
|
rawMessage: "PASS oauth:token_goes_here",
|
|
rawPrefix: "",
|
|
},
|
|
{
|
|
parsed: &Message{
|
|
Command: "PRIVMSG",
|
|
Params: []string{"#some:channel", "http://example.com"},
|
|
},
|
|
rawMessage: "PRIVMSG #some:channel http://example.com",
|
|
rawPrefix: "",
|
|
},
|
|
}
|
|
|
|
// -----
|
|
// PREFIX
|
|
// -----
|
|
|
|
func TestPrefix_IsHostmask(t *testing.T) {
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no prefix
|
|
if test.parsed == nil || test.parsed.Prefix.Empty() {
|
|
continue
|
|
}
|
|
|
|
if test.hostmask && !test.parsed.Prefix.IsHostmask() {
|
|
t.Errorf("Prefix %d should be recognized as a hostmask!", i)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestPrefix_IsServer(t *testing.T) {
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no prefix
|
|
if test.parsed == nil || test.parsed.Prefix.Empty() {
|
|
continue
|
|
}
|
|
|
|
if test.server && !test.parsed.Prefix.IsServer() {
|
|
t.Errorf("Prefix %d should be recognized as a server!", i)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestPrefix_String(t *testing.T) {
|
|
var s string
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no prefix
|
|
if test.parsed == nil || test.parsed.Prefix.Empty() {
|
|
continue
|
|
}
|
|
|
|
// Convert the prefix
|
|
s = test.parsed.Prefix.String()
|
|
|
|
// Result should be the same as the value in rawMessage.
|
|
if s != test.rawPrefix {
|
|
t.Errorf("Failed to stringify prefix %d:", i)
|
|
t.Logf("Output: %s", s)
|
|
t.Logf("Expected: %s", test.rawPrefix)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPrefix_Len(t *testing.T) {
|
|
var l int
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no prefix
|
|
if test.parsed == nil || test.parsed.Prefix.Empty() {
|
|
continue
|
|
}
|
|
|
|
l = test.parsed.Prefix.Len()
|
|
|
|
// Result should be the same as the value in rawMessage.
|
|
if l != len(test.rawPrefix) {
|
|
t.Errorf("Failed to calculate prefix length %d:", i)
|
|
t.Logf("Output: %d", l)
|
|
t.Logf("Expected: %d", len(test.rawPrefix))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParsePrefix(t *testing.T) {
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no prefix
|
|
if test.parsed == nil || test.parsed.Prefix.Empty() {
|
|
continue
|
|
}
|
|
|
|
// Parse the prefix
|
|
p := ParsePrefix(test.rawPrefix)
|
|
|
|
// Result struct should be the same as the value in parsed.
|
|
if p != test.parsed.Prefix {
|
|
t.Errorf("Failed to parse prefix %d:", i)
|
|
t.Logf("Output: %#v", p)
|
|
t.Logf("Expected: %#v", test.parsed.Prefix)
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// MESSAGE
|
|
// -----
|
|
|
|
func TestMessage_String(t *testing.T) {
|
|
var s string
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no valid struct
|
|
if test.parsed == nil {
|
|
continue
|
|
}
|
|
|
|
// Convert the prefix
|
|
s = test.parsed.String()
|
|
|
|
// Result should be the same as the value in rawMessage.
|
|
if s != test.rawMessage {
|
|
t.Errorf("Failed to stringify message %d:", i)
|
|
t.Logf("Output: %s", s)
|
|
t.Logf("Expected: %s", test.rawMessage)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMessage_Len(t *testing.T) {
|
|
var l int
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip tests that have no valid struct
|
|
if test.parsed == nil {
|
|
continue
|
|
}
|
|
|
|
l = test.parsed.Len()
|
|
|
|
// Result should be the same as the value in rawMessage.
|
|
if l != len(test.rawMessage) {
|
|
t.Errorf("Failed to calculate message length %d:", i)
|
|
t.Logf("Output: %d", l)
|
|
t.Logf("Expected: %d", len(test.rawMessage))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMessage_Param(t *testing.T) {
|
|
msg := &Message{
|
|
Params: []string{"foo", "bar", "baz"},
|
|
}
|
|
|
|
tests := [...]struct {
|
|
i int
|
|
value string
|
|
}{
|
|
{i: 0, value: "foo"},
|
|
{i: 2, value: "baz"},
|
|
{i: 99, value: ""},
|
|
{i: -1, value: ""},
|
|
}
|
|
for i, test := range tests {
|
|
p := msg.Param(test.i)
|
|
if p != test.value {
|
|
t.Errorf("Failed to get parameter: %d", i)
|
|
t.Logf("Output: %s", p)
|
|
t.Logf("Expected: %s", test.value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseMessage(t *testing.T) {
|
|
var p *Message
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Parse the prefix
|
|
p = ParseMessage(test.rawMessage)
|
|
|
|
// Result struct should be the same as the value in parsed.
|
|
if !reflect.DeepEqual(p, test.parsed) {
|
|
t.Errorf("Failed to parse message %d:", i)
|
|
t.Logf("Output: %#v", p)
|
|
t.Logf("Expected: %#v", test.parsed)
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// MESSAGE DECODE -> ENCODE
|
|
// -----
|
|
|
|
func TestMessageDecodeEncode(t *testing.T) {
|
|
var (
|
|
p *Message
|
|
s string
|
|
)
|
|
|
|
for i, test := range messageTests {
|
|
|
|
// Skip invalid messages
|
|
if test.parsed == nil {
|
|
continue
|
|
}
|
|
|
|
// Decode the message, then encode it again.
|
|
p = ParseMessage(test.rawMessage)
|
|
s = p.String()
|
|
|
|
// Result struct should be the same as the original.
|
|
if s != test.rawMessage {
|
|
t.Errorf("Message %d failed decode-encode sequence!", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----
|
|
// BENCHMARK
|
|
// -----
|
|
|
|
func BenchmarkPrefix_String_short(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
prefix := new(Prefix)
|
|
prefix.Name = "Namename"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = prefix.String()
|
|
}
|
|
}
|
|
func BenchmarkPrefix_String_long(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
prefix := new(Prefix)
|
|
prefix.Name = "Namename"
|
|
prefix.User = "Username"
|
|
prefix.Host = "Hostname"
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = prefix.String()
|
|
}
|
|
}
|
|
func BenchmarkParsePrefix_short(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
ParsePrefix("Namename")
|
|
}
|
|
}
|
|
func BenchmarkParsePrefix_long(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
ParsePrefix("Namename!Username@Hostname")
|
|
}
|
|
}
|
|
func BenchmarkMessage_String(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_ = messageTests[0].parsed.String()
|
|
}
|
|
}
|
|
func BenchmarkParseMessage_short(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
ParseMessage("COMMAND arg1 :Message\r\n")
|
|
}
|
|
}
|
|
func BenchmarkParseMessage_medium(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
ParseMessage(":Namename COMMAND arg6 arg7 :Message message message\r\n")
|
|
}
|
|
}
|
|
func BenchmarkParseMessage_long(b *testing.B) {
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
ParseMessage(":Namename!username@hostname COMMAND arg1 arg2 arg3 arg4 arg5 arg6 arg7 :Message message message message message\r\n")
|
|
}
|
|
}
|