From e010561d0842c58c43203f7c29dca1d518372b5d Mon Sep 17 00:00:00 2001 From: Simon Sawicki Date: Mon, 18 Aug 2025 22:20:28 +0200 Subject: [PATCH] [test] Allow skipping tests that likely segfault in CI --- devscripts/run_tests.py | 26 ++++++++++++---- test/conftest.py | 3 ++ test/test_http_proxy.py | 9 ++++-- test/test_networking.py | 68 ++++++++++++++++++++++++++--------------- test/test_socks.py | 8 ++--- test/test_websockets.py | 4 +-- 6 files changed, 79 insertions(+), 39 deletions(-) diff --git a/devscripts/run_tests.py b/devscripts/run_tests.py index ebb3500b6c..3c446c8aef 100755 --- a/devscripts/run_tests.py +++ b/devscripts/run_tests.py @@ -19,12 +19,14 @@ def parse_args(): 'test', help='an extractor test, test path, or one of "core" or "download"', nargs='*') parser.add_argument( '-k', help='run a test matching EXPRESSION. Same as "pytest -k"', metavar='EXPRESSION') + parser.add_argument( + '--no-segfaults', action='store_true', help='skip tests known to cause segfaults in CI') parser.add_argument( '--pytest-args', help='arguments to passthrough to pytest') return parser.parse_args() -def run_tests(*tests, pattern=None, ci=False): +def run_tests(*tests, pattern=None, ci=False, allow_segfaults=True): # XXX: hatch uses `tests` if no arguments are passed run_core = 'core' in tests or 'tests' in tests or (not pattern and not tests) run_download = 'download' in tests @@ -35,15 +37,25 @@ def run_tests(*tests, pattern=None, ci=False): arguments.append('--color=yes') if pattern: arguments.extend(['-k', pattern]) + + positional = [] + markers = [] + if not allow_segfaults: + markers.append('not segfaults') if run_core: - arguments.extend(['-m', 'not download']) + markers.append('not download') elif run_download: - arguments.extend(['-m', 'download']) + markers.append('download') else: - arguments.extend( + positional = [ test if '/' in test else f'test/test_download.py::TestDownload::test_{fix_test_name(test)}' - for test in tests) + for test in tests + ] + + if markers: + arguments.extend(['-m', ' and '.join(markers)]) + arguments.extend(positional) print(f'Running {arguments}', flush=True) try: @@ -72,6 +84,8 @@ if __name__ == '__main__': args = parse_args() os.chdir(Path(__file__).parent.parent) - sys.exit(run_tests(*args.test, pattern=args.k, ci=bool(os.getenv('CI')))) + sys.exit(run_tests( + *args.test, pattern=args.k, ci=bool(os.getenv('CI')), + allow_segfaults=not args.no_segfaults)) except KeyboardInterrupt: pass diff --git a/test/conftest.py b/test/conftest.py index a8b92f811e..343b6c60ca 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -53,6 +53,9 @@ def skip_handlers_if(request, handler): def pytest_configure(config): + config.addinivalue_line( + 'markers', 'segfaults: mark a test as potentially segfaulting in CI', + ) config.addinivalue_line( 'markers', 'skip_handler(handler): skip test for the given handler', ) diff --git a/test/test_http_proxy.py b/test/test_http_proxy.py index e903ff8beb..b56c92d737 100644 --- a/test/test_http_proxy.py +++ b/test/test_http_proxy.py @@ -245,8 +245,11 @@ def ctx(request): return CTX_MAP[request.param]() -@pytest.mark.parametrize( - 'handler', ['Urllib', 'Requests', 'CurlCFFI'], indirect=True) +@pytest.mark.parametrize('handler', [ + 'Urllib', + 'Requests', + pytest.param('CurlCFFI', marks=pytest.mark.segfaults), +], indirect=True) @pytest.mark.parametrize('ctx', ['http'], indirect=True) # pure http proxy can only support http class TestHTTPProxy: def test_http_no_auth(self, handler, ctx): @@ -313,7 +316,7 @@ class TestHTTPProxy: @pytest.mark.parametrize( 'handler,ctx', [ ('Requests', 'https'), - ('CurlCFFI', 'https'), + pytest.param('CurlCFFI', 'https', marks=pytest.mark.segfaults), ], indirect=True) class TestHTTPConnectProxy: def test_http_connect_no_auth(self, handler, ctx): diff --git a/test/test_networking.py b/test/test_networking.py index afdd0c7aa7..c357172812 100644 --- a/test/test_networking.py +++ b/test/test_networking.py @@ -310,7 +310,11 @@ class TestRequestHandlerBase: cls.https_server_thread.start() -@pytest.mark.parametrize('handler', ['Urllib', 'Requests', 'CurlCFFI'], indirect=True) +@pytest.mark.parametrize('handler', [ + 'Urllib', + 'Requests', + pytest.param('CurlCFFI', marks=pytest.mark.segfaults), +], indirect=True) class TestHTTPRequestHandler(TestRequestHandlerBase): def test_verify_cert(self, handler): @@ -737,7 +741,11 @@ class TestHTTPRequestHandler(TestRequestHandlerBase): assert res.read() == b'