bpo-35347: Cleanup test_socket.NonBlockingTCPTests (GH-10818)

* Replace testInheritFlags() with two tests:
  testInheritFlagsBlocking() and testInheritFlagsTimeout()
  to test different default socket timeout. Moreover, the test now
  checks sock.gettimeout() rather than a functional test on recv().
* Replace time.time() with time.monotonic()
* Add socket_setdefaulttimeout() context manager to restore the
  default timeout when the test completes.
* Remove testConnect(): accept() wasn't blocking and testAccept()
  already tests non-blocking accept().
* Remove accept() functional test from testInitNonBlocking():
  already tested by testAccept()
* Rewrite testSetBlocking() with a new assert_sock_timeout() method
* Use addCleanup() and context manager to close sockets
* Replace assertTrue(x < y) with assertLess(x, y)
This commit is contained in:
Victor Stinner 2018-11-30 13:22:44 +01:00 committed by GitHub
parent ebd5d6d6e6
commit 304315d251
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 74 additions and 89 deletions

View File

@ -34,7 +34,8 @@ except ImportError:
fcntl = None fcntl = None
HOST = support.HOST HOST = support.HOST
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8') ## test unicode string and carriage return # test unicode string and carriage return
MSG = 'Michael Gilfix was here\u1234\r\n'.encode('utf-8')
MAIN_TIMEOUT = 60.0 MAIN_TIMEOUT = 60.0
VSOCKPORT = 1234 VSOCKPORT = 1234
@ -111,9 +112,14 @@ def _have_socket_vsock():
return ret return ret
def _is_fd_in_blocking_mode(sock): @contextlib.contextmanager
return not bool( def socket_setdefaulttimeout(timeout):
fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK) & os.O_NONBLOCK) old_timeout = socket.getdefaulttimeout()
try:
socket.setdefaulttimeout(timeout)
yield
finally:
socket.setdefaulttimeout(old_timeout)
HAVE_SOCKET_CAN = _have_socket_can() HAVE_SOCKET_CAN = _have_socket_can()
@ -1069,18 +1075,16 @@ class GeneralModuleTests(unittest.TestCase):
s.close() s.close()
# Set the default timeout to 10, and see if it propagates # Set the default timeout to 10, and see if it propagates
socket.setdefaulttimeout(10) with socket_setdefaulttimeout(10):
self.assertEqual(socket.getdefaulttimeout(), 10) self.assertEqual(socket.getdefaulttimeout(), 10)
s = socket.socket() with socket.socket() as sock:
self.assertEqual(s.gettimeout(), 10) self.assertEqual(sock.gettimeout(), 10)
s.close()
# Reset the default timeout to None, and see if it propagates # Reset the default timeout to None, and see if it propagates
socket.setdefaulttimeout(None) socket.setdefaulttimeout(None)
self.assertEqual(socket.getdefaulttimeout(), None) self.assertEqual(socket.getdefaulttimeout(), None)
s = socket.socket() with socket.socket() as sock:
self.assertEqual(s.gettimeout(), None) self.assertEqual(sock.gettimeout(), None)
s.close()
# Check that setting it to an invalid value raises ValueError # Check that setting it to an invalid value raises ValueError
self.assertRaises(ValueError, socket.setdefaulttimeout, -1) self.assertRaises(ValueError, socket.setdefaulttimeout, -1)
@ -4218,55 +4222,42 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
self.event = threading.Event() self.event = threading.Event()
ThreadedTCPSocketTest.__init__(self, methodName=methodName) ThreadedTCPSocketTest.__init__(self, methodName=methodName)
def assert_sock_timeout(self, sock, timeout):
self.assertEqual(self.serv.gettimeout(), timeout)
blocking = (timeout != 0.0)
self.assertEqual(sock.getblocking(), blocking)
if fcntl is not None:
# When a Python socket has a non-zero timeout, it's switched
# internally to a non-blocking mode. Later, sock.sendall(),
# sock.recv(), and other socket operations use a select() call and
# handle EWOULDBLOCK/EGAIN on all socket operations. That's how
# timeouts are enforced.
fd_blocking = (timeout is None)
flag = fcntl.fcntl(sock, fcntl.F_GETFL, os.O_NONBLOCK)
self.assertEqual(not bool(flag & os.O_NONBLOCK), fd_blocking)
def testSetBlocking(self): def testSetBlocking(self):
# Testing whether set blocking works # Test setblocking() and settimeout() methods
self.serv.setblocking(True) self.serv.setblocking(True)
self.assertIsNone(self.serv.gettimeout()) self.assert_sock_timeout(self.serv, None)
self.assertTrue(self.serv.getblocking())
if fcntl:
self.assertTrue(_is_fd_in_blocking_mode(self.serv))
self.serv.setblocking(False) self.serv.setblocking(False)
self.assertEqual(self.serv.gettimeout(), 0.0) self.assert_sock_timeout(self.serv, 0.0)
self.assertFalse(self.serv.getblocking())
if fcntl:
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
self.serv.settimeout(None) self.serv.settimeout(None)
self.assertTrue(self.serv.getblocking()) self.assert_sock_timeout(self.serv, None)
if fcntl:
self.assertTrue(_is_fd_in_blocking_mode(self.serv))
self.serv.settimeout(0) self.serv.settimeout(0)
self.assertFalse(self.serv.getblocking()) self.assert_sock_timeout(self.serv, 0)
self.assertEqual(self.serv.gettimeout(), 0)
if fcntl:
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
self.serv.settimeout(10) self.serv.settimeout(10)
self.assertTrue(self.serv.getblocking()) self.assert_sock_timeout(self.serv, 10)
self.assertEqual(self.serv.gettimeout(), 10)
if fcntl:
# When a Python socket has a non-zero timeout, it's
# switched internally to a non-blocking mode.
# Later, sock.sendall(), sock.recv(), and other socket
# operations use a `select()` call and handle EWOULDBLOCK/EGAIN
# on all socket operations. That's how timeouts are
# enforced.
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
self.serv.settimeout(0) self.serv.settimeout(0)
self.assertFalse(self.serv.getblocking()) self.assert_sock_timeout(self.serv, 0)
if fcntl:
self.assertFalse(_is_fd_in_blocking_mode(self.serv))
start = time.time()
try:
self.serv.accept()
except OSError:
pass
end = time.time()
self.assertTrue((end - start) < 1.0, "Error setting non-blocking mode.")
def _testSetBlocking(self): def _testSetBlocking(self):
pass pass
@ -4277,8 +4268,10 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
import _testcapi import _testcapi
if _testcapi.UINT_MAX >= _testcapi.ULONG_MAX: if _testcapi.UINT_MAX >= _testcapi.ULONG_MAX:
self.skipTest('needs UINT_MAX < ULONG_MAX') self.skipTest('needs UINT_MAX < ULONG_MAX')
self.serv.setblocking(False) self.serv.setblocking(False)
self.assertEqual(self.serv.gettimeout(), 0.0) self.assertEqual(self.serv.gettimeout(), 0.0)
self.serv.setblocking(_testcapi.UINT_MAX + 1) self.serv.setblocking(_testcapi.UINT_MAX + 1)
self.assertIsNone(self.serv.gettimeout()) self.assertIsNone(self.serv.gettimeout())
@ -4288,50 +4281,51 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
'test needs socket.SOCK_NONBLOCK') 'test needs socket.SOCK_NONBLOCK')
@support.requires_linux_version(2, 6, 28) @support.requires_linux_version(2, 6, 28)
def testInitNonBlocking(self): def testInitNonBlocking(self):
# reinit server socket # create a socket with SOCK_NONBLOCK
self.serv.close() self.serv.close()
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM | self.serv = socket.socket(socket.AF_INET,
socket.SOCK_NONBLOCK) socket.SOCK_STREAM | socket.SOCK_NONBLOCK)
self.assertFalse(self.serv.getblocking()) self.assert_sock_timeout(self.serv, 0)
self.assertEqual(self.serv.gettimeout(), 0)
self.port = support.bind_port(self.serv)
self.serv.listen()
# actual testing
start = time.time()
try:
self.serv.accept()
except OSError:
pass
end = time.time()
self.assertTrue((end - start) < 1.0, "Error creating with non-blocking mode.")
def _testInitNonBlocking(self): def _testInitNonBlocking(self):
pass pass
def testInheritFlags(self): def testInheritFlagsBlocking(self):
# Issue #7995: when calling accept() on a listening socket with a # bpo-7995: accept() on a listening socket with a timeout and the
# timeout, the resulting socket should not be non-blocking. # default timeout is None, the resulting socket must be blocking.
with socket_setdefaulttimeout(None):
self.serv.settimeout(10) self.serv.settimeout(10)
try:
conn, addr = self.serv.accept() conn, addr = self.serv.accept()
message = conn.recv(len(MSG)) self.addCleanup(conn.close)
finally: self.assertIsNone(conn.gettimeout())
conn.close()
self.serv.settimeout(None)
def _testInheritFlags(self): def _testInheritFlagsBlocking(self):
time.sleep(0.1) self.cli.connect((HOST, self.port))
def testInheritFlagsTimeout(self):
# bpo-7995: accept() on a listening socket with a timeout and the
# default timeout is None, the resulting socket must inherit
# the default timeout.
default_timeout = 20.0
with socket_setdefaulttimeout(default_timeout):
self.serv.settimeout(10)
conn, addr = self.serv.accept()
self.addCleanup(conn.close)
self.assertEqual(conn.gettimeout(), default_timeout)
def _testInheritFlagsTimeout(self):
self.cli.connect((HOST, self.port)) self.cli.connect((HOST, self.port))
time.sleep(0.5)
self.cli.send(MSG)
def testAccept(self): def testAccept(self):
# Testing non-blocking accept # Testing non-blocking accept
self.serv.setblocking(0) self.serv.setblocking(0)
# connect() didn't start: non-blocking accept() fails # connect() didn't start: non-blocking accept() fails
start_time = time.monotonic()
with self.assertRaises(BlockingIOError): with self.assertRaises(BlockingIOError):
conn, addr = self.serv.accept() conn, addr = self.serv.accept()
dt = time.monotonic() - start_time
self.assertLess(dt, 1.0)
self.event.set() self.event.set()
@ -4351,15 +4345,6 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
self.cli.connect((HOST, self.port)) self.cli.connect((HOST, self.port))
def testConnect(self):
# Testing non-blocking connect
conn, addr = self.serv.accept()
conn.close()
def _testConnect(self):
self.cli.settimeout(10)
self.cli.connect((HOST, self.port))
def testRecv(self): def testRecv(self):
# Testing non-blocking recv # Testing non-blocking recv
conn, addr = self.serv.accept() conn, addr = self.serv.accept()