| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 1 | # test asynchat -- requires threading | 
|  | 2 |  | 
| Guido van Rossum | 9df3eab | 2001-04-14 14:35:43 +0000 | [diff] [blame] | 3 | import thread # If this fails, we can't test this module | 
| Guido van Rossum | dca060c | 2001-04-06 16:43:49 +0000 | [diff] [blame] | 4 | import asyncore, asynchat, socket, threading, time | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 5 | import unittest | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 6 | import sys | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 7 | from test import test_support | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 8 |  | 
|  | 9 | HOST = "127.0.0.1" | 
| Michael W. Hudson | 7390942 | 2005-06-20 13:45:34 +0000 | [diff] [blame] | 10 | PORT = 54322 | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 11 | SERVER_QUIT = b'QUIT\n' | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 12 |  | 
|  | 13 | class echo_server(threading.Thread): | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 14 | # parameter to determine the number of bytes passed back to the | 
|  | 15 | # client each send | 
|  | 16 | chunk_size = 1 | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 17 |  | 
|  | 18 | def run(self): | 
|  | 19 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | 
| Guido van Rossum | f3ee46b | 2001-04-15 00:42:13 +0000 | [diff] [blame] | 20 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 21 | global PORT | 
|  | 22 | PORT = test_support.bind_port(sock, HOST, PORT) | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 23 | sock.listen(1) | 
|  | 24 | conn, client = sock.accept() | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 25 | self.buffer = b"" | 
|  | 26 | # collect data until quit message is seen | 
|  | 27 | while SERVER_QUIT not in self.buffer: | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 28 | data = conn.recv(1) | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 29 | if not data: | 
|  | 30 | break | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 31 | self.buffer = self.buffer + data | 
|  | 32 |  | 
|  | 33 | # remove the SERVER_QUIT message | 
|  | 34 | self.buffer = self.buffer.replace(SERVER_QUIT, b'') | 
|  | 35 |  | 
|  | 36 | # re-send entire set of collected data | 
|  | 37 | try: | 
|  | 38 | # this may fail on some tests, such as test_close_when_done, since | 
|  | 39 | # the client closes the channel when it's done sending | 
|  | 40 | while self.buffer: | 
|  | 41 | n = conn.send(self.buffer[:self.chunk_size]) | 
|  | 42 | time.sleep(0.001) | 
|  | 43 | self.buffer = self.buffer[n:] | 
|  | 44 | except: | 
|  | 45 | pass | 
|  | 46 |  | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 47 | conn.close() | 
|  | 48 | sock.close() | 
|  | 49 |  | 
|  | 50 | class echo_client(asynchat.async_chat): | 
|  | 51 |  | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 52 | def __init__(self, terminator): | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 53 | asynchat.async_chat.__init__(self) | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 54 | self.contents = [] | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 55 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | 
|  | 56 | self.connect((HOST, PORT)) | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 57 | self.set_terminator(terminator) | 
| Guido van Rossum | 076da09 | 2007-07-12 07:58:54 +0000 | [diff] [blame] | 58 | self.buffer = b"" | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 59 |  | 
|  | 60 | def handle_connect(self): | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 61 | pass | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 62 |  | 
|  | 63 | if sys.platform == 'darwin': | 
|  | 64 | # select.poll returns a select.POLLHUP at the end of the tests | 
|  | 65 | # on darwin, so just ignore it | 
|  | 66 | def handle_expt(self): | 
|  | 67 | pass | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 68 |  | 
|  | 69 | def collect_incoming_data(self, data): | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 70 | self.buffer += data | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 71 |  | 
|  | 72 | def found_terminator(self): | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 73 | self.contents.append(self.buffer) | 
| Guido van Rossum | 076da09 | 2007-07-12 07:58:54 +0000 | [diff] [blame] | 74 | self.buffer = b"" | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 75 |  | 
| Guido van Rossum | 6617252 | 2001-04-06 16:32:22 +0000 | [diff] [blame] | 76 |  | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 77 | class TestAsynchat(unittest.TestCase): | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 78 | usepoll = False | 
|  | 79 |  | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 80 | def setUp (self): | 
|  | 81 | pass | 
|  | 82 |  | 
|  | 83 | def tearDown (self): | 
|  | 84 | pass | 
|  | 85 |  | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 86 | def line_terminator_check(self, term, server_chunk): | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 87 | s = echo_server() | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 88 | s.chunk_size = server_chunk | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 89 | s.start() | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 90 | time.sleep(0.5) # Give server time to initialize | 
|  | 91 | c = echo_client(term) | 
|  | 92 | c.push(b"hello ") | 
|  | 93 | c.push(bytes("world%s" % term)) | 
|  | 94 | c.push(bytes("I'm not dead yet!%s" % term)) | 
|  | 95 | c.push(SERVER_QUIT) | 
|  | 96 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
| Michael W. Hudson | 7390942 | 2005-06-20 13:45:34 +0000 | [diff] [blame] | 97 | s.join() | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 98 |  | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 99 | self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"]) | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 100 |  | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 101 | # the line terminator tests below check receiving variously-sized | 
|  | 102 | # chunks back from the server in order to exercise all branches of | 
|  | 103 | # async_chat.handle_read | 
|  | 104 |  | 
|  | 105 | def test_line_terminator1(self): | 
|  | 106 | # test one-character terminator | 
|  | 107 | for l in (1,2,3): | 
|  | 108 | self.line_terminator_check(b'\n', l) | 
|  | 109 |  | 
|  | 110 | def test_line_terminator2(self): | 
|  | 111 | # test two-character terminator | 
|  | 112 | for l in (1,2,3): | 
|  | 113 | self.line_terminator_check(b'\r\n', l) | 
|  | 114 |  | 
|  | 115 | def test_line_terminator3(self): | 
|  | 116 | # test three-character terminator | 
|  | 117 | for l in (1,2,3): | 
|  | 118 | self.line_terminator_check(b'qqq', l) | 
|  | 119 |  | 
|  | 120 | def numeric_terminator_check(self, termlen): | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 121 | # Try reading a fixed number of bytes | 
|  | 122 | s = echo_server() | 
|  | 123 | s.start() | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 124 | time.sleep(0.5) # Give server time to initialize | 
|  | 125 | c = echo_client(termlen) | 
|  | 126 | data = b"hello world, I'm not dead yet!\n" | 
|  | 127 | c.push(data) | 
|  | 128 | c.push(SERVER_QUIT) | 
|  | 129 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
| Michael W. Hudson | 7390942 | 2005-06-20 13:45:34 +0000 | [diff] [blame] | 130 | s.join() | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 131 |  | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 132 | self.assertEqual(c.contents, [data[:termlen]]) | 
|  | 133 |  | 
|  | 134 | def test_numeric_terminator1(self): | 
|  | 135 | # check that ints & longs both work (since type is | 
|  | 136 | # explicitly checked in async_chat.handle_read) | 
|  | 137 | self.numeric_terminator_check(1) | 
|  | 138 |  | 
|  | 139 | def test_numeric_terminator2(self): | 
|  | 140 | self.numeric_terminator_check(6) | 
|  | 141 |  | 
|  | 142 | def test_none_terminator(self): | 
|  | 143 | # Try reading a fixed number of bytes | 
|  | 144 | s = echo_server() | 
|  | 145 | s.start() | 
|  | 146 | time.sleep(0.5) # Give server time to initialize | 
|  | 147 | c = echo_client(None) | 
|  | 148 | data = b"hello world, I'm not dead yet!\n" | 
|  | 149 | c.push(data) | 
|  | 150 | c.push(SERVER_QUIT) | 
|  | 151 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
|  | 152 | s.join() | 
|  | 153 |  | 
|  | 154 | self.assertEqual(c.contents, []) | 
|  | 155 | self.assertEqual(c.buffer, data) | 
|  | 156 |  | 
|  | 157 | def test_simple_producer(self): | 
|  | 158 | s = echo_server() | 
|  | 159 | s.start() | 
|  | 160 | time.sleep(0.5) # Give server time to initialize | 
|  | 161 | c = echo_client(b'\n') | 
|  | 162 | data = b"hello world\nI'm not dead yet!\n" | 
|  | 163 | p = asynchat.simple_producer(data+SERVER_QUIT, buffer_size=8) | 
|  | 164 | c.push_with_producer(p) | 
|  | 165 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
|  | 166 | s.join() | 
|  | 167 |  | 
|  | 168 | self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"]) | 
|  | 169 |  | 
|  | 170 | def test_string_producer(self): | 
|  | 171 | s = echo_server() | 
|  | 172 | s.start() | 
|  | 173 | time.sleep(0.5) # Give server time to initialize | 
|  | 174 | c = echo_client(b'\n') | 
|  | 175 | data = b"hello world\nI'm not dead yet!\n" | 
|  | 176 | c.push_with_producer(data+SERVER_QUIT) | 
|  | 177 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
|  | 178 | s.join() | 
|  | 179 |  | 
|  | 180 | self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"]) | 
|  | 181 |  | 
|  | 182 | def test_empty_line(self): | 
|  | 183 | # checks that empty lines are handled correctly | 
|  | 184 | s = echo_server() | 
|  | 185 | s.start() | 
|  | 186 | time.sleep(0.5) # Give server time to initialize | 
|  | 187 | c = echo_client(b'\n') | 
|  | 188 | c.push("hello world\n\nI'm not dead yet!\n") | 
|  | 189 | c.push(SERVER_QUIT) | 
|  | 190 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
|  | 191 | s.join() | 
|  | 192 |  | 
|  | 193 | self.assertEqual(c.contents, | 
|  | 194 | [b"hello world", b"", b"I'm not dead yet!"]) | 
|  | 195 |  | 
|  | 196 | def test_close_when_done(self): | 
|  | 197 | s = echo_server() | 
|  | 198 | s.start() | 
|  | 199 | time.sleep(0.5) # Give server time to initialize | 
|  | 200 | c = echo_client(b'\n') | 
|  | 201 | c.push("hello world\nI'm not dead yet!\n") | 
|  | 202 | c.push(SERVER_QUIT) | 
|  | 203 | c.close_when_done() | 
|  | 204 | asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) | 
|  | 205 | s.join() | 
|  | 206 |  | 
|  | 207 | self.assertEqual(c.contents, []) | 
|  | 208 | # the server might have been able to send a byte or two back, but this | 
|  | 209 | # at least checks that it received something and didn't just fail | 
|  | 210 | # (which could still result in the client not having received anything) | 
|  | 211 | self.assertTrue(len(s.buffer) > 0) | 
|  | 212 |  | 
|  | 213 |  | 
|  | 214 | class TestAsynchat_WithPoll(TestAsynchat): | 
|  | 215 | usepoll = True | 
|  | 216 |  | 
|  | 217 | class TestHelperFunctions(unittest.TestCase): | 
|  | 218 | def test_find_prefix_at_end(self): | 
|  | 219 | self.assertEqual(asynchat.find_prefix_at_end("qwerty\r", "\r\n"), 1) | 
|  | 220 | self.assertEqual(asynchat.find_prefix_at_end("qwertydkjf", "\r\n"), 0) | 
|  | 221 |  | 
|  | 222 | class TestFifo(unittest.TestCase): | 
|  | 223 | def test_basic(self): | 
|  | 224 | f = asynchat.fifo() | 
|  | 225 | f.push(7) | 
|  | 226 | f.push(b'a') | 
|  | 227 | self.assertEqual(len(f), 2) | 
|  | 228 | self.assertEqual(f.first(), 7) | 
|  | 229 | self.assertEqual(f.pop(), (1, 7)) | 
|  | 230 | self.assertEqual(len(f), 1) | 
|  | 231 | self.assertEqual(f.first(), b'a') | 
|  | 232 | self.assertEqual(f.is_empty(), False) | 
|  | 233 | self.assertEqual(f.pop(), (1, b'a')) | 
|  | 234 | self.assertEqual(len(f), 0) | 
|  | 235 | self.assertEqual(f.is_empty(), True) | 
|  | 236 | self.assertEqual(f.pop(), (0, None)) | 
|  | 237 |  | 
|  | 238 | def test_given_list(self): | 
|  | 239 | f = asynchat.fifo([b'x', 17, 3]) | 
|  | 240 | self.assertEqual(len(f), 3) | 
|  | 241 | self.assertEqual(f.pop(), (1, b'x')) | 
|  | 242 | self.assertEqual(f.pop(), (1, 17)) | 
|  | 243 | self.assertEqual(f.pop(), (1, 3)) | 
|  | 244 | self.assertEqual(f.pop(), (0, None)) | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 245 |  | 
|  | 246 |  | 
|  | 247 | def test_main(verbose=None): | 
| Guido van Rossum | 806c246 | 2007-08-06 23:33:07 +0000 | [diff] [blame] | 248 | test_support.run_unittest(TestAsynchat, TestAsynchat_WithPoll, | 
|  | 249 | TestHelperFunctions, TestFifo) | 
| Andrew M. Kuchling | 5ac2534 | 2005-06-09 14:56:31 +0000 | [diff] [blame] | 250 |  | 
|  | 251 | if __name__ == "__main__": | 
|  | 252 | test_main(verbose=True) |