PageRenderTime 7ms CodeModel.GetById 2ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/greenio_test.py

https://bitbucket.org/temoto/eventlet
Python | 825 lines | 787 code | 20 blank | 18 comment | 10 complexity | a70ea525121fddab3f33fdae3a7bf907 MD5 | raw file
  1import socket as _orig_sock
  2from tests import LimitedTestCase, skip_with_pyevent, main, skipped, s2b, skip_if, skip_on_windows
  3from eventlet import event, greenio, debug
  4from eventlet.hubs import get_hub
  5from eventlet.green import socket, time
  6from eventlet.support import get_errno
  7import errno
  8
  9import eventlet
 10import os
 11import sys
 12import array
 13import tempfile, shutil
 14
 15
 16def bufsized(sock, size=1):
 17    """ Resize both send and receive buffers on a socket.
 18    Useful for testing trampoline.  Returns the socket.
 19
 20    >>> import socket
 21    >>> sock = bufsized(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
 22    """
 23    sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, size)
 24    sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, size)
 25    return sock
 26
 27
 28def min_buf_size():
 29    """Return the minimum buffer size that the platform supports."""
 30    test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 31    test_sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)
 32    return test_sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
 33
 34
 35def using_epoll_hub(_f):
 36        try:
 37            return 'epolls' in type(get_hub()).__module__
 38        except Exception:
 39            return False
 40
 41
 42class TestGreenSocket(LimitedTestCase):
 43    def assertWriteToClosedFileRaises(self, fd):
 44        if sys.version_info[0] < 3:
 45            # 2.x socket._fileobjects are odd: writes don't check
 46            # whether the socket is closed or not, and you get an
 47            # AttributeError during flush if it is closed
 48            fd.write('a')
 49            self.assertRaises(Exception, fd.flush)
 50        else:
 51            # 3.x io write to closed file-like pbject raises ValueError
 52            self.assertRaises(ValueError, fd.write, 'a')
 53
 54    def test_connect_timeout(self):
 55        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 56        s.settimeout(0.1)
 57        gs = greenio.GreenSocket(s)
 58        try:
 59            gs.connect(('192.0.2.1', 80))
 60            self.fail("socket.timeout not raised")
 61        except socket.timeout, e:
 62            self.assert_(hasattr(e, 'args'))
 63            self.assertEqual(e.args[0], 'timed out')
 64        except socket.error, e:
 65            # unreachable is also a valid outcome
 66            if not get_errno(e) in (errno.EHOSTUNREACH, errno.ENETUNREACH):
 67                raise
 68
 69    def test_accept_timeout(self):
 70        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 71        s.bind(('', 0))
 72        s.listen(50)
 73
 74        s.settimeout(0.1)
 75        gs = greenio.GreenSocket(s)
 76        try:
 77            gs.accept()
 78            self.fail("socket.timeout not raised")
 79        except socket.timeout, e:
 80            self.assert_(hasattr(e, 'args'))
 81            self.assertEqual(e.args[0], 'timed out')
 82
 83    def test_connect_ex_timeout(self):
 84        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 85        s.settimeout(0.1)
 86        gs = greenio.GreenSocket(s)
 87        e = gs.connect_ex(('192.0.2.1', 80))
 88        if not e in (errno.EHOSTUNREACH, errno.ENETUNREACH):
 89            self.assertEquals(e, errno.EAGAIN)
 90
 91    def test_recv_timeout(self):
 92        listener = greenio.GreenSocket(socket.socket())
 93        listener.bind(('', 0))
 94        listener.listen(50)
 95
 96        evt = event.Event()
 97
 98        def server():
 99            # accept the connection in another greenlet
