Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ACCESS-ESM1.5: QA Tests #41

Merged
merged 14 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "model_config_tests"
version = "0.0.2"
version = "0.0.3"
authors = [
{ name = "ACCESS-NRI" },
]
Expand Down
4 changes: 4 additions & 0 deletions src/model_config_tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,7 @@ def pytest_configure(config):
config.addinivalue_line(
"markers", "access_om3: mark as access-om3 specific tests in quick QA CI checks"
)
config.addinivalue_line(
"markers",
"access_esm1p5: mark as access-esm1.5 specific tests in quick QA CI checks",
)
167 changes: 167 additions & 0 deletions src/model_config_tests/qa/test_access_esm1p5_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Copyright 2024 ACCESS-NRI and contributors. See the top-level COPYRIGHT file for details.
# SPDX-License-Identifier: Apache-2.0

"""ACCESS-ESM1.5 specific configuration tests"""

import re
import warnings
from typing import Any

import pytest

from model_config_tests.util import get_git_branch_name

### Bunch of expected values for tests ###
VALID_REALMS: set[str] = {"atmosphere", "land", "ocean", "ocnBgchm", "seaIce"}
VALID_KEYWORDS: set[str] = {"global", "access-esm1.5"}
VALID_NOMINAL_RESOLUTION: str = "100 km"
VALID_REFERENCE: str = "https://doi.org/10.1071/ES19035"
VALID_PREINDUSTRIAL_START: dict[str, int] = {"year": 101, "month": 1, "day": 1}
VALID_HISTORICAL_START: dict[str, int] = {"year": 1850, "month": 1, "day": 1}
VALID_RUNTIME: dict[str, int] = {"year": 1, "month": 0, "day": 0}
VALID_RESTART_FREQ: str = "20YS"
aidanheerdegen marked this conversation as resolved.
Show resolved Hide resolved


### Some functions to avoid copying assertion error text
def error_field_nonexistence(field: str, file: str) -> str:
return f"Field '{field}' is null or does not exist in {file}."

Check warning on line 27 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L27

Added line #L27 was not covered by tests


def error_field_incorrect(field: str, file: str, expected: Any) -> str:
return f"Field '{field}' in {file} is not expected value: {expected}"

Check warning on line 31 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L31

Added line #L31 was not covered by tests


class AccessEsm1p5Branch:
"""Use the naming patterns of the branch name to infer information of
the ACCESS-ESM1.5 config"""

def __init__(self, branch_name):
self.branch_name = branch_name
self.config_scenario = self.set_config_scenario()
self.config_modifiers = self.set_config_modifiers()

Check warning on line 41 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L39-L41

Added lines #L39 - L41 were not covered by tests

def set_config_scenario(self) -> str:
# Regex below is split into three sections:
# Config type start section: '^.+-' for 'release-', 'dev-'...
# Scenario section: '([^+]+)' for 'preindustrial', 'historical'...anything that isn't the '+' modifier sigil
# Modifiers end section: '(?:\+.+)*' any amount of '+modifer' sections
scenario_match = re.match(

Check warning on line 48 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L48

Added line #L48 was not covered by tests
r"^.+-(?P<scenario>[^+]+)(?:\+.+)*$", self.branch_name
aidanheerdegen marked this conversation as resolved.
Show resolved Hide resolved
)
if not scenario_match or "scenario" not in scenario_match.groupdict():
pytest.fail(

Check warning on line 52 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L51-L52

Added lines #L51 - L52 were not covered by tests
f"Could not find a scenario in the branch {self.branch_name}. "
+ "Branches must be of the form 'type-scenario[+modifier...]'. "
+ "See README.md for more information."
)
return scenario_match.group("scenario")

Check warning on line 57 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L57

Added line #L57 was not covered by tests

def set_config_modifiers(self) -> list[str]:
# Regex below is essentially 'give me the 'modifier' part in all the '+modifier's in the branch name'
return re.findall(r"\+([^+]+)", self.branch_name)

Check warning on line 61 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L61

Added line #L61 was not covered by tests
aidanheerdegen marked this conversation as resolved.
Show resolved Hide resolved


@pytest.fixture(scope="class")
def branch(control_path, target_branch):
branch_name = target_branch
if branch_name is None:

Check warning on line 67 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L66-L67

Added lines #L66 - L67 were not covered by tests
# Default to current branch name
branch_name = get_git_branch_name(control_path)
assert (

Check warning on line 70 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L69-L70

Added lines #L69 - L70 were not covered by tests
branch_name is not None
), f"Failed getting git branch name of control path: {control_path}"
warnings.warn(

Check warning on line 73 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L73

Added line #L73 was not covered by tests
"Target branch is not specifed, defaulting to current git branch: "
f"{branch_name}. As some ACCESS-ESM1.5 tests infer information, "
"such as resolution, from the target branch name, some tests may "
"not be run. To set use --target-branch flag in pytest call"
aidanheerdegen marked this conversation as resolved.
Show resolved Hide resolved
)

return AccessEsm1p5Branch(branch_name)

Check warning on line 80 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L80

Added line #L80 was not covered by tests


