CommandSequenceArbitrary
Rasuvaeff\PropertyTesting\Arbitrary\CommandSequenceArbitrary
Class — Package: property-testing-core — Source — Version: v1.0.0-1-g88fdee2
Implements: Swarmable, ArbitraryInterface
Generates valid Command sequences for stateful / model-based testing and shrinks them by dropping and simplifying steps.
Generation is model-aware: at each step a command generator is drawn at random, and the produced command is appended only if its StateMachine\Command::preCondition() holds in the running model, which is then advanced via StateMachine\Command::nextState(). The sequence is therefore valid by construction; if no applicable command is found within a bounded number of attempts the sequence stops early.
Shrinking removes whole blocks of commands (most aggressive first, down to a single command so a failing step in the middle can be isolated) and then simplifies individual commands through their own shrink trees. Dropped or simplified sequences are not re-validated here — StateMachine\StateMachine::check() skips any command whose precondition a change invalidated, keeping every candidate sound.
Constructor
__construct(
mixed $initialModel,
array<array-key,\ArbitraryInterface> $commandGenerators,
int $minLength = 0,
int $maxLength = 100,
)| Parameter | Type | Default | Description |
|---|---|---|---|
$initialModel | mixed | required | |
$commandGenerators | array<array-key,\ArbitraryInterface> | required | Each must produce a StateMachine\Command. |
$minLength | int | 0 | |
$maxLength | int | 100 |
Methods
generate()
generate(Random $random): ShrinkableProduce one random value from this arbitrary's space, together with its shrink tree. Candidates must be ordered most aggressive first (typically toward a zero/empty/identity element) and every branch of the tree must be finite, so shrinking terminates.
Documentation inherited from ArbitraryInterface.
variantCount()
variantCount(): intHow many variants this generator chooses among. Must be positive: a choice generator with nothing to choose from is not a choice, and Arbitrary\SwarmArbitrary rejects a source that reports otherwise rather than drawing from an empty alphabet. Deliberately typed int and not int<1, max> — the bound is a promise implementations make, and a swarm still checks it at runtime because it cannot analyse the implementations it will be handed.
Documentation inherited from Swarmable.
withVariants()
withVariants(list<int> $indices): selfThe restricted copy keeps the initial model and both length bounds — a swarm narrows the command alphabet and nothing else. That is also where its sharpest edge is: with a $minLength above zero, a subset from which no applicable command reaches that length throws GenerationExhaustedException exactly as the unrestricted generator does when the model starves it. That is the contract, not an accident — a sequence shorter than its minimum has never been a valid result here, and silently returning one would be the worse answer.
$indices— Variant positions to keep, each in[0, variantCount() - 1].
Throws:
InvalidArgumentException— When an index falls outside the command generators.