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