packages/sql-faker/tests/Unit/MySql/Generation/Rewrite/Expression/ConcatenationRuleTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\Generation\Rewrite\Expression;
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\MySql\Generation\Rewrite\Expression\ConcatenationRule;
19
20#[CoversClass(ConcatenationRule::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 ConcatenationRuleTest extends TestCase
29{
30    public function testRewritePreservesNestedOperandsAndPrecedence(): void
31    {
32        $trace = new DerivationTrace('simple_expr');
33        $trace->expand(0, new Production([new NonTerminal('simple_expr'), new Terminal('OR_OR_SYM'), new NonTerminal('simple_expr')]), 0);
34        $trace->expand(0, new Production([new NonTerminal('simple_expr'), new Terminal('OR_OR_SYM'), new NonTerminal('simple_expr')]), 0);
35        $trace->expand(0, new Production([new Terminal('A')]), 1);
36        $trace->expand(2, new Production([new Terminal('B')]), 1);
37        $trace->expand(4, new Production([new Terminal('C')]), 1);
38        $input = $trace->terminals();
39        $rule = new ConcatenationRule();
40        $result = $rule->rewrite($input);
41        self::assertSame(['CONCAT_FUNCTION_NAME', '(', 'CONCAT_FUNCTION_NAME', '(', 'A', ',', 'B', ')', ',', 'C', ')'], $result->names());
42        self::assertSame($input->original, $result->original);
43        self::assertSame($input->productions, $result->productions);
44        self::assertSame($input->terminals[0], $result->terminals[4]);
45        self::assertSame($input->terminals[2], $result->terminals[6]);
46        self::assertSame($input->terminals[4], $result->terminals[9]);
47        self::assertSame($result, $rule->rewrite($result));
48        self::assertCount(2, $result->operations);
49        $ids = array_column($result->terminals, 'id');
50        self::assertSame($ids, array_values(array_unique($ids)));
51    }
52
53    #[DataProvider('providerOtherProductions')]
54    public function testRewritePreservesUnrelatedAndEmptyProductions(TerminalSequence $input): void
55    {
56        self::assertSame($input, (new ConcatenationRule())->rewrite($input));
57    }
58
59    /**
60     * @return iterable<array{TerminalSequence}>
61     */
62    public static function providerOtherProductions(): iterable
63    {
64        yield [TerminalSequence::fromNames(['A', 'OR2_SYM', 'B'])];
65        yield [new TerminalSequence([], [], [], [new ProductionOccurrence(0, null, 'simple_expr', 0)])];
66        $trace = new DerivationTrace('simple_expr');
67        $trace->expand(0, new Production([new NonTerminal('unrelated')]), 0);
68        $trace->expand(0, new Production([new Terminal('OR_OR_SYM')]), 0);
69        yield [$trace->terminals()];
70    }
71}
72