Testing¶
Running the suite¶
pip install -e ".[dev]"
pytest
pytest.ini points at tests/ and enables live INFO logging, so you see the same
reconstructor log lines the library emits at runtime.
Useful invocations:
pytest tests/test_tomographicReconstructor.py # one module
pytest -k "atmosphere" # by name
pytest -x -vv # stop at the first failure, verbose
pytest --cov=pyTomoAO --cov-report=term-missing # coverage with uncovered lines
Version-specific behaviour is easiest to reproduce in a throwaway environment:
conda create -y -n pytomoao310 python=3.10
conda run -n pytomoao310 pip install ".[dev]"
conda run -n pytomoao310 pytest
What is covered¶
Test module |
Covers |
|---|---|
|
Layer validation, airmass and |
|
Asterism geometry and direction vectors |
|
Lenslet maps, |
|
Actuator maps and geometry validation |
|
Influence functions and the fitting matrix |
|
End-to-end reconstructor construction |
The parameter classes are the cheapest place to add tests, and the place where a bad configuration does the most damage — new validation logic should always come with a test for both the accepted and rejected cases.
The CI gate¶
.github/workflows/test.yml runs on pull requests and on pushes to main and dev. It
installs the package itself — pip install ".[dev]" — and runs pytest against a matrix of
Python 3.9 through 3.13, so both the code and the dependency metadata are checked on every
supported version.
On the 3.12 leg it additionally runs a wrapper script rather than pytest directly:
python .github/github_pytest_workflow.py --fail-on-problems --verbose --coverage-threshold 50
The wrapper runs pytest with coverage and then applies two additional checks:
Coverage threshold — the run fails if total coverage falls below the given percentage (50% in CI; the script’s own default is 75%).
Untested-file heuristic — source files above
--line-count-thresholdlines (default 150) with no corresponding test file are reported as problems.
With --fail-on-problems set, either condition fails the job. Run the same command locally
before opening a pull request if you want to see exactly what CI will say.
Writing tests¶
Use the configurations under
examples/benchmark/rather than inventing new YAML where possible — they exercise realistic geometries.Reconstructor construction for the full KAPA configuration is not fast; prefer the single-channel or REVOLT configurations for tests that only need a reconstructor.
Test validation failures explicitly, since raising the right exception type is part of the parameter classes’ contract:
import pytest def test_negative_diameter_rejected(config): config["lgs_wfs_parameters"]["D"] = -1 with pytest.raises(ValueError): lgsWfsParameters(config, asterism_params)
GPU code paths cannot run on the CI runner. Guard any CuPy-dependent test with
pytest.importorskip("cupy").Importing a module gives you the module. No module shares a name with a class it defines, so ordinary imports and
unittest.mockstring targets both behave normally:from pyTomoAO import reconstructor with patch.object(reconstructor, "atmosphereParameters"): ...
Before 2.0 the package re-exported
tomographicReconstructorandfittingunder their own modules’ names, so those dotted paths resolved to the classes. Tests had to reach forimportlib.import_module, and string patch targets silently failed on Python 3.9 and 3.10. Both modules were renamed (reconstructor,dm_fitting); the workarounds are gone.
Note
Coverage depends on which backend the machine selects. tomographyUtilsCPU and
tomographyUtilsGPU are alternative implementations chosen at import, so whichever one is
not loaded reports 0%: the CI runner has no GPU and covers the CPU kernels, while a
CUDA-equipped workstation covers the GPU kernels instead. Construct a reconstructor with
force_cpu=True if you want to exercise the CPU path on a GPU machine.