python3 -m pytest tests/ → 42 passed in ~7 s.
| Module | Tests | Focus |
|---|---|---|
test_stats.py |
14 | Wilson CI values, monotonicity, closed-form runs-needed, classifier verdicts. |
test_runner.py |
13 | Subprocess pass/fail counting, timeouts, parallel equivalence, early-stop callback. |
test_cli.py |
15 | flakeguard --help, run, certify, JSON output, success-code override, timeout-as-failure, parallel, missing-binary regression, confidence-label regression. |
All tests use real subprocesses (no mocking). Deterministic commands (exit 0, exit 1) + one probabilistic smoke test (asserts only that the certify pipeline completes without crashing).
$ flakeguard run -n 20 --quiet --target-rate 0.01 -- python3 -c "import sys; sys.exit(0)"
flakeguard run — 20 runs
trials : 20
pass : 20
fail : 0
rate : 0.00%
Wilson 95% CI : [0.00%, 16.11%]
mean run duration : 0.016s
to certify <= 1.00% at 95% confidence: need ~361 additional clean runs.
The "need ~361" matches the closed form ⌈z²·(1-0.01)/0.01⌉ = 381 minus the 20 already done → 361 ✔.
$ flakeguard run -n 200 -j 4 --quiet -- python3 -c "import random,sys; sys.exit(0 if random.random() > 0.1 else 1)"
flakeguard run — 200 runs
trials : 200; pass: 180; fail: 20; rate: 10.00%
Wilson 95% CI : [6.57%, 14.94%]
CI contains the true rate 10 % ✔; tight ±5 pp at n=200.
$ flakeguard certify --max-rate 0.05 --max-runs 200 --quiet -- python3 -c "import sys; sys.exit(0)"
flakeguard certify — [OK] CERTIFIED (max-rate=5.00%, conf=95%)
trials : 73; pass: 73; fail: 0
already certified <= 5.00% at 95% confidence.
exit=0
Early-stop at 73 trials — exactly when the Wilson upper bound first crosses 5 % ✔.
$ flakeguard certify --max-rate 0.01 --max-runs 100 --quiet -- python3 -c "import random,sys; sys.exit(0 if random.random() > 0.2 else 1)"
flakeguard certify — [FAIL] FLAKY (max-rate=1.00%, conf=95%)
trials : 2; pass: 1; fail: 1
exit=1
After 1 fail in 2 trials, Wilson lower (9.45 %) > 1 % → declared FLAKY at exit 1 ✔.
$ flakeguard run -n 3 --quiet -- /no/such/binary
flakeguard run — 3 runs
trials : 3; pass: 0; fail: 3; rate: 100.00%
Wilson 95% CI : [43.85%, 100.00%]
exit=0
Previous (pre-fix) behaviour: uncaught FileNotFoundError, traceback, crash. Now: trial is counted as a failure with reason captured in stderr_tail. Regression test added: test_missing_binary_records_failures_not_crash.
Previously displayed as Wilson 99% CI (int(0.999*100) = 99). Now displayed correctly:
Wilson 99.9% CI : [0.00%, 17.80%]
Regression test: test_confidence_label_keeps_decimals.
$ flakeguard run -n 6 --quiet --json -- python3 -c "import sys; sys.exit(0)"
{
"mode": "run",
"trials": 6,
"passes": 6,
"failures": 0,
"rate": 0.0,
"confidence": 0.95,
"wilson_ci": [0.0, 0.391...],
...
}
Validated as parseable by json.loads (test_run_json_output_parses).
| Scenario | Wall time | Notes |
|---|---|---|
1000 × python -c "sys.exit(0)" -j 8 |
4.5 s | dominated by Python startup |
| 200 × random-exit -j 4 | 1.1 s | |
| 42-test suite | 6.9 s |
No hangs, no leaks observed.
--helpproduces a clear usage block with subcommand examples.- Live progress bar (
[####------] 47/200 pass=45 fail=2) is only emitted when stderr is a TTY; in pipes/CI it stays silent. - Errors print to stderr; results print to stdout, so
flakeguard ... | jqworks cleanly. - Three distinct verdict labels (
CERTIFIED,FLAKY,INCONCLUSIVE) with corresponding exit codes (0, 1, 2). Easy to wire into a CI gate (if flakeguard certify ...; then merge; fi). - The "to certify ≤ X% you need Y more clean runs" line is the killer-feature explanation that no existing tool we found provides.
- Pass/fail is reduced to exit code. Sub-test granularity is the user's responsibility (e.g. run
pytest path::test_namerather than the whole suite). - No causal classification —
flakeguardmeasures flakiness, doesn't diagnose its cause. - Default parallelism = 1; raising
-jrequires the user to ensure test isolation (no shared temp dirs, ports, fixtures). - Wilson is one of several reasonable interval choices; Clopper-Pearson not implemented.
- The lower-bound exit at very small n (e.g. 2 trials, 1 fail) is mathematically correct but may feel aggressive. Future: optional
--min-runsto delay verdict resolution.
--min-runs Nto gate early stops.- Clopper-Pearson interval as an opt-in (
--method clopper-pearson). - Auto-detect appropriate
-jfrom CPU count when test isolation is declared safe. - Pytest plugin: collect per-test exit codes from a single suite invocation instead of N full reruns.
- Persistent history (
~/.flakeguard/history.json) for tracking a command's flake rate over commits.
Math: verified. Edge cases: covered (and 2 bugs caught + fixed during validation). Performance: comfortable. UX: clean, scriptable, CI-friendly.
A competent developer would say: this is solid.
Decision: ship.