packages/deriver/tests/Unit/Deriver/Evaluator/Enumeration/Finite/DemandTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Enumeration\Finite;
6
7use Deriver\Deriver\Evaluator\Call\Evaluators;
8use Deriver\Deriver\Evaluator\Enumeration\Choices;
9use Deriver\Deriver\Evaluator\Enumeration\Finite\Demand;
10use Deriver\Deriver\Evaluator\Evaluator;
11use Deriver\Deriver\Evaluator\EvaluatorConfig;
12use Deriver\Deriver\Evaluator\Evidence\Provenance;
13use Deriver\Deriver\Evaluator\Index\DefinitionIndex;
14use Deriver\Deriver\Evaluator\Request\Budget;
15use Deriver\Deriver\Evaluator\Scope\Context;
16use Deriver\Deriver\Evaluator\Term;
17use Deriver\Deriver\Evaluator\TermKind;
18use Deriver\Deriver\Lowerer\Declaration\Index;
19use Deriver\Deriver\Parser\Parser;
20use Deriver\Deriver\Parser\ParserConfig;
21use Deriver\Deriver\Resolver\Resolver;
22use Deriver\Deriver\Sources\Source;
23use PHPUnit\Framework\Attributes\CoversNothing;
24use PHPUnit\Framework\TestCase;
25
26#[CoversNothing]
27final class DemandTest extends TestCase
28{
29 public function testDemandKeepsRemainingMembersInItsCacheIdentity(): void
30 {
31 $all = Demand::truth();
32 $one = Demand::truth(1);
33 self::assertNotSame($all->identity, $one->identity);
34 self::assertSame(['b:0' => true, 'b:1' => true], $all->remaining);
35 }
36
37 public function testTruthUsesOnlyTheRequestedBooleanMembers(): void
38 {
39 self::assertSame(['b:1' => true], Demand::truth(2)->remaining);
40 self::assertSame('b:0', Demand::truth()->key(Term::constant('0')));
41 self::assertSame('b:1', Demand::truth()->key(Term::constant('yes')));
42 }
43
44 public function testCollectDoesNotOpenAlternativesAfterCoverage(): void
45 {
46 $opened = 0;
47 $value = Demand::truth()->collect([
48 static fn (): Term => Term::constant(0),
49 static fn (): Term => Term::constant(7),
50 static function () use (&$opened): Term {
51 ++$opened;
52 return Term::constant(9);
53 },
54 ], new Provenance());
55 self::assertSame([0,7], array_map(static fn (array $row) => $row[0]->native(), iterator_to_array((new Choices())->alternatives($value))));
56 self::assertSame(0, $opened);
57 }
58
59 public function testCollectRetainsObservedFailuresAfterCoverage(): void
60 {
61 $failure = new Term(TermKind::Throwable, 'TypeError');
62 $value = Demand::truth()->collect([
63 static fn (): Term => $failure,
64 static fn (): Term => Term::constant(false),
65 static fn (): Term => Term::constant(true),
66 ], new Provenance());
67 $values = array_map(static fn (array $row): Term => $row[0], iterator_to_array((new Choices())->alternatives($value)));
68 self::assertContains($failure, $values);
69 self::assertCount(3, $values);
70 }
71
72 public function testKeyDoesNotInventAValueFromItsDeclaredType(): void
73 {
74 self::assertNull(Demand::truth()->key(Term::parameter('x', 'bool')));
75 }
76
77 public function testIdentityDistinguishesEnumCasesWithEqualBackingValues(): void
78 {
79 $first = new Term(TermKind::Enum, 'First::A');
80 $second = new Term(TermKind::Enum, 'Second::A');
81 self::assertNotSame(Demand::identity($first), Demand::identity($second));
82 self::assertNull(Demand::identity(Term::parameter('x')));
83 }
84
85 public function testThroughChecksStrictDeclarationsBeforeCountingWitnesses(): void
86 {
87 $source = 'function subject(){return 1;}';
88 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('a.php', '<?php ' . $source)));
89 $index = new DefinitionIndex(new Index('s', [$tree]));
90 $config = new EvaluatorConfig();
91 $engine = new Evaluator(new Context($index, $config, new Evaluators($config->target), new Budget()));
92 $strict = Demand::truth()->through($engine, 'bool', true);
93 $weak = Demand::truth()->through($engine, 'bool', false);
94 self::assertNull($strict->key(Term::constant(1)));
95 self::assertSame('b:1', $weak->key(Term::constant(1)));
96 $invalid = Term::constant(1);
97 $candidates = $strict->collect([
98 static fn (): Term => $invalid,
99 static fn (): Term => Term::constant(false),
100 static fn (): Term => Term::constant(true),
101 ], $engine->context->provenance);
102 self::assertContains($invalid, array_map(static fn (array $row): Term => $row[0], iterator_to_array((new Choices())->alternatives($candidates))));
103 }
104}
105