diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 411e674c..c79c0155 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,6 +31,9 @@ jobs: matrix: php: [ 8.2, 8.3, 8.4, 8.5 ] dependencies: [ lowest , highest ] + include: + - php: 8.1 + dependencies: highest max-parallel: 2 name: Tests PHP${{ matrix.php }}-${{ matrix.dependencies }} @@ -52,10 +55,19 @@ jobs: coverage: none - name: Install Composer dependencies + if: matrix.php != '8.1' uses: ramsey/composer-install@v3 with: dependency-versions: ${{ matrix.dependencies }} + - name: Install and downgrade for PHP 8.1 + if: matrix.php == '8.1' + uses: php-internal/actions/downgrade@v1 + with: + php-version: '8.1' + dependency-versions: ${{ matrix.dependencies }} + paths: core plugin bridge tests testo.php + # Bin tools carry deps with a higher PHP floor than the matrix (e.g. tools/double). # They install everywhere but only load where the platform allows, so ignore the check. - name: Install bin tools diff --git a/bridge/symfony-console/resources/stubs/testo.php b/bridge/symfony-console/resources/stubs/testo.php.stub similarity index 100% rename from bridge/symfony-console/resources/stubs/testo.php rename to bridge/symfony-console/resources/stubs/testo.php.stub diff --git a/bridge/symfony-console/src/Command/Init.php b/bridge/symfony-console/src/Command/Init.php index a57b96c5..33303d5e 100644 --- a/bridge/symfony-console/src/Command/Init.php +++ b/bridge/symfony-console/src/Command/Init.php @@ -27,7 +27,7 @@ )] final class Init extends Command { - private const STUB = __DIR__ . '/../../resources/stubs/testo.php'; + private const STUB = __DIR__ . '/../../resources/stubs/testo.php.stub'; private const CONFIG_FILENAME = 'testo.php'; private const KNOWN_SUITES = ['Unit', 'Integration', 'Functional', 'Acceptance', 'Feature', 'E2E', 'Contract']; private const SCRIPT_ALL_KEY = 'test'; diff --git a/composer.json b/composer.json index f144fae2..cbf9c216 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "require": { "php": ">=8.2", "ext-tokenizer": "*", - "internal/container": "^1.0", + "internal/container": "^1.1", "internal/destroy": "^1.0", "internal/path": "^1.3", "psr/container": "1 - 2", diff --git a/core/Application/Config/ApplicationConfig.php b/core/Application/Config/ApplicationConfig.php index 8453323b..b576e77c 100644 --- a/core/Application/Config/ApplicationConfig.php +++ b/core/Application/Config/ApplicationConfig.php @@ -4,6 +4,7 @@ namespace Testo\Application\Config; +use Internal\Container\Attribute\ScopeShared; use Internal\Path; use Testo\Application\Config\Plugin\ApplicationPlugins; use Testo\Common\PluginConfigurator; @@ -13,6 +14,7 @@ * * @api */ +#[ScopeShared] final readonly class ApplicationConfig { /** diff --git a/core/Application/Config/RunConfiguration.php b/core/Application/Config/RunConfiguration.php index 67a8d904..02152c40 100644 --- a/core/Application/Config/RunConfiguration.php +++ b/core/Application/Config/RunConfiguration.php @@ -4,6 +4,7 @@ namespace Testo\Application\Config; +use Internal\Container\Attribute\ScopeShared; use Internal\Path; /** @@ -22,6 +23,7 @@ * @psalm-immutable * @api */ +#[ScopeShared] final readonly class RunConfiguration { /** diff --git a/core/Application/Internal/MessengerHub.php b/core/Application/Internal/MessengerHub.php index 854ae297..8090b1e9 100644 --- a/core/Application/Internal/MessengerHub.php +++ b/core/Application/Internal/MessengerHub.php @@ -4,6 +4,7 @@ namespace Testo\Application\Internal; +use Internal\Container\Attribute\ScopeShared; use Psr\EventDispatcher\EventDispatcherInterface; use Testo\Application\Internal\Messenger\State; use Testo\Application\Internal\Messenger\MutableContainer; @@ -23,6 +24,7 @@ * * @internal */ +#[ScopeShared] final readonly class MessengerHub implements Messenger { private MutableContainer $state; diff --git a/core/Application/Internal/Runner/CaseRunner.php b/core/Application/Internal/Runner/CaseRunner.php index 39af6a66..25d7d619 100644 --- a/core/Application/Internal/Runner/CaseRunner.php +++ b/core/Application/Internal/Runner/CaseRunner.php @@ -4,6 +4,7 @@ namespace Testo\Application\Internal\Runner; +use Internal\Container\Attribute\ScopeShared; use Psr\EventDispatcher\EventDispatcherInterface; use Testo\Core\Context\CaseInfo; use Testo\Core\Context\CaseResult; @@ -25,6 +26,7 @@ * @internal * @psalm-internal Testo\Application */ +#[ScopeShared] final readonly class CaseRunner { public function __construct( diff --git a/core/Application/Internal/Runner/SuiteRunner.php b/core/Application/Internal/Runner/SuiteRunner.php index 8bdc2132..607c185d 100644 --- a/core/Application/Internal/Runner/SuiteRunner.php +++ b/core/Application/Internal/Runner/SuiteRunner.php @@ -4,6 +4,7 @@ namespace Testo\Application\Internal\Runner; +use Internal\Container\Attribute\ScopeShared; use Psr\EventDispatcher\EventDispatcherInterface; use Testo\Application\Internal\SimpleCaseInstantiator; use Testo\Common\ErrorReporter; @@ -30,6 +31,7 @@ * @internal * @psalm-internal Testo\Application */ +#[ScopeShared] final readonly class SuiteRunner { public function __construct( diff --git a/core/Application/Internal/Runner/TestRunner.php b/core/Application/Internal/Runner/TestRunner.php index cd006e31..2c8ca2a0 100644 --- a/core/Application/Internal/Runner/TestRunner.php +++ b/core/Application/Internal/Runner/TestRunner.php @@ -4,6 +4,7 @@ namespace Testo\Application\Internal\Runner; +use Internal\Container\Attribute\ScopeShared; use Psr\EventDispatcher\EventDispatcherInterface; use Testo\Application\Exception\PipelineFailure; use Testo\Core\Context\TestInfo; @@ -37,6 +38,7 @@ * @internal * @psalm-internal Testo\Application */ +#[ScopeShared] final readonly class TestRunner { public function __construct( diff --git a/core/Common/ErrorReporter.php b/core/Common/ErrorReporter.php index b1f0aec9..e77cee35 100644 --- a/core/Common/ErrorReporter.php +++ b/core/Common/ErrorReporter.php @@ -4,6 +4,7 @@ namespace Testo\Common; +use Internal\Container\Attribute\ScopeShared; use Testo\Core\Log\Level; /** @@ -17,6 +18,7 @@ * * @internal */ +#[ScopeShared] final readonly class ErrorReporter { public function __construct( diff --git a/core/Output/ConsoleStreams.php b/core/Output/ConsoleStreams.php index c330e319..5298040c 100644 --- a/core/Output/ConsoleStreams.php +++ b/core/Output/ConsoleStreams.php @@ -4,6 +4,8 @@ namespace Testo\Output; +use Internal\Container\Attribute\ScopeShared; + /** * The process's stdout/stderr as an injectable pair of streams. * @@ -12,6 +14,7 @@ * * @api */ +#[ScopeShared] final readonly class ConsoleStreams { /** @var resource */ diff --git a/core/Pipeline/Internal/AttributesInterceptor.php b/core/Pipeline/Internal/AttributesInterceptor.php index 6e9042c5..f3720d95 100644 --- a/core/Pipeline/Internal/AttributesInterceptor.php +++ b/core/Pipeline/Internal/AttributesInterceptor.php @@ -4,6 +4,7 @@ namespace Testo\Pipeline\Internal; +use Internal\Container\Attribute\ScopeShared; use Testo\Common\Reflection; use Testo\Core\Context\CaseInfo; use Testo\Core\Context\CaseResult; @@ -25,6 +26,7 @@ * @psalm-internal Testo\Pipeline */ #[InterceptorOptions(order: InterceptorOptions::ORDER_ATTRIBUTES)] +#[ScopeShared] final readonly class AttributesInterceptor implements TestRunInterceptor, TestCaseRunInterceptor { public function __construct( diff --git a/core/Testing/Internal/InjectInterceptor.php b/core/Testing/Internal/InjectInterceptor.php index d0ed087b..206e2825 100644 --- a/core/Testing/Internal/InjectInterceptor.php +++ b/core/Testing/Internal/InjectInterceptor.php @@ -4,6 +4,7 @@ namespace Testo\Testing\Internal; +use Internal\Container\Attribute\ScopeShared; use Internal\Container\Container; use Testo\Core\Context\CaseInfo; use Testo\Core\Context\CaseResult; @@ -21,6 +22,7 @@ * @psalm-internal Testo */ #[InterceptorOptions(order: InterceptorOptions::ORDER_DEFAULT)] +#[ScopeShared] final readonly class InjectInterceptor implements TestCaseRunInterceptor { public function __construct( diff --git a/plugin/assert/composer.json b/plugin/assert/composer.json index 3f8407a4..d0cd4ac9 100644 --- a/plugin/assert/composer.json +++ b/plugin/assert/composer.json @@ -21,6 +21,7 @@ ], "require": { "php": ">=8.2", + "internal/container": "^1.1", "testo/testo": "0.10.44 - 1" }, "autoload": { diff --git a/plugin/assert/src/Internal/Middleware/AssertCollectorInterceptor.php b/plugin/assert/src/Internal/Middleware/AssertCollectorInterceptor.php index 6eea016a..3d780d48 100644 --- a/plugin/assert/src/Internal/Middleware/AssertCollectorInterceptor.php +++ b/plugin/assert/src/Internal/Middleware/AssertCollectorInterceptor.php @@ -4,6 +4,7 @@ namespace Testo\Assert\Internal\Middleware; +use Internal\Container\Attribute\ScopeShared; use Testo\Assert\AssertPlugin; use Testo\Assert\Internal\HistoryRenderer; use Testo\Assert\Internal\StaticState; @@ -28,6 +29,7 @@ * Supports both synchronous and asynchronous (Fiber-based) environments. */ #[InterceptorOptions(order: InterceptorOptions::ORDER_ASSERTIONS - 10)] +#[ScopeShared] final readonly class AssertCollectorInterceptor implements TestRunInterceptor { public function __construct( diff --git a/plugin/bench/composer.json b/plugin/bench/composer.json index 3a1cb7c6..4a9577f8 100644 --- a/plugin/bench/composer.json +++ b/plugin/bench/composer.json @@ -21,6 +21,7 @@ ], "require": { "php": ">=8.2", + "internal/container": "^1.1", "testo/data": "^0.1.9", "testo/filter": "^0.1.7", "testo/inline": "^0.1.9", diff --git a/plugin/bench/src/Internal/BenchHandler.php b/plugin/bench/src/Internal/BenchHandler.php index e736623a..cc8fa002 100644 --- a/plugin/bench/src/Internal/BenchHandler.php +++ b/plugin/bench/src/Internal/BenchHandler.php @@ -4,6 +4,7 @@ namespace Testo\Bench\Internal; +use Internal\Container\Attribute\ScopeShared; use Testo\Assert\Internal\StaticState; use Testo\Assert\State\Assertion\AssertionException; use Testo\Assert\State\Assertion\AssertionSuccess; @@ -21,6 +22,7 @@ /** * @internal */ +#[ScopeShared] final readonly class BenchHandler { public function __construct( @@ -239,7 +241,7 @@ private static function runCase(\Closure $function, int $calls): Snap # peak is reset first so it reflects this iteration only, and the collection cycle keeps # garbage from a previous case out of the window. \gc_collect_cycles(); - \memory_reset_peak_usage(); + \function_exists('memory_reset_peak_usage') and \memory_reset_peak_usage(); $beforeMem = \memory_get_peak_usage(); $beforeTime = \hrtime(true); for ($i = 0; $i < $calls; ++$i) { diff --git a/plugin/bench/src/Internal/Pipeline/BenchFinder.php b/plugin/bench/src/Internal/Pipeline/BenchFinder.php index 590b7c83..4f4de489 100644 --- a/plugin/bench/src/Internal/Pipeline/BenchFinder.php +++ b/plugin/bench/src/Internal/Pipeline/BenchFinder.php @@ -4,6 +4,7 @@ namespace Testo\Bench\Internal\Pipeline; +use Internal\Container\Attribute\ScopeShared; use Testo\Bench; use Testo\Bench\Internal\BenchHandler; use Testo\Common\Reflection; @@ -22,6 +23,7 @@ * @internal */ #[InterceptorOptions(order: -20_000, testType: TestType::BenchInline)] +#[ScopeShared] final readonly class BenchFinder implements FileLocatorInterceptor, CaseLocatorInterceptor { /** @var \Closure(TestInfo): mixed Invoker for the test method. */ diff --git a/plugin/codecov/composer.json b/plugin/codecov/composer.json index 7fc9c567..7cc31021 100644 --- a/plugin/codecov/composer.json +++ b/plugin/codecov/composer.json @@ -22,6 +22,7 @@ "require": { "php": ">=8.2", "ext-xmlwriter": "*", + "internal/container": "^1.1", "testo/data": "^0.1.9", "testo/inline": "^0.1.8", "testo/testo": "0.10.44 - 1" diff --git a/plugin/codecov/src/Internal/CoverageCollector.php b/plugin/codecov/src/Internal/CoverageCollector.php index 4bf6779a..ac77b418 100644 --- a/plugin/codecov/src/Internal/CoverageCollector.php +++ b/plugin/codecov/src/Internal/CoverageCollector.php @@ -4,6 +4,7 @@ namespace Testo\Codecov\Internal; +use Internal\Container\Attribute\ScopeShared; use Internal\Destroy\Destroyable; use Psr\EventDispatcher\EventDispatcherInterface; use Testo\Codecov\Result\CoverageResult; @@ -21,6 +22,7 @@ * * @internal */ +#[ScopeShared] final readonly class CoverageCollector implements Destroyable { private Cache $cache; diff --git a/plugin/filter/Filter.php b/plugin/filter/Filter.php index 715f58dc..395a1968 100644 --- a/plugin/filter/Filter.php +++ b/plugin/filter/Filter.php @@ -4,6 +4,7 @@ namespace Testo; +use Internal\Container\Attribute\ScopeShared; use Internal\Path; use Testo\Core\Value\TestType; @@ -15,6 +16,7 @@ * * @api */ +#[ScopeShared] final readonly class Filter { /** diff --git a/plugin/filter/composer.json b/plugin/filter/composer.json index 11ec43a5..4494b487 100644 --- a/plugin/filter/composer.json +++ b/plugin/filter/composer.json @@ -21,6 +21,7 @@ ], "require": { "php": ">=8.2", + "internal/container": "^1.1", "testo/testo": "0.10.46 - 1" }, "autoload": { diff --git a/plugin/filter/src/Internal/SuiteFilterInterceptor.php b/plugin/filter/src/Internal/SuiteFilterInterceptor.php index 98c1c92a..230dc571 100644 --- a/plugin/filter/src/Internal/SuiteFilterInterceptor.php +++ b/plugin/filter/src/Internal/SuiteFilterInterceptor.php @@ -4,6 +4,7 @@ namespace Testo\Filter\Internal; +use Internal\Container\Attribute\ScopeShared; use Internal\Path; use Testo\Application\Config\ApplicationConfig; use Testo\Application\Config\FinderConfig; @@ -23,6 +24,7 @@ * @psalm-internal Testo\Filter */ #[InterceptorOptions(order: InterceptorOptions::ORDER_FILTER, onConflict: ConflictPolicy::First)] +#[ScopeShared] final readonly class SuiteFilterInterceptor implements SuiteLocatorInterceptor { public function __construct( diff --git a/tests/Output/Unit/Rendering/Diff/DifferStrategiesTest.php b/tests/Output/Unit/Rendering/Diff/DifferStrategiesTest.php index ff9a9579..4158a106 100644 --- a/tests/Output/Unit/Rendering/Diff/DifferStrategiesTest.php +++ b/tests/Output/Unit/Rendering/Diff/DifferStrategiesTest.php @@ -6,6 +6,7 @@ use Testo\Assert; use Testo\Codecov\Covers; +use Testo\Core\Exception\SkipTest; use Testo\Data\DataCross; use Testo\Data\DataProvider; use Testo\Output\Rendering\Diff\DiffLine; @@ -242,6 +243,10 @@ public function ratcliffStaysCorrectWithPopularLines(): void */ public function hirschbergUsesFarLessMemoryThanTheLcsTable(): void { + \function_exists('memory_reset_peak_usage') or throw new SkipTest( + 'Per-call peak measurement needs memory_reset_peak_usage() (PHP 8.2+).', + ); + $expected = \implode("\n", \array_map(static fn(int $i): string => "row {$i}", \range(1, 400))); $actual = \str_replace('row 200', 'row CHANGED', $expected);