From 3f164d92bfc856709f5467b0c3346414564a96e2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 Apr 2017 23:42:46 +0200 Subject: [PATCH 1/3] Fix/optimize test_asyncore.test_quick_connect() (#1188) Don't use addCleanup() in test_quick_connect() because it keeps the Thread object alive and so @reap_threads fails on its timeout of 1 second. "./python -m test -v test_asyncore -m test_quick_connect" now takes 185 ms, instead of 11 seconds. Other minor changes: * Use "with sock:" to close the socket instead of try/finally: sock.close() * Use self.skipTest() in test_quick_connect() to remove one indentation level and notice user that the test is specific to AF_INET and AF_INET6 --- Lib/test/test_asyncore.py | 77 +++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index d05462b7efce35..270e9ccba6bd78 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -755,50 +755,49 @@ def test_bind(self): def test_set_reuse_addr(self): if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX: self.skipTest("Not applicable to AF_UNIX sockets.") - sock = socket.socket(self.family) - try: - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - except OSError: - unittest.skip("SO_REUSEADDR not supported on this platform") - else: - # if SO_REUSEADDR succeeded for sock we expect asyncore - # to do the same - s = asyncore.dispatcher(socket.socket(self.family)) - self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR)) - s.socket.close() - s.create_socket(self.family) - s.set_reuse_addr() - self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR)) - finally: - sock.close() + + with socket.socket(self.family) as sock: + try: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + except OSError: + unittest.skip("SO_REUSEADDR not supported on this platform") + else: + # if SO_REUSEADDR succeeded for sock we expect asyncore + # to do the same + s = asyncore.dispatcher(socket.socket(self.family)) + self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR)) + s.socket.close() + s.create_socket(self.family) + s.set_reuse_addr() + self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR)) @unittest.skipUnless(threading, 'Threading required for this test.') @support.reap_threads def test_quick_connect(self): # see: http://bugs.python.org/issue10340 - if self.family in (socket.AF_INET, getattr(socket, "AF_INET6", object())): - server = BaseServer(self.family, self.addr) - t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, - count=500)) - t.start() - def cleanup(): - t.join(timeout=TIMEOUT) - if t.is_alive(): - self.fail("join() timed out") - self.addCleanup(cleanup) - - s = socket.socket(self.family, socket.SOCK_STREAM) - s.settimeout(.2) - s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, - struct.pack('ii', 1, 0)) - try: - s.connect(server.address) - except OSError: - pass - finally: - s.close() + if self.family not in (socket.AF_INET, getattr(socket, "AF_INET6", object())): + self.skipTest("test specific to AF_INET and AF_INET6") + + server = BaseServer(self.family, self.addr) + t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, + count=500), name="ident") + t.start() + try: + with socket.socket(self.family, socket.SOCK_STREAM) as s: + s.settimeout(.2) + s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, + struct.pack('ii', 1, 0)) + + try: + s.connect(server.address) + except OSError: + pass + finally: + t.join(timeout=TIMEOUT) + if t.is_alive(): + self.fail("join() timed out") class TestAPI_UseIPv4Sockets(BaseTestAPI): family = socket.AF_INET From fed2a0529c995a93e6a562e07740de0d5867e666 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 20 Apr 2017 02:55:39 +0200 Subject: [PATCH 2/3] bpo-30106: Fix tearDown() of test_asyncore (#1194) Call asyncore.close_all() with ignore_all=True in the tearDown() method of the test_asyncore base test case. It should prevent keeping alive sockets in asyncore.socket_map if close() fails with an unexpected error. Revert also an unwanted change of my previous commit: remove name parameter of Thread in test_quick_connect(). --- Lib/test/test_asyncore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 270e9ccba6bd78..1e276ec58b9f76 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -502,7 +502,7 @@ def handle_connect(self): class BaseTestAPI: def tearDown(self): - asyncore.close_all() + asyncore.close_all(ignore_all=True) def loop_waiting_for_flag(self, instance, timeout=5): timeout = float(timeout) / 100 @@ -782,7 +782,7 @@ def test_quick_connect(self): server = BaseServer(self.family, self.addr) t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, - count=500), name="ident") + count=500)) t.start() try: with socket.socket(self.family, socket.SOCK_STREAM) as s: From 34595da83e14b9a325b716e4fafd158b55688cc5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 21 Apr 2017 13:51:53 +0200 Subject: [PATCH 3/3] bpo-30106: Fix test_asyncore.test_quick_connect() (#1234) test_quick_connect() runs a thread up to 50 seconds, whereas the socket is connected in 0.2 second and then the thread is expected to end in less than 3 second. On Linux, the thread ends quickly because select() seems to always return quickly. On FreeBSD, sometimes select() fails with timeout and so the thread runs much longer than expected. Fix the thread timeout to fix a race condition in the test. --- Lib/test/test_asyncore.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py index 1e276ec58b9f76..dc2f716e0bb828 100644 --- a/Lib/test/test_asyncore.py +++ b/Lib/test/test_asyncore.py @@ -781,8 +781,9 @@ def test_quick_connect(self): self.skipTest("test specific to AF_INET and AF_INET6") server = BaseServer(self.family, self.addr) + # run the thread 500 ms: the socket should be connected in 200 ms t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, - count=500)) + count=5)) t.start() try: with socket.socket(self.family, socket.SOCK_STREAM) as s: