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