Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/docs
diff options
context:
space:
mode:
authorLeitwolf112026-10-02 03:27:05 +0300
committerLeitwolf112026-10-02 03:27:05 +0300
commit5d55a3226360fae1eaa5ef91908b0fa7d7d07234 (patch)
tree61e94c3bd1684b4caa256bc34e380c272b67cdb6 /docs
parentfebb5b1e244174da0985b9994f8d5ed1676950e8 (diff)
downloadcatch3-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.md66
-rw-r--r--docs/why-catch3.md2
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.