Tests and markers#

The suite is deliberately tiered: a pull request should not wait on numerics that take hours, and an expensive accuracy check should not be silently skipped just because it is expensive. Validation Matrix records what each tier currently covers; this page is how to run them.

Tiers#

Tier

Command

Runs in

Smoke and regression

uv run pytest -q

Pull-request CI and main

Long-run

uv run pytest --longrun

Developer or dedicated runs

Full accuracy

uv run pytest -m full_accuracy --full-accuracy

CI Full (weekly and on demand), on a CPU

There is no benchmark tier any more: the drivers that produced timing, cache and parameter-sweep evidence are no longer in the tree, and what a measurement of this library has to record is Benchmarks and reproducibility.

The commands above assume UV_PROJECT_ENVIRONMENT points at the conda environment, as in Installation; without it uv run uses the project’s own .venv, which has no OpenCL runtime.

uv run pytest -q                     # default suite
uv run pytest --longrun              # include long-running checks
uv run pytest --full-accuracy        # include the high-cost accuracy markers
uv run pytest -m 'not slow'          # skip the wall-clock hogs

Targeted checks worth running after any environment change:

uv run pytest -q test/test_import.py
uv run pytest -q test/test_public_surface.py
uv run pytest -q test/test_duffy_tanh_sinh.py

Markers and options#

test/conftest.py owns everything the whole suite relies on, and registers its markers in pytest_configure so they work regardless of which configuration file pytest picks up.

full_accuracy

High-cost derivative and direct-reference accuracy tests, and the volume FMM convergence, PDE-residual and split-versus-nonsplit regressions. Skipped unless --full-accuracy is passed, so a plain pytest run neither pays for them nor pretends to have run them.

slow

Labels, but does not skip, the handful of tests that dominate the wall clock. -m 'not slow' gives a quick run.

--longrun

Enables the larger table-generation and quadrature cases, through the longrun fixture.

The ctx_factory fixture is re-exported from pyopencl.tools and parametrizes OpenCL-using tests over the available platforms, so set PYOPENCL_TEST (see Device selection) to pin which one. conftest.py also carries the xfail policy for OpenCL platforms known to crash, a session-scoped table_2d_order1 near-field table shared by every test that needs it, and end-of-session cleanup of stray table caches.

The device of the tests that build their own context#

Some tests do not take the ctx_factory fixture. The volume FMM regressions, the full-accuracy sweeps, the direct references of test_windowed_rke.py, the manufactured batched Duffy checks and the batched FMMLib stage tests need double precision whatever platform the fixture was pinned to, and the near-field tables built by the session-scoped table_2d_order1 and by test_nearfield_potential_table.py want one queue, not one per platform. They get their device from test/_opencl_test_utils.py, which reads PYOPENCL_CTX, not PYOPENCL_TEST:

  • With PYOPENCL_CTX set, they run on exactly the device it selects, CPU or GPU. The fp64 tests skip only if that device lacks fp64. A selector that matches no device, or several such as portable:0,1, is an error, not a skip.

  • Without it, the fp64 tests take the first fp64 GPU, and the first fp64 CPU when there is none. This default never picks the Intel(R) OpenCL platform, which conftest.py marks as crashing on these paths; select it explicitly to run there anyway. The table builds keep the rule they always had: the first device of the first platform other than that one, and the first device of that one only when no other platform has a device.

So set both variables to the same device, as Device selection does, and the whole suite runs where you pointed it.

Until 2026-09 the eleven manufactured checks of test_duffy_batched_manufactured.py skipped on any device but a GPU, so they ran in no CI job. On a CPU they take seconds, so they run in the default suite rather than the full-accuracy tier.

A CPU runs the whole full-accuracy tier, but in a different cost class from a GPU. On the PoCL CPU of a CI Full runner it took 90 minutes, 57 of them in the two 3D split-versus-nonsplit tests of test_volume_fmm.py. VOLUMENTIAL_FULL_ACCURACY_REDUCED=1 runs those two at multipole order 16 instead of 24 and without their source-derivative pair, and CI Full sets it. In two runs at that size they took 21 and 14 minutes and the tier 55 and 38. Each of the three runs had a runner with a different CPU; the 55-minute one ran the rest of the tier about as fast as the 90-minute one. The docstring of _split_3d_full_accuracy_size says why neither change loosens the comparison. Leave the variable unset for the full size, on a GPU or whenever a change touches the 3D split.

VOLUMENTIAL_FULL_ACCURACY_REDUCED=1 uv run pytest -m full_accuracy --full-accuracy

Configuration#

The pytest configuration is [tool.pytest.ini_options] in pyproject.toml, and it is the only one. Do not add a pytest.ini: pytest reads that file first, and whenever both exist it ignores the pyproject.toml table (the run header says so, as WARNING: ignoring pytest config in pyproject.toml!).

Examples as tests#

Four examples run in a reduced configuration on a pull request targeting main — laplace2d.py, laplace2d_adaptive.py, helmholtz2d.py and helmholtz3d.py. On a stacked pull request nothing runs at all; see CI and the review bots.

VOLUMENTIAL_EXAMPLE_SMOKE=1 python examples/laplace2d.py

Smoke mode drops the quadrature order, level count and multipole order and uses a separate table cache file, so it finishes in seconds. An example that stops working in smoke mode is a broken example.

The other maintained examples — laplace3d.py, poisson3d.py and branched_flow_helmholtz2d.py — run at full settings in CI Full, which has no pull_request trigger. Nothing gates them on a pull request, so run the one you touched yourself.

Those three smoke examples are now the whole of the Examples (Smoke) job. It used to run five benchmark drivers’ smoke modes beside them; the drivers left the tree, and the job’s benchmark lines left with them.