Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Classes/Composer/ComposerPackageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function getPackageInfoWithFallback(string $nameOrExtensionKeyOrPath): ?P
*/
public function getPackageInfo(string $name): ?PackageInfo
{
return self::$packages[$this->resolvePackageName($name)] ?? null;
return self::$packages[$name] ?? self::$packages[$this->resolvePackageName($name)] ?? null;
Comment thread
sbuerk marked this conversation as resolved.
}

/**
Expand Down Expand Up @@ -655,7 +655,6 @@ private function removePrefixPaths(string $path): string
rtrim($this->rootPackage()->getVendorDir(), '/') . '/',
rtrim($this->rootPackage()->getWebDir(), '/') . '/',
rtrim($this->getRootPath(), '/') . '/',
basename($this->getRootPath()) . '/',
];
foreach ($removePaths as $removePath) {
if (str_starts_with($path, $removePath)) {
Expand Down
2 changes: 1 addition & 1 deletion Classes/Core/Testbase.php
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ public function setUpPackageStates(

// Activate test extensions that have been symlinked before
foreach ($testExtensionPaths as $extensionPath) {
if ($packageInfo = $this->composerPackageManager->getPackageInfo($extensionPath)) {
if ($packageInfo = $this->composerPackageManager->getPackageInfoWithFallback($extensionPath)) {
$extensionName = $packageInfo->getExtensionKey();
} else {
$extensionName = basename($extensionPath);
Expand Down
39 changes: 39 additions & 0 deletions Tests/Unit/Composer/ComposerPackageManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,27 @@ public function coreExtensionCanBeResolvedWithRelativeLegacyPathPrefix(): void
self::assertTrue($packageInfo->isSystemExtension());
}

/**
* A composer package name whose vendor segment matches one of the path
* prefixes removed for classic mode notation must not be rewritten before
* looking it up. The fixture extension used here is named
* `testing-framework/extension-absolute`, and `testing-framework` is the
* project root folder name of a default checkout.
*/
#[Test]
public function knownPackageNameIsNotModifiedBeforeLookup(): void
{
$subject = new ComposerPackageManager();
// Register the fixture extension, it is not required by the root composer.json.
$subject->getPackageInfoWithFallback(__DIR__ . '/Fixtures/Extensions/ext_absolute');

$packageInfo = $subject->getPackageInfo('testing-framework/extension-absolute');

self::assertInstanceOf(PackageInfo::class, $packageInfo);
self::assertSame('testing-framework/extension-absolute', $packageInfo->getName());
self::assertSame('absolute_real', $packageInfo->getExtensionKey());
}

#[Test]
public function extensionWithoutJsonCanBeResolvedByAbsolutePath(): void
{
Expand Down Expand Up @@ -471,6 +492,24 @@ public function prepareResolvePackageNameReturnsExpectedValues(string $name, str
self::assertSame($expected, $resolved, sprintf('"%s" resolved to "%s"', $name, $expected));
}

/**
* The project root folder name must not be removed as path prefix. It is a
* relative single segment prefix, and it therefore also matches the vendor
* segment of a composer package name, for example `typo3/cms-core` for a
* project checked out into a folder named `typo3`.
*/
#[Test]
public function prepareResolvePackageNameKeepsProjectRootFolderNamePrefix(): void
{
$composerPackageManager = new ComposerPackageManager();
$name = basename($composerPackageManager->getRootPath()) . '/some-package';

$prepareResolvePackageNameReflectionMethod = new \ReflectionMethod($composerPackageManager, 'prepareResolvePackageName');
$resolved = $prepareResolvePackageNameReflectionMethod->invoke($composerPackageManager, $name);

self::assertSame($name, $resolved, sprintf('"%s" resolved to "%s"', $name, $resolved));
}

public static function resolvePackageNameReturnsExpectedPackageNameDataProvider(): \Generator
{
yield 'Composer package name returns unchanged (not checked for existence)' => [
Expand Down
20 changes: 20 additions & 0 deletions Tests/Unit/Core/Fixtures/Extensions/ext_folder_name/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "typo3/testing-framework-diverging-extension-key",
"description": "Test extension with a folder name differing from its extension key",
"type": "typo3-cms-extension",
"license": [
"GPL-2.0-or-later"
],
"require": {
"php": "*"
},
"extra": {
"typo3/cms": {
"extension-key": "diverging_extension_key",
"version": "1.0.0",
"Package": {
"providesPackages": {}
}
}
}
}
88 changes: 88 additions & 0 deletions Tests/Unit/Core/TestbaseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

declare(strict_types=1);

namespace TYPO3\TestingFramework\Tests\Unit\Core;

/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use TYPO3\TestingFramework\Core\Testbase;

final class TestbaseTest extends TestCase
{
private string $instancePath = '';

protected function tearDown(): void
{
if ($this->instancePath !== '' && is_dir($this->instancePath)) {
$this->removeDirectory($this->instancePath);
}
$this->instancePath = '';
parent::tearDown();
}

/**
* Testbase::linkTestExtensionsToInstance() symlinks a test extension using the
* extension key determined by ComposerPackageManager::getPackageInfoWithFallback().
* Testbase::setUpPackageStates() must determine the very same name, otherwise the
* written PackageStates.php points to a non-existing package path.
*/
#[Test]
public function setUpPackageStatesUsesExtensionKeyOfTestExtensionAndNotItsFolderName(): void
{
$extensionPath = __DIR__ . '/Fixtures/Extensions/ext_folder_name';
// Folder name and extension key of the fixture extension differ on purpose.
self::assertSame('ext_folder_name', basename($extensionPath));

$this->instancePath = $this->createTestInstance();
// Symlink the extension the way linkTestExtensionsToInstance() does it.
symlink($extensionPath, $this->instancePath . '/typo3conf/ext/diverging_extension_key');

$subject = new Testbase();
$subject->setUpPackageStates($this->instancePath, [], [], [$extensionPath], []);

$packageStates = require $this->instancePath . '/typo3conf/PackageStates.php';

self::assertArrayHasKey('diverging_extension_key', $packageStates['packages']);
self::assertArrayNotHasKey('ext_folder_name', $packageStates['packages']);
self::assertSame(
'typo3conf/ext/diverging_extension_key/',
$packageStates['packages']['diverging_extension_key']['packagePath']
);
}

private function createTestInstance(): string
{
$instancePath = sys_get_temp_dir() . '/testbase-' . bin2hex(random_bytes(8));
mkdir($instancePath . '/typo3conf/ext', 0775, true);
return $instancePath;
}

private function removeDirectory(string $path): void
{
foreach ((array)scandir($path) as $entry) {
if ($entry === '.' || $entry === '..') {
continue;
}
$entryPath = $path . '/' . $entry;
if (is_link($entryPath) || is_file($entryPath)) {
unlink($entryPath);
continue;
}
$this->removeDirectory($entryPath);
}
rmdir($path);
}
}