From 0ce0b17911a49e6ff7cec9757f74b3afd26c6634 Mon Sep 17 00:00:00 2001 From: Max Gurela Date: Mon, 23 May 2016 22:39:39 -0400 Subject: [PATCH] irc: test suite enhancement Comes with some tweaks to support tests Daemonizes the ping and timeout threads (they should have been in the first place) --- sopel/irc.py | 6 +- test/test_irc.py | 147 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 test/test_irc.py diff --git a/sopel/irc.py b/sopel/irc.py index 376d70d2c3..d7b04e2e1b 100644 --- a/sopel/irc.py +++ b/sopel/irc.py @@ -60,6 +60,7 @@ def __init__(self, config): self.stack = {} self.ca_certs = ca_certs + self.enabled_capabilities = set() self.hasquit = False self.sending = threading.RLock() @@ -189,7 +190,8 @@ def quit(self, message): def handle_close(self): self.connection_registered = False - self._shutdown() + if hasattr(self, '_shutdown'): + self._shutdown() stderr('Closed!') # This will eventually call asyncore dispatchers close method, which @@ -231,8 +233,10 @@ def handle_connect(self): stderr('Connected.') self.last_ping_time = datetime.now() timeout_check_thread = threading.Thread(target=self._timeout_check) + timeout_check_thread.daemon = True timeout_check_thread.start() ping_thread = threading.Thread(target=self._send_ping) + ping_thread.daemon = True ping_thread.start() def _timeout_check(self): diff --git a/test/test_irc.py b/test/test_irc.py new file mode 100644 index 0000000000..44c22c1495 --- /dev/null +++ b/test/test_irc.py @@ -0,0 +1,147 @@ +# coding=utf8 +"""Tests for message formatting""" +from __future__ import unicode_literals + +import pytest + +import asynchat +import os +import shutil +import socket +import select +import tempfile +import threading +import time +import asyncore + +from sopel import irc +from sopel.tools import stderr, Identifier +import sopel.config as conf + + +HOST = '127.0.0.1' +SERVER_QUIT = 'QUIT' + + +class BasicServer(asyncore.dispatcher): + def __init__(self, address, handler): + asyncore.dispatcher.__init__(self) + self.response_handler = handler + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.bind(address) + self.address = self.socket.getsockname() + self.listen(1) + return + + def handle_accept(self): + # Called when a client connects to our socket + client_info = self.accept() + BasicHandler(sock=client_info[0], handler=self.response_handler) + self.handle_close() + return + + def handle_close(self): + self.close() + +class BasicHandler(asynchat.async_chat): + ac_in_buffer_size = 512 + ac_out_buffer_size = 512 + + def __init__(self, sock, handler): + self.received_data = [] + asynchat.async_chat.__init__(self, sock) + self.handler_function = handler + self.set_terminator(b'\n') + return + + def collect_incoming_data(self, data): + self.received_data.append(data.decode('utf-8')) + + def found_terminator(self): + self._process_command() + + def _process_command(self): + command = ''.join(self.received_data) + response = self.handler_function(self, command) + self.push(':fake.server {}\n'.format(response).encode()) + self.received_data = [] + + +def start_server(rpl_function=None): + def rpl_func(msg): + print(msg) + return msg + + if rpl_function is None: + rpl_function = rpl_func + + address = ('localhost', 0) # let the kernel give us a port + server = BasicServer(address, rpl_function) + return server + + +@pytest.fixture +def bot(request): + cfg_dir = tempfile.mkdtemp() + print(cfg_dir) + filename = tempfile.mkstemp(dir=cfg_dir)[1] + os.mkdir(os.path.join(cfg_dir, 'modules')) + def fin(): + print('teardown config file') + shutil.rmtree(cfg_dir) + request.addfinalizer(fin) + + def gen(data): + with open(filename, 'w') as fileo: + fileo.write(data) + cfg = conf.Config(filename) + irc_bot = irc.Bot(cfg) + irc_bot.config = cfg + return irc_bot + + return gen + + +def test_bot_init(bot): + test_bot = bot( + '[core]\n' + 'owner=Baz\n' + 'nick=Foo\n' + 'user=Bar\n' + 'name=Sopel\n' + ) + assert test_bot.nick == Identifier('Foo') + assert test_bot.user == 'Bar' + assert test_bot.name == 'Sopel' + + +def basic_irc_replies(server, msg): + if msg.startswith('NICK'): + return '001 Foo :Hello' + elif msg.startswith('USER'): + # Quit here because good enough + server.close() + elif msg.startswith('PING'): + return 'PONG{}'.format(msg.replace('PING','',1)) + elif msg.startswith('CAP'): + return 'CAP * :' + elif msg.startswith('QUIT'): + server.close() + else: + return '421 {} :Unknown command'.format(msg) + + +def test_bot_connect(bot): + test_bot = bot( + '[core]\n' + 'owner=Baz\n' + 'nick=Foo\n' + 'user=Bar\n' + 'name=Sopel\n' + 'host=127.0.0.1\n' + 'timeout=10\n' + ) + s = start_server(basic_irc_replies) + + # Do main run + test_bot.run(HOST, s.address[1])