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