Add message parser and write first actual test.

This commit is contained in:
Valentin Lorentz 2015-12-19 08:43:45 +01:00
parent 7568e0d5d3
commit d99f8b39dc
4 changed files with 78 additions and 2 deletions

View File

@ -1,6 +1,8 @@
import socket
import unittest
from .irc_utils import message_parser
class _IrcTestCase(unittest.TestCase):
controllerClass = None # Will be set by __main__.py
@ -22,7 +24,9 @@ class ClientTestCase(_IrcTestCase):
def acceptClient(self):
"""Make the server accept a client connection. Blocking."""
(self.conn, addr) = self.server.accept()
self.conn_file = self.conn.makefile()
self.conn_file = self.conn.makefile(newline='\r\n')
def getLine(self):
return self.conn_file.readline().strip()
def getMessage(self):
return message_parser.parse_message(self.conn_file.readline())

View File

@ -1,4 +1,5 @@
from irctest.cases import ClientTestCase
from irctest.irc_utils.message_parser import Message
class CapTestCase(ClientTestCase):
def testSendCap(self):
@ -9,4 +10,9 @@ class CapTestCase(ClientTestCase):
authentication=None,
)
self.acceptClient()
print(self.getLine())
m = self.getMessage()
self.assertEqual(m.command, 'CAP',
'First message is not CAP LS.')
self.assertEqual(m.subcommand, 'LS',
'First message is not CAP LS.')
self.assertIn(m.params, ([], ['302'])) # IRCv3.1 or IRVv3.2

View File

View File

@ -0,0 +1,66 @@
import re
import collections
import supybot.utils
# http://ircv3.net/specs/core/message-tags-3.2.html#escaping-values
TAG_ESCAPE = [
('\\', '\\\\'), # \ -> \\
(' ', r'\s'),
(';', r'\:'),
('\r', r'\r'),
('\n', r'\n'),
]
unescape_tag_value = supybot.utils.str.MultipleReplacer(
dict(map(lambda x:(x[1],x[0]), TAG_ESCAPE)))
# TODO: validate host
tag_key_validator = re.compile('(\S+/)?[a-zA-Z0-9-]+')
def parse_tags(s):
tags = {}
for tag in s.split(';'):
if '=' not in tag:
tags[tag] = None
else:
(key, value) = tag.split('=', 1)
assert tag_key_validator.match(key), \
'Invalid tag key: {}'.format(key)
tags[key] = unescape_tag_value(value)
return tags
Message = collections.namedtuple('Message',
'tags prefix command subcommand params')
def parse_message(s):
"""Parse a message according to
http://tools.ietf.org/html/rfc1459#section-2.3.1
and
http://ircv3.net/specs/core/message-tags-3.2.html"""
assert s.endswith('\r\n'), 'Message does not end with CR LF'
s = s[0:-2]
if ' :' in s:
(other_tokens, trailing_param) = s.split(' :')
tokens = list(filter(bool, other_tokens.split(' '))) + [trailing_param]
else:
tokens = list(filter(bool, s.split(' ')))
if tokens[0].startswith('@'):
tags = parse_tags(tokens.pop(0))
else:
tags = []
if tokens[0].startswith(':'):
prefix = tokens.pop(0)
else:
prefix = None
command = tokens.pop(0)
if command == 'CAP':
subcommand = tokens.pop(0)
else:
subcommand = None
params = tokens
return Message(
tags=tags,
prefix=prefix,
command=command,
subcommand=subcommand,
params=params,
)