Skip to content

Commit

Permalink
Add test script for GIS examples and run that in CI (#241)
Browse files Browse the repository at this point in the history
This PR adds a script to test all the GIS examples from the mesa-examples gis folder with pytest. It also adds a GitHub Actions CI workflow that runs that script continuously in CI.

- tests/test_GIS_examples.py, lend from Mesa itself.
- .github/workflows/examples.yml, also lend from Mesa
- pyproject.toml: Adds optional dependencies to test the examples.
  • Loading branch information
EwoutH authored Sep 3, 2024
1 parent 5fb2dde commit d8a1356
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 0 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Test GIS examples

on:
push:
branches:
- main
- release**
- "**maintenance"
paths-ignore:
- '**.md'
pull_request:
paths-ignore:
- '**.md'
workflow_dispatch:
schedule:
- cron: '0 6 * * 1'

jobs:
examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: 'pip'
- name: Install uv
run: pip install uv
- name: Install Mesa
run: uv pip install --system .[examples]
- name: Checkout mesa-examples
uses: actions/checkout@v4
with:
repository: projectmesa/mesa-examples
path: mesa-examples
- name: Test examples
run: |
cd mesa-examples
pytest -rA -Werror -Wdefault::FutureWarning test_gis_examples.py
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ docs = [
"myst_nb",
"myst-parser", # Markdown in Sphinx
]
examples = [
"pytest",
"momepy",
]

[project.urls]
homepage = "https://github.com/projectmesa/mesa-geo"
Expand Down
68 changes: 68 additions & 0 deletions tests/test_GIS_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import contextlib
import importlib
import os.path
import sys
import unittest


def classcase(name):
return "".join(x.capitalize() for x in name.replace("-", "_").split("_"))


@unittest.skip(
"Skipping TextExamples, because examples folder was moved. More discussion needed."
)
class TestExamples(unittest.TestCase):
"""
Test examples' models. This creates a model object and iterates it through
some steps. The idea is to get code coverage, rather than to test the
details of each example's model.
"""

EXAMPLES = os.path.abspath(os.path.join(os.path.dirname(__file__), "../gis"))

@contextlib.contextmanager
def active_example_dir(self, example):
"save and restore sys.path and sys.modules"
old_sys_path = sys.path[:]
old_sys_modules = sys.modules.copy()
old_cwd = os.getcwd()
example_path = os.path.abspath(os.path.join(self.EXAMPLES, example))
try:
sys.path.insert(0, example_path)
os.chdir(example_path)
yield
finally:
os.chdir(old_cwd)
added = [m for m in sys.modules if m not in old_sys_modules]
for mod in added:
del sys.modules[mod]
sys.modules.update(old_sys_modules)
sys.path[:] = old_sys_path

def test_examples(self):
for example in os.listdir(self.EXAMPLES):
if not os.path.isdir(os.path.join(self.EXAMPLES, example)):
continue
if hasattr(self, f"test_{example.replace('-', '_')}"):
# non-standard example; tested below
continue

print(f"testing example {example!r}")
with self.active_example_dir(example):
try:
# model.py at the top level
mod = importlib.import_module("model")
server = importlib.import_module("server")
server.server.render_model()
except ImportError:
# <example>/model.py
mod = importlib.import_module(f"{example.replace('-', '_')}.model")
server = importlib.import_module(
f"{example.replace('-', '_')}.server"
)
server.server.render_model()
model_class = getattr(mod, classcase(example))
model = model_class()
for _ in range(10):
model.step()

0 comments on commit d8a1356

Please sign in to comment.