Skip to content

Commit 0a110b2

Browse files
committed
Make pytest_upload_assets more xdist-compatible
ref: eu-cdse/openeo-cdse-infra#566
1 parent 4dabeae commit 0a110b2

File tree

3 files changed

+83
-11
lines changed

3 files changed

+83
-11
lines changed

qa/tools/apex_algorithm_qa_tools/pytest/pytest_upload_assets.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def test_dummy(upload_assets_on_fail, tmp_path):
4747
_log = logging.getLogger(__name__)
4848

4949
_UPLOAD_ASSETS_PLUGIN_NAME = "upload_assets"
50+
_USER_PROPERTY = "upload_assets"
5051

5152

5253
def pytest_addoption(parser: pytest.Parser):
@@ -94,6 +95,9 @@ def collect(self, path: Path, name: str):
9495
"""Collect assets to upload"""
9596
assert self.collected_assets is not None, "No active collection of assets"
9697
self.collected_assets[name] = path
98+
# TODO: this stat won't work properly in pytest-xdist context
99+
# because incrementing stat happens on xdist workers,
100+
# while summary is done from xdist controller).
97101
self.upload_stats["collected"] += 1
98102

99103
def pytest_runtest_logstart(self, nodeid):
@@ -103,26 +107,40 @@ def pytest_runtest_logstart(self, nodeid):
103107
def pytest_runtest_logreport(self, report: pytest.TestReport):
104108
# TODO #22: option to upload on other outcome as well?
105109
if report.when == "call" and report.outcome == "failed":
106-
self._upload_collected_assets(nodeid=report.nodeid)
110+
self._upload_collected_assets(nodeid=report.nodeid, report=report)
111+
112+
# Merge stats
113+
for upload_info in (
114+
v for k, v in report.user_properties if k == _USER_PROPERTY
115+
):
116+
for k, v in upload_info["stats"].items():
117+
self.upload_stats[k] += v
118+
if upload_info["uploads"]:
119+
self.upload_reports[report.nodeid] = upload_info["uploads"]
107120

108121
def pytest_runtest_logfinish(self, nodeid):
109122
# Reset collection of assets
110123
self.collected_assets = None
111124

112-
def _upload_collected_assets(self, nodeid: str):
113-
upload_report = {}
125+
def _upload_collected_assets(self, nodeid: str, report: pytest.TestReport):
126+
upload_info = {
127+
"stats": {"uploaded": 0, "failed": 0},
128+
"uploads": {},
129+
}
114130
for name, path in self.collected_assets.items():
115131
try:
116132
url = self._upload_asset(nodeid=nodeid, name=name, path=path)
117-
upload_report[name] = {"url": url}
118-
self.upload_stats["uploaded"] += 1
133+
upload_info["uploads"][name] = {"url": url}
134+
upload_info["stats"]["uploaded"] += 1
119135
except Exception as e:
120136
_log.error(
121137
f"Failed to upload asset {name=} from {path=}: {e=}", exc_info=True
122138
)
123-
upload_report[name] = {"error": str(e)}
124-
self.upload_stats["failed"] += 1
125-
self.upload_reports[nodeid] = upload_report
139+
upload_info["uploads"][name] = {"error": str(e)}
140+
upload_info["stats"]["failed"] += 1
141+
142+
# Pass upload info through user_properties to be xdist-compatible
143+
report.user_properties.append([_USER_PROPERTY, upload_info])
126144

127145
def _upload_asset(self, nodeid: str, name: str, path: Path) -> str:
128146
safe_nodeid = re.sub(r"[^a-zA-Z0-9_.-]", "_", nodeid)

qa/unittests/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ git+https://github.com/ESA-APEx/esa-apex-toolbox-python.git@main
33
pytest>=8.2.0
44
moto[s3, server]>=5.0.13
55
dirty-equals>=0.8.0
6+
pytest-xdist>=3.5.0

qa/unittests/tests/pytest/test_pytest_upload_assets.py

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22

33

4-
def test_basic_upload_on_fail(
4+
def test_upload_on_fail_basic(
55
pytester: pytest.Pytester, moto_server, s3_client, s3_bucket, monkeypatch
66
):
77
pytester.makeconftest(
@@ -44,7 +44,7 @@ def test_fail_and_upload(upload_assets_on_fail, tmp_path):
4444
run_result.stdout.re_match_lines(
4545
[
4646
r".*upload_assets summary",
47-
r"- stats: \{'uploaded': 1\}",
47+
r"- stats: \{.*'uploaded': 1.*\}",
4848
r"\s+-\s+'hello.txt' uploaded to 'http://.*?/test-bucket-\w+/test-run-123!test_file_maker.py__test_fail_and_upload!hello.txt'",
4949
]
5050
)
@@ -87,6 +87,59 @@ def test_success(upload_assets_on_fail, tmp_path):
8787
run_result.stdout.re_match_lines(
8888
[
8989
r".*upload_assets summary",
90-
r"- stats: \{'uploaded': 0\}",
90+
r"- stats: \{.*'uploaded': 0.*\}",
91+
]
92+
)
93+
94+
95+
def test_upload_on_fail_xdist(
96+
pytester: pytest.Pytester, moto_server, s3_client, s3_bucket, monkeypatch
97+
):
98+
pytester.makeconftest(
99+
"""
100+
pytest_plugins = [
101+
"apex_algorithm_qa_tools.pytest.pytest_upload_assets",
102+
]
103+
"""
104+
)
105+
pytester.makepyfile(
106+
test_file_maker="""
107+
def test_fail_and_upload(upload_assets_on_fail, tmp_path):
108+
path = tmp_path / "hello.txt"
109+
path.write_text("Hello world.")
110+
upload_assets_on_fail(path)
111+
assert 3 == 5
112+
"""
113+
)
114+
115+
monkeypatch.setenv("APEX_ALGORITHMS_S3_ENDPOINT_URL", moto_server)
116+
monkeypatch.setenv("APEX_ALGORITHMS_RUN_ID", "test-run-123")
117+
118+
run_result = pytester.runpytest_subprocess(
119+
f"--upload-assets-s3-bucket={s3_bucket}",
120+
"--numprocesses=2",
121+
)
122+
run_result.stdout.re_match_lines(
123+
[
124+
r"Plugin `upload_assets` is active, with upload to 'test-bucket-",
125+
r"2 workers \[1 item\]",
126+
]
127+
)
128+
run_result.assert_outcomes(failed=1)
129+
130+
object_listing = s3_client.list_objects(Bucket=s3_bucket)
131+
assert len(object_listing["Contents"])
132+
keys = [obj["Key"] for obj in object_listing["Contents"]]
133+
expected_key = "test-run-123!test_file_maker.py__test_fail_and_upload!hello.txt"
134+
assert keys == [expected_key]
135+
136+
actual = s3_client.get_object(Bucket=s3_bucket, Key=expected_key)
137+
assert actual["Body"].read().decode("utf8") == "Hello world."
138+
139+
run_result.stdout.re_match_lines(
140+
[
141+
r".*upload_assets summary",
142+
r"- stats: \{.*'uploaded': 1.*\}",
143+
r"\s+-\s+'hello.txt' uploaded to 'http://.*?/test-bucket-\w+/test-run-123!test_file_maker.py__test_fail_and_upload!hello.txt'",
91144
]
92145
)

0 commit comments

Comments
 (0)