packages/sql-faker/tests/Unit/MySql/Generation/Lexeme/BoundedIntegerLexemeGeneratorTest.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\BoundedIntegerLexemeGenerator;
15
16#[CoversClass(BoundedIntegerLexemeGenerator::class)]
17#[UsesClass(\SqlFaker\Generation\Candidate\IntegerLexemeGenerator::class)]
18#[UsesClass(\SqlFaker\Generation\Lexeme\Lexeme::class)]
19#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeCandidates::class)]
20#[UsesClass(LexemeInput::class)]
21#[UsesClass(\SqlFaker\Generation\Lexeme\LexemeSequence::class)]
22#[UsesClass(ResolvedOutput::class)]
23#[UsesClass(\SqlFaker\Generation\Lexeme\SpacingConstraint::class)]
24#[UsesClass(\SqlFaker\Generation\Token\ProductionOccurrence::class)]
25#[UsesClass(\SqlFaker\Generation\Token\TerminalOccurrence::class)]
26#[UsesClass(TerminalSequence::class)]
27#[UsesClass(\SqlFaker\Generation\Value\CharacterDomain::class)]
28#[UsesClass(\SqlFaker\Generation\Value\IntegerDomain::class)]
29#[UsesClass(\SqlFaker\MySql\Generation\Value\RadixDomain::class)]
30#[UsesClass(\SqlFaker\Generation\Value\SequenceDomain::class)]
31#[UsesClass(\SqlFaker\Generation\Value\WordDomain::class)]
32final class BoundedIntegerLexemeGeneratorTest extends TestCase
33{
34 public function testGenerateSupportsASingletonDomainAndContinuesPastInvalidDefaults(): void
35 {
36 $generator = new BoundedIntegerLexemeGenerator('NUMBER', 1, 1, ['0', '1', '2', '0x1'], 'source');
37 $result = $generator->generate(new LexemeInput(TerminalSequence::fromNames(['NUMBER']), 0, new ResolvedOutput()));
38 self::assertNotNull($result);
39 self::assertSame(['1', '0x1'], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, [...$result->sequences()]));
40 self::assertCount(2, array_unique(array_map(static fn ($candidate): string => $candidate->id, [...$result->sequences()])));
41 }
42
43 public function testAcceptsUsesBothConfiguredBoundsForDecimalAndHexadecimalValues(): void
44 {
45 $delay = new BoundedIntegerLexemeGenerator('DELAY', 0, 2147483647, ['0'], 'delay');
46 self::assertTrue($delay->accepts('0'));
47 self::assertTrue($delay->accepts("X''"));
48 self::assertTrue($delay->accepts('0x7fffffff'));
49 self::assertFalse($delay->accepts('0x80000000'));
50 $pages = new BoundedIntegerLexemeGenerator('PAGES', 1, 65535, ['1'], 'pages');
51 self::assertFalse($pages->accepts('0'));
52 self::assertTrue($pages->accepts('65535'));
53 self::assertTrue($pages->accepts('0xffff'));
54 self::assertFalse($pages->accepts('65536'));
55 self::assertFalse($pages->accepts('0x10000'));
56 }
57
58 #[DataProvider('providerIndices')]
59 public function testAcceptsUnsignedFormsWithinTheSourceInterval(string $spelling, bool $valid): void
60 {
61 self::assertSame($valid, (new BoundedIntegerLexemeGenerator('BINLOG_RESET_INDEX', 1, 2000000000, ['1', '2000000000', '0x1'], 'sql/sql_yacc.yy:source_reset_options'))->accepts($spelling));
62 }
63
64 /**
65 * @return list<array{string, bool}>
66 */
67 public static function providerIndices(): array
68 {
69 return [['1', true], ['0001', true], ['2000000000', true], ['0x77359400', true], ["X'77359400'", true], ["x'0001'", true], ['0x000000001', true], ['0', false], ['2000000001', false], ['0x77359401', false], ["X'77359401'", false], ['0xfffffffff', false], ['0x000', false], ["X''", false], ["X'1'", false], ['0X1', false], ['1e0', false], ['-1', false], ['', false]];
70 }
71
72 public function testGenerateKeepsTheExplicitHexSpellingAndOrigin(): void
73 {
74 $sequence = TerminalSequence::fromNames(['BINLOG_RESET_INDEX']);
75 $generator = new BoundedIntegerLexemeGenerator('BINLOG_RESET_INDEX', 1, 2000000000, ['1', '2000000000', '0x1'], 'sql/sql_yacc.yy:source_reset_options');
76 $result = $generator->generate(new LexemeInput($sequence, 0, new ResolvedOutput(), "X'0001'"));
77 self::assertNotNull($result);
78 self::assertSame("X'0001'", [...$result->sequences()][0]->lexemes[0]->text);
79 self::assertSame($sequence->terminals[0], [...$result->sequences()][0]->lexemes[0]->origin);
80 $invalid = $generator->generate(new LexemeInput($sequence, 0, new ResolvedOutput(), '0'));
81 self::assertNotNull($invalid);
82 self::assertSame([], [...$invalid->sequences()]);
83 }
84
85 public function testGenerateProvidesBoundaryRepresentativesWithoutClaimingOtherNumbers(): void
86 {
87 $generator = new BoundedIntegerLexemeGenerator('BINLOG_RESET_INDEX', 1, 2000000000, ['1', '2000000000', '0x1'], 'sql/sql_yacc.yy:source_reset_options');
88 $result = $generator->generate(new LexemeInput(TerminalSequence::fromNames(['BINLOG_RESET_INDEX']), 0, new ResolvedOutput()));
89 self::assertNotNull($result);
90 self::assertSame(['1', '2000000000', '0x1'], array_map(static fn ($candidate): string => $candidate->lexemes[0]->text, [...$result->sequences()]));
91 self::assertNull($generator->generate(new LexemeInput(TerminalSequence::fromNames(['NUM']), 0, new ResolvedOutput())));
92 }
93}
94