You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
verifyAll() renders the call log when an expectation goes unmet. Two
code paths reported the same failure and only one showed the calls: verify() rendered through FailureReport, while the verification sweep
had a sprintf of its own. Since verifyAll() is the path every runner
adapter takes, the alias table and the * argument marks — the part that
says WHICH call differed — were missing from almost every failure anyone
saw. Both paths now render the same report, the failure carries observedCalls, and the sentence the two used to word differently ("but it
was called never") is settled. A test asserting the exact text of an unmet
expectation may need updating. (#77, #79)
Doubling a built-in interface no longer emits a deprecation notice.
PHP's own interfaces carry tentative return types, which ReflectionMethod::getReturnType() reports as null, so Understudy::for(Repo::class, Countable::class) generated count(): mixed
and PHP answered with should either be compatible with Countable::count(): int. The unifier reads the tentative type, which
satisfies the contract exactly and needs no #[\ReturnTypeWillChange]. The
defect was invisible for ArrayAccess and JsonSerializable, whose
tentative type is mixed anyway. (#78, #80)
A documentation site for the whole family, at https://rasuvaeff.github.io/understudy/: the guide, migration guides from
Mockery and PHPUnit, a cookbook of real incidents whose output the build
diffs against the scripts that produce it, and an API reference reflected
out of all five packages' src/ — free functions and analyser rule
identifiers included, neither of which a class-only reference would show. MIGRATION.md at the root is generated from the same pages. (#82)
The README's Mockery table mapped ->atLeast()->once() to times(minimum: 1), which is not what that does.times() branches on
the number of arguments it was given, so one argument is an exact count
however it is spelled; the open range is times(1, null). Both READMEs now
show all four forms and say why. (#81)
The escaped-mutant hunt: 240 → 158, MSI 90.4% → 94.0%, gate raised to 92
(the full trajectory and the reason live next to the number in infection.json5). About sixty targeted tests and fixtures, each written
from an escaped mutant's diff rather than from the line it sits on:
constant defaults now assert their rendered SOURCE (\Cfg::LIMIT, uppercase SELF::/PARENT:: resolved through the declaring hierarchy) where the old
tests compared values a mutant renders identically; static satisfaction
gets its satisfied complements (a type-distinct variadic tail, untyped and
union return contracts); by-reference detection is asserted at every
parameter position; FinalStripper pins the glued-comment and bare final const boundaries; settle() is pinned to drop satisfied claims and
keep every stub; Fiber routing is pinned for by-reference returns, callOriginal() and ordering claims; the structured failure fields
(actualCount, both bounds of unused(), observedCalls filtering, expectedCalls as strings, both halves of allVerified()) are asserted
exactly, as are six refusal messages formerly checked by fragments. Three
more test classes attribute #[Covers] they exercised all along. Three
manual arbitrations confirmed the remainder is dominated by genuine
equivalents — redundant second guards, set-map values read through array_keys(), perf fastpaths, warning-only offsets.
Honest-coverage sweep after 0.4.0. Twelve targeted tests kill the
escaped mutants the new code left behind (hooked-property collection and
rendering, cross-Fiber property routing, forwarding write-through, captor
registration, the specification-hole boundary, retired-scope invisibility),
and three test classes now attribute #[Covers] they exercised all along —
which is what let several of those mutants escape unmapped. A new matcher
property pins "no matcher throws on a hostile argument, every one describes
itself" across the whole Arg:: catalog, rest() and a captor's capture() included.
examples/ caught up with 0.4.0: Arg::rest() and a typed captor in basic-usage.php, delegate() and lean() in modes.php, and a new property-hooks.php (self-skipping on PHP 8.3). The Mockery migration
table gains the makePartial()/withAnyArgs()/Mockery::capture() rows,
and three edge behaviours are now stated: a capture() in an expectSequence() step matches without recording, lean() cannot release
the stable slot behind a & return, and a clone does not carry written
property values over.