Skip to content

Commit 646c6f6

Browse files
fixes implements and extends rules
1 parent 92dad93 commit 646c6f6

File tree

6 files changed

+181
-3
lines changed

6 files changed

+181
-3
lines changed

src/Analyzer/FileVisitor.php

+5
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public function enterNode(Node $node): void
6363
$this->classDescriptionBuilder->setClassName($node->namespacedName->toCodeString());
6464
$this->classDescriptionBuilder->setEnum(true);
6565

66+
foreach ($node->implements as $interface) {
67+
$this->classDescriptionBuilder
68+
->addInterface($interface->toString(), $interface->getLine());
69+
}
70+
6671
foreach ($node->attrGroups as $attributeGroup) {
6772
foreach ($attributeGroup->attrs as $attribute) {
6873
$this->classDescriptionBuilder

src/Expression/ForClasses/Implement.php

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public function describe(ClassDescription $theClass, string $because): Descripti
2727
return new Description("should implement {$this->interface}", $because);
2828
}
2929

30+
public function appliesTo(ClassDescription $theClass): bool
31+
{
32+
return !($theClass->isInterface() || $theClass->isTrait());
33+
}
34+
3035
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
3136
{
3237
if ($theClass->isInterface() || $theClass->isTrait()) {

src/Expression/ForClasses/NotImplement.php

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public function describe(ClassDescription $theClass, string $because): Descripti
2727
return new Description("should not implement {$this->interface}", $because);
2828
}
2929

30+
public function appliesTo(ClassDescription $theClass): bool
31+
{
32+
return !($theClass->isInterface() || $theClass->isTrait());
33+
}
34+
3035
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
3136
{
3237
if ($theClass->isInterface() || $theClass->isTrait()) {
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Arkitect\Tests\Integration;
6+
7+
use Arkitect\Expression\ForClasses\Extend;
8+
use Arkitect\Expression\ForClasses\HaveNameMatching;
9+
use Arkitect\Rules\Rule;
10+
use Arkitect\Tests\Utils\TestRunner;
11+
use org\bovigo\vfs\vfsStream;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class ExtendsThrowableTest extends TestCase
15+
{
16+
public function test_naming_is_enforced(): void
17+
{
18+
$dir = vfsStream::setup('root', null, $this->createDirStructure())->url();
19+
20+
$runner = TestRunner::create('8.2');
21+
22+
$rule = Rule::allClasses()
23+
->that(new Extend(\Throwable::class))
24+
->should(new HaveNameMatching('*Exception'))
25+
->because('reasons');
26+
27+
$runner->run($dir, $rule);
28+
29+
$this->assertCount(1, $runner->getViolations());
30+
$this->assertCount(0, $runner->getParsingErrors());
31+
32+
$this->assertStringContainsString('should have a name that matches *Exception because', $runner->getViolations()->get(0)->getError());
33+
}
34+
35+
public function createDirStructure(): array
36+
{
37+
return [
38+
'App' => [
39+
'BillingEnum.php' => '<?php
40+
41+
namespace App;
42+
43+
enum BillingEnum {
44+
case PENDING;
45+
case PAYED;
46+
}
47+
',
48+
'AnException.php' => '<?php
49+
50+
namespace App;
51+
52+
class AnException extends \Throwable { }
53+
',
54+
'AThrowable.php' => '<?php
55+
56+
namespace App;
57+
58+
class AThrowable extends \Throwable { }
59+
',
60+
'OneTrait.php' => '<?php
61+
62+
namespace App;
63+
64+
trait OneTrait {
65+
public function one() {}
66+
}
67+
',
68+
'AnInterface.php' => '<?php
69+
70+
namespace App;
71+
72+
interface AnInterface {
73+
public function amethod();
74+
}
75+
',
76+
],
77+
];
78+
}
79+
}

tests/Integration/ImplementsTest.php

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Arkitect\Tests\Integration;
6+
7+
use Arkitect\Expression\ForClasses\HaveNameMatching;
8+
use Arkitect\Expression\ForClasses\Implement;
9+
use Arkitect\Rules\Rule;
10+
use Arkitect\Tests\Utils\TestRunner;
11+
use org\bovigo\vfs\vfsStream;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class ImplementsTest extends TestCase
15+
{
16+
public function test_naming_is_enforced(): void
17+
{
18+
$dir = vfsStream::setup('root', null, $this->createDirStructure())->url();
19+
20+
$runner = TestRunner::create('8.2');
21+
22+
$rule = Rule::allClasses()
23+
->that(new Implement('App\AnInterface'))
24+
->should(new HaveNameMatching('An*'))
25+
->because('reasons');
26+
27+
$runner->run($dir, $rule);
28+
29+
$this->assertCount(0, $runner->getParsingErrors());
30+
$this->assertCount(2, $runner->getViolations());
31+
32+
$this->assertEquals('App\AClass', $runner->getViolations()->get(0)->getFqcn());
33+
$this->assertStringContainsString('should have a name that matches An* because reasons', $runner->getViolations()->get(0)->getError());
34+
35+
$this->assertEquals('App\AEnum', $runner->getViolations()->get(1)->getFqcn());
36+
$this->assertStringContainsString('should have a name that matches An* because reasons', $runner->getViolations()->get(1)->getError());
37+
}
38+
39+
public function createDirStructure(): array
40+
{
41+
return [
42+
'App' => [
43+
'AEnum.php' => '<?php
44+
45+
namespace App;
46+
47+
enum AEnum implements AnInterface {
48+
case PENDING;
49+
case PAYED;
50+
51+
public function amethod() {}
52+
}
53+
',
54+
'OneTrait.php' => '<?php
55+
56+
namespace App;
57+
58+
trait OneTrait {
59+
public function one() {}
60+
}
61+
',
62+
'AClass.php' => '<?php
63+
64+
namespace App;
65+
66+
class AClass implements AnInterface {
67+
public function amethod() {}
68+
}
69+
',
70+
'AnInterface.php' => '<?php
71+
72+
namespace App;
73+
74+
interface AnInterface {
75+
public function amethod();
76+
}
77+
',
78+
],
79+
];
80+
}
81+
}

tests/Unit/Analyzer/FileVisitorTest.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -739,24 +739,27 @@ public function test_should_implement_exact_classname(): void
739739
interface Order
740740
{
741741
}
742+
742743
interface OrderTwo
743744
{
744745
}
745-
class test implements Order
746+
747+
class Test implements Order
746748
{
747749
}
750+
748751
EOF;
749752

750753
/** @var FileParser $fp */
751754
$fp = FileParserFactory::createFileParser(TargetPhpVersion::create('8.1'));
752755
$fp->parse($code, 'relativePathName');
753756

754-
$cd = $fp->getClassDescriptions();
757+
$cd = $fp->getClassDescriptions()[2]; // class Test
755758

756759
$violations = new Violations();
757760

758761
$implement = new Implement('Foo\Order');
759-
$implement->evaluate($cd[0], $violations, 'we want to add this rule for our software');
762+
$implement->evaluate($cd, $violations, 'we want to add this rule for our software');
760763

761764
$this->assertCount(0, $violations, $violations->toString());
762765
}

0 commit comments

Comments
 (0)