packages/sql-faker/tests/Unit/Sqlite/Generation/Rewrite/FunctionArgumentRuleTest.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\Generation\Token\ProductionOccurrence;
13use SqlFaker\Generation\Token\TerminalOccurrence;
14use SqlFaker\Generation\Token\TerminalSequence;
15use SqlFaker\Grammar\Model\NonTerminal;
16use SqlFaker\Grammar\Model\Production;
17use SqlFaker\Grammar\Model\Terminal;
18use SqlFaker\Sqlite\Generation\Rewrite\FunctionArgumentRule;
19
20#[CoversClass(FunctionArgumentRule::class)]
21#[UsesClass(DerivationTrace::class)]
22#[UsesClass(ProductionOccurrence::class)]
23#[UsesClass(TerminalOccurrence::class)]
24#[UsesClass(TerminalSequence::class)]
25#[UsesClass(NonTerminal::class)]
26#[UsesClass(Production::class)]
27#[UsesClass(Terminal::class)]
28final class FunctionArgumentRuleTest extends TestCase
29{
30    /**
31     * @param list<string> $expected
32     */
33    #[DataProvider('providerArgumentLists')]
34    public function testRewriteLimitsDirectFunctionArguments(TerminalSequence $input, array $expected): void
35    {
36        $rule = new FunctionArgumentRule();
37        $result = $rule->rewrite($input);
38        self::assertSame($expected, $result->names());
39        self::assertSame($input->original, $result->original);
40        self::assertSame($input->productions, $result->productions);
41        self::assertSame($input->terminals[2], $result->terminals[2]);
42        self::assertNotEmpty($input->terminals);
43        self::assertNotEmpty($result->terminals);
44        self::assertSame($input->terminals[array_key_last($input->terminals)], $result->terminals[array_key_last($result->terminals)]);
45        self::assertSame($result, $rule->rewrite($result));
46    }
47
48    /**
49     * @return iterable<array{TerminalSequence, list<string>}>
50     */
51    public static function providerArgumentLists(): iterable
52    {
53        foreach ([['expr', 'idj', true], ['expr', 'nm', false], ['ordinary', 'idj', false]] as [$scope, $name, $limited]) {
54            foreach ([1, 126, 127, 128, 130] as $count) {
55                $trace = new DerivationTrace($scope);
56                $trace->expand(0, new Production([new NonTerminal($name), new Terminal('LP'), new NonTerminal('exprlist'), new Terminal('RP'), new Terminal('TAIL')]), 0);
57                $trace->expand(0, new Production([new Terminal('NAME')]), 0);
58                $trace->expand(2, new Production([new NonTerminal('nexprlist')]), 0);
59                for ($remaining = $count; $remaining > 1; --$remaining) {
60                    $trace->expand(2, new Production([new NonTerminal('nexprlist'), new Terminal('COMMA'), new NonTerminal('expr')]), 0);
61                }
62                $trace->expand(2, new Production([new NonTerminal('expr')]), 1);
63                $expected = ['NAME', 'LP'];
64                for ($index = 0; $index < $count; ++$index) {
65                    $trace->expand(2 + 4 * $index, new Production([new NonTerminal('expr'), new Terminal('PLUS'), new Terminal('INTEGER')]), 0);
66                    $trace->expand(2 + 4 * $index, new Production([new Terminal('ARG_' . $index)]), 0);
67                    if (!$limited || $index < 127) {
68                        if ($index !== 0) {
69                            $expected[] = 'COMMA';
70                        }
71                        array_push($expected, 'ARG_' . $index, 'PLUS', 'INTEGER');
72                    }
73                }
74                yield [$trace->terminals(), [...$expected, 'RP', 'TAIL']];
75            }
76        }
77    }
78
79    public function testRewritePreservesEmptyArgumentsAndStandaloneLists(): void
80    {
81        $trace = new DerivationTrace('expr');
82        $trace->expand(0, new Production([new NonTerminal('idj'), new Terminal('LP'), new NonTerminal('exprlist'), new Terminal('RP')]), 0);
83        $trace->expand(0, new Production([new Terminal('NAME')]), 0);
84        $trace->expand(2, new Production([]), 0);
85        $input = $trace->terminals();
86        self::assertSame($input, (new FunctionArgumentRule())->rewrite($input));
87        $standalone = new TerminalSequence([], [], [], [new ProductionOccurrence(0, null, 'exprlist', 0)]);
88        self::assertSame($standalone, (new FunctionArgumentRule())->rewrite($standalone));
89    }
90}
91