100            sock, addr = listener.accept()
101            evt.wait()
102
103        gt = eventlet.spawn(server)
104
105        addr = listener.getsockname()
106
107        client = greenio.GreenSocket(socket.socket())
108        client.settimeout(0.1)
109
110        client.connect(addr)
111
112        try:
113            client.recv(8192)
114            self.fail("socket.timeout not raised")
115        except socket.timeout, e:
116            self.assert_(hasattr(e, 'args'))
117            self.assertEqual(e.args[0], 'timed out')
118
119        evt.send()
120        gt.wait()
121
122    def test_recvfrom_timeout(self):
123        gs = greenio.GreenSocket(
124            socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
125        gs.settimeout(.1)
126        gs.bind(('', 0))
127
128        try:
129            gs.recvfrom(8192)
130            self.fail("socket.timeout not raised")
131        except socket.timeout, e:
132            self.assert_(hasattr(e, 'args'))
133            self.assertEqual(e.args[0], 'timed out')
134
135    def test_recvfrom_into_timeout(self):
136        buf = buffer(array.array('B'))
137
138        gs = greenio.GreenSocket(
139            socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
140        gs.settimeout(.1)
141        gs.bind(('', 0))
142
143        try:
144            gs.recvfrom_into(buf)
145            self.fail("socket.timeout not raised")
146        except socket.timeout, e:
147            self.assert_(hasattr(e, 'args'))
148            self.assertEqual(e.args[0], 'timed out')
149
150    def test_recv_into_timeout(self):
151        buf = buffer(array.array('B'))
152
153        listener = greenio.GreenSocket(socket.socket())
154        listener.bind(('', 0))
155        listener.listen(50)
156
157        evt = event.Event()
158
159        def server():
160            # accept the connection in another greenlet
161            sock, addr = listener.accept()
162            evt.wait()
163
164        gt = eventlet.spawn(server)
165
166        addr = listener.getsockname()
167
168        client = greenio.GreenSocket(socket.socket())
169        client.settimeout(0.1)
170
171        client.connect(addr)
172
173        try:
174            client.recv_into(buf)
175            self.fail("socket.timeout not raised")
176        except socket.timeout, e:
177            self.assert_(hasattr(e, 'args'))
178            self.assertEqual(e.args[0], 'timed out')
179
180        evt.send()
181        gt.wait()
182
183    def test_send_timeout(self):
184        listener = bufsized(eventlet.listen(('', 0)))
185
186        evt = event.Event()
187
188        def server():
189            # accept the connection in another greenlet
190            sock, addr = listener.accept()
191            sock = bufsized(sock)
192            evt.wait()
193
194        gt = eventlet.spawn(server)
195
196        addr = listener.getsockname()
197
198        client = bufsized(greenio.GreenSocket(socket.socket()))
199        client.connect(addr)
200        try:
201            client.settimeout(0.00001)
202            msg = s2b("A") * 100000  # large enough number to overwhelm most buffers
203
204            total_sent = 0
205            # want to exceed the size of the OS buffer so it'll block in a
206            # single send
207            for x in range(10):
208                total_sent += client.send(msg)
209            self.fail("socket.timeout not raised")
210        except socket.timeout, e:
211            self.assert_(hasattr(e, 'args'))
212            self.assertEqual(e.args[0], 'timed out')
213
214        evt.send()
215        gt.wait()
216
217    def test_sendall_timeout(self):
218        listener = greenio.GreenSocket(socket.socket())
219        listener.bind(('', 0))
220        listener.listen(50)
221
222        evt = event.Event()
223
224        def server():
225            # accept the connection in another greenlet
226            sock, addr = listener.accept()
227            evt.wait()
228
229        gt = eventlet.spawn(server)
230
231        addr = listener.getsockname()
232
233        client = greenio.GreenSocket(socket.socket())
234        client.settimeout(0.1)
235        client.connect(addr)
236
237        try:
238            msg = s2b("A") * (8 << 20)
239
240            # want to exceed the size of the OS buffer so it'll block
241            client.sendall(msg)
242            self.fail("socket.timeout not raised")
243        except socket.timeout, e:
244            self.assert_(hasattr(e, 'args'))
245            self.assertEqual(e.args[0], 'timed out')
246
247        evt.send()
248        gt.wait()
249
250    def test_close_with_makefile(self):
251        def accept_close_early(listener):
252            # verify that the makefile and the socket are truly independent
253            # by closing the socket prior to using the made file
254            try:
255                conn, addr = listener.accept()
256                fd = conn.makefile('w')
257                conn.close()
258                fd.write('hello\n')
259                fd.close()
260                self.assertWriteToClosedFileRaises(fd)
261                self.assertRaises(socket.error, conn.send, s2b('b'))
262            finally:
263                listener.close()
264
265        def accept_close_late(listener):
266            # verify that the makefile and the socket are truly independent
267            # by closing the made file and then sending a character
268            try:
269                conn, addr = listener.accept()
270                fd = conn.makefile('w')
271                fd.write('hello')
272                fd.close()
273                conn.send(s2b('\n'))
274                conn.close()
275                self.assertWriteToClosedFileRaises(fd)
276                self.assertRaises(socket.error, conn.send, s2b('b'))
277            finally:
278                listener.close()
279
280        def did_it_work(server):
281            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
282            client.connect(('127.0.0.1', server.getsockname()[1]))
283            fd = client.makefile()
284            client.close()
285            assert fd.readline() == 'hello\n'
286            assert fd.read() == ''
287            fd.close()
288
289        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
290        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
291        server.bind(('0.0.0.0', 0))
292        server.listen(50)
293        killer = eventlet.spawn(accept_close_early, server)
294        did_it_work(server)
295        killer.wait()
296
297        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
298        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
299        server.bind(('0.0.0.0', 0))
300        server.listen(50)
301        killer = eventlet.spawn(accept_close_late, server)
302        did_it_work(server)
303        killer.wait()
304
305    def test_del_closes_socket(self):
306        def accept_once(listener):
307            # delete/overwrite the original conn
308            # object, only keeping the file object around
309            # closing the file object should close everything
310            try:
311                conn, addr = listener.accept()
312                conn = conn.makefile('w')
313                conn.write('hello\n')
314                conn.close()
315                self.assertWriteToClosedFileRaises(conn)
316            finally:
317                listener.close()
318
319        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
320        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
321        server.bind(('127.0.0.1', 0))
322        server.listen(50)
323        killer = eventlet.spawn(accept_once, server)
324        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
325        client.connect(('127.0.0.1', server.getsockname()[1]))
326        fd = client.makefile()
327        client.close()
328        assert fd.read() == 'hello\n'
329        assert fd.read() == ''
330
331        killer.wait()
332
333    def test_full_duplex(self):
334        large_data = s2b('*') * 10 * min_buf_size()
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        listener.listen(50)
339        bufsized(listener)
340
341        def send_large(sock):
342            sock.sendall(large_data)
343
344        def read_large(sock):
345            result = sock.recv(len(large_data))
346            while len(result) < len(large_data):
347                result += sock.recv(len(large_data))
348            self.assertEquals(result, large_data)
349
350        def server():
351            (sock, addr) = listener.accept()
352            sock = bufsized(sock)
353            send_large_coro = eventlet.spawn(send_large, sock)
354            eventlet.sleep(0)
355            result = sock.recv(10)
356            expected = s2b('hello world')
357            while len(result) < len(expected):
358                result += sock.recv(10)
359            self.assertEquals(result, expected)
360            send_large_coro.wait()
361
362        server_evt = eventlet.spawn(server)
363        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
364        client.connect(('127.0.0.1', listener.getsockname()[1]))
365        bufsized(client)
366        large_evt = eventlet.spawn(read_large, client)
367        eventlet.sleep(0)
368        client.sendall(s2b('hello world'))
369        server_evt.wait()
370        large_evt.wait()
371        client.close()
372
373    def test_sendall(self):
374        # test adapted from Marcus Cavanaugh's email
375        # it may legitimately take a while, but will eventually complete
376        self.timer.cancel()
377        second_bytes = 10
378
379        def test_sendall_impl(many_bytes):
380            bufsize = max(many_bytes // 15, 2)
381
382            def sender(listener):
383                (sock, addr) = listener.accept()
384                sock = bufsized(sock, size=bufsize)
385                sock.sendall(s2b('x') * many_bytes)
386                sock.sendall(s2b('y') * second_bytes)
387
388            listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
389            listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
390            listener.bind(("", 0))
391            listener.listen(50)
392            sender_coro = eventlet.spawn(sender, listener)
393            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
394            client.connect(('127.0.0.1', listener.getsockname()[1]))
395            bufsized(client, size=bufsize)
396            total = 0
397            while total < many_bytes:
398                data = client.recv(min(many_bytes - total, many_bytes // 10))
399                if not data:
400                    break
401                total += len(data)
402
403            total2 = 0
404            while total < second_bytes:
405                data = client.recv(second_bytes)
406                if not data:
407                    break
408                total2 += len(data)
409
410            sender_coro.wait()
411            client.close()
412
413        for how_many in (1000, 10000, 100000, 1000000):
414            test_sendall_impl(how_many)
415
416    def test_wrap_socket(self):
417        try:
418            import ssl
419        except ImportError:
420            pass  # pre-2.6
421        else:
422            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
423            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
424            sock.bind(('127.0.0.1', 0))
425            sock.listen(50)
426            ssl.wrap_socket(sock)
427
428    def test_timeout_and_final_write(self):
429        # This test verifies that a write on a socket that we've
430        # stopped listening for doesn't result in an incorrect switch
431        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
432        server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
433        server.bind(('127.0.0.1', 0))
434        server.listen(50)
435        bound_port = server.getsockname()[1]
436
437        def sender(evt):
438            s2, addr = server.accept()
439            wrap_wfile = s2.makefile('w')
440
441            eventlet.sleep(0.02)
442            wrap_wfile.write('hi')
443            s2.close()
444            evt.send('sent via event')
445
446        evt = event.Event()
447        eventlet.spawn(sender, evt)
448        eventlet.sleep(0)  # lets the socket enter accept mode, which
449                      # is necessary for connect to succeed on windows
450        try:
451            # try and get some data off of this pipe
452            # but bail before any is sent
453            eventlet.Timeout(0.01)
454            client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
455            client.connect(('127.0.0.1', bound_port))
456            wrap_rfile = client.makefile()
457            wrap_rfile.read(1)
458            self.fail()
459        except eventlet.TimeoutError:
460            pass
461
462        result = evt.wait()
463        self.assertEquals(result, 'sent via event')
464        server.close()
465        client.close()
466
467    @skip_with_pyevent
468    def test_raised_multiple_readers(self):
469        debug.hub_prevent_multiple_readers(True)
470
471        def handle(sock, addr):
472            sock.recv(1)
473            sock.sendall("a")
474            raise eventlet.StopServe()
475
476        listener = eventlet.listen(('127.0.0.1', 0))
477        eventlet.spawn(eventlet.serve, listener, handle)
478
479        def reader(s):
480            s.recv(1)
481
482        s = eventlet.connect(('127.0.0.1', listener.getsockname()[1]))
483        a = eventlet.spawn(reader, s)
484        eventlet.sleep(0)
485        self.assertRaises(RuntimeError, s.recv, 1)
486        s.sendall('b')
487        a.wait()
488
489    @skip_with_pyevent
490    @skip_if(using_epoll_hub)
491    def test_closure(self):
492        def spam_to_me(address):
493            sock = eventlet.connect(address)
494            while True:
495                try:
496                    sock.sendall('hello world')
497                except socket.error, e:
498                    if get_errno(e) == errno.EPIPE:
499                        return
500                    raise
501
502        server = eventlet.listen(('127.0.0.1', 0))
503        sender = eventlet.spawn(spam_to_me, server.getsockname())
504        client, address = server.accept()
505        server.close()
506
507        def reader():
508            try:
509                while True:
510                    data = client.recv(1024)
511                    self.assert_(data)
512            except socket.error, e:
513                # we get an EBADF because client is closed in the same process
514                # (but a different greenthread)
515                if get_errno(e) != errno.EBADF:
516                    raise
517
518        def closer():
519            client.close()
520
521        reader = eventlet.spawn(reader)
522        eventlet.spawn_n(closer)
523        reader.wait()
524        sender.wait()
525
526    def test_invalid_connection(self):
527        # find an unused port by creating a socket then closing it
528        port = eventlet.listen(('127.0.0.1', 0)).getsockname()[1]
529        self.assertRaises(socket.error, eventlet.connect, ('127.0.0.1', port))
530
531    def test_zero_timeout_and_back(self):
532        listen = eventlet.listen(('', 0))
533        # Keep reference to server side of socket
534        server = eventlet.spawn(listen.accept)
535        client = eventlet.connect(listen.getsockname())
536
537        client.settimeout(0.05)
538        # Now must raise socket.timeout
539        self.assertRaises(socket.timeout, client.recv, 1)
540
541        client.settimeout(0)
542        # Now must raise socket.error with EAGAIN
543        try:
544            client.recv(1)
545            assert False
546        except socket.error, e:
547            assert get_errno(e) == errno.EAGAIN
548
549        client.settimeout(0.05)
550        # Now socket.timeout again
551        self.assertRaises(socket.timeout, client.recv, 1)
552        server.wait()
553
554
555class TestGreenPipe(LimitedTestCase):
556    @skip_on_windows
557    def setUp(self):
558        super(self.__class__, self).setUp()
559        self.tempdir = tempfile.mkdtemp('_green_pipe_test')
560
561    def tearDown(self):
562        shutil.rmtree(self.tempdir)
563        super(self.__class__, self).tearDown()
564
565    def test_pipe(self):
566        r, w = os.pipe()
567        rf = greenio.GreenPipe(r, 'r')
568        wf = greenio.GreenPipe(w, 'w', 0)
569
570        def sender(f, content):
571            for ch in content:
572                eventlet.sleep(0.0001)
573                f.write(ch)
574            f.close()
575
576        one_line = "12345\n"
577        eventlet.spawn(sender, wf, one_line * 5)
578        for i in xrange(5):
579            line = rf.readline()
580            eventlet.sleep(0.01)
581            self.assertEquals(line, one_line)
582        self.assertEquals(rf.readline(), '')
583
584    def test_pipe_read(self):
585        # ensure that 'readline' works properly on GreenPipes when data is not
586        # immediately available (fd is nonblocking, was raising EAGAIN)
587        # also ensures that readline() terminates on '\n' and '\r\n'
588        r, w = os.pipe()
589
590        r = greenio.GreenPipe(r)
591        w = greenio.GreenPipe(w, 'w')
592
593        def writer():
594            eventlet.sleep(.1)
595
596            w.write('line\n')
597            w.flush()
598
599            w.write('line\r\n')
600            w.flush()
601
602        gt = eventlet.spawn(writer)
603
604        eventlet.sleep(0)
605
606        line = r.readline()
607        self.assertEquals(line, 'line\n')
608
609        line = r.readline()
610        self.assertEquals(line, 'line\r\n')
611
612        gt.wait()
613
614    def test_pipe_writes_large_messages(self):
615        r, w = os.pipe()
616
617        r = greenio.GreenPipe(r)
618        w = greenio.GreenPipe(w, 'w')
619
620        large_message = "".join([1024 * chr(i) for i in xrange(65)])
621
622        def writer():
623            w.write(large_message)
624            w.close()
625
626        gt = eventlet.spawn(writer)
627
628        for i in xrange(65):
629            buf = r.read(1024)
630            expected = 1024 * chr(i)
631            self.assertEquals(buf, expected,
632                "expected=%r..%r, found=%r..%r iter=%d"
633                % (expected[:4], expected[-4:], buf[:4], buf[-4:], i))
634        gt.wait()
635
636    def test_seek_on_buffered_pipe(self):
637        f = greenio.GreenPipe(self.tempdir + "/TestFile", 'w+', 1024)
638        self.assertEquals(f.tell(), 0)
639        f.seek(0, 2)
640        self.assertEquals(f.tell(), 0)
641        f.write('1234567890')
642        f.seek(0, 2)
643        self.assertEquals(f.tell(), 10)
644        f.seek(0)
645        value = f.read(1)
646        self.assertEqual(value, '1')
647        self.assertEquals(f.tell(), 1)
648        value = f.read(1)
649        self.assertEqual(value, '2')
650        self.assertEquals(f.tell(), 2)
651        f.seek(0, 1)
652        self.assertEqual(f.readline(), '34567890')
653        f.seek(-5, 1)
654        self.assertEqual(f.readline(), '67890')
655        f.seek(0)
656        self.assertEqual(f.readline(), '1234567890')
657        f.seek(0, 2)
658        self.assertEqual(f.readline(), '')
659
660    def test_truncate(self):
661        f = greenio.GreenPipe(self.tempdir + "/TestFile", 'w+', 1024)
662        f.write('1234567890')
663        f.truncate(9)
664        self.assertEquals(f.tell(), 9)
665
666
667class TestGreenIoLong(LimitedTestCase):
668    TEST_TIMEOUT = 10  # the test here might take a while depending on the OS
669
670    @skip_with_pyevent
671    def test_multiple_readers(self, clibufsize=False):
672        debug.hub_prevent_multiple_readers(False)
673        recvsize = 2 * min_buf_size()
674        sendsize = 10 * recvsize
675
676        # test that we can have multiple coroutines reading
677        # from the same fd.  We make no guarantees about which one gets which
678        # bytes, but they should both get at least some
679        def reader(sock, results):
680            while True:
681                data = sock.recv(recvsize)
682                if not data:
683                    break
684                results.append(data)
685
686        results1 = []
687        results2 = []
688        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
689        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
690        listener.bind(('127.0.0.1', 0))
691        listener.listen(50)
692
693        def server():
694            (sock, addr) = listener.accept()
695            sock = bufsized(sock)
696            try:
697                c1 = eventlet.spawn(reader, sock, results1)
698                c2 = eventlet.spawn(reader, sock, results2)
699                try:
700                    c1.wait()
701                    c2.wait()
702                finally:
703                    c1.kill()
704                    c2.kill()
705            finally:
706                sock.close()
707
708        server_coro = eventlet.spawn(server)
709        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
710        client.connect(('127.0.0.1', listener.getsockname()[1]))
711        if clibufsize:
712            bufsized(client, size=sendsize)
713        else:
714            bufsized(client)
715        client.sendall(s2b('*') * sendsize)
716        client.close()
717        server_coro.wait()
718        listener.close()
719        self.assert_(len(results1) > 0)
720        self.assert_(len(results2) > 0)
721        debug.hub_prevent_multiple_readers()
722
723    @skipped  # by rdw because it fails but it's not clear how to make it pass
724    @skip_with_pyevent
725    def test_multiple_readers2(self):
726        self.test_multiple_readers(clibufsize=True)
727
728
729class TestGreenIoStarvation(LimitedTestCase):
730    # fixme: this doesn't succeed, because of eventlet's predetermined
731    # ordering.  two processes, one with server, one with client eventlets
732    # might be more reliable?
733
734    TEST_TIMEOUT = 300  # the test here might take a while depending on the OS
735
736    @skipped  # by rdw, because it fails but it's not clear how to make it pass
737    @skip_with_pyevent
738    def test_server_starvation(self, sendloops=15):
739        recvsize = 2 * min_buf_size()
740        sendsize = 10000 * recvsize
741
742        results = [[] for i in xrange(5)]
743
744        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
745        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
746        listener.bind(('127.0.0.1', 0))
747        port = listener.getsockname()[1]
748        listener.listen(50)
749
750        base_time = time.time()
751
752        def server(my_results):
753            sock, addr = listener.accept()
754
755            datasize = 0
756
757            t1 = None
758            t2 = None
759            try:
760                while True:
761                    data = sock.recv(recvsize)
762                    if not t1:
763                        t1 = time.time() - base_time
764                    if not data:
765                        t2 = time.time() - base_time
766                        my_results.append(datasize)
767                        my_results.append((t1, t2))
768                        break
769                    datasize += len(data)
770            finally:
771                sock.close()
772
773        def client():
774            pid = os.fork()
775            if pid:
776                return pid
777
778            client = _orig_sock.socket(socket.AF_INET, socket.SOCK_STREAM)
779            client.connect(('127.0.0.1', port))
780
781            bufsized(client, size=sendsize)
782
783            for i in range(sendloops):
784                client.sendall(s2b('*') * sendsize)
785            client.close()
786            os._exit(0)
787
788        clients = []
789        servers = []
790        for r in results:
791            servers.append(eventlet.spawn(server, r))
792        for r in results:
793            clients.append(client())
794
795        for s in servers:
796            s.wait()
797        for c in clients:
798            os.waitpid(c, 0)
799
800        listener.close()
801
802        # now test that all of the server receive intervals overlap, and
803        # that there were no errors.
804        for r in results:
805            assert len(r) == 2, "length is %d not 2!: %s\n%s" % (len(r), r, results)
806            assert r[0] == sendsize * sendloops
807            assert len(r[1]) == 2
808            assert r[1][0] is not None
809            assert r[1][1] is not None
810
811        starttimes = sorted(r[1][0] for r in results)
812        endtimes = sorted(r[1][1] for r in results)
813        runlengths = sorted(r[1][1] - r[1][0] for r in results)
814
815        # assert that the last task started before the first task ended
816        # (our no-starvation condition)
817        assert starttimes[-1] < endtimes[0], "Not overlapping: starts %s ends %s" % (starttimes, endtimes)
818
819        maxstartdiff = starttimes[-1] - starttimes[0]
820
821        assert maxstartdiff * 2 < runlengths[0], "Largest difference in starting times more than twice the shortest running time!"
822        assert runlengths[0] * 2 > runlengths[-1], "Longest runtime more than twice as long as shortest!"
823
824if __name__ == '__main__':
825    main()