packages/sql-faker/tests/Unit/Compiler/Bison/Lexer/ActionScannerTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Compiler\Bison\Lexer;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Compiler\Bison\Lexer\ActionScanner;
11use SqlFaker\Compiler\Bison\Lexer\BisonLexeme;
12use SqlFaker\Compiler\Bison\Lexer\BisonToken;
13use SqlFaker\Compiler\Bison\Lexer\BisonTrivia;
14use SqlFaker\Compiler\Bison\Lexer\SourceCursor;
15
16#[CoversClass(ActionScanner::class)]
17#[UsesClass(BisonLexeme::class)]
18#[UsesClass(BisonToken::class)]
19#[UsesClass(BisonTrivia::class)]
20#[UsesClass(SourceCursor::class)]
21#[UsesClass(\SqlFaker\Compiler\GrammarParseException::class)]
22final class ActionScannerTest extends TestCase
23{
24 public function testHandlesClaimsOnlyAnOpeningBrace(): void
25 {
26 $scanner = new ActionScanner();
27
28 self::assertTrue($scanner->handles('{'));
29 self::assertFalse($scanner->handles('}'));
30 }
31
32 public function testScanExcludesTheOuterBracesFromTheBody(): void
33 {
34 $cursor = new SourceCursor('{ $$ = $1; } rest');
35
36 $token = (new ActionScanner())->scan($cursor);
37
38 self::assertSame(BisonLexeme::Action, $token->type);
39 self::assertSame(' $$ = $1; ', $token->value);
40 self::assertSame(0, $token->offset);
41 self::assertSame(' rest', $cursor->takeRest());
42 }
43
44 public function testScanReadsAnEmptyActionAsAnEmptyBody(): void
45 {
46 $cursor = new SourceCursor('{}');
47
48 self::assertSame('', (new ActionScanner())->scan($cursor)->value);
49 }
50
51 public function testScanKeepsNestedBracesInTheBody(): void
52 {
53 $cursor = new SourceCursor('{ if (x) { y(); } } rest');
54
55 self::assertSame(' if (x) { y(); } ', (new ActionScanner())->scan($cursor)->value);
56 self::assertSame(' rest', $cursor->takeRest());
57 }
58
59 public function testScanIgnoresABraceInsideAString(): void
60 {
61 $cursor = new SourceCursor('{ printf("}"); } rest');
62
63 self::assertSame(' printf("}"); ', (new ActionScanner())->scan($cursor)->value);
64 self::assertSame(' rest', $cursor->takeRest());
65 }
66
67 public function testScanIgnoresABraceInsideACharacterLiteral(): void
68 {
69 $cursor = new SourceCursor("{ c = '}'; } rest");
70
71 self::assertSame(" c = '}'; ", (new ActionScanner())->scan($cursor)->value);
72 self::assertSame(' rest', $cursor->takeRest());
73 }
74
75 public function testScanIgnoresABraceInsideALineComment(): void
76 {
77 $cursor = new SourceCursor("{ // }\n x; } rest");
78
79 self::assertSame(" // }\n x; ", (new ActionScanner())->scan($cursor)->value);
80 self::assertSame(' rest', $cursor->takeRest());
81 }
82
83 public function testScanIgnoresABraceInsideABlockComment(): void
84 {
85 $cursor = new SourceCursor('{ /* } */ x; } rest');
86
87 self::assertSame(' /* } */ x; ', (new ActionScanner())->scan($cursor)->value);
88 self::assertSame(' rest', $cursor->takeRest());
89 }
90
91 public function testScanReadsALoneSlashAsBodyText(): void
92 {
93 $cursor = new SourceCursor('{ a / b } rest');
94
95 self::assertSame(' a / b ', (new ActionScanner())->scan($cursor)->value);
96 }
97
98 public function testScanRunsAnUnclosedActionToTheEndOfTheFile(): void
99 {
100 $cursor = new SourceCursor('{ never closed');
101
102 self::assertSame(' never closed', (new ActionScanner())->scan($cursor)->value);
103 self::assertTrue($cursor->atEnd());
104 }
105}
106