diff --git a/src/Rules/AndThatShould.php b/src/Rules/AndThatShould.php index c6848f53..03030be1 100644 --- a/src/Rules/AndThatShould.php +++ b/src/Rules/AndThatShould.php @@ -19,15 +19,17 @@ public function __construct(RuleBuilder $expressionBuilder) public function andThat(Expression $expression): AndThatShouldParser { - $this->ruleBuilder->addThat($expression); + $ruleBuilder = clone $this->ruleBuilder; + $ruleBuilder->addThat($expression); - return $this; + return new self($ruleBuilder); } public function should(Expression $expression): BecauseParser { - $this->ruleBuilder->addShould($expression); + $ruleBuilder = clone $this->ruleBuilder; + $ruleBuilder->addShould($expression); - return new Because($this->ruleBuilder); + return new Because($ruleBuilder); } } diff --git a/src/Rules/RuleBuilder.php b/src/Rules/RuleBuilder.php index e6f12bf2..e56eb41d 100644 --- a/src/Rules/RuleBuilder.php +++ b/src/Rules/RuleBuilder.php @@ -30,6 +30,12 @@ public function __construct() $this->runOnlyThis = false; } + public function __clone() + { + $this->thats = clone $this->thats; + $this->shoulds = clone $this->shoulds; + } + public function addThat(Expression $that): self { $this->thats->add($that); diff --git a/tests/Unit/Rules/RuleBuilderTest.php b/tests/Unit/Rules/RuleBuilderTest.php new file mode 100644 index 00000000..5c4dad85 --- /dev/null +++ b/tests/Unit/Rules/RuleBuilderTest.php @@ -0,0 +1,75 @@ +build(); + + $rectors = Rule::allClasses() + ->that(new ResideInOneOfTheseNamespaces('App\Rector')); + + $nameMatchingRule = $rectors + ->should(new HaveNameMatching('*Rector')) + ->because('rector classes should have Rector suffix'); + + $extendsRule = $rectors + ->should(new Extend('Rector\Core\Rector\AbstractRector')) + ->because('rector classes should extend AbstractRector'); + + $nameMatchingViolations = new Violations(); + $nameMatchingRule->check($classViolatingBothRules, $nameMatchingViolations); + + $extendsViolations = new Violations(); + $extendsRule->check($classViolatingBothRules, $extendsViolations); + + self::assertCount(1, $nameMatchingViolations); + self::assertCount(1, $extendsViolations); + self::assertStringContainsString('should have a name that matches', $nameMatchingViolations->get(0)->getError()); + self::assertStringContainsString('should extend', $extendsViolations->get(0)->getError()); + } + + public function test_reusing_that_for_multiple_and_that_produces_independent_rules(): void + { + $classInBaseNamespaceOnly = ClassDescription::getBuilder('App\Service\MyService', 'src/Service/MyService.php') + ->build(); + + $services = Rule::allClasses() + ->that(new ResideInOneOfTheseNamespaces('App\Service')); + + $internalRule = $services + ->andThat(new ResideInOneOfTheseNamespaces('App\Service\Internal')) + ->should(new HaveNameMatching('*Service')) + ->because('internal services should have Service suffix'); + + $externalRule = $services + ->andThat(new ResideInOneOfTheseNamespaces('App\Service\External')) + ->should(new HaveNameMatching('*Client')) + ->because('external services should have Client suffix'); + + $internalViolations = new Violations(); + $internalRule->check($classInBaseNamespaceOnly, $internalViolations); + + $externalViolations = new Violations(); + $externalRule->check($classInBaseNamespaceOnly, $externalViolations); + + self::assertCount(0, $internalViolations); + self::assertCount(0, $externalViolations); + } +}