PageRenderTime 57ms CodeModel.GetById 1ms app.highlight 51ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/greenio_test.py

https://bitbucket.org/jmalicki/eventlet
Python | 416 lines | 392 code | 8 blank | 16 comment | 3 complexity | f2f87df54d137baa30daa01e67d2c666 MD5 | raw file
  1import socket as _orig_sock
  2from tests import LimitedTestCase, skip_with_pyevent, main
  3from eventlet import greenio
  4from eventlet import debug
  5from eventlet.green import socket
  6from eventlet.green import time
  7from eventlet.green.socket import GreenSSLObject
  8import errno
  9
 10import eventlet
 11import os
 12import sys
 13
 14def bufsized(sock, size=1):
 15    """ Resize both send and receive buffers on a socket.
 16    Useful for testing trampoline.  Returns the socket.
 17    
 18    >>> import socket
 19    >>> sock = bufsized(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
 20    """
 21    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, size)
 22    sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, size)
 23    return sock
 24
 25def min_buf_size():
 26    """Return the minimum buffer size that the platform supports."""
 27    test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 28    test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)
 29    return test_sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
 30
 31class TestGreenIo(LimitedTestCase):
 32    def test_connect_timeout(self):
 33        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 34        s.settimeout(0.1)
 35        gs = greenio.GreenSocket(s)
 36        try:
 37            self.assertRaises(socket.timeout, gs.connect, ('192.0.2.1', 80))
 38        except socket.error, e:
 39            # unreachable is also a valid outcome
 40            if e[0] != errno.EHOSTUNREACH:
 41                raise
 42
 43    def test_close_with_makefile(self):
 44        def accept_close_early(listener):
 45            # verify that the makefile and the socket are truly independent
 46            # by closing the socket prior to using the made file
 47            try:
 48                conn, addr = listener.accept()
 49                fd = conn.makefile()
 50                conn.close()
 51                fd.write('hello\n')
 52                fd.close()
 53                # socket._fileobjects are odd: writes don't check
 54                # whether the socket is closed or not, and you get an
 55                # AttributeError during flush if it is closed
 56                fd.write('a')
 57                self.assertRaises(Exception, fd.flush)
 58                self.assertRaises(socket.error, conn.send, 'b')
 59            finally:
 60                listener.close()
 61
 62        def accept_close_late(listener):
 63            # verify that the makefile and the socket are truly independent
 64            # by closing the made file and then sending a character
 65            try:
 66                conn, addr = listener.accept()
 67                fd = conn.makefile()
 68                fd.write('hello')
 69                fd.close()
 70                conn.send('\n')
 71                conn.close()
 72                fd.write('a')
 73                self.assertRaises(Exception, fd.flush)
 74                self.assertRaises(socket.error, conn.send, 'b')
 75            finally:
 76                listener.close()
 77                
 78        def did_it_work(server):
 79            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 80            client.connect(('127.0.0.1', server.getsockname()[1]))
 81            fd = client.makefile()
 82            client.close()
 83            assert fd.readline() == 'hello\n'    
 84            assert fd.read() == ''
 85            fd.close()
 86            
 87        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 88        server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
 89        server.bind(('0.0.0.0', 0))
 90        server.listen(50)
 91        killer = eventlet.spawn(accept_close_early, server)
 92        did_it_work(server)
 93        killer.wait()
 94        
 95        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 96        server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
 97        server.bind(('0.0.0.0', 0))
 98        server.listen(50)
 99        killer = eventlet.spawn(accept_close_late, server)
