packages/deriver/tests/Unit/Deriver/Evaluator/Invocation/Path/TraversalTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Invocation\Path;
6
7use Deriver\Deriver\Evaluator\Call\Evaluators;
8use Deriver\Deriver\Evaluator\Enumeration\Choices;
9use Deriver\Deriver\Evaluator\Evaluator;
10use Deriver\Deriver\Evaluator\EvaluatorConfig;
11use Deriver\Deriver\Evaluator\Index\DefinitionIndex;
12use Deriver\Deriver\Evaluator\Invocation\Path\Traversal;
13use Deriver\Deriver\Evaluator\Request\Budget;
14use Deriver\Deriver\Evaluator\Request\CallPath;
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\Parser\Parser;
21use Deriver\Deriver\Parser\ParserConfig;
22use Deriver\Deriver\Resolver\Resolver;
23use Deriver\Deriver\Sources\Source;
24use PHPUnit\Framework\Attributes\CoversNothing;
25use PHPUnit\Framework\TestCase;
26
27#[CoversNothing]
28final class TraversalTest extends TestCase
29{
30    public function testSelectBindsEverySelectedContext(): void
31    {
32        $source = 'function foo($x){return $x;}function bar(){foo(7);}function other(){foo(9);}';
33        $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
34        $index = new DefinitionIndex(new Index('fixture', [$tree]));
35        $settings = new EvaluatorConfig();
36        $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
37        $graph = $index->graph('foo');
38        self::assertNotNull($graph);
39        $value = (new Traversal($engine, new CallPath(['bar'])))->select($graph, fn (Frame $frame): Term => $engine->returns($frame, 32));
40        self::assertSame(7, iterator_to_array((new Choices())->alternatives($value))[0][0]->native());
41    }
42
43    public function testWalkLeavesTheOuterPrefixOpen(): void
44    {
45        $source = 'function foo($x){return $x;}function bar(){foo(7);}function other(){foo(9);}';
46        $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
47        $index = new DefinitionIndex(new Index('fixture', [$tree]));
48        $settings = new EvaluatorConfig();
49        $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
50        $graph = $index->graph('bar');
51        self::assertNotNull($graph);
52        $value = (new Traversal($engine, new CallPath(['bar'])))->walk($graph, -1, static fn (Frame $frame): Term => Term::constant($frame->invocation), 32);
53        self::assertFalse(iterator_to_array((new Choices())->alternatives($value))[0][0]->native());
54    }
55
56    public function testResidualKeepsUnfinishedAncestryOnTheObservedValue(): void
57    {
58        $source = 'function foo($x){return $x;}function bar(){foo(7);}function other(){foo(9);}';
59        $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
60        $index = new DefinitionIndex(new Index('fixture', [$tree]));
61        $settings = new EvaluatorConfig();
62        $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
63        $graph = $index->graph('foo');
64        self::assertNotNull($graph);
65        $value = (new Traversal($engine, new CallPath(['bar'])))->residual($graph, static fn (): Term => Term::constant(7), 'DEPTH_LIMIT');
66        self::assertSame(TermKind::ControlledValue, $value->kind);
67        self::assertSame('DEPTH_LIMIT', $value->attributes['reason']);
68        self::assertSame(7, $value->operands[0]->native());
69    }
70
71    public function testJoinPreservesIndependentAlternatives(): void
72    {
73        $source = 'function foo($x){return $x;}function bar(){foo(7);}function other(){foo(9);}';
74        $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('fixture.php', '<?php ' . $source)));
75        $index = new DefinitionIndex(new Index('fixture', [$tree]));
76        $settings = new EvaluatorConfig();
77        $engine = new Evaluator(new Context($index, $settings, new Evaluators($settings->target), new Budget()));
78        $value = (new Traversal($engine, new CallPath(['bar'])))->join([static fn (): Term => Term::constant(7), static fn (): Term => Term::constant(9)]);
79        self::assertSame([7,9], array_map(static fn (array $row) => $row[0]->native(), iterator_to_array((new Choices())->alternatives($value))));
80    }
81
82}
83