Skip to content

Deadline and time budget: #[Property(timeoutMs:, budgetMs:)]#17

Merged
rasuvaeff merged 2 commits into
masterfrom
feat/2.6.0-deadline
Jul 18, 2026
Merged

Deadline and time budget: #[Property(timeoutMs:, budgetMs:)]#17
rasuvaeff merged 2 commits into
masterfrom
feat/2.6.0-deadline

Conversation

@rasuvaeff

Copy link
Copy Markdown
Owner

Deadline + time budget (roadmap 2.6.0, «Идея 4»): pathological inputs (catastrophic regex, deep recursion, unbounded backoff, slow stateful sequences) show up as time, not as assertion failures. Two opt-in wall-clock caps turn them into reported failures.

What changed

#[Property(runs: 200, timeoutMs: 100, budgetMs: 5_000)]

timeoutMs — deadline for a single run (random or explicit example). A body that takes longer fails the property with a new @api DeadlineExceededException (fields: propertyName, arguments incl. draw#N, elapsedMs, timeoutMs):

Property "holds" run exceeded its 100 ms deadline (took 812.4 ms) for x="a{28}b". The input is
pathological for the code under test, or the deadline is too tight.

budgetMs — budget for the whole random phase. Running out before runs successful checks complete fails with a new @api TimeBudgetExceededException (budgetMs, elapsedMs, successfulRuns, requiredRuns) — a slow property cannot silently check less than it claims.

Semantics (deliberate)

  • Post-run measurement (hrtime around the body): a body that never returns cannot be interrupted in synchronous PHP — this catches bounded-but-slow inputs, not true hangs (cf. the time-limiter non-viability analysis).
  • Assertion failure wins over a deadline overrun in the same run — the falsified counterexample is the actionable signal.
  • Deadline failures are NOT shrunk: shrink acceptance would re-measure wall time, and timing noise makes that descent non-deterministic. The offending input is reported as-is.
  • Examples phase honours timeoutMs too; positional example arguments are reported under their parameter names.
  • Both default to null (disabled); values must be >= 1 ms (attribute validation).
  • Comparisons run on integer nanoseconds (hrtime domain); ms floats are computed only for reporting.

Verification

  • composer build: green — 645 tests / 270407 assertions
  • composer rector (dry-run): clean
  • make mutation: MSI 90.5% ≥ minMsi 90 (exit 0); +6 escaped vs previous run are timing-boundary equivalents (> vs >= on ns values — a 1 ns exact tie is unreachable deterministically)
  • Timing tests use usleep deltas (5/20 ms) far above scheduler noise, with wide assertion bands (>= 19 ms, < 60 s) that also kill unit-conversion mutants
  • README / llms.txt / CHANGELOG (## 2.6.0 — Unreleased) updated

Closes the 2.6.0 feature set: generics (#15) + shrink diagnostics (#16) + deadline (this PR). After merge, 2.6.0 release = CHANGELOG date + tag on post-merge HEAD.

- timeoutMs: wall-clock deadline for a single run (random or example);
  overrun fails with DeadlineExceededException naming the pathological
  input (reported unshrunk - timed shrinking is non-deterministic).
  Measured post-run; an assertion failure in the same run wins.
- budgetMs: wall-clock budget for the whole random phase; running out
  before the requested checks complete fails with
  TimeBudgetExceededException exposing completed/required counts.
@rasuvaeff
rasuvaeff merged commit 53c9c49 into master Jul 18, 2026
7 checks passed
@rasuvaeff
rasuvaeff deleted the feat/2.6.0-deadline branch July 18, 2026 12:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant