MERA.jl Testing Framework
This page is the authoritative reference for the MERA.jl test suite: how to run it, how it is structured, which datasets it uses, and how coverage is measured and published.
Why local testing with heavy data
MERA.jl reads and analyses real RAMSES adaptive-mesh-refinement (AMR) simulation output. Meaningful tests therefore need production-scale data:
- Data scale — realistic RAMSES outputs contain millions of AMR cells across 10+ refinement levels.
- Physical validity — results must obey conservation/decomposition relations (mass, momentum, energy) and reproduce textbook formulas.
- Reader correctness — the binary RAMSES readers have format-specific branches (e.g. legacy vs. family/tag particle formats) that only exercise against actual simulation files.
These datasets are far too large to ship to GitHub Actions runners, so the suite is split into a CI-friendly smoke subset and a full local run.
Running the tests
The suite supports three modes, all driven by environment variables read in test/test_config.jl and test/runtests.jl:
# 1. Smoke run — data-independent only (this is what CI runs):
MERA_SMOKE_ONLY=1 julia --project -e 'using Pkg; Pkg.test("Mera")'
# 2. Full local run — requires RAMSES test data mounted:
julia --project -e 'using Pkg; Pkg.test("Mera")'
# 3. Full local run + coverage + Codecov upload (maintainer):
UPLOAD=1 ./scripts/run_local_coverage.shTwo additional environment variables help during development:
| Variable | Effect |
|---|---|
MERA_TEST_DATA | Override the simulation-data directory. Defaults to /Volumes/FASTStorage/Simulations/Mera-Tests. |
MERA_SMOKE_ONLY=1 | Run only the data-independent tiers (Aqua, unit system, type system). |
MERA_FOCUS=a.jl,b.jl | Run only the listed test files, in isolation — useful for spot-checking one file or for mutation testing. |
# Example: run two files in isolation
MERA_FOCUS=07_regions.jl,21_untested_surfaces_tests.jl \
julia --project -e 'using Pkg; Pkg.test("Mera")'When the simulation directory is absent (or MERA_SMOKE_ONLY=1 is set), the data-dependent tiers are skipped cleanly, so Pkg.test("Mera") always succeeds — CI and contributors without data still get a valid reduced run.
Full local run: roughly 5 minutes on an Apple-silicon laptop.
GitHub Actions workflows
Four workflows live in .github/workflows/. Only the first runs tests; the others handle docs, dependency maintenance, and releases.
| Workflow | Trigger | Purpose |
|---|---|---|
CI.yml | push / PR to master | Runs the smoke subset (MERA_SMOKE_ONLY=1) on a matrix of Julia 1.10, 1.11, and 1.12, across Ubuntu and macOS, plus a documentation build job. This is the only workflow that runs tests — the full data-backed suite cannot run here because the RAMSES datasets are too large to ship to CI runners. |
documentation.yml | push / PR / tags | Builds the Documenter site (docs/make.jl) and deploys it to the gh-pages branch served at https://manuelbehrendt.github.io/Mera.jl. |
CompatHelper.yml | daily cron | Opens PRs to bump [compat] bounds in Project.toml when dependencies publish new versions. |
TagBot.yml | Julia registry comment | Creates the GitHub release and git tag automatically once a new version is registered in the Julia General registry. |
Julia version coverage
The CI.yml matrix pins 1.10 (the LTS, and the package's declared minimum in Project.toml), 1.11, and 1.12 (the current stable). setup-julia resolves each minor-version string to its newest patch release at run time, so the suite always runs against the latest 1.10.x, 1.11.x, and 1.12.x.
Pre-releases (e.g. 1.13, which at time of writing is only a release candidate) are intentionally not included — they are moving targets and upstream rc bugs would produce spurious failures. When 1.13.0 ships as stable, add a '1.13' row (and optionally drop an older series). To get early warning of breakage on an upcoming release, add a non-blocking 'pre' (or 'nightly') matrix entry; fail-fast: false is already set so such a row can fail without failing the whole build.
Test suite structure
test/runtests.jl executes the files below in tiered order. Tier 1 is data-independent and always runs; the rest run only when simulation data is available and MERA_SMOKE_ONLY is not set.
| Group | File | Focus | Data |
|---|---|---|---|
| Quality & Fundamentals | 01_aqua_quality.jl | Aqua.jl quality checks (ambiguities, unbound args, stale deps, piracy) | No |
| Quality & Fundamentals | 02_unit_system.jl | Physical constants, unit scales, CODATA validation | No |
| Quality & Fundamentals | 22_types_tests.jl | Type constructors, getproperty aliases, JLD2 conversion methods | No |
| Quality & Fundamentals | 30_doc_codeblocks.jl | Syntax-lints every julia code block in docs/src (runs on the 1.10/1.11/1.12 CI matrix) | No |
| Core Functionality | 03_data_readers.jl | getinfo/gethydro/getparticles/getgravity; legacy + new particle formats | Yes |
| Core Functionality | 04_basic_calculations.jl | msum, center_of_mass/com, bulk_velocity variants | Yes |
| Core Functionality | 05_derived_variables.jl | Temperature, sound speed, Mach, Jeans length/mass, free-fall time | Yes |
| Analysis Functions | 06_projections.jl | Hydro/particle projections; mode/pxsize/data_center options; ground-truth + conservation matrix | Yes |
| Analysis Functions | 07_regions.jl | subregion/shellregion with conservation and ID-tag preservation | Yes |
| Scientific Validation | 08_physics_and_contracts.jl | Reference values, per-cell getvar formulas, unit-kwarg dispatch contracts | Yes |
| Scientific Validation | 09_determinism.jl | Projection repeated-call equality (parallel-table-build guard) | Yes |
| I/O and Integration | 10_io_export.jl | savedata/loaddata round-trip, MERA I/O | Yes |
| I/O and Integration | 11_error_handling.jl | Edge cases, invalid inputs, error paths, latent-gap pattern | Partial |
| I/O and Integration | 12_integration_workflows.jl | End-to-end cross-step pipelines | Yes |
| Utilities & Notifications | 13_additional_coverage.jl | viewfields, wstat, overview functions, global-state setters | Yes |
| Utilities & Notifications | 14_io_notifications.jl | Zulip/email notification stack, system info helpers | Yes |
| Clumps | 20_clump_tests.jl | getclumps, clump getvar, clump subregion/shellregion | Yes |
| Untested API Surfaces | 21_untested_surfaces_tests.jl | Gravity/particle getvar variants; non-hydro region selection | Yes |
| VTK Export | 19_vtk_export_tests.jl | VTK file export and validation | Yes |
| Filter Macros | 25_filter_macro_tests.jl | @filter macro on hydro/particle data | Yes |
| I/O Configuration | 26_io_config_tests.jl | I/O configuration helpers (server-side tuning recommendations) | Yes |
| Data Conversion | 27_data_conversion_tests.jl | convertdata, batch_convert_mera, round-trip vs. RAMSES | Yes |
| Extended Coverage | 28_coverage_boost_tests.jl | Additional helper / overview function coverage | Yes |
| Parallel Execution | 29_parallel_execution_tests.jl | Parallel vs. serial equivalence (julia -t 4) | Yes |
Support files:
| File | Purpose |
|---|---|
test_config.jl | SIMULATION_PATH, DATASETS dict, tolerances, CODATA constants, mode flags |
test_utilities.jl | Helpers: load_test_info, load_test_hydro, region-extent validators, etc. |
run_coverage.jl | Alternative entry point that runs every existing test file |
Test datasets
All datasets live under SIMULATION_PATH (test_config.jl), overridable via MERA_TEST_DATA.
| Key | Directory | Output | Hydro | Gravity | Particles | Clumps | Primary use |
|---|---|---|---|---|---|---|---|
:spiral_clumps | spiral_clumps | 100 | x | x | x | Primary dataset for most tests | |
:spiral_ugrid | spiral_ugrid | 1 | x | x | x | Particle tests, uniform-grid tests | |
:mw_L10 | mw_L10 | 300 | Multi-CPU info reading | ||||
:manu_sf | manu_sim_sf_L14 | 400 | x | x | Clumps + legacy (pversion 0) particles | ||
:mlike | mlike | 500 | x | Gravity-only readers | |||
:manu_stable | manu_stable_2019 | 1 | x | x | Particle data readers |
Any RAMSES output can be substituted by editing SIMULATION_PATH and the DATASETS dictionary in test_config.jl.
Coverage workflow
Because the data lives only on the maintainer's machine, coverage is measured locally and uploaded to Codecov — CI does not measure it.
scripts/run_local_coverage.sh:
- Wipes stale
*.covandcoverage.lcovfiles. - Runs
Pkg.test("Mera"; coverage=true). - Aggregates
src/**/*.covintocoverage.lcovviascripts/process_coverage.jl(which excludessrc/dev/,src/benchmarks/, andsrc/visualization/— non-library code). - When
UPLOAD=1and aCODECOV_TOKENis available, uploads to Codecov.
The token can be stored in ~/.config/mera/codecov.env (mode 600) instead of being exported manually.
Notebook / tutorial tests (real-world workflows)
The Jupyter tutorial notebooks in the documentation (Mera-Docs/version_1) double as an end-to-end, real-world workflow test tier. Where the unit suite exercises functions in isolation, the notebooks run the full analysis pipelines a user would actually follow — load info → read hydro/particles/gravity/clumps → select regions → derive variables → project → mask/filter → save/load Mera files → export VTK — against the real RAMSES datasets. Two distinct kinds of value:
- Executable regression tests. Every notebook is run headless with
jupyter nbconvert --executeand scanned for error cells. A failing cell is a real, user-facing breakage. This catches integration-level bugs the unit suite can miss — e.g. the Julia 1.12dataoverviewcrash (Core.TypeName.mtremoved) was surfaced by the notebooks while the unit suite had it marked@test_broken. - Visual verification for authors/reviewers. The executed notebooks preserve their outputs — tables, projection plots, VTK previews — so a maintainer can see that results look physically correct, not merely that no exception was thrown. This is the human-in-the-loop check that pure assertions cannot provide.
Running them
The notebooks use a dedicated Julia 1.12 kernel bound to the docs environment, which devs Mera from this repo (so they test the working tree, not a released version). Create the kernels once via IJulia.installkernel from the notebooks project:
using IJulia
NB = "/path/to/Mera-Docs/version_1"
# plain execution kernel (4 threads)
installkernel("Mera-Docs 1.12 (4t)", "--project=$NB",
env=Dict("JULIA_NUM_THREADS" => "4"))
# coverage kernel — tracks only this repo's source
installkernel("Mera-Docs 1.12 cov", "--project=$NB",
"--code-coverage=@/path/to/Mera.jl",
env=Dict("JULIA_NUM_THREADS" => "4"))Executed copies are written to version_1/executed/ (gitignored); the originals are left untouched.
Wired into coverage
scripts/run_coverage_with_notebooks.sh produces a combined coverage report from both the unit suite and the notebooks:
- Wipes stale
*.cov. - Runs
Pkg.test("Mera"; coverage=true)→*.covnext tosrc/. - Executes every tutorial notebook with the coverage kernel (
--code-coverage=@<repo>), which appends more*.covto the samesrc/files — so notebook execution counts toward library coverage. - Aggregates everything via
scripts/process_coverage.jland (withUPLOAD=1) uploads to Codecov under thelocal-full-notebooksflag.
Because both phases write *.cov next to the source, the tutorials raise coverage of paths the unit suite under-exercises (display/viewfields, dataoverview, projection and VTK variants). The coverage kernel's @<repo> path restriction keeps *.cov confined to Mera.jl/src — none leak into the notebooks repo (which also gitignores *.cov as a safety net).
What the tests validate
The suite is designed for meaningful coverage, not line-hit padding:
- Physics formulas are re-derived from primitive variables in CGS and compared to Mera's output (sound speed, temperature, Jeans length/mass, free-fall time).
- Region selections validate the extent of the returned cells, not just the count — every
subregion/shellregiontest checks that selected cells actually lie inside the requested geometry. - I/O round-trips compare loaded data cell-by-cell against the original RAMSES read, including info metadata and scale factors.
- Error paths are exercised with
@test_throwsfor invalid inputs. - Partition invariants —
subregionplus itsinversemust reconstruct the parent dataset exactly.
The suite has been spot-checked with manual mutation testing: deliberately breaking a physics factor, a reader branch, or a region bound each causes the corresponding test to fail — confirming the assertions bite.
For JOSS reviewers
- CI verification —
MERA_SMOKE_ONLY=1 julia --project -e 'using Pkg; Pkg.test("Mera")'passes without simulation data. Aqua.jl checks code quality (no ambiguities, no unbound type parameters, no stale deps, no type piracy). - Full verification — with RAMSES data mounted, the full suite runs in ~5 minutes and exercises data I/O, derived quantities, projections, regions, conservation relations, parallel safety, clump analysis, VTK export, and save/load round-trips.
- Coverage — measured locally and published to Codecov (badge above). Uncovered code is concentrated in rarely-used backends (multi-CPU parallel readers, sink-particle paths) and interactive display methods.
- Reproducibility — point
MERA_TEST_DATAat any RAMSES output and edit theDATASETSdictionary intest_config.jl. - Real-world workflows — the tutorial notebooks are executed end-to-end and their rendered outputs (tables, projection plots, VTK previews) can be inspected directly, demonstrating that complete user pipelines work and produce physically sensible results (see Notebook / tutorial tests).
Testing philosophy
MERA.jl's testing ensures both software reliability and scientific validity: traditional software-testing practices (unit, integration, error-path, quality-assurance) combined with the validation needs of astrophysical simulation analysis (conservation relations, AMR boundary handling, coordinate transforms, numerical precision). The two-mode design keeps CI fast and deterministic while the local run delivers full, physically meaningful validation.