packages/sql-faker/tests/Unit/Grammar/Model/ProductionTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Grammar\Model;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\TestCase;
9use SqlFaker\Grammar\Model\NonTerminal;
10use SqlFaker\Grammar\Model\Production;
11use SqlFaker\Grammar\Model\Terminal;
12
13#[CoversClass(Production::class)]
14#[CoversClass(Terminal::class)]
15#[CoversClass(NonTerminal::class)]
16final class ProductionTest extends TestCase
17{
18 public function testExposesTheSymbolsItWasBuiltFrom(): void
19 {
20 $symbols = [new Terminal('A'), new NonTerminal('b')];
21 $production = new Production($symbols);
22
23 self::assertSame($symbols, $production->symbols);
24 }
25
26 public function testExposesAnEmptySymbolListForAnEmptyProduction(): void
27 {
28 $production = new Production([]);
29
30 self::assertSame([], $production->symbols);
31 }
32
33 public function testSymbolsAreAccessible(): void
34 {
35 $terminal = new Terminal('X');
36 $production = new Production([$terminal]);
37
38 self::assertSame($terminal, $production->symbols[0]);
39 }
40
41 public function testHasTerminalFindsATerminalTheProductionWrites(): void
42 {
43 self::assertTrue((new Production([new NonTerminal('a'), new Terminal('WITHIN')]))->hasTerminal('WITHIN'));
44 }
45
46 public function testHasTerminalRejectsATerminalTheProductionDoesNotWrite(): void
47 {
48 self::assertFalse((new Production([new NonTerminal('WITHIN')]))->hasTerminal('WITHIN'));
49 }
50
51 public function testHasNonTerminalFindsARuleTheProductionExpandsTo(): void
52 {
53 self::assertTrue((new Production([new Terminal('A'), new NonTerminal('frame_opt')]))->hasNonTerminal('frame_opt'));
54 }
55
56 public function testHasNonTerminalRejectsARuleTheProductionDoesNotExpandTo(): void
57 {
58 self::assertFalse((new Production([new Terminal('frame_opt')]))->hasNonTerminal('frame_opt'));
59 }
60
61 public function testHasAnyTerminalReportsAProductionThatWritesSomethingOfItsOwn(): void
62 {
63 self::assertTrue((new Production([new NonTerminal('a'), new Terminal('OVER')]))->hasAnyTerminal());
64 }
65
66 public function testHasAnyTerminalRejectsAProductionMadeOnlyOfRules(): void
67 {
68 self::assertFalse((new Production([new NonTerminal('a'), new NonTerminal('b')]))->hasAnyTerminal());
69 }
70
71 public function testTerminalAtAnswersTheTerminalAtThatPosition(): void
72 {
73 $terminal = new Terminal('TABLE');
74
75 self::assertSame($terminal, (new Production([new Terminal('DROP'), $terminal]))->terminalAt(1));
76 }
77
78 public function testTerminalAtAnswersNothingForANonTerminalOrAPositionPastTheEnd(): void
79 {
80 $production = new Production([new NonTerminal('a')]);
81
82 self::assertNull($production->terminalAt(0));
83 self::assertNull($production->terminalAt(5));
84 }
85
86 public function testNonTerminalAtAnswersTheRuleAtThatPosition(): void
87 {
88 $nonTerminal = new NonTerminal('insert_cmd');
89
90 self::assertSame($nonTerminal, (new Production([new NonTerminal('with'), $nonTerminal]))->nonTerminalAt(1));
91 }
92
93 public function testNonTerminalAtAnswersNothingForATerminalOrAPositionPastTheEnd(): void
94 {
95 $production = new Production([new Terminal('A')]);
96
97 self::assertNull($production->nonTerminalAt(0));
98 self::assertNull($production->nonTerminalAt(5));
99 }
100
101 public function testNonTerminalNamesListsTheRulesInTheOrderTheyAreWritten(): void
102 {
103 $production = new Production([new NonTerminal('nm'), new Terminal('AS'), new NonTerminal('frame_opt')]);
104
105 self::assertSame(['nm', 'frame_opt'], $production->nonTerminalNames());
106 }
107}
108