Skip to content

Commit 41a25d8

Browse files
committed
Review comments
1 parent f2776b4 commit 41a25d8

2 files changed

Lines changed: 18 additions & 26 deletions

File tree

features/steps/generic_steps.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,9 @@ def step_impl(context, name):
331331
check_file_exists(name)
332332

333333

334-
def check_json(path: Union[str, os.PathLike], content: str) -> None:
334+
def check_json(path: Union[str, os.PathLike], content: str, context) -> None:
335335
"""Check a JSON file for exact equality (after normalising formatting)."""
336+
content = apply_archive_substitutions(content, context)
336337
with open(path, "r", encoding="UTF-8") as file_to_check:
337338
actual_json = json.load(file_to_check)
338339
expected_json = json.loads(content)
@@ -346,7 +347,7 @@ def check_json(path: Union[str, os.PathLike], content: str) -> None:
346347
@then("the '{name}' file contains")
347348
def step_impl(context, name):
348349
if name.endswith(".json"):
349-
check_json(name, context.text)
350+
check_json(name, context.text, context)
350351
else:
351352
check_file(name, context.text)
352353

features/steps/json_steps.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,10 @@ def _normalise_json(obj):
3838
def _json_subset_matches(expected, actual) -> bool:
3939
"""Return *True* when *expected* is a subset of *actual* (recursive).
4040
41-
**List matching is greedy and order-sensitive.** Each item in *expected*
42-
is matched against *actual* in order, claiming the first unused actual
43-
item that satisfies the subset check. This means an earlier expected
44-
item can consume the only actual item that a later, more specific
45-
expected item would need. For example, with::
46-
47-
expected = [{"a": 1}, {"a": 1, "b": 2}]
48-
actual = [{"a": 1, "b": 2}]
49-
50-
the first expected item matches ``{"a": 1, "b": 2}`` (leaving nothing
51-
for the second), so the overall match returns *False* even though
52-
``{"a": 1, "b": 2}`` satisfies the second item. Consumers should
53-
**not** rely on non-deterministic matching; instead, pre-order *expected*
54-
lists from most-specific to least-specific to avoid this behaviour.
41+
List matching uses backtracking so that all possible assignments between
42+
expected and actual items are explored. This avoids the greedy pitfall
43+
where an earlier expected item claims the only actual item that a later,
44+
more specific expected item would need.
5545
"""
5646
if isinstance(expected, dict):
5747
if not isinstance(actual, dict):
@@ -63,17 +53,18 @@ def _json_subset_matches(expected, actual) -> bool:
6353
if isinstance(expected, list):
6454
if not isinstance(actual, list):
6555
return False
66-
matched = [False] * len(actual)
67-
for exp_item in expected:
68-
found = False
56+
57+
def _try_match(exp_index: int, used: set) -> bool:
58+
if exp_index == len(expected):
59+
return True
60+
exp_item = expected[exp_index]
6961
for i, act_item in enumerate(actual):
70-
if not matched[i] and _json_subset_matches(exp_item, act_item):
71-
matched[i] = True
72-
found = True
73-
break
74-
if not found:
75-
return False
76-
return True
62+
if i not in used and _json_subset_matches(exp_item, act_item):
63+
if _try_match(exp_index + 1, used | {i}):
64+
return True
65+
return False
66+
67+
return _try_match(0, set())
7768
return expected == actual
7869

7970

0 commit comments

Comments
 (0)