Test suite layout#
The tests/ tree mirrors ams/ source layout: tests for code
under ams/<layer>/ live under tests/<layer>/. Failures point
unambiguously at the layer they exercise, and contributors can
locate the right home for a new test from the source path alone.
Tree#
tests/
├── conftest.py # shared deepcopy fixtures + solver flags
├── system/ # ams/system.py — addressing, repr, paths,
│ # warnings, parameter correction
├── io/ # ams/io/ + result-export — load, case
│ # formats, csv/json export, report
├── core/ # ams/core/ — non-CVXPY machinery:
│ # model, routine_ns, matprocessor, service
├── models/ # ams/models/ — group registry
├── opt/ # ams/opt/ — CVXPY wrapper layer (omodel)
├── routines/ # ams/routines/ — Routine base + per-family
│ # parametrized scenarios
├── interop/ # ams/interface.py — ANDES bridge
├── cli/ # ams/cli.py — argv parsing, version, dispatch
├── bench/ # ams/bench/ — benchmark suite smoke
└── codegen/ # docs/codegen + first-run pycode
Folder scope#
tests/system/System-level concerns that don't fit a finer layer: addressing,
__repr__, search paths, warnings,System.setup()parameter correction.tests/io/Case loading (matpower, psse, json, xlsx), result export (CSV/JSON), and report generation. The CLI's
ams reportinvokes the report machinery, but the report logic itself lives here — the CLI test only verifies the trigger (seetests/cli/below).tests/core/Non-CVXPY machinery from
ams/core/:Model,RoutineNS,MatProcessor(PTDF/LODF/OTDF),Service. These are infrastructure beneath the opt layer — they don't touch CVXPY.tests/models/Device-model wiring: group registry, parameter inheritance.
tests/opt/The CVXPY wrapper layer in
ams/opt/—OModel,Constraint,Expression,Objective. Tests that exercise CVXPY symbol assembly.tests/routines/Routine subclasses and the parametrized scenario suites that cover the common scenario battery (init / trip_gen / trip_line / set_load / vBus / dc2ac) across routine families. See Parametrized scenario modules.
tests/interop/The AMS↔ANDES bridge (
ams.interface).Jumperdevice pass-through is folded in here because the assertion is "the bridge carries the device", not a Jumper-internal property.tests/cli/The CLI entry point and the
ams.mainglue it dispatches to: argv parsing,--version, the preamble, and themain.doc/main.versioninfo/main.misc/main.selftest/main.run(profile=True)paths. Subject- matter tests for what those paths invoke belong in the dedicated layer (e.g.ams reportcontent →tests/io/test_report.py;ams benchnumbers →tests/bench/test_bench.py). The split is thin glue here, deep behaviour there.tests/bench/Smoke tests for
ams/bench/— the benchmark suite invocation, not the reported numbers.tests/codegen/Symbolic codegen and first-run pycode generation (
test_generator.py), plusdoc_all()-style introspection that touches every model and group (test_first_run_docs.py).
Parametrized scenario modules#
Routine variants that share a common scenario body live in
parametrized modules under tests/routines/. Adding a new
routine variant is a one-line _ROUTINES entry — no new class
needed.
Module |
Family |
Routines |
|---|---|---|
|
A — single-period DC |
DCOPF, DCOPF2, RTED, RTEDDG, RTEDES, RTED2, RTED2DG, RTED2ES |
|
C — power flow |
DCPF, PFlow, DCPF1, PFlow1, DCOPF1, ACOPF1 |
|
D — multiperiod LP |
ED, EDDG, EDES, ED2, ED2DG, ED2ES |
|
E — multiperiod MILP |
UC, UCDG, UCES, UC2, UC2DG, UC2ES |
|
MATPOWER cross-check |
DCPF, PFlow, DCOPF, Matrices on case14/39/118 |
Routine-specific extras (test_align_*, test_pb_formula,
test_ch_decision, test_export_csv) stay in their original
test_rtn_<name>.py files. Family B (ACOPF) and OPF are not
parametrized — ACOPF is a single routine; OPF has dual DC/AC modes
that don't fit a single body.
Adding a test#
Find the source layer. A test for
ams/core/<x>.pygoes undertests/core/. A test for a new routine variant goes into the matchingtests/routines/test_scenarios_*.pyas a one-line_ROUTINESentry plus any expected-values overrides.Pick a fixture. If the case you need is already in
conftest.py, use it. If you need a new case file used in more than one place, add it toconftest.pyvia_make_fresh_fixture.Run the local subset first.
pytest tests/<layer>/is fast — use it while iterating.pytest tests/runs the full suite (~58 s) before opening a PR.
CLI selectors#
After the layer reorganization (PR #254), tests previously addressed
as tests/test_<name>.py::Test<Class> now live under
tests/<layer>/test_<name>.py. Pytest still discovers them
automatically; explicit selectors must be updated to include the
subfolder. The full suite count is unchanged (348 collected).