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