diff options
| author | Leitwolf11 | 2026-10-02 03:27:05 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-10-02 03:27:05 +0300 |
| commit | 5d55a3226360fae1eaa5ef91908b0fa7d7d07234 (patch) | |
| tree | 61e94c3bd1684b4caa256bc34e380c272b67cdb6 /docs | |
| parent | febb5b1e244174da0985b9994f8d5ed1676950e8 (diff) | |
| download | catch3-feature/property-combinators.tar.gz | |
Add composite property generators, single-trial replay and stable counterexample printingfeature/property-combinators
Property tests could only generate one integer or Boolean. This adds the
composite strategies the documentation listed as missing, without changing
any Catch2 file or the existing Progmasoft API.
Generators:
- Tuple(generators...) draws components left to right and shrinks one
component at a time, which is how a property receives several arguments.
- Vector(element, minimumSize, maximumSize) and Text(alphabet,
minimumLength, maximumLength) share one ordered shrink: shortest allowed
prefix, the two halves, single removals, then single element
simplifications. A shrunk value never falls below its minimum, each step
offers at most 256 candidates, and a size limit above 1,048,576 is
rejected.
- Element(values) picks from a fixed list and shrinks toward earlier
entries.
Runner:
- PropertyOptions::ReplayTrialSeed runs exactly one trial with a reported
replay seed, including its shrinking, so a failure from a log can be
reproduced without rerunning earlier trials. The report now ends with the
line that names this option and the seed.
- Counterexamples of tuples, pairs and vectors are printed element by
element instead of relying on Catch2's optional string makers, so the
report does not depend on configuration macros or include order.
The headers still compile for clients built without language exceptions.
Thirteen new test cases cover bounds, determinism, shrink order, candidate
limits, minimized counterexamples, replay and invalid configurations, and
every build description (Bazel, CMake, Meson, Xmake, Premake) lists the new
headers and test file.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/progmasoft-catch3.md | 66 | ||||
| -rw-r--r-- | docs/why-catch3.md | 2 |
2 files changed, 62 insertions, 6 deletions
diff --git a/docs/progmasoft-catch3.md b/docs/progmasoft-catch3.md index 43a73f03..55114f32 100644 --- a/docs/progmasoft-catch3.md +++ b/docs/progmasoft-catch3.md @@ -33,7 +33,10 @@ The current property API is split into these headers: bounded sampling, inclusive integer ranges, and per-trial seed derivation. - `<Progmasoft/Catch3/Generator.hpp>`: user-defined generation/shrinking pair. - `<Progmasoft/Catch3/Generators/Integer.hpp>` and `Boolean.hpp`: built-in - strategies with deterministic shrink candidates. + scalar strategies with deterministic shrink candidates. +- `<Progmasoft/Catch3/Generators/Vector.hpp>`, `Tuple.hpp`, `Element.hpp` and + `Text.hpp`: composite strategies described under + [Composite generators](#composite-generators). - `<Progmasoft/Catch3/Property.hpp>`: trial execution and structured results. - `<Progmasoft/Catch3/Assertions.hpp>`: `CATCH3_CHECK_PROPERTY`, which reports the failing trial, replay seed, reason, and minimized counterexample through @@ -53,10 +56,63 @@ minimal value. A false predicate or an exception from the predicate fails the property. Exceptions from generation or shrinking are reported as execution errors, distinct from an ordinary failing property. -The API currently accepts one generated value per property. Tuple strategies, -state-machine commands, parallel trials, and adaptive shrinking are deliberately -not implied by this first module; they need their own deterministic contracts and -tests before being added. +A failing report ends with the line +`replay: set PropertyOptions::ReplayTrialSeed to <seed>`. Setting that option +runs exactly one trial with the given seed, including its shrinking, and +ignores `Seed` and `Trials`; the reported trial number is then 1. This +reproduces a failure from a CI log without rerunning the trials before it. + +```cpp +Progmasoft::Catch3::PropertyOptions{ .ReplayTrialSeed = 1234567890123456789ULL } +``` + +### Composite generators + +A property still receives one generated value. Several arguments are passed +as one tuple: + +```cpp +using namespace Progmasoft::Catch3; + +CATCH3_CHECK_PROPERTY( + Tuple(Vector(Integer<int>(0, 100), 0, 20), Text("abc", 1, 8)), + [](const std::tuple<std::vector<int>, std::string> &input) { + return Accepts(std::get<0>(input), std::get<1>(input)); + }, + PropertyOptions{ .Trials = 500 }); +``` + +- `Tuple(generators...)` draws its components strictly left to right from the + trial's random source and shrinks one component at a time: every candidate + of the first component, then every candidate of the second, and so on. +- `Vector(element, minimumSize, maximumSize)` draws a length in the inclusive + range and then the elements in index order. `Text(alphabet, minimumLength, + maximumLength)` does the same for byte strings over the given alphabet; + `PrintableAscii()` is the default alphabet. Both shrink in a fixed order: + the shortest allowed prefix, the first and second half, the value with one + element removed, and finally the value with one element simplified. A + shrunk value is never shorter than its minimum, each step offers at most + 256 candidates, and a size limit above 1,048,576 elements is rejected. +- `Element(values)` picks uniformly from a fixed non-empty list. The list + order is the simplicity order: a value shrinks to the values listed before + it. `Text` treats its alphabet the same way, so put the simplest value or + character first. + +Counterexamples of tuples, pairs and vectors are printed element by element, +for example `{ 7, { true, false }, "text" }`. This does not depend on Catch2's +optional tuple or range string makers, so the report is the same whatever +configuration macros a client defines. Scalars still use `Catch::StringMaker`, +including user specializations. + +Invalid factory arguments, such as an empty alphabet or a minimum above its +maximum, throw `std::invalid_argument`; a client built without exceptions +gets the message on standard error followed by `std::abort`. + +State-machine commands, parallel trials, mapped or filtered generators, and +adaptive shrinking are deliberately not implied by this module; they need +their own deterministic contracts and tests before being added. Shrinking a +composite value is greedy and local like scalar shrinking: it finds a small +counterexample, not a provably smallest one. ## Snapshots diff --git a/docs/why-catch3.md b/docs/why-catch3.md index 30dd5159..7fbeef44 100644 --- a/docs/why-catch3.md +++ b/docs/why-catch3.md @@ -25,7 +25,7 @@ Catch2 runner. The current property runner handles one generated value per property. Shrinking is deterministic and bounded, but greedy rather than globally minimal. There is -not yet built-in support for state-machine properties, generated tuples, +not yet built-in support for state-machine properties, mapped or filtered generators, discard/classification policies, parallel execution, or a standalone Catch3 runner. Those features are not implied by the initial API. |