Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 15 Aug 03:45
· 57 commits to master since this release
9b3a0fc
  • Added EdgeCases, the explicit switch for the numeric boundary bias:
    PropertyConfig(edgeCases: EdgeCases::None) generates uniformly instead of
    returning an in-range edge value roughly one draw in five. The bias is right
    until the edges are what a property cannot use — a body discarding 0, a
    range end that violates a precondition — where it costs one run in five and
    the discard budget pays. The roll that chooses an edge is still consumed
    under None, so the two modes stay aligned on the same seed: switching
    changes which values are edges rather than every draw after the first, which
    is what keeps a suite comparable to itself. jqwik's FIRST is deliberately
    absent — explicit examples and the corpus already run before the random
    phase, with values chosen rather than guessed.
  • Added Gen::forClass(): a generator built from what a constructor already
    declares. Per parameter, in order — an override, the @param psalm type, the
    native type. The docblock wins because it says more: int and int<0, 100>
    are the same native type and a very different value space, and reading the
    narrower one is what keeps a validating constructor from rejecting four
    generated values in five. The supported subset is bounded and documented
    (int<a, b>, positive-int and friends, non-empty-string, list<T>,
    array<K, V>, literal unions such as 'draft'|'published', ?T, unions,
    enums, DateTimeImmutable, and other classes followed to maxDepth with
    cycles refused by name); anything outside it throws naming the parameter
    rather than widening to a guess, because a guessed generator fails later, in
    somebody else's test. A constructor that rejects a value throws by default —
    that is information — and skipInvalid: true discards and redraws through
    the same Gen::filter() machinery, discarding exceptions only, never
    Errors.
  • Added RedisCorpus: the regression corpus in Redis instead of a directory, so
    a falsification found on a laptop replays in CI and one found in CI replays on
    the next laptop. The stored document is byte-identical to the filesystem
    backend's, so moving a corpus between the two is a copy rather than a
    migration — asserted, not assumed. It takes a two-method client seam
    (Runner\Redis\CorpusClient), shipped over ext-redis
    (PhpRedisCorpusClient) and predis (PredisCorpusClient); a consumer with a
    pool or a namespaced wrapper can supply its own. Writes are optimistic — read,
    compare-and-set through one Lua script, retry — and give up quietly after
    RedisCorpus::MAX_ATTEMPTS, because a corpus is memory rather than a ledger
    and failing a passing run to record a counterexample is the wrong trade.
  • Documented the corpus as a CI artifact: the three GitHub Actions steps that
    carry a corpus across runs, and why each exists. The combined cache action
    declares post-if: success(), so it never saves on the red job that just
    recorded the counterexample; run_attempt has to be in the key or a re-run
    writes nothing; restore-keys is what actually carries the corpus forward.
    Plus the two patterns beyond a cache — a committed fixture, and a store
    shared between CI and developers — and what must not be committed or shared.