# coding: utf-8 import asyncio import copy import selectors import functools import os import socket try: import ssl except ImportError: ssl = None import sys import unittest import asynctest class Selector_TestCase(unittest.TestCase): def setUp(self): asynctest.selector.FileDescriptor.next_fd = 0 class Test_FileDescriptor(Selector_TestCase): def test_is_an_int(self): self.assertIsInstance(asynctest.selector.FileDescriptor(), int) def test_init_increments_value(self): self.assertEqual(0, asynctest.selector.FileDescriptor()) self.assertEqual(1, asynctest.selector.FileDescriptor()) self.assertNotEqual(asynctest.selector.FileDescriptor(), asynctest.selector.FileDescriptor()) def test_init_increments_value_with_fixed_value(self): self.assertEqual(5, asynctest.selector.FileDescriptor(5)) self.assertEqual(6, asynctest.selector.FileDescriptor()) class Test_FileMock(Selector_TestCase): def test_fileno_returns_FileDescriptor(self): self.assertIsInstance(asynctest.selector.FileMock().fileno(), asynctest.selector.FileDescriptor) class Test_SocketMock(Selector_TestCase): def test_is_socket(self): self.assertIsInstance(asynctest.selector.SocketMock(), socket.socket) if ssl: class Test_SSLSocketMock(Selector_TestCase): def test_is_ssl_socket(self): self.assertIsInstance(asynctest.selector.SSLSocketMock(), ssl.SSLSocket) def _mock_register(selector, key): def register(fileobj, events, data=None): selector.get_map.return_value[key.fd] = key return key return register def _mock_unregister(selector, key): def unregister(fileobj): del selector.get_map.return_value[key.fd] return key return unregister def selector_subtest(method): @functools.wraps(method) def wrapper(self): with self.subTest(test='without_selector'): method(self, asynctest.selector.TestSelector(), None) with self.subTest(test='with_selector'): mock = unittest.mock.Mock(selectors.BaseSelector) mock.get_map.return_value = mock._fd_to_key = {} method(self, asynctest.selector.TestSelector(mock), mock) return wrapper class Test_TestSelector(Selector_TestCase): @selector_subtest def test_register_mock(self, selector, selector_mock): mock = asynctest.selector.FileMock() key = selector.register(mock, selectors.EVENT_READ, "data") self.assertEqual(key, selector.get_map()[mock]) if selector_mock: self.assertFalse(selector_mock.register.called) @selector_subtest def test_register_fileno(self, selector, selector_mock): with open(os.devnull, 'r') as devnull: if selector_mock: returned_key = selectors.SelectorKey( devnull, devnull.fileno(), selectors.EVENT_READ, "data" ) selector_mock.register.side_effect = _mock_register(selector_mock, returned_key) key = selector.register(devnull, selectors.EVENT_READ, "data") self.assertEqual(key, selector.get_map()[devnull]) if selector_mock: selector_mock.register.assert_called_with(devnull, selectors.EVENT_READ, "data") @selector_subtest def test_unregister_mock(self, selector, selector_mock): mock = asynctest.selector.FileMock() selector.register(mock, selectors.EVENT_READ, "data") selector.unregister(mock) self.assertNotIn(mock, selector.get_map()) self.assertNotIn(mock.fileno(), selector.get_map()) if selector_mock: self.assertFalse(selector_mock.unregister.called) @selector_subtest def test_unregister_fileno(self, selector, selector_mock): with open(os.devnull, 'r') as devnull: if selector_mock: key = selectors.SelectorKey(devnull, devnull.fileno(), selectors.EVENT_READ, "data") selector_mock.register.side_effect = _mock_register(selector_mock, key) selector_mock.unregister.side_effect = _mock_unregister(selector_mock, key) selector.register(devnull, selectors.EVENT_READ, "data") selector.unregister(devnull) self.assertNotIn(devnull, selector.get_map()) self.assertNotIn(devnull.fileno(), selector.get_map()) @selector_subtest def test_modify_mock(self, selector, selector_mock): mock = asynctest.selector.FileMock() original_key = selector.register(mock, selectors.EVENT_READ, "data") # modify may update the original key, keep a copy original_key = copy.copy(original_key) RW = selectors.EVENT_READ | selectors.EVENT_WRITE key = selector.modify(mock, RW, "data") self.assertNotEqual(original_key, key) self.assertEqual(key, selector.get_map()[mock]) @selector_subtest def test_modify_fileno(self, selector, selector_mock): with open(os.devnull, 'r') as devnull: if selector_mock: returned_key = selectors.SelectorKey( devnull, devnull.fileno(), selectors.EVENT_READ, "data2" ) selector_mock.modify.side_effect = _mock_register(selector_mock, returned_key) original_key = selector.register(devnull, selectors.EVENT_READ, "data") # modify may update the original key, keep a copy original_key = copy.copy(original_key) key = selector.modify(devnull, selectors.EVENT_READ, "data2") self.assertNotEqual(original_key, key) self.assertEqual(key, selector.get_map()[devnull]) if selector_mock: selector_mock.modify.assert_called_with(devnull, selectors.EVENT_READ, "data2") @selector_subtest def test_modify_fd(self, selector, selector_mock): fd = 1 if selector_mock: returned_key = selectors.SelectorKey(fd, fd, selectors.EVENT_READ, "data2") selector_mock.modify.side_effect = _mock_register(selector_mock, returned_key) original_key = selector.register(fd, selectors.EVENT_READ, "data") original_key = copy.copy(original_key) key = selector.modify(fd, selectors.EVENT_READ, "data2") self.assertNotEqual(original_key, key) self.assertEqual(key, selector.get_map()[fd]) if selector_mock: selector_mock.modify.assert_called_with(fd, selectors.EVENT_READ, "data2") @selector_subtest def test_modify_but_selector_raises(self, selector, selector_mock): if not selector_mock: return exception = RuntimeError() selector_mock.modify.side_effect = exception with open(os.devnull, 'r') as devnull: selector.register(devnull, selectors.EVENT_READ, "data") with self.assertRaises(type(exception)) as ctx: selector.modify(devnull, selectors.EVENT_READ, "data2") self.assertIs(exception, ctx.exception) self.assertNotIn(devnull, selector.get_map()) @selector_subtest def test_select(self, selector, selector_mock): if selector_mock: selector_mock.select.return_value = ["ProbeValue"] self.assertEqual(["ProbeValue"], selector.select(5)) selector_mock.select.assert_called_with(5) else: self.assertEqual([], selector.select()) @selector_subtest def test_close(self, selector, selector_mock): if not selector_mock: return selector.close() selector_mock.close.assert_called_with() class Test_set_read_write_ready(Selector_TestCase): def setUp(self): super().setUp() self.loop = asyncio.new_event_loop() self.loop._selector = asynctest.selector.TestSelector(self.loop._selector) self.addCleanup(self.loop.close) self.mock = asynctest.selector.FileMock() # Older versions of asyncio may complain with PYTHONASYNCIODEBUG=1 if sys.version_info < (3, 5): asyncio.set_event_loop(self.loop) def test_nothing_scheduled(self): # nothing will happen (no exception) for mode in ('read', 'write'): with self.subTest(mode=mode): getattr(asynctest.selector, 'set_{}_ready'.format(mode))(self.mock, self.loop) self.loop.run_until_complete(asyncio.sleep(0, loop=self.loop)) def test_callback_scheduled(self): for mode in ('read', 'write'): with self.subTest(mode=mode): future = asyncio.Future(loop=self.loop) callback_mock = unittest.mock.Mock() # We need at least two iterations of the loop self.loop.call_soon(self.loop.call_soon, future.set_result, None) getattr(self.loop, 'add_{}er'.format(mode.strip('e')))(self.mock, callback_mock) getattr(asynctest.selector, 'set_{}_ready'.format(mode))(self.mock, self.loop) self.loop.run_until_complete(future) callback_mock.assert_called_with() def test_callback_scheduled_during_current_iteration(self): for mode in ('read', 'write'): with self.subTest(mode=mode): future = asyncio.Future(loop=self.loop) callback_mock = unittest.mock.Mock() # We need at least two iterations of the loop self.loop.call_soon(self.loop.call_soon, future.set_result, None) self.loop.call_soon(getattr(self.loop, 'add_{}er'.format(mode.strip('e'))), self.mock, callback_mock) getattr(asynctest.selector, 'set_{}_ready'.format(mode))(self.mock, self.loop) self.loop.run_until_complete(future) callback_mock.assert_called_with() @unittest.mock.patch.dict('asynctest._fail_on.DEFAULTS', clear=True, active_selector_callbacks=True) class Test_fail_on_active_selector_callbacks(Selector_TestCase): def test_passes_without_callbacks_set(self): class TestCase(asynctest.TestCase): def runTest(self): pass TestCase().debug() def test_passes_when_no_callbacks_left(self): class TestCase(asynctest.TestCase): def runTest(self): mock = asynctest.selector.FileMock() self.loop.add_reader(mock, lambda: None) self.loop.remove_reader(mock) TestCase().debug() def test_events_watched_outside_test_are_ignored(self): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: mock = asynctest.selector.FileMock() loop.add_reader(mock, lambda: None) self.addCleanup(loop.remove_reader, mock) class TestCase(asynctest.TestCase): use_default_loop = False def runTest(self): pass TestCase().debug() finally: loop.close() asyncio.set_event_loop(None) def test_fail_on_active_selector_callbacks_on_mock_files(self): class TestCase(asynctest.TestCase): def runTest(self): mock = asynctest.selector.FileMock() self.loop.add_reader(mock, lambda: None) # it's too late to check that during cleanup self.addCleanup(self.loop.remove_reader, mock) with self.assertRaisesRegex(AssertionError, "some events watched " "during the tests were not removed"): TestCase().debug() def test_fail_on_original_selector_callback(self): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: with unittest.mock.patch.object(loop, "_selector") as mock: class TestCase(asynctest.TestCase): use_default_loop = True def runTest(self): # add a dummy event handle = asyncio.Handle(lambda: None, (), self.loop) key = selectors.SelectorKey(1, 1, selectors.EVENT_READ, (handle, None)) mock.get_map.return_value = {1: key} with self.assertRaisesRegex(AssertionError, "some events watched during the " "tests were not removed"): TestCase().debug() finally: loop.close() asyncio.set_event_loop(None)