Coverage for tests/test_utils.py: 100%
25 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-09 04:47 +0000
1"""
2Utility functions for tests.
3Mainly manage the sys.path temporary modifications.
4"""
6import sys
8from contextlib import contextmanager
10from typing import Iterator
11from typing import Any
14@contextmanager
15def temp_sys_path(
16 *paths: Any
17) -> Iterator[None]:
18 """Temporarily add paths to sys.path."""
19 old_sys_path = sys.path.copy()
20 sys.path[:0] = paths
21 try:
22 yield
23 finally:
24 sys.path = old_sys_path
27def assert_equals_approx(
28 value: float,
29 expected: float,
30 delta: float = 0.01,
31) -> None:
32 """Assert that two floats are approximately equal within a tolerance."""
33 assert abs(value - expected) < delta, (
34 f"Expected {value:.2f} to be approximately equal to {expected:.2f} within tolerance {delta}"
35 )
38def assert_equal_dict(
39 actual: dict[Any, Any],
40 expected: dict[Any, Any],
41 name: str = "dict",
42 delta: float = 0.01,
43 _path: str = "",
44) -> None:
45 """Recursively compare two nested dicts.
47 At each level the key sets must match exactly. Leaf values are compared
48 with ``assert_equals_approx`` when they are floats, otherwise with ``==``.
49 """
50 label = _path or name
51 assert set(actual.keys()) == set(expected.keys()), (
52 f"{label}: keys differ: {set(actual.keys())} != {set(expected.keys())}"
53 )
54 for key in expected:
55 exp_val = expected[key]
56 act_val = actual[key]
57 child_path = f"{label}[{key}]"
58 if isinstance(exp_val, dict):
59 assert_equal_dict(act_val, exp_val, name=name, delta=delta, _path=child_path)
60 elif isinstance(exp_val, float):
61 assert abs(act_val - exp_val) < delta, (
62 f"{child_path}: expected {exp_val:.2f}, got {act_val:.2f} (delta {delta})"
63 )
64 else:
65 assert act_val == exp_val, (
66 f"{child_path}: expected {exp_val}, got {act_val}"
67 )