From 980b967618338628feebe4a39aba1d71c9c802b2 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 14 Nov 2026 18:02:15 +0100 Subject: [PATCH 1/2] [PHPUnit60] Skip test methods in abstract classes in AddDoesNotPerformAssertionToNonAssertingTestRector --- ...nherited_test_with_asserting_child.php.inc | 21 +++++++++++++++++++ .../skip_abstract_class_test_method.php.inc | 14 +++++++++++++ ...rformAssertionToNonAssertingTestRector.php | 15 +++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_inherited_test_with_asserting_child.php.inc create mode 100644 rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_test_method.php.inc diff --git a/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_inherited_test_with_asserting_child.php.inc b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_inherited_test_with_asserting_child.php.inc new file mode 100644 index 00000000..3a993b19 --- /dev/null +++ b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_inherited_test_with_asserting_child.php.inc @@ -0,0 +1,21 @@ +assertTrue(true); + } +} diff --git a/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_test_method.php.inc b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_test_method.php.inc new file mode 100644 index 00000000..a3b05170 --- /dev/null +++ b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/skip_abstract_class_test_method.php.inc @@ -0,0 +1,14 @@ +isInAbstractClass($classMethod)) { + return true; + } + // the parent test case asserts in its own integration methods if ($this->isInTwigIntegrationTestCase($classMethod)) { return true; @@ -220,6 +225,16 @@ private function isInTrait(ClassMethod $classMethod): bool return $classReflection->isTrait(); } + private function isInAbstractClass(ClassMethod $classMethod): bool + { + $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $classReflection->isAbstract(); + } + private function isInTwigIntegrationTestCase(ClassMethod $classMethod): bool { $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod); From 9f32f07010b2ae21d81f26df89f8e8fb1e97cca8 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 14 Nov 2026 18:05:24 +0100 Subject: [PATCH 2/2] [bench] Add AddDoesNotPerformAssertionToNonAssertingTestRector bench on abstract test classes --- .github/workflows/code_analysis.yaml | 4 + ...perform_assertion_abstract_class_bench.php | 106 ++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 bench/does_not_perform_assertion_abstract_class_bench.php diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index abf35b6d..5a7c5c1e 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -40,6 +40,10 @@ jobs: name: 'Bench' run: php bench/does_not_perform_assertion_lifecycle_bench.php + - + name: 'Bench abstract classes' + run: php bench/does_not_perform_assertion_abstract_class_bench.php + name: ${{ matrix.actions.name }} runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/bench/does_not_perform_assertion_abstract_class_bench.php b/bench/does_not_perform_assertion_abstract_class_bench.php new file mode 100644 index 00000000..364392c4 --- /dev/null +++ b/bench/does_not_perform_assertion_abstract_class_bench.php @@ -0,0 +1,106 @@ +create(); +$rectorConfig->boot(); + +/** @var TestingParser $testingParser */ +$testingParser = $rectorConfig->make(TestingParser::class); + +/** @var BetterNodeFinder $betterNodeFinder */ +$betterNodeFinder = $rectorConfig->make(BetterNodeFinder::class); + +$rector = $rectorConfig->make(AddDoesNotPerformAssertionToNonAssertingTestRector::class); + +$classMethods = []; +foreach ($testFilePaths as $testFilePath) { + $stmts = $testingParser->parseFileToDecoratedNodes($testFilePath); + $classMethods = [...$classMethods, ...$betterNodeFinder->findInstanceOf($stmts, ClassMethod::class)]; +} + +$cpuSeconds = static function (): float { + $usage = getrusage(); + return $usage['ru_utime.tv_sec'] + $usage['ru_utime.tv_usec'] / 1_000_000 + + $usage['ru_stime.tv_sec'] + $usage['ru_stime.tv_usec'] / 1_000_000; +}; + +$wallStart = microtime(true); +$cpuStart = $cpuSeconds(); + +$changedCount = 0; +foreach ($classMethods as $classMethod) { + if ($rector->refactor($classMethod) instanceof ClassMethod) { + ++$changedCount; + } +} + +$cpu = $cpuSeconds() - $cpuStart; +$wall = microtime(true) - $wallStart; + +array_map('unlink', glob($directory . '/*.php')); +@rmdir($directory); + +printf('test classes: %d, test methods: %d, changed: %d%s', count($testFilePaths), count($classMethods), $changedCount, PHP_EOL); +printf('CPU: %.4f s (%.3f ms/method)%s', $cpu, $cpu / count($classMethods) * 1000, PHP_EOL); +printf('wall: %.4f s%s', $wall, PHP_EOL); + +if ($cpu > MAX_CPU_SECONDS) { + printf('CPU %.4f s is over the %.1f s limit%s', $cpu, MAX_CPU_SECONDS, PHP_EOL); + exit(1); +}