Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Requires psutil to be installed or it throws an error

Copy link
Contributor

Choose a reason for hiding this comment

The 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 ##################
Expand Down
3 changes: 3 additions & 0 deletions docs/examples/ch03NiModelling/solutions/diffpy-cmi/fitNPPt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 fitBulkNi.py refines these values, then fitNPPt.py grabs these refined values and uses them. CI cannot find these values from the Ni fit so I just pasted them here. This somewhat changes how the tutorial will be ran because the user doesn't need to fit the Ni first, but I don't feel great about adding special handling in the test for this file (potentially more maintenance in the future). Let me know if you feel differently about this.

Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,6 @@ def plot_results(recipe, figname):
diff = g - gcalc + diffzero

mpl.rcParams.update(mpl.rcParamsDefault)
plt.style.use(
str(PWD.parent.parent.parent / "utils" / "billinge.mplstyle")
)

fig, ax1 = plt.subplots(1, 1)

Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorials/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Despite its utility, PDF fitting and analysis can be challenging.
The process of understand PDFs requires time, care, and the development of intuition to connect structural models to experimental data.
Hopefully by the end of this tutorial series, PDF fitting will go from being a mysterious black box to a powerful tool in your structural analysis.

Example usage of ``diffpy.cmi`` can be found at `this GitHub repo <https://github.com/diffpy/pdfttp_data>`_.
Example usage of ``diffpy.cmi`` can be found at `this GitHub repo <https://github.com/Billingegroup/pdfttp_data>`_.

.. _structural-parameters:

Expand Down
23 changes: 23 additions & 0 deletions news/test-tutorial-CI.rst
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>
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ include = ["*"] # package names should match these glob patterns (["*"] by defa
exclude = [] # exclude packages matching these glob patterns (empty by default)
namespaces = false # to disable scanning PEP 420 namespaces (true by default)

[tool.coverage.run]
omit = [
"*/tests/*",
"*/examples_copy*/*",
"*/__pycache__/*",
]

[project.scripts]
cmi = "diffpy.cmi.cli:main"

Expand Down
30 changes: 30 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import json
import shutil
import warnings
from pathlib import Path

import matplotlib
import pytest

# Suppress specific UserWarnings when running mpl headless
warnings.filterwarnings(
"ignore", category=UserWarning, message=".*FigureCanvasAgg.*"
)

EXAMPLES_DIR = Path(__file__).parent.parent / "docs" / "examples"
TESTS_DIR = Path(__file__).parent


@pytest.fixture
def user_filesystem(tmp_path):
Expand All @@ -17,3 +28,22 @@ 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")


@pytest.fixture(scope="session")
def examples_tmpdir(tmp_path_factory):
"""Make a temp copy of all examples for safe testing.

Removes the temp copy after tests are done.
"""
temp_dir = tmp_path_factory.mktemp("examples_copy")
temp_examples = temp_dir / "examples"
shutil.copytree(EXAMPLES_DIR, temp_examples, dirs_exist_ok=True)
yield temp_examples
shutil.rmtree(temp_dir, ignore_errors=True)
25 changes: 25 additions & 0 deletions tests/examples_exist_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from conftest import TESTS_DIR


def test_example_dirs_have_tests(examples_tmpdir):
"""Verify that each example directory has a corresponding test
file."""
example_dirs = [d for d in examples_tmpdir.iterdir() if d.is_dir()]
missing_tests = []
for example_dir in example_dirs:
# Test file expected in TESTS_DIR, named e.g. test_<example_dir>.py
test_file = TESTS_DIR / f"test_{example_dir.name}.py"
if not test_file.exists():
missing_tests.append(example_dir.name)
assert not missing_tests, (
f"The following example dirs have no test file: {missing_tests}.",
"Test file must be named test_<example_dir>.py.",
)


def test_examples_tmpdir_exists(examples_tmpdir):
"""Ensure that the examples temporary directory has been created."""
# Check the directory itself exists
assert (
examples_tmpdir.exists() and examples_tmpdir.is_dir()
), f"Temporary examples directory does not exist: {examples_tmpdir}"
43 changes: 43 additions & 0 deletions tests/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import importlib.util
import os
from pathlib import Path


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):
"""Run a script with a main() function."""
old_cwd = os.getcwd()
os.chdir(script_path.parent)
try:
module = load_module_from_path(script_path)
module.main()
finally:
os.chdir(old_cwd)


def run_all_scripts_for_given_example(examples_tmpdir, test_file_path):
"""Run all Python scripts in the chapter corresponding to this test
file.

Only scripts that define a main() function will be executed.
"""
chapter_dir_name = Path(test_file_path).stem.replace("test_", "")
chapter_dir = examples_tmpdir / chapter_dir_name
assert chapter_dir.exists(), f"Chapter dir does not exist: {chapter_dir}"

# Recursively find all .py scripts
scripts = list(chapter_dir.rglob("*.py"))
for script_path in scripts:
module = load_module_from_path(script_path)
if hasattr(module, "main"):
run_cmi_script(script_path)
else:
# automatically skip helper or non-example scripts
print(f"Skipping file without main(): {script_path.name}")
5 changes: 5 additions & 0 deletions tests/test_ch03NiModelling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from helpers import run_all_scripts_for_given_example


def test_scripts(examples_tmpdir):
run_all_scripts_for_given_example(examples_tmpdir, __file__)
5 changes: 5 additions & 0 deletions tests/test_ch05Fit2Phase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from helpers import run_all_scripts_for_given_example


def test_scripts(examples_tmpdir):
run_all_scripts_for_given_example(examples_tmpdir, __file__)
5 changes: 5 additions & 0 deletions tests/test_ch06RefineCrystalStructureGen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from helpers import run_all_scripts_for_given_example


def test_scripts(examples_tmpdir):
run_all_scripts_for_given_example(examples_tmpdir, __file__)
5 changes: 5 additions & 0 deletions tests/test_ch07StructuralPhaseTransitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from helpers import run_all_scripts_for_given_example


def test_scripts(examples_tmpdir):
run_all_scripts_for_given_example(examples_tmpdir, __file__)
5 changes: 5 additions & 0 deletions tests/test_ch08NPRefinement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from helpers import run_all_scripts_for_given_example


def test_scripts(examples_tmpdir):
run_all_scripts_for_given_example(examples_tmpdir, __file__)
5 changes: 5 additions & 0 deletions tests/test_ch11ClusterXYZ.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from helpers import run_all_scripts_for_given_example


def test_scripts(examples_tmpdir):
run_all_scripts_for_given_example(examples_tmpdir, __file__)
Loading