Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TASK] Collection of backports #633

Merged
merged 6 commits into from
Nov 29, 2024
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
2 changes: 2 additions & 0 deletions Build/phpstan/phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ parameters:
- ../../Classes/Core/Acceptance/*
# Text fixtures extensions uses $_EXTKEY phpstan would be report as "might not defined"
- ../../Tests/Unit/*/Fixtures/Extensions/*/ext_emconf.php
- ../../Tests/Unit/*/Fixtures/Packages/*/ext_emconf.php
- ../../Tests/Unit/Fixtures/Packages/*/ext_emconf.php
107 changes: 96 additions & 11 deletions Classes/Composer/ComposerPackageManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,22 @@
*/

use Composer\InstalledVersions;
use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;

/**
* `typo3/testing-framework` internal composer package manager, used to gather source
* information of extensions already loaded by the root composer installation with
* the additional ability to register test fixture packages and extensions during
* runtime to create {@see FunctionalTestCase} test instances and provide symlinks
* of extensions into the classic mode test instance or retrieve files from a composer
* package or extension unrelated where they are placed on the filesystem.
*
* - {@see Testbase::setUpInstanceCoreLinks()}
* - {@see Testbase::linkTestExtensionsToInstance()}
* - {@see Testbase::linkFrameworkExtensionsToInstance()}
* - {@see Testbase::setUpLocalConfiguration()}
* - {@see Testbase::setUpPackageStates()}
*
* @internal This class is for testing-framework internal processing and not part of public testing API.
*/
final class ComposerPackageManager
Expand Down Expand Up @@ -61,28 +75,33 @@ final class ComposerPackageManager

public function __construct()
{
// @todo Remove this from the constructor.
$this->build();
}

