Skip to content

Commit

Permalink
Create a And expression
Browse files Browse the repository at this point in the history
This will allow for complex nested expressions, like ANDs inside ORs.
  • Loading branch information
Herberto Graca committed Jul 27, 2023
1 parent 976c200 commit 701fb06
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/Expression/Boolean/Andx.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Arkitect\Expression\Boolean;

use Arkitect\Analyzer\ClassDescription;
use Arkitect\Expression\Description;
use Arkitect\Expression\Expression;
use Arkitect\Rules\Violation;
use Arkitect\Rules\ViolationMessage;
use Arkitect\Rules\Violations;

final class Andx implements Expression
{
/** @var Expression[] */
private $expressions;

public function __construct(Expression ...$expressions)
{
$this->expressions = $expressions;
}

public function describe(ClassDescription $theClass, string $because): Description
{
$expressionsDescriptions = [];
foreach ($this->expressions as $expression) {
$expressionsDescriptions[] = $expression->describe($theClass, '')->toString();
}
return new Description(
'all expressions must be true (' . implode(', ', $expressionsDescriptions) . ')',
$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 "
. $expression->describe($theClass, '')->toString()
)
));

return;
}
}
}
}
105 changes: 105 additions & 0 deletions tests/Unit/Expressions/Boolean/AndxTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

declare(strict_types=1);

namespace Arkitect\Tests\Unit\Expressions\Boolean;

use Arkitect\Analyzer\ClassDescription;
use Arkitect\Analyzer\FullyQualifiedClassName;
use Arkitect\Expression\Boolean\Andx;
use Arkitect\Expression\ForClasses\Extend;
use Arkitect\Expression\ForClasses\Implement;
use Arkitect\Rules\Violations;
use PHPUnit\Framework\TestCase;

class AndxTest extends TestCase
{
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(
'all expressions must be true (should implement SomeInterface, should extend SomeClass) because reasons',
$violationError
);
$this->assertEquals(
"The class 'HappyIsland' violated the expression should extend SomeClass, but "
. "all expressions must be true (should implement SomeInterface, should extend SomeClass) because reasons",
$violations->get(0)->getError()
);
}
}

0 comments on commit 701fb06

Please sign in to comment.