packages/sql-faker/tests/Unit/Compiler/Bison/Lexer/BisonTriviaTest.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\BisonTrivia;
11use SqlFaker\Compiler\Bison\Lexer\SourceCursor;
12use SqlFaker\Compiler\GrammarParseException;
13
14#[CoversClass(BisonTrivia::class)]
15#[UsesClass(GrammarParseException::class)]
16#[UsesClass(SourceCursor::class)]
17final class BisonTriviaTest extends TestCase
18{
19 public function testSkipFromLeavesTheCursorOnTheFirstLexeme(): void
20 {
21 $cursor = new SourceCursor(" \n /* a */ // b\n /* c */ foo");
22
23 (new BisonTrivia())->skipFrom($cursor);
24
25 self::assertSame('foo', $cursor->takeRest());
26 }
27
28 public function testSkipFromRunsToTheEndWhenOnlyTriviaRemains(): void
29 {
30 $cursor = new SourceCursor("/* a */\n // b");
31
32 (new BisonTrivia())->skipFrom($cursor);
33
34 self::assertTrue($cursor->atEnd());
35 }
36
37 public function testSkipFromLeavesALexemeAlone(): void
38 {
39 $cursor = new SourceCursor('foo');
40
41 (new BisonTrivia())->skipFrom($cursor);
42
43 self::assertSame(0, $cursor->offset());
44 }
45
46 public function testSkipFromReportsASlashThatOpensNoComment(): void
47 {
48 $cursor = new SourceCursor(' / not a comment');
49
50 $this->expectException(GrammarParseException::class);
51 $this->expectExceptionMessage("Unexpected '/' at offset 2");
52
53 (new BisonTrivia())->skipFrom($cursor);
54 }
55
56 public function testSkipCommentAtConsumesALineComment(): void
57 {
58 $cursor = new SourceCursor("// note\nfoo");
59
60 self::assertTrue((new BisonTrivia())->skipCommentAt($cursor));
61 self::assertSame("\nfoo", $cursor->takeRest());
62 }
63
64 public function testSkipCommentAtConsumesABlockComment(): void
65 {
66 $cursor = new SourceCursor('/* note */foo');
67
68 self::assertTrue((new BisonTrivia())->skipCommentAt($cursor));
69 self::assertSame('foo', $cursor->takeRest());
70 }
71
72 public function testSkipCommentAtLeavesALoneSlashInPlace(): void
73 {
74 $cursor = new SourceCursor('/ division');
75
76 self::assertFalse((new BisonTrivia())->skipCommentAt($cursor));
77 self::assertSame(0, $cursor->offset());
78 }
79
80 public function testSkipCommentAtLeavesAnythingElseInPlace(): void
81 {
82 $cursor = new SourceCursor('foo');
83
84 self::assertFalse((new BisonTrivia())->skipCommentAt($cursor));
85 self::assertSame(0, $cursor->offset());
86 }
87
88 public function testSkipToEndOfLineStopsBeforeTheNewline(): void
89 {
90 $cursor = new SourceCursor("// note\nfoo");
91
92 self::assertTrue((new BisonTrivia())->skipToEndOfLine($cursor));
93 self::assertSame("\nfoo", $cursor->takeRest());
94 }
95
96 public function testSkipToCommentCloseConsumesTheTerminator(): void
97 {
98 $cursor = new SourceCursor('/* note */foo');
99
100 self::assertTrue((new BisonTrivia())->skipToCommentClose($cursor));
101 self::assertSame('foo', $cursor->takeRest());
102 }
103
104 public function testSkipToCommentCloseRunsToTheEndWhenNeverClosed(): void
105 {
106 $cursor = new SourceCursor('/* never closed');
107
108 self::assertTrue((new BisonTrivia())->skipToCommentClose($cursor));
109 self::assertTrue($cursor->atEnd());
110 }
111}
112