14 from collections
import defaultdict
15 from pathlib
import Path
16 from typing
import Callable, Generator, Optional
21 from GaudiTesting.pytest.ctest_measurements_reporter
import results
25 expand_reference_file_name,
37 yaml.representer.SafeRepresenter.add_representer(str, str_representer)
38 yaml.representer.SafeRepresenter.add_representer(
39 AlwaysTrueDefaultDict, yaml.representer.SafeRepresenter.represent_dict
59 config.addinivalue_line(
61 "shared_cwd(id): make SubprocessBaseTest tests share a working directory",
63 config.addinivalue_line(
65 "do_not_collect_source: flag the test code as not to be collected",
71 Record source code of tests.
74 if isinstance(item, pytest.Function)
and not item.get_closest_marker(
75 "do_not_collect_source"
78 f
"{item.parent.name}.{item.originalname}"
79 if isinstance(item.parent, pytest.Class)
80 else item.originalname
82 source_code =
CodeWrapper(inspect.getsource(item.function),
"python")
83 results[f
"{name}.source_code"] = source_code
88 Extract the id of the shared cwd directory needed by the class, if any.
90 If the class is marked with shared_cwd multiple times only the last one
91 is taken into account.
93 Return the id or None.
95 if hasattr(cls,
"pytestmark"):
96 for mark
in cls.pytestmark:
97 if mark.name ==
"shared_cwd":
104 Return the path to the shared directory identified by id.
106 name = f
"gaudi.{get_platform()}.{cwd_id}".replace(
"/",
"_")
107 return config.cache.mkdir(name)
110 @pytest.fixture(scope=
"class")
112 request: pytest.FixtureRequest,
113 tmp_path_factory: Callable,
114 ) -> Generator[FixtureResult,
None,
None]:
117 if cls
and issubclass(cls, SubprocessBaseTest):
118 if hasattr(cls,
"popen_kwargs")
and "cwd" not in cls.popen_kwargs:
120 cls.popen_kwargs[
"cwd"] = (
123 else tmp_path_factory.mktemp(
"workdir")
125 result = cls.run_program(
126 tmp_path=tmp_path_factory.mktemp(
"tmp-", numbered=
True)
132 @pytest.fixture(scope=
"class")
134 fixture_result: FixtureResult,
135 ) -> Generator[subprocess.CompletedProcess,
None,
None]:
136 yield fixture_result.completed_process
if fixture_result
else None
139 @pytest.fixture(scope=
"class")
141 completed_process: subprocess.CompletedProcess,
142 ) -> Generator[bytes,
None,
None]:
143 yield completed_process.stdout
if completed_process
else None
146 @pytest.fixture(scope=
"class")
148 completed_process: subprocess.CompletedProcess,
149 ) -> Generator[bytes,
None,
None]:
150 yield completed_process.stderr
if completed_process
else None
153 @pytest.fixture(scope=
"class")
155 completed_process: subprocess.CompletedProcess,
156 ) -> Generator[int,
None,
None]:
157 yield completed_process.returncode
if completed_process
else None
160 @pytest.fixture(scope=
"class")
161 def cwd(fixture_result: FixtureResult) -> Generator[Path,
None,
None]:
162 yield Path(fixture_result.cwd)
if fixture_result
else None
165 @pytest.fixture(autouse=
True)
167 request: pytest.FixtureRequest, fixture_result: FixtureResult
171 and fixture_result.run_exception
is not None
172 and "test_fixture_setup" not in request.keywords
174 pytest.skip(f
"{fixture_result.run_exception}")
177 @pytest.fixture(scope=
"class", autouse=
True)
179 request: pytest.FixtureRequest,
182 if cls
and cls.__doc__:
183 results[f
"{cls.__name__}.doc"] = inspect.getdoc(cls)
186 @pytest.fixture(scope=
"class")
190 if not hasattr(cls,
"reference")
or cls.reference
is None:
193 elif isinstance(cls.reference, (str, os.PathLike)):
195 if hasattr(cls,
"resolve_path"):
196 path = cls.resolve_path(path)
199 yield Path(path)
if path
else None
201 elif isinstance(cls.reference, dict):
206 f
"Invalid reference type {type(cls.reference)}, only str, PathLike or dict are allowed"
210 @pytest.fixture(scope=
"class")
211 def reference(request, reference_path: Optional[Path]) -> Generator[dict,
None,
None]:
213 original_reference_data =
None
214 current_reference_data =
None
217 if reference_path.exists()
and reference_path.stat().st_size > 0:
218 with open(reference_path,
"r")
as f:
219 original_reference_data = yaml.safe_load(f)
226 current_reference_data = original_reference_data.copy()
228 elif hasattr(cls,
"reference")
and isinstance(cls.reference, dict):
229 original_reference_data = cls.reference
230 current_reference_data = original_reference_data.copy()
232 yield current_reference_data
234 if current_reference_data == original_reference_data:
239 newrefname = f
"{reference_path}.new"
240 while os.path.exists(newrefname):
242 newrefname = f
"{reference_path}.~{cnt}~.new"
244 if os.environ.get(
"GAUDI_TEST_IGNORE_STDOUT_VALIDATION") ==
"1":
246 newrefname = reference_path
248 os.makedirs(os.path.dirname(newrefname), exist_ok=
True)
249 with open(newrefname,
"w")
as f:
250 yaml.safe_dump(current_reference_data, f, sort_keys=
False)