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