packages/sql-faker/tests/Unit/Generation/Candidate/SequenceLexemeGeneratorTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\SqlFaker\Generation\Candidate;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\UsesClass;
9use PHPUnit\Framework\TestCase;
10use SqlFaker\Generation\Candidate\ChoiceLexemeGenerator;
11use SqlFaker\Generation\Candidate\FixedLexemeGenerator;
12use SqlFaker\Generation\Candidate\MatchingLexemeGenerator;
13use SqlFaker\Generation\Candidate\SequenceLexemeGenerator;
14use SqlFaker\Generation\Lexeme\LexemeCandidates;
15use SqlFaker\Generation\Lexeme\LexemeInput;
16use SqlFaker\Generation\Lexeme\LexemeSequence;
17use SqlFaker\Generation\Lexeme\ResolvedOutput;
18use SqlFaker\Generation\Token\TerminalSequence;
19
20#[CoversClass(SequenceLexemeGenerator::class)]
21#[UsesClass(FixedLexemeGenerator::class)]
22#[UsesClass(\SqlFaker\Generation\Lexeme\Lexeme::class)]
23#[UsesClass(LexemeCandidates::class)]
24#[UsesClass(LexemeInput::class)]
25#[UsesClass(LexemeSequence::class)]
26#[UsesClass(MatchingLexemeGenerator::class)]
27#[UsesClass(ResolvedOutput::class)]
28#[UsesClass(\SqlFaker\Generation\Token\TerminalOccurrence::class)]
29#[UsesClass(TerminalSequence::class)]
30#[UsesClass(ChoiceLexemeGenerator::class)]
31#[UsesClass(\SqlFaker\Generation\Lexeme\SpacingConstraint::class)]
32#[UsesClass(\SqlFaker\Generation\Token\ProductionOccurrence::class)]
33final class SequenceLexemeGeneratorTest extends TestCase
34{
35    public function testGenerateACompoundCandidateKeepsOutputOrderAndConsumesOneOccurrence(): void
36    {
37        $generator = new SequenceLexemeGenerator(
38            new FixedLexemeGenerator('WITH', 'keyword', 'with'),
39            new ChoiceLexemeGenerator(
40                new FixedLexemeGenerator('ROLLUP', 'keyword', 'rollup'),
41                new FixedLexemeGenerator('CUBE', 'keyword', 'cube'),
42            ),
43        );
44        $input = new LexemeInput(TerminalSequence::fromNames(['WITH_ROLLUP_SYM']), 0, new ResolvedOutput());
45        $result = $generator->generate($input);
46        self::assertNotNull($result);
47        $candidates = [...$result->sequences()];
48        self::assertCount(2, $candidates);
49        self::assertSame(['WITH', 'ROLLUP'], array_column($candidates[0]->lexemes, 'text'));
50        self::assertSame(['WITH', 'CUBE'], array_column($candidates[1]->lexemes, 'text'));
51        self::assertSame([[0, 0], [0, 0]], array_map(
52            static fn ($candidate): array => array_map(static fn ($lexeme): int => $lexeme->origin->id, $candidate->lexemes),
53            $candidates,
54        ));
55    }
56
57    public function testARequiredNonApplicableChildCannotBeOmitted(): void
58    {
59        $generator = new SequenceLexemeGenerator(
60            new FixedLexemeGenerator('WITH', 'keyword', 'with'),
61            new MatchingLexemeGenerator('OTHER', new FixedLexemeGenerator('ROLLUP', 'keyword', 'rollup')),
62        );
63        self::assertNull($generator->generate(new LexemeInput(TerminalSequence::fromNames(['WITH_ROLLUP_SYM']), 0, new ResolvedOutput())));
64    }
65
66    public function testProductRetainsTheWholePrefixAndRejectsAnEmptyRequiredFactor(): void
67    {
68        $prefix = new LexemeSequence([], 'prefix');
69        $generator = new SequenceLexemeGenerator();
70        self::assertSame([$prefix], [...$generator->product([], 0, $prefix)]);
71        self::assertSame([], [...$generator->product([LexemeCandidates::of()], 0, $prefix)]);
72    }
73
74}
75