blob: ba89f125dbdcd41c64ebe5cba4fb9f2593f20e4e [file] [log] [blame]
Guido van Rossumb5a755e2007-07-18 18:15:48 +00001import asyncore
2import unittest
3import select
4import os
5import socket
6import threading
7import sys
8import time
9
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
11from test.support import TESTFN, run_unittest, unlink
Christian Heimes5e696852008-04-09 08:37:03 +000012from io import BytesIO
13from io import StringIO
Guido van Rossumb5a755e2007-07-18 18:15:48 +000014
Benjamin Petersonee8712c2008-05-20 21:35:26 +000015HOST = support.HOST
Guido van Rossumb5a755e2007-07-18 18:15:48 +000016
17class dummysocket:
18 def __init__(self):
19 self.closed = False
20
21 def close(self):
22 self.closed = True
23
24 def fileno(self):
25 return 42
26
27class dummychannel:
28 def __init__(self):
29 self.socket = dummysocket()
30
Josiah Carlsond74900e2008-07-07 04:15:08 +000031 def close(self):
32 self.socket.close()
33
Guido van Rossumb5a755e2007-07-18 18:15:48 +000034class exitingdummy:
35 def __init__(self):
36 pass
37
38 def handle_read_event(self):
39 raise asyncore.ExitNow()
40
41 handle_write_event = handle_read_event
Josiah Carlson91823c72008-07-11 23:26:37 +000042 handle_close = handle_read_event
Guido van Rossumb5a755e2007-07-18 18:15:48 +000043 handle_expt_event = handle_read_event
44
45class crashingdummy:
46 def __init__(self):
47 self.error_handled = False
48
49 def handle_read_event(self):
50 raise Exception()
51
52 handle_write_event = handle_read_event
Josiah Carlson91823c72008-07-11 23:26:37 +000053 handle_close = handle_read_event
Guido van Rossumb5a755e2007-07-18 18:15:48 +000054 handle_expt_event = handle_read_event
55
56 def handle_error(self):
57 self.error_handled = True
58
59# used when testing senders; just collects what it gets until newline is sent
Christian Heimes5e696852008-04-09 08:37:03 +000060def capture_server(evt, buf, serv):
Guido van Rossumb5a755e2007-07-18 18:15:48 +000061 try:
Guido van Rossum806c2462007-08-06 23:33:07 +000062 serv.listen(5)
Guido van Rossumb5a755e2007-07-18 18:15:48 +000063 conn, addr = serv.accept()
64 except socket.timeout:
65 pass
66 else:
67 n = 200
68 while n > 0:
Guido van Rossum36e0a922007-07-20 04:05:57 +000069 r, w, e = select.select([conn], [], [])
70 if r:
71 data = conn.recv(10)
Guido van Rossum36e0a922007-07-20 04:05:57 +000072 # keep everything except for the newline terminator
73 buf.write(data.replace(b'\n', b''))
74 if b'\n' in data:
75 break
Guido van Rossumb5a755e2007-07-18 18:15:48 +000076 n -= 1
77 time.sleep(0.01)
78
79 conn.close()
80 finally:
81 serv.close()
82 evt.set()
83
84
85class HelperFunctionTests(unittest.TestCase):
86 def test_readwriteexc(self):
87 # Check exception handling behavior of read, write and _exception
88
89 # check that ExitNow exceptions in the object handler method
90 # bubbles all the way up through asyncore read/write/_exception calls
91 tr1 = exitingdummy()
92 self.assertRaises(asyncore.ExitNow, asyncore.read, tr1)
93 self.assertRaises(asyncore.ExitNow, asyncore.write, tr1)
94 self.assertRaises(asyncore.ExitNow, asyncore._exception, tr1)
95
96 # check that an exception other than ExitNow in the object handler
97 # method causes the handle_error method to get called
98 tr2 = crashingdummy()
99 asyncore.read(tr2)
100 self.assertEqual(tr2.error_handled, True)
101
102 tr2 = crashingdummy()
103 asyncore.write(tr2)
104 self.assertEqual(tr2.error_handled, True)
105
106 tr2 = crashingdummy()
107 asyncore._exception(tr2)
108 self.assertEqual(tr2.error_handled, True)
109
Guido van Rossum806c2462007-08-06 23:33:07 +0000110 # asyncore.readwrite uses constants in the select module that
111 # are not present in Windows systems (see this thread:
112 # http://mail.python.org/pipermail/python-list/2001-October/109973.html)
113 # These constants should be present as long as poll is available
114
115 if hasattr(select, 'poll'):
116 def test_readwrite(self):
117 # Check that correct methods are called by readwrite()
118
R. David Murray78532ba2009-04-12 15:35:44 +0000119 attributes = ('read', 'expt', 'write', 'closed', 'error_handled')
120
121 expected = (
122 (select.POLLIN, 'read'),
123 (select.POLLPRI, 'expt'),
124 (select.POLLOUT, 'write'),
125 (select.POLLERR, 'closed'),
126 (select.POLLHUP, 'closed'),
127 (select.POLLNVAL, 'closed'),
128 )
129
Guido van Rossum806c2462007-08-06 23:33:07 +0000130 class testobj:
131 def __init__(self):
132 self.read = False
133 self.write = False
Josiah Carlson9f2f8332008-07-07 05:04:12 +0000134 self.closed = False
Guido van Rossum806c2462007-08-06 23:33:07 +0000135 self.expt = False
R. David Murray78532ba2009-04-12 15:35:44 +0000136 self.error_handled = False
Guido van Rossum806c2462007-08-06 23:33:07 +0000137
138 def handle_read_event(self):
139 self.read = True
140
141 def handle_write_event(self):
142 self.write = True
143
Josiah Carlson91823c72008-07-11 23:26:37 +0000144 def handle_close(self):
Josiah Carlson9f2f8332008-07-07 05:04:12 +0000145 self.closed = True
146
Guido van Rossum806c2462007-08-06 23:33:07 +0000147 def handle_expt_event(self):
148 self.expt = True
149
150 def handle_error(self):
151 self.error_handled = True
152
R. David Murray78532ba2009-04-12 15:35:44 +0000153 for flag, expectedattr in expected:
Guido van Rossum806c2462007-08-06 23:33:07 +0000154 tobj = testobj()
R. David Murray78532ba2009-04-12 15:35:44 +0000155 self.assertEqual(getattr(tobj, expectedattr), False)
Guido van Rossum806c2462007-08-06 23:33:07 +0000156 asyncore.readwrite(tobj, flag)
R. David Murray78532ba2009-04-12 15:35:44 +0000157
158 # Only the attribute modified by the routine we expect to be
159 # called should be True.
160 for attr in attributes:
161 self.assertEqual(getattr(tobj, attr), attr==expectedattr)
Guido van Rossum806c2462007-08-06 23:33:07 +0000162
163 # check that ExitNow exceptions in the object handler method
164 # bubbles all the way up through asyncore readwrite call
165 tr1 = exitingdummy()
166 self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag)
167
168 # check that an exception other than ExitNow in the object handler
169 # method causes the handle_error method to get called
170 tr2 = crashingdummy()
R. David Murray78532ba2009-04-12 15:35:44 +0000171 self.assertEqual(tr2.error_handled, False)
Guido van Rossum806c2462007-08-06 23:33:07 +0000172 asyncore.readwrite(tr2, flag)
173 self.assertEqual(tr2.error_handled, True)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000174
175 def test_closeall(self):
176 self.closeall_check(False)
177
178 def test_closeall_default(self):
179 self.closeall_check(True)
180
181 def closeall_check(self, usedefault):
182 # Check that close_all() closes everything in a given map
183
184 l = []
185 testmap = {}
186 for i in range(10):
187 c = dummychannel()
188 l.append(c)
189 self.assertEqual(c.socket.closed, False)
190 testmap[i] = c
191
192 if usedefault:
193 socketmap = asyncore.socket_map
194 try:
195 asyncore.socket_map = testmap
196 asyncore.close_all()
197 finally:
198 testmap, asyncore.socket_map = asyncore.socket_map, socketmap
199 else:
200 asyncore.close_all(testmap)
201
202 self.assertEqual(len(testmap), 0)
203
204 for c in l:
205 self.assertEqual(c.socket.closed, True)
206
207 def test_compact_traceback(self):
208 try:
209 raise Exception("I don't like spam!")
210 except:
211 real_t, real_v, real_tb = sys.exc_info()
212 r = asyncore.compact_traceback()
213 else:
214 self.fail("Expected exception")
215
216 (f, function, line), t, v, info = r
217 self.assertEqual(os.path.split(f)[-1], 'test_asyncore.py')
218 self.assertEqual(function, 'test_compact_traceback')
219 self.assertEqual(t, real_t)
220 self.assertEqual(v, real_v)
221 self.assertEqual(info, '[%s|%s|%s]' % (f, function, line))
222
223
224class DispatcherTests(unittest.TestCase):
225 def setUp(self):
226 pass
227
228 def tearDown(self):
229 asyncore.close_all()
230
231 def test_basic(self):
232 d = asyncore.dispatcher()
233 self.assertEqual(d.readable(), True)
234 self.assertEqual(d.writable(), True)
235
236 def test_repr(self):
237 d = asyncore.dispatcher()
238 self.assertEqual(repr(d), '<asyncore.dispatcher at %#x>' % id(d))
239
240 def test_log(self):
241 d = asyncore.dispatcher()
242
243 # capture output of dispatcher.log() (to stderr)
244 fp = StringIO()
245 stderr = sys.stderr
246 l1 = "Lovely spam! Wonderful spam!"
247 l2 = "I don't like spam!"
248 try:
249 sys.stderr = fp
250 d.log(l1)
251 d.log(l2)
252 finally:
253 sys.stderr = stderr
254
255 lines = fp.getvalue().splitlines()
256 self.assertEquals(lines, ['log: %s' % l1, 'log: %s' % l2])
257
258 def test_log_info(self):
259 d = asyncore.dispatcher()
260
261 # capture output of dispatcher.log_info() (to stdout via print)
262 fp = StringIO()
263 stdout = sys.stdout
264 l1 = "Have you got anything without spam?"
265 l2 = "Why can't she have egg bacon spam and sausage?"
266 l3 = "THAT'S got spam in it!"
267 try:
268 sys.stdout = fp
269 d.log_info(l1, 'EGGS')
270 d.log_info(l2)
271 d.log_info(l3, 'SPAM')
272 finally:
273 sys.stdout = stdout
274
275 lines = fp.getvalue().splitlines()
R. David Murray847f30e2009-04-13 01:22:04 +0000276 expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3]
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000277
278 self.assertEquals(lines, expected)
279
280 def test_unhandled(self):
281 d = asyncore.dispatcher()
R. David Murray78532ba2009-04-12 15:35:44 +0000282 d.ignore_log_types = ()
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000283
284 # capture output of dispatcher.log_info() (to stdout via print)
285 fp = StringIO()
286 stdout = sys.stdout
287 try:
288 sys.stdout = fp
289 d.handle_expt()
290 d.handle_read()
291 d.handle_write()
292 d.handle_connect()
293 d.handle_accept()
294 finally:
295 sys.stdout = stdout
296
297 lines = fp.getvalue().splitlines()
R. David Murray78532ba2009-04-12 15:35:44 +0000298 expected = ['warning: unhandled incoming priority event',
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000299 'warning: unhandled read event',
300 'warning: unhandled write event',
301 'warning: unhandled connect event',
302 'warning: unhandled accept event']
303 self.assertEquals(lines, expected)
304
305
306
307class dispatcherwithsend_noread(asyncore.dispatcher_with_send):
308 def readable(self):
309 return False
310
311 def handle_connect(self):
312 pass
313
314class DispatcherWithSendTests(unittest.TestCase):
315 usepoll = False
316
317 def setUp(self):
318 pass
319
320 def tearDown(self):
321 asyncore.close_all()
322
323 def test_send(self):
324 self.evt = threading.Event()
Christian Heimes5e696852008-04-09 08:37:03 +0000325 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
326 self.sock.settimeout(3)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000327 self.port = support.bind_port(self.sock)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000328
Christian Heimes5e696852008-04-09 08:37:03 +0000329 cap = BytesIO()
330 args = (self.evt, cap, self.sock)
331 threading.Thread(target=capture_server, args=args).start()
Guido van Rossum806c2462007-08-06 23:33:07 +0000332
333 # wait a little longer for the server to initialize (it sometimes
334 # refuses connections on slow machines without this wait)
335 time.sleep(0.2)
336
337 data = b"Suppose there isn't a 16-ton weight?"
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000338 d = dispatcherwithsend_noread()
339 d.create_socket(socket.AF_INET, socket.SOCK_STREAM)
Christian Heimes5e696852008-04-09 08:37:03 +0000340 d.connect((HOST, self.port))
Guido van Rossum806c2462007-08-06 23:33:07 +0000341
342 # give time for socket to connect
343 time.sleep(0.1)
344
345 d.send(data)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000346 d.send(data)
Guido van Rossumdf4a7432007-07-18 20:57:44 +0000347 d.send(b'\n')
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000348
349 n = 1000
350 while d.out_buffer and n > 0:
351 asyncore.poll()
352 n -= 1
353
354 self.evt.wait()
355
Guido van Rossum806c2462007-08-06 23:33:07 +0000356 self.assertEqual(cap.getvalue(), data*2)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000357
358
359class DispatcherWithSendTests_UsePoll(DispatcherWithSendTests):
360 usepoll = True
361
362if hasattr(asyncore, 'file_wrapper'):
363 class FileWrapperTest(unittest.TestCase):
364 def setUp(self):
Antoine Pitrou9cadb1b2008-09-15 23:02:56 +0000365 self.d = b"It's not dead, it's sleeping!"
366 open(TESTFN, 'wb').write(self.d)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000367
368 def tearDown(self):
369 unlink(TESTFN)
370
371 def test_recv(self):
372 fd = os.open(TESTFN, os.O_RDONLY)
373 w = asyncore.file_wrapper(fd)
Hirokazu Yamamoto7aa42ea2008-09-02 20:41:25 +0000374 os.close(fd)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000375
Josiah Carlsond74900e2008-07-07 04:15:08 +0000376 self.assertNotEqual(w.fd, fd)
377 self.assertNotEqual(w.fileno(), fd)
Guido van Rossumdf4a7432007-07-18 20:57:44 +0000378 self.assertEqual(w.recv(13), b"It's not dead")
379 self.assertEqual(w.read(6), b", it's")
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000380 w.close()
381 self.assertRaises(OSError, w.read, 1)
382
383 def test_send(self):
Antoine Pitrou9cadb1b2008-09-15 23:02:56 +0000384 d1 = b"Come again?"
385 d2 = b"I want to buy some cheese."
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000386 fd = os.open(TESTFN, os.O_WRONLY | os.O_APPEND)
387 w = asyncore.file_wrapper(fd)
Hirokazu Yamamoto7aa42ea2008-09-02 20:41:25 +0000388 os.close(fd)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000389
390 w.write(d1)
391 w.send(d2)
392 w.close()
Antoine Pitrou9cadb1b2008-09-15 23:02:56 +0000393 self.assertEqual(open(TESTFN, 'rb').read(), self.d + d1 + d2)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000394
395
396def test_main():
397 tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests,
398 DispatcherWithSendTests_UsePoll]
399 if hasattr(asyncore, 'file_wrapper'):
400 tests.append(FileWrapperTest)
401
402 run_unittest(*tests)
403
404if __name__ == "__main__":
405 test_main()