Skip to content

Commit 6c52eb5

Browse files
committed
test: centralize tooling excluded directories and cleanup fixture handling
1 parent fe313cc commit 6c52eb5

9 files changed

Lines changed: 153 additions & 81 deletions

File tree

.agents/skills/phpunit-tests/SKILL.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,57 @@ Choose the smallest command that proves the change.
8383
- Re-run after fixes until the targeted tests pass.
8484
- If failures come from unrelated pre-existing breakage, call that out separately and do not silently claim success.
8585

86+
### Composer plugin validation flow
87+
88+
Some repository test scenarios are only valid when DevTools is executed as a Composer plugin.
89+
Create a temporary fixture under `backup/` before running plugin-based verification.
90+
91+
```bash
92+
PROJECT_ROOT="$(pwd)"
93+
PLUGIN_FIXTURE="$PROJECT_ROOT/backup/composer-plugin-consumer"
94+
mkdir -p "$PROJECT_ROOT/backup"
95+
rm -rf "$PLUGIN_FIXTURE"
96+
mkdir -p "$PLUGIN_FIXTURE"
97+
98+
cat > "$PLUGIN_FIXTURE/composer.json" <<'JSON'
99+
{
100+
"name": "fast-forward/dev-tools-composer-plugin-consumer-fixture",
101+
"description": "Fixture project used to validate DevTools plugin behavior.",
102+
"license": "MIT",
103+
"type": "project",
104+
"require": {
105+
"fast-forward/dev-tools": "*@dev"
106+
},
107+
"repositories": [
108+
{
109+
"type": "path",
110+
"url": "../..",
111+
"options": {
112+
"symlink": true
113+
}
114+
}
115+
],
116+
"minimum-stability": "dev",
117+
"prefer-stable": true,
118+
"config": {
119+
"allow-plugins": {
120+
"ergebnis/composer-normalize": true,
121+
"fast-forward/dev-tools": true,
122+
"phpdocumentor/shim": true,
123+
"phpro/grumphp-shim": true,
124+
"pyrech/composer-changelogs": true
125+
}
126+
},
127+
"scripts": {
128+
"dev-tools": "dev-tools",
129+
"dev-tools:fix": "@dev-tools --fix"
130+
}
131+
}
132+
JSON
133+
134+
cd "$PLUGIN_FIXTURE"
135+
composer install
136+
composer tests
137+
```
138+
86139
Read [references/generation-checklist.md](references/generation-checklist.md) before writing or repairing tests in an unfamiliar repository.

.php-cs-fixer.dist.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
declare(strict_types=1);
44

