packages/sql-faker/tests/Unit/MySql/Generation/Lexeme/CharsetLexemeGeneratorTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\MySql\Generation\Lexeme;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\UsesClass;
10use PHPUnit\Framework\TestCase;
11use SqlFaker\Generation\Lexeme\LexemeInput;
12use SqlFaker\Generation\Lexeme\ResolvedOutput;
13use SqlFaker\Generation\Token\TerminalSequence;
14use SqlFaker\MySql\Generation\Lexeme\CharsetLexemeGenerator;
15
16#[CoversClass(CharsetLexemeGenerator::class)]
17#[UsesClass(LexemeInput::class)]
18#[UsesClass(ResolvedOutput::class)]
19#[UsesClass(TerminalSequence::class)]
20#[UsesClass(\SqlFaker\Generation\Candidate\ChoiceLexemeGenerator::class)]
21#[UsesClass(\SqlFaker\Generation\Candidate\IntegerLexemeGenerator::class)]
22#[UsesClass(\SqlFaker\Generation\Lexeme\Lexeme::class)]
23#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeCandidates::class)]
24#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeSequence::class)]
25#[UsesClass(\SqlFaker\Generation\Candidate\ValueLexemeGenerator::class)]
26#[UsesClass(\SqlFaker\Generation\Token\TerminalOccurrence::class)]
27#[UsesClass(\SqlFaker\Generation\Lexeme\SpacingConstraint::class)]
28#[UsesClass(\SqlFaker\Generation\Token\ProductionOccurrence::class)]
29#[UsesClass(\SqlFaker\Generation\Value\CharacterDomain::class)]
30#[UsesClass(\SqlFaker\Generation\Value\ChoiceDomain::class)]
31#[UsesClass(\SqlFaker\Generation\Value\IntegerDomain::class)]
32#[UsesClass(\SqlFaker\Generation\Value\SequenceDomain::class)]
33#[UsesClass(\SqlFaker\MySql\Generation\Value\DollarQuotedDomain::class)]
34#[UsesClass(\SqlFaker\Generation\Lexeme\OutputPart::class)]
35#[UsesClass(\SqlFaker\Generation\Value\ValueChoices::class)]
36#[UsesClass(\SqlFaker\MySql\Generation\Value\RadixDomain::class)]
37#[UsesClass(\SqlFaker\Generation\Value\Utf8::class)]
38#[UsesClass(\SqlFaker\Generation\Value\WordDomain::class)]
39final class CharsetLexemeGeneratorTest extends TestCase
40{
41    #[DataProvider('providerEncodings')]
42    public function testAcceptsChecksTheSelectedCharsetRatherThanAssumingUtf8(string $charset, string $bytes, bool $expected): void
43    {
44        self::assertSame($expected, (new CharsetLexemeGenerator())->accepts($charset, $bytes));
45    }
46
47    /**
48     * @return list<array{string, string, bool}>
49     */
50    public static function providerEncodings(): array
51    {
52        return [['_utf8mb4', "\xff", false], ['_UTF8MB4', '😀', true], ['_utf8mb3', '猫', true], ['_utf8mb3', '😀', false], ['_ascii', "\0\x7f", true], ['_ascii', 'é', false], ['_latin1', "\xff", true], ['_binary', "\xff", true], ['unknown', 'a', false]];
53    }
54
55    #[DataProvider('providerBytes')]
56    public function testBytesDecodesQuotedAndPrefixBinaryFormsWithoutDroppingLeadingPartialBytes(string $literal, string $bytes): void
57    {
58        self::assertSame($bytes, (new CharsetLexemeGenerator())->bytes($literal));
59    }
60
61    /**
62     * @return list<array{string, string}>
63     */
64    public static function providerBytes(): array
65    {
66        return [["X'ffa600'", "\xff\xa6\0"], ['0x123', "\x01\x23"], ["X''", ''], ["B'111'", "\x07"], ['0b100000001', "\x01\x01"], ["B''", ''], ["'猫'", "'猫'"]];
67    }
68
69    public function testGenerateKeepsOnlyCharsetsCompatibleWithTheResolvedLiteral(): void
70    {
71        $tokens = TerminalSequence::fromNames(['UNDERSCORE_CHARSET', 'HEX_NUM']);
72        $right = new ResolvedOutput([new \SqlFaker\Generation\Lexeme\OutputPart(new \SqlFaker\Generation\Lexeme\Lexeme("X'ff'", 'number', $tokens->terminals[1], 'hex'), '', 'hex')]);
73        $generator = new CharsetLexemeGenerator();
74        $result = $generator->generate(new LexemeInput($tokens, 0, $right));
75        self::assertNotNull($result);
76        self::assertSame(['_latin1', '_binary'], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, [...$result->sequences()]));
77        $invalid = $generator->generate(new LexemeInput($tokens, 0, $right, '_utf8mb4'));
78        self::assertNotNull($invalid);
79        self::assertSame([], [...$invalid->sequences()]);
80        self::assertNull($generator->generate(new LexemeInput($tokens, 1, new ResolvedOutput())));
81    }
82}
83