Integrated shrinking
generate() возвращает значение + ленивое shrink-дерево вместе — трансформированные генераторы (map, flatMap) shrink'аются корректно бесплатно.
Property-based testing для PHP 8.3+, плагин для Testo.
Property falsified after 246 successful run(s); seed=7382910
Original: maxAttempts=17, baseSeconds=91, cap=847, attempts=23
Shrunk: maxAttempts=1, baseSeconds=848, cap=847, attempts=1 (12 shrink step(s), 41 trial(s))
Changed: maxAttempts=17 -> 1, baseSeconds=91 -> 848, attempts=23 -> 1На вход пошли четыре сгенерированных аргумента; строка Changed: говорит, что на падение реально влияют только три из них — shrinker нашёл это поиском, вам не пришлось идти в дебаггер, чтобы это увидеть.
// Три составляющие по отдельности, без раннера Testo.
$ints = Gen::intBetween(0, 1000);
$failing = null;
for ($run = 0; $run < 100; ++$run) {
$shrinkable = $ints->generate($random);
if ($shrinkable->value % 2 !== 0) {
$failing = $shrinkable;
break;
}
}
// -> shrink к простейшему нечётному int в диапазоне#[Test]
final class ListReversalProperties
{
#[Property(runs: 200)]
public function reversingTwiceRestoresTheList(array $xs): void
{
Assert::same(array_reverse(array_reverse($xs)), $xs);
}
/** @return array<string, ArbitraryInterface> */
public static function reversingTwiceRestoresTheListGenerators(): array
{
return ['xs' => Gen::arrayOf(Gen::intBetween(-100, 100))];
}
}// Сэмплирование генератора напрямую — быстрый способ посмотреть, что он производит.
Gen::sample(Gen::intBetween(1, 6), count: 5, seed: 42);
// [3, 1, 6, 6, 2]
Gen::sampleShrinks(Gen::intBetween(0, 100), seed: 1);
// ['value' => 87, 'shrinks' => [0, 44, 66, 77, 82, 85, 86]]#[Property(runs: 200)]
public function stackBehavesLikeItsModel(CommandSequence $sequence): void
{
StateMachine::check($sequence, static fn(): ExampleStack => new ExampleStack());
}
/** @return array<string, ArbitraryInterface> */
public static function stackBehavesLikeItsModelGenerators(): array
{
return ['sequence' => Gen::commands([], [
Gen::map(Gen::intBetween(0, 99), static fn(int $v) => new Push($v)),
Gen::constant(new Pop()),
])];
}Полные, исполняемые версии всех четырёх — в examples/; что каждый из них показывает — на странице Примеры.