Skip to content

Commit ce7e700

Browse files
test: drop :experimental + :robustness tag machinery, run sweeps always
After the deterministic+sweep rework no testitems use :experimental anymore, and the sweeps add ~5 seconds in aggregate on top of a ~10-minute matrix — not worth a parallel filter framework. Drops both INCLUDE_EXPERIMENTAL and INCLUDE_ROBUSTNESS env vars and the ti.tags closure. The robustness sweeps now run on every PR (along with their deterministic baselines). Keeps a `/benchmark/` path filter following Piccolissimo.jl's convention — the benchmark subtree has its own Project.toml + deps + workflow, so its @testItems shouldn't be discovered by `@run_package_tests`. This also preemptively unbreaks PR #93's CI (which fails today because benchmark/convergence/convergence.jl is being picked up). Verified locally on Julia 1.12: 355 pass / 1 broken / 0 fail. TimeConsistency sweep: pass_rate = 1.0 (20/20) NonlinearKnotPoint sweep: pass_rate = 0.8 (16/20, threshold 0.65) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e0bcd3b commit ce7e700

4 files changed

Lines changed: 25 additions & 59 deletions

File tree

README.md

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -105,42 +105,32 @@ solve!(prob; max_iter=100)
105105

106106
## Testing
107107

108-
To run the standard test suite (what CI gates every PR on):
109108
```bash
110109
julia --project=. test/runtests.jl
111110
```
112111

113-
`@testitem`s are filtered by tag. Two opt-in tags expand coverage:
112+
`runtests.jl` runs every `@testitem` in `src/`, `ext/`, and `test/`. Tests in
113+
`benchmark/` are skipped — that subdirectory ships its own `Project.toml`
114+
(extra deps like `HarmoniqsBenchmarks`) and has a dedicated workflow.
114115

115-
| Env var | Tag | What it adds |
116-
| --------------------------- | --------------- | ----------------------------------------------------------------------------------------------------------- |
117-
| `INCLUDE_EXPERIMENTAL=1` | `:experimental` | Known-flaky tests held out of PR CI. Useful for local diagnosis while a fix is being worked out. |
118-
| `INCLUDE_ROBUSTNESS=1` | `:robustness` | Multi-seed sweeps (K=20) that assert a minimum fraction of seeds pass within tolerance. Per-test threshold chosen with buffer above the observed baseline pass rate — typically 0.80 for clean tests, lower for inherently noisy ones (e.g. finite-difference comparisons). |
116+
### Stochastic / numerical primitives — two-layer testing
119117

120-
```bash
121-
INCLUDE_EXPERIMENTAL=1 julia --project=. test/runtests.jl
122-
INCLUDE_ROBUSTNESS=1 julia --project=. test/runtests.jl
123-
```
118+
A single seeded `MersenneTwister` is reproducible on one Julia version but
119+
small downstream numerics can drift across the CI matrix (1.10 / 1.11 / 1.12).
120+
For tests that touch non-deterministic surfaces (solver convergence from
121+
random init, finite-difference derivative comparisons) we pair each test:
122+
123+
1. **Deterministic baseline**: a single seeded trajectory + multiplier.
124+
A failure is a real regression on a specific (Julia version, seed) pair.
125+
2. **Robustness sweep**: K=20 independent seeds; passes if a fraction of
126+
seeds (per-test threshold, chosen with buffer above the observed baseline
127+
rate — typically 0.80, lower for inherently noisy checks like norm-based
128+
finite-diff) land within tolerance. Detects regressions that drop the
129+
true pass rate well below the threshold with very high probability
130+
(binomial), while staying insensitive to lucky/unlucky single draws.
124131

