packages/requirements/tests/Unit/Ears/LiteralMaskTest.php

1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Ears;
6
7use PHPUnit\Framework\Attributes\CoversClass;
8use PHPUnit\Framework\Attributes\DataProvider;
9use PHPUnit\Framework\Attributes\Small;
10use PHPUnit\Framework\TestCase;
11use Requirements\Ears\LiteralMask;
12use Requirements\Input\InvalidInputException;
13
14#[CoversClass(LiteralMask::class)]
15#[Small]
16final class LiteralMaskTest extends TestCase
17{
18    #[DataProvider('providerApplyMasksLiterals')]
19    public function testApplyMasksLiterals(string $text, string $expected): void
20    {
21        self::assertSame($expected, (new LiteralMask())->apply($text));
22    }
23
24    /**
25     * @return array<string, array{string, string}>
26     */
27    public static function providerApplyMasksLiterals(): array
28    {
29        return [
30            'no literal' => ['The reader shall read.', 'The reader shall read.'],
31            'empty' => ['', ''],
32            'double quotes' => ['emit "shall, then" now', 'emit xxxxxxxxxxxxx now'],
33            'single quotes' => ["emit 'a, b' now", 'emit xxxxxx now'],
34            'backticks' => ['emit `shall` now', 'emit xxxxxxx now'],
35            'two literals' => ['"a" and "b"', 'xxx and xxx'],
36            'escaped quote' => ['"a\"b" c', 'xxxxxx c'],
37            'escaped backslash' => ['"a\\\\" c', 'xxxxx c'],
38            'other quote inside' => ['"it\'s" c', 'xxxxxx c'],
39            'apostrophe after letter' => ["the user's input", "the user's input"],
40            'apostrophe after digit' => ["the 90's", "the 90's"],
41            'quote at start' => ["'abc' d", 'xxxxx d'],
42            'quote after space' => ["a 'b' c", 'a xxx c'],
43            'quote after punctuation' => ["(='b')", '(=xxx)'],
44            'multibyte literal' => ['"é" x', 'xxxx x'],
45        ];
46    }
47
48    #[DataProvider('providerApplyRejectsUnclosedLiterals')]
49    public function testApplyRejectsUnclosedLiterals(string $text): void
50    {
51        $this->expectException(InvalidInputException::class);
52        $this->expectExceptionMessage('EARS: close quoted or code literals.');
53        (new LiteralMask())->apply($text);
54    }
55
56    /**
57     * @return array<string, array{string}>
58     */
59    public static function providerApplyRejectsUnclosedLiterals(): array
60    {
61        return [
62            'double quote' => ['The reader shall emit a "token.'],
63            'backtick' => ['The reader shall emit `code.'],
64            'single quote' => ["The reader shall emit 'token."],
65            'escaped closing quote' => ['emit "a\"'],
66            'trailing backslash' => ['emit "a\\'],
67            'mismatched quotes' => ['emit "a\' now'],
68        ];
69    }
70}
71