packages/requirements/tests/Unit/Ears/SystemResponseTest.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\Attributes\UsesClass;
11use PHPUnit\Framework\TestCase;
12use Requirements\Ears\SystemResponse;
13use Requirements\Ears\Wording;
14use Requirements\Input\InvalidInputException;
15
16#[CoversClass(SystemResponse::class)]
17#[UsesClass(Wording::class)]
18#[Small]
19final class SystemResponseTest extends TestCase
20{
21    #[DataProvider('providerValidateAcceptsSystemClauses')]
22    public function testValidateAcceptsSystemClauses(string $clause, ?string $trigger): void
23    {
24        (new SystemResponse())->validate($clause, $trigger);
25        $this->addToAssertionCount(1);
26    }
27
28    /**
29     * @return array<string, array{string, ?string}>
30     */
31    public static function providerValidateAcceptsSystemClauses(): array
32    {
33        return [
34            'ubiquitous' => ['The reader shall preserve positions.', null],
35            'after when' => ['the reader shall preserve positions.', 'when'],
36            'after if' => ['then the reader shall report an error.', 'if'],
37            'uppercase' => ['THEN THE READER SHALL REPORT.', 'if'],
38            'surrounding space' => ["  The reader shall read \n", null],
39            'multiword system' => ['The token reader of the parser shall read.', null],
40            'multiline response' => ["The reader shall read\nand write.", null],
41            'word containing shall' => ['The reader shall read shallow files.', null],
42        ];
43    }
44
45    #[DataProvider('providerValidateRejectsClausesWithoutThe')]
46    public function testValidateRejectsClausesWithoutThe(string $clause, ?string $trigger): void
47    {
48        $this->expectException(InvalidInputException::class);
49        $this->expectExceptionMessage('EARS: expected The <system name> shall <system response>.');
50        (new SystemResponse())->validate($clause, $trigger);
51    }
52
53    /**
54     * @return array<string, array{string, ?string}>
55     */
56    public static function providerValidateRejectsClausesWithoutThe(): array
57    {
58        return [
59            'no article' => ['reader shall return the tree.', 'when'],
60            'then without if' => ['then the reader shall return the tree.', 'when'],
61            'then without trigger' => ['Then the reader shall return the tree.', null],
62            'no system' => ['The shall record positions.', null],
63            'punctuation system' => ['The ... shall record positions.', null],
64            'no response' => ['The reader shall .', null],
65            'empty response' => ['The reader shall', null],
66            'missing shall' => ['The reader will return the tree.', null],
67            'article joined to word' => ['Thereader shall read.', null],
68        ];
69    }
70
71    #[DataProvider('providerValidateRejectsClausesWithoutThenAfterIf')]
72    public function testValidateRejectsClausesWithoutThenAfterIf(string $clause): void
73    {
74        $this->expectException(InvalidInputException::class);
75        $this->expectExceptionMessage('EARS: expected Then the <system name> shall <system response>.');
76        (new SystemResponse())->validate($clause, 'if');
77    }
78
79    /**
80     * @return array<string, array{string}>
81     */
82    public static function providerValidateRejectsClausesWithoutThenAfterIf(): array
83    {
84        return [
85            'missing then' => ['the reader shall return the tree.'],
86            'then without article' => ['then reader shall return the tree.'],
87            'no system' => ['then the shall report.'],
88            'no response' => ['then the reader shall !'],
89        ];
90    }
91
92    #[DataProvider('providerValidateRejectsASecondShall')]
93    public function testValidateRejectsASecondShall(string $clause): void
94    {
95        $this->expectException(InvalidInputException::class);
96        $this->expectExceptionMessage('EARS: use one system clause; combine responses after its shall.');
97        (new SystemResponse())->validate($clause, null);
98    }
99
100    /**
101     * @return array<string, array{string}>
102     */
103    public static function providerValidateRejectsASecondShall(): array
104    {
105        return [
106            'second shall' => ['The reader shall return the tree and shall print it.'],
107            'two systems' => ['The reader shall return the tree, the printer shall print it.'],
108            'uppercase second shall' => ['The reader shall read and SHALL print.'],
109        ];
110    }
111}
112