Passed
Push — master ( 92d243...5a69a1 )
by Eric
12:07
created

testShowFilesTableOutputAboveThreshold()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 27
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 20
nc 1
nop 0
dl 0
loc 27
rs 9.6
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of PHPUnit Coverage Check.
7
 *
8
 * (c) Eric Sizemore <[email protected]>
9
 * (c) Richard Regeer <[email protected]>
10
 *
11
 * This source file is subject to the MIT license. For the full copyright,
12
 * license information, and credits/acknowledgements, please view the LICENSE
13
 * and README files that were distributed with this source code.
14
 */
15
16
namespace Esi\CoverageCheck\Tests\Command;
17
18
use Esi\CoverageCheck\Application;
19
use Esi\CoverageCheck\Command\CoverageCheckCommand;
20
use Esi\CoverageCheck\CoverageCheck;
21
use Esi\CoverageCheck\Style\CoverageCheckStyle;
22
use Esi\CoverageCheck\Utils;
23
use InvalidArgumentException;
24
use PHPUnit\Framework\Attributes\CoversClass;
1 ignored issue
show
Bug introduced by
The type PHPUnit\Framework\Attributes\CoversClass was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use PHPUnit\Framework\TestCase;
26
use Symfony\Component\Console\Command\Command;
27
use Symfony\Component\Console\Tester\ApplicationTester;
28
use Symfony\Component\Console\Tester\CommandTester;
29
30
use function preg_replace;
31
use function str_replace;
32
use function trim;
33
34
use const PHP_EOL;
35
36
/**
37
 * @internal
38
 */