public function getPackageInfoWithFallback(string $name): ?PackageInfo
/**
* Get composer package information {@see PackageInfo} for `$nameOrExtensionKeyOrPath`.
*/
public function getPackageInfoWithFallback(string $nameOrExtensionKeyOrPath): ?PackageInfo
{
if ($packageInfo = $this->getPackageInfo($name)) {
if ($packageInfo = $this->getPackageInfo($nameOrExtensionKeyOrPath)) {
return $packageInfo;
}
if ($packageInfo = $this->getPackageFromPath($name)) {
if ($packageInfo = $this->getPackageFromPath($nameOrExtensionKeyOrPath)) {
return $packageInfo;
}
if ($packageInfo = $this->getPackageFromPathFallback($name)) {
if ($packageInfo = $this->getPackageFromPathFallback($nameOrExtensionKeyOrPath)) {
return $packageInfo;
}

return null;
}

/**
* Get {@see PackageInfo} for package name or extension key `$name`.
*/
public function getPackageInfo(string $name): ?PackageInfo
{
$name = $this->resolvePackageName($name);
return self::$packages[$name] ?? null;
return self::$packages[$this->resolvePackageName($name)] ?? null;
}

/**
Expand Down Expand Up @@ -402,9 +421,23 @@ private function getExtEmConf(string $path): ?array
return null;
}

/**
* Returns resolved composer package name when $name is a known extension key
* for a known package, otherwise return $name unchanged.
*
* Used to determine the package name to look up as composer package within {@see self::$packages}
*
* Supports also relative classic mode notation:
*
* - typo3/sysext/backend
* - typo3conf/ext/my_ext_key
*
* {@see self::prepareResolvePackageName()} for details for normalisation.
*/
private function resolvePackageName(string $name): string
{
return self::$extensionKeyToPackageNameMap[$this->normalizeExtensionKey(basename($name))] ?? $name;
$name = $this->prepareResolvePackageName($name);
return self::$extensionKeyToPackageNameMap[$name] ?? $name;
}

/**
Expand Down Expand Up @@ -545,11 +578,20 @@ private function determineExtensionKey(
?array $info = null,
?array $extEmConf = null
): string {
$isExtension = in_array($info['type'] ?? '', ['typo3-cms-framework', 'typo3-cms-extension'], true)
|| ($extEmConf !== null);
if (!$isExtension) {
$isComposerExtensionType = ($info !== null && array_key_exists('type', $info) && is_string($info['type']) && in_array($info['type'], ['typo3-cms-framework', 'typo3-cms-extension'], true));
$hasExtEmConf = $extEmConf !== null;
if (!($isComposerExtensionType || $hasExtEmConf)) {
return '';
}
$hasComposerExtensionKey = (
is_array($info)
&& isset($info['extra']['typo3/cms']['extension-key'])
&& is_string($info['extra']['typo3/cms']['extension-key'])
&& $info['extra']['typo3/cms']['extension-key'] !== ''
);
if ($hasComposerExtensionKey) {
return $info['extra']['typo3/cms']['extension-key'];
}
$baseName = basename($packagePath);
if (($info['type'] ?? '') === 'typo3-csm-framework'
&& str_starts_with($baseName, 'cms-')
Expand Down Expand Up @@ -630,4 +672,47 @@ private function getFirstPathElement(string $path): string
}
return explode('/', $path)[0] ?? '';
}

/**
* Extension can be specified with their composer name, extension key or with classic mode relative path
* prefixes (`typo3/sysext/<extensionkey>` or `typo3conf/ext/<extensionkey>`) for functional tests to
* configure which extension should be provided in the test instance.
*
* This method normalizes a handed over name by removing the specified extra information, so it can be
* used to resolve it either as direct package name or as extension name.
*
* Handed over value also removes known environment prefix paths, like the full path to the root (project rook),
* vendor folder or web folder using {@see self::removePrefixPaths()} which is safe, as this method is and most
* only be used for {@see self::resolvePackageName()} to find a composer package in {@see self::$packages}, after
* mapping extension-key to composer package name.
*
* Example for processed changes:
* -----------------------------_
*
* - typo3/sysext/backend => backend
* - typo3conf/ext/my_ext_key => my_ext_key
*
* Example not processed values:
* -----------------------------
*
* valid names
* - typo3/cms-core => typo3/cms-core
* - my-vendor/my-package-name => my-vendor/my-package-name
* - my-package-name-without-vendor => my-package-name-without-vendor
*/
private function prepareResolvePackageName($name): string
{
$name = trim($this->removePrefixPaths($name), '/');
$relativePrefixPaths = [
'typo3/sysext/',
'typo3conf/ext/',
];
foreach ($relativePrefixPaths as $relativePrefixPath) {
if (!str_starts_with($name, $relativePrefixPath)) {
continue;
}
$name = substr($name, mb_strlen($relativePrefixPath));
}
return $name;
}
}
12 changes: 10 additions & 2 deletions Classes/Core/Acceptance/Extension/BackendEnvironment.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Codeception\Events;
use Codeception\Extension;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\DataSet;
use TYPO3\TestingFramework\Core\Testbase;
Expand Down Expand Up @@ -261,8 +262,15 @@ public function bootstrapTypo3Environment(SuiteEvent $suiteEvent)
$testbase->testDatabaseNameIsNotTooLong($originalDatabaseName, $localConfiguration);
if ($dbDriver === 'mysqli' || $dbDriver === 'pdo_mysql') {
$localConfiguration['DB']['Connections']['Default']['charset'] = 'utf8mb4';
$localConfiguration['DB']['Connections']['Default']['tableoptions']['charset'] = 'utf8mb4';
$localConfiguration['DB']['Connections']['Default']['tableoptions']['collate'] = 'utf8mb4_unicode_ci';
if ((new Typo3Version())->getMajorVersion() >= 12) {
// @todo Use this as default when TYPO3 v11 support is dropped.
$localConfiguration['DB']['Connections']['Default']['defaultTableOptions']['charset'] = 'utf8mb4';
$localConfiguration['DB']['Connections']['Default']['defaultTableOptions']['collation'] = 'utf8mb4_unicode_ci';
} else {
// @todo Remove this when TYPO3 v11 support is dropped.
$localConfiguration['DB']['Connections']['Default']['tableoptions']['charset'] = 'utf8mb4';
$localConfiguration['DB']['Connections']['Default']['tableoptions']['collate'] = 'utf8mb4_unicode_ci';
}
}
} else {
// sqlite dbs of all tests are stored in a dir parallel to instance roots. Allows defining this path as tmpfs.
Expand Down
69 changes: 55 additions & 14 deletions Classes/Core/Functional/FunctionalTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ abstract class FunctionalTestCase extends BaseTestCase implements ContainerInter
*
* A default list of core extensions is always loaded.
*
* System extension can be provided by their extension key or composer package name,
* and also as classic mode relative path
*
* ```
* protected array $coreExensionToLoad = [
* // As composer package name
* 'typo3/cms-core',
* // As extension-key
* 'core',
* // As relative classic mode system installation path
* 'typo3/sysext/core',
* ];
* ```
*
* Note that system extensions must be available, which means either added as require or
* require-dev to the root composer.json or required and installed by a required package.
*
* @see FunctionalTestCaseUtility $defaultActivatedCoreExtensions
*
* @var non-empty-string[]
Expand All @@ -121,16 +138,32 @@ abstract class FunctionalTestCase extends BaseTestCase implements ContainerInter
* Array of test/fixture extensions paths that should be loaded for a test.
*
* This property will stay empty in this abstract, so it is possible
* to just overwrite it in extending classes. Extensions noted here will
* be loaded for every test of a test case, and it is not possible to change
* the list of loaded extensions between single tests of a test case.
* to just overwrite it in extending classes.
*
* IMPORTANT: Extension list is concrete and used to create the test instance on first
* test execution and is **NOT** changeable between single test permutations.
*
* Given path is expected to be relative to your document root, example:
*
* array(
* 'typo3conf/ext/some_extension/Tests/Functional/Fixtures/Extensions/test_extension',
* ```
* protected array $testExtensionToLoad = [
*
* // Virtual relative classic mode installation path
* 'typo3conf/ext/base_extension',
* );
*
* // Virtual relative classic mode installation path subfolder test fixture
* 'typo3conf/ext/some_extension/Tests/Functional/Fixtures/Extensions/test_extension',
*
* // Relative to current test case (recommended for test fixture extension)
* __DIR__ . '/../Fixtures/Extensions/another_test_extension',
*
* // composer package name when available as `require` or `require-dev` in root composer.json
* 'vendor/some-extension',
*
* // extension key when available as package loaded as `require` or `require-dev` in root composer.json
* 'my_extension_key',
* ];
* ```
*
* Extensions in this array are linked to the test instance, loaded
* and their ext_tables.sql will be applied.
Expand All @@ -147,18 +180,22 @@ abstract class FunctionalTestCase extends BaseTestCase implements ContainerInter
* be linked for every test of a test case, and it is not possible to change
* the list of folders between single tests of a test case.
*
* array(
* ```
* protected array $pathsToLinkInTestInstance = [
* 'link-source' => 'link-destination'
* );
* ];
* ```
*
* Given paths are expected to be relative to the test instance root.
* The array keys are the source paths and the array values are the destination
* paths, example:
*
* [
* ```
* protected array $pathsToLinkInTestInstance = [
* 'typo3/sysext/impext/Tests/Functional/Fixtures/Folders/fileadmin/user_upload' =>
* 'fileadmin/user_upload',
* ]
* ];
* ```
*
* To be able to link from my_own_ext the extension path needs also to be registered in
* property $testExtensionsToLoad
Expand All @@ -172,12 +209,14 @@ abstract class FunctionalTestCase extends BaseTestCase implements ContainerInter
* paths are really duplicated and provided in the instance - instead of
* using symbolic links. Examples:
*
* [
* ```
* protected array $pathsToProvideInTestInstance = [
* // Copy an entire directory recursive to fileadmin
* 'typo3/sysext/lowlevel/Tests/Functional/Fixtures/testImages/' => 'fileadmin/',
* // Copy a single file into some deep destination directory
* 'typo3/sysext/lowlevel/Tests/Functional/Fixtures/testImage/someImage.jpg' => 'fileadmin/_processed_/0/a/someImage.jpg',
* ]
* ];
* ```
*
* @var array<string, non-empty-string>
*/
Expand Down Expand Up @@ -208,9 +247,11 @@ abstract class FunctionalTestCase extends BaseTestCase implements ContainerInter
* To create additional folders add the paths to this array. Given paths are expected to be
* relative to the test instance root and have to begin with a slash. Example:
*
* [
* ```
* protected array $additionalFoldersToCreate = [
* 'fileadmin/user_upload'
* ]
* ];
* ```
*
* @var non-empty-string[]
*/
Expand Down
Loading