climate_ref_core.testing
#
Test infrastructure for diagnostic testing.
This module provides: - TestCase and TestDataSpecification for defining test scenarios - YAML serialization for dataset catalogs (with local paths stored separately, under the cache)
TestCase
#
A single test case for a diagnostic.
Test cases define scenarios for testing, with data resolved via:
- requests: ESGF requests to fetch data (use ref test-cases fetch)
- datasets_file: Path to a pre-built catalog YAML file
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
datasets_file = None
class-attribute
instance-attribute
#
Path to YAML file with dataset specification (relative to package).
description
instance-attribute
#
Human-readable description of what this test case covers.
name
instance-attribute
#
Name of the test case (e.g., 'default', 'short-timeseries').
requests = None
class-attribute
instance-attribute
#
Optional ESGF requests to fetch data for this test case.
TestCasePaths
#
Path resolver for test case data.
Provides access to all paths within a test case directory: - catalog.yaml: Dataset metadata (tracked in git) - regression/: Regression outputs (tracked in git)
The local-paths sidecar (catalog.paths.yaml) is the exception: it lives under the
dataset cache rather than in the package tree. The paths it records are machine-specific
and point into the various caches (the dataset cache, the intake-esgf cache), so the
sidecar belongs with those caches rather than with a checkout. It is shared by every
checkout on the machine, and a fresh checkout cannot lose it.
Can be constructed from: - A diagnostic + test case name (auto-resolves provider's test-data dir) - An explicit test_data_dir + provider slug + diagnostic slug + test case name
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | |
catalog
property
#
Path to catalog.yaml.
catalog_paths
property
#
Path to catalog.paths.yaml, under the dataset cache rather than the package tree.
The sidecar maps each dataset to its local file, so it is a property of the machine, not of the checkout. Namespaced by provider because diagnostic slugs are only unique within a provider.
manifest
property
#
Path to manifest.json (the regression bundle manifest, tracked in git).
output
property
#
Path to the output/ directory (gitignored; holds materialised native slots).
provider_slug
instance-attribute
#
Slug of the provider owning the diagnostic, used to namespace the paths sidecar.
regression
property
#
Path to regression/ directory.
root
instance-attribute
#
The test case directory (test_data_dir / diagnostic_slug / test_case_name).
test_data_dir
property
#
Path to the test-data directory (parent of diagnostic slug dir).
create()
#
exists()
#
from_diagnostic(diagnostic, test_case)
classmethod
#
Create from a diagnostic, auto-resolving the provider's test-data directory.
Returns None if the provider's test-data directory cannot be determined (e.g., not a development checkout).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
diagnostic
|
Diagnostic
|
The diagnostic to get paths for |
required |
test_case
|
str
|
Test case name (e.g., 'default') |
required |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
from_test_data_dir(test_data_dir, diagnostic_slug, test_case, provider_slug)
classmethod
#
Create from an explicit test data directory.
Use this when you have a test_data_dir fixture (in tests) or know the base path explicitly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
test_data_dir
|
Path
|
Base test data directory (e.g., from test fixture) |
required |
diagnostic_slug
|
str
|
The diagnostic slug |
required |
test_case
|
str
|
Test case name (e.g., 'default') |
required |
provider_slug
|
str
|
The provider slug, used to namespace the paths sidecar |
required |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
output_slot(label='latest')
#
Path to a named output slot under output/ (gitignored).
A slot is a self-contained, inspectable snapshot of one execute/materialise:
the curated native set (flat, at manifest-relative paths) plus a regression/
subdirectory holding the rebuilt committed bundle.
latest (the default) is overwritten on every run; named slots persist so two
runs can be diffed (e.g. --label before vs --label after).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
label
|
str
|
Slot name. Must be a single path segment (no separators or |
'latest'
|
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
TestDataSpecification
#
Test data specification for a diagnostic.
Contains multiple named test cases for testing different input datasets.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
case_names
property
#
Get names of all test cases.
test_cases = field(factory=tuple)
class-attribute
instance-attribute
#
Collection of test cases for this diagnostic.
get_case(name)
#
Get a test case by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Name of the test case to retrieve |
required |
Returns:
| Type | Description |
|---|---|
TestCase
|
The matching test case |
Raises:
| Type | Description |
|---|---|
StopIteration
|
If no test case with that name exists |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
catalog_changed_since_regression(paths)
#
Check if the catalog has changed since regression data was generated.
The baseline's input hash is read from manifest.json (catalog_hash),
the single coupling record; there is no separate sidecar.
Returns True if:
- No regression data exists (new test case)
- No manifest, or the manifest records no catalog_hash (legacy regression data)
- No catalog file exists
- The current catalog hash differs from the one recorded in the manifest
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
paths
|
TestCasePaths
|
TestCasePaths for the test case |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if regression should be regenerated, False otherwise |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
collect_test_case_params(provider)
#
Collect all diagnostic/test_case pairs from a provider for parameterized testing.
Returns a list of pytest.param objects with (diagnostic, test_case_name) tuples, each with an id of "{diagnostic.slug}/{test_case.name}".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
DiagnosticProvider
|
The diagnostic provider to collect test cases from |
required |
Returns:
| Type | Description |
|---|---|
list[ParameterSet]
|
List of pytest.param objects for use with @pytest.mark.parametrize |
Example
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
excluded_test_case_diagnostics()
#
Diagnostic identifiers to skip when fetching or running test cases.
Reads the REF_TEST_CASES_SKIP environment variable
as a comma-separated list of diagnostic slugs or provider/diagnostic pairs.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
get_catalog_hash(path)
#
Get the hash stored in an existing catalog file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the catalog YAML file |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
The hash string if found, None if file doesn't exist or has no hash |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
is_test_case_excluded(provider_slug, diagnostic_slug)
#
Return True if the diagnostic is excluded via REF_TEST_CASES_SKIP.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
load_datasets_from_yaml(path, paths_file)
#
Load ExecutionDatasetCollection from a YAML file.
The YAML file structure:
cmip6:
slug_column: instance_id
selector:
source_id: ACCESS-ESM1-5
datasets:
- instance_id: CMIP6.CMIP...
variable_id: tas
filename: tas_Amon_ACCESS-ESM1-5_historical_r1i1p1f1_gn_185001-201412.nc
# ... other metadata
Paths are loaded from paths_file if it exists, allowing the main catalog to be
version-controlled while paths remain machine-specific. Multi-file datasets have
multiple rows with paths keyed by {instance_id}::{filename}.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the catalog YAML file |
required |
paths_file
|
Path
|
Path to the local-paths sidecar (see :attr: |
required |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
save_datasets_to_yaml(datasets, path, paths_file, *, force=False)
#
Save ExecutionDatasetCollection to a YAML file.
Paths are saved to a separate sidecar file to allow the main catalog to be version-controlled while paths remain machine-specific.
Multi-file datasets (e.g., time-chunked data) are stored as multiple rows,
one per file. Paths are keyed by {instance_id}::{filename} to support
multiple files per dataset.
By default, the catalog is only written if the content has changed
(detected via hash comparison). Use force=True to always write.
The paths sidecar is regenerated on every save, even when the catalog content is unchanged. Local cache contents can change independently of the version-controlled catalog, so retaining an existing sidecar can leave missing or stale paths behind.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
datasets
|
ExecutionDatasetCollection
|
The datasets to save |
required |
path
|
Path
|
Path to write the YAML file |
required |
paths_file
|
Path
|
Path to write the local-paths sidecar (see :attr: |
required |
force
|
bool
|
If True, always write the catalog even if unchanged |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the catalog was (re)written, False if the catalog was left unchanged (the paths sidecar may still have been regenerated). |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
validate_catalog_paths(path, paths_file)
#
Load a catalog and check that every row resolves to a local file.
Loading remains permissive because callers also use catalogs for metadata-only operations. Fetch and execution entry points can call this function when they require usable input files.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
Path to the catalog YAML file. |
required |
paths_file
|
Path
|
Path to the machine-local paths sidecar. |
required |
Returns:
| Type | Description |
|---|---|
ExecutionDatasetCollection
|
The loaded datasets, so callers need not parse the catalog a second time. |
Raises:
| Type | Description |
|---|---|
DatasetResolutionError
|
If the sidecar is absent for a non-empty catalog, incomplete, or points to missing files. |