mirror of
https://github.com/python/cpython.git
synced 2025-10-17 12:18:23 +00:00
bpo-35347: Fix test_socket.NonBlockingTCPTests (GH-10791)
testAccept() and testRecv() of test_socket.NonBlockingTCPTests have a race condition: time.sleep() is used as a weak synchronization primitive and the tests fail randomly on slow buildbots. Use a reliable threading.Event to fix these tests. Other changes: * Replace send() with sendall() * Expect specific BlockingIOError rather than generic OSError * Add a timeout to select() in testAccept() and testRecv() * Use addCleanup() to close sockets * Use assertRaises()
This commit is contained in:
parent
55e498058f
commit
ebd5d6d6e6
1 changed files with 39 additions and 25 deletions
|
@ -35,6 +35,7 @@ except ImportError:
|
||||||
|
|
||||||
HOST = support.HOST
|
HOST = support.HOST
|
||||||
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return
|
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return
|
||||||
|
MAIN_TIMEOUT = 60.0
|
||||||
|
|
||||||
VSOCKPORT = 1234
|
VSOCKPORT = 1234
|
||||||
|
|
||||||
|
@ -4214,6 +4215,7 @@ class BasicSocketPairTest(SocketPairTest):
|
||||||
class NonBlockingTCPTests(ThreadedTCPSocketTest):
|
class NonBlockingTCPTests(ThreadedTCPSocketTest):
|
||||||
|
|
||||||
def __init__(self, methodName='runTest'):
|
def __init__(self, methodName='runTest'):
|
||||||
|
self.event = threading.Event()
|
||||||
ThreadedTCPSocketTest.__init__(self, methodName=methodName)
|
ThreadedTCPSocketTest.__init__(self, methodName=methodName)
|
||||||
|
|
||||||
def testSetBlocking(self):
|
def testSetBlocking(self):
|
||||||
|
@ -4326,22 +4328,27 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
|
||||||
def testAccept(self):
|
def testAccept(self):
|
||||||
# Testing non-blocking accept
|
# Testing non-blocking accept
|
||||||
self.serv.setblocking(0)
|
self.serv.setblocking(0)
|
||||||
try:
|
|
||||||
|
# connect() didn't start: non-blocking accept() fails
|
||||||
|
with self.assertRaises(BlockingIOError):
|
||||||
conn, addr = self.serv.accept()
|
conn, addr = self.serv.accept()
|
||||||
except OSError:
|
|
||||||
pass
|
self.event.set()
|
||||||
else:
|
|
||||||
self.fail("Error trying to do non-blocking accept.")
|
read, write, err = select.select([self.serv], [], [], MAIN_TIMEOUT)
|
||||||
read, write, err = select.select([self.serv], [], [])
|
if self.serv not in read:
|
||||||
if self.serv in read:
|
|
||||||
conn, addr = self.serv.accept()
|
|
||||||
self.assertIsNone(conn.gettimeout())
|
|
||||||
conn.close()
|
|
||||||
else:
|
|
||||||
self.fail("Error trying to do accept after select.")
|
self.fail("Error trying to do accept after select.")
|
||||||
|
|
||||||
|
# connect() completed: non-blocking accept() doesn't block
|
||||||
|
conn, addr = self.serv.accept()
|
||||||
|
self.addCleanup(conn.close)
|
||||||
|
self.assertIsNone(conn.gettimeout())
|
||||||
|
|
||||||
def _testAccept(self):
|
def _testAccept(self):
|
||||||
time.sleep(0.1)
|
# don't connect before event is set to check
|
||||||
|
# that non-blocking accept() raises BlockingIOError
|
||||||
|
self.event.wait()
|
||||||
|
|
||||||
self.cli.connect((HOST, self.port))
|
self.cli.connect((HOST, self.port))
|
||||||
|
|
||||||
def testConnect(self):
|
def testConnect(self):
|
||||||
|
@ -4356,25 +4363,32 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
|
||||||
def testRecv(self):
|
def testRecv(self):
|
||||||
# Testing non-blocking recv
|
# Testing non-blocking recv
|
||||||
conn, addr = self.serv.accept()
|
conn, addr = self.serv.accept()
|
||||||
|
self.addCleanup(conn.close)
|
||||||
conn.setblocking(0)
|
conn.setblocking(0)
|
||||||
try:
|
|
||||||
|
# the server didn't send data yet: non-blocking recv() fails
|
||||||
|
with self.assertRaises(BlockingIOError):
|
||||||
msg = conn.recv(len(MSG))
|
msg = conn.recv(len(MSG))
|
||||||
except OSError:
|
|
||||||
pass
|
self.event.set()
|
||||||
else:
|
|
||||||
self.fail("Error trying to do non-blocking recv.")
|
read, write, err = select.select([conn], [], [], MAIN_TIMEOUT)
|
||||||
read, write, err = select.select([conn], [], [])
|
if conn not in read:
|
||||||
if conn in read:
|
|
||||||
msg = conn.recv(len(MSG))
|
|
||||||
conn.close()
|
|
||||||
self.assertEqual(msg, MSG)
|
|
||||||
else:
|
|
||||||
self.fail("Error during select call to non-blocking socket.")
|
self.fail("Error during select call to non-blocking socket.")
|
||||||
|
|
||||||
|
# the server sent data yet: non-blocking recv() doesn't block
|
||||||
|
msg = conn.recv(len(MSG))
|
||||||
|
self.assertEqual(msg, MSG)
|
||||||
|
|
||||||
def _testRecv(self):
|
def _testRecv(self):
|
||||||
self.cli.connect((HOST, self.port))
|
self.cli.connect((HOST, self.port))
|
||||||
time.sleep(0.1)
|
|
||||||
self.cli.send(MSG)
|
# don't send anything before event is set to check
|
||||||
|
# that non-blocking recv() raises BlockingIOError
|
||||||
|
self.event.wait()
|
||||||
|
|
||||||
|
# send data: recv() will no longer block
|
||||||
|
self.cli.sendall(MSG)
|
||||||
|
|
||||||
|
|
||||||
class FileObjectClassTestCase(SocketConnectedTest):
|
class FileObjectClassTestCase(SocketConnectedTest):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue