From f9b2712a7850696f9b4a2ec8a5ab404a09b213d2 Mon Sep 17 00:00:00 2001 From: Geert Broekmans Date: Mon, 6 Nov 2023 21:39:44 +0100 Subject: [PATCH 1/2] Add ShouldHaveAttribute assertion --- docs/documentation/assertions.md | 3 ++ extension.neon | 6 +++ .../Assertion/Relation/RelationAssertion.php | 5 +- .../ClassAttributeRule.php | 15 ++++++ .../ShouldHaveAttribute.php | 50 +++++++++++++++++++ src/Test/Builder/AssertionStep.php | 8 +++ tests/fixtures/Simple/SimpleAttributeTwo.php | 6 +++ .../ClassAttributeTest.php | 49 ++++++++++++++++++ .../SimpleClassAttributeTest.php | 49 ++++++++++++++++++ 9 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php create mode 100644 src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php create mode 100644 tests/fixtures/Simple/SimpleAttributeTwo.php create mode 100644 tests/unit/rules/ShouldHaveAttribute/ClassAttributeTest.php create mode 100644 tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php diff --git a/docs/documentation/assertions.md b/docs/documentation/assertions.md index 7d5833c4..fad01858 100644 --- a/docs/documentation/assertions.md +++ b/docs/documentation/assertions.md @@ -34,6 +34,9 @@ It asserts that the selected classes **do not depend** on the target classes. ## shouldNotConstruct() It asserts that the selected classes **do not use the constructor** of the target classes. +## shouldHaveAttribute() +It asserts that the selected classes **apply** the target attributes. + ## canOnlyDependOn() It asserts that the selected classes **do not depend** on anything else than the target classes. diff --git a/extension.neon b/extension.neon index 6cfc8e3b..34bb348f 100644 --- a/extension.neon +++ b/extension.neon @@ -218,6 +218,12 @@ services: tags: - phpstan.rules.rule + # ShouldHaveAttribute rules + - + class: PHPat\Rule\Assertion\Relation\ShouldHaveAttribute\ClassAttributeRule + tags: + - phpstan.rules.rule + parametersSchema: phpat: structure([ ignore_doc_comments: bool(), diff --git a/src/Rule/Assertion/Relation/RelationAssertion.php b/src/Rule/Assertion/Relation/RelationAssertion.php index 9cc5e33d..2f696a82 100644 --- a/src/Rule/Assertion/Relation/RelationAssertion.php +++ b/src/Rule/Assertion/Relation/RelationAssertion.php @@ -5,6 +5,7 @@ use PHPat\Configuration; use PHPat\Rule\Assertion\Assertion; use PHPat\Rule\Assertion\Relation\ShouldExtend\ShouldExtend; +use PHPat\Rule\Assertion\Relation\ShouldHaveAttribute\ShouldHaveAttribute; use PHPat\Rule\Assertion\Relation\ShouldImplement\ShouldImplement; use PHPat\Selector\Classname; use PHPat\Selector\SelectorInterface; @@ -97,8 +98,8 @@ protected function ruleApplies(Scope $scope, array $nodes): bool return false; } - // Can not skip if the rule is a ShouldExtend or ShouldImplement rule - if (is_a($this, ShouldExtend::class) || is_a($this, ShouldImplement::class)) { + // Can not skip if the rule is a ShouldExtend, ShouldImplement or ShouldHaveAttribute rule + if (is_a($this, ShouldExtend::class) || is_a($this, ShouldImplement::class) || is_a($this, ShouldHaveAttribute::class)) { return true; } diff --git a/src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php b/src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php new file mode 100644 index 00000000..bca0322e --- /dev/null +++ b/src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php @@ -0,0 +1,15 @@ + + */ +final class ClassAttributeRule extends ShouldHaveAttribute implements Rule +{ + use ClassAttributeExtractor; +} diff --git a/src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php b/src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php new file mode 100644 index 00000000..34a7654e --- /dev/null +++ b/src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php @@ -0,0 +1,50 @@ +applyShould($ruleName, $subject, $targets, $targetExcludes, $nodes, $tips); + } + + protected function getMessage(string $ruleName, string $subject, string $target): string + { + return $this->prepareMessage( + $ruleName, + sprintf('%s should have attribute %s', $subject, $target), + ); + } +} diff --git a/src/Test/Builder/AssertionStep.php b/src/Test/Builder/AssertionStep.php index a84cdbfe..fb2000f6 100644 --- a/src/Test/Builder/AssertionStep.php +++ b/src/Test/Builder/AssertionStep.php @@ -10,6 +10,7 @@ use PHPat\Rule\Assertion\Declaration\ShouldNotBeFinal\ShouldNotBeFinal; use PHPat\Rule\Assertion\Relation\CanOnlyDepend\CanOnlyDepend; use PHPat\Rule\Assertion\Relation\ShouldExtend\ShouldExtend; +use PHPat\Rule\Assertion\Relation\ShouldHaveAttribute\ShouldHaveAttribute; use PHPat\Rule\Assertion\Relation\ShouldImplement\ShouldImplement; use PHPat\Rule\Assertion\Relation\ShouldNotConstruct\ShouldNotConstruct; use PHPat\Rule\Assertion\Relation\ShouldNotDepend\ShouldNotDepend; @@ -108,4 +109,11 @@ public function shouldHaveOnlyOnePublicMethod(): Rule return new BuildStep($this->rule); } + + public function shouldHaveAttribute(): TargetStep + { + $this->rule->assertion = ShouldHaveAttribute::class; + + return new TargetStep($this->rule); + } } diff --git a/tests/fixtures/Simple/SimpleAttributeTwo.php b/tests/fixtures/Simple/SimpleAttributeTwo.php new file mode 100644 index 00000000..fc86e12c --- /dev/null +++ b/tests/fixtures/Simple/SimpleAttributeTwo.php @@ -0,0 +1,6 @@ + + * @internal + * @coversNothing + */ +class ClassAttributeTest extends RuleTestCase +{ + public const RULE_NAME = 'test_FixtureClassShouldHaveSimpleAttributeTwo'; + + public function testRule(): void + { + $this->analyse(['tests/fixtures/FixtureClass.php'], [ + [sprintf('%s should have attribute %s', FixtureClass::class, SimpleAttributeTwo::class), 29], + ]); + } + + protected function getRule(): Rule + { + $testParser = FakeTestParser::create( + self::RULE_NAME, + ShouldHaveAttribute::class, + [new Classname(FixtureClass::class, false)], + [new Classname(SimpleAttributeTwo::class, false)] + ); + + return new ClassAttributeRule( + new StatementBuilderFactory($testParser), + new Configuration(false, true, false), + $this->createReflectionProvider(), + self::getContainer()->getByType(FileTypeMapper::class) + ); + } +} diff --git a/tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php b/tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php new file mode 100644 index 00000000..6f0054a5 --- /dev/null +++ b/tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php @@ -0,0 +1,49 @@ + + * @internal + * @coversNothing + */ +class SimpleClassAttributeTest extends RuleTestCase +{ + public const RULE_NAME = 'test_SimpleClassShouldHaveSimpleAttribute'; + + public function testRule(): void + { + $this->analyse(['tests/fixtures/Simple/SimpleClass.php'], [ + [sprintf('%s should have attribute %s', SimpleClass::class, SimpleAttribute::class), 5], + ]); + } + + protected function getRule(): Rule + { + $testParser = FakeTestParser::create( + self::RULE_NAME, + ShouldHaveAttribute::class, + [new Classname(SimpleClass::class, false)], + [new Classname(SimpleAttribute::class, false)] + ); + + return new ClassAttributeRule( + new StatementBuilderFactory($testParser), + new Configuration(false, true, false), + $this->createReflectionProvider(), + self::getContainer()->getByType(FileTypeMapper::class) + ); + } +} From 03a6c5b0cec52025b5b28aa867ec02d39a24598f Mon Sep 17 00:00:00 2001 From: Geert Broekmans Date: Fri, 10 Nov 2023 21:08:16 +0100 Subject: [PATCH 2/2] Rephrase from "should have" to "should apply" --- docs/documentation/assertions.md | 2 +- extension.neon | 4 ++-- src/Rule/Assertion/Relation/RelationAssertion.php | 6 +++--- .../ClassAttributeRule.php | 4 ++-- .../ShouldApplyAttribute.php} | 6 +++--- src/Test/Builder/AssertionStep.php | 6 +++--- .../ClassAttributeTest.php | 12 ++++++------ .../SimpleClassAttributeTest.php | 12 ++++++------ 8 files changed, 26 insertions(+), 26 deletions(-) rename src/Rule/Assertion/Relation/{ShouldHaveAttribute => ShouldApplyAttribute}/ClassAttributeRule.php (61%) rename src/Rule/Assertion/Relation/{ShouldHaveAttribute/ShouldHaveAttribute.php => ShouldApplyAttribute/ShouldApplyAttribute.php} (86%) rename tests/unit/rules/{ShouldHaveAttribute => ShouldApplyAttribute}/ClassAttributeTest.php (72%) rename tests/unit/rules/{ShouldHaveAttribute => ShouldApplyAttribute}/SimpleClassAttributeTest.php (72%) diff --git a/docs/documentation/assertions.md b/docs/documentation/assertions.md index fad01858..8eb7bf70 100644 --- a/docs/documentation/assertions.md +++ b/docs/documentation/assertions.md @@ -34,7 +34,7 @@ It asserts that the selected classes **do not depend** on the target classes. ## shouldNotConstruct() It asserts that the selected classes **do not use the constructor** of the target classes. -## shouldHaveAttribute() +## shouldApplyAttribute() It asserts that the selected classes **apply** the target attributes. ## canOnlyDependOn() diff --git a/extension.neon b/extension.neon index 34bb348f..4fa08b47 100644 --- a/extension.neon +++ b/extension.neon @@ -218,9 +218,9 @@ services: tags: - phpstan.rules.rule - # ShouldHaveAttribute rules + # ShouldApplyAttribute rules - - class: PHPat\Rule\Assertion\Relation\ShouldHaveAttribute\ClassAttributeRule + class: PHPat\Rule\Assertion\Relation\ShouldApplyAttribute\ClassAttributeRule tags: - phpstan.rules.rule diff --git a/src/Rule/Assertion/Relation/RelationAssertion.php b/src/Rule/Assertion/Relation/RelationAssertion.php index 2f696a82..b4d9075c 100644 --- a/src/Rule/Assertion/Relation/RelationAssertion.php +++ b/src/Rule/Assertion/Relation/RelationAssertion.php @@ -4,8 +4,8 @@ use PHPat\Configuration; use PHPat\Rule\Assertion\Assertion; +use PHPat\Rule\Assertion\Relation\ShouldApplyAttribute\ShouldApplyAttribute; use PHPat\Rule\Assertion\Relation\ShouldExtend\ShouldExtend; -use PHPat\Rule\Assertion\Relation\ShouldHaveAttribute\ShouldHaveAttribute; use PHPat\Rule\Assertion\Relation\ShouldImplement\ShouldImplement; use PHPat\Selector\Classname; use PHPat\Selector\SelectorInterface; @@ -98,8 +98,8 @@ protected function ruleApplies(Scope $scope, array $nodes): bool return false; } - // Can not skip if the rule is a ShouldExtend, ShouldImplement or ShouldHaveAttribute rule - if (is_a($this, ShouldExtend::class) || is_a($this, ShouldImplement::class) || is_a($this, ShouldHaveAttribute::class)) { + // Can not skip if the rule is a ShouldExtend, ShouldImplement or ShouldApplyAttribute rule + if (is_a($this, ShouldExtend::class) || is_a($this, ShouldImplement::class) || is_a($this, ShouldApplyAttribute::class)) { return true; } diff --git a/src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php b/src/Rule/Assertion/Relation/ShouldApplyAttribute/ClassAttributeRule.php similarity index 61% rename from src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php rename to src/Rule/Assertion/Relation/ShouldApplyAttribute/ClassAttributeRule.php index bca0322e..87e2b22a 100644 --- a/src/Rule/Assertion/Relation/ShouldHaveAttribute/ClassAttributeRule.php +++ b/src/Rule/Assertion/Relation/ShouldApplyAttribute/ClassAttributeRule.php @@ -1,6 +1,6 @@ */ -final class ClassAttributeRule extends ShouldHaveAttribute implements Rule +final class ClassAttributeRule extends ShouldApplyAttribute implements Rule { use ClassAttributeExtractor; } diff --git a/src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php b/src/Rule/Assertion/Relation/ShouldApplyAttribute/ShouldApplyAttribute.php similarity index 86% rename from src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php rename to src/Rule/Assertion/Relation/ShouldApplyAttribute/ShouldApplyAttribute.php index 34a7654e..8a500b29 100644 --- a/src/Rule/Assertion/Relation/ShouldHaveAttribute/ShouldHaveAttribute.php +++ b/src/Rule/Assertion/Relation/ShouldApplyAttribute/ShouldApplyAttribute.php @@ -1,6 +1,6 @@ prepareMessage( $ruleName, - sprintf('%s should have attribute %s', $subject, $target), + sprintf('%s should apply the attribute %s', $subject, $target), ); } } diff --git a/src/Test/Builder/AssertionStep.php b/src/Test/Builder/AssertionStep.php index fb2000f6..292e4ebc 100644 --- a/src/Test/Builder/AssertionStep.php +++ b/src/Test/Builder/AssertionStep.php @@ -9,8 +9,8 @@ use PHPat\Rule\Assertion\Declaration\ShouldNotBeAbstract\ShouldNotBeAbstract; use PHPat\Rule\Assertion\Declaration\ShouldNotBeFinal\ShouldNotBeFinal; use PHPat\Rule\Assertion\Relation\CanOnlyDepend\CanOnlyDepend; +use PHPat\Rule\Assertion\Relation\ShouldApplyAttribute\ShouldApplyAttribute; use PHPat\Rule\Assertion\Relation\ShouldExtend\ShouldExtend; -use PHPat\Rule\Assertion\Relation\ShouldHaveAttribute\ShouldHaveAttribute; use PHPat\Rule\Assertion\Relation\ShouldImplement\ShouldImplement; use PHPat\Rule\Assertion\Relation\ShouldNotConstruct\ShouldNotConstruct; use PHPat\Rule\Assertion\Relation\ShouldNotDepend\ShouldNotDepend; @@ -110,9 +110,9 @@ public function shouldHaveOnlyOnePublicMethod(): Rule return new BuildStep($this->rule); } - public function shouldHaveAttribute(): TargetStep + public function shouldApplyAttribute(): TargetStep { - $this->rule->assertion = ShouldHaveAttribute::class; + $this->rule->assertion = ShouldApplyAttribute::class; return new TargetStep($this->rule); } diff --git a/tests/unit/rules/ShouldHaveAttribute/ClassAttributeTest.php b/tests/unit/rules/ShouldApplyAttribute/ClassAttributeTest.php similarity index 72% rename from tests/unit/rules/ShouldHaveAttribute/ClassAttributeTest.php rename to tests/unit/rules/ShouldApplyAttribute/ClassAttributeTest.php index 16e1208e..0f03ca3f 100644 --- a/tests/unit/rules/ShouldHaveAttribute/ClassAttributeTest.php +++ b/tests/unit/rules/ShouldApplyAttribute/ClassAttributeTest.php @@ -1,10 +1,10 @@ analyse(['tests/fixtures/FixtureClass.php'], [ - [sprintf('%s should have attribute %s', FixtureClass::class, SimpleAttributeTwo::class), 29], + [sprintf('%s should apply the attribute %s', FixtureClass::class, SimpleAttributeTwo::class), 29], ]); } @@ -34,7 +34,7 @@ protected function getRule(): Rule { $testParser = FakeTestParser::create( self::RULE_NAME, - ShouldHaveAttribute::class, + ShouldApplyAttribute::class, [new Classname(FixtureClass::class, false)], [new Classname(SimpleAttributeTwo::class, false)] ); diff --git a/tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php b/tests/unit/rules/ShouldApplyAttribute/SimpleClassAttributeTest.php similarity index 72% rename from tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php rename to tests/unit/rules/ShouldApplyAttribute/SimpleClassAttributeTest.php index 6f0054a5..c43c9f45 100644 --- a/tests/unit/rules/ShouldHaveAttribute/SimpleClassAttributeTest.php +++ b/tests/unit/rules/ShouldApplyAttribute/SimpleClassAttributeTest.php @@ -1,10 +1,10 @@ analyse(['tests/fixtures/Simple/SimpleClass.php'], [ - [sprintf('%s should have attribute %s', SimpleClass::class, SimpleAttribute::class), 5], + [sprintf('%s should apply the attribute %s', SimpleClass::class, SimpleAttribute::class), 5], ]); } @@ -34,7 +34,7 @@ protected function getRule(): Rule { $testParser = FakeTestParser::create( self::RULE_NAME, - ShouldHaveAttribute::class, + ShouldApplyAttribute::class, [new Classname(SimpleClass::class, false)], [new Classname(SimpleAttribute::class, false)] );