packages/sql-faker/tests/Unit/Generation/Derivation/CompletionMemoTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Generation\Derivation;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Generation\Derivation\CompletionCosts;
11use SqlFaker\Generation\Derivation\CompletionFrontier;
12use SqlFaker\Generation\Derivation\CompletionMemo;
13use SqlFaker\Generation\Derivation\CompletionReduction;
14use SqlFaker\Generation\Derivation\CompletionState;
15use SqlFaker\Generation\Derivation\ConstrainedCompletion;
16use SqlFaker\Generation\Derivation\ConstraintDependencies;
17use SqlFaker\Generation\Plan\GenerationPlan;
18use SqlFaker\Generation\Plan\ProductionPattern;
19use SqlFaker\Grammar\Model\Grammar;
20use SqlFaker\Grammar\Model\NonTerminal;
21use SqlFaker\Grammar\Model\Production;
22use SqlFaker\Grammar\Model\ProductionRule;
23use SqlFaker\Grammar\Model\Terminal;
24
25#[CoversClass(CompletionMemo::class)]
26#[UsesClass(CompletionCosts::class)]
27#[UsesClass(CompletionFrontier::class)]
28#[UsesClass(CompletionState::class)]
29#[UsesClass(CompletionReduction::class)]
30#[UsesClass(ConstraintDependencies::class)]
31#[UsesClass(ConstrainedCompletion::class)]
32#[UsesClass(GenerationPlan::class)]
33#[UsesClass(ProductionPattern::class)]
34#[UsesClass(Grammar::class)]
35#[UsesClass(NonTerminal::class)]
36#[UsesClass(Production::class)]
37#[UsesClass(ProductionRule::class)]
38#[UsesClass(Terminal::class)]
39#[UsesClass(\SqlFaker\Generation\Choice\BytePlanCompiler::class)]
40#[UsesClass(\SqlFaker\Generation\Derivation\Completion\PatternProductions::class)]
41#[UsesClass(\SqlFaker\Generation\Derivation\Completion\CompletionWitness::class)]
42final class CompletionMemoTest extends TestCase
43{
44 public function testRecallDoesNotConfuseAnAffordableWitnessWithAnExactMinimum(): void
45 {
46 $memo = new CompletionMemo();
47 $plan = GenerationPlan::all();
48 self::assertNull($memo->recall($plan, 'pending', 20, false));
49 $memo->remember($plan, 'pending', 20, 10, false);
50 self::assertSame(10, $memo->recall($plan, 'pending', 10, false));
51 self::assertNull($memo->recall($plan, 'pending', 9, false));
52 self::assertNull($memo->recall($plan, 'pending', 20, true));
53 $memo->remember($plan, 'pending', 9, 7, true);
54 self::assertSame(7, $memo->recall($plan, 'pending', 20, true));
55 self::assertSame(PHP_INT_MAX, $memo->recall($plan, 'pending', 6, false));
56 }
57
58 public function testRememberExhaustionDoesNotRuleOutLargerBudgetsOrAnotherPlan(): void
59 {
60 $memo = new CompletionMemo();
61 $plan = GenerationPlan::all();
62 self::assertNull($memo->recall($plan, 'pending', 5, true));
63 $memo->remember($plan, 'pending', 5, PHP_INT_MAX, false);
64 $memo->remember($plan, 'pending', 3, PHP_INT_MAX, true);
65 self::assertSame(PHP_INT_MAX, $memo->recall($plan, 'pending', 5, true));
66 self::assertNull($memo->recall($plan, 'pending', 6, true));
67 self::assertNull($memo->recall(GenerationPlan::fromRule('different'), 'pending', 5, true));
68 }
69
70 public function testActivateDropsProofsForAnotherPlanEvenWhenItsPendingFormMatches(): void
71 {
72 $memo = new CompletionMemo();
73 $first = GenerationPlan::all();
74 $second = GenerationPlan::fromRule('other');
75 $memo->remember($first, 'pending', 5, 3, true);
76 $memo->activate($second);
77 self::assertNull($memo->recall($second, 'pending', 5, true));
78 $memo->remember($first, 'pending', 5, PHP_INT_MAX, false);
79 self::assertNull($memo->recall($second, 'pending', 5, false));
80 }
81}
82