packages/sql-faker/tests/Unit/Generation/Derivation/CompletionReductionTest.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(CompletionReduction::class)]
26#[UsesClass(CompletionCosts::class)]
27#[UsesClass(CompletionFrontier::class)]
28#[UsesClass(CompletionState::class)]
29#[UsesClass(CompletionMemo::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 CompletionReductionTest extends TestCase
43{
44 public function testReduceKeepsBothIndependentOutputPossibilitiesAndTheirExactCosts(): void
45 {
46 $grammar = new Grammar('free', [
47 'free' => new ProductionRule('free', [new Production([]), new Production([new NonTerminal('output')])]),
48 'output' => new ProductionRule('output', [new Production([new Terminal('T')])]),
49 'fixed' => new ProductionRule('fixed', [new Production([]), new Production([new Terminal('T')])]),
50 ]);
51 $costs = new CompletionCosts($grammar, static fn (string $name): bool => false);
52 $frontier = new CompletionFrontier($costs, 10);
53 $reduction = new CompletionReduction($costs, new ConstraintDependencies($grammar));
54 $plan = GenerationPlan::all()->withPatternForEveryOccurrence('fixed', ProductionPattern::at(0));
55 self::assertTrue($reduction->reduce(new CompletionState([new NonTerminal('free'), new NonTerminal('fixed')], [], true, 0), $plan, $frontier));
56 self::assertTrue($frontier->take()?->nonEmpty);
57 self::assertFalse($frontier->take()?->nonEmpty);
58 self::assertNull($frontier->take());
59 self::assertFalse($reduction->reduce(new CompletionState([new NonTerminal('fixed')], [], true, 0), $plan, $frontier));
60 self::assertTrue($reduction->reduce(new CompletionState([new NonTerminal('free')], [], false, 3), $plan, $frontier));
61 self::assertSame(4, $frontier->take()?->spent);
62 }
63}
64