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