Skip to content

Commit 0e4a588

Browse files
fixes also readonly and final checks
1 parent cc95e06 commit 0e4a588

13 files changed

+222
-111
lines changed

src/Expression/ForClasses/IsFinal.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ public function describe(ClassDescription $theClass, string $because): Descripti
1818
return new Description("{$theClass->getName()} should be final", $because);
1919
}
2020

21+
public function appliesTo(ClassDescription $theClass): bool
22+
{
23+
return !($theClass->isInterface() || $theClass->isTrait() || $theClass->isEnum() || $theClass->isAbstract());
24+
}
25+
2126
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
2227
{
23-
if ($theClass->isAbstract() || $theClass->isInterface() || $theClass->isFinal() || $theClass->isTrait()
24-
|| $theClass->isEnum()) {
28+
if ($theClass->isFinal()) {
2529
return;
2630
}
2731

src/Expression/ForClasses/IsNotAbstract.php

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public function describe(ClassDescription $theClass, string $because): Descripti
1818
return new Description("{$theClass->getName()} should not be abstract", $because);
1919
}
2020

21+
public function appliesTo(ClassDescription $theClass): bool
22+
{
23+
return !($theClass->isInterface() || $theClass->isTrait() || $theClass->isEnum() || $theClass->isFinal());
24+
}
25+
2126
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
2227
{
2328
if (!$theClass->isAbstract()) {

src/Expression/ForClasses/IsNotReadonly.php

+5
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public function describe(ClassDescription $theClass, string $because): Descripti
1818
return new Description("{$theClass->getName()} should not be readonly", $because);
1919
}
2020

21+
public function appliesTo(ClassDescription $theClass): bool
22+
{
23+
return !($theClass->isInterface() || $theClass->isTrait() || $theClass->isEnum());
24+
}
25+
2126
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
2227
{
2328
if (!$theClass->isReadonly()) {

src/Expression/ForClasses/IsReadonly.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ public function describe(ClassDescription $theClass, string $because): Descripti
1818
return new Description("{$theClass->getName()} should be readonly", $because);
1919
}
2020

21+
public function appliesTo(ClassDescription $theClass): bool
22+
{
23+
return !($theClass->isInterface() || $theClass->isTrait() || $theClass->isEnum());
24+
}
25+
2126
public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void
2227
{
23-
if ($theClass->isReadonly() || $theClass->isInterface() || $theClass->isTrait() || $theClass->isEnum()) {
28+
if ($theClass->isReadonly()) {
2429
return;
2530
}
2631

tests/E2E/PHPUnit/CheckClassHaveAttributeTest.php

+3-10
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Arkitect\Tests\E2E\PHPUnit;
66

7-
use Arkitect\ClassSet;
87
use Arkitect\Expression\ForClasses\HaveAttribute;
98
use Arkitect\Expression\ForClasses\HaveNameMatching;
109
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
@@ -18,14 +17,12 @@ public function test_entities_should_reside_in_app_model(): void
1817
{
1918
$runner = TestRunner::create('8.4');
2019

21-
$set = ClassSet::fromDir(__DIR__.'/../_fixtures/mvc');
22-
2320
$rule = Rule::allClasses()
2421
->that(new HaveAttribute('Entity'))
2522
->should(new ResideInOneOfTheseNamespaces('App\Model'))
2623
->because('we use an ORM');
2724

28-
$runner->run($set, $rule);
25+
$runner->run(__DIR__.'/../_fixtures/mvc', $rule);
2926

3027
$this->assertCount(0, $runner->getViolations());
3128
$this->assertCount(0, $runner->getParsingErrors());
@@ -35,14 +32,12 @@ public function test_controllers_should_have_name_ending_in_controller(): void
3532
{
3633
$runner = TestRunner::create('8.4');
3734

38-
$set = ClassSet::fromDir(__DIR__.'/../_fixtures/mvc');
39-
4035
$rule = Rule::allClasses()
4136
->that(new HaveAttribute('AsController'))
4237
->should(new HaveNameMatching('*Controller'))
4338
->because('its a symfony thing');
4439

45-
$runner->run($set, $rule);
40+
$runner->run(__DIR__.'/../_fixtures/mvc', $rule);
4641

4742
$this->assertCount(1, $runner->getViolations());
4843
$this->assertCount(0, $runner->getParsingErrors());
@@ -54,14 +49,12 @@ public function test_controllers_should_have_controller_attribute(): void
5449
{
5550
$runner = TestRunner::create('8.4');
5651

57-
$set = ClassSet::fromDir(__DIR__.'/../_fixtures/mvc');
58-
5952
$rule = Rule::allClasses()
6053
->that(new HaveNameMatching('*Controller'))
6154
->should(new HaveAttribute('AsController'))
6255
->because('it configures the service container');
6356

64-
$runner->run($set, $rule);
57+
$runner->run(__DIR__.'/../_fixtures/mvc', $rule);
6558

6659
$this->assertCount(0, $runner->getViolations());
6760
$this->assertCount(0, $runner->getParsingErrors());

tests/Integration/CheckAttributeDependencyTest.php

+2-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
declare(strict_types=1);
44

5-
namespace Arkitect\Tests\E2E\PHPUnit;
5+
namespace Arkitect\Tests\Integration;
66

7-
use Arkitect\ClassSet;
87
use Arkitect\Expression\ForClasses\NotDependsOnTheseNamespaces;
98
use Arkitect\Expression\ForClasses\ResideInOneOfTheseNamespaces;
109
use Arkitect\Rules\Rule;
@@ -20,14 +19,12 @@ public function test_assertion_should_fail_on_invalid_dependency(): void
2019

2120
$runner = TestRunner::create('8.4');
2221

23-
$set = ClassSet::fromDir($dir);
24-
2522
$rule = Rule::allClasses()
2623
->that(new ResideInOneOfTheseNamespaces('App'))
2724
->should(new NotDependsOnTheseNamespaces('App\Invalid'))
2825
->because('i said so');
2926

30-
$runner->run($set, $rule);
27+
$runner->run($dir, $rule);
3128

3229
$this->assertCount(1, $runner->getViolations());
3330
$this->assertCount(0, $runner->getParsingErrors());

tests/Integration/IsAbstractTest.php

+5-16
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22

33
declare(strict_types=1);
44

5-
namespace Arkitect\Tests\E2E\PHPUnit;
5+
namespace Arkitect\Tests\Integration;
66

7-
use Arkitect\ClassSet;
87
use Arkitect\Expression\ForClasses\HaveNameMatching;
98
use Arkitect\Expression\ForClasses\IsAbstract;
109
use Arkitect\Expression\ForClasses\IsNotAbstract;
@@ -25,14 +24,12 @@ public function test_is_abstract_in_that_should_not_consider_final_traits_enums_
2524
{
2625
$runner = TestRunner::create('8.4');
2726

28-
$set = ClassSet::fromDir($this->createClasses());
29-
3027
$rule = Rule::allClasses()
3128
->that(new IsAbstract())
3229
->should(new HaveNameMatching('*Abstract'))
3330
->because('we want to prefix abstract classes');
3431

35-
$runner->run($set, $rule);
32+
$runner->run($this->createClasses(), $rule);
3633

3734
$this->assertCount(0, $runner->getViolations());
3835
$this->assertCount(0, $runner->getParsingErrors());
@@ -42,14 +39,12 @@ public function test_is_abstract_in_should_should_consider_final_traits_enums_in
4239
{
4340
$runner = TestRunner::create('8.4');
4441

45-
$set = ClassSet::fromDir($this->createClasses());
46-
4742
$rule = Rule::allClasses()
4843
->that(new HaveNameMatching('My*'))
4944
->should(new IsAbstract())
5045
->because('everything in the app namespace should be abstract');
5146

52-
$runner->run($set, $rule);
47+
$runner->run($this->createClasses(), $rule);
5348

5449
$this->assertCount(4, $runner->getViolations());
5550
$this->assertCount(0, $runner->getParsingErrors());
@@ -64,14 +59,12 @@ public function test_is_not_abstract_in_should_should_consider_final_traits_enum
6459
{
6560
$runner = TestRunner::create('8.4');
6661

67-
$set = ClassSet::fromDir($this->createClasses());
68-
6962
$rule = Rule::allClasses()
7063
->that(new HaveNameMatching('My*'))
7164
->should(new IsNotAbstract())
7265
->because('everything in the app namespace should be abstract');
7366

74-
$runner->run($set, $rule);
67+
$runner->run($this->createClasses(), $rule);
7568

7669
$this->assertCount(1, $runner->getViolations());
7770
$this->assertCount(0, $runner->getParsingErrors());
@@ -144,12 +137,8 @@ public function __construct(HappyClass $happy)
144137
],
145138
];
146139

147-
$dir = vfsStream::setup('root', null, $structure)->url();
148-
149140
$runner = TestRunner::create('8.4');
150141

151-
$set = ClassSet::fromDir($dir);
152-
153142
$rule = Rule::allClasses()
154143
->that(new ResideInOneOfTheseNamespaces('App\BadCode'))
155144
->andThat(new ResideInOneOfTheseNamespaces('App\HappyIsland'))
@@ -161,7 +150,7 @@ public function __construct(HappyClass $happy)
161150
->andShould(new IsNotTrait())
162151
->because('some reason');
163152

164-
$runner->run($set, $rule);
153+
$runner->run(vfsStream::setup('root', null, $structure)->url(), $rule);
165154

166155
$this->assertCount(0, $runner->getViolations());
167156
$this->assertCount(0, $runner->getParsingErrors());

tests/Integration/IsFinalTest.php

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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\IsFinal;
9+
use Arkitect\Expression\ForClasses\IsNotFinal;
10+
use Arkitect\Rules\Rule;
11+
use Arkitect\Tests\Utils\TestRunner;
12+
use org\bovigo\vfs\vfsStream;
13+
use PHPUnit\Framework\TestCase;
14+
15+
class IsFinalTest extends TestCase
16+
{
17+
public function test_is_final_in_that_should_not_consider_abstract_traits_enums_interfaces(): void
18+
{
19+
$runner = TestRunner::create('8.4');
20+
21+
$rule = Rule::allClasses()
22+
->that(new IsFinal())
23+
->should(new HaveNameMatching('*Final'))
24+
->because('we want to prefix final classes');
25+
26+
$runner->run($this->createClasses(), $rule);
27+
28+
$this->assertCount(0, $runner->getViolations());
29+
$this->assertCount(0, $runner->getParsingErrors());
30+
}
31+
32+
public function test_is_final_in_should_should_consider_final_traits_enums_interfaces(): void
33+
{
34+
$runner = TestRunner::create('8.4');
35+
36+
$rule = Rule::allClasses()
37+
->that(new HaveNameMatching('My*'))
38+
->should(new IsFinal())
39+
->because('everything in the app namespace should be final');
40+
41+
$runner->run($this->createClasses(), $rule);
42+
43+
$this->assertCount(4, $runner->getViolations());
44+
$this->assertCount(0, $runner->getParsingErrors());
45+
46+
$this->assertEquals('App\MyAbstract', $runner->getViolations()->get(0)->getFqcn());
47+
$this->assertEquals('App\MyEnum', $runner->getViolations()->get(1)->getFqcn());
48+
$this->assertEquals('App\MyInterface', $runner->getViolations()->get(2)->getFqcn());
49+
$this->assertEquals('App\MyTrait', $runner->getViolations()->get(3)->getFqcn());
50+
}
51+
52+
public function test_is_not_final_in_should_should_consider_final_traits_enums_interfaces(): void
53+
{
54+
$runner = TestRunner::create('8.4');
55+
56+
$rule = Rule::allClasses()
57+
->that(new HaveNameMatching('My*'))
58+
->should(new IsNotFinal())
59+
->because('everything in the app namespace should not be final');
60+
61+
$runner->run($this->createClasses(), $rule);
62+
63+
$this->assertCount(1, $runner->getViolations());
64+
$this->assertCount(0, $runner->getParsingErrors());
65+
66+
$this->assertEquals('App\MyFinal', $runner->getViolations()->get(0)->getFqcn());
67+
}
68+
69+
protected function createClasses(): string
70+
{
71+
$structure = [
72+
'App' => [
73+
'MyAbstract.php' => '<?php namespace App { abstract class MyAbstract {} };',
74+
'MyFinal.php' => '<?php namespace App { final class MyFinal {} };',
75+
'MyInterface.php' => '<?php namespace App { interface MyInterface {} };',
76+
'MyEnum.php' => '<?php namespace App { enum MyEnum {} };',
77+
'MyTrait.php' => '<?php namespace App { trait MyTrait {} };',
78+
],
79+
];
80+
81+
return vfsStream::setup('root', null, $structure)->url();
82+
}
83+
}

tests/Integration/IsReadonlyTest.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\IsNotReadonly;
9+
use Arkitect\Expression\ForClasses\IsReadonly;
10+
use Arkitect\Rules\Rule;
11+
use Arkitect\Tests\Utils\TestRunner;
12+
use org\bovigo\vfs\vfsStream;
13+
use PHPUnit\Framework\TestCase;
14+
15+
class IsReadonlyTest extends TestCase
16+
{
17+
public function test_is_readonly_in_that_should_not_consider_traits_enums_interfaces(): void
18+
{
19+
$runner = TestRunner::create('8.4');
20+
21+
$rule = Rule::allClasses()
22+
->that(new IsReadonly())
23+
->should(new HaveNameMatching('*Readonly'))
24+
->because('we want to prefix readonly classes');
25+
26+
$runner->run($this->createClasses(), $rule);
27+
28+
$this->assertCount(0, $runner->getViolations());
29+
$this->assertCount(0, $runner->getParsingErrors());
30+
}
31+
32+
public function test_is_readonly_in_should_should_consider_traits_enums_interfaces(): void
33+
{
34+
$runner = TestRunner::create('8.4');
35+
36+
$rule = Rule::allClasses()
37+
->that(new HaveNameMatching('My*'))
38+
->should(new IsReadonly())
39+
->because('everything in the app namespace should be readonly');
40+
41+
$runner->run($this->createClasses(), $rule);
42+
43+
$this->assertCount(3, $runner->getViolations());
44+
$this->assertCount(0, $runner->getParsingErrors());
45+
46+
$this->assertEquals('App\MyEnum', $runner->getViolations()->get(0)->getFqcn());
47+
$this->assertEquals('App\MyInterface', $runner->getViolations()->get(1)->getFqcn());
48+
$this->assertEquals('App\MyTrait', $runner->getViolations()->get(2)->getFqcn());
49+
}
50+
51+
public function test_is_not_readonly_in_should_should_consider_traits_enums_interfaces(): void
52+
{
53+
$runner = TestRunner::create('8.4');
54+
55+
$rule = Rule::allClasses()
56+
->that(new HaveNameMatching('My*'))
57+
->should(new IsNotReadonly())
58+
->because('everything in the app namespace should not be final');
59+
60+
$runner->run($this->createClasses(), $rule);
61+
62+
$this->assertCount(1, $runner->getViolations());
63+
$this->assertCount(0, $runner->getParsingErrors());
64+
65+
$this->assertEquals('App\MyReadonly', $runner->getViolations()->get(0)->getFqcn());
66+
}
67+
68+
protected function createClasses(): string
69+
{
70+
$structure = [
71+
'App' => [
72+
'MyInterface.php' => '<?php namespace App { interface MyInterface {} };',
73+
'MyEnum.php' => '<?php namespace App { enum MyEnum {} };',
74+
'MyTrait.php' => '<?php namespace App { trait MyTrait {} };',
75+
'MyReadonly.php' => '<?php namespace App { readonly class MyReadonly {} };',
76+
],
77+
];
78+
79+
return vfsStream::setup('root', null, $structure)->url();
80+
}
81+
}

0 commit comments

Comments
 (0)