From e527f2baaf380811256f3df4de5ba4fa6210ef07 Mon Sep 17 00:00:00 2001 From: "v.razuvaev" Date: Fri, 4 Sep 2026 12:50:24 +0300 Subject: [PATCH] Say what shipped and what the operations do, in the places that were guessing --- AGENTS.md | 8 +++++--- CHANGELOG.md | 14 ++++++++++++++ MIGRATION.md | 2 +- README.md | 8 ++++---- README.ru.md | 8 ++++---- docs/src/guide/lifecycle/forget.md | 2 +- docs/src/guide/lifecycle/index.md | 4 ++-- docs/src/guide/migrating-from-phpunit.md | 2 +- examples/README.md | 6 ++++++ llms.txt | 12 +++++++----- 10 files changed, 45 insertions(+), 21 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index efa6444..60c87e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,8 +10,9 @@ $repo->find(123))->returns($book)`. There are no method-name strings and no service methods on the double itself. Public API lives in `Rasuvaeff\Understudy`: the `Understudy` facade, the free -functions `when()`/`verify()` in `src/functions.php`, `Arg`, `Invocation`, -`Outcome`, `WhenBuilder`, and the exceptions under `Exception\`. Everything +functions `when()`/`expect()`/`expectSequence()`/`verify()` in +`src/functions.php`, `Arg`, `Invocation`, `Outcome`, `WhenBuilder`, and the +exceptions under `Exception\`. Everything under `Codegen\`, `Runtime\`, `Expectation\`, `Defaults\` and `Matcher\` is `@internal`. @@ -438,7 +439,8 @@ seconds instead of the full run's minute — and the full run stays the gate. `docs/` holds the family site — one VitePress site for all five packages, deployed to from `master`. The plan -it was built from is `_plans/UNDERSTUDY-DOCS-SITE-PLAN.md` in the monorepo. +it was built from was retired from the monorepo once the site shipped; what it +decided lives in this section and in `docs/scripts/check-integrity.mjs`. | Rule | Why | |---|---| diff --git a/CHANGELOG.md b/CHANGELOG.md index 1404101..946b0c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## Unreleased + +- **Documentation review fixes.** llms.txt no longer claims `bypassFinals()` + and the runner adapters are «being built next» (both shipped long ago), and + now lists all four free functions and the `: static` loose-default rule. + The Prophecy migration table no longer teaches a non-existent + `expect(…)->never()` — the supported form is `expect(…)->times(0)`. + `idle()` comments across the READMEs, llms.txt and the guide now say what + the operation does: it covers every context of the test, not the current + one. The Performance table's `5.38³` footnote marker was a damaged + character. `examples/README.md` now documents `case-studies/` and the gate + that runs it (`make docs-cookbook`). AGENTS.md's dead link to the retired + site plan replaced with where the decisions live. + ## 0.7.1 — 2026-09-04 - **`bypassFinals()` missed a target whose declaration is written in another diff --git a/MIGRATION.md b/MIGRATION.md index 98d68d4..cb7d1c0 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -278,7 +278,7 @@ PHPUnit 10 dropped Prophecy from the box, so this is a common second hop. | `Argument::any()` / `type()` / `that()` | `Arg::any()` / `Arg::instanceOf()` / `Arg::satisfies()` | | `->shouldBeCalled()` | `expect(…)` | | `->shouldBeCalledTimes(n)` | `expect(…)->times(n)` | -| `->shouldNotBeCalled()` | `expect(…)->never()` | +| `->shouldNotBeCalled()` | `expect(…)->times(0)` | | `->shouldHaveBeenCalled()` | [`verify(…)`](https://rasuvaeff.github.io/understudy/guide/expectations/verify) | | `$prophet->checkPredictions()` | `Understudy::verifyAll()`, or the adapter | diff --git a/README.md b/README.md index 651ca26..fdbcfc0 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ tables and the environment in [perf/README.md](perf/README.md). | stub: build, stub, one call, tear down | 10.6µs | +17% | +76% | **−17%**¹ | | mock: build, expect, call, verify | 12.8µs | +4% | +128% | **−27%**² | | marginal cost of one call to a stub | 0.86µs | 1.61µs | 1.51µs | **0.69µs**¹ | -| added to process start (cold) | **1.00×** | 1.50× | 4.96× | 5.38׳ | +| added to process start (cold) | **1.00×** | 1.50× | 4.96× | 5.38³ | | retained per live double | **467–482 B** | 513 B | ~8.5 KB | ~1.25 KB | ¹ `createStub()` ² `createMock()` ³ a ratio rather than milliseconds: cold start @@ -500,7 +500,7 @@ stopped at. Understudy::checkpoint(); // verify, then forget what is settled $result = Understudy::scope(fn () => ...); // nested context, verified on success echo Understudy::transcript($repository); // every call and its outcome -Understudy::idle(); // true when the context holds no doubles +Understudy::idle(); // true when the test holds no doubles, in any context ``` `transcript()` retains every invocation until `reset()` or `checkpoint()`. @@ -810,7 +810,7 @@ prose. ```php Understudy::reset(); -Understudy::idle(); // true when the current context holds no doubles +Understudy::idle(); // true when the test holds no doubles, in any context ``` The [understudy-testo](https://github.com/rasuvaeff/understudy-testo) and @@ -870,7 +870,7 @@ so `bin/package-audit` runs them as a gate rather than linting them. ## Development ```bash -make build # validate, normalize, require-checker, cs, psalm, test +make build # validate, normalize, require-checker, cs, psalm, unit, integration, examples make cs-fix make psalm make test diff --git a/README.ru.md b/README.ru.md index 0fd7b03..6ebe08d 100644 --- a/README.ru.md +++ b/README.ru.md @@ -80,7 +80,7 @@ when(fn () => $repository->find(123))->returns($book); | stub: создать, застабить, вызвать, снести | 10.6µs | +17% | +76% | **−17%**¹ | | mock: создать, expect, вызвать, verify | 12.8µs | +4% | +128% | **−27%**² | | предельная цена одного вызова к стабу | 0.86µs | 1.61µs | 1.51µs | **0.69µs**¹ | -| добавляет к старту процесса (холодный) | **1.00×** | 1.50× | 4.96× | 5.38׳ | +| добавляет к старту процесса (холодный) | **1.00×** | 1.50× | 4.96× | 5.38³ | | удерживает на каждый живой дубль | **467–482 B** | 513 B | ~8.5 KB | ~1.25 KB | ¹ `createStub()` ² `createMock()` ³ отношение, а не миллисекунды: холодный @@ -501,7 +501,7 @@ The protocol is: Understudy::checkpoint(); // проверить и забыть завершённое $result = Understudy::scope(fn () => ...); // вложенный контекст, проверяемый при успехе echo Understudy::transcript($repository); // все вызовы и их исходы -Understudy::idle(); // true, если в контексте нет дублей +Understudy::idle(); // true, когда тест не держит дублей ни в одном контексте ``` `transcript()` хранит каждый вызов до `reset()` или `checkpoint()`. Не @@ -808,7 +808,7 @@ Configure it first: when(fn () => $double->tag(...))->returns(...) ```php Understudy::reset(); -Understudy::idle(); // true, если в текущем контексте нет ни одного дубля +Understudy::idle(); // true, когда тест не держит дублей ни в одном контексте ``` Адаптеры [understudy-testo](https://github.com/rasuvaeff/understudy-testo) и @@ -869,7 +869,7 @@ Understudy генерирует по классу на набор контрак ## Разработка ```bash -make build # validate, normalize, require-checker, cs, psalm, test +make build # validate, normalize, require-checker, cs, psalm, unit, integration, examples make cs-fix make psalm make test diff --git a/docs/src/guide/lifecycle/forget.md b/docs/src/guide/lifecycle/forget.md index 4325af0..5b2cac8 100644 --- a/docs/src/guide/lifecycle/forget.md +++ b/docs/src/guide/lifecycle/forget.md @@ -34,7 +34,7 @@ One-way, like every other form of forgetting here. ```php Understudy::reset(); -Understudy::idle(); // true when the current context holds no doubles +Understudy::idle(); // true when the test holds no doubles, in any context ``` The [Testo](/adapters/testo) and [PHPUnit](/adapters/phpunit) adapters call diff --git a/docs/src/guide/lifecycle/index.md b/docs/src/guide/lifecycle/index.md index 33ca30b..368843e 100644 --- a/docs/src/guide/lifecycle/index.md +++ b/docs/src/guide/lifecycle/index.md @@ -9,7 +9,7 @@ description: "checkpoint(), scope(), transcript() and the call log — what owns Understudy::checkpoint(); // verify, then forget what is settled $result = Understudy::scope(fn () => ...); // nested context, verified on success echo Understudy::transcript($repository); // every call and its outcome -Understudy::idle(); // true when the context holds no doubles +Understudy::idle(); // true when the test holds no doubles, in any context ``` ## The context @@ -77,7 +77,7 @@ problem, not a size one. ```php Understudy::reset(); -Understudy::idle(); // true when the current context holds no doubles +Understudy::idle(); // true when the test holds no doubles, in any context ``` The [Testo](/adapters/testo) and [PHPUnit](/adapters/phpunit) adapters verify diff --git a/docs/src/guide/migrating-from-phpunit.md b/docs/src/guide/migrating-from-phpunit.md index 4538549..1b2ed58 100644 --- a/docs/src/guide/migrating-from-phpunit.md +++ b/docs/src/guide/migrating-from-phpunit.md @@ -128,7 +128,7 @@ PHPUnit 10 dropped Prophecy from the box, so this is a common second hop. | `Argument::any()` / `type()` / `that()` | `Arg::any()` / `Arg::instanceOf()` / `Arg::satisfies()` | | `->shouldBeCalled()` | `expect(…)` | | `->shouldBeCalledTimes(n)` | `expect(…)->times(n)` | -| `->shouldNotBeCalled()` | `expect(…)->never()` | +| `->shouldNotBeCalled()` | `expect(…)->times(0)` | | `->shouldHaveBeenCalled()` | [`verify(…)`](/guide/expectations/verify) | | `$prophet->checkPredictions()` | `Understudy::verifyAll()`, or the adapter | diff --git a/examples/README.md b/examples/README.md index 08ff451..091e85d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -20,6 +20,12 @@ demonstrate changes. `_check.php` is the shared assertion helper the scripts include; the leading underscore is what marks it an include rather than a script of its own. +`case-studies/` holds the cookbook scenarios: each one reproduces a real +failure message quoted on a [cookbook](docs/src/cookbook) page of the +documentation site. They are gated by `make docs-cookbook`, which diffs their +output against the pages — not by `composer build` — and their `_bootstrap.php` +is the include, same convention as `_check.php`. + There is deliberately no `bypassFinals()` example. It depends on load order and on the source arriving through `file://`, so a script cannot assert its own outcome everywhere; those claims live in the acceptance scenarios diff --git a/llms.txt b/llms.txt index 041fbb3..01f8a59 100644 --- a/llms.txt +++ b/llms.txt @@ -18,7 +18,8 @@ No runtime deps; `ext-mbstring` is not needed. - The closure must contain **exactly one** direct call on a double. Anything else raises `InvalidCallSpecification`. - A double exposes nothing beyond its contract. All operations are static - methods on `Understudy`, or the free functions `when()` / `verify()`. + methods on `Understudy`, or the free functions `when()` / `expect()` / + `expectSequence()` / `verify()`. - Interfaces and classes. A class may be the first target only (PHP has single inheritance); interfaces follow it. The target's constructor and destructor never run, private and static methods are left alone, protected ones are @@ -347,7 +348,7 @@ where the stack trace points at `verifyAll()`. Rules, all of them load-bearing: Understudy::checkpoint(); // verify, then forget what is settled $result = Understudy::scope(fn () => ...); // nested context, verified on success Understudy::transcript($repo); // every call and outcome, for reading -Understudy::idle(); // true when the current context holds no doubles +Understudy::idle(); // true when the test holds no doubles, in any context ``` `scope()` returns whatever the callback returns and drops its context either @@ -408,7 +409,9 @@ Understudy::reset(); // drop the current context Loose (default) answers with a type-safe default: `null` for `void`/`mixed`/ nullable, `false`, `0`, `0.0`, `''`, `[]`, `new stdClass()`, an empty `Generator`, an `EmptyIterator`, a no-op callable. A type with no safe default -raises `NoDefaultValue`; a `never` method raises `NeverMethodCalled`. +raises `NoDefaultValue`; a `never` method raises `NeverMethodCalled`. A method +declared `: static` answers with the double itself; `: self` names the contract +and answers with a nested double. ### Exceptions @@ -465,5 +468,4 @@ change. New kinds and newly-populated fields are additive. ## Not supported Static method mocking, patching at the point of use, deep stubs, duck doubles, -sharing state across processes. `bypassFinals()` and the runner adapters are -being built next. +sharing state across processes.