From 33e3bf28889966dcea31512847e0f61f797b1262 Mon Sep 17 00:00:00 2001 From: nuno maduro Date: Tue, 7 Jul 2026 13:40:02 +0100 Subject: [PATCH] chore: moves to phpunit 13.2 --- composer.json | 4 +-- src/Concerns/Testable.php | 26 +++++++++++----- src/Expectations/OppositeExpectation.php | 4 +-- src/Logging/TeamCity/TeamCityLogger.php | 6 ++++ src/Plugins/Cache.php | 2 +- .../Parallel/Paratest/WrapperRunner.php | 21 +++++++++++++ .../Parallel/Support/CompactPrinter.php | 10 ++++++ src/Plugins/Tia/TestPaths.php | 4 +-- ...isual_snapshot_of_help_command_output.snap | 31 ++++++++++++------- 9 files changed, 81 insertions(+), 27 deletions(-) diff --git a/composer.json b/composer.json index a9fd8345..8ad33b39 100644 --- a/composer.json +++ b/composer.json @@ -25,12 +25,12 @@ "pestphp/pest-plugin-arch": "^5.0.0", "pestphp/pest-plugin-mutate": "^5.0.0", "pestphp/pest-plugin-profanity": "^5.0.0", - "phpunit/phpunit": "^13.1.8", + "phpunit/phpunit": "^13.2.3", "symfony/process": "^8.1.0" }, "conflict": { "filp/whoops": "<2.18.3", - "phpunit/phpunit": ">13.1.8", + "phpunit/phpunit": ">13.2.3", "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, diff --git a/src/Concerns/Testable.php b/src/Concerns/Testable.php index b978139c..c1c25637 100644 --- a/src/Concerns/Testable.php +++ b/src/Concerns/Testable.php @@ -23,6 +23,8 @@ use PHPUnit\Framework\Attributes\PostCondition; use PHPUnit\Framework\IncompleteTest; use PHPUnit\Framework\SkippedTest; use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestCase\ExceptionExpectation; +use PHPUnit\Framework\TestCase\OutputBuffer; use ReflectionException; use ReflectionFunction; use ReflectionParameter; @@ -431,15 +433,15 @@ trait Testable unset($this->{$property}); } - $hasOutputExpectation = Closure::bind(fn (): bool => is_string($this->outputExpectedString) || is_string($this->outputExpectedRegex), $this, TestCase::class)(); + $outputBuffer = Closure::bind(fn () => $this->outputBuffer, $this, TestCase::class)(); - if ($hasOutputExpectation) { + if ($outputBuffer->hasExpectation()) { ob_clean(); Closure::bind(function (): void { - $this->outputExpectedString = null; - $this->outputExpectedRegex = null; - }, $this, TestCase::class)(); + $this->expectedString = null; + $this->expectedRegularExpression = null; + }, $outputBuffer, OutputBuffer::class)(); } $this->setUp(); @@ -455,7 +457,9 @@ trait Testable */ private function __isExpectedException(Throwable $e): bool { - $read = fn (string $property): mixed => Closure::bind(fn () => $this->{$property}, $this, TestCase::class)(); + $expectation = Closure::bind(fn () => $this->exceptionExpectation, $this, TestCase::class)(); + + $read = fn (string $property): mixed => Closure::bind(fn () => $this->{$property}, $expectation, ExceptionExpectation::class)(); $expectedClass = $read('expectedException'); @@ -463,13 +467,19 @@ trait Testable return $e instanceof $expectedClass; } - $expectedMessage = $read('expectedExceptionMessage'); + $expectedMessage = $read('expectedMessage'); if ($expectedMessage !== null) { return str_contains($e->getMessage(), (string) $expectedMessage); } - $expectedCode = $read('expectedExceptionCode'); + $expectedMessageRegex = $read('expectedMessageRegularExpression'); + + if ($expectedMessageRegex !== null) { + return preg_match($expectedMessageRegex, $e->getMessage()) === 1; + } + + $expectedCode = $read('expectedCode'); if ($expectedCode !== null) { return $e->getCode() === $expectedCode; diff --git a/src/Expectations/OppositeExpectation.php b/src/Expectations/OppositeExpectation.php index 4f99c89f..d1693cb6 100644 --- a/src/Expectations/OppositeExpectation.php +++ b/src/Expectations/OppositeExpectation.php @@ -576,7 +576,7 @@ final readonly class OppositeExpectation return Targeted::make( $original, - fn (ObjectDescription $object): bool => array_all($traits, fn (string $trait): bool => ! (isset($object->reflectionClass) && in_array($trait, $object->reflectionClass->getTraitNames(), true))), + fn (ObjectDescription $object): bool => array_all($traits, fn (string $trait): bool => ! isset($object->reflectionClass) || ! in_array($trait, $object->reflectionClass->getTraitNames(), true)), "not to use traits '".implode("', '", $traits)."'", FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), ); @@ -596,7 +596,7 @@ final readonly class OppositeExpectation return Targeted::make( $original, - fn (ObjectDescription $object): bool => array_all($interfaces, fn (string $interface): bool => ! (isset($object->reflectionClass) && $object->reflectionClass->implementsInterface($interface))), + fn (ObjectDescription $object): bool => array_all($interfaces, fn (string $interface): bool => ! isset($object->reflectionClass) || ! $object->reflectionClass->implementsInterface($interface)), "not to implement '".implode("', '", $interfaces)."'", FileLineFinder::where(fn (string $line): bool => str_contains($line, 'class')), ); diff --git a/src/Logging/TeamCity/TeamCityLogger.php b/src/Logging/TeamCity/TeamCityLogger.php index cbbf0347..e02ce8df 100644 --- a/src/Logging/TeamCity/TeamCityLogger.php +++ b/src/Logging/TeamCity/TeamCityLogger.php @@ -241,6 +241,12 @@ final class TeamCityLogger $telemetry->memoryUsageSinceStart(), $telemetry->durationSincePrevious(), $telemetry->memoryUsageSincePrevious(), + $telemetry->userCpuTimeSinceStart(), + $telemetry->systemCpuTimeSinceStart(), + $telemetry->totalCpuTimeSinceStart(), + $telemetry->userCpuTimeSincePrevious(), + $telemetry->systemCpuTimeSincePrevious(), + $telemetry->totalCpuTimeSincePrevious(), ); } diff --git a/src/Plugins/Cache.php b/src/Plugins/Cache.php index 3ae0433b..b0ff8a6d 100644 --- a/src/Plugins/Cache.php +++ b/src/Plugins/Cache.php @@ -50,7 +50,7 @@ final class Cache implements HandlesArguments } } - if (! $this->hasArgument('--parallel', $arguments)) { + if (! $this->hasArgument('--parallel', $arguments) && ! $this->hasArgument('--do-not-cache-result', $arguments) && ! $this->hasArgument('--cache-result', $arguments)) { return $this->pushArgument('--cache-result', $arguments); } diff --git a/src/Plugins/Parallel/Paratest/WrapperRunner.php b/src/Plugins/Parallel/Paratest/WrapperRunner.php index 900192f6..c271b6ad 100644 --- a/src/Plugins/Parallel/Paratest/WrapperRunner.php +++ b/src/Plugins/Parallel/Paratest/WrapperRunner.php @@ -349,6 +349,20 @@ final class WrapperRunner implements RunnerInterface // @phpstan-ignore-next-line array_merge_recursive($testResultSum->testRunnerTriggeredWarningEvents(), $testResult->testRunnerTriggeredWarningEvents()), // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssueDeprecationEvents(), $testResult->testRunnerTriggeredIssueDeprecationEvents()), + // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssueErrorEvents(), $testResult->testRunnerTriggeredIssueErrorEvents()), + // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssueNoticeEvents(), $testResult->testRunnerTriggeredIssueNoticeEvents()), + // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssuePhpDeprecationEvents(), $testResult->testRunnerTriggeredIssuePhpDeprecationEvents()), + // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssuePhpNoticeEvents(), $testResult->testRunnerTriggeredIssuePhpNoticeEvents()), + // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssuePhpWarningEvents(), $testResult->testRunnerTriggeredIssuePhpWarningEvents()), + // @phpstan-ignore-next-line + array_merge_recursive($testResultSum->testRunnerTriggeredIssueWarningEvents(), $testResult->testRunnerTriggeredIssueWarningEvents()), + // @phpstan-ignore-next-line array_merge_recursive($testResultSum->errors(), $testResult->errors()), // @phpstan-ignore-next-line array_merge_recursive($testResultSum->deprecations(), $testResult->deprecations()), @@ -386,6 +400,13 @@ final class WrapperRunner implements RunnerInterface $testResultSum->testRunnerTriggeredWarningEvents(), fn (WarningTriggered $event): bool => ! str_contains($event->message(), 'No tests found') )), + $testResultSum->testRunnerTriggeredIssueDeprecationEvents(), + $testResultSum->testRunnerTriggeredIssueErrorEvents(), + $testResultSum->testRunnerTriggeredIssueNoticeEvents(), + $testResultSum->testRunnerTriggeredIssuePhpDeprecationEvents(), + $testResultSum->testRunnerTriggeredIssuePhpNoticeEvents(), + $testResultSum->testRunnerTriggeredIssuePhpWarningEvents(), + $testResultSum->testRunnerTriggeredIssueWarningEvents(), $testResultSum->errors(), $testResultSum->deprecations(), $testResultSum->notices(), diff --git a/src/Plugins/Parallel/Support/CompactPrinter.php b/src/Plugins/Parallel/Support/CompactPrinter.php index 2119646e..09b4dcbb 100644 --- a/src/Plugins/Parallel/Support/CompactPrinter.php +++ b/src/Plugins/Parallel/Support/CompactPrinter.php @@ -7,6 +7,7 @@ namespace Pest\Plugins\Parallel\Support; use NunoMaduro\Collision\Adapters\Phpunit\State; use NunoMaduro\Collision\Adapters\Phpunit\Style; use ParaTest\Options; +use PHPUnit\Event\Telemetry\CpuTime; use PHPUnit\Event\Telemetry\GarbageCollectorStatus; use PHPUnit\Event\Telemetry\HRTime; use PHPUnit\Event\Telemetry\Info; @@ -147,11 +148,20 @@ final class CompactPrinter MemoryUsage::fromBytes(0), MemoryUsage::fromBytes(0), $garbageCollectorStatus, + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), ), $telemetryDuration, MemoryUsage::fromBytes(0), \PHPUnit\Event\Telemetry\Duration::fromSecondsAndNanoseconds(0, 0), MemoryUsage::fromBytes(0), + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), + CpuTime::fromSecondsAndNanoseconds(0, 0), ); $this->style->writeRecap($state, $telemetry, $testResult); diff --git a/src/Plugins/Tia/TestPaths.php b/src/Plugins/Tia/TestPaths.php index 8012e067..0b5308da 100644 --- a/src/Plugins/Tia/TestPaths.php +++ b/src/Plugins/Tia/TestPaths.php @@ -47,9 +47,7 @@ final readonly class TestPaths $suffix = $directory->suffix(); - if ($suffix !== '') { - $suffixes[] = str_starts_with($suffix, '.') ? $suffix : '.'.$suffix; - } + $suffixes[] = str_starts_with($suffix, '.') ? $suffix : '.'.$suffix; } foreach ($suite->files() as $file) { diff --git a/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap b/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap index e037b3ed..ebf1d8ec 100644 --- a/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap +++ b/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap @@ -15,6 +15,7 @@ --cache-directory [dir] ............................ Specify cache directory --generate-configuration Generate configuration file with suggested settings --migrate-configuration ....... Migrate configuration file to current format + --validate-configuration ................... Validate XML configuration file --generate-baseline [file] .................... Generate baseline for issues --use-baseline [file] ........................ Use baseline to ignore issues --ignore-baseline ..................... Do not use baseline to ignore issues @@ -40,12 +41,15 @@ --uses [name] ..................... Only run tests that intend to use [name] --requires-php-extension [name] Only run tests that require PHP extension [name] --list-test-files ................................ List available test files + --list-test-ids ........................... List available tests as test IDs --list-tests .......................................... List available tests --list-tests-xml [file] ................. List available tests in XML format --filter [pattern] ............................... Filter which tests to run --exclude-filter [pattern] .. Exclude tests for the specified filter pattern --test-suffix [suffixes] Only search for test in files with specified suffix(es). Default: Test.php,.phpt --test-files-file [file] Only run test files listed in file (one file by line) + --run-test-id [test-id] Only run the test identified by the specified test ID + --test-id-filter-file [file] Only run tests listed by test ID in file (one test ID per line) EXECUTION OPTIONS: --parallel ........................................... Run tests in parallel @@ -54,20 +58,21 @@ --globals-backup ................. Backup and restore $GLOBALS for each test --static-backup ......... Backup and restore static properties for each test --strict-coverage ................... Be strict about code coverage metadata + --require-coverage-contribution Be strict about tests that do not contribute to code coverage --strict-global-state .............. Be strict about changes to global state --disallow-test-output ................. Be strict about output during tests --enforce-time-limit ................. Enforce time limit based on test size --default-time-limit [sec] Timeout in seconds for tests that have no declared size --do-not-report-useless-tests Do not report tests that do not test anything - --stop-on-defect ... Stop after first error, failure, warning, or risky test - --stop-on-error ..................................... Stop after first error - --stop-on-failure ................................. Stop after first failure - --stop-on-warning ................................. Stop after first warning - --stop-on-risky ................................ Stop after first risky test - --stop-on-deprecation ... Stop after first test that triggered a deprecation - --stop-on-notice ............. Stop after first test that triggered a notice - --stop-on-skipped ............................ Stop after first skipped test - --stop-on-incomplete ...................... Stop after first incomplete test + --stop-on-defect[=[n]] Stop after first (or n-th) error, failure, warning, or risky test + --stop-on-error[=[n]] ..................... Stop after first (or n-th) error + --stop-on-failure[=[n]] ................. Stop after first (or n-th) failure + --stop-on-warning[=[n]] ................. Stop after first (or n-th) warning + --stop-on-risky[=[n]] ................ Stop after first (or n-th) risky test + --stop-on-deprecation[=[n]] Stop after first (or n-th) test that triggered a deprecation + --stop-on-notice[=[n]] Stop after first (or n-th) test that triggered a notice + --stop-on-skipped[=[n]] ............ Stop after first (or n-th) skipped test + --stop-on-incomplete[=[n]] ...... Stop after first (or n-th) incomplete test --fail-on-empty-test-suite Signal failure using shell exit code when no tests were run --fail-on-warning Signal failure using shell exit code when a warning was triggered --fail-on-risky Signal failure using shell exit code when a test was considered risky @@ -91,7 +96,7 @@ --do-not-fail-on-incomplete Do not signal failure using shell exit code when a test was marked incomplete --cache-result ............................ Write test results to cache file --do-not-cache-result .............. Do not write test results to cache file - --order-by [order] Run tests in order: default|defects|depends|duration|no-depends|random|reverse|size + --order-by [order] Run tests in order: default|defects|depends|duration-ascending|duration-descending|no-depends|random|reverse|size-ascending|size-descending --resolve-dependencies ...................... Alias for "--order-by depends" --ignore-dependencies .................... Alias for "--order-by no-depends" --random-order ............................... Alias for "--order-by random" @@ -102,6 +107,7 @@ --colors=[flag] ......... Use colors in output ("never", "auto" or "always") --columns [n] ................. Number of columns to use for progress output --columns max ............ Use maximum number of columns for progress output + --diff-context [n] Number of context lines shown around changes in diffs (default: 3) --stderr ................................. Write to STDERR instead of STDOUT --no-progress .................... Disable output of test execution progress --no-results ................................ Disable output of test results @@ -116,6 +122,7 @@ --display-warnings ......... Display details for warnings triggered by tests --display-all-issues ..... Display details for all issues that are triggered --reverse-list .............................. Print defects in reverse order + --compact ... Replace default progress and result output with compact format --teamcity . Replace default progress and result output with TeamCity format --testdox ................ Replace default result output with TestDox format --testdox-summary Repeat TestDox output for tests with errors, failures, or issues @@ -152,8 +159,10 @@ --exclude-source-from-xml-coverage Exclude [source] element from code coverage report in XML format --warm-coverage-cache ........................... Warm static analysis cache --coverage-filter [dir] ........... Include [dir] in code coverage reporting - --path-coverage .......... Report path coverage in addition to line coverage + --branch-coverage ...... Report branch coverage in addition to line coverage + --path-coverage ... Report path coverage in addition to line/branch coverage --disable-coverage-ignore ...... Disable metadata for ignoring code coverage + --disable-coverage-targeting .. Disable metadata for code coverage targeting --no-coverage Ignore code coverage reporting configured in the XML configuration file AI OPTIONS: