packages/deriver/tests/Unit/Deriver/Evaluator/Invocation/Path/EdgeTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Invocation\Path;
6
7use Deriver\Deriver\BranchEvaluators;
8use Deriver\Deriver\Evaluator\Call\Evaluators;
9use Deriver\Deriver\Evaluator\Evaluator;
10use Deriver\Deriver\Evaluator\EvaluatorConfig;
11use Deriver\Deriver\Evaluator\Index\DefinitionIndex;
12use Deriver\Deriver\Evaluator\Invocation\Path\Callers;
13use Deriver\Deriver\Evaluator\Invocation\Path\Edge;
14use Deriver\Deriver\Evaluator\Request\Budget;
15use Deriver\Deriver\Evaluator\Scope\Context;
16use Deriver\Deriver\Evaluator\Scope\Frame;
17use Deriver\Deriver\Evaluator\Term;
18use Deriver\Deriver\Evaluator\TermKind;
19use Deriver\Deriver\Lowerer\Declaration\Index;
20use Deriver\Deriver\MicroEvaluatorConversion;
21use Deriver\Deriver\Parser\Parser;
22use Deriver\Deriver\Parser\ParserConfig;
23use Deriver\Deriver\Resolver\Resolver;
24use Deriver\Deriver\Sources\Source;
25use PHPUnit\Framework\Attributes\CoversNothing;
26use PHPUnit\Framework\TestCase;
27
28#[CoversNothing]
29final class EdgeTest extends TestCase
30{
31 public function testFollowBindsTheSelectedCallAndRetainsItsEvidence(): void
32 {
33 $source = 'function foo($x){return $x;}function bar(){foo(7);}function other(){foo(9);}';
34 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
35 $index = new DefinitionIndex(new Index('fixture', [$tree]));
36 $settings = new EvaluatorConfig();
37 $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
38 $graph = $index->graph('foo');
39 self::assertNotNull($graph);
40 [$caller, $call] = (new Callers($index))->sites('foo', 'bar')[0];
41 $value = (new Edge($engine))->follow(new Frame($caller, 'bar'), $call, $graph, 32, fn (Frame $frame): Term => $engine->returns($frame, 32));
42 self::assertSame(7, $value->native());
43 self::assertNotNull($engine->context->provenance->get($value));
44 }
45
46 public function testDispatchRejectsAnotherResolvedCallee(): void
47 {
48 $source = 'function foo($x){return $x;}function bar(){foo(7);}function other(){foo(9);}';
49 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
50 $index = new DefinitionIndex(new Index('fixture', [$tree]));
51 $settings = new EvaluatorConfig();
52 $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
53 $graph = $index->graph('other');
54 self::assertNotNull($graph);
55 [$caller, $call] = (new Callers($index))->sites('foo', 'bar')[0];
56 $opened = 0;
57 $value = (new Edge($engine))->dispatch(new Frame($caller, 'bar'), $call, $graph, 32, static function () use (&$opened): Term {
58 ++$opened;
59 return Term::constant(99);
60 });
61 self::assertSame(TermKind::Choice, $value->kind);
62 self::assertSame([], $value->operands);
63 self::assertSame(0, $opened);
64 }
65
66 public function testObservePrunesBeforeOpeningTheValue(): void
67 {
68 $source = 'function foo($x){return $x;}function bar(){if(false){foo(7);}}';
69 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
70 $index = new DefinitionIndex(new Index('fixture', [$tree]));
71 $settings = new EvaluatorConfig(evaluators: array_map((new MicroEvaluatorConversion())->convert(...), BranchEvaluators::truth()));
72 $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
73 [$caller, $call] = (new Callers($index))->sites('foo', 'bar')[0];
74 $opened = 0;
75 $value = (new Edge($engine))->observe(new Frame($caller, 'bar'), $caller->positions[$call->result][0], 32, static function () use (&$opened): Term {
76 ++$opened;
77 return Term::constant(99);
78 });
79 self::assertSame(TermKind::Choice, $value->kind);
80 self::assertSame([], $value->operands);
81 self::assertSame(0, $opened);
82 }
83
84}
85