Source code for testing.assertions
"""Functions asserting certain conditions are met (used e.g., in integration tests)."""
import json
import logging
from pathlib import Path
import h5py
import yaml
from simtools.job_execution.log_inspector import check_plain_logs, check_tar_logs
from simtools.sim_events import validate_run_outputs
_logger = logging.getLogger(__name__)
[docs]
def assert_file_type(file_type, file_name):
"""
Assert that the file is of the given type.
Parameters
----------
file_type: str
File type (json, yaml).
file_name: str
File name.
"""
if file_type == "json":
try:
with open(file_name, encoding="utf-8") as file:
json.load(file)
return True
except json.JSONDecodeError, FileNotFoundError:
return False
if file_type in ("yaml", "yml"):
if Path(file_name).suffix[1:] not in ("yaml", "yml"):
return False
try:
with open(file_name, encoding="utf-8") as file:
yaml.safe_load(file)
return True
except yaml.YAMLError, FileNotFoundError:
return False
# no dedicated tests for other file types, checking suffix only
_logger.info(f"File type test is checking suffix only for {file_name} (suffix: {file_type}))")
return Path(file_name).suffix[1:] == file_type
[docs]
def assert_hdf5_datasets(file_name, expected_datasets):
"""Assert that an HDF5 file contains the requested root datasets.
Parameters
----------
file_name : str or pathlib.Path
HDF5 file to inspect.
expected_datasets : sequence of str
Names of root-level HDF5 datasets that must be present.
Returns
-------
bool
``True`` when all requested datasets are present.
Raises
------
AssertionError
If a requested item is missing or is not an HDF5 dataset.
OSError
If ``file_name`` is not a readable HDF5 file.
"""
with h5py.File(file_name, "r") as hdf5_file:
missing = []
invalid = []
for dataset_name in expected_datasets:
if dataset_name not in hdf5_file:
missing.append(dataset_name)
elif not isinstance(hdf5_file[dataset_name], h5py.Dataset):
invalid.append(dataset_name)
if missing or invalid:
details = []
if missing:
details.append(f"missing {missing}")
if invalid:
details.append(f"not datasets {invalid}")
raise AssertionError(
f"HDF5 file {file_name} has invalid output structure: "
f"{'; '.join(details)}. Available root items: {list(hdf5_file)}"
)
return True
[docs]
def assert_hdf5_dataset_min_rows(file_name, expected_min_rows):
"""Assert that named HDF5 datasets contain at least the requested number of rows.
Parameters
----------
file_name : str or pathlib.Path
HDF5 file to inspect.
expected_min_rows : mapping
Mapping from HDF5 dataset names to their minimum required row counts.
Returns
-------
bool
``True`` when every requested dataset meets its minimum row count.
Raises
------
AssertionError
If a requested item is missing, is not an HDF5 dataset, or contains too few rows.
OSError
If ``file_name`` is not a readable HDF5 file.
"""
with h5py.File(file_name, "r") as hdf5_file:
for dataset_name, minimum_rows in expected_min_rows.items():
if dataset_name not in hdf5_file or not isinstance(
hdf5_file[dataset_name], h5py.Dataset
):
raise AssertionError(
f"HDF5 file {file_name} has no dataset named '{dataset_name}'."
)
actual_rows = len(hdf5_file[dataset_name])
if actual_rows < minimum_rows:
raise AssertionError(
f"HDF5 dataset '{dataset_name}' in {file_name} has {actual_rows} row(s), "
f"expected at least {minimum_rows}."
)
return True
[docs]
def check_output_from_sim_telarray(file, file_test):
"""
Check that the sim_telarray simulation result is reasonable and matches the expected output.
Parameters
----------
file: Path
Path to the sim_telarray file.
file_test: dict
File test description including expected output and metadata.
Raises
------
ValueError
If the file is not a zstd compressed file.
"""
if (
"expected_sim_telarray_output" not in file_test
and "expected_sim_telarray_metadata" not in file_test
):
_logger.debug(f"No expected output or metadata provided, skipping checks {file_test}")
return True
assert_sim_telarray = []
event_type = file_test.get("expected_sim_telarray_output") or {}
event_type = event_type.get("event_type", "shower")
if "expected_sim_telarray_output" in file_test:
assert_sim_telarray.append(
validate_run_outputs.assert_expected_sim_telarray_output_and_event_type(
file=file,
expected_sim_telarray_output=file_test["expected_sim_telarray_output"],
event_type=event_type,
)
)
if "expected_sim_telarray_metadata" in file_test:
assert_sim_telarray.append(
validate_run_outputs.assert_expected_sim_telarray_metadata(
file=file,
expected_sim_telarray_metadata=file_test["expected_sim_telarray_metadata"],
)
)
if event_type == "shower":
assert_sim_telarray.append(validate_run_outputs.assert_n_showers_and_energy_range(file))
if "expected_sim_telarray_output" not in file_test:
assert_sim_telarray.append(
validate_run_outputs.assert_events_of_type(file, event_type=event_type)
)
return all(assert_sim_telarray)
[docs]
def check_log_files(log_file, file_test):
"""
Check log file (plain, tar) for wanted and forbidden patterns.
Parameters
----------
log_file : str
Path to the log file.
file_test : dict
Dictionary with the test configuration.
Returns
-------
bool
True if the logs are correct.
"""
if str(log_file).endswith(".tar.gz"):
return check_tar_logs(log_file, file_test)
return check_plain_logs(log_file, file_test)