Skip to content

Commit 1ebe014

Browse files
authored
[3.11] gh-109015: Add test.support.socket_helper.tcp_blackhole() (#109016) (#109042)
gh-109015: Add test.support.socket_helper.tcp_blackhole() (#109016) Skip test_asyncio, test_imaplib and test_socket tests if FreeBSD TCP blackhole is enabled (net.inet.tcp.blackhole=2). (cherry picked from commit a52a350)
1 parent 1f115a8 commit 1ebe014

File tree

7 files changed

+80
-1
lines changed

7 files changed

+80
-1
lines changed

Lib/test/support/socket_helper.py

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import contextlib
22
import errno
33
import socket
4-
import unittest
4+
import subprocess
55
import sys
6+
import unittest
67

78
from .. import support
89
from . import warnings_helper
@@ -270,3 +271,62 @@ def filter_error(err):
270271
# __cause__ or __context__?
271272
finally:
272273
socket.setdefaulttimeout(old_timeout)
274+
275+
276+
# consider that sysctl values should not change while tests are running
277+
_sysctl_cache = {}
278+
279+
def _get_sysctl(name):
280+
"""Get a sysctl value as an integer."""
281+
try:
282+
return _sysctl_cache[name]
283+
except KeyError:
284+
pass
285+
286+
# At least Linux and FreeBSD support the "-n" option
287+
cmd = ['sysctl', '-n', name]
288+
proc = subprocess.run(cmd,
289+
stdout=subprocess.PIPE,
290+
stderr=subprocess.STDOUT,
291+
text=True)
292+
if proc.returncode:
293+
support.print_warning(f"{' '.join(cmd)!r} command failed with "
294+
f"exit code {proc.returncode}")
295+
# cache the error to only log the warning once
296+
_sysctl_cache[name] = None
297+
return None
298+
output = proc.stdout
299+
300+
# Parse '0\n' to get '0'
301+
try:
302+
value = int(output.strip())
303+
except Exception as exc:
304+
support.print_warning(f"Failed to parse {' '.join(cmd)!r} "
305+
f"command output {output!r}: {exc!r}")
306+
# cache the error to only log the warning once
307+
_sysctl_cache[name] = None
308+
return None
309+
310+
_sysctl_cache[name] = value
311+
return value
312+
313+
314+
def tcp_blackhole():
315+
if not sys.platform.startswith('freebsd'):
316+
return False
317+
318+
# gh-109015: test if FreeBSD TCP blackhole is enabled
319+
value = _get_sysctl('net.inet.tcp.blackhole')
320+
if value is None:
321+
# don't skip if we fail to get the sysctl value
322+
return False
323+
return (value != 0)
324+
325+
326+
def skip_if_tcp_blackhole(test):
327+
"""Decorator skipping test if TCP blackhole is enabled."""
328+
skip_if = unittest.skipIf(
329+
tcp_blackhole(),
330+
"TCP blackhole is enabled (sysctl net.inet.tcp.blackhole)"
331+
)
332+
return skip_if(test)

Lib/test/test_asyncio/test_events.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ def test_create_connection_local_addr(self):
671671
self.assertEqual(port, expected)
672672
tr.close()
673673

674+
@socket_helper.skip_if_tcp_blackhole
674675
def test_create_connection_local_addr_skip_different_family(self):
675676
# See https://github.com/python/cpython/issues/86508
676677
port1 = socket_helper.find_unused_port()
@@ -692,6 +693,7 @@ async def getaddrinfo(host, port, *args, **kwargs):
692693
with self.assertRaises(OSError):
693694
self.loop.run_until_complete(f)
694695

696+
@socket_helper.skip_if_tcp_blackhole
695697
def test_create_connection_local_addr_nomatch_family(self):
696698
# See https://github.com/python/cpython/issues/86508
697699
port1 = socket_helper.find_unused_port()
@@ -1248,6 +1250,7 @@ def connection_made(self, transport):
12481250

12491251
server.close()
12501252

1253+
@socket_helper.skip_if_tcp_blackhole
12511254
def test_server_close(self):
12521255
f = self.loop.create_server(MyProto, '0.0.0.0', 0)
12531256
server = self.loop.run_until_complete(f)

Lib/test/test_asyncio/test_sock_lowlevel.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
from test import support
1111
from test.support import socket_helper
1212

13+
if socket_helper.tcp_blackhole():
14+
raise unittest.SkipTest('Not relevant to ProactorEventLoop')
15+
16+
1317
def tearDownModule():
1418
asyncio.set_event_loop_policy(None)
1519

Lib/test/test_asyncio/test_sslproto.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import unittest
66
import weakref
77
from test import support
8+
from test.support import socket_helper
89
from unittest import mock
910
try:
1011
import ssl
@@ -350,6 +351,7 @@ async def client(addr):
350351
support.gc_collect()
351352
self.assertIsNone(client_context())
352353

354+
@socket_helper.skip_if_tcp_blackhole
353355
def test_start_tls_client_buf_proto_1(self):
354356
HELLO_MSG = b'1' * self.PAYLOAD_SIZE
355357

@@ -502,6 +504,7 @@ async def client(addr):
502504
asyncio.wait_for(client(srv.addr),
503505
timeout=support.SHORT_TIMEOUT))
504506

507+
@socket_helper.skip_if_tcp_blackhole
505508
def test_start_tls_server_1(self):
506509
HELLO_MSG = b'1' * self.PAYLOAD_SIZE
507510
ANSWER = b'answer'

Lib/test/test_imaplib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def test_that_Time2Internaldate_returns_a_result(self):
7777
for t in self.timevalues():
7878
imaplib.Time2Internaldate(t)
7979

80+
@socket_helper.skip_if_tcp_blackhole
8081
def test_imap4_host_default_value(self):
8182
# Check whether the IMAP4_PORT is truly unavailable.
8283
with socket.socket() as s:

Lib/test/test_socket.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5171,6 +5171,7 @@ def mocked_socket_module(self):
51715171
finally:
51725172
socket.socket = old_socket
51735173

5174+
@socket_helper.skip_if_tcp_blackhole
51745175
def test_connect(self):
51755176
port = socket_helper.find_unused_port()
51765177
cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -5179,6 +5180,7 @@ def test_connect(self):
51795180
cli.connect((HOST, port))
51805181
self.assertEqual(cm.exception.errno, errno.ECONNREFUSED)
51815182

5183+
@socket_helper.skip_if_tcp_blackhole
51825184
def test_create_connection(self):
51835185
# Issue #9792: errors raised by create_connection() should have
51845186
# a proper errno attribute.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fix test_asyncio, test_imaplib and test_socket tests on FreeBSD if the TCP
2+
blackhole is enabled (``sysctl net.inet.tcp.blackhole``). Skip the few tests
3+
which failed with ``ETIMEDOUT`` which such non standard configuration.
4+
Currently, the `FreeBSD GCP image enables TCP and UDP blackhole
5+
<https://reviews.freebsd.org/D41751>`_ (``sysctl net.inet.tcp.blackhole=2``
6+
and ``sysctl net.inet.udp.blackhole=1``). Patch by Victor Stinner.

0 commit comments

Comments
 (0)