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

1""" 

2Utility functions for tests. 

3Mainly manage the sys.path temporary modifications. 

4""" 

5 

6import sys 

7 

8from contextlib import contextmanager 

9 

10from typing import Iterator 

11from typing import Any 

12 

13 

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 

25 

26 

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 ) 

36 

37 

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. 

46 

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 )