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