Skip to content

Stateful / model-based testing (Wave 4, 2.2.0)#8

Merged
rasuvaeff merged 2 commits into
masterfrom
feat/2.2.0-stateful
Jul 7, 2026
Merged

Stateful / model-based testing (Wave 4, 2.2.0)#8
rasuvaeff merged 2 commits into
masterfrom
feat/2.2.0-stateful

Conversation

@rasuvaeff

Copy link
Copy Markdown
Owner

Wave 4 — stateful / model-based testing (2.2.0)

Tests sequences of operations against a simplified model and shrinks the
failing sequence to the shortest one that still breaks — the single biggest gap
vs. non-PHP property-testing libraries.

Public API (new Rasuvaeff\PropertyTesting\StateMachine namespace)

  • Command extends \StringablepreCondition($model), nextState($model),
    run($model, $system), postCondition($model, $result), __toString().
  • Gen::commands($initialModel, $commandGenerators, $minLength = 0, $maxLength = 100)
    CommandSequenceArbitrary, generating valid-by-construction sequences.
  • StateMachine::check(CommandSequence $sequence, Closure $systemFactory) — call
    from the property body; drives the sequence against a fresh system per run.
  • CommandSequence (\Stringable value) and PostconditionViolation.

Design

Reuses the existing #[Property] machinery: the command sequence is a normal
arbitrary passed as one argument, so seed reproducibility, maxShrinks,
PROPERTY_RUNS/SEED/VERBOSE and reporting all come for free. Validity is
enforced at run time, not shrink time — the runner skips any command whose
precondition a dropped/simplified step invalidated (fast-check model), so
shrinking stays a cheap pure list operation. Shrinking removes command blocks
(down to a single command, isolating a failing middle step) then simplifies each
command's parameters through its own tree.

Cross-cutting changes

  • Counterexamples and verbose logs render \Stringable arguments via
    __toString() instead of the class name.
  • A shrunk property reports the minimised run's failure, so the Failure:
    line matches the Shrunk: arguments.

Verification

  • composer build green — 394 tests.
  • bc-check vs v2.1.0: no backwards-incompatible changes (additive only).
  • Mutation MSI 97% (minMsi 90); 4 accepted equivalents (attempt-budget loop
    bound; CommandSequence::__toString empty-return / array_map unwrap).
  • bin/package-audit: 0 errors, 0 warnings; all 4 examples run.

Roadmap

Post-2.1.0 waves now ship as two minors instead of three: 2.2.0 = Wave 4
(this PR); 2.3.0 = Waves 5 + 6 merged. See ROADMAP.md.

rasuvaeff added 2 commits July 7, 2026 20:57
New Rasuvaeff\PropertyTesting\StateMachine namespace: the Command interface
(preCondition/nextState/run/postCondition + Stringable label), the
CommandSequence value, StateMachine::check() runner, and PostconditionViolation.
Gen::commands() returns CommandSequenceArbitrary, which generates valid-by-
construction command sequences and shrinks them by dropping command blocks
(down to a single step, isolating a failing middle command) and simplifying
each command's parameters; the runner skips any command whose precondition a
dropped step invalidated.

Also: counterexamples and verbose logs render Stringable arguments via
__toString(), and a shrunk property reports the minimised run's failure rather
than the original draw's.

Docs: README/llms.txt sections, CHANGELOG 2.2.0, examples/state_machine.php,
package AGENTS invariants, and ROADMAP updated to the 2-release plan
(2.2.0 = Wave 4; 2.3.0 = Waves 5+6 merged).
@rasuvaeff rasuvaeff merged commit eeee370 into master Jul 7, 2026
7 checks passed
@rasuvaeff rasuvaeff deleted the feat/2.2.0-stateful branch July 7, 2026 18:12
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