@pytest.mark.access_esm1p5
class TestAccessEsm1p5:
"""ACCESS-ESM1.5 Specific configuration and metadata tests"""

@pytest.mark.parametrize(
"field,expected", [("realm", VALID_REALMS), ("keyword", VALID_KEYWORDS)]
)
def test_metadata_field_equal_expected_sequence(self, field, expected, metadata):

assert (

Check warning on line 92 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L92

Added line #L92 was not covered by tests
field in metadata and metadata[field] is not None
), error_field_nonexistence(field, "metadata.yaml")

field_set: set[str] = set(metadata[field])

Check warning on line 96 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L96

Added line #L96 was not covered by tests

assert field_set == expected, error_field_incorrect(

Check warning on line 98 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L98

Added line #L98 was not covered by tests
field, "metadata.yaml", "sequence", expected
)

@pytest.mark.parametrize(
"field,expected",
[
("nominal_resolution", VALID_NOMINAL_RESOLUTION),
("reference", VALID_REFERENCE),
],
)
def test_metadata_field_equal_expected_value(self, field, expected, metadata):
assert field in metadata and metadata[field] == expected, error_field_incorrect(

Check warning on line 110 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L110

Added line #L110 was not covered by tests
field, "metadata.yaml", expected
)

def test_config_start(self, branch, config):
assert (

Check warning on line 115 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L115

Added line #L115 was not covered by tests
"calendar" in config
and config["calendar"] is not None
and "start" in config["calendar"]
and config["calendar"]["start"] is not None
), error_field_nonexistence("calendar.start", "config.yaml")

start: dict[str, int] = config["calendar"]["start"]

Check warning on line 122 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L122

Added line #L122 was not covered by tests

if branch.config_scenario == "preindustrial":
assert start == VALID_PREINDUSTRIAL_START, error_field_incorrect(

Check warning on line 125 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L124-L125

Added lines #L124 - L125 were not covered by tests
"calendar.start", "config.yaml", VALID_PREINDUSTRIAL_START
)
elif branch.config_scenario == "historical":
assert start == VALID_HISTORICAL_START, error_field_incorrect(

Check warning on line 129 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L128-L129

Added lines #L128 - L129 were not covered by tests
"calendar.start", "config.yaml", VALID_HISTORICAL_START
)
else:
pytest.fail(f"Cannot test unknown scenario {branch.config_scenario}.")

Check warning on line 133 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L133

Added line #L133 was not covered by tests

def test_config_runtime(self, config):
assert (

Check warning on line 136 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L136

Added line #L136 was not covered by tests
"calendar" in config
and config["calendar"] is not None
and "runtime" in config["calendar"]
and config["calendar"]["runtime"] is not None
), error_field_nonexistence("calendar.runtime", "config.yaml")

runtime: dict[str, int] = config["calendar"]["runtime"]

Check warning on line 143 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L143

Added line #L143 was not covered by tests

assert runtime == VALID_RUNTIME, error_field_incorrect(

Check warning on line 145 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L145

Added line #L145 was not covered by tests
"calendar.runtime", "config.yaml", VALID_RUNTIME
)

def test_config_restart_freq(self, config):
assert (

Check warning on line 150 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L150

Added line #L150 was not covered by tests
"restart_freq" in config and config["restart_freq"] is not None
), error_field_nonexistence("restart_freq", "config.yaml")
assert config["restart_freq"] == VALID_RESTART_FREQ, error_field_incorrect(

Check warning on line 153 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L153

Added line #L153 was not covered by tests
"restart_freq", "config.yaml", VALID_RESTART_FREQ
)

def test_mppncombine_fast_collate_exe(self, config):
# TODO: We don't check for high resolution here like we do in the ACCESS-OM2 version of the test. Should we?
pattern = r"/g/data/vk83/apps/mppnccombine-fast/.*/bin/mppnccombine-fast"
if "collate" in config:
assert re.match(

Check warning on line 161 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L159-L161

Added lines #L159 - L161 were not covered by tests
pattern, config["collate"]["exe"]
), "Expect collate executable set to mppnccombine-fast"

assert config["collate"][

Check warning on line 165 in src/model_config_tests/qa/test_access_esm1p5_config.py

View check run for this annotation

Codecov / codecov/patch

src/model_config_tests/qa/test_access_esm1p5_config.py#L165

Added line #L165 was not covered by tests
"mpi"
], "Expect `mpi: true` when using mppnccombine-fast"
aidanheerdegen marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import f90nml
import pytest

from model_config_tests.test_config import check_manifest_exes_in_spack_location
from model_config_tests.qa.test_config import check_manifest_exes_in_spack_location
from model_config_tests.util import get_git_branch_name

# Mutually exclusive topic keywords
Expand Down
2 changes: 1 addition & 1 deletion src/model_config_tests/test_access_esm1p5_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import pytest

from model_config_tests.test_config import check_manifest_exes_in_spack_location
from model_config_tests.qa.test_config import check_manifest_exes_in_spack_location

# Name of module on NCI
ACCESS_ESM1P5_MODULE_NAME = "access-esm1p5"
Expand Down
Loading