Skip to content

Commit b78294b

Browse files
committed
Improve code, documentation and modularize test cases for config
1 parent 1f1f827 commit b78294b

4 files changed

Lines changed: 72 additions & 61 deletions

File tree

benchcab/benchcab.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ def fluxsite_submit_job(
133133
) -> None:
134134
"""Submits the PBS job script step in the fluxsite test workflow."""
135135
config = self._get_config(config_path)
136-
137136
self._validate_environment(project=config["project"], modules=config["modules"])
138137
if self.benchcab_exe_path is None:
139138
msg = "Path to benchcab executable is undefined."

benchcab/config.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
"""A module containing all *_config() functions."""
55
import os
6-
import sys
76
from pathlib import Path
87

98
import yaml
@@ -91,10 +90,14 @@ def read_optional_key(config: dict):
9190
raise ValueError(msg)
9291
config["project"] = os.environ["PROJECT"]
9392

94-
# Directory List is obtained from Gadi Resources - https://opus.nci.org.au/display/Help/0.+Welcome+to+Gadi
95-
data_dirs = ["/g/data", "/scratch"]
9693
groups = list(
97-
set([group for data_dir in data_dirs for group in os.listdir(data_dir)])
94+
set(
95+
[
96+
group
97+
for data_dir in internal.USER_PROJECT_DIRS
98+
for group in os.listdir(data_dir)
99+
]
100+
)
98101
)
99102

100103
if config["project"] not in groups:

benchcab/internal.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
_, NODENAME, _, _, _ = os.uname()
1212

13-
CONFIG_REQUIRED_KEYS = ["realisations", "modules", "experiment"]
13+
CONFIG_REQUIRED_KEYS = ["realisations", "modules"]
1414

1515
# Parameters for job script:
1616
QSUB_FNAME = "benchmark_cable_qsub.sh"
@@ -28,6 +28,10 @@
2828
# Path to the user's current working directory
2929
CWD = Path.cwd()
3030

31+
# Directory List is obtained from Gadi User Guide in Section - Gadi Resources
32+
# https://opus.nci.org.au/display/Help/0.+Welcome+to+Gadi
33+
USER_PROJECT_DIRS = ["/g/data", "/scratch"]
34+
3135
# Path to the user's home directory
3236
HOME_DIR = Path(os.environ["HOME"])
3337

tests/test_config.py

Lines changed: 60 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,21 @@
1212
import benchcab.internal as bi
1313
import benchcab.utils as bu
1414

15-
# Temporarily set $PROJECT for testing module
15+
NO_OPTIONAL_CONFIG_PROJECT = "hh5"
1616
OPTIONAL_CONFIG_PROJECT = "ks32"
1717

1818

19+
# Temporarily set $PROJECT for testing module
20+
@pytest.fixture(autouse=True, scope="module")
21+
def _set_project_validation_dirs():
22+
with mock.patch("os.listdir") as mocked_listdir:
23+
mocked_listdir.return_value = [
24+
NO_OPTIONAL_CONFIG_PROJECT,
25+
OPTIONAL_CONFIG_PROJECT,
26+
]
27+
yield
28+
29+
1930
@pytest.fixture(autouse=True)
2031
def _set_project_env_variable(monkeypatch):
2132
# Clear existing environment variables first
@@ -24,13 +35,6 @@ def _set_project_env_variable(monkeypatch):
2435
yield
2536

2637

27-
@pytest.fixture(autouse=True)
28-
def _set_project_validation_dirs():
29-
with mock.patch("os.listdir") as mocked_listdir:
30-
mocked_listdir.return_value = ["hh5", OPTIONAL_CONFIG_PROJECT]
31-
yield
32-
33-
3438
@pytest.fixture()
3539
def config_str(request) -> str:
3640
"""Provide relative YAML path string of data files."""
@@ -50,10 +54,10 @@ def empty_config() -> dict:
5054

5155

