diff --git a/data/Controller/ValidReadonlyController.php b/data/Controller/ValidReadonlyController.php new file mode 100644 index 0000000..71e3c7e --- /dev/null +++ b/data/Controller/ValidReadonlyController.php @@ -0,0 +1,11 @@ + + */ +class ClassMustBeFinalRuleWithAbstractNotIgnoredTest extends RuleTestCase +{ + protected function getRule(): \PHPStan\Rules\Rule + { + // Do NOT ignore abstract classes + return new ClassMustBeFinalRule( + patterns: ['/Service$/'], + ignoreAbstractClasses: false + ); + } + + public function testConcreteClassMustBeFinal(): void + { + // Test that concrete (non-abstract) classes are checked when ignoreAbstractClasses is false + // This ensures the flag doesn't prevent checking of regular classes + $this->analyse([__DIR__ . '/../../../data/Service/MissingFinalRuleService.php'], [ + [ + 'Class App\Service\MissingFinalRuleService must be final.', + 5, + ], + ]); + } +} + diff --git a/tests/TestCases/Architecture/ClassMustBeReadonlyRuleTest.php b/tests/TestCases/Architecture/ClassMustBeReadonlyRuleTest.php index 18d9511..24076f6 100644 --- a/tests/TestCases/Architecture/ClassMustBeReadonlyRuleTest.php +++ b/tests/TestCases/Architecture/ClassMustBeReadonlyRuleTest.php @@ -34,4 +34,10 @@ public function testRule(): void // the test fails, if the expected error does not occur, // or if there are other errors reported beside the expected one } + + public function testValidReadonlyClass(): void + { + // Test that readonly classes don't trigger errors + $this->analyse([__DIR__ . '/../../../data/Controller/ValidReadonlyController.php'], []); + } } diff --git a/tests/TestCases/Architecture/MethodSignatureMustMatchRuleTest.php b/tests/TestCases/Architecture/MethodSignatureMustMatchRuleTest.php index 3605760..b7239dc 100644 --- a/tests/TestCases/Architecture/MethodSignatureMustMatchRuleTest.php +++ b/tests/TestCases/Architecture/MethodSignatureMustMatchRuleTest.php @@ -46,6 +46,71 @@ protected function getRule(): Rule ], 'visibilityScope' => 'public', ], + [ + 'pattern' => '/^TestClass::testMaxParams$/', + 'minParameters' => 1, + 'maxParameters' => 2, + 'signature' => [], + 'visibilityScope' => 'public', + ], + [ + 'pattern' => '/^TestClass::testNameMismatch$/', + 'minParameters' => 2, + 'maxParameters' => 2, + 'signature' => [ + ['type' => 'int', 'pattern' => '/^param/'], // Name pattern doesn't match + ['type' => 'string', 'pattern' => '/^param/'], // Name pattern doesn't match + ], + 'visibilityScope' => 'public', + ], + [ + 'pattern' => '/^TestClass::testNullableTypes$/', + 'minParameters' => 2, + 'maxParameters' => 2, + 'signature' => [ + ['type' => '?int', 'pattern' => '/^nullable/'], + ['type' => '?string', 'pattern' => '/^nullable/'], + ], + 'visibilityScope' => 'public', + ], + [ + 'pattern' => '/^TestClass::testClassTypes$/', + 'minParameters' => 2, + 'maxParameters' => 2, + 'signature' => [ + ['type' => 'DummyClass', 'pattern' => '/^dummy/'], + ['type' => 'string', 'pattern' => '/^name/'], + ], + 'visibilityScope' => 'public', + ], + [ + 'pattern' => '/^TestClass::testProtectedMethod$/', + 'minParameters' => 1, + 'maxParameters' => 1, + 'signature' => [ + ['type' => 'int', 'pattern' => '/^value/'], + ], + 'visibilityScope' => 'protected', + ], + [ + 'pattern' => '/^TestClass::testNoVisibilityReq$/', + 'minParameters' => 1, + 'maxParameters' => 1, + 'signature' => [ + ['type' => 'int', 'pattern' => '/^x/'], + ], + // No visibilityScope specified + ], + [ + 'pattern' => '/^TestClass::testValidMethod$/', + 'minParameters' => 2, + 'maxParameters' => 2, + 'signature' => [ + ['type' => 'int', 'pattern' => '/^alpha/'], + ['type' => 'string', 'pattern' => '/^beta/'], + ], + 'visibilityScope' => 'public', + ], ]); } @@ -55,15 +120,15 @@ public function testRule(): void // Errors for testMethod (type checking enabled) [ 'Method TestClass::testMethod has 1 parameters, but at least 2 required.', - 5, + 9, ], [ 'Method TestClass::testMethod is missing parameter #2 of type string.', - 5, + 9, ], [ 'Method TestClass::testMethod must be private.', - 5, + 9, ], // No errors for testMethodNoType since: // - First parameter has no type specified, so type checking is skipped @@ -76,8 +141,34 @@ public function testRule(): void // - Second parameter has no type specified, so type checking is skipped [ 'Method TestClass::testMethodWithWrongType parameter #1 should be of type string, int given.', - 13, + 17, + ], + + // Errors for testMaxParams - exceeds max parameters + [ + 'Method TestClass::testMaxParams has 4 parameters, but at most 2 allowed.', + 22, + ], + + // Errors for testNameMismatch - parameter names don't match patterns + [ + 'Method TestClass::testNameMismatch parameter #1 name "wrongName" does not match pattern /^param/.', + 27, + ], + [ + 'Method TestClass::testNameMismatch parameter #2 name "anotherWrong" does not match pattern /^param/.', + 27, ], + + // No errors for testNullableTypes - nullable types should match correctly + + // No errors for testClassTypes - class types should match correctly + + // No errors for testProtectedMethod - protected visibility matches + + // No errors for testNoVisibilityReq - no visibility requirement specified + + // No errors for testValidMethod - everything matches correctly ]); } } diff --git a/tests/TestCases/CleanCode/MaxLineLengthRuleIgnoreUseTest.php b/tests/TestCases/CleanCode/MaxLineLengthRuleIgnoreUseTest.php new file mode 100644 index 0000000..286d796 --- /dev/null +++ b/tests/TestCases/CleanCode/MaxLineLengthRuleIgnoreUseTest.php @@ -0,0 +1,28 @@ + + */ +class MaxLineLengthRuleIgnoreUseTest extends RuleTestCase +{ + protected function getRule(): Rule + { + // Ignore use statements (3rd parameter is true) + return new MaxLineLengthRule(80, [], true); + } + + public function testUseStatementsAreIgnored(): void + { + // All use statements in this file are very long, but should be ignored + $this->analyse([__DIR__ . '/../../../data/MaxLineLengthUseStatementsClass.php'], []); + } +} + diff --git a/tests/TestCases/CleanCode/MaxLineLengthRuleTest.php b/tests/TestCases/CleanCode/MaxLineLengthRuleTest.php index bcc78e2..25d9a84 100644 --- a/tests/TestCases/CleanCode/MaxLineLengthRuleTest.php +++ b/tests/TestCases/CleanCode/MaxLineLengthRuleTest.php @@ -28,28 +28,18 @@ public function testRule(): void ]); } - public function testRuleWithExcludePatterns(): void + public function testMultipleLongLinesInFile(): void { - $rule = new MaxLineLengthRule(80, ['/.*Excluded.*/']); - - $this->analyse([__DIR__ . '/../../../data/MaxLineLengthExcludedClass.php'], [ + // Test that multiple long lines in the same file are all detected + $this->analyse([__DIR__ . '/../../../data/MaxLineLengthMultipleLines.php'], [ [ - 'Line 7 exceeds the maximum length of 80 characters (found 81 characters).', - 7, + 'Line 5 exceeds the maximum length of 80 characters (found 115 characters).', + 5, ], [ - 'Line 9 exceeds the maximum length of 80 characters (found 86 characters).', - 9, + 'Line 7 exceeds the maximum length of 80 characters (found 110 characters).', + 7, ], ]); } - - - - public function testRuleWithIgnoreUseStatements(): void - { - $rule = new MaxLineLengthRule(80, [], true); - - $this->analyse([__DIR__ . '/../../../data/MaxLineLengthUseStatementsClass.php'], []); - } } diff --git a/tests/TestCases/CleanCode/MaxLineLengthRuleWithExclusionTest.php b/tests/TestCases/CleanCode/MaxLineLengthRuleWithExclusionTest.php new file mode 100644 index 0000000..1573329 --- /dev/null +++ b/tests/TestCases/CleanCode/MaxLineLengthRuleWithExclusionTest.php @@ -0,0 +1,39 @@ + + */ +class MaxLineLengthRuleWithExclusionTest extends RuleTestCase +{ + protected function getRule(): Rule + { + // Exclude files matching "Excluded" in their path + return new MaxLineLengthRule(80, ['/.*Excluded.*/']); + } + + public function testExcludedFileIsNotChecked(): void + { + // This file should be excluded, so no errors should be reported + $this->analyse([__DIR__ . '/../../../data/MaxLineLengthExcludedClass.php'], []); + } + + public function testNonExcludedFileIsChecked(): void + { + // This file is NOT excluded, so errors should be reported + $this->analyse([__DIR__ . '/../../../data/MaxLineLengthTestClass.php'], [ + [ + 'Line 7 exceeds the maximum length of 80 characters (found 84 characters).', + 7, + ], + ]); + } +} + diff --git a/tests/TestCases/CleanCode/TooManyArgumentsRuleTest.php b/tests/TestCases/CleanCode/TooManyArgumentsRuleTest.php index 36aca39..2334830 100644 --- a/tests/TestCases/CleanCode/TooManyArgumentsRuleTest.php +++ b/tests/TestCases/CleanCode/TooManyArgumentsRuleTest.php @@ -25,6 +25,14 @@ public function testRule(): void 'Method App\TooManyArgumentsClass::methodWithTooManyArguments has too many arguments (4). Maximum allowed is 3.', 7, ], + [ + 'Method App\Service\TooManyArgsService::methodWithTooManyArguments has too many arguments (5). Maximum allowed is 3.', + 22, + ], + [ + 'Method App\Other\TooManyArgsOther::methodWithTooManyArguments has too many arguments (5). Maximum allowed is 3.', + 32, + ], ]); } } diff --git a/tests/TestCases/CleanCode/TooManyArgumentsRuleWithPatternsTest.php b/tests/TestCases/CleanCode/TooManyArgumentsRuleWithPatternsTest.php new file mode 100644 index 0000000..75124f9 --- /dev/null +++ b/tests/TestCases/CleanCode/TooManyArgumentsRuleWithPatternsTest.php @@ -0,0 +1,33 @@ + + */ +class TooManyArgumentsRuleWithPatternsTest extends RuleTestCase +{ + protected function getRule(): Rule + { + // Only apply to classes ending with "Service" + return new TooManyArgumentsRule(3, ['/Service$/']); + } + + public function testRuleWithPatterns(): void + { + $this->analyse([__DIR__ . '/../../../data/TooManyArgumentsClass.php'], [ + [ + 'Method App\Service\TooManyArgsService::methodWithTooManyArguments has too many arguments (5). Maximum allowed is 3.', + 22, + ], + // TooManyArgumentsClass and TooManyArgsOther should NOT trigger errors because patterns don't match + ]); + } +} +