packages/sql-faker/tests/Unit/Sqlite/Generation/Rewrite/CompoundSelectRuleTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Sqlite\Generation\Rewrite;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Derivation\DerivationTrace;
12use SqlFaker\Grammar\Model\NonTerminal;
13use SqlFaker\Grammar\Model\Production;
14use SqlFaker\Grammar\Model\Terminal;
15use SqlFaker\Sqlite\Generation\Rewrite\CompoundSelectRule;
16
17#[CoversClass(CompoundSelectRule::class)]
18#[UsesClass(DerivationTrace::class)]
19#[UsesClass(\SqlFaker\Generation\Token\ProductionOccurrence::class)]
20#[UsesClass(\SqlFaker\Generation\Token\TerminalOccurrence::class)]
21#[UsesClass(\SqlFaker\Generation\Token\TerminalSequence::class)]
22#[UsesClass(NonTerminal::class)]
23#[UsesClass(Production::class)]
24#[UsesClass(Terminal::class)]
25final class CompoundSelectRuleTest extends TestCase
26{
27    /**
28     * @param non-empty-list<string> $operator
29     */
30    #[DataProvider('providerOperators')]
31    public function testRewriteKeepsNestedAndFinalClausesForEveryCompoundOperator(array $operator): void
32    {
33        $trace = new DerivationTrace('selectnowith');
34        $trace->expand(0, new Production([new NonTerminal('selectnowith'), new NonTerminal('multiselect_op'), new NonTerminal('oneselect')]), 1);
35        $trace->expand(0, new Production([new NonTerminal('oneselect')]), 0);
36        $trace->expand(0, new Production([new Terminal('SELECT'), new NonTerminal('expr'), new NonTerminal('orderby_opt'), new NonTerminal('limit_opt')]), 0);
37        $trace->expand(1, new Production([new Terminal('LP'), new NonTerminal('selectnowith'), new Terminal('RP')]), 0);
38        $trace->expand(2, new Production([new NonTerminal('oneselect')]), 0);
39        $trace->expand(2, new Production([new Terminal('SELECT'), new Terminal('INTEGER'), new NonTerminal('orderby_opt'), new NonTerminal('limit_opt')]), 0);
40        $trace->expand(4, new Production([new Terminal('ORDER'), new Terminal('BY'), new Terminal('INTEGER')]), 0);
41        $trace->expand(7, new Production([new Terminal('LIMIT'), new Terminal('INTEGER')]), 0);
42        $trace->expand(10, new Production([new Terminal('ORDER'), new Terminal('BY'), new Terminal('INTEGER')]), 0);
43        $trace->expand(13, new Production([new Terminal('LIMIT'), new Terminal('INTEGER')]), 0);
44        $trace->expand(15, new Production(array_map(static fn (string $name): Terminal => new Terminal($name), $operator)), 0);
45        $trace->expand(15 + count($operator), new Production([new Terminal('SELECT'), new Terminal('INTEGER'), new NonTerminal('orderby_opt'), new NonTerminal('limit_opt')]), 0);
46        $trace->expand(17 + count($operator), new Production([new Terminal('ORDER'), new Terminal('BY'), new Terminal('INTEGER')]), 0);
47        $trace->expand(20 + count($operator), new Production([new Terminal('LIMIT'), new Terminal('INTEGER')]), 0);
48        $input = $trace->terminals();
49        $rule = new CompoundSelectRule();
50        $result = $rule->rewrite($input);
51        self::assertSame(['SELECT', 'LP', 'SELECT', 'INTEGER', 'ORDER', 'BY', 'INTEGER', 'LIMIT', 'INTEGER', 'RP', ...$operator, 'SELECT', 'INTEGER', 'ORDER', 'BY', 'INTEGER', 'LIMIT', 'INTEGER'], $result->names());
52        self::assertSame($input->original, $result->original);
53        self::assertCount(count($result->terminals), array_unique(array_map(static fn ($terminal): int => $terminal->id, $result->terminals)));
54        self::assertSame($input->productions, $result->productions);
55        self::assertSame($result, $rule->rewrite($result));
56    }
57
58    /**
59     * @return list<array{non-empty-list<string>}>
60     */
61    public static function providerOperators(): array
62    {
63        return [[['UNION']], [['UNION', 'ALL']], [['INTERSECT']], [['EXCEPT']]];
64    }
65
66    public function testRewriteLeavesASingleSelectAndEmptyPriorClausesUnchanged(): void
67    {
68        $trace = new DerivationTrace('selectnowith');
69        $trace->expand(0, new Production([new NonTerminal('selectnowith'), new Terminal('UNION'), new NonTerminal('oneselect')]), 1);
70        $trace->expand(0, new Production([new NonTerminal('oneselect')]), 0);
71        $trace->expand(0, new Production([new Terminal('SELECT'), new Terminal('INTEGER'), new NonTerminal('orderby_opt'), new NonTerminal('limit_opt')]), 0);
72        $trace->expand(2, new Production([]), 0);
73        $trace->expand(2, new Production([]), 0);
74        $trace->expand(3, new Production([new Terminal('SELECT'), new Terminal('INTEGER'), new NonTerminal('limit_opt')]), 0);
75        $trace->expand(5, new Production([new Terminal('LIMIT'), new Terminal('INTEGER')]), 0);
76        $input = $trace->terminals();
77        self::assertSame($input, (new CompoundSelectRule())->rewrite($input));
78        $single = new DerivationTrace('selectnowith');
79        $single->expand(0, new Production([new NonTerminal('oneselect')]), 0);
80        $single->expand(0, new Production([new Terminal('SELECT'), new Terminal('INTEGER'), new NonTerminal('limit_opt')]), 0);
81        $single->expand(2, new Production([new Terminal('LIMIT'), new Terminal('INTEGER')]), 0);
82        $input = $single->terminals();
83        self::assertSame($input, (new CompoundSelectRule())->rewrite($input));
84    }
85}
86