100        did_it_work(server)
101        killer.wait()
102    
103    def test_del_closes_socket(self):
104        def accept_once(listener):
105            # delete/overwrite the original conn
106            # object, only keeping the file object around
107            # closing the file object should close everything
108            try:
109                conn, addr = listener.accept()
110                conn = conn.makefile()
111                conn.write('hello\n')
112                conn.close()
113                conn.write('a')
114                self.assertRaises(Exception, conn.flush)
115            finally:
116                listener.close()
117        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
118        server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
119        server.bind(('127.0.0.1', 0))
120        server.listen(50)
121        killer = eventlet.spawn(accept_once, server)
122        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
123        client.connect(('127.0.0.1', server.getsockname()[1]))
124        fd = client.makefile()
125        client.close()
126        assert fd.read() == 'hello\n'    
127        assert fd.read() == ''
128        
129        killer.wait()
130     
131    def test_full_duplex(self):
132        large_data = '*' * 10 * min_buf_size()
133        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
134        listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
135        listener.bind(('127.0.0.1', 0))
136        listener.listen(50)
137        bufsized(listener)
138
139        def send_large(sock):
140            sock.sendall(large_data)
141            
142        def read_large(sock):
143            result = sock.recv(len(large_data))
144            expected = 'hello world'
145            while len(result) < len(large_data):
146                result += sock.recv(len(large_data))
147            self.assertEquals(result, large_data)
148
149        def server():
150            (sock, addr) = listener.accept()
151            sock = bufsized(sock)
152            send_large_coro = eventlet.spawn(send_large, sock)
153            eventlet.sleep(0)
154            result = sock.recv(10)
155            expected = 'hello world'
156            while len(result) < len(expected):
157                result += sock.recv(10)
158            self.assertEquals(result, expected)
159            send_large_coro.wait()
160                
161        server_evt = eventlet.spawn(server)
162        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
163        client.connect(('127.0.0.1', listener.getsockname()[1]))
164        bufsized(client)
165        large_evt = eventlet.spawn(read_large, client)
166        eventlet.sleep(0)
167        client.sendall('hello world')
168        server_evt.wait()
169        large_evt.wait()
170        client.close()
171     
172    def test_sendall(self):
173        # test adapted from Marcus Cavanaugh's email
174        # it may legitimately take a while, but will eventually complete
175        self.timer.cancel()
176        second_bytes = 10
177        def test_sendall_impl(many_bytes):
178            bufsize = max(many_bytes/15, 2)
179            def sender(listener):
180                (sock, addr) = listener.accept()
181                sock = bufsized(sock, size=bufsize)
182                sock.sendall('x'*many_bytes)
183                sock.sendall('y'*second_bytes)
184            
185            listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
186            listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
187            listener.bind(("", 0))
188            listener.listen(50)
189            sender_coro = eventlet.spawn(sender, listener)
190            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
191            client.connect(('127.0.0.1', listener.getsockname()[1]))
192            bufsized(client, size=bufsize)
193            total = 0
194            while total < many_bytes:
195                data = client.recv(min(many_bytes - total, many_bytes/10))
196                if data == '':
197                    break
198                total += len(data)
199            
200            total2 = 0
201            while total < second_bytes:
202                data = client.recv(second_bytes)
203                if data == '':
204                    break
205                total2 += len(data)
206    
207            sender_coro.wait()
208            client.close()
209        
210        for bytes in (1000, 10000, 100000, 1000000):
211            test_sendall_impl(bytes)
212        
213    def test_wrap_socket(self):
214        try:
215            import ssl
216        except ImportError:
217            pass  # pre-2.6
218        else:
219            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
220            sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
221            sock.bind(('127.0.0.1', 0))
222            sock.listen(50)
223            ssl_sock = ssl.wrap_socket(sock)
224            
225    def test_timeout_and_final_write(self):
226        # This test verifies that a write on a socket that we've
227        # stopped listening for doesn't result in an incorrect switch
228        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
229        server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
230        server.bind(('127.0.0.1', 0))
231        server.listen(50)
232        bound_port = server.getsockname()[1]
233        
234        def sender(evt):
235            s2, addr = server.accept()
236            wrap_wfile = s2.makefile()
237            
238            eventlet.sleep(0.02)
239            wrap_wfile.write('hi')
240            s2.close()
241            evt.send('sent via event')
242
243        from eventlet import event
244        evt = event.Event()
245        eventlet.spawn(sender, evt)
246        eventlet.sleep(0)  # lets the socket enter accept mode, which
247                      # is necessary for connect to succeed on windows
248        try:
249            # try and get some data off of this pipe
250            # but bail before any is sent
251            eventlet.Timeout(0.01)
252            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
253            client.connect(('127.0.0.1', bound_port))
254            wrap_rfile = client.makefile()
255            _c = wrap_rfile.read(1)
256            self.fail()
257        except eventlet.TimeoutError:
258            pass
259
260        result = evt.wait()
261        self.assertEquals(result, 'sent via event')
262        server.close()
263        client.close()
264        
265
266class TestGreenIoLong(LimitedTestCase):
267    TEST_TIMEOUT=10  # the test here might take a while depending on the OS
268    @skip_with_pyevent
269    def test_multiple_readers(self, clibufsize=False):
270        recvsize = 2 * min_buf_size()
271        sendsize = 10 * recvsize        
272        # test that we can have multiple coroutines reading
273        # from the same fd.  We make no guarantees about which one gets which
274        # bytes, but they should both get at least some
275        def reader(sock, results):
276            while True:
277                data = sock.recv(recvsize)
278                if data == '':
279                    break
280                results.append(data)
281            
282        results1 = []
283        results2 = []
284        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
285        listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
286        listener.bind(('127.0.0.1', 0))
287        listener.listen(50)
288        def server():
289            (sock, addr) = listener.accept()
290            sock = bufsized(sock)
291            try:
292                c1 = eventlet.spawn(reader, sock, results1)
293                c2 = eventlet.spawn(reader, sock, results2)
294                try:
295                    c1.wait()
296                    c2.wait()
297                finally:
298                    c1.kill()
299                    c2.kill()
300            finally:
301                sock.close()
302
303        server_coro = eventlet.spawn(server)
304        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
305        client.connect(('127.0.0.1', listener.getsockname()[1]))
306        if clibufsize:
307            bufsized(client, size=sendsize)
308        else:
309            bufsized(client)
310        client.sendall('*' * sendsize)
311        client.close()
312        server_coro.wait()
313        listener.close()
314        self.assert_(len(results1) > 0)
315        self.assert_(len(results2) > 0)
316
317    @skip_with_pyevent
318    def test_multiple_readers2(self):
319        self.test_multiple_readers(clibufsize=True)
320
321class TestGreenIoStarvation(LimitedTestCase):
322
323    # fixme: this doesn't fail, because of eventlet's predetermined
324    # ordering.  two processes, one with server, one with client eventlets
325    # might be more reliable?
326    
327    TEST_TIMEOUT=300  # the test here might take a while depending on the OS
328    @skip_with_pyevent
329    def test_server_starvation(self, sendloops=15):
330        recvsize = 2 * min_buf_size()
331        sendsize = 10000 * recvsize
332
333        results = [[] for i in xrange(5)]
334
335        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
336        listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
337        listener.bind(('127.0.0.1', 0))
338        port = listener.getsockname()[1]
339        listener.listen(50)
340
341        base_time = time.time()
342
343        def server(my_results):
344            (sock, addr) = listener.accept()
345
346            datasize = 0
347            
348            t1 = None
349            t2 = None
350            try:
351                while True:
352                    data = sock.recv(recvsize)
353                    if not t1:
354                        t1 = time.time() - base_time
355                    if data == '':
356                        t2 = time.time() - base_time
357                        my_results.append(datasize)
358                        my_results.append((t1,t2))
359                        break
360                    datasize += len(data)
361            finally:
362                sock.close()
363
364        def client():
365            pid = os.fork()
366            if pid:
367                return pid
368    
369            client = _orig_sock.socket(socket.AF_INET, socket.SOCK_STREAM)
370            client.connect(('127.0.0.1', port))
371
372            bufsized(client, size=sendsize)
373
374            for i in range(sendloops):
375                client.sendall('*' * sendsize)
376            client.close()
377            os._exit(0)
378
379        clients = []
380        servers = []
381        for r in results:
382            servers.append(eventlet.spawn(server, r))
383        for r in results:
384            clients.append(client())
385
386        for s in servers:
387            s.wait()
388        for c in clients:
389            os.waitpid(c, 0)
390
391        listener.close()
392
393        # now test that all of the server receive intervals overlap, and
394        # that there were no errors.
395        for r in results:
396            assert len(r) == 2, "length is %d not 2!: %s\n%s" % (len(r), r, results)
397            assert r[0] == sendsize * sendloops
398            assert len(r[1]) == 2
399            assert r[1][0] is not None
400            assert r[1][1] is not None
401
402        starttimes = sorted(r[1][0] for r in results)
403        endtimes = sorted(r[1][1] for r in results)
404        runlengths = sorted(r[1][1] - r[1][0] for r in results)
405
406        # assert that the last task started before the first task ended
407        # (our no-starvation condition)
408        assert starttimes[-1] < endtimes[0], "Not overlapping: starts %s ends %s" % (starttimes, endtimes)
409
410        maxstartdiff = starttimes[-1] - starttimes[0]
411
412        assert maxstartdiff * 2 < runlengths[0], "Largest difference in starting times more than twice the shortest running time!"
413        assert runlengths[0] * 2 > runlengths[-1], "Longest runtime more than twice as long as shortest!"
414
415if __name__ == '__main__':
416    main()