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 10 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",
)
168 changes: 168 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,168 @@
# 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 = "10YS"
VALID_MPPNCCOMBINE_EXE: str = "mppnccombine.spack"


### 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 28 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#L28

Added line #L28 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 32 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#L32

Added line #L32 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 42 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#L40-L42

Added lines #L40 - L42 were not covered by tests

def set_config_scenario(self) -> str:
# Regex below is split into three sections:
# Config type start section: '(?:release|dev)-' for 'release-' or '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 49 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#L49

Added line #L49 was not covered by tests
r"^(?:release|dev)-(?P<scenario>[^+]+)(?:\+.+)*$", self.branch_name
)
if not scenario_match or "scenario" not in scenario_match.groupdict():
pytest.fail(

Check warning on line 53 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#L52-L53

Added lines #L52 - L53 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 58 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#L58

Added line #L58 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 62 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#L62

Added line #L62 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 68 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#L67-L68

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

Check warning on line 71 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#L70-L71

Added lines #L70 - L71 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 74 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#L74

Added line #L74 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 scenario and modifiers, from the target branch name, some "
"tests may not be run. To set use --target-branch flag in pytest call"
)

return AccessEsm1p5Branch(branch_name)

Check warning on line 81 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#L81

Added line #L81 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 93 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#L93

Added line #L93 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 97 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#L97

Added line #L97 was not covered by tests

assert field_set == expected, error_field_incorrect(

Check warning on line 99 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#L99

Added line #L99 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 111 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#L111

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

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

Check warning on line 116 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#L116

Added line #L116 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 123 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#L123

Added line #L123 was not covered by tests

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

Check warning on line 126 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#L125-L126

Added lines #L125 - L126 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 130 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#L129-L130

Added lines #L129 - L130 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 134 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#L134

Added line #L134 was not covered by tests

def test_config_runtime(self, config):
assert (

Check warning on line 137 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#L137

Added line #L137 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 144 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#L144

Added line #L144 was not covered by tests

assert runtime == VALID_RUNTIME, error_field_incorrect(

Check warning on line 146 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#L146

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

def test_config_restart_freq(self, config):
assert (

Check warning on line 151 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#L151

Added line #L151 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 154 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#L154

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

def test_mppnccombine_fast_collate_exe(self, config):
if "collate" in config:
assert (

Check warning on line 160 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-L160

Added lines #L159 - L160 were not covered by tests
config["collate"]["exe"] == VALID_MPPNCCOMBINE_EXE
), error_field_incorrect(
"collate.exe", "config.yaml", VALID_MPPNCCOMBINE_EXE
)

assert config["collate"]["mpi"], error_field_incorrect(

Check warning on line 166 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#L166

Added line #L166 was not covered by tests
"collate.mpi", "config.yaml", "true"
)
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