Skip to content

Commit 46e30b2

Browse files
committed
Handle file classmaps and normalized wiki targets
1 parent 84f47a7 commit 46e30b2

4 files changed

Lines changed: 116 additions & 2 deletions

File tree

src/Console/Command/WikiCommand.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
133133
$jsonOutput = $this->isJsonOutput($input);
134134
$processOutput = $jsonOutput ? new BufferedOutput() : $output;
135135
$target = (string) $input->getOption('target');
136-
$isDefaultWikiTarget = ProjectCapabilitiesResolverInterface::DEFAULT_WIKI_TARGET === $target;
136+
$isDefaultWikiTarget = $this->isDefaultWikiTarget($target);
137137
$cacheEnabled = $this->isCacheEnabled($input);
138138

139139
if ($input->getOption('init')) {
@@ -213,6 +213,38 @@ protected function execute(InputInterface $input, OutputInterface $output): int
213213
);
214214
}
215215

216+
/**
217+
* Detects whether a target option still points at the default wiki target path.
218+
*
219+
* @param string $target the wiki target option received from the CLI
220+
*
221+
* @return bool true when the provided path is equivalent to the default wiki target
222+
*/
223+
private function isDefaultWikiTarget(string $target): bool
224+
{
225+
return $this->normalizeProjectRelativePath($target) === $this->normalizeProjectRelativePath(
226+
ProjectCapabilitiesResolverInterface::DEFAULT_WIKI_TARGET
227+
);
228+
}
229+
230+
/**
231+
* Normalizes a project-relative path for resilient default-option comparisons.
232+
*
233+
* @param string $path the project-relative path to normalize
234+
*
235+
* @return string the normalized project-relative path
236+
*/
237+
private function normalizeProjectRelativePath(string $path): string
238+
{
239+
$normalizedPath = str_replace('\\', '/', $path);
240+
241+
while (str_starts_with($normalizedPath, './')) {
242+
$normalizedPath = substr($normalizedPath, 2);
243+
}
244+
245+
return rtrim($normalizedPath, '/');
246+
}
247+
216248
/**
217249
* Adds the repository wiki as a Git submodule when the target path is missing.
218250
*

src/Project/ProjectCapabilitiesResolver.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
use function array_key_first;
2626
use function array_values;
2727
use function is_dir;
28+
use function is_file;
2829
use function rtrim;
30+
use function str_ends_with;
31+
use function strtolower;
2932

3033
/**
3134
* Resolves which repository surfaces are available to documentation, testing, and wiki tooling.
@@ -69,7 +72,7 @@ public function resolve(
6972
$this->filesystem->exists($guideDirectory),
7073
$this->filesystem->exists($testsPath),
7174
$this->filesystem->exists($wikiTarget),
72-
[] !== $apiDirectories,
75+
$this->resolveHasPhpSourceFiles($apiDirectories),
7376
);
7477
}
7578

@@ -133,6 +136,34 @@ private function resolveRelativeApiDirectory(string $path): ?string
133136
return $this->filesystem->makePathRelative($absolutePath);
134137
}
135138

139+
/**
140+
* Resolves whether Composer autoload metadata exposes testable PHP source for the repository.
141+
*
142+
* @param list<string> $apiDirectories the resolved API directories exposed by Composer autoload metadata
143+
*/
144+
private function resolveHasPhpSourceFiles(array $apiDirectories): bool
145+
{
146+
if ([] !== $apiDirectories) {
147+
return true;
148+
}
149+
150+
foreach (self::API_AUTOLOAD_TYPES as $autoloadType) {
151+
foreach ($this->normalizeAutoloadPaths($this->composer->getAutoload($autoloadType)) as $path) {
152+
$absolutePath = $this->filesystem->getAbsolutePath($path);
153+
154+
if (! \is_string($absolutePath)) {
155+
continue;
156+
}
157+
158+
if (is_file($absolutePath) && str_ends_with(strtolower($absolutePath), '.php')) {
159+
return true;
160+
}
161+
}
162+
}
163+
164+
return false;
165+
}
166+
136167
/**
137168
* Flattens Composer autoload path definitions into a normalized list of non-empty paths.
138169
*

tests/Console/Command/WikiCommandTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use FastForward\DevTools\Path\WorkingProjectPathResolver;
3434
use PHPUnit\Framework\Attributes\CoversClass;
3535
use PHPUnit\Framework\Attributes\Test;
36+
use PHPUnit\Framework\Attributes\TestWith;
3637
use PHPUnit\Framework\Attributes\UsesClass;
3738
use PHPUnit\Framework\Attributes\UsesTrait;
3839
use PHPUnit\Framework\TestCase;
@@ -226,6 +227,33 @@ public function executeWillSkipWhenWikiTargetDoesNotExist(): void
226227
self::assertSame(WikiCommand::SUCCESS, $this->executeCommand());
227228
}
228229

230+
/**
231+
* @return void
232+
*/
233+
#[Test]
234+
#[TestWith(['./.github/wiki'])]
235+
#[TestWith(['.github/wiki/'])]
236+
public function executeWillTreatEquivalentDefaultWikiTargetsAsDefault(string $target): void
237+
{
238+
$this->input->getOption('target')
239+
->willReturn($target);
240+
$this->projectCapabilitiesResolver->resolve(Argument::any(), Argument::any(), Argument::any())
241+
->willReturn(new ProjectCapabilities([], null, false, false, false, false));
242+
$this->processQueue->add(Argument::cetera())
243+
->shouldNotBeCalled();
244+
$this->logger->info('Generating wiki documentation...', Argument::that(
245+
static fn(array $context): bool => $context['input'] instanceof InputInterface
246+
))->shouldBeCalled();
247+
$this->logger->log(
248+
'warning',
249+
'Skipping wiki documentation generation because the wiki target does not exist at {target}.',
250+
Argument::that(static fn(array $context): bool => $target === $context['target']
251+
&& $context['input'] instanceof InputInterface),
252+
)->shouldBeCalled();
253+
254+
self::assertSame(WikiCommand::SUCCESS, $this->executeCommand());
255+
}
256+
229257
/**
230258
* @return void
231259
*/

tests/Project/ProjectCapabilitiesResolverTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,29 @@ public function resolveWillIgnoreToolingPhpFilesWhenDetectingTestablePhpSource()
133133
self::assertTrue($capabilities->canGenerateMetrics());
134134
}
135135

136+
/**
137+
* @return void
138+
*/
139+
#[Test]
140+
public function resolveWillDetectPhpSourceFromFileBasedClassmapEntries(): void
141+
{
142+
mkdir($this->workspace . '/legacy', recursive: true);
143+
file_put_contents($this->workspace . '/legacy/LegacyClass.php', "<?php\n\nfinal class LegacyClass {}\n");
144+
145+
$this->composer->getAutoload('psr-4')
146+
->willReturn([]);
147+
$this->composer->getAutoload('psr-0')
148+
->willReturn([]);
149+
$this->composer->getAutoload('classmap')
150+
->willReturn(['legacy/LegacyClass.php']);
151+
152+
$capabilities = $this->resolver->resolve();
153+
154+
self::assertTrue($capabilities->hasPhpSourceFiles());
155+
self::assertTrue($capabilities->canRunTests());
156+
self::assertFalse($capabilities->canGenerateApiDocumentation());
157+
}
158+
136159
/**
137160
* @return void
138161
*/

0 commit comments

Comments
 (0)