test_windows_events.py 5.2 KB
Newer Older
1 2 3
import os
import sys
import unittest
4
from unittest import mock
5 6 7 8

if sys.platform != 'win32':
    raise unittest.SkipTest('Windows only')

9 10
import _winapi

11
import asyncio
12
from asyncio import _overlapped
13
from asyncio import test_utils
14
from asyncio import windows_events
15 16


17
class UpperProto(asyncio.Protocol):
18 19 20 21 22 23 24 25 26 27 28 29 30
    def __init__(self):
        self.buf = []

    def connection_made(self, trans):
        self.trans = trans

    def data_received(self, data):
        self.buf.append(data)
        if b'\n' in data:
            self.trans.write(b''.join(self.buf).upper())
            self.trans.close()


31
class ProactorTests(test_utils.TestCase):
32 33

    def setUp(self):
34
        self.loop = asyncio.ProactorEventLoop()
35
        self.set_event_loop(self.loop)
36 37 38

    def test_close(self):
        a, b = self.loop._socketpair()
39
        trans = self.loop._make_socket_transport(a, asyncio.Protocol())
40
        f = asyncio.ensure_future(self.loop.sock_recv(b, 100))
41 42 43
        trans.close()
        self.loop.run_until_complete(f)
        self.assertEqual(f.result(), b'')
44
        b.close()
45 46 47 48 49

    def test_double_bind(self):
        ADDRESS = r'\\.\pipe\test_double_bind-%s' % os.getpid()
        server1 = windows_events.PipeServer(ADDRESS)
        with self.assertRaises(PermissionError):
50
            windows_events.PipeServer(ADDRESS)
51 52 53 54 55 56 57 58 59 60 61
        server1.close()

    def test_pipe(self):
        res = self.loop.run_until_complete(self._test_pipe())
        self.assertEqual(res, 'done')

    def _test_pipe(self):
        ADDRESS = r'\\.\pipe\_test_pipe-%s' % os.getpid()

        with self.assertRaises(FileNotFoundError):
            yield from self.loop.create_pipe_connection(
62
                asyncio.Protocol, ADDRESS)
63 64 65 66 67 68 69

        [server] = yield from self.loop.start_serving_pipe(
            UpperProto, ADDRESS)
        self.assertIsInstance(server, windows_events.PipeServer)

        clients = []
        for i in range(5):
70
            stream_reader = asyncio.StreamReader(loop=self.loop)
71 72
            protocol = asyncio.StreamReaderProtocol(stream_reader,
                                                    loop=self.loop)
73
            trans, proto = yield from self.loop.create_pipe_connection(
74
                lambda: protocol, ADDRESS)
75
            self.assertIsInstance(trans, asyncio.Transport)
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
            self.assertEqual(protocol, proto)
            clients.append((stream_reader, trans))

        for i, (r, w) in enumerate(clients):
            w.write('lower-{}\n'.format(i).encode())

        for i, (r, w) in enumerate(clients):
            response = yield from r.readline()
            self.assertEqual(response, 'LOWER-{}\n'.format(i).encode())
            w.close()

        server.close()

        with self.assertRaises(FileNotFoundError):
            yield from self.loop.create_pipe_connection(
91
                asyncio.Protocol, ADDRESS)
92 93

        return 'done'
94

95 96 97 98 99 100 101 102 103 104 105 106
    def test_connect_pipe_cancel(self):
        exc = OSError()
        exc.winerror = _overlapped.ERROR_PIPE_BUSY
        with mock.patch.object(_overlapped, 'ConnectPipe', side_effect=exc) as connect:
            coro = self.loop._proactor.connect_pipe('pipe_address')
            task = self.loop.create_task(coro)

            # check that it's possible to cancel connect_pipe()
            task.cancel()
            with self.assertRaises(asyncio.CancelledError):
                self.loop.run_until_complete(task)

107 108 109 110
    def test_wait_for_handle(self):
        event = _overlapped.CreateEvent(None, True, False, None)
        self.addCleanup(_winapi.CloseHandle, event)

111
        # Wait for unset event with 0.5s timeout;
112
        # result should be False at timeout
113
        fut = self.loop._proactor.wait_for_handle(event, 0.5)
114
        start = self.loop.time()
115
        done = self.loop.run_until_complete(fut)
116
        elapsed = self.loop.time() - start
117 118

        self.assertEqual(done, False)
119
        self.assertFalse(fut.result())
120
        self.assertTrue(0.48 < elapsed < 0.9, elapsed)
121 122 123

        _overlapped.SetEvent(event)

124
        # Wait for set event;
125
        # result should be True immediately
126
        fut = self.loop._proactor.wait_for_handle(event, 10)
127
        start = self.loop.time()
128
        done = self.loop.run_until_complete(fut)
129
        elapsed = self.loop.time() - start
130 131

        self.assertEqual(done, True)
132
        self.assertTrue(fut.result())
133
        self.assertTrue(0 <= elapsed < 0.3, elapsed)
134

135 136
        # asyncio issue #195: cancelling a done _WaitHandleFuture
        # must not crash
137 138 139 140 141
        fut.cancel()

    def test_wait_for_handle_cancel(self):
        event = _overlapped.CreateEvent(None, True, False, None)
        self.addCleanup(_winapi.CloseHandle, event)
142 143 144

        # Wait for unset event with a cancelled future;
        # CancelledError should be raised immediately
145 146
        fut = self.loop._proactor.wait_for_handle(event, 10)
        fut.cancel()
147
        start = self.loop.time()
148
        with self.assertRaises(asyncio.CancelledError):
149
            self.loop.run_until_complete(fut)
150
        elapsed = self.loop.time() - start
151
        self.assertTrue(0 <= elapsed < 0.1, elapsed)
152

153 154
        # asyncio issue #195: cancelling a _WaitHandleFuture twice
        # must not crash
155 156 157 158
        fut = self.loop._proactor.wait_for_handle(event)
        fut.cancel()
        fut.cancel()

159 160 161

if __name__ == '__main__':
    unittest.main()