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

Organise unit tests to use pytest fixtures #172

Merged
merged 1 commit into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
118 changes: 0 additions & 118 deletions tests/common.py

This file was deleted.

152 changes: 141 additions & 11 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,156 @@

import os
import shutil
import tempfile
from pathlib import Path
from subprocess import CalledProcessError, CompletedProcess
from typing import Optional

import pytest

from .common import MOCK_CWD
from benchcab.environment_modules import EnvironmentModulesInterface
from benchcab.utils.subprocess import SubprocessWrapperInterface


@pytest.fixture()
def mock_cwd():
"""Create and return a unique temporary directory to use as the CWD.

The return value is the path of the directory.
"""
return Path(tempfile.mkdtemp(prefix="benchcab_tests"))


@pytest.fixture(autouse=True)
def _run_around_tests():
"""`pytest` autouse fixture that runs around each test."""
# Setup:
def _run_around_tests(mock_cwd):
"""Change into the `mock_cwd` directory."""
prevdir = Path.cwd()
if MOCK_CWD.exists():
shutil.rmtree(MOCK_CWD)
MOCK_CWD.mkdir()
os.chdir(MOCK_CWD.expanduser())
os.chdir(mock_cwd.expanduser())

# Run the test:
yield

# Teardown:
os.chdir(prevdir)
shutil.rmtree(MOCK_CWD)
shutil.rmtree(mock_cwd)


@pytest.fixture()
def config():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I am going to approve this section, but bearing in mind that some of the changes I've made to the configuration validation system (which now has the bonus feature of installing a data directory [which contains tests]) have simplified this process significantly.

I'll have a fun merge on my hands after this one...

"""Returns a valid mock config."""
return {
"project": "bar",
"experiment": "five-site-test",
"modules": [
"intel-compiler/2021.1.1",
"openmpi/4.1.0",
"netcdf/4.7.4",
],
"realisations": [
{
"name": "trunk",
"revision": 9000,
"path": "trunk",
"patch": {},
"patch_remove": {},
"build_script": "",
},
{
"name": "v3.0-YP-changes",
"revision": -1,
"path": "branches/Users/sean/my-branch",
"patch": {"cable": {"cable_user": {"ENABLE_SOME_FEATURE": False}}},
"patch_remove": {},
"build_script": "",
},
],
"science_configurations": [
{
"cable": {
"cable_user": {
"GS_SWITCH": "medlyn",
"FWSOIL_SWITCH": "Haverd2013",
}
}
},
{
"cable": {
"cable_user": {
"GS_SWITCH": "leuning",
"FWSOIL_SWITCH": "Haverd2013",
}
}
},
],
"fluxsite": {
"pbs": {
"ncpus": 16,
"mem": "64G",
"walltime": "01:00:00",
"storage": ["gdata/foo123"],
},
"multiprocessing": True,
},
}


# Global string literal used so that it is accessible in tests
DEFAULT_STDOUT = "mock standard output"


@pytest.fixture()
def mock_subprocess_handler():
"""Returns a mock implementation of `SubprocessWrapperInterface`."""

class MockSubprocessWrapper(SubprocessWrapperInterface):
"""A mock implementation of `SubprocessWrapperInterface` used for testing."""

def __init__(self) -> None:
self.commands: list[str] = []
self.stdout = DEFAULT_STDOUT
self.error_on_call = False
self.env = {}

def run_cmd(
self,
cmd: str,
capture_output: bool = False,
output_file: Optional[Path] = None,
verbose: bool = False,
env: Optional[dict] = None,
) -> CompletedProcess:
self.commands.append(cmd)
if self.error_on_call:
raise CalledProcessError(returncode=1, cmd=cmd, output=self.stdout)
if output_file:
output_file.touch()
if env:
self.env = env
return CompletedProcess(cmd, returncode=0, stdout=self.stdout)

return MockSubprocessWrapper()


@pytest.fixture()
def mock_environment_modules_handler():
"""Returns a mock implementation of `EnvironmentModulesInterface`."""

class MockEnvironmentModules(EnvironmentModulesInterface):
"""A mock implementation of `EnvironmentModulesInterface` used for testing."""

def __init__(self) -> None:
self.commands: list[str] = []

def module_is_avail(self, *args: str) -> bool:
self.commands.append("module is-avail " + " ".join(args))
return True

Check warning on line 145 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L144-L145

Added lines #L144 - L145 were not covered by tests

def module_is_loaded(self, *args: str) -> bool:
self.commands.append("module is-loaded " + " ".join(args))
return True

Check warning on line 149 in tests/conftest.py

View check run for this annotation

Codecov / codecov/patch

tests/conftest.py#L148-L149

Added lines #L148 - L149 were not covered by tests

def module_load(self, *args: str) -> None:
self.commands.append("module load " + " ".join(args))

def module_unload(self, *args: str) -> None:
self.commands.append("module unload " + " ".join(args))

return MockEnvironmentModules()
Loading