diff --git a/src/Expression/ForClasses/IsA.php b/src/Expression/ForClasses/IsA.php index bda51c46..44f41176 100644 --- a/src/Expression/ForClasses/IsA.php +++ b/src/Expression/ForClasses/IsA.php @@ -26,7 +26,7 @@ public function __construct(string $allowedFqcn) public function describe(ClassDescription $theClass, string $because = ''): Description { - return new Description("should inherit from: $this->allowedFqcn", $because); + return new Description("{$theClass->getName()} should be a $this->allowedFqcn", $because); } public function evaluate(ClassDescription $theClass, Violations $violations, string $because = ''): void diff --git a/src/Expression/ForClasses/IsNotA.php b/src/Expression/ForClasses/IsNotA.php new file mode 100644 index 00000000..04c9c31b --- /dev/null +++ b/src/Expression/ForClasses/IsNotA.php @@ -0,0 +1,44 @@ +disallowedFqcn = $disallowedFqcn; + } + + public function describe(ClassDescription $theClass, string $because = ''): Description + { + return new Description("{$theClass->getName()} should not be a $this->disallowedFqcn", $because); + } + + public function evaluate(ClassDescription $theClass, Violations $violations, string $because = ''): void + { + if (is_a($theClass->getFQCN(), $this->disallowedFqcn, true)) { + $violation = Violation::create( + $theClass->getFQCN(), + ViolationMessage::selfExplanatory($this->describe($theClass, $because)), + $theClass->getFilePath() + ); + + $violations->add($violation); + } + } +} diff --git a/tests/Unit/Expressions/ForClasses/IsATest.php b/tests/Unit/Expressions/ForClasses/IsATest.php index cac51400..cff0bcaa 100644 --- a/tests/Unit/Expressions/ForClasses/IsATest.php +++ b/tests/Unit/Expressions/ForClasses/IsATest.php @@ -7,11 +7,11 @@ use Arkitect\Analyzer\ClassDescriptionBuilder; use Arkitect\Expression\ForClasses\IsA; use Arkitect\Rules\Violations; -use Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Animal\Dog; -use Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Fruit\Banana; -use Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Fruit\CavendishBanana; -use Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Fruit\DwarfCavendishBanana; -use Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Fruit\FruitInterface; +use Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Animal\Dog; +use Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Fruit\Banana; +use Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Fruit\CavendishBanana; +use Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Fruit\DwarfCavendishBanana; +use Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Fruit\FruitInterface; use PHPUnit\Framework\TestCase; final class IsATest extends TestCase @@ -48,7 +48,7 @@ public function test_it_should_have_no_violation_when_it_extends(): void self::assertEquals(0, $violations->count()); } - public function test_it_should_have_violation_if_it_doesnt_extend(): void + public function test_it_should_have_violation_when_it_doesnt_extend(): void { $interface = FruitInterface::class; $isA = new IsA($interface); @@ -62,12 +62,12 @@ public function test_it_should_have_violation_if_it_doesnt_extend(): void self::assertEquals(1, $violations->count()); self::assertEquals( - "should inherit from: $interface", + "Dog should be a $interface", $isA->describe($classDescription, '')->toString() ); } - public function test_it_should_have_violation_if_it_doesnt_implement(): void + public function test_it_should_have_violation_when_it_doesnt_implement(): void { $class = Banana::class; $isA = new IsA($class); @@ -81,19 +81,19 @@ public function test_it_should_have_violation_if_it_doesnt_implement(): void self::assertEquals(1, $violations->count()); self::assertEquals( - "should inherit from: $class", + "Dog should be a $class", $isA->describe($classDescription, '')->toString() ); } } -namespace Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Animal; +namespace Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Animal; final class Dog { } -namespace Arkitect\Tests\Unit\Expressions\ForClasses\IsOneOfTest\Fruit; +namespace Arkitect\Tests\Unit\Expressions\ForClasses\IsATest\Fruit; interface FruitInterface { diff --git a/tests/Unit/Expressions/ForClasses/IsNotATest.php b/tests/Unit/Expressions/ForClasses/IsNotATest.php new file mode 100644 index 00000000..af7d470d --- /dev/null +++ b/tests/Unit/Expressions/ForClasses/IsNotATest.php @@ -0,0 +1,112 @@ +setFilePath('src/Foo.php') + ->setClassName(Dog::class) + ->build(); + + $violations = new Violations(); + $isNotA->evaluate($classDescription, $violations, ''); + + self::assertEquals(0, $violations->count()); + } + + public function test_it_should_have_no_violation_when_it_doesnt_implement(): void + { + $class = Banana::class; + $isNotA = new IsNotA($class); + $classDescription = (new ClassDescriptionBuilder()) + ->setFilePath('src/Foo.php') + ->setClassName(Dog::class) + ->build(); + + $violations = new Violations(); + $isNotA->evaluate($classDescription, $violations, ''); + + self::assertEquals(0, $violations->count()); + } + + public function test_it_should_have_violation_when_it_implements(): void + { + $interface = FruitInterface::class; + $isNotA = new IsNotA($interface); + $classDescription = (new ClassDescriptionBuilder()) + ->setFilePath('src/Foo.php') + ->setClassName(CavendishBanana::class) + ->addInterface($interface, 10) + ->build(); + + $violations = new Violations(); + $isNotA->evaluate($classDescription, $violations, ''); + + self::assertEquals(1, $violations->count()); + self::assertEquals( + "CavendishBanana should not be a $interface", + $isNotA->describe($classDescription, '')->toString() + ); + } + + public function test_it_should_have_violation_when_it_extends(): void + { + $class = Banana::class; + $isNotA = new IsNotA($class); + $classDescription = (new ClassDescriptionBuilder()) + ->setFilePath('src/Foo.php') + ->setClassName(DwarfCavendishBanana::class) + ->addExtends($class, 10) + ->build(); + + $violations = new Violations(); + $isNotA->evaluate($classDescription, $violations, ''); + + self::assertEquals(1, $violations->count()); + self::assertEquals( + "DwarfCavendishBanana should not be a $class", + $isNotA->describe($classDescription, '')->toString() + ); + } +} + +namespace Arkitect\Tests\Unit\Expressions\ForClasses\IsNotA\Animal; + +final class Dog +{ +} + +namespace Arkitect\Tests\Unit\Expressions\ForClasses\IsNotA\Fruit; + +interface FruitInterface +{ +} + +class Banana implements FruitInterface +{ +} + +class CavendishBanana extends Banana +{ +} + +final class DwarfCavendishBanana extends CavendishBanana +{ +}