Skip to content

Commit 842d973

Browse files
committed
ci: add pytest-xdist
Add xdist to speed up test runs some.
1 parent 22eae79 commit 842d973

File tree

5 files changed

+34
-25
lines changed

5 files changed

+34
-25
lines changed

_appmap/test/conftest.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import _appmap
1616
import appmap
17-
from _appmap.test.web_framework import TEST_HOST, TEST_PORT
17+
from _appmap.test.web_framework import TEST_HOST
1818
from appmap import generation
1919

2020
from .. import utils
@@ -198,14 +198,22 @@ def _starter(controldir, xprocess):
198198

199199
return _starter
200200

201+
@pytest.fixture(name="server_port")
202+
def server_port_fixture(worker_id):
203+
if worker_id == "master":
204+
offset = "0"
205+
else:
206+
offset = worker_id[2:]
207+
return 8000 + int(offset)
208+
201209

202210
@pytest.fixture(name="server_base")
203-
def server_base_fixture(request):
211+
def server_base_fixture(request, server_port):
204212
marker = request.node.get_closest_marker("server")
205213
debug = marker.kwargs.get("debug", False)
206214
server_env = os.environ.copy()
207215
server_env.update(marker.kwargs.get("env", {}))
208216

209-
info = ServerInfo(debug=debug, host=TEST_HOST, port=TEST_PORT, env=server_env)
217+
info = ServerInfo(debug=debug, host=TEST_HOST, port=server_port, env=server_env)
210218
info.factory = partial(server_starter, info)
211219
return info

_appmap/test/web_framework.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import multiprocessing
77
import os
8+
import re
89
import time
910
import traceback
1011
from os.path import exists
@@ -20,7 +21,6 @@
2021
from .normalize import normalize_appmap
2122

2223
TEST_HOST = "127.0.0.1"
23-
TEST_PORT = 8000
2424

2525
_SR = SystemRandom()
2626

@@ -323,28 +323,25 @@ def test_can_record(self, data_dir, client):
323323
res = client.delete("/_appmap/record")
324324
assert res.status_code == 404
325325

326+
@pytest.mark.xdist_group("group1")
326327
class _TestRecordRequests:
327328
"""Common tests for per-requests recording (record requests.)"""
328329

329330
@classmethod
330-
def server_url(cls):
331-
return f"http://{TEST_HOST}:{TEST_PORT}"
332-
333-
@classmethod
334-
def record_request_thread(cls):
331+
def record_request_thread(cls, server_url):
335332
# I've seen occasional test failures, seemingly because the test servers can't handle the
336333
# barrage of requests. A tiny bit of delay still causes many, many concurrent requests, but
337334
# eliminates the failures.
338335
time.sleep(_SR.uniform(0, 0.1))
339-
return requests.get(cls.server_url() + "/test", timeout=30)
336+
return requests.get(server_url + "/test", timeout=30)
340337

341-
def record_requests(self, record_remote):
338+
def record_requests(self, record_remote, server_url):
342339
# pylint: disable=too-many-locals
343340
if record_remote:
344341
# when remote recording is enabled, this test also
345342
# verifies the global recorder doesn't save duplicate
346343
# events when per-request recording is enabled
347-
response = requests.post(self.server_url() + "/_appmap/record", timeout=30)
344+
response = requests.post(server_url + "/_appmap/record", timeout=30)
348345
assert response.status_code == 200
349346

350347
with concurrent.futures.ThreadPoolExecutor(
@@ -354,7 +351,7 @@ def record_requests(self, record_remote):
354351
max_number_of_threads = 400
355352
future_to_request_number = {}
356353
for n in range(max_number_of_threads):
357-
future = executor.submit(self.record_request_thread)
354+
future = executor.submit(self.record_request_thread, server_url)
358355
future_to_request_number[future] = n
359356

360357
# wait for all threads to complete
@@ -384,9 +381,8 @@ def record_requests(self, record_remote):
384381
appmap_file_name_basename_part = "_".join(
385382
appmap_file_name_basename.split("_")[2:]
386383
)
387-
assert (
388-
appmap_file_name_basename_part
389-
== "http_127_0_0_1_8000_test.appmap.json"
384+
assert re.match(
385+
r"http_127_0_0_1_8[0-9]*_test.appmap.json", appmap_file_name_basename_part
390386
)
391387

392388
with open(appmap_file_name, encoding="utf-8") as f:
@@ -414,12 +410,12 @@ def record_requests(self, record_remote):
414410
@pytest.mark.appmap_enabled
415411
@pytest.mark.server(debug=True)
416412
def test_record_requests_with_remote(self, server):
417-
self.record_requests(server.debug)
413+
self.record_requests(server.debug, server.url)
418414

419415
@pytest.mark.appmap_enabled
420416
@pytest.mark.server(debug=False)
421417
def test_record_requests_without_remote(self, server):
422-
self.record_requests(server.debug)
418+
self.record_requests(server.debug, server.url)
423419

424420
@pytest.mark.server(debug=False)
425421
def test_remote_disabled_in_prod(self, server):

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ fastapi = "^0.110.0"
7878
httpx = "^0.27.0"
7979
pytest-env = "^1.1.3"
8080
pytest-console-scripts = "^1.4.1"
81+
pytest-xdist = "^3.6.1"
82+
psutil = "^6.0.0"
8183

8284
[build-system]
8385
requires = ["poetry-core>=1.1.0"]

pytest.ini

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ markers =
1010
testpaths = _appmap/test
1111
pytester_example_dir = _appmap/test/data
1212

13-
# running in a subprocess ensures that environment variables are set
14-
# correctly and no classes are loaded.
15-
addopts = --runpytest subprocess --ignore vendor
13+
# running in a subprocess ensures that environment variables are set correctly and no classes are
14+
# loaded. Also, the remote-recording tests can't be run in parallel, so they're marked to run in the
15+
# same load group and distribution is done by loadgroup.
16+
addopts = --runpytest subprocess --ignore vendor --tb=short --dist loadgroup
1617

1718
# We're stuck at pytest ~6.1.2. This warning got removed in a later
1819
# version.

tox.ini

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ deps=
1111
sqlalchemy >=2.0, <3.0
1212

1313
[testenv]
14+
passenv =
15+
PYTEST_XDIST_AUTO_NUM_WORKERS
1416
allowlist_externals =
1517
env
1618
bash
@@ -26,10 +28,10 @@ deps=
2628

2729
commands =
2830
poetry install -v
29-
web: poetry run appmap-python {posargs:pytest}
30-
django3: poetry run appmap-python pytest _appmap/test/test_django.py
31-
flask2: poetry run appmap-python pytest _appmap/test/test_flask.py
32-
sqlalchemy1: poetry run appmap-python pytest _appmap/test/test_sqlalchemy.py
31+
web: poetry run appmap-python {posargs:pytest -n logical}
32+
django3: poetry run appmap-python pytest -n logical _appmap/test/test_django.py
33+
flask2: poetry run appmap-python pytest -n logical _appmap/test/test_flask.py
34+
sqlalchemy1: poetry run appmap-python pytest -n logical _appmap/test/test_sqlalchemy.py
3335

3436
[testenv:lint]
3537
skip_install = True

0 commit comments

Comments
 (0)