-
Notifications
You must be signed in to change notification settings - Fork 5
CI: test that runs pdf pack examples in CI #40
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
Changes from 16 commits
1e2632f
856596e
c379a11
522c561
ac32634
3840f07
f02c948
bce5b86
6d2764e
0ef05db
4d70e15
f1c2ac2
5b27183
d0e17cf
89e89bb
f6ad035
9408042
b2fd64a
666707d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,7 +70,7 @@ | |
|
|
||
| # If we want to run using multiprocessors, we can switch this to 'True'. | ||
| # This requires that the 'psutil' python package installed. | ||
| RUN_PARALLEL = True | ||
| RUN_PARALLEL = False | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Requires psutil to be installed or it throws an error
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't want to do this either. Is there a reason not to install psutil? or make it conditional on whether psutil is installed? We don't want to change the behavior of the examples just so the CI will run. |
||
|
|
||
|
|
||
| # Functions that will carry out the refinement ################## | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,6 +87,9 @@ | |
| print("The Ni example refines instrument parameters\n") | ||
| print("The instrument parameters are necessary to run this fit\n") | ||
| print("Please run the Ni example first\n") | ||
| print("Setting Q_damp and Q_broad to refined values\n") | ||
| QDAMP_I = 0.045298 | ||
| QBROAD_I = 0.016809 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pytest was failing on CI (but not locally) without this. The reason why is because
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we put this in a conditional, so it runs the old way but does it the new way if it can't find the other results?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, that is what it is doing here. |
||
|
|
||
| # If we want to run using multiprocessors, we can switch this to 'True'. | ||
| # This requires that the 'psutil' python package installed. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| **Added:** | ||
|
|
||
| * Add CI for testing examples of the PDF pack. | ||
|
|
||
| **Changed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Deprecated:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Removed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Fixed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Security:** | ||
|
|
||
| * <news item> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,12 @@ | ||
| import importlib.util | ||
| import json | ||
| from pathlib import Path | ||
|
|
||
| import matplotlib | ||
| import pytest | ||
|
|
||
| __examples_dir__ = Path(__file__).parent.parent / "docs" / "examples" | ||
|
||
|
|
||
|
|
||
| @pytest.fixture | ||
| def user_filesystem(tmp_path): | ||
|
|
@@ -17,3 +21,25 @@ def user_filesystem(tmp_path): | |
| json.dump(home_config_data, f) | ||
|
|
||
| yield tmp_path | ||
|
|
||
|
|
||
| @pytest.fixture(scope="session", autouse=True) | ||
| def use_headless_matplotlib(): | ||
| """Force matplotlib to use a headless backend during tests.""" | ||
| matplotlib.use("Agg") | ||
|
|
||
|
|
||
| def load_module_from_path(path: Path): | ||
| """Load a module given an absolute Path.""" | ||
| spec = importlib.util.spec_from_file_location(path.stem, path) | ||
| module = importlib.util.module_from_spec(spec) | ||
| spec.loader.exec_module(module) | ||
| return module | ||
|
|
||
|
|
||
| def run_cmi_script(script_path: Path): | ||
| """General runner for example scripts with a main().""" | ||
| module = load_module_from_path(script_path) | ||
| if not hasattr(module, "main"): | ||
| pytest.skip(f"{script_path} has no main() function") | ||
| module.main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from conftest import __examples_dir__, run_cmi_script | ||
|
|
||
| chapter = "ch03NiModelling" | ||
|
||
| chapter_dir = Path(__examples_dir__) / chapter | ||
| example_scripts = list(chapter_dir.rglob("*.py")) | ||
|
|
||
|
|
||
| # Runs all example scripts in chapter 3, skips files if main() is not defined. | ||
| # Passes if script runs without error. | ||
| @pytest.mark.parametrize("script_path", example_scripts, ids=lambda p: p.name) | ||
| def test_ch03_examples(script_path): | ||
| run_cmi_script(script_path) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from conftest import __examples_dir__, run_cmi_script | ||
|
|
||
| chapter = "ch05Fit2Phase" | ||
| chapter_dir = Path(__examples_dir__) / chapter | ||
| example_scripts = list(chapter_dir.rglob("*.py")) | ||
|
|
||
|
|
||
| # Runs all example scripts in chapter 5, skips files if main() is not defined. | ||
| # Passes if script runs without error. | ||
| @pytest.mark.parametrize("script_path", example_scripts, ids=lambda p: p.name) | ||
| def test_ch03_examples(script_path): | ||
| run_cmi_script(script_path) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from conftest import __examples_dir__, run_cmi_script | ||
|
|
||
| chapter = "ch06RefineCrystalStructureGen" | ||
| chapter_dir = Path(__examples_dir__) / chapter | ||
| example_scripts = list(chapter_dir.rglob("*.py")) | ||
|
|
||
|
|
||
| # Runs all example scripts in chapter 6, skips files if main() is not defined. | ||
| # Passes if script runs without error. | ||
| @pytest.mark.parametrize("script_path", example_scripts, ids=lambda p: p.name) | ||
| def test_ch03_examples(script_path): | ||
| run_cmi_script(script_path) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from conftest import __examples_dir__, run_cmi_script | ||
|
|
||
| chapter = "ch07StructuralPhaseTransitions" | ||
| chapter_dir = Path(__examples_dir__) / chapter | ||
| example_scripts = list(chapter_dir.rglob("*.py")) | ||
|
|
||
|
|
||
| # Runs all example scripts in chapter 7, skips files if main() is not defined. | ||
| # Passes if script runs without error. | ||
| @pytest.mark.parametrize("script_path", example_scripts, ids=lambda p: p.name) | ||
| def test_ch03_examples(script_path): | ||
| run_cmi_script(script_path) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from conftest import __examples_dir__, run_cmi_script | ||
|
|
||
| chapter = "ch08NPRefinement" | ||
| chapter_dir = Path(__examples_dir__) / chapter | ||
| example_scripts = list(chapter_dir.rglob("*.py")) | ||
|
|
||
|
|
||
| # Runs all example scripts in chapter 8, skips files if main() is not defined. | ||
| # Passes if script runs without error. | ||
| @pytest.mark.parametrize("script_path", example_scripts, ids=lambda p: p.name) | ||
| def test_ch03_examples(script_path): | ||
| run_cmi_script(script_path) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
| from conftest import __examples_dir__, run_cmi_script | ||
|
|
||
| chapter = "ch11ClusterXYZ" | ||
| chapter_dir = Path(__examples_dir__) / chapter | ||
| example_scripts = list(chapter_dir.rglob("*.py")) | ||
|
|
||
|
|
||
| # Runs all example scripts in chapter 11, skips files if main() is not defined. | ||
| # Passes if script runs without error. | ||
| @pytest.mark.parametrize("script_path", example_scripts, ids=lambda p: p.name) | ||
| def test_ch03_examples(script_path): | ||
| run_cmi_script(script_path) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import re | ||
| from pathlib import Path | ||
|
|
||
| EXAMPLES_DIR = Path(__file__).parent.parent / "docs" / "examples" | ||
| TESTS_DIR = Path(__file__).parent | ||
|
|
||
|
|
||
| def get_example_chapters(): | ||
| """Return a dict {chapter_name: Path} for all dirs in examples/.""" | ||
| return {d.name: d for d in EXAMPLES_DIR.iterdir() if d.is_dir()} | ||
|
|
||
|
|
||
| def get_test_chapters(): | ||
| """Return a set of real chapter names that have test files.""" | ||
| chapters = set() | ||
| for test_file in TESTS_DIR.glob("test_ch*.py"): | ||
| m = re.match(r"test_(ch\d+)\.py", test_file.name) | ||
| if m: | ||
| prefix = m.group(1) | ||
| match = next( | ||
| ( | ||
| d.name | ||
| for d in EXAMPLES_DIR.iterdir() | ||
| if d.name.startswith(prefix) | ||
| ), | ||
| None, | ||
| ) | ||
| if match: | ||
| chapters.add(match) | ||
| return chapters | ||
|
|
||
|
|
||
| def test_chapters_have_valid_names_and_tests(): | ||
| """Check that all example chapters are named properly and have | ||
| tests.""" | ||
| example_chapters = get_example_chapters() | ||
| test_chapters = get_test_chapters() | ||
| errors = [] | ||
| # 1. Fail if any dir does not follow "ch" + number convention | ||
| bad_names = [ | ||
| name for name in example_chapters if not re.match(r"^ch\d+", name) | ||
| ] | ||
| if bad_names: | ||
| errors.append( | ||
| "Invalid example chapter names (must match '^ch[0-9]+'): " | ||
| + str(bad_names) | ||
| ) | ||
| # 2. Fail if any example chapter has no corresponding test | ||
| missing_tests = set(example_chapters) - test_chapters | ||
| if missing_tests: | ||
| errors.append( | ||
| "Missing test files for chapters or bad test file name " | ||
| "(must be named 'test_ch<NN>.py'): " + str(sorted(missing_tests)) | ||
| ) | ||
| assert not errors, "\n".join(errors) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The examples create these dirs. This prevents any accidental commits of the data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not good. This is precisely what we want to avoid (test-generated junk). How do the examples decide where to write these files? Can you copy the files to tmpdir and then run them?