diff --git a/src/Expression/Boolean/Andx.php b/src/Expression/Boolean/Andx.php new file mode 100644 index 00000000..5f4cb222 --- /dev/null +++ b/src/Expression/Boolean/Andx.php @@ -0,0 +1,56 @@ +expressions = $expressions; + } + + public function describe(ClassDescription $theClass, string $because): Description + { + $expressionsDescriptions = []; + foreach ($this->expressions as $expression) { + $expressionsDescriptions[] = $expression->describe($theClass, $because)->toString(); + } + $expressionsDescriptionsString = "(\n" + .implode("\nAND\n", array_unique(array_map('trim', $expressionsDescriptions))) + ."\n)"; + + return new Description($expressionsDescriptionsString, $because); + } + + public function evaluate(ClassDescription $theClass, Violations $violations, string $because): void + { + foreach ($this->expressions as $expression) { + $newViolations = new Violations(); + $expression->evaluate($theClass, $newViolations, $because); + if (0 !== $newViolations->count()) { + $violations->add(Violation::create( + $theClass->getFQCN(), + ViolationMessage::withDescription( + $this->describe($theClass, $because), + "The class '".$theClass->getFQCN()."' violated the expression\n" + .$expression->describe($theClass, '')->toString() + ) + )); + + return; + } + } + } +} diff --git a/tests/Unit/Expressions/Boolean/AndxTest.php b/tests/Unit/Expressions/Boolean/AndxTest.php new file mode 100644 index 00000000..31ace709 --- /dev/null +++ b/tests/Unit/Expressions/Boolean/AndxTest.php @@ -0,0 +1,127 @@ +setClassName('My\Class') + ->setExtends('My\BaseClass', 10) + ->build(); + + $violations = new Violations(); + $and->evaluate($classDescription, $violations, 'because'); + + self::assertEquals(0, $violations->count()); + } + + public function test_it_should_pass_the_rule(): void + { + $interface = 'interface'; + $class = 'SomeClass'; + $classDescription = new ClassDescription( + FullyQualifiedClassName::fromString('HappyIsland'), + [], + [FullyQualifiedClassName::fromString($interface)], + FullyQualifiedClassName::fromString($class), + false, + false, + false, + false, + false + ); + $implementConstraint = new Implement($interface); + $extendsConstraint = new Extend($class); + $andConstraint = new Andx($implementConstraint, $extendsConstraint); + + $because = 'reasons'; + $violations = new Violations(); + $andConstraint->evaluate($classDescription, $violations, $because); + + self::assertEquals(0, $violations->count()); + } + + public function test_it_should_pass_the_rule_when_and_is_empty(): void + { + $interface = 'interface'; + $class = 'SomeClass'; + $classDescription = new ClassDescription( + FullyQualifiedClassName::fromString('HappyIsland'), + [], + [FullyQualifiedClassName::fromString($interface)], + FullyQualifiedClassName::fromString($class), + false, + false, + false, + false, + false + ); + $andConstraint = new Andx(); + + $because = 'reasons'; + $violations = new Violations(); + $andConstraint->evaluate($classDescription, $violations, $because); + + self::assertEquals(0, $violations->count()); + } + + public function test_it_should_not_pass_the_rule(): void + { + $interface = 'SomeInterface'; + $class = 'SomeClass'; + + $classDescription = new ClassDescription( + FullyQualifiedClassName::fromString('HappyIsland'), + [], + [FullyQualifiedClassName::fromString($interface)], + null, + false, + false, + false, + false, + false + ); + + $implementConstraint = new Implement($interface); + $extendsConstraint = new Extend($class); + $andConstraint = new Andx($implementConstraint, $extendsConstraint); + + $because = 'reasons'; + $violationError = $andConstraint->describe($classDescription, $because)->toString(); + + $violations = new Violations(); + $andConstraint->evaluate($classDescription, $violations, $because); + self::assertNotEquals(0, $violations->count()); + + $this->assertEquals( + "(\nshould implement SomeInterface because reasons\nAND\nshould extend SomeClass because reasons\n) because reasons", + $violationError + ); + $this->assertEquals( + "The class 'HappyIsland' violated the expression\n" + ."should extend SomeClass\n" + ."from the rule\n" + ."(\n" + ."should implement SomeInterface because reasons\n" + ."AND\n" + ."should extend SomeClass because reasons\n" + .') because reasons', + $violations->get(0)->getError() + ); + } +}