Skip to content

Commit 654fed2

Browse files
author
André Luiz
authored
fix: fallback to origin when lab/runtime is missing in standardize_labs (#1844)
* fix: fallback to origin when lab/runtime is missing in standardize_labs When a build or test has no lab (builds) or runtime (tests) field in misc, use the origin field as a fallback value. This avoids losing origin context for entries that lack explicit lab information. Closes #1752 * refactor: extract _standardize_lab_field to remove duplicated logic Move the shared build/test lab normalization logic into a private helper _standardize_lab_field(item, field). Both builds ("lab") and tests ("runtime") now delegate to it, eliminating the duplicated AUTOMATIC_LAB_FIELD + origin-fallback blocks. Add TestStandardizeLabField unit tests covering all branches of the helper.
1 parent f28dd76 commit 654fed2

2 files changed

Lines changed: 137 additions & 20 deletions

File tree

backend/kernelCI_app/management/commands/helpers/kcidbng_ingester.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,24 +91,37 @@ def standardize_tree_names(
9191
checkout["tree_name"] = correct_tree
9292

9393

94+
def _standardize_lab_field(item: dict[str, Any], field: str) -> None:
95+
"""
96+
Moves automatic lab/runtime value to AUTOMATIC_LAB_FIELD and falls back to origin.
97+
98+
lab is AUTOMATIC_LAB -> fallback to origin
99+
lab is None -> fallback to origin
100+
lab is not None -> do nothing
101+
"""
102+
lab = item.get("misc", {}).get(field)
103+
is_automatic = lab and AUTOMATIC_LABS.match(lab)
104+
if is_automatic:
105+
item["misc"][AUTOMATIC_LAB_FIELD] = lab
106+
item["misc"].pop(field, None)
107+
lab = None
108+
109+
if not lab or is_automatic:
110+
origin = item.get("origin")
111+
if origin:
112+
item.setdefault("misc", {})[field] = origin
113+
114+
94115
def standardize_labs(input_data: dict[str, Any]) -> None:
95116
"""
96-
Standardize labs in data, moving automatic lab names to AUTOMATIC_LAB_FIELD
117+
Standardize labs in data, moving automatic lab names to AUTOMATIC_LAB_FIELD.
118+
Falls back to 'origin' when 'lab' (builds) or 'runtime' (tests) is missing.
97119
"""
120+
for build in input_data.get("builds", []):
121+
_standardize_lab_field(build, "lab")
98122

99-
builds: list[dict[str, Any]] = input_data.get("builds", [])
100-
for build in builds:
101-
lab = build.get("misc", {}).get("lab")
102-
if lab and AUTOMATIC_LABS.match(lab):
103-
build["misc"][AUTOMATIC_LAB_FIELD] = lab
104-
build["misc"].pop("lab", None)
105-
106-
tests: list[dict[str, Any]] = input_data.get("tests", [])
107-
for test in tests:
108-
lab = test.get("misc", {}).get("runtime")
109-
if lab and AUTOMATIC_LABS.match(lab):
110-
test["misc"][AUTOMATIC_LAB_FIELD] = lab
111-
test["misc"].pop("runtime", None)
123+
for test in input_data.get("tests", []):
124+
_standardize_lab_field(test, "runtime")
112125

113126

114127
def _extract_origins_info(data: Optional[dict[str, Any]]) -> str:

backend/kernelCI_app/tests/unitTests/commands/monitorSubmissions/kcidbng_ingester_test.py

Lines changed: 110 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
SubmissionFileMetadata,
2121
standardize_tree_names,
2222
standardize_labs,
23+
_standardize_lab_field,
2324
prepare_file_data,
2425
consume_buffer,
2526
flush_buffers,
@@ -200,17 +201,64 @@ def test_standardize_tree_names_missing_tree_name(self):
200201
assert "tree_name" not in input_data["checkouts"][1]
201202

202203

204+
class TestStandardizeLabField:
205+
"""Test cases for _standardize_lab_field helper."""
206+
207+
# Test cases:
208+
# - automatic value with origin → moved to AUTOMATIC_LAB_FIELD, origin used as fallback
209+
# - automatic value without origin → moved to AUTOMATIC_LAB_FIELD, field left absent
210+
# - missing field with origin → origin used as fallback
211+
# - missing field without origin → field stays absent
212+
# - real value → unchanged
213+
214+
def test_automatic_lab_with_origin(self):
215+
"""Automatic value is moved to AUTOMATIC_LAB_FIELD and origin fills the field."""
216+
item = {"misc": {"lab": "shell"}, "origin": "maestro"}
217+
_standardize_lab_field(item, "lab")
218+
assert item["misc"][AUTOMATIC_LAB_FIELD] == "shell"
219+
assert item["misc"]["lab"] == "maestro"
220+
221+
def test_automatic_lab_without_origin(self):
222+
"""Automatic value is moved to AUTOMATIC_LAB_FIELD; field stays absent."""
223+
item = {"misc": {"lab": "k8s"}}
224+
_standardize_lab_field(item, "lab")
225+
assert item["misc"][AUTOMATIC_LAB_FIELD] == "k8s"
226+
assert "lab" not in item["misc"]
227+
228+
def test_missing_field_with_origin(self):
229+
"""Missing field is filled with origin."""
230+
item = {"misc": {}, "origin": "broonie"}
231+
_standardize_lab_field(item, "runtime")
232+
assert item["misc"]["runtime"] == "broonie"
233+
234+
def test_missing_field_without_origin(self):
235+
"""Missing field stays absent when there is no origin."""
236+
item = {"misc": {}}
237+
_standardize_lab_field(item, "runtime")
238+
assert "runtime" not in item["misc"]
239+
240+
def test_real_value_unchanged(self):
241+
"""A real (non-automatic) lab value is left untouched."""
242+
item = {"misc": {"lab": "collabora"}, "origin": "redhat"}
243+
_standardize_lab_field(item, "lab")
244+
assert item["misc"]["lab"] == "collabora"
245+
assert AUTOMATIC_LAB_FIELD not in item["misc"]
246+
247+
203248
class TestStandardizeLabs:
204249
"""Test cases for standardize_labs function."""
205250

206251
# Test cases:
207-
# - builds/tests with missing misc field
208-
# - builds/tests with misc but not lab field
252+
# - builds/tests with missing misc field, no origin
253+
# - builds/tests with missing misc field, with origin (fallback)
254+
# - builds/tests with misc but not lab field, no origin
255+
# - builds/tests with misc but not lab field, with origin (fallback)
209256
# - empty builds/tests data
210257
# - builds/tests with real/automatic labs/runtimes
258+
# - builds/tests with automatic lab and origin (origin IS used as fallback)
211259

212-
def test_standardize_labs_missing_misc(self):
213-
"""Test with builds/tests missing misc field."""
260+
def test_standardize_labs_missing_misc_no_origin(self):
261+
"""Test with builds/tests missing misc field and no origin: misc not created."""
214262
input_data = {
215263
"builds": [
216264
{"id": "build1"},
@@ -225,8 +273,25 @@ def test_standardize_labs_missing_misc(self):
225273
assert "misc" not in input_data["builds"][0]
226274
assert "misc" not in input_data["tests"][0]
227275

228-
def test_standardize_labs_missing_lab_runtime(self):
229-
"""Test with builds/tests having misc but missing lab/runtime field."""
276+
def test_standardize_labs_missing_misc_with_origin_fallback(self):
277+
"""Test with builds/tests missing misc but with origin: misc created with origin as lab."""
278+
origin = "broonie"
279+
input_data = {
280+
"builds": [
281+
{"id": "build1", "origin": origin},
282+
],
283+
"tests": [
284+
{"id": "test1", "origin": origin},
285+
],
286+
}
287+
288+
standardize_labs(input_data)
289+
290+
assert input_data["builds"][0]["misc"]["lab"] == origin
291+
assert input_data["tests"][0]["misc"]["runtime"] == origin
292+
293+
def test_standardize_labs_missing_lab_runtime_no_origin(self):
294+
"""Test with builds/tests having misc but missing lab/runtime and no origin: unchanged."""
230295
input_data = {
231296
"builds": [
232297
{
@@ -251,6 +316,25 @@ def test_standardize_labs_missing_lab_runtime(self):
251316
assert "runtime" not in input_data["tests"][0]["misc"]
252317
assert AUTOMATIC_LAB_FIELD not in input_data["tests"][0]["misc"]
253318

319+
def test_standardize_labs_missing_lab_runtime_with_origin_fallback(self):
320+
"""Test with builds/tests having misc but missing lab/runtime with origin: origin used."""
321+
origin = "redhat"
322+
input_data = {
323+
"builds": [
324+
{"misc": {"other_field": "value"}, "origin": origin},
325+
],
326+
"tests": [
327+
{"misc": {"platform": "x86"}, "origin": origin},
328+
],
329+
}
330+
331+
standardize_labs(input_data)
332+
333+
assert input_data["builds"][0]["misc"]["lab"] == origin
334+
assert AUTOMATIC_LAB_FIELD not in input_data["builds"][0]["misc"]
335+
assert input_data["tests"][0]["misc"]["runtime"] == origin
336+
assert AUTOMATIC_LAB_FIELD not in input_data["tests"][0]["misc"]
337+
254338
def test_standardize_labs_empty_data(self):
255339
"""Test with empty builds and tests."""
256340
input_data = {
@@ -263,6 +347,26 @@ def test_standardize_labs_empty_data(self):
263347
assert input_data["builds"] == []
264348
assert input_data["tests"] == []
265349

350+
def test_standardize_labs_automatic_lab_with_origin_falls_back(self):
351+
"""Test that when an automatic lab is present, origin IS used as fallback for lab/runtime."""
352+
origin = "maestro"
353+
input_data = {
354+
"builds": [
355+
{"misc": {"lab": "shell"}, "origin": origin},
356+
],
357+
"tests": [
358+
{"misc": {"runtime": "k8s"}, "origin": origin},
359+
],
360+
}
361+
362+
standardize_labs(input_data)
363+
364+
# Automatic lab moved to AUTOMATIC_LAB_FIELD, origin used as fallback for lab/runtime
365+
assert input_data["builds"][0]["misc"][AUTOMATIC_LAB_FIELD] == "shell"
366+
assert input_data["builds"][0]["misc"]["lab"] == origin
367+
assert input_data["tests"][0]["misc"][AUTOMATIC_LAB_FIELD] == "k8s"
368+
assert input_data["tests"][0]["misc"]["runtime"] == origin
369+
266370
def test_standardize_labs_mixed_builds_and_tests(self):
267371
"""Test with a mix of automatic and real labs/runtimes."""
268372
input_data = {

0 commit comments

Comments
 (0)