125-
### Testing philosophy for stochastic / numerical primitives
126-
127-
A single `Random.seed!(0)` covers reproducibility on one Julia version but
128-
can drift across the CI matrix (1.10 / 1.11 / 1.12). For tests that touch
129-
non-deterministic surfaces — solver convergence with random initial
130-
conditions, finite-difference derivative comparisons — we use a two-layer
131-
approach:
132-
133-
1. **Deterministic baseline** (untagged, runs every PR): a single seeded
134-
trajectory + multiplier. A failure here means a real regression on the
135-
specific (Julia version, seed) pair.
136-
2. **Robustness sweep** (`:robustness`, opt-in / nightly): K independent
137-
seeds; pass if ≥80% land within the tolerance. A K=20 sweep detects
138-
regressions that drop the true pass rate below ~80% with very high
139-
probability (binomial), while staying robust against random unlucky
140-
draws on the deterministic baseline.
141-
142-
When writing a new flaky test, prefer adding both rather than tagging
143-
`:experimental` indefinitely.
132+
The sweeps are cheap enough (a handful of seconds in aggregate) to run on
133+
every PR.
144134

145135
## Contributing
146136

src/constraints/linear/time_consistency_constraint.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ end
130130
@test abs(t[1]) < 1e-8
131131
end
132132

133-
@testitem "TimeConsistencyConstraint free-time robustness sweep" tags=[:robustness] begin
133+
@testitem "TimeConsistencyConstraint free-time robustness sweep" begin
134134
include("../../../test/test_utils.jl")
135135
using NamedTrajectories
136136
using Random

src/constraints/nonlinear/knot_point_constraint.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ end
340340
test_constraint(NLC2, traj; atol = 1e-3, rng = MersenneTwister(1))
341341
end
342342

343-
@testitem "NonlinearKnotPointConstraint vector syntax robustness sweep" tags=[:robustness] begin
343+
@testitem "NonlinearKnotPointConstraint vector syntax robustness sweep" begin
344344
using DirectTrajOpt: CommonInterface
345345
using Random
346346

test/runtests.jl

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,7 @@ using TestItemRunner
33

44
include("test_snippets.jl")
55

6-
# Tag taxonomy controlling which @testitems run in CI.
7-
#
8-
# Defaults (untagged):
9-
# Always run. Fast, deterministic, must pass on every PR.
10-
#
11-
# :experimental
12-
# Known-flaky or environment-sensitive. Excluded by default.
13-
# Opt in with INCLUDE_EXPERIMENTAL=1 for local diagnosis.
14-
# Goal: eventually rewrite as deterministic + :robustness pair, then drop the tag.
15-
#
16-
# :robustness
17-
# Multi-seed sweeps that assert ≥80% of seeds pass within tolerance.
18-
# Excluded by default because they re-solve a problem many times.
19-
# Opt in with INCLUDE_ROBUSTNESS=1 (e.g. nightly / scheduled workflows).
20-
# A regression that drops the true pass rate below ~80% will fail this gate
21-
# with very high probability (binomial, K=20).
22-
#
23-
# The filter is a single closure so it stays trivial to absorb into a more
24-
# sophisticated upstream filter (e.g. upfront test-item discovery) later.
25-
const INCLUDE_EXPERIMENTAL = haskey(ENV, "INCLUDE_EXPERIMENTAL")
26-
const INCLUDE_ROBUSTNESS = haskey(ENV, "INCLUDE_ROBUSTNESS")
27-
28-
@run_package_tests filter =
29-
ti -> begin
30-
tags = get(ti, :tags, Symbol[])
31-
(INCLUDE_EXPERIMENTAL || !(:experimental in tags)) &&
32-
(INCLUDE_ROBUSTNESS || !(:robustness in tags))
33-
end
6+
# Exclude `benchmark/` testitems from the main test run — they live in a
7+
# subproject with its own Project.toml (different deps, e.g. HarmoniqsBenchmarks)
8+
# and are exercised by a dedicated workflow.
9+
@run_package_tests filter = ti -> !occursin("/benchmark/", ti.filename)

0 commit comments

Comments
 (0)