39
#[CoversClass(CoverageCheckCommand::class)]
40
#[CoversClass(Application::class)]
41
#[CoversClass(CoverageCheckStyle::class)]
42
#[CoversClass(CoverageCheck::class)]
43
#[CoversClass(Utils::class)]
44
class CoverageCheckCommandTest extends TestCase
45
{
46
    protected Application $application;
47
48
    protected ApplicationTester $tester;
49
50
    /**
51
     * @var string[]
52
     */
53
    private static array $fixtures;
54
55
    #[\Override]
56
    protected function setUp(): void
57
    {
58
        self::$fixtures = [
59
            'valid'        => \dirname(__FILE__, 3) . '/fixtures/clover.xml',
60
            'notexist'     => \dirname(__FILE__, 3) . '/fixtures/clovr.xml',
61
            'empty'        => \dirname(__FILE__, 3) . '/fixtures/empty.xml',
62
            'invalid_root' => \dirname(__FILE__, 3) . '/fixtures/invalid_root_element.xml',
63
            'no_children'  => \dirname(__FILE__, 3) . '/fixtures/no_children.xml',
64
            'no_metrics'   => \dirname(__FILE__, 3) . '/fixtures/no_project_metrics.xml',
65
            'thisLibrary'  => \dirname(__FILE__, 3) . '/fixtures/self_clover.xml',
66
        ];
67
68
        $coverageCheckCommand = new CoverageCheckCommand(new CoverageCheck());
69
        $commandName          = $coverageCheckCommand->getName() ?? 'coverage:check';
70
71
        $this->application = new Application();
72
        $this->application->setAutoExit(false);
73
        $this->application->add($coverageCheckCommand);
74
        $this->application->setDefaultCommand($commandName, true);
75
76
        $this->tester = new ApplicationTester($this->application);
77
    }
78
79
    public function testCloverFileInvalidRootElement(): void
80
    {
81
        $this->tester->run([
82
            'cloverfile' => self::$fixtures['invalid_root'],
83
            'threshold'  => 90,
84
        ]);
85
86
        self::assertSame(self::$fixtures['invalid_root'], $this->tester->getInput()->getArgument('cloverfile'));
87
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
88
89
        self::assertSame(
90
            '[ERROR] Clover file appears to be invalid. Are you sure this is a PHPUnit generated clover report?',
91
            self::stripWhitespace($this->tester->getDisplay())
92
        );
93
        self::assertSame(Command::INVALID, $this->tester->getStatusCode());
94
    }
95
96
    public function testCloverFileNoChildren(): void
97
    {
98
        $this->tester->run([
99
            'cloverfile' => self::$fixtures['no_children'],
100
            'threshold'  => 90,
101
        ]);
102
103
        self::assertSame(self::$fixtures['no_children'], $this->tester->getInput()->getArgument('cloverfile'));
104
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
105
106
        self::assertSame(
107
            '[ERROR] Clover file appears to be invalid. Are you sure this is a PHPUnit generated clover report?',
108
            self::stripWhitespace($this->tester->getDisplay())
109
        );
110
        self::assertSame(Command::INVALID, $this->tester->getStatusCode());
111
    }
112
113
    public function testCloverFileNoProjectMetrics(): void
114
    {
115
        $this->tester->run([
116
            'cloverfile' => self::$fixtures['no_metrics'],
117
            'threshold'  => 90,
118
        ]);
119
120
        self::assertSame(self::$fixtures['no_metrics'], $this->tester->getInput()->getArgument('cloverfile'));
121
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
122
123
        self::assertSame(
124
            '[ERROR] Clover file appears to be invalid. Are you sure this is a PHPUnit generated clover report?',
125
            self::stripWhitespace($this->tester->getDisplay())
126
        );
127
        self::assertSame(Command::INVALID, $this->tester->getStatusCode());
128
    }
129
130
    public function testRunInvalidCloverFile(): void
131
    {
132
        $this->expectException(InvalidArgumentException::class);
133
        $this->expectExceptionMessageMatches('/Invalid input file provided. Was given: (.*?)clovr.xml/');
134
        $commandTester = new CommandTester($this->application->find('coverage:check'));
135
        $commandTester->execute([
136
            'cloverfile' => self::$fixtures['notexist'],
137
            'threshold'  => 90,
138
        ]);
139
    }
140
141
    public function testRunInvalidThresholdHigh(): void
142
    {
143
        $this->expectException(InvalidArgumentException::class);
144
        $this->expectExceptionMessage('The threshold must be a minimum of 1 and a maximum of 100, 101 given');
145
        $commandTester = new CommandTester($this->application->find('coverage:check'));
146
        $commandTester->execute([
147
            'cloverfile' => self::$fixtures['valid'],
148
            'threshold'  => 101,
149
        ]);
150
    }
151
152
    public function testRunInvalidThresholdLow(): void
153
    {
154
        $this->expectException(InvalidArgumentException::class);
155
        $this->expectExceptionMessage('The threshold must be a minimum of 1 and a maximum of 100, 0 given');
156
        $commandTester = new CommandTester($this->application->find('coverage:check'));
157
        $commandTester->execute([
158
            'cloverfile' => self::$fixtures['valid'],
159
            'threshold'  => 0,
160
        ]);
161
    }
162
163
    public function testRunNotEnoughCode(): void
164
    {
165
        $this->tester->run([
166
            'cloverfile' => self::$fixtures['empty'],
167
            'threshold'  => 90,
168
        ]);
169
170
        self::assertSame(self::$fixtures['empty'], $this->tester->getInput()->getArgument('cloverfile'));
171
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
172
173
        self::assertSame(
174
            '[ERROR] Insufficient data for calculation. Please add more code.',
175
            trim($this->tester->getDisplay())
176
        );
177
        self::assertSame(Command::FAILURE, $this->tester->getStatusCode());
178
    }
179
180
    public function testRunNotEnoughCodePercentageOnly(): void
181
    {
182
        $this->tester->run([
183
            'cloverfile'        => self::$fixtures['empty'],
184
            'threshold'         => 90,
185
            '--only-percentage' => true,
186
        ]);
187
188
        self::assertSame(self::$fixtures['empty'], $this->tester->getInput()->getArgument('cloverfile'));
189
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
190
        self::assertTrue($this->tester->getInput()->getOption('only-percentage'));
191
192
        self::assertSame(
193
            '[ERROR] Insufficient data for calculation. Please add more code.',
194
            trim($this->tester->getDisplay())
195
        );
196
        self::assertSame(Command::FAILURE, $this->tester->getStatusCode());
197
    }
198
199
    public function testRunValidNonPassingOptions(): void
200
    {
201
        $this->tester->run([
202
            'cloverfile' => self::$fixtures['valid'],
203
            'threshold'  => 100,
204
        ]);
205
206
        self::assertSame(self::$fixtures['valid'], $this->tester->getInput()->getArgument('cloverfile'));
207
        self::assertSame(100, $this->tester->getInput()->getArgument('threshold'));
208
209
        self::assertSame(
210
            '[ERROR] Total code coverage is 90.32% which is below the accepted 100%',
211
            trim($this->tester->getDisplay())
212
        );
213
        self::assertSame(Command::FAILURE, $this->tester->getStatusCode());
214
    }
215
216
    public function testRunValidOptionsNonPassingPercentageOnly(): void
217
    {
218
        $this->tester->run([
219
            'cloverfile'        => self::$fixtures['valid'],
220
            'threshold'         => 100,
221
            '--only-percentage' => true,
222
        ]);
223
224
        self::assertSame(self::$fixtures['valid'], $this->tester->getInput()->getArgument('cloverfile'));
225
        self::assertSame(100, $this->tester->getInput()->getArgument('threshold'));
226
        self::assertTrue($this->tester->getInput()->getOption('only-percentage'));
227
228
        self::assertSame(
229
            '90.32%',
230
            trim($this->tester->getDisplay())
231
        );
232
        self::assertSame(Command::FAILURE, $this->tester->getStatusCode());
233
    }
234
235
    public function testRunValidOptionsPassing(): void
236
    {
237
        $this->tester->run([
238
            'cloverfile' => self::$fixtures['valid'],
239
            'threshold'  => 90,
240
        ]);
241
242
        self::assertSame(self::$fixtures['valid'], $this->tester->getInput()->getArgument('cloverfile'));
243
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
244
245
        self::assertSame(
246
            '[OK] Total code coverage is 90.32%',
247
            trim($this->tester->getDisplay())
248
        );
249
        self::assertSame(Command::SUCCESS, $this->tester->getStatusCode());
250
    }
251
252
    public function testRunValidOptionsPassingPercentageOnly(): void
253
    {
254
        $this->tester->run([
255
            'cloverfile'        => self::$fixtures['valid'],
256
            'threshold'         => 90,
257
            '--only-percentage' => true,
258
        ]);
259
260
        self::assertSame(self::$fixtures['valid'], $this->tester->getInput()->getArgument('cloverfile'));
261
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
262
        self::assertTrue($this->tester->getInput()->getOption('only-percentage'));
263
264
        self::assertSame(
265
            '90.32%',
266
            trim($this->tester->getDisplay())
267
        );
268
        self::assertSame(Command::SUCCESS, $this->tester->getStatusCode());
269
    }
270
271
    public function testShowFilesTableOutputAboveThreshold(): void
272
    {
273
        $this->tester->run([
274
            'cloverfile'   => self::$fixtures['thisLibrary'],
275
            'threshold'    => 90,
276
            '--show-files' => true,
277
        ]);
278
279
        self::assertSame(self::$fixtures['thisLibrary'], $this->tester->getInput()->getArgument('cloverfile'));
280
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
281
282
        $eol = PHP_EOL;
283
284
        $expected = '------------------------------------------------------------------- -------------------------- ---------- ' . $eol;
285
        $expected .= '  File                                                                Elements (Covered/Total)   Coverage  ' . $eol;
286
        $expected .= ' ------------------------------------------------------------------- -------------------------- ---------- ' . $eol;
287
        $expected .= '  [...]\phpunit-coverage-check\src\Application.php                    10/10                      100.00%   ' . $eol;
288
        $expected .= '  [...]\phpunit-coverage-check\src\Command\CoverageCheckCommand.php   77/77                      100.00%   ' . $eol;
289
        $expected .= '  [...]\phpunit-coverage-check\src\CoverageCheck.php                  63/63                      100.00%   ' . $eol;
290
        $expected .= '  [...]\phpunit-coverage-check\src\Style\CoverageCheckStyle.php       4/4                        100.00%   ' . $eol;
291
        $expected .= '  [...]\phpunit-coverage-check\src\Utils.php                          16/16                      100.00%   ' . $eol;
292
        $expected .= ' ------------------------------------------------------------------- -------------------------- ---------- ' . $eol;
293
        $expected .= '  Overall Totals                                                      170/170                    100.00%   ' . $eol;
294
        $expected .= ' ------------------------------------------------------------------- -------------------------- ----------';
295
296
        self::assertEquals($expected, trim($this->tester->getDisplay()));
297
        self::assertSame(Command::SUCCESS, $this->tester->getStatusCode());
298
    }
299
300
    public function testShowFilesTableOutputBelowThreshold(): void
301
    {
302
        $this->tester->run([
303
            'cloverfile'   => self::$fixtures['valid'],
304
            'threshold'    => 90,
305
            '--show-files' => true,
306
        ]);
307
308
        self::assertSame(self::$fixtures['valid'], $this->tester->getInput()->getArgument('cloverfile'));
309
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
310
311
        $eol = PHP_EOL;
312
313
        $expected = '----------------------------- -------------------------- ---------- ' . $eol;
314
        $expected .= '  File                          Elements (Covered/Total)   Coverage  ' . $eol;
315
        $expected .= ' ----------------------------- -------------------------- ---------- ' . $eol;
316
        $expected .= '  /tmp/Example/String.php       36/38                      94.74%    ' . $eol;
317
        $expected .= '  /tmp/Example/StringList.php   20/24                      83.33%    ' . $eol;
318
        $expected .= ' ----------------------------- -------------------------- ---------- ' . $eol;
319
        $expected .= '  Overall Totals                56/62                      89.04%    ' . $eol;
320
        $expected .= ' ----------------------------- -------------------------- ----------';
321
322
        self::assertEquals($expected, trim($this->tester->getDisplay()));
323
        self::assertSame(Command::FAILURE, $this->tester->getStatusCode());
324
    }
325
326
    public function testShowFilesTableOutputEmpty(): void
327
    {
328
        $this->tester->run([
329
            'cloverfile'   => self::$fixtures['empty'],
330
            'threshold'    => 90,
331
            '--show-files' => true,
332
        ]);
333
334
        self::assertSame(self::$fixtures['empty'], $this->tester->getInput()->getArgument('cloverfile'));
335
        self::assertSame(90, $this->tester->getInput()->getArgument('threshold'));
336
337
        self::assertSame('[ERROR] Insufficient data for calculation. Please add more code.', trim($this->tester->getDisplay()));
338
        self::assertSame(Command::FAILURE, $this->tester->getStatusCode());
339
    }
340
341
    /**
342
     * Could probably be done better, but it works.
343
     */
344
    protected static function stripWhitespace(string $output): string
345
    {
346
        $output = (string) preg_replace('#\h{2,}#', '', $output);
347
        $output = (string) preg_replace('#\\n#', ' ', $output);
348
        $output = str_replace('  ', ' ', $output);
349
350
        return trim($output);
351
    }
352
}
353