Skip to content

Commit

Permalink
Merge pull request #36 from MODFLOW-USGS/v0.1.0
Browse files Browse the repository at this point in the history
Release 0.1.0
  • Loading branch information
wpbonelli authored Dec 29, 2022
2 parents 160348e + 1d447dd commit 12c69a2
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 74 deletions.
6 changes: 6 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### Version 0.1.0

#### Refactoring

* [refactor(fixtures)](https://github.com/modflowpy/flopy/commit/23593df7fb427d6de1d33f9aa408697d2536e473): Fix/refactor model-loading fixtures (#33). Committed by w-bonelli on 2022-12-29.

### Version 0.0.8

#### Bug fixes
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MODFLOW developer tools

### Version 0.0.8 — release candidate
### Version 0.1.0 — release candidate
[![GitHub tag](https://img.shields.io/github/tag/MODFLOW-USGS/modflow-devtools.svg)](https://github.com/MODFLOW-USGS/modflow-devtools/tags/latest)
[![PyPI Version](https://img.shields.io/pypi/v/modflow-devtools.png)](https://pypi.python.org/pypi/modflow-devtools)
[![PyPI Versions](https://img.shields.io/pypi/pyversions/modflow-devtools.png)](https://pypi.python.org/pypi/modflow-devtools)
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

project = 'modflow-devtools'
author = 'MODFLOW Team'
release = 0.0.8
release = 0.1.0

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
26 changes: 18 additions & 8 deletions docs/md/fixtures.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Several `pytest` fixtures are provided to help with testing.

## Keepable temporary directory fixtures
## Keepable temporary directories

Tests often need to exercise code that reads from and/or writes to disk. The test harness may also need to create test data during setup and clean up the filesystem on teardown. Temporary directories are built into `pytest` via the [`tmp_path`](https://docs.pytest.org/en/latest/how-to/tmp_path.html#the-tmp-path-fixture) and `tmp_path_factory` fixtures.

Expand Down Expand Up @@ -40,9 +40,9 @@ pytest --keep <path>

There is also a `--keep-failed <path>` option which preserves outputs only from failing test cases.

## Model-loading fixtures
## Loading example models

Fixtures are provided to load models from the MODFLOW 6 example and test model repositories and feed them to test functions. Models can be loaded from:
Fixtures are provided to find and enumerate models from the MODFLOW 6 example and test model repositories and feed them to test functions. Models can be loaded from:

- [`MODFLOW-USGS/modflow6-examples`](https://github.com/MODFLOW-USGS/modflow6-examples)
- [`MODFLOW-USGS/modflow6-testmodels`](https://github.com/MODFLOW-USGS/modflow6-testmodels)
Expand All @@ -61,14 +61,15 @@ To use these fixtures, the environment variable `REPOS_PATH` must point to the l

### Test models

The `test_model_mf5to6`, `test_model_mf6` and `large_test_model` fixtures are each a `Path` to the directory containing the model's namefile. For instance, to load `mf5to6` models from the [`MODFLOW-USGS/modflow6-testmodels`](https://github.com/MODFLOW-USGS/modflow6-testmodels) repository:
The `test_model_mf5to6`, `test_model_mf6` and `large_test_model` fixtures are each a `Path` to the model's namefile. For example:, to load `mf5to6` models from the repository:

```python
def test_mf5to6_model(testmodel_mf5to6):
assert testmodel_mf5to6.is_dir()
def test_mf5to6_model(test_model_mf5to6):
assert test_model_mf5to6.is_file()
assert test_model_mf5to6.suffix == ".nam"
```

This test function will be parametrized with all `mf5to6` models found in the `testmodels` repository (likewise for `mf6` models, and for large test models in their own repository).
This test function will be parametrized with all `mf5to6` models found in the [`MODFLOW-USGS/modflow6-testmodels`](https://github.com/MODFLOW-USGS/modflow6-testmodels).

### Example scenarios

Expand All @@ -87,4 +88,13 @@ def test_example_scenario(tmp_path, example_scenario):
model_ws.mkdir()
# load and run model
# ...
```
```

### Utility functions

Model-loading fixtures use a set of utility functions to find and enumerate models. These functions can be imported from `modflow_devtools.misc` for use in other contexts:

- `get_model_dir_paths()`
- `get_namefile_paths()`

See this project's test suite for usage examples.
2 changes: 1 addition & 1 deletion modflow_devtools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__author__ = "Joseph D. Hughes"
__date__ = "Dec 29, 2022"
__version__ = "0.0.8"
__version__ = "0.1.0"
__maintainer__ = "Joseph D. Hughes"
__email__ = "[email protected]"
__status__ = "Production"
Expand Down
20 changes: 12 additions & 8 deletions modflow_devtools/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
from typing import Dict, List, Optional

import pytest
from modflow_devtools.misc import get_model_paths, get_packages
from modflow_devtools.misc import (
get_model_dir_paths,
get_namefile_paths,
get_packages,
)

# temporary directory fixtures

Expand Down Expand Up @@ -174,7 +178,7 @@ def pytest_generate_tests(metafunc):
key = "test_model_mf6"
if key in metafunc.fixturenames:
models = (
get_model_paths(
get_namefile_paths(
Path(repos_path) / "modflow6-testmodels" / "mf6",
prefix="test",
excluded=["test205_gwtbuy-henrytidal"],
Expand All @@ -184,12 +188,12 @@ def pytest_generate_tests(metafunc):
if repos_path
else []
)
metafunc.parametrize(key, models, ids=[m.name for m in models])
metafunc.parametrize(key, models, ids=[str(m) for m in models])

key = "test_model_mf5to6"
if key in metafunc.fixturenames:
models = (
get_model_paths(
get_namefile_paths(
Path(repos_path) / "modflow6-testmodels" / "mf5to6",
prefix="test",
namefile="*.nam",
Expand All @@ -200,23 +204,23 @@ def pytest_generate_tests(metafunc):
if repos_path
else []
)
metafunc.parametrize(key, models, ids=[m.name for m in models])
metafunc.parametrize(key, models, ids=[str(m) for m in models])

key = "large_test_model"
if key in metafunc.fixturenames:
models = (
get_model_paths(
get_namefile_paths(
Path(repos_path) / "modflow6-largetestmodels",
prefix="test",
namefile="*.nam",
namefile="mfsim.nam",
excluded=[],
selected=models_selected,
packages=packages_selected,
)
if repos_path
else []
)
metafunc.parametrize(key, models, ids=[m.name for m in models])
metafunc.parametrize(key, models, ids=[str(m) for m in models])

key = "example_scenario"
if key in metafunc.fixturenames:
Expand Down
65 changes: 43 additions & 22 deletions modflow_devtools/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,66 +160,87 @@ def has_package(namefile_path: PathLike, package: str) -> bool:
return package.lower in packages


def get_model_paths(
def get_namefile_paths(
path: PathLike,
prefix: str = None,
namefile: str = "mfsim.nam",
excluded=None,
selected=None,
packages=None,
) -> List[Path]:
):
"""
Find models recursively in the given location.
Models can be filtered or excluded by pattern,
filtered by packages used or naming convention
for namefiles, or by parent folder name prefix.
The path to the model folder (i.e., the folder
containing the model's namefile) is returned.
Find namefiles recursively in the given location.
Namefiles can be filtered or excluded by pattern,
by parent directory name prefix or pattern, or by
packages used.
"""

# if path doesn't exist, return empty list
if not Path(path).is_dir():
return []

# find namfiles
namfile_paths = [
# find namefiles
paths = [
p
for p in Path(path).rglob(
f"{prefix}*/**/{namefile}" if prefix else namefile
)
]

# remove excluded
namfile_paths = [
paths = [
p
for p in namfile_paths
for p in paths
if (not excluded or not any(e in str(p) for e in excluded))
]

# filter by package
if packages:
filtered = []
for nfp in namfile_paths:
for nfp in paths:
nf_pkgs = get_packages(nfp)
shared = set(nf_pkgs).intersection(
set([p.lower() for p in packages])
)
if any(shared):
filtered.append(nfp)
namfile_paths = filtered

# get model folder paths
model_paths = [p.parent for p in namfile_paths]
paths = filtered

# filter by model name
if selected:
model_paths = [
model
for model in model_paths
if any(s in model.name for s in selected)
paths = [
namfile_path
for (namfile_path, model_path) in zip(
paths, [p.parent for p in paths]
)
if any(s in model_path.name for s in selected)
]

return sorted(model_paths)
return sorted(paths)


def get_model_dir_paths(
path: PathLike,
prefix: str = None,
namefile: str = "mfsim.nam",
excluded=None,
selected=None,
packages=None,
) -> List[Path]:
"""
Find model directories recursively in the given location.
A model directory is any directory containing one or more
namefiles. Model directories can be filtered or excluded,
by prefix, pattern, namefile name, or packages used.
"""

namefile_paths = get_namefile_paths(
path, prefix, namefile, excluded, selected, packages
)
model_paths = sorted(
list(set([p.parent for p in namefile_paths if p.parent.name]))
)
return model_paths


def is_connected(hostname):
Expand Down
12 changes: 7 additions & 5 deletions modflow_devtools/test/test_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,16 +249,18 @@ def test_example_scenario(example_scenario):

def test_test_model_mf6(test_model_mf6):
assert isinstance(test_model_mf6, Path)
assert (test_model_mf6 / "mfsim.nam").is_file()
assert test_model_mf6.is_file()
assert test_model_mf6.name == "mfsim.nam"


def test_test_model_mf5to6(test_model_mf5to6):
assert isinstance(test_model_mf5to6, Path)
assert any(list(test_model_mf5to6.glob("*.nam")))
assert test_model_mf5to6.is_file()
assert test_model_mf5to6.suffix == ".nam"


def test_large_test_model(large_test_model):
print(large_test_model)
assert isinstance(large_test_model, Path)
assert (large_test_model / "mfsim.nam").is_file() or any(
list(large_test_model.glob("*.nam"))
)
assert large_test_model.is_file()
assert large_test_model.suffix == ".nam"
Loading

0 comments on commit 12c69a2

Please sign in to comment.