packages/sql-parser/tests/Unit/Table/TableCodecTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Table;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\Small;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use RuntimeException;
12use SqlParser\Grammar\SymbolTable;
13use SqlParser\Table\ActionCode;
14use SqlParser\Table\ArrayRows;
15use SqlParser\Table\PackedRows;
16use SqlParser\Table\ParseTable;
17use SqlParser\Table\TableCodec;
18use SqlParser\Table\TableRule;
19
20#[CoversClass(TableCodec::class)]
21#[UsesClass(ActionCode::class)]
22#[UsesClass(ArrayRows::class)]
23#[UsesClass(PackedRows::class)]
24#[UsesClass(ParseTable::class)]
25#[UsesClass(SymbolTable::class)]
26#[UsesClass(TableRule::class)]
27#[Small]
28final class TableCodecTest extends TestCase
29{
30    public function testEncodeAndDecodeRoundTrip(): void
31    {
32        $symbols = new SymbolTable(['$end', 'ID', 'ABORT', 'ANY'], ['$accept', 's']);
33        $table = new ParseTable($symbols, [new TableRule(4, 2, 0), new TableRule(5, 1, 3, true)], [ActionCode::reduce(0), ActionCode::ERROR], new ArrayRows([[1 => 2, 5 => -3], []]), [2 => 1], 3);
34        $codec = new TableCodec();
35        $decoded = $codec->decode($codec->encode($table));
36
37        self::assertSame(['$end', 'ID', 'ABORT', 'ANY'], $decoded->symbols->terminals());
38        self::assertSame(['$accept', 's'], $decoded->symbols->nonterminals());
39        self::assertSame(2, $decoded->rules[0]->length);
40        self::assertTrue($decoded->rules[1]->hidden);
41        self::assertSame(3, $decoded->rules[1]->ordinal);
42        self::assertSame([ActionCode::reduce(0), ActionCode::ERROR], $decoded->defaults);
43        self::assertSame([1 => 2, 5 => -3], $decoded->rows->row(0));
44        self::assertSame([], $decoded->rows->row(1));
45        self::assertSame([2 => 1], $decoded->fallbacks);
46        self::assertSame(3, $decoded->wildcard);
47    }
48
49    public function testDecodeKeepsAMissingWildcard(): void
50    {
51        $codec = new TableCodec();
52        $table = new ParseTable(new SymbolTable(['$end'], ['$accept']), [], [], new ArrayRows([]));
53
54        self::assertNull($codec->decode($codec->encode($table))->wildcard);
55        self::assertSame(0, $codec->decode($codec->encode($table))->stateCount());
56    }
57
58    public function testDecodeRejectsForeignBytes(): void
59    {
60        $this->expectException(RuntimeException::class);
61
62        (new TableCodec())->decode('not a table');
63    }
64
65    public function testEncodeRowSortsBySymbol(): void
66    {
67        $codec = new TableCodec();
68
69        self::assertSame(pack('vv', 1, 5) . pack('ll', 9, 8), $codec->encodeRow([5 => 8, 1 => 9]));
70    }
71
72    public function testIntegers(): void
73    {
74        self::assertSame([1, 65535], TableCodec::integers('v*', pack('vv', 1, 65535)));
75        self::assertSame([], TableCodec::integers('v*', ''));
76    }
77
78    public function testIntegersRejectsPartialValues(): void
79    {
80        $this->expectException(RuntimeException::class);
81
82        TableCodec::integers('l*', 'abc');
83    }
84}
85