Automated tests¶
The pipeline uses the PipelineTester framework (tests/testing_utils.py) for both
component and regression tests.
Component tests validate individual pipeline tasks or small task sequences in isolation. They focus on specific functionality and run faster than full regression tests.
Located in
tests/component/Execute specific tasks with controlled inputs
Test edge cases without running the full pipeline recipe
Regression tests validate complete pipeline recipes or PPR (Pipeline Processing Request) executions end-to-end, comparing comprehensive output metrics against versioned reference results.
Located in
tests/regression/fast/andtests/regression/slow/Run complete recipes using PPR files or recipe XML files
Slow tests (marked
slow) require the--longtestsflag and are not actively maintained due to computing resource constraints
Tests are organized under tests/:
tests/
├── component/
│ └── component_test.py
├── regression/
│ ├── fast/
│ │ ├── alma_if_fast_test.py
│ │ ├── alma_sd_fast_test.py
│ │ ├── vla_fast_test.py
│ │ ├── vlass_fast_test.py
│ │ └── nobeyama_sd_fast_test.py
│ └── slow/
│ ├── alma_if_slow_test.py
│ ├── alma_sd_slow_test.py
│ ├── vla_slow_test.py
│ └── vlass_slow_test.py
├── testing_utils.py # PipelineTester framework
└── test_pipeline_testing_framework.py
Running tests¶
Before running locally, configure CASA to find your test data in ~/.casa/config.py
(see Test environment setup for the full casa-data setup):
datapath = ['/path/to/pipeline-testdata']
Using pixi tasks¶
The recommended way to run tests. Pixi tasks set the necessary environment variables
(OMP_NUM_THREADS=1, OPENBLAS_NUM_THREADS=1) and paths automatically:
# unit tests with coverage report
pixi run test-unit
# fast regression tests (xdist n=12, non-mpi tests only, with coverage)
pixi run test-regression
# single small ALMA-IF fast regression test (quick smoke test)
pixi run test-pltest1
By default pixi tasks run from the project root. To control where CASA logs and output
land, invoke pytest directly with pixi run python:
cd /my/workdir
pixi run python -m pytest -n 12 --dist worksteal \
-m "not mpi" <pipeline_dir>/tests/regression/fast/
From outside the source tree, use --manifest-path:
pixi run --manifest-path /path/to/pipeline/pyproject.toml test-regression
Component tests¶
PYTHONNOUSERSITE=1 ${casa_dir}/bin/python3 -m pytest -vv --junitxml=component-results.xml \
-n 4 <pipeline_dir>/tests/component/.
Alternatively via CASA’s shell:
xvfb-run -d ${casa_dir}/bin/casa --nogui --nologger --agg -c \
"import pytest; pytest.main(['-vv', '--junitxml=component-results.xml', '-n', '4', \
'<pipeline_dir>/tests/component'])"
Regression tests¶
Run all fast tests (slow tests excluded by default):
PYTHONNOUSERSITE=1 ${casa_dir}/bin/python3 -m pytest -vv --junitxml=regression-results.xml \
<pipeline_dir>/tests/regression/fast/.
Include slow tests:
PYTHONNOUSERSITE=1 ${casa_dir}/bin/python3 -m pytest -vv --longtests \
--junitxml=regression-results.xml <pipeline_dir>/tests/regression/.
Warning
Slow tests (marked slow, enabled by --longtests) are not actively maintained due to
computing resource constraints. They may have stale expected results or fail on current
pipeline versions. Use them with caution and verify results independently.
Select by node ID:
xvfb-run -d ${casa_dir}/bin/casa --nogui --nologger --agg -c \
"import pytest; pytest.main(['-vv', '--junitxml=regression-results.xml', \
'<pipeline_dir>/tests/regression/fast/alma_if_fast_test.py::test_uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small__PPR__regression'])"
Select by marker or name substring:
# all tests with 'hifa' in their name (keyword match)
xvfb-run -d ${casa_dir}/bin/casa --nogui --nologger --agg -c \
"import pytest; pytest.main(['-vv', '--junitxml=regression-results.xml', \
'-k', 'hifa', '<pipeline_dir>/tests/regression'])"
# tests with a specific dataset name
xvfb-run -d ${casa_dir}/bin/casa --nogui --nologger --agg -c \
"import pytest; pytest.main(['-vv', '--junitxml=regression-results.xml', \
'-k', 'mg2_20170525142607_180419', '<pipeline_dir>/tests/regression'])"
Note
Choosing -n for xdist. Benchmark runs of the full fast regression suite (18 tests,
24 CPUs, --dist worksteal):
Workers ( |
MaxRSS |
Elapsed |
|---|---|---|
6 |
157 GB |
14:48 h |
12 |
161 GB |
08:33 h |
18 |
163 GB |
08:24 h |
Recommended: -n 12. Going from 6 to 12 cuts elapsed time by ~42% with negligible
extra memory. Beyond 12, gains are less than 2% — the worksteal scheduler already
balances load effectively at that point.
Note
Per-test timing reference (xdist n=12, fast regression suite, 2026-04-22):
Test (abbreviated) |
Subsystem |
Elapsed |
|---|---|---|
|
alma_if |
7:48 h |
|
alma_if |
5:58 h |
|
alma_sd |
3:30 h |
|
vla |
3:06 h |
|
alma_if |
2:42 h |
|
nobeyama_sd |
1:48 h |
|
alma_if |
1:41 h |
|
alma_if |
1:09 h |
|
alma_if |
0:55 h |
|
vla |
0:44 h |
|
alma_if |
0:43 h |
|
vla |
0:43 h |
|
nobeyama_sd |
0:31 h |
|
alma_if |
0:17 h |
|
alma_sd |
0:16 h |
|
alma_if |
0:11 h |
|
vlass |
0:05 h |
|
vla |
0:02 h |
Coverage¶
For a single regression test, using CASA’s Python directly (skips the casashell layer):
PYTHONNOUSERSITE=1 xvfb-run -d ${casa_dir}/bin/python3 -m pytest -v --pyclean \
--cov=pipeline --cov-report=html \
${pipeline_dir}/tests/regression/fast/alma_if_fast_test.py::test_uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small__PPR__regression
The coverage report is saved to htmlcov/index.html. To merge .coverage.* data files
from parallel or separate runs into a single report:
# merge all .coverage* files from subdirectories
coverage combine --keep $(find ./* -name ".coverage*")
# generate the final HTML report
coverage html
Running mpi tests¶
The mpi marker tags tests that are recommended for an MPI-enabled CASA session.
There is no auto-skip logic — control execution explicitly with -m expressions.
Serial session: exclude mpi tests (recommended default for regular runs):
PYTHONNOUSERSITE=1 ${casa_dir}/bin/python3 -m pytest -m "not mpi" \
tests/regression/fast/
mpicasa session: run mpi-marked tests (5 processes: 1 client + 4 workers):
PYTHONNOUSERSITE=1 xvfb-run -d ${casa_dir}/bin/mpicasa \
-display-allocation -display-map --report-bindings -oversubscribe -n 5 \
${casa_dir}/bin/casa --cachedir ./rcdir --configfile ./rcdir/config.py \
--startupfile ./rcdir/startup.py --nologger --log2term --nogui --agg -c \
"import pytest; pytest.main(['-m', 'mpi', '--junitxml=./regression.xml', \
'<pipeline_dir>/tests/regression/fast/'])"
Combine with other markers:
# ALMA fast tests, mpi only, under mpicasa
mpicasa -n 5 casa --nogui --log2term \
-c "import pytest; pytest.main(['-m', 'alma and mpi', 'tests/'])"
# All fast tests, skip mpi, serial session
PYTHONNOUSERSITE=1 ${casa_dir}/bin/python3 -m pytest \
-m "fast and not mpi" tests/regression/fast/
Quick reference:
Session |
Goal |
|
|---|---|---|
serial |
skip mpi tests |
|
serial |
force-run mpi tests (expect failures) |
|
mpicasa |
mpi tests only |
|
mpicasa |
non-mpi tests only |
|
Warning
Do not combine mpicasa with pytest -n N (xdist). They are incompatible:
xdist spawns independent subprocesses via Python’s
multiprocessing. These are not MPI ranks — inside each worker,MPIEnvironment.is_mpi_enabledisFalse, so MPI code paths silently fall back to serial behaviour.MPI collective operations (barriers, broadcasts, reduces) require all ranks to participate together. With xdist running independent tests concurrently, ranks will be in different pipeline tasks at different times and will deadlock.
Tool |
Parallelism unit |
Use for |
|---|---|---|
|
MPI ranks within one pipeline run |
parallelize work inside a single test |
|
independent test worker processes |
run multiple tests concurrently |
Safe patterns:
# MPI-parallelized pipeline, one test at a time (correct)
mpicasa -n 8 casa --nogui --log2term \
-c "import pytest; pytest.main(['-m', 'mpi', 'tests/regression/fast/'])"
# Multiple serial tests in parallel (correct)
pytest -n 12 -m "not mpi" tests/regression/fast/
# DO NOT DO THIS — deadlock / wrong results
# mpicasa -n 8 casa -c "import pytest; pytest.main(['-n', '4', 'tests/regression/fast/'])"
Compare-only mode¶
Re-evaluate results against reference values without re-running the pipeline. Useful for tweaking tolerances, updating expected results files, or debugging comparison logic:
xvfb-run casa --nogui --nologger --log2term --agg -c \
"import pytest; pytest.main(['-vv', '-m alma and fast', '--compare-only', '<pipeline_dir>'])"
Adding new tests¶
Test inputs¶
Writing a new test requires the following inputs in the pipeline-testdata repository. Review the README before adding new data.
Input SDM/MS¶
Use the smallest, fastest dataset available. Check whether the dataset already exists in
pl-unittest/ or elsewhere in the repository before adding a new one. To add a new dataset,
follow the testdata repository README instructions.
Expected output¶
Expected output follows the format generated by infrastructure/renderer/regression.py,
with ::: and an optional relative tolerance appended to each line:
s15.hifa_gfluxscaleflag.uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small.num_rows_flagged.after=268048:::
s16.hifa_gfluxscale.uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small.field_0.spw_0.I=2.2542400978094923:::1e-6
If no tolerance is supplied, the default tolerance in PipelineTester.run() applies
(currently 1e-7). To add new values to check, add to or modify a class in
infrastructure/renderer/regression.py (e.g. FluxcalflagRegressionExtractor).
PPR (optional)¶
If a PPR is supplied, the framework uses executeppr (ALMA) or executevlappr (VLA). Without
a PPR, tests use recipereducer with a recipe XML file.
Versioned results files¶
Results files carry the CASA and Pipeline versions in their names:
<dataset_name>.casa-<CASA_version>-pipeline-<Pipeline_version>.results.txt
For example:
uid___A002_Xc46ab2_X15ae.casa-6.5.1-15-pipeline-2023.1.0.8.results.txt
uid___A002_Xc46ab2_X15ae.casa-6.6.0-21-pipeline-2024.1.0.12.results.txt
When expectedoutput_dir is used instead of a specific expectedoutput_file, the framework
scans for *.results.txt files, parses their versions, and automatically selects the file
with versions closest to (but not exceeding) the current running versions. Store all inputs
under pl-regressiontest/:
pl-regressiontest/
└── uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small/
├── PPR.xml
├── uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small.casa-6.1.1-15-pipeline-2020.1.0.40.results.txt
└── uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small.casa-6.1.3-2-pipeline-2021.1.1.6.results.txt
Writing component tests¶
Add new functions to tests/component/component_test.py using mode='component'. The
tasks parameter is a list of (task_name, kwargs) tuples:
@pytest.mark.importdata
@pytest.mark.selfcal
def test_dataset__task_sequence__component():
"""Run test of specific task sequence.
Dataset(s): dataset_name.ms
Task(s): hifa_importdata, hif_selfcal
"""
data_dir = 'pl-unittest'
visname = 'dataset_name.ms'
tasks = [
('hifa_importdata', {'vis': casa_tools.utils.resolve(os.path.join(data_dir, visname)),
'datacolumns': {'data': 'regcal_contline'}}),
('hif_selfcal', {}),
('hif_selfcal', {'restore_only': True}),
]
pt = PipelineTester(
visname=[visname],
mode='component',
tasks=tasks,
output_dir='test_output_dir',
expectedoutput_dir='pl-componenttest/test_name',
)
pt.run()
Writing regression tests¶
Add new functions to tests/regression/fast/ or tests/regression/slow/. The mode
parameter defaults to 'regression'.
Using a PPR:
@pytest.mark.seven
def test_uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small__PPR__regression():
"""Run ALMA cal+image regression on a small test dataset with a PPR file.
PPR: pl-regressiontest/uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small/PPR.xml
Dataset: uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small.ms
"""
ref_directory = 'pl-regressiontest/uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small'
pt = PipelineTester(
visname=['uid___A002_Xc46ab2_X15ae_repSPW_spw16_17_small.ms'],
ppr=f'{ref_directory}/PPR.xml',
input_dir='pl-unittest',
expectedoutput_dir=ref_directory,
)
pt.run()
Using a recipe XML file:
def test_dataset__recipe_name__regression():
"""Run test with recipe XML file.
Recipe: procedure_hifa_image
Dataset: uid___A002_Xef72bb_X9d29
"""
ref_directory = 'pl-regressiontest/uid___A002_Xef72bb_X9d29'
pt = PipelineTester(
visname=['uid___A002_Xef72bb_X9d29'],
recipe='procedure_hifa_image.xml',
input_dir=ref_directory,
expectedoutput_dir=ref_directory,
)
pt.run()
Note
Build success and failure is reported in Bamboo with email notification. Failure does not prevent a pre-release tarball from being published.