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
« 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
4# noinspection PyPackageRequirements
5import pytest
6from pycommons.io.csv import COMMENT_START
7from pycommons.types import type_error
9from moptipy.api.space import Space, check_space
10from moptipy.tests.component import validate_component
11from moptipy.utils.logger import SECTION_END, SECTION_START
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.
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)
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.")
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)}.")
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)
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).")
66 if make_element_valid is None:
67 return
69 x1 = make_element_valid(x1)
71 if not (hasattr(space, "validate")
72 and callable(getattr(space, "validate"))):
73 raise ValueError("space must have method validate.")
74 space.validate(x1)
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}.")
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)
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!")
120 with pytest.raises(ValueError): # noqa
121 space.validate(x2)