diff --git a/autoconf/test_mode.py b/autoconf/test_mode.py index b22d8e7..22b14ba 100644 --- a/autoconf/test_mode.py +++ b/autoconf/test_mode.py @@ -1,4 +1,5 @@ import os +from pathlib import Path def test_mode_level(): @@ -61,3 +62,32 @@ def skip_latents(): ``compute_latent_samples`` pass. """ return is_test_mode() or os.environ.get("PYAUTO_SKIP_LATENTS", "0") == "1" + + +def with_test_mode_segment(base: Path) -> Path: + """ + Return ``base`` with a ``test_mode`` segment appended when + ``PYAUTO_TEST_MODE`` is active, else return ``base`` unchanged. + + Workspace scripts that compose their own output paths (e.g. the + ``guides/results/aggregator/`` tutorials in ``autolens_workspace`` and + ``autogalaxy_workspace``) must agree with PyAutoFit's internal + ``_test_mode_segment`` (``autofit/non_linear/paths/abstract.py``), which + inserts ``output/test_mode/...`` whenever ``PYAUTO_TEST_MODE`` is set. + This helper exposes the same namespacing rule so workspace path + composition stays consistent without duplicating the env-var check. + + The name avoids a leading ``test_`` so pytest does not try to + collect callsites in test modules as test functions. + + Examples + -------- + >>> # PYAUTO_TEST_MODE unset + >>> with_test_mode_segment(Path("output")) / "results_folder" + PosixPath('output/results_folder') + + >>> # PYAUTO_TEST_MODE=2 + >>> with_test_mode_segment(Path("output")) / "results_folder" + PosixPath('output/test_mode/results_folder') + """ + return base / "test_mode" if is_test_mode() else base diff --git a/test_autoconf/test_test_mode.py b/test_autoconf/test_test_mode.py new file mode 100644 index 0000000..5b27c8b --- /dev/null +++ b/test_autoconf/test_test_mode.py @@ -0,0 +1,56 @@ +"""Tests for autoconf.test_mode helpers — focused on +``with_test_mode_segment`` since the other helpers (``is_test_mode``, +``skip_fit_output``, etc.) are exercised by PyAutoFit/PyAutoArray +integration tests downstream.""" + +import os +from pathlib import Path + +import pytest + +from autoconf.test_mode import ( + is_test_mode, + test_mode_level, + with_test_mode_segment, +) + + +@pytest.fixture(autouse=True) +def _restore_test_mode_env(): + """Save/restore PYAUTO_TEST_MODE around each test so a failure + can't leak state to its neighbours.""" + + saved = os.environ.get("PYAUTO_TEST_MODE") + yield + if saved is None: + os.environ.pop("PYAUTO_TEST_MODE", None) + else: + os.environ["PYAUTO_TEST_MODE"] = saved + + +def test_with_test_mode_segment__env_unset_returns_base_unchanged(): + os.environ.pop("PYAUTO_TEST_MODE", None) + assert with_test_mode_segment(Path("output")) == Path("output") + + +def test_with_test_mode_segment__env_zero_returns_base_unchanged(): + """``PYAUTO_TEST_MODE=0`` is the documented off state, so the + helper must treat it the same as unset (no test_mode segment).""" + os.environ["PYAUTO_TEST_MODE"] = "0" + assert is_test_mode() is False + assert with_test_mode_segment(Path("output")) == Path("output") + + +@pytest.mark.parametrize("level", ["1", "2", "3"]) +def test_with_test_mode_segment__env_set_inserts_segment(level): + os.environ["PYAUTO_TEST_MODE"] = level + assert with_test_mode_segment(Path("output")) == Path("output") / "test_mode" + + +def test_with_test_mode_segment__chains_with_pathlib_concat(): + """The helper's return value must compose with further ``/`` ops so + workspace scripts can write ``with_test_mode_segment(base) / 'name'`` + in a single line.""" + os.environ["PYAUTO_TEST_MODE"] = "2" + composed = with_test_mode_segment(Path("output")) / "results_folder" + assert composed == Path("output") / "test_mode" / "results_folder"