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