文件
gprMax/reframe_tests/tests/regression_checks.py
2025-03-17 12:17:13 +00:00

177 行
5.6 KiB
Python

from os import PathLike
from pathlib import Path
from shutil import copyfile
from typing import Literal, Optional, Union
import reframe.utility.sanity as sn
from reframe.core.runtime import runtime
from reframe.utility import osext
class RegressionCheck:
"""Compare two files using diff"""
def __init__(
self, output_file: Union[str, PathLike], reference_file: Union[str, PathLike]
) -> None:
"""Create a new regression check.
Args:
output_file: Path to output file generate by the test.
reference_file: Path to reference file to run the regression
check against.
"""
self.output_file = Path(output_file)
self.reference_file = Path(reference_file)
self.cmd = "diff"
self.options: list[str] = []
@property
def error_msg(self) -> str:
"""Message to display if the regression check fails"""
return "Failed regression check"
def create_reference_file(self) -> bool:
"""Create reference file if it does not already exist.
The reference file is created as a copy of the current output
file.
Returns:
file_created: Returns True if a new file was created, False
if the path already exists.
"""
if not sn.path_exists(self.reference_file):
self.reference_file.parent.mkdir(parents=True, exist_ok=True)
copyfile(self.output_file, self.reference_file)
return True
else:
return False
def reference_file_exists(self) -> bool:
"""Check if the reference file exists.
Returns:
file_exists: Returns true if the reference filepath is a
regular file, False otherwise.
"""
return sn.path_isfile(self.reference_file)
def run(self) -> Literal[True]:
"""Run the regression check.
Returns:
check_passed: Returns True if the output file matches the
reference file (i.e. no output from diff). Otherwise,
raises a SanityError.
Raises:
reframe.core.exceptions.SanityError: If the output file does
not exist, or the regression check fails.
"""
completed_process = osext.run_command(
[
self.cmd,
*self.options,
str(self.output_file.absolute()),
str(self.reference_file),
]
)
return sn.assert_true(
sn.path_isfile(self.output_file),
f"Expected output file '{self.output_file}' does not exist",
) and sn.assert_false(
completed_process.stdout,
(
f"{self.error_msg}\n"
f"For more details run: '{' '.join(completed_process.args)}'\n"
f"To re-create regression file, delete '{self.reference_file}' and rerun the test."
),
)
class H5RegressionCheck(RegressionCheck):
"""Compare two hdf5 files using h5diff"""
def __init__(
self, output_file: Union[str, PathLike], reference_file: Union[str, PathLike]
) -> None:
super().__init__(output_file, reference_file)
if runtime().system.name == "archer2":
self.cmd = "/opt/cray/pe/hdf5/default/bin/h5diff"
else:
self.cmd = "h5diff"
class ReceiverRegressionCheck(H5RegressionCheck):
"""Run regression check on individual reveivers in output files.
This can include arbitrary receivers in each file, or two receivers
in the same file.
"""
def __init__(
self,
output_file: Union[str, PathLike],
reference_file: Union[str, PathLike],
output_receiver: str,
reference_receiver: Optional[str] = None,
) -> None:
"""Create a new receiver regression check.
Args:
output_file: Path to output file generate by the test.
reference_file: Path to reference file to run the regression
check against.
output_receiver: Output receiver to check.
reference_receiver: Optional receiver to check against in
the reference file. If None, this will be the same as
the output receiver.
"""
super().__init__(output_file, reference_file)
self.output_receiver = output_receiver
self.reference_receiver = reference_receiver
self.options.append(f"rxs/{self.output_receiver}")
if self.reference_receiver is not None:
self.options.append(f"rxs/{self.reference_receiver}")
@property
def error_msg(self) -> str:
return f"Receiver '{self.output_receiver}' failed regression check"
class SnapshotRegressionCheck(H5RegressionCheck):
"""Run regression check on a gprMax Snapshot."""
@property
def error_msg(self) -> str:
return f"Snapshot '{self.output_file.name}' failed regression check"
class GeometryObjectRegressionCheck(H5RegressionCheck):
"""Run regression check on a GprMax GeometryObject."""
@property
def error_msg(self) -> str:
return f"GeometryObject '{self.output_file.name}' failed regression check"
class GeometryObjectMaterialsRegressionCheck(RegressionCheck):
"""Run regression check on materials output by a GeometryObject."""
@property
def error_msg(self) -> str:
return f"GeometryObject materials file '{self.output_file}' failed regression check"
class GeometryViewRegressionCheck(H5RegressionCheck):
"""Run regression check on a GprMax GeometryView."""
@property
def error_msg(self) -> str:
return f"GeometryView '{self.output_file.name}' failed regression check"