5+
require __DIR__ . '/vendor/autoload.php';
6+
7+
use FastForward\DevTools\Path\WorkingProjectPathResolver;
8+
59
$rules = [
610
'phpdoc_indent' => true,
711
'phpdoc_order' => [
@@ -39,11 +43,7 @@
3943

4044
$finder = PhpCsFixer\Finder::create()
4145
->in([getcwd()])
42-
->exclude('public')
43-
->exclude('resources')
44-
->exclude('vendor')
45-
->exclude('tmp')
46-
;
46+
->exclude(WorkingProjectPathResolver::TOOLING_EXCLUDED_DIRECTORIES);
4747

4848
return (new PhpCsFixer\Config())
4949
->setRiskyAllowed(false)

docs/commands/metrics.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Options
3333
Comma-separated directories that should be excluded from analysis.
3434

3535
Default:
36-
``vendor,tmp,cache,spec,build,.dev-tools,backup,resources,tests/Fixtures``.
36+
``vendor,tmp,cache,spec,build,.dev-tools,backup,resources``.
3737

3838
``--target=<directory>``
3939
Output directory for the generated metrics reports.

src/Console/Command/MetricsCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ protected function configure(): void
8989
name: 'exclude',
9090
mode: InputOption::VALUE_OPTIONAL,
9191
description: 'Comma-separated directories that SHOULD be excluded from analysis.',
92-
default: 'vendor,tmp,cache,spec,build,.dev-tools,backup,resources,tests/Fixtures',
92+
default: 'vendor,tmp,cache,spec,build,.dev-tools,backup,resources',
9393
)
9494
->addOption(
9595
name: 'target',

src/Path/WorkingProjectPathResolver.php

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@
2929
*/
3030
final class WorkingProjectPathResolver
3131
{
32+
/**
33+
* @var list<string> repository-local directories ignored by tooling
34+
*/
35+
public const array TOOLING_EXCLUDED_DIRECTORIES = [
36+
'.dev-tools',
37+
'backup',
38+
'cache',
39+
'public',
40+
'resources',
41+
'tmp',
42+
'vendor',
43+
'*/vendor',
44+
'*/vendor/*',
45+
'**/vendor',
46+
'**/vendor/*',
47+
];
48+
3249
/**
3350
* Returns the current working project directory or a path under it.
3451
*
@@ -52,19 +69,27 @@ public static function getProjectPath(string $path = ''): string
5269
*/
5370
public static function getToolingExcludedDirectories(string $baseDir = ''): array
5471
{
55-
return [
56-
ManagedWorkspace::getOutputDirectory(baseDir: $baseDir),
57-
Path::join($baseDir, 'backup'),
58-
Path::join($baseDir, 'cache'),
59-
Path::join($baseDir, 'public'),
60-
Path::join($baseDir, 'resources'),
61-
Path::join($baseDir, 'tmp'),
62-
Path::join($baseDir, 'vendor'),
63-
Path::join($baseDir, '*/vendor'),
64-
Path::join($baseDir, '*/vendor/*'),
65-
Path::join($baseDir, '**/vendor'),
66-
Path::join($baseDir, '**/vendor/*'),
72+
$excludeFromBaseDir = [
73+
'.dev-tools' => ManagedWorkspace::getOutputDirectory(baseDir: $baseDir),
74+
'backup' => Path::join($baseDir, 'backup'),
75+
'cache' => Path::join($baseDir, 'cache'),
76+
'public' => Path::join($baseDir, 'public'),
77+
'resources' => Path::join($baseDir, 'resources'),
78+
'tmp' => Path::join($baseDir, 'tmp'),
79+
'vendor' => Path::join($baseDir, 'vendor'),
80+
'*/vendor' => Path::join($baseDir, '*/vendor'),
81+
'*/vendor/*' => Path::join($baseDir, '*/vendor/*'),
82+
'**/vendor' => Path::join($baseDir, '**/vendor'),
83+
'**/vendor/*' => Path::join($baseDir, '**/vendor/*'),
6784
];
85+
86+
$directories = [];
87+
88+
foreach (self::TOOLING_EXCLUDED_DIRECTORIES as $excludedDirectory) {
89+
$directories[] = $excludeFromBaseDir[$excludedDirectory];
90+
}
91+
92+
return $directories;
6893
}
6994

7095
/**
@@ -81,7 +106,7 @@ public static function getToolingSourcePaths(string $baseDir = ''): array
81106
->files()
82107
->name('*.php')
83108
->in($workingDirectory)
84-
->exclude(['.dev-tools', 'backup', 'cache', 'public', 'resources', 'tmp', 'vendor'])
109+
->exclude(self::TOOLING_EXCLUDED_DIRECTORIES)
85110
->sortByName();
86111
$paths = [];
87112

tests/Console/Command/MetricsCommandTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ protected function setUp(): void
7373
$this->process = $this->prophesize(Process::class);
7474

7575
$this->input->getOption('exclude')
76-
->willReturn('vendor,tests/Fixtures');
76+
->willReturn('vendor');
7777
$this->input->getOption('target')
7878
->willReturn(ManagedWorkspace::getOutputDirectory(ManagedWorkspace::METRICS));
7979
$this->input->getOption('junit')

tests/Fixtures/composer-plugin-consumer/.gitignore

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/Fixtures/composer-plugin-consumer/composer.json

Lines changed: 0 additions & 38 deletions
This file was deleted.

tests/Path/WorkingProjectPathResolverTest.php

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
use PHPUnit\Framework\Attributes\UsesClass;
2727
use PHPUnit\Framework\TestCase;
2828

29+
use function Safe\scandir;
30+
use function Safe\rmdir;
31+
use function Safe\unlink;
2932
use function Safe\file_put_contents;
3033
use function Safe\getcwd;
3134
use function Safe\mkdir;
3235
use function Safe\realpath;
33-
use function sys_get_temp_dir;
3436
use function uniqid;
3537

3638
#[CoversClass(WorkingProjectPathResolver::class)]
@@ -113,9 +115,9 @@ public function itWillExposeRelativeToolingSkipPatternsByDefault(): void
113115
* @return void
114116
*/
115117
#[Test]
116-
public function itWillExposeToolingSourcePathsWithoutTraversingVendorDirectories(): void
118+
public function itWillExposeToolingSourcePathsIgnoringExcludedDirectories(): void
117119
{
118-
$fixtureDirectory = sys_get_temp_dir() . '/dev-tools-path-resolver-' . uniqid();
120+
$fixtureDirectory = \dirname(__DIR__, 2) . '/backup/dev-tools-path-resolver-' . uniqid();
119121

120122
mkdir($fixtureDirectory . '/src', recursive: true);
121123
mkdir($fixtureDirectory . '/tests/Fixtures/consumer/vendor/package/src', recursive: true);
@@ -130,13 +132,55 @@ public function itWillExposeToolingSourcePathsWithoutTraversingVendorDirectories
130132
file_put_contents($fixtureDirectory . '/backup/Backup.php', '<?php');
131133
file_put_contents($fixtureDirectory . '/.dev-tools/cache/Cached.php', '<?php');
132134

133-
self::assertSame(
134-
[
135-
realpath($fixtureDirectory) . '/src/Example.php',
136-
realpath($fixtureDirectory) . '/tests/Fixtures/Example.php',
137-
],
138-
WorkingProjectPathResolver::getToolingSourcePaths(realpath($fixtureDirectory))
139-
);
135+
try {
136+
self::assertSame(
137+
[
138+
realpath($fixtureDirectory) . '/src/Example.php',
139+
realpath($fixtureDirectory) . '/tests/Fixtures/Example.php',
140+
],
141+
WorkingProjectPathResolver::getToolingSourcePaths(realpath($fixtureDirectory))
142+
);
143+
} finally {
144+
self::cleanupFixtureDirectory($fixtureDirectory);
145+
}
146+
}
147+
148+
/**
149+
* @param string $fixtureDirectory
150+
*
151+
* @return void
152+
*/
153+
private static function cleanupFixtureDirectory(string $fixtureDirectory): void
154+
{
155+
if (! is_dir($fixtureDirectory) && ! is_link($fixtureDirectory)) {
156+
return;
157+
}
158+
159+
$entries = scandir($fixtureDirectory);
160+
if (false === $entries) {
161+
return;
162+
}
163+
164+
foreach ($entries as $entry) {
165+
if ('.' === $entry) {
166+
continue;
167+
}
168+
169+
if ('..' === $entry) {
170+
continue;
171+
}
172+
173+
$path = $fixtureDirectory . '/' . $entry;
174+
175+
if (is_link($path) || is_file($path)) {
176+
unlink($path);
177+
continue;
178+
}
179+
180+
self::cleanupFixtureDirectory($path);
181+
}
182+
183+
rmdir($fixtureDirectory);
140184
}
141185

142186
/**

0 commit comments

Comments
 (0)