This commit is contained in:
nuno maduro
2026-08-07 01:55:34 +01:00
parent 37bdf60a1c
commit 1f79660add
18 changed files with 6 additions and 316 deletions
-9
View File
@@ -8,11 +8,6 @@ afterEach(function (): void {
Project::destroyAll();
});
/*
* Invariant 5 — writes land on the branch that ran and only there — and
* invariant 6 — nothing is unbounded — under every branch shape git allows.
*/
test('a branch name git allows is a branch key TIA can hold', function (string $branch): void {
$project = Project::make('master');
$project->seed('master');
@@ -164,8 +159,6 @@ test('a project below the git repository root refuses to run and writes nothing'
$project = Project::make('master');
$nested = $project->nested();
// git addresses paths from the repository root while the graph is
// project-relative, so the two have to coincide. TIA says so and stops.
$result = $project->pestIn($nested, '--tia', ...$arguments);
expect($result->exitCode)->toBe(1, $result->describe())
@@ -180,8 +173,6 @@ test('a repository with no commits says so, and leaves plain runs alone', functi
$project->git()->run(['checkout', '--quiet', '-b', 'master']);
$project->git()->addOrigin();
// Every git call TIA makes asks about HEAD, which does not exist yet. That
// used to surface as `requires "git"`, with git installed and working.
$tia = $project->pest('--tia');
expect($tia->exitCode)->toBe(1, $tia->describe())
-17
View File
@@ -8,19 +8,6 @@ afterEach(function (): void {
Project::destroyAll();
});
/*
* An active coverage report owns the coverage driver, so TIA cannot open a
* session of its own and has to piggyback on PHPUnit's — which is scoped to
* `phpunit.xml`'s <source>, not to the whole project. Edges recorded that way
* are missing every source file outside that scope, and a change to one of them
* would select nothing and replay a pass. Invariant 3 at its most dangerous.
*
* The rows below assert what a coverage run may and may not leave behind. They
* are deliberately silent about exit codes and result counts: `--coverage`
* itself fails on an interpreter with no driver, so only the graph's fate is
* the same everywhere.
*/
test('a coverage report does not found a dependency graph', function (array $arguments): void {
$project = Project::make('master');
@@ -41,16 +28,12 @@ test('a plain run after a coverage run records the whole project scope', functio
$graph = $project->graph();
// Nothing to assert without a driver: there is no graph either way, and the
// point of the row is that the *plain* run is the one that founds it.
if ($graph === null) {
expect($project->graphExists())->toBeFalse();
return;
}
// Self-edges included — they are the first thing a coverage-scoped
// recording drops, since test files are not in <source>.
expect(array_keys($graph['edges']))->toEqualCanonicalizing(array_keys(Project::EDGES))
->and($graph['files'])->toContain('tests/Unit/CalculatorTest.php')
->and($graph['files'])->toContain('app/Calculator.php');
@@ -75,9 +75,6 @@ test('filtered mode falls back to a full replay when a cached failure cannot be
$project->seed('master', failing: ['adds two numbers']);
// A path this project cannot address at all — recorded on another machine.
// A path that merely no longer exists is a *deleted* test, not a lost one,
// and widening the run would not find it either; see StateReclamation.
$project->mutateGraph(function (array $graph): array {
$testId = Project::testId('tests/Unit/CalculatorTest.php', 'adds two numbers');
-5
View File
@@ -8,11 +8,6 @@ afterEach(function (): void {
Project::destroyAll();
});
/*
* Invariant 7 — a hostile state dir cannot break a run. Whatever is in
* graph.json, the suite still runs and exits on the tests' merit.
*/
test('a graph mangled beyond use still lets the suite run', function (string $contents): void {
$project = Project::make('master');
$project->seed('master');
-7
View File
@@ -8,13 +8,6 @@ afterEach(function (): void {
Project::destroyAll();
});
/*
* A test that triggers a notice, deprecation or warning is reported by PHPUnit
* as passed, and emits Passed. Recording it as a plain success made the cache
* hide the issue: a later run under --fail-on-* came back green where a fresh
* run failed. Invariant 3 — replay is faithful — at its most dangerous.
*/
function tiaTriggering(string $call): string
{
return <<<PHP
-13
View File
@@ -9,14 +9,6 @@ afterEach(function (): void {
});
/**
* The remote-baseline path is the only one where a graph arrives from another
* machine, so every one of these rows is really a hostile-input row: whatever
* the artifact carries, the suite still has to run and exit on its own merit.
*
* `Project::gh()` installs a stand-in for the GitHub CLI, so none of this
* touches the network. The graph it serves is a real seeded one, taken out of
* the state dir with `detachGraph()` so the run has to fetch it back.
*
* @param callable(array<string, mixed>): array<string, mixed>|null $mutator
* @return array{0: Project, 1: array<string, string>}
*/
@@ -52,8 +44,6 @@ test('a fetched baseline that will not decode is discarded rather than trusted',
$result = $project->pestWithEnvironment($project->path(), $environment, '--tia', '--baselined');
// Nothing may be replayed out of it. Whether the run then records a graph
// of its own depends on the coverage driver, so that is not asserted here.
expect($result->exitCode)->toBe(0, $result->describe())
->and($result->output)->toContain('The dependency graph could not be read')
->and($result->tally())->toContain(Project::TOTAL_TESTS.' passed')
@@ -120,9 +110,6 @@ test('a network failure warns and lets the suite run', function (string $mode):
test('no published baseline yet starts a cooldown, and a corrupt cooldown does not break the run', function (): void {
[$project, $environment] = tiaPublishedBaseline('no-runs');
// On a machine with a coverage driver each run below records a graph of its
// own, and a run that has a graph never reaches the fetch at all. Take it
// away between runs, so what is under test is the cooldown and nothing else.
$discardGraph = function () use ($project): void {
if ($project->graphExists()) {
$project->detachGraph();
-15
View File
@@ -8,13 +8,6 @@ afterEach(function (): void {
Project::destroyAll();
});
/**
* The selection paths below are all driven from a *seeded* graph rather than a
* recorded one: Blade and Inertia edges are recorded through Laravel hooks the
* fixture project does not have, and a changed `.php` source file would trip
* the driverless full-suite fallback. Views and JS files are neither, so what
* `Graph::affected()` does with them is measurable on any interpreter.
*/
function tiaSeedWithView(Project $project, string $view): void
{
$project->seed('master');
@@ -51,8 +44,6 @@ test('a committed rename selects the tests that depended on the old path', funct
tiaSeedWithView($project, 'resources/views/greeting.blade.php');
// git reports only the destination of a rename unless asked not to, so the
// path the graph holds an edge for is the one that must still show up.
$project->git()->run(['mv', 'resources/views/greeting.blade.php', 'resources/views/hello.blade.php']);
$project->git()->commit('move the view');
$project->snapshot();
@@ -70,10 +61,6 @@ test('an affected test file that is gone does not strand a filtered run', functi
$project->git()->commit('add view');
$project->seed('master');
// A graph written before this checkout existed — a fetched baseline, or a
// branch that deleted the file — can hold an edge for a test file nothing
// can run. Selecting it would filter the suite down to nothing and report
// success on a change no test looked at.
$project->mutateGraph(function (array $graph): array {
$id = count($graph['files']);
$graph['files'][$id] = 'resources/views/page.blade.php';
@@ -174,8 +161,6 @@ test('a changed partial selects the test that rendered its ancestor', function (
'resources/views/page.blade.php' => "<x-card>hi</x-card>\n",
'resources/views/components/card.blade.php' => "<div>one</div>\n",
], 'resources/views/components/card.blade.php'],
// Two partials that include each other: the ancestor walk has to notice it
// has seen them and stop, rather than chase the cycle forever.
'include cycle' => [[
'resources/views/page.blade.php' => "@include('partials.a')\n",
'resources/views/partials/a.blade.php' => "@include('partials.b')\n",
-17
View File
@@ -129,8 +129,6 @@ test('the fallback still reaches a branch that has never run a test file', funct
$project->git()->switchTo('feature-x', new: true);
// A narrowed run mints the branch key holding only the Greeter entries, so
// the layering must still serve master's cached failure for the Calculator.
$project->pest('--filter=greets a person');
$result = $project->pest('--tia', '--filtered');
@@ -212,12 +210,6 @@ test('a malformed baseline entry cannot break the run', function (array $argumen
->and($result->tally())->toContain(Project::TOTAL_TESTS.' passed');
})->with(Project::SEQUENTIAL_AND_PARALLEL)->skipOnWindows();
/*
* Invariant 1 — sequential and parallel must agree — under a process that is
* torn down in the middle of a test file. A worker that flushed what it got to
* before dying has not seen enough of that file to license pruning the
* siblings it never reached.
*/
test('a run torn down mid-file does not prune the tests it never reached', function (array $arguments): void {
$project = Project::make('master');
$project->seed('master');
@@ -281,10 +273,6 @@ test('a fatal error mid-file is a test error, not a truncation', function (array
->and($delta->structureMoved())->toBeFalse($delta->summary());
})->with(Project::SEQUENTIAL_AND_PARALLEL)->skipOnWindows();
/*
* Invariant 2 — every command lands in exactly one tier and stays inside it —
* for the combinations that were never exercised.
*/
test('a green complete run leaves the graph exactly as it found it', function (array $arguments): void {
$project = Project::make('master');
$project->seed('master');
@@ -351,16 +339,11 @@ test('a graph whose recorded commit is gone is re-anchored, not warned about for
$recordedSha = $project->graph()['baselines']['master']['sha'];
// A rebase, a force-push, a reset: the commit the baseline was recorded at
// is no longer an ancestor of HEAD, so nothing can be diffed against it.
$project->git()->run(['reset', '--quiet', '--hard', 'HEAD~1']);
$project->snapshot();
$first = $project->pest('--tia', ...$arguments);
// The whole suite runs, and its results are the truth at HEAD — so the
// recorded revision has to move, whether or not a coverage driver was
// around to refresh the edges. Without that, the run below repeats forever.
expect($first->exitCode)->toBe(0, $first->describe())
->and($first->output)->toContain('no longer reachable')
->and($first->tally())->toContain(Project::TOTAL_TESTS.' passed')