5256
@pytest.fixture()
53-
def default_only_config() -> dict:
57+
def no_optional_config() -> dict:
5458
"""Config with no optional parameters.
5559
56-
Reads from config-basic.yml
60+
Expected value after reading from config-basic.yml
5761
"""
5862
return {
5963
"modules": ["intel-compiler/2021.1.1", "netcdf/4.7.4", "openmpi/4.1.0"],
@@ -69,12 +73,12 @@ def default_only_config() -> dict:
6973

7074

7175
@pytest.fixture()
72-
def all_optional_default_config(default_only_config) -> dict:
73-
"""Config with all optional parameters set as default.
76+
def all_optional_default_config(no_optional_config) -> dict:
77+
"""Populate all keys in config with default optional values.
7478
75-
Reads from config-basic.yml
79+
Expected value after reading from config-basic.yml
7680
"""
77-
config = default_only_config | {
81+
config = no_optional_config | {
7882
"project": OPTIONAL_CONFIG_PROJECT,
7983
"fluxsite": {
8084
"experiment": bi.FLUXSITE_DEFAULT_EXPERIMENT,
@@ -90,13 +94,13 @@ def all_optional_default_config(default_only_config) -> dict:
9094

9195

9296
@pytest.fixture()
93-
def all_optional_custom_config(default_only_config) -> dict:
94-
"""Config with custom optional parameters.
97+
def all_optional_custom_config(no_optional_config) -> dict:
98+
"""Populate all keys in config with custom optional values.
9599
96-
Reads from config-optional.yml
100+
Expected value after reading from config-optional.yml
97101
"""
98-
config = default_only_config | {
99-
"project": "hh5",
102+
config = no_optional_config | {
103+
"project": NO_OPTIONAL_CONFIG_PROJECT,
100104
"fluxsite": {
101105
"experiment": "AU-Tum",
102106
"multiprocess": False,
@@ -126,7 +130,7 @@ def all_optional_custom_config(default_only_config) -> dict:
126130
@pytest.mark.parametrize(
127131
("config_str", "output_config", "pytest_error"),
128132
[
129-
("config-basic.yml", "default_only_config", does_not_raise()),
133+
("config-basic.yml", "no_optional_config", does_not_raise()),
130134
("config-optional.yml", "all_optional_custom_config", does_not_raise()),
131135
("config-missing.yml", "empty_config", pytest.raises(FileNotFoundError)),
132136
],
@@ -155,44 +159,45 @@ def test_validate_config(config_str, pytest_error):
155159
assert bc.validate_config(config)
156160

157161

158-
@pytest.mark.parametrize(
159-
("input_config", "output_config"),
160-
[
161-
("default_only_config", "all_optional_default_config"),
162-
("all_optional_default_config", "all_optional_default_config"),
163-
("all_optional_custom_config", "all_optional_custom_config"),
164-
],
165-
)
166-
def test_read_optional_key_add_data(input_config, output_config, request):
167-
"""Test default key-values are added if not provided by config.yaml, and existing keys stay intact."""
168-
config = request.getfixturevalue(input_config)
169-
bc.read_optional_key(config)
170-
assert pformat(config) == pformat(request.getfixturevalue(output_config))
171-
162+
class TestReadOptionalKey:
163+
"""Tests related to adding optional keys in config."""
172164

173-
def test_no_project_name(default_only_config, monkeypatch):
174-
"""If project key and $PROJECT are not provided, then raise error."""
175-
monkeypatch.delenv("PROJECT")
176-
err_msg = re.escape(
177-
"""Couldn't resolve project: check 'project' in config.yaml
178-
and/or $PROJECT set in ~/.config/gadi-login.conf
179-
"""
165+
@pytest.mark.parametrize(
166+
("input_config", "output_config"),
167+
[
168+
("no_optional_config", "all_optional_default_config"),
169+
("all_optional_default_config", "all_optional_default_config"),
170+
("all_optional_custom_config", "all_optional_custom_config"),
171+
],
180172
)
181-
with pytest.raises(ValueError, match=err_msg):
182-
bc.read_optional_key(default_only_config)
183-
173+
def test_read_optional_key_add_data(self, input_config, output_config, request):
174+
"""Test default key-values are added if not provided by config.yaml, and existing keys stay intact."""
175+
config = request.getfixturevalue(input_config)
176+
bc.read_optional_key(config)
177+
assert pformat(config) == pformat(request.getfixturevalue(output_config))
184178

185-
def test_user_not_in_project(default_only_config):
186-
"""If user is not in viewable NCI projects, raise error."""
187-
default_only_config["project"] = "non_existing"
188-
err_msg = re.escape(
189-
"User is not a member of project [non_existing]: Check if project key is correct"
190-
)
191-
with pytest.raises(
192-
ValueError,
193-
match=err_msg,
194-
):
195-
bc.read_optional_key(default_only_config)
179+
def test_no_project_name(self, no_optional_config, monkeypatch):
180+
"""If project key and $PROJECT are not provided, then raise error."""
181+
monkeypatch.delenv("PROJECT")
182+
err_msg = re.escape(
183+
"""Couldn't resolve project: check 'project' in config.yaml
184+
and/or $PROJECT set in ~/.config/gadi-login.conf
185+
"""
186+
)
187+
with pytest.raises(ValueError, match=err_msg):
188+
bc.read_optional_key(no_optional_config)
189+
190+
def test_user_not_in_project(self, no_optional_config):
191+
"""If user is not in viewable NCI projects, raise error."""
192+
no_optional_config["project"] = "non_existing"
193+
err_msg = re.escape(
194+
"User is not a member of project [non_existing]: Check if project key is correct"
195+
)
196+
with pytest.raises(
197+
ValueError,
198+
match=err_msg,
199+
):
200+
bc.read_optional_key(no_optional_config)
196201

197202

198203
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)