Coverage for moptipy / tests / space.py: 68%

69 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-24 08:49 +0000

1"""Functions that can be used to test spaces.""" 

2from typing import Any, Callable 

3 

4# noinspection PyPackageRequirements 

5import pytest 

6from pycommons.io.csv import COMMENT_START 

7from pycommons.types import type_error 

8 

9from moptipy.api.space import Space, check_space 

10from moptipy.tests.component import validate_component 

11from moptipy.utils.logger import SECTION_END, SECTION_START 

12 

13 

14def validate_space( 

15 space: Space, 

16 make_element_valid: Callable[[Any], Any] | None = lambda x: x, 

17 make_element_invalid: Callable[[Any], Any] | None = None) -> None: 

18 """ 

19 Check whether an object is a moptipy space. 

20 

21 :param space: the space to test 

22 :param make_element_valid: a method that can turn a point from the 

23 space into a valid point 

24 :param make_element_invalid: a method can a valid point from the 

25 space into an invalid one 

26 :raises ValueError: if `space` is not a valid instance of 

27 :class:`~moptipy.api.space.Space` 

28 :raises TypeError: if incorrect types are encountered 

29 """ 

30 if not isinstance(space, Space): 

31 raise type_error(space, "space", Space) 

32 check_space(space) 

33 validate_component(space) 

34 

35 if not (hasattr(space, "create") and callable(getattr(space, "create"))): 

36 raise ValueError("space must have method create.") 

37 x1 = space.create() 

38 if x1 is None: 

39 raise ValueError("Spaces must create() valid objects, " 

40 "but returned None.") 

41 x2 = space.create() 

42 if x2 is None: 

43 raise ValueError("Spaces must create() valid objects, " 

44 "but returned None.") 

45 if x2 is x1: 

46 raise ValueError("The create() method must produce different " 

47 "instances when invoked twice, but returned the " 

48 "same object.") 

49 

50 if type(x1) is not type(x2): 

51 raise ValueError("The create() method must produce instances of " 

52 f"the same type, but got {type(x1)} and {type(x2)}.") 

53 

54 if not (hasattr(space, "copy") 

55 and callable(getattr(space, "copy"))): 

56 raise ValueError("space must have method copy.") 

57 space.copy(x2, x1) 

58 

59 if not (hasattr(space, "is_equal") 

60 and callable(getattr(space, "is_equal"))): 

61 raise ValueError("space must have method is_equal.") 

62 if not space.is_equal(x1, x2): 

63 raise ValueError("space.copy(x1, x2) did not lead to " 

64 "space.is_equal(x1, x2).") 

65 

66 if make_element_valid is None: 

67 return 

68 

69 x1 = make_element_valid(x1) 

70 

71 if not (hasattr(space, "validate") 

72 and callable(getattr(space, "validate"))): 

73 raise ValueError("space must have method validate.") 

74 space.validate(x1) 

75 

76 if not (hasattr(space, "to_str") and callable(getattr(space, "to_str"))): 

77 raise ValueError("space must have method to_str.") 

78 strstr = space.to_str(x1) 

79 if not isinstance(strstr, str): 

80 raise type_error(strstr, f"space.to_str(x) for {x1}", str) 

81 if len(strstr) <= 0: 

82 raise ValueError( 

83 "space.to_str(x) must not produce empty strings, " 

84 f"but we got {strstr!r}.") 

85 if strstr.strip() != strstr: 

86 raise ValueError( 

87 "space.to_str(x) must not include leading or trailing spaces," 

88 f" but we go {strstr!r}.") 

89 if SECTION_START in strstr: 

90 raise ValueError(f"space.to_str() must not include " 

91 f"{SECTION_START!r}, but is {strstr!r}.") 

92 if SECTION_END in strstr: 

93 raise ValueError(f"space.to_str() must not include " 

94 f"{SECTION_END!r}, but is {strstr!r}.") 

95 if COMMENT_START in strstr: 

96 raise ValueError(f"space.to_str() must not include " 

97 f"{COMMENT_START!r}, but is {strstr!r}.") 

98 

99 if not (hasattr(space, "from_str") 

100 and callable(getattr(space, "from_str"))): 

101 raise ValueError("space must have method from_str.") 

102 x3 = space.from_str(strstr) 

103 if (x3 is x1) or (x3 is x2): 

104 raise ValueError("from_str() cannot return the same object as " 

105 "create().") 

106 if not space.is_equal(x1, x3): 

107 raise ValueError("from_str(to_str()) must return equal object.") 

108 if space.to_str(x3) != strstr: 

109 raise ValueError("to_str(from_str(to_str())) must return same " 

110 "string.") 

111 space.validate(x3) 

112 

113 if make_element_invalid is None: 

114 return 

115 x2 = make_element_invalid(x3) 

116 if space.is_equal(x1, x2): 

117 raise ValueError( 

118 "make_element_invalid did not lead to a change in element!") 

119 

120 with pytest.raises(ValueError): # noqa 

121 space.validate(x2)