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 paths stored separately) - RegressionValidator for validating pre-stored outputs - Utilities for CMEC bundle validation
RegressionValidator
#
Validate diagnostic outputs from pre-stored regression data.
Loads regression outputs and validates CMEC bundles without running the diagnostic. Suitable for fast CI validation.
The regression data is expected at: test_data_dir/{diagnostic}/{test_case}/regression/
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | |
paths
property
#
Get paths for this test case.
has_regression_data()
#
Check if regression data exists for this test case.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
load_regression_definition(tmp_dir)
#
Load regression data and create an ExecutionDefinition.
Copies regression data to tmp_dir and replaces path placeholders.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
validate(definition)
#
Validate CMEC bundles and series in the regression output.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
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) - catalog.paths.yaml: Local file paths (gitignored) - regression/: Regression outputs (tracked in git)
Can be constructed from: - A diagnostic + test case name (auto-resolves provider's test-data dir) - An explicit test_data_dir + diagnostic slug + test case name
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
120 121 122 123 124 125 126 127 128 129 130 131 132 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 | |
catalog
property
#
Path to catalog.yaml.
catalog_paths
property
#
Path to catalog.paths.yaml (gitignored, contains local file paths).
regression
property
#
Path to regression/ directory.
regression_catalog_hash
property
#
Path to catalog hash file in 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)
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 |
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.
Returns True if: - No regression data exists (new test case) - No stored catalog hash exists (legacy regression data) - The catalog hash differs from the stored one
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
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
load_datasets_from_yaml(path)
#
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 a separate .paths.yaml file if it exists,
allowing the main catalog to be version-controlled while paths
remain user-specific. Multi-file datasets have multiple rows with
paths keyed by {instance_id}::{filename}.
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
save_datasets_to_yaml(datasets, path, *, force=False)
#
Save ExecutionDatasetCollection to a YAML file.
Paths are saved to a separate .paths.yaml file to allow the main
catalog to be version-controlled while paths remain user-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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
datasets
|
ExecutionDatasetCollection
|
The datasets to save |
required |
path
|
Path
|
Path to write the YAML file |
required |
force
|
bool
|
If True, always write the catalog even if unchanged |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the catalog was written, False if skipped (unchanged) |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | |
validate_cmec_bundles(diagnostic, result)
#
Validate CMEC bundles in an execution result.
Performs structural validation of the metric and output bundles.
Raises:
| Type | Description |
|---|---|
AssertionError
|
If the result is not successful or bundles are invalid |
Source code in packages/climate-ref-core/src/climate_ref_core/testing.py
validate_series_regression(expected_path, actual_path, *, slug, replacements=None)
#
Assert that regenerated series match the committed regression series.
If expected_path does not exist (legacy regression data without a stored series),
the check is skipped.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
expected_path
|
Path
|
Path to the committed |
required |
actual_path
|
Path
|
Path to the freshly regenerated |
required |
slug
|
str
|
The diagnostic slug, used for error messages. |
required |
replacements
|
dict[str, str] | None
|
Optional |
None
|
Raises:
| Type | Description |
|---|---|
AssertionError
|
If the regenerated series differ from the committed series. |