pytest-xdist: pytest in parallel

Require now pytest-xdist from tests/http/requirements.txt and
run pytest in 'auto' parallel mode (counts cpu cores).

For CI runs, set the worker count to 4, overriding the
core count of 2 exposed in the images.

- use Filelock to generate allocated ports at start for all
  workers and have subsequent workers just read the file and
  take the ports for their slot
- make httpd config clearing a function fixture so every test
  starts with a clean httpd config
- have fixture `configures_httpd` as parameter of test cases
  that configure httpd anyway, saving one reload
- add pytest-xdist and filelock to required pyhton modules
- add installs to ruff CI
- give live checks waiting for a server to start up longer time
- add fixtures to tests that rely on a server
- do not stop servers unnecessarily. failures may not start them
  properly again, leading to unexpected fails in whatever follows
- add a https: port to httpd that is *not* back by QUIC to allow
  failover tests without stopping the QUIC server

Closes #17295
This commit is contained in:
Stefan Eissing 2025-05-12 15:49:49 +02:00 committed by Daniel Stenberg
parent f0bf43e209
commit 30ef79ed93
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
39 changed files with 644 additions and 449 deletions

View file

@ -39,16 +39,9 @@ log = logging.getLogger(__name__)
reason=f"missing: {Env.incomplete_reason()}")
class TestProxyAuth:
@pytest.fixture(autouse=True, scope='class')
def _class_scope(self, env, httpd, nghttpx_fwd):
if env.have_nghttpx():
nghttpx_fwd.start_if_needed()
httpd.clear_extra_configs()
def httpd_configure(self, env, httpd):
httpd.set_proxy_auth(True)
httpd.reload()
yield
httpd.set_proxy_auth(False)
httpd.reload()
httpd.reload_if_config_changed()
def get_tunnel_proto_used(self, r: ExecResult):
for line in r.trace_lines:
@ -59,7 +52,8 @@ class TestProxyAuth:
return None
# download via http: proxy (no tunnel), no auth
def test_13_01_proxy_no_auth(self, env: Env, httpd):
def test_13_01_proxy_no_auth(self, env: Env, httpd, configures_httpd):
self.httpd_configure(env, httpd)
curl = CurlClient(env=env)
url = f'http://localhost:{env.http_port}/data.json'
r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
@ -67,7 +61,8 @@ class TestProxyAuth:
r.check_response(count=1, http_status=407)
# download via http: proxy (no tunnel), auth
def test_13_02_proxy_auth(self, env: Env, httpd):
def test_13_02_proxy_auth(self, env: Env, httpd, configures_httpd):
self.httpd_configure(env, httpd)
curl = CurlClient(env=env)
url = f'http://localhost:{env.http_port}/data.json'
xargs = curl.get_proxy_args(proxys=False)
@ -79,7 +74,8 @@ class TestProxyAuth:
@pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'),
reason='curl lacks HTTPS-proxy support')
@pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available")
def test_13_03_proxys_no_auth(self, env: Env, httpd, nghttpx_fwd):
def test_13_03_proxys_no_auth(self, env: Env, httpd, configures_httpd, nghttpx_fwd):
self.httpd_configure(env, httpd)
curl = CurlClient(env=env)
url = f'http://localhost:{env.http_port}/data.json'
xargs = curl.get_proxy_args(proxys=True)
@ -90,7 +86,8 @@ class TestProxyAuth:
@pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'),
reason='curl lacks HTTPS-proxy support')
@pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available")
def test_13_04_proxys_auth(self, env: Env, httpd, nghttpx_fwd):
def test_13_04_proxys_auth(self, env: Env, httpd, configures_httpd, nghttpx_fwd):
self.httpd_configure(env, httpd)
curl = CurlClient(env=env)
url = f'http://localhost:{env.http_port}/data.json'
xargs = curl.get_proxy_args(proxys=True)
@ -99,7 +96,8 @@ class TestProxyAuth:
extra_args=xargs)
r.check_response(count=1, http_status=200)
def test_13_05_tunnel_http_no_auth(self, env: Env, httpd):
def test_13_05_tunnel_http_no_auth(self, env: Env, httpd, configures_httpd, nghttpx_fwd):
self.httpd_configure(env, httpd)
curl = CurlClient(env=env)
url = f'http://localhost:{env.http_port}/data.json'
xargs = curl.get_proxy_args(proxys=False, tunnel=True)
@ -108,7 +106,8 @@ class TestProxyAuth:
# expect "COULD_NOT_CONNECT"
r.check_response(exitcode=56, http_status=None)
def test_13_06_tunnel_http_auth(self, env: Env, httpd):
def test_13_06_tunnel_http_auth(self, env: Env, httpd, configures_httpd):
self.httpd_configure(env, httpd)
curl = CurlClient(env=env)
url = f'http://localhost:{env.http_port}/data.json'
xargs = curl.get_proxy_args(proxys=False, tunnel=True)
@ -122,7 +121,8 @@ class TestProxyAuth:
reason='curl lacks HTTPS-proxy support')
@pytest.mark.parametrize("proto", ['http/1.1', 'h2'])
@pytest.mark.parametrize("tunnel", ['http/1.1', 'h2'])
def test_13_07_tunnels_no_auth(self, env: Env, httpd, proto, tunnel):
def test_13_07_tunnels_no_auth(self, env: Env, httpd, configures_httpd, nghttpx_fwd, proto, tunnel):
self.httpd_configure(env, httpd)
if tunnel == 'h2' and not env.curl_uses_lib('nghttp2'):
pytest.skip('only supported with nghttp2')
curl = CurlClient(env=env)
@ -140,7 +140,8 @@ class TestProxyAuth:
reason='curl lacks HTTPS-proxy support')
@pytest.mark.parametrize("proto", ['http/1.1', 'h2'])
@pytest.mark.parametrize("tunnel", ['http/1.1', 'h2'])
def test_13_08_tunnels_auth(self, env: Env, httpd, proto, tunnel):
def test_13_08_tunnels_auth(self, env: Env, httpd, configures_httpd, nghttpx_fwd, proto, tunnel):
self.httpd_configure(env, httpd)
if tunnel == 'h2' and not env.curl_uses_lib('nghttp2'):
pytest.skip('only supported with nghttp2')
curl = CurlClient(env=env)
@ -156,7 +157,8 @@ class TestProxyAuth:
@pytest.mark.skipif(condition=not Env.curl_has_feature('SPNEGO'),
reason='curl lacks SPNEGO support')
def test_13_09_negotiate_http(self, env: Env, httpd):
def test_13_09_negotiate_http(self, env: Env, httpd, configures_httpd):
self.httpd_configure(env, httpd)
run_env = os.environ.copy()
run_env['https_proxy'] = f'http://127.0.0.1:{env.proxy_port}'
curl = CurlClient(env=env, run_env=run_env)