packages/requirements/tests/Unit/Test/RunnerConfigTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Unit\Test;
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\Input\Fields;
13use Requirements\Input\InvalidInputException;
14use Requirements\Test\RunnerConfig;
15
16#[CoversClass(RunnerConfig::class)]
17#[UsesClass(Fields::class)]
18#[Small]
19final class RunnerConfigTest extends TestCase
20{
21 public function testFromReadsEveryField(): void
22 {
23 $config = RunnerConfig::from(['extension' => 'behat', 'command' => ['php', 'vendor/bin/behat'], 'cwd' => 'tests', 'timeout' => 5], '/project');
24 self::assertSame('behat', $config->extension);
25 self::assertSame(['php', 'vendor/bin/behat'], $config->command);
26 self::assertSame('/project/tests', $config->directory);
27 self::assertSame(5.0, $config->timeout);
28 }
29
30 public function testFromDefaultsToTheConfigurationDirectoryAndSixtySeconds(): void
31 {
32 $config = RunnerConfig::from(['extension' => 'phpunit', 'command' => ['php', 'vendor/bin/phpunit']], '/project');
33 self::assertSame('/project/.', $config->directory);
34 self::assertSame(60.0, $config->timeout);
35 }
36
37 public function testFromTreatsANullTimeoutAsAbsent(): void
38 {
39 self::assertSame(60.0, RunnerConfig::from(['extension' => 'phpunit', 'command' => ['phpunit'], 'timeout' => null], '/project')->timeout);
40 }
41
42 public function testFromKeepsAnAbsoluteWorkingDirectory(): void
43 {
44 self::assertSame('/srv/app', RunnerConfig::from(['extension' => 'phpunit', 'command' => ['phpunit'], 'cwd' => '/srv/app'], '/project')->directory);
45 }
46
47 public function testFromAcceptsAFractionalTimeout(): void
48 {
49 self::assertSame(0.5, RunnerConfig::from(['extension' => 'phpunit', 'command' => ['phpunit'], 'timeout' => 0.5], '/project')->timeout);
50 }
51
52 public function testFromKeepsRepeatedCommandArguments(): void
53 {
54 self::assertSame(['php', '-d', 'a=1', '-d', 'b=1'], RunnerConfig::from(['extension' => 'phpunit', 'command' => ['php', '-d', 'a=1', '-d', 'b=1']], '/project')->command);
55 }
56
57 public function testTimeoutDefaultsToSixtySecondsWhenConstructed(): void
58 {
59 $config = new RunnerConfig('phpunit', ['php', 'vendor/bin/phpunit'], '/project');
60 self::assertSame(60.0, $config->timeout);
61 }
62
63 #[DataProvider('providerFromRejectsInvalidEntries')]
64 public function testFromRejectsInvalidEntries(mixed $value, string $message): void
65 {
66 $this->expectException(InvalidInputException::class);
67 $this->expectExceptionMessage($message);
68 RunnerConfig::from($value, '/project');
69 }
70
71 /**
72 * @return array<string, array{mixed, string}>
73 */
74 public static function providerFromRejectsInvalidEntries(): array
75 {
76 $runner = ['extension' => 'phpunit', 'command' => ['phpunit']];
77 $command = 'Runners require a nonempty command and positive timeout.';
78 return [
79 'not a mapping' => ['phpunit', 'runner must be a mapping.'],
80 'unknown field' => [[...$runner, 'env' => []], "runner: unknown field 'env'."],
81 'missing command' => [['extension' => 'phpunit'], $command],
82 'empty command' => [[...$runner, 'command' => []], $command],
83 'command string' => [[...$runner, 'command' => 'phpunit'], 'runner.command must be a list.'],
84 'blank command entry' => [[...$runner, 'command' => ['php', ' ']], 'runner.command must contain nonempty strings.'],
85 'zero timeout' => [[...$runner, 'timeout' => 0], $command],
86 'negative timeout' => [[...$runner, 'timeout' => -1.5], $command],
87 'string timeout' => [[...$runner, 'timeout' => '5'], $command],
88 'infinite timeout' => [[...$runner, 'timeout' => INF], $command],
89 'missing extension' => [['command' => ['phpunit']], 'extension must be a nonempty string.'],
90 'blank cwd' => [[...$runner, 'cwd' => ''], 'cwd must be a nonempty string.'],
91 ];
92 }
93}
94