blob: eb52687866d3763b40c30623526c392f0720b7fe [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
31class exitingdummy:
32 def __init__(self):
33 pass
34
35 def handle_read_event(self):
36 raise asyncore.ExitNow()
37
38 handle_write_event = handle_read_event
39 handle_expt_event = handle_read_event
40
41class crashingdummy:
42 def __init__(self):
43 self.error_handled = False
44
45 def handle_read_event(self):
46 raise Exception()
47
48 handle_write_event = handle_read_event
49 handle_expt_event = handle_read_event
50
51 def handle_error(self):
52 self.error_handled = True
53
54# used when testing senders; just collects what it gets until newline is sent
Christian Heimes5e696852008-04-09 08:37:03 +000055def capture_server(evt, buf, serv):
Guido van Rossumb5a755e2007-07-18 18:15:48 +000056 try:
Guido van Rossum806c2462007-08-06 23:33:07 +000057 serv.listen(5)
Guido van Rossumb5a755e2007-07-18 18:15:48 +000058 conn, addr = serv.accept()
59 except socket.timeout:
60 pass
61 else:
62 n = 200
63 while n > 0:
Guido van Rossum36e0a922007-07-20 04:05:57 +000064 r, w, e = select.select([conn], [], [])
65 if r:
66 data = conn.recv(10)
Guido van Rossum36e0a922007-07-20 04:05:57 +000067 # keep everything except for the newline terminator
68 buf.write(data.replace(b'\n', b''))
69 if b'\n' in data:
70 break
Guido van Rossumb5a755e2007-07-18 18:15:48 +000071 n -= 1
72 time.sleep(0.01)
73
74 conn.close()
75 finally:
76 serv.close()
77 evt.set()
78
79
80class HelperFunctionTests(unittest.TestCase):
81 def test_readwriteexc(self):
82 # Check exception handling behavior of read, write and _exception
83
84 # check that ExitNow exceptions in the object handler method
85 # bubbles all the way up through asyncore read/write/_exception calls
86 tr1 = exitingdummy()
87 self.assertRaises(asyncore.ExitNow, asyncore.read, tr1)
88 self.assertRaises(asyncore.ExitNow, asyncore.write, tr1)
89 self.assertRaises(asyncore.ExitNow, asyncore._exception, tr1)
90
91 # check that an exception other than ExitNow in the object handler
92 # method causes the handle_error method to get called
93 tr2 = crashingdummy()
94 asyncore.read(tr2)
95 self.assertEqual(tr2.error_handled, True)
96
97 tr2 = crashingdummy()
98 asyncore.write(tr2)
99 self.assertEqual(tr2.error_handled, True)
100
101 tr2 = crashingdummy()
102 asyncore._exception(tr2)
103 self.assertEqual(tr2.error_handled, True)
104
Guido van Rossum806c2462007-08-06 23:33:07 +0000105 # asyncore.readwrite uses constants in the select module that
106 # are not present in Windows systems (see this thread:
107 # http://mail.python.org/pipermail/python-list/2001-October/109973.html)
108 # These constants should be present as long as poll is available
109
110 if hasattr(select, 'poll'):
111 def test_readwrite(self):
112 # Check that correct methods are called by readwrite()
113
114 class testobj:
115 def __init__(self):
116 self.read = False
117 self.write = False
118 self.expt = False
119
120 def handle_read_event(self):
121 self.read = True
122
123 def handle_write_event(self):
124 self.write = True
125
126 def handle_expt_event(self):
127 self.expt = True
128
129 def handle_error(self):
130 self.error_handled = True
131
132 for flag in (select.POLLIN, select.POLLPRI):
133 tobj = testobj()
134 self.assertEqual(tobj.read, False)
135 asyncore.readwrite(tobj, flag)
136 self.assertEqual(tobj.read, True)
137
138 # check that ExitNow exceptions in the object handler method
139 # bubbles all the way up through asyncore readwrite call
140 tr1 = exitingdummy()
141 self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag)
142
143 # check that an exception other than ExitNow in the object handler
144 # method causes the handle_error method to get called
145 tr2 = crashingdummy()
146 asyncore.readwrite(tr2, flag)
147 self.assertEqual(tr2.error_handled, True)
148
149 tobj = testobj()
150 self.assertEqual(tobj.write, False)
151 asyncore.readwrite(tobj, select.POLLOUT)
152 self.assertEqual(tobj.write, True)
153
154 # check that ExitNow exceptions in the object handler method
155 # bubbles all the way up through asyncore readwrite call
156 tr1 = exitingdummy()
157 self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1,
158 select.POLLOUT)
159
160 # check that an exception other than ExitNow in the object handler
161 # method causes the handle_error method to get called
162 tr2 = crashingdummy()
163 asyncore.readwrite(tr2, select.POLLOUT)
164 self.assertEqual(tr2.error_handled, True)
165
166 for flag in (select.POLLERR, select.POLLHUP, select.POLLNVAL):
167 tobj = testobj()
168 self.assertEqual(tobj.expt, False)
169 asyncore.readwrite(tobj, flag)
170 self.assertEqual(tobj.expt, True)
171
172 # check that ExitNow exceptions in the object handler method
173 # bubbles all the way up through asyncore readwrite calls
174 tr1 = exitingdummy()
175 self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag)
176
177 # check that an exception other than ExitNow in the object handler
178 # method causes the handle_error method to get called
179 tr2 = crashingdummy()
180 asyncore.readwrite(tr2, flag)
181 self.assertEqual(tr2.error_handled, True)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000182
183 def test_closeall(self):
184 self.closeall_check(False)
185
186 def test_closeall_default(self):
187 self.closeall_check(True)
188
189 def closeall_check(self, usedefault):
190 # Check that close_all() closes everything in a given map
191
192 l = []
193 testmap = {}
194 for i in range(10):
195 c = dummychannel()
196 l.append(c)
197 self.assertEqual(c.socket.closed, False)
198 testmap[i] = c
199
200 if usedefault:
201 socketmap = asyncore.socket_map
202 try:
203 asyncore.socket_map = testmap
204 asyncore.close_all()
205 finally:
206 testmap, asyncore.socket_map = asyncore.socket_map, socketmap
207 else:
208 asyncore.close_all(testmap)
209
210 self.assertEqual(len(testmap), 0)
211
212 for c in l:
213 self.assertEqual(c.socket.closed, True)
214
215 def test_compact_traceback(self):
216 try:
217 raise Exception("I don't like spam!")
218 except:
219 real_t, real_v, real_tb = sys.exc_info()
220 r = asyncore.compact_traceback()
221 else:
222 self.fail("Expected exception")
223
224 (f, function, line), t, v, info = r
225 self.assertEqual(os.path.split(f)[-1], 'test_asyncore.py')
226 self.assertEqual(function, 'test_compact_traceback')
227 self.assertEqual(t, real_t)
228 self.assertEqual(v, real_v)
229 self.assertEqual(info, '[%s|%s|%s]' % (f, function, line))
230
231
232class DispatcherTests(unittest.TestCase):
233 def setUp(self):
234 pass
235
236 def tearDown(self):
237 asyncore.close_all()
238
239 def test_basic(self):
240 d = asyncore.dispatcher()
241 self.assertEqual(d.readable(), True)
242 self.assertEqual(d.writable(), True)
243
244 def test_repr(self):
245 d = asyncore.dispatcher()
246 self.assertEqual(repr(d), '<asyncore.dispatcher at %#x>' % id(d))
247
248 def test_log(self):
249 d = asyncore.dispatcher()
250
251 # capture output of dispatcher.log() (to stderr)
252 fp = StringIO()
253 stderr = sys.stderr
254 l1 = "Lovely spam! Wonderful spam!"
255 l2 = "I don't like spam!"
256 try:
257 sys.stderr = fp
258 d.log(l1)
259 d.log(l2)
260 finally:
261 sys.stderr = stderr
262
263 lines = fp.getvalue().splitlines()
264 self.assertEquals(lines, ['log: %s' % l1, 'log: %s' % l2])
265
266 def test_log_info(self):
267 d = asyncore.dispatcher()
268
269 # capture output of dispatcher.log_info() (to stdout via print)
270 fp = StringIO()
271 stdout = sys.stdout
272 l1 = "Have you got anything without spam?"
273 l2 = "Why can't she have egg bacon spam and sausage?"
274 l3 = "THAT'S got spam in it!"
275 try:
276 sys.stdout = fp
277 d.log_info(l1, 'EGGS')
278 d.log_info(l2)
279 d.log_info(l3, 'SPAM')
280 finally:
281 sys.stdout = stdout
282
283 lines = fp.getvalue().splitlines()
284 if __debug__:
285 expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3]
286 else:
287 expected = ['EGGS: %s' % l1, 'SPAM: %s' % l3]
288
289 self.assertEquals(lines, expected)
290
291 def test_unhandled(self):
292 d = asyncore.dispatcher()
293
294 # capture output of dispatcher.log_info() (to stdout via print)
295 fp = StringIO()
296 stdout = sys.stdout
297 try:
298 sys.stdout = fp
299 d.handle_expt()
300 d.handle_read()
301 d.handle_write()
302 d.handle_connect()
303 d.handle_accept()
304 finally:
305 sys.stdout = stdout
306
307 lines = fp.getvalue().splitlines()
308 expected = ['warning: unhandled exception',
309 'warning: unhandled read event',
310 'warning: unhandled write event',
311 'warning: unhandled connect event',
312 'warning: unhandled accept event']
313 self.assertEquals(lines, expected)
314
315
316
317class dispatcherwithsend_noread(asyncore.dispatcher_with_send):
318 def readable(self):
319 return False
320
321 def handle_connect(self):
322 pass
323
324class DispatcherWithSendTests(unittest.TestCase):
325 usepoll = False
326
327 def setUp(self):
328 pass
329
330 def tearDown(self):
331 asyncore.close_all()
332
333 def test_send(self):
334 self.evt = threading.Event()
Christian Heimes5e696852008-04-09 08:37:03 +0000335 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
336 self.sock.settimeout(3)
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000337 self.port = support.bind_port(self.sock)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000338
Christian Heimes5e696852008-04-09 08:37:03 +0000339 cap = BytesIO()
340 args = (self.evt, cap, self.sock)
341 threading.Thread(target=capture_server, args=args).start()
Guido van Rossum806c2462007-08-06 23:33:07 +0000342
343 # wait a little longer for the server to initialize (it sometimes
344 # refuses connections on slow machines without this wait)
345 time.sleep(0.2)
346
347 data = b"Suppose there isn't a 16-ton weight?"
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000348 d = dispatcherwithsend_noread()
349 d.create_socket(socket.AF_INET, socket.SOCK_STREAM)
Christian Heimes5e696852008-04-09 08:37:03 +0000350 d.connect((HOST, self.port))
Guido van Rossum806c2462007-08-06 23:33:07 +0000351
352 # give time for socket to connect
353 time.sleep(0.1)
354
355 d.send(data)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000356 d.send(data)
Guido van Rossumdf4a7432007-07-18 20:57:44 +0000357 d.send(b'\n')
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000358
359 n = 1000
360 while d.out_buffer and n > 0:
361 asyncore.poll()
362 n -= 1
363
364 self.evt.wait()
365
Guido van Rossum806c2462007-08-06 23:33:07 +0000366 self.assertEqual(cap.getvalue(), data*2)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000367
368
369class DispatcherWithSendTests_UsePoll(DispatcherWithSendTests):
370 usepoll = True
371
372if hasattr(asyncore, 'file_wrapper'):
373 class FileWrapperTest(unittest.TestCase):
374 def setUp(self):
375 self.d = "It's not dead, it's sleeping!"
Guido van Rossumdf4a7432007-07-18 20:57:44 +0000376 open(TESTFN, 'w').write(self.d)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000377
378 def tearDown(self):
379 unlink(TESTFN)
380
381 def test_recv(self):
382 fd = os.open(TESTFN, os.O_RDONLY)
383 w = asyncore.file_wrapper(fd)
384
385 self.assertEqual(w.fd, fd)
386 self.assertEqual(w.fileno(), fd)
Guido van Rossumdf4a7432007-07-18 20:57:44 +0000387 self.assertEqual(w.recv(13), b"It's not dead")
388 self.assertEqual(w.read(6), b", it's")
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000389 w.close()
390 self.assertRaises(OSError, w.read, 1)
391
392 def test_send(self):
393 d1 = "Come again?"
394 d2 = "I want to buy some cheese."
395 fd = os.open(TESTFN, os.O_WRONLY | os.O_APPEND)
396 w = asyncore.file_wrapper(fd)
397
398 w.write(d1)
399 w.send(d2)
400 w.close()
Guido van Rossumdf4a7432007-07-18 20:57:44 +0000401 self.assertEqual(open(TESTFN).read(), self.d + d1 + d2)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000402
403
404def test_main():
405 tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests,
406 DispatcherWithSendTests_UsePoll]
407 if hasattr(asyncore, 'file_wrapper'):
408 tests.append(FileWrapperTest)
409
410 run_unittest(*tests)
411
412if __name__ == "__main__":
413 test_main()