packages/deriver/tests/Unit/Deriver/Providers/DomainProviderTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Providers;
6
7use Deriver\Deriver\Evaluator\Call\Provider\RefinementEvaluator;
8use Deriver\Deriver\Evaluator\Domain\AbstractDomain;
9use Deriver\Deriver\Evaluator\Domain\DomainFact;
10use Deriver\Deriver\Evaluator\Projection;
11use Deriver\Deriver\Evaluator\Term;
12use Deriver\Deriver\Evaluator\TermKind;
13use Deriver\Deriver\Providers\DomainProvider;
14use Override;
15use PHPUnit\Framework\Attributes\CoversClass;
16use PHPUnit\Framework\Attributes\Small;
17use PHPUnit\Framework\Attributes\UsesClass;
18use PHPUnit\Framework\TestCase;
19
20/**
21 * @covers \Deriver\Deriver\Providers\DomainProvider
22 */
23#[CoversClass(DomainProvider::class)]
24#[UsesClass(AbstractDomain::class)]
25#[Small]
26final class DomainProviderTest extends TestCase
27{
28 public function testDomainsContributesThePolicyLattice(): void
29 {
30 $provider = new class () implements DomainProvider, RefinementEvaluator {
31 /**
32 * @return string Stable provider identity
33 */
34 #[Override]
35 public function id(): string
36 {
37 return 'example.policy-provider';
38 }
39 /**
40 * @return string Semantic version
41 */
42 #[Override]
43 public function version(): string
44 {
45 return '1';
46 }
47 /**
48 * @return list<AbstractDomain> Captured domains
49 */
50 #[Override]
51 public function domains(): array
52 {
53 return [new class () implements AbstractDomain {
54 /**
55 * @return string Domain identity
56 */
57 #[Override]
58 public function id(): string
59 {
60 return 'example.policy';
61 }
62 /**
63 * @return string Semantic version
64 */
65 #[Override]
66 public function version(): string
67 {
68 return '1';
69 }
70 /**
71 * @return DomainFact Empty set
72 */
73 #[Override]
74 public function bottom(): DomainFact
75 {
76 return new DomainFact($this->id(), new Term(TermKind::Bottom));
77 }
78 /**
79 * @return DomainFact All policy records
80 */
81 #[Override]
82 public function top(): DomainFact
83 {
84 return new DomainFact($this->id(), new Term(TermKind::Top));
85 }
86 /**
87 * @param DomainFact $a Contained fact
88 * @param DomainFact $b Containing fact
89 * @return bool Flat-lattice inclusion
90 */
91 #[Override]
92 public function lessOrEqual(DomainFact $a, DomainFact $b): bool
93 {
94 return $a->representation->kind === TermKind::Bottom || $b->representation->kind === TermKind::Top || $a->representation->isConcrete() && $b->representation->isConcrete() && $a->representation->native() === $b->representation->native();
95 }
96 /**
97 * @param DomainFact $a First possibility
98 * @param DomainFact $b Second possibility
99 * @return DomainFact Inclusive alternative
100 */
101 #[Override]
102 public function join(DomainFact $a, DomainFact $b): DomainFact
103 {
104 if ($this->lessOrEqual($a, $b)) {
105 return $b;
106 }
107 return $this->lessOrEqual($b, $a) ? $a : $this->top();
108 }
109 /**
110 * @param DomainFact $previous Previous approximation
111 * @param DomainFact $next New approximation
112 * @return DomainFact Finite widening
113 */
114 #[Override]
115 public function widen(DomainFact $previous, DomainFact $next): DomainFact
116 {
117 return $this->join($previous, $next);
118 }
119 /**
120 * @param DomainFact $value Policy record
121 * @param Projection $projection Selected fields
122 * @return DomainFact Projected fact
123 */
124 #[Override]
125 public function project(DomainFact $value, Projection $projection): DomainFact
126 {
127 $term = $value->representation;
128 foreach ($projection->path as $key) {
129 $term = $term->operands[$key] ?? new Term(TermKind::Top);
130 }
131 return new DomainFact($this->id(), $term);
132 }
133 }];
134 }
135 /**
136 * @param Term $predicate Branch predicate
137 * @param bool $truth Selected polarity
138 * @return Term Guaranteed implication
139 */
140 #[Override]
141 public function refine(Term $predicate, bool $truth): Term
142 {
143 return $truth ? $predicate : new Term(TermKind::Unary, '!', [$predicate]);
144 }
145 };
146 self::assertSame('example.policy', $provider->domains()[0]->id());
147 }
148}
149