mirror of
https://github.com/pestphp/pest.git
synced 2026-07-21 17:10:03 +02:00
fix: tia
This commit is contained in:
+7
-2
@@ -562,7 +562,7 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
|
|||||||
return $exitCode;
|
return $exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->snapshotTestResults();
|
$this->snapshotTestResults(markKnownTestFiles: true);
|
||||||
|
|
||||||
return $exitCode;
|
return $exitCode;
|
||||||
}
|
}
|
||||||
@@ -1394,12 +1394,13 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$graph->markKnownTestFiles(array_keys($touchedFiles));
|
||||||
$graph->pruneStaleResults($this->branch, array_keys($touchedFiles), array_keys($results));
|
$graph->pruneStaleResults($this->branch, array_keys($touchedFiles), array_keys($results));
|
||||||
|
|
||||||
$collector->reset();
|
$collector->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function snapshotTestResults(): void
|
private function snapshotTestResults(bool $markKnownTestFiles = false): void
|
||||||
{
|
{
|
||||||
/** @var ResultCollector $collector */
|
/** @var ResultCollector $collector */
|
||||||
$collector = Container::getInstance()->get(ResultCollector::class);
|
$collector = Container::getInstance()->get(ResultCollector::class);
|
||||||
@@ -1442,6 +1443,10 @@ final class Tia implements AddsOutput, HandlesArguments, Terminable
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($markKnownTestFiles) {
|
||||||
|
$graph->markKnownTestFiles(array_keys($touchedFiles));
|
||||||
|
}
|
||||||
|
|
||||||
$graph->pruneStaleResults($this->branch, array_keys($touchedFiles), array_keys($results));
|
$graph->pruneStaleResults($this->branch, array_keys($touchedFiles), array_keys($results));
|
||||||
|
|
||||||
$this->saveGraph($graph);
|
$this->saveGraph($graph);
|
||||||
|
|||||||
@@ -813,6 +813,38 @@ final class Graph
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark test files that executed under a recorded coverage session as "known",
|
||||||
|
* seeding an empty edge set for any that produced zero project-source edges.
|
||||||
|
*
|
||||||
|
* Without this, a test that covers no application source (e.g. a pure unit
|
||||||
|
* test asserting on language primitives) never becomes an edge key, so
|
||||||
|
* {@see self::knowsTest()} reports it as unknown and it re-runs on every TIA
|
||||||
|
* run. Recording it with an empty edge set lets it be replayed/skipped while
|
||||||
|
* unchanged; it is still re-run whenever its own file changes, via
|
||||||
|
* {@see self::applyTestFileChanges()}.
|
||||||
|
*
|
||||||
|
* Must only be called from the recording path, where coverage was actually
|
||||||
|
* collected — otherwise a missing edge set could mean "coverage was off",
|
||||||
|
* not "genuinely covered nothing".
|
||||||
|
*
|
||||||
|
* @param array<int, string> $testFiles Absolute or project-relative test file paths.
|
||||||
|
*/
|
||||||
|
public function markKnownTestFiles(array $testFiles): void
|
||||||
|
{
|
||||||
|
foreach ($testFiles as $testFile) {
|
||||||
|
$rel = $this->relative($testFile);
|
||||||
|
|
||||||
|
if ($rel === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! isset($this->edges[$rel])) {
|
||||||
|
$this->edges[$rel] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, array<int, string>> $testToTables
|
* @param array<string, array<int, string>> $testToTables
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ final readonly class TestPaths
|
|||||||
$directories[] = $rel;
|
$directories[] = $rel;
|
||||||
}
|
}
|
||||||
|
|
||||||
$suffix = $directory->suffix();
|
$suffixes[] = $directory->suffix();
|
||||||
|
|
||||||
$suffixes[] = str_starts_with($suffix, '.') ? $suffix : '.'.$suffix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($suite->files() as $file) {
|
foreach ($suite->files() as $file) {
|
||||||
@@ -61,7 +59,7 @@ final readonly class TestPaths
|
|||||||
|
|
||||||
if ($suffixes === []) {
|
if ($suffixes === []) {
|
||||||
foreach ($configuration->testSuffixes() as $suffix) {
|
foreach ($configuration->testSuffixes() as $suffix) {
|
||||||
$suffixes[] = str_starts_with($suffix, '.') ? $suffix : '.'.$suffix;
|
$suffixes[] = $suffix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable) {
|
} catch (Throwable) {
|
||||||
|
|||||||
@@ -1821,6 +1821,11 @@
|
|||||||
✓ output format → it returns a 32-character hex xxh128 hash
|
✓ output format → it returns a 32-character hex xxh128 hash
|
||||||
✓ output format → it returns a stable hash for empty content
|
✓ output format → it returns a stable hash for empty content
|
||||||
|
|
||||||
|
PASS Tests\Unit\Plugins\Tia\Graph
|
||||||
|
✓ markKnownTestFiles() → it makes a test file with no edges known
|
||||||
|
✓ markKnownTestFiles() → it does not clobber edges of an already-known test file
|
||||||
|
✓ markKnownTestFiles() → it ignores paths outside the project root
|
||||||
|
|
||||||
PASS Tests\Unit\Plugins\Tia\IsEnabledForRun
|
PASS Tests\Unit\Plugins\Tia\IsEnabledForRun
|
||||||
✓ does not throw when an integer --random-order-seed is passed as a separate argv element
|
✓ does not throw when an integer --random-order-seed is passed as a separate argv element
|
||||||
✓ still detects --tia when an integer argument is present
|
✓ still detects --tia when an integer argument is present
|
||||||
@@ -1836,6 +1841,14 @@
|
|||||||
✓ it detects an added or removed non-platform dependency
|
✓ it detects an added or removed non-platform dependency
|
||||||
✓ it is independent of package ordering
|
✓ it is independent of package ordering
|
||||||
|
|
||||||
|
PASS Tests\Unit\Plugins\Tia\TestPaths
|
||||||
|
✓ isTestFile() → it matches a standard test file under a configured directory
|
||||||
|
✓ isTestFile() → it does not match a non-test file that shares the directory
|
||||||
|
✓ isTestFile() → it does not match files outside the configured directories
|
||||||
|
✓ isTestFile() → it matches an explicitly listed file
|
||||||
|
✓ isTestFile() → it honours a bare .php suffix
|
||||||
|
✓ isTestFile() → it does not require a dot before the suffix
|
||||||
|
|
||||||
PASS Tests\Unit\Plugins\Tia\ViteDepsHelper
|
PASS Tests\Unit\Plugins\Tia\ViteDepsHelper
|
||||||
✓ it strips JSONC down to something JSON.parse accepts with ('plain-object')
|
✓ it strips JSONC down to something JSON.parse accepts with ('plain-object')
|
||||||
✓ it strips JSONC down to something JSON.parse accepts with ('plain-array')
|
✓ it strips JSONC down to something JSON.parse accepts with ('plain-array')
|
||||||
@@ -2084,4 +2097,4 @@
|
|||||||
✓ pass with dataset with ('my-datas-set-value')
|
✓ pass with dataset with ('my-datas-set-value')
|
||||||
✓ within describe → pass with dataset with ('my-datas-set-value')
|
✓ within describe → pass with dataset with ('my-datas-set-value')
|
||||||
|
|
||||||
Tests: 1 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1462 passed (3220 assertions)
|
Tests: 1 deprecated, 4 warnings, 5 incomplete, 2 notices, 40 todos, 35 skipped, 1471 passed (3232 assertions)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Plugins\Tia\Graph;
|
||||||
|
|
||||||
|
describe('markKnownTestFiles()', function (): void {
|
||||||
|
it('makes a test file with no edges known', function (): void {
|
||||||
|
$graph = new Graph(sys_get_temp_dir());
|
||||||
|
|
||||||
|
expect($graph->knowsTest('tests/Unit/ExampleTest.php'))->toBeFalse();
|
||||||
|
|
||||||
|
$graph->markKnownTestFiles(['tests/Unit/ExampleTest.php']);
|
||||||
|
|
||||||
|
expect($graph->knowsTest('tests/Unit/ExampleTest.php'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not clobber edges of an already-known test file', function (): void {
|
||||||
|
$graph = new Graph(sys_get_temp_dir());
|
||||||
|
$graph->link('tests/Feature/UserTest.php', 'app/Models/User.php');
|
||||||
|
|
||||||
|
$graph->markKnownTestFiles(['tests/Feature/UserTest.php']);
|
||||||
|
|
||||||
|
// The pre-existing edge survives — the test still depends on the source file.
|
||||||
|
$affected = $graph->affected(['app/Models/User.php']);
|
||||||
|
|
||||||
|
expect($affected)->toContain('tests/Feature/UserTest.php');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ignores paths outside the project root', function (): void {
|
||||||
|
$graph = new Graph(sys_get_temp_dir());
|
||||||
|
|
||||||
|
$graph->markKnownTestFiles(['/somewhere/else/tests/FooTest.php']);
|
||||||
|
|
||||||
|
expect($graph->knowsTest('/somewhere/else/tests/FooTest.php'))->toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Pest\Plugins\Tia\TestPaths;
|
||||||
|
|
||||||
|
describe('isTestFile()', function (): void {
|
||||||
|
it('matches a standard test file under a configured directory', function (): void {
|
||||||
|
$paths = new TestPaths(
|
||||||
|
directories: ['tests/Unit', 'tests/Feature'],
|
||||||
|
files: [],
|
||||||
|
suffixes: ['Test.php'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($paths->isTestFile('tests/Unit/ExampleTest.php'))->toBeTrue()
|
||||||
|
->and($paths->isTestFile('tests/Feature/UserTest.php'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not match a non-test file that shares the directory', function (): void {
|
||||||
|
$paths = new TestPaths(
|
||||||
|
directories: ['tests/Unit'],
|
||||||
|
files: [],
|
||||||
|
suffixes: ['Test.php'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($paths->isTestFile('tests/Unit/Helper.php'))->toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not match files outside the configured directories', function (): void {
|
||||||
|
$paths = new TestPaths(
|
||||||
|
directories: ['tests/Unit'],
|
||||||
|
files: [],
|
||||||
|
suffixes: ['Test.php'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($paths->isTestFile('app/Models/UserTest.php'))->toBeFalse();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('matches an explicitly listed file', function (): void {
|
||||||
|
$paths = new TestPaths(
|
||||||
|
directories: [],
|
||||||
|
files: ['tests/Pest.php'],
|
||||||
|
suffixes: ['Test.php'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($paths->isTestFile('tests/Pest.php'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('honours a bare .php suffix', function (): void {
|
||||||
|
$paths = new TestPaths(
|
||||||
|
directories: ['tests'],
|
||||||
|
files: [],
|
||||||
|
suffixes: ['.php'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($paths->isTestFile('tests/Unit/ExampleTest.php'))->toBeTrue()
|
||||||
|
->and($paths->isTestFile('tests/Unit/Helper.php'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Regression: a suffix of "Test.php" must match "ExampleTest.php". A previous
|
||||||
|
// normalisation prepended a dot (".Test.php"), which matched nothing and left
|
||||||
|
// edited tests looking unchanged to TIA, replaying stale results.
|
||||||
|
it('does not require a dot before the suffix', function (): void {
|
||||||
|
$paths = new TestPaths(
|
||||||
|
directories: ['tests/Unit'],
|
||||||
|
files: [],
|
||||||
|
suffixes: ['.Test.php'],
|
||||||
|
);
|
||||||
|
|
||||||
|
expect($paths->isTestFile('tests/Unit/ExampleTest.php'))->toBeFalse();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -24,13 +24,13 @@ test('parallel', function () use ($run): void {
|
|||||||
$file = file_get_contents(__FILE__);
|
$file = file_get_contents(__FILE__);
|
||||||
$file = preg_replace(
|
$file = preg_replace(
|
||||||
'/\$expected = \'.*?\';/',
|
'/\$expected = \'.*?\';/',
|
||||||
"\$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1445 passed (3167 assertions)';",
|
"\$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1454 passed (3179 assertions)';",
|
||||||
$file,
|
$file,
|
||||||
);
|
);
|
||||||
file_put_contents(__FILE__, $file);
|
file_put_contents(__FILE__, $file);
|
||||||
}
|
}
|
||||||
|
|
||||||
$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1445 passed (3167 assertions)';
|
$expected = '1 deprecated, 4 warnings, 5 incomplete, 3 notices, 40 todos, 27 skipped, 1454 passed (3179 assertions)';
|
||||||
|
|
||||||
expect($output)
|
expect($output)
|
||||||
->toContain("Tests: {$expected}")
|
->toContain("Tests: {$expected}")
|
||||||
|
|||||||
Reference in New Issue
Block a user