1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types = 1); |
4
|
|
|
|
5
|
|
|
namespace PHPChunkit\Command; |
6
|
|
|
|
7
|
|
|
use Closure; |
8
|
|
|
use PHPChunkit\ChunkedTests; |
9
|
|
|
use PHPChunkit\TestChunker; |
10
|
|
|
use PHPChunkit\TestFinder; |
11
|
|
|
use PHPChunkit\TestRunner; |
12
|
|
|
use PHPChunkit\Configuration; |
13
|
|
|
use Symfony\Component\Console\Command\Command; |
14
|
|
|
use Symfony\Component\Console\Helper\ProgressBar; |
15
|
|
|
use Symfony\Component\Console\Input\InputInterface; |
16
|
|
|
use Symfony\Component\Console\Input\InputOption; |
17
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
18
|
|
|
use Symfony\Component\Process\Process; |
19
|
|
|
use Symfony\Component\Stopwatch\Stopwatch; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @testClass PHPChunkit\Test\Command\RunTest |
23
|
|
|
*/ |
24
|
|
|
class Run implements CommandInterface |
25
|
|
|
{ |
26
|
|
|
const NAME = 'run'; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var TestRunner |
30
|
|
|
*/ |
31
|
|
|
private $testRunner; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var Configuration |
35
|
|
|
*/ |
36
|
|
|
private $configuration; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var TestChunker |
40
|
|
|
*/ |
41
|
|
|
private $testChunker; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var TestFinder |
45
|
|
|
*/ |
46
|
|
|
private $testFinder; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var InputInterface |
50
|
|
|
*/ |
51
|
|
|
private $input; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var OutputInterface |
55
|
|
|
*/ |
56
|
|
|
private $output; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var Stopwatch |
60
|
|
|
*/ |
61
|
|
|
private $stopwatch; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var bool |
65
|
|
|
*/ |
66
|
|
|
private $verbose = false; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var bool |
70
|
|
|
*/ |
71
|
|
|
private $parallel = false; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var bool |
75
|
|
|
*/ |
76
|
|
|
private $showProgressBar = true; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var integer |
80
|
|
|
*/ |
81
|
|
|
private $numChunks = 1; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var null|integer |
85
|
|
|
*/ |
86
|
|
|
private $chunk = null; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @var ChunkedTests |
90
|
|
|
*/ |
91
|
|
|
private $chunkedTests; |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @var integer |
95
|
|
|
*/ |
96
|
|
|
private $numParallelProcesses = 1; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @var integer |
100
|
|
|
*/ |
101
|
|
|
private $numAssertions = 0; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @var integer |
105
|
|
|
*/ |
106
|
|
|
private $numFailures = 0; |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @var integer |
110
|
|
|
*/ |
111
|
|
|
private $numChunkFailures = 0; |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* @var integer |
115
|
|
|
*/ |
116
|
|
|
private $totalTestsRan = 0; |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @var array |
120
|
|
|
*/ |
121
|
|
|
private $codes = []; |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* @var array |
125
|
|
|
*/ |
126
|
|
|
private $processes = []; |
127
|
|
|
|
128
|
1 |
|
public function __construct( |
129
|
|
|
TestRunner $testRunner, |
130
|
|
|
Configuration $configuration, |
131
|
|
|
TestChunker $testChunker, |
132
|
|
|
TestFinder $testFinder) |
133
|
|
|
{ |
134
|
1 |
|
$this->testRunner = $testRunner; |
135
|
1 |
|
$this->configuration = $configuration; |
136
|
1 |
|
$this->testChunker = $testChunker; |
137
|
1 |
|
$this->testFinder = $testFinder; |
138
|
1 |
|
} |
139
|
|
|
|
140
|
|
|
public function getName() : string |
141
|
|
|
{ |
142
|
|
|
return self::NAME; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function configure(Command $command) |
146
|
|
|
{ |
147
|
|
|
$command |
148
|
|
|
->setDescription('Run tests.') |
149
|
|
|
->addOption('debug', null, InputOption::VALUE_NONE, 'Run tests in debug mode.') |
150
|
|
|
->addOption('memory-limit', null, InputOption::VALUE_REQUIRED, 'Memory limit for PHP.') |
151
|
|
|
->addOption('stop', null, InputOption::VALUE_NONE, 'Stop on failure or error.') |
152
|
|
|
->addOption('failed', null, InputOption::VALUE_NONE, 'Track tests that have failed.') |
153
|
|
|
->addOption('create-dbs', null, InputOption::VALUE_NONE, 'Create the test databases before running tests.') |
154
|
|
|
->addOption('sandbox', null, InputOption::VALUE_NONE, 'Configure unique names.') |
155
|
|
|
->addOption('chunk', null, InputOption::VALUE_REQUIRED, 'Run a specific chunk of tests.') |
156
|
|
|
->addOption('num-chunks', null, InputOption::VALUE_REQUIRED, 'The number of chunks to run tests in.') |
157
|
|
|
->addOption('group', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run all tests in these groups.') |
158
|
|
|
->addOption('exclude-group', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run all tests excluding these groups.') |
159
|
|
|
->addOption('changed', null, InputOption::VALUE_NONE, 'Run changed tests.') |
160
|
|
|
->addOption('parallel', null, InputOption::VALUE_REQUIRED, 'Run test chunks in parallel.') |
161
|
|
|
->addOption('filter', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Filter tests by path/file name and run them.') |
162
|
|
|
->addOption('contains', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run tests that match the given content.') |
163
|
|
|
->addOption('not-contains', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run tests that do not match the given content.') |
164
|
|
|
->addOption('file', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Run test file.') |
165
|
|
|
->addOption('phpunit-opt', null, InputOption::VALUE_REQUIRED, 'Pass through phpunit options.') |
166
|
|
|
; |
167
|
|
|
} |
168
|
|
|
|
169
|
1 |
|
public function execute(InputInterface $input, OutputInterface $output) |
170
|
|
|
{ |
171
|
1 |
|
$this->initialize($input, $output); |
172
|
|
|
|
173
|
1 |
|
$this->chunkedTests = $this->chunkTestFiles( |
174
|
1 |
|
$this->findTestFiles() |
175
|
|
|
); |
176
|
|
|
|
177
|
1 |
|
if (!$this->chunkedTests->hasTests()) { |
178
|
|
|
$this->output->writeln('<error>No tests found to run.</error>'); |
179
|
|
|
|
180
|
|
|
return; |
181
|
|
|
} |
182
|
|
|
|
183
|
1 |
|
$this->outputHeader(); |
184
|
|
|
|
185
|
1 |
|
$this->setupSandbox(); |
186
|
|
|
|
187
|
1 |
|
$this->runChunks(); |
188
|
|
|
|
189
|
1 |
|
$this->outputFooter(); |
190
|
|
|
|
191
|
1 |
|
return $this->hasFailed() ? 1 : 0; |
192
|
|
|
} |
193
|
|
|
|
194
|
1 |
|
private function initialize(InputInterface $input, OutputInterface $output) |
195
|
|
|
{ |
196
|
1 |
|
$this->stopwatch = new Stopwatch(); |
197
|
1 |
|
$this->stopwatch->start('Tests'); |
198
|
|
|
|
199
|
1 |
|
$this->input = $input; |
200
|
1 |
|
$this->output = $output; |
201
|
1 |
|
$this->verbose = $output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE; |
202
|
1 |
|
$this->parallel = (bool) $this->input->getOption('parallel'); |
203
|
1 |
|
$this->showProgressBar = !$this->verbose && !$this->parallel; |
204
|
1 |
|
$this->numChunks = $this->getNumChunks(); |
205
|
1 |
|
$this->chunk = (int) $this->input->getOption('chunk'); |
206
|
1 |
|
} |
207
|
|
|
|
208
|
1 |
|
private function getNumChunks() : int |
209
|
|
|
{ |
210
|
1 |
|
return (int) $this->input->getOption('num-chunks') |
211
|
1 |
|
?: $this->configuration->getNumChunks() ?: 1; |
212
|
|
|
} |
213
|
|
|
|
214
|
1 |
|
private function findTestFiles() |
215
|
|
|
{ |
216
|
1 |
|
$files = $this->input->getOption('file'); |
217
|
|
|
|
218
|
1 |
|
if (!empty($files)) { |
219
|
|
|
return $files; |
220
|
|
|
} |
221
|
|
|
|
222
|
1 |
|
$groups = $this->input->getOption('group'); |
223
|
1 |
|
$excludeGroups = $this->input->getOption('exclude-group'); |
224
|
1 |
|
$changed = $this->input->getOption('changed'); |
225
|
1 |
|
$filters = $this->input->getOption('filter'); |
226
|
1 |
|
$contains = $this->input->getOption('contains'); |
227
|
1 |
|
$notContains = $this->input->getOption('not-contains'); |
228
|
|
|
|
229
|
1 |
|
$this->testFinder |
230
|
1 |
|
->inGroups($groups) |
231
|
1 |
|
->notInGroups($excludeGroups) |
232
|
1 |
|
->changed($changed) |
233
|
|
|
; |
234
|
|
|
|
235
|
1 |
|
foreach ($filters as $filter) { |
236
|
|
|
$this->testFinder->filter($filter); |
237
|
|
|
} |
238
|
|
|
|
239
|
1 |
|
foreach ($contains as $contain) { |
240
|
|
|
$this->testFinder->contains($contain); |
241
|
|
|
} |
242
|
|
|
|
243
|
1 |
|
foreach ($notContains as $notContain) { |
244
|
|
|
$this->testFinder->notContains($notContain); |
245
|
|
|
} |
246
|
|
|
|
247
|
1 |
|
return $this->testFinder->getFiles(); |
248
|
|
|
} |
249
|
|
|
|
250
|
1 |
|
private function chunkTestFiles(array $testFiles) : ChunkedTests |
251
|
|
|
{ |
252
|
1 |
|
$chunkedTests = (new ChunkedTests()) |
253
|
1 |
|
->setNumChunks($this->numChunks) |
254
|
1 |
|
->setChunk($this->chunk) |
255
|
|
|
; |
256
|
|
|
|
257
|
1 |
|
if (!$testFiles) { |
|
|
|
|
258
|
|
|
return $chunkedTests; |
259
|
|
|
} |
260
|
|
|
|
261
|
1 |
|
$this->testChunker->chunkTestFiles($chunkedTests, $testFiles); |
262
|
|
|
|
263
|
1 |
|
return $chunkedTests; |
264
|
|
|
} |
265
|
|
|
|
266
|
1 |
|
private function outputHeader() |
267
|
|
|
{ |
268
|
1 |
|
$chunks = $this->chunkedTests->getChunks(); |
269
|
1 |
|
$testsPerChunk = $this->chunkedTests->getTestsPerChunk(); |
270
|
1 |
|
$totalTests = $this->chunkedTests->getTotalTests(); |
271
|
1 |
|
$numChunks = $this->chunkedTests->getNumChunks(); |
272
|
|
|
|
273
|
1 |
|
$this->output->writeln(sprintf('Total Tests: <info>%s</info>', $totalTests)); |
274
|
1 |
|
$this->output->writeln(sprintf('Number of Chunks Configured: <info>%s</info>', $numChunks)); |
275
|
1 |
|
$this->output->writeln(sprintf('Number of Chunks Produced: <info>%s</info>', count($chunks))); |
276
|
1 |
|
$this->output->writeln(sprintf('Tests Per Chunk: <info>~%s</info>', $testsPerChunk)); |
277
|
|
|
|
278
|
1 |
|
if ($chunk = $this->chunkedTests->getChunk()) { |
279
|
|
|
$this->output->writeln(sprintf('Chunk: <info>%s</info>', $chunk)); |
280
|
|
|
} |
281
|
|
|
|
282
|
1 |
|
$this->output->writeln('-----------'); |
283
|
1 |
|
$this->output->writeln(''); |
284
|
1 |
|
} |
285
|
|
|
|
286
|
1 |
|
private function setupSandbox() |
287
|
|
|
{ |
288
|
1 |
|
if ($this->input->getOption('sandbox')) { |
289
|
1 |
|
$this->testRunner->runTestCommand('sandbox'); |
290
|
|
|
} |
291
|
1 |
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* @return null|integer |
295
|
|
|
*/ |
296
|
1 |
|
private function runChunks() |
297
|
|
|
{ |
298
|
1 |
|
$chunks = $this->chunkedTests->getChunks(); |
299
|
|
|
|
300
|
1 |
|
foreach ($chunks as $i => $chunk) { |
301
|
|
|
// drop and recreate dbs before running this chunk of tests |
302
|
|
|
if ($this->input->getOption('create-dbs')) { |
303
|
|
|
$this->testRunner->runTestCommand('create-dbs', [ |
304
|
|
|
'--quiet' => true, |
305
|
|
|
]); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
$chunkNum = $i + 1; |
309
|
|
|
|
310
|
|
|
$code = $this->runChunk($chunkNum, $chunk); |
311
|
|
|
|
312
|
|
|
if ($code > 0) { |
313
|
|
|
return $code; |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
317
|
1 |
|
if ($this->parallel) { |
318
|
|
|
$this->waitForProcesses(); |
319
|
|
|
} |
320
|
1 |
|
} |
321
|
|
|
|
322
|
|
|
private function runChunk(int $chunkNum, array $chunk) |
323
|
|
|
{ |
324
|
|
|
$numTests = $this->countNumTestsInChunk($chunk); |
325
|
|
|
|
326
|
|
|
$this->totalTestsRan += $numTests; |
327
|
|
|
|
328
|
|
|
$process = $this->getChunkProcess($chunk); |
329
|
|
|
$this->processes[$chunkNum] = $process; |
330
|
|
|
|
331
|
|
|
$callback = $this->createProgressCallback($numTests); |
332
|
|
|
|
333
|
|
|
if ($this->parallel) { |
334
|
|
|
return $this->runChunkProcessParallel( |
335
|
|
|
$chunkNum, $process, $callback |
336
|
|
|
); |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
return $this->runChunkProcessSerial( |
340
|
|
|
$chunkNum, $process, $callback |
341
|
|
|
); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
private function getChunkProcess(array $chunk) : Process |
345
|
|
|
{ |
346
|
|
|
$files = $this->buildFilesFromChunk($chunk); |
347
|
|
|
|
348
|
|
|
$config = $this->testRunner->generatePhpunitXml($files); |
349
|
|
|
|
350
|
|
|
$command = sprintf('-c %s', $config); |
351
|
|
|
|
352
|
|
|
return $this->testRunner->getPhpunitProcess($command); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
private function createProgressCallback(int $numTests) : Closure |
356
|
|
|
{ |
357
|
|
|
if ($this->showProgressBar) { |
358
|
|
|
$this->progressBar = $this->createChunkProgressBar($numTests); |
|
|
|
|
359
|
|
|
|
360
|
|
|
return $this->createProgressBarCallback($this->progressBar); |
|
|
|
|
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
if ($this->verbose) { |
364
|
|
|
return function($type, $out) { |
365
|
|
|
$this->extractDataFromPhpunitOutput($out); |
366
|
|
|
|
367
|
|
|
$this->output->write($out); |
368
|
|
|
}; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
return function($type, $out) { |
372
|
|
|
$this->extractDataFromPhpunitOutput($out); |
373
|
|
|
}; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
private function createProgressBarCallback(ProgressBar $progressBar) |
377
|
|
|
{ |
378
|
|
|
return function(string $type, string $buffer) use ($progressBar) { |
379
|
|
|
$this->extractDataFromPhpunitOutput($buffer); |
380
|
|
|
|
381
|
|
|
if ($progressBar) { |
382
|
|
|
if (in_array($buffer, ['F', 'E'])) { |
383
|
|
|
$progressBar->setBarCharacter('<fg=red>=</>'); |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
if (in_array($buffer, ['F', 'E', 'S', '.'])) { |
387
|
|
|
$progressBar->advance(); |
388
|
|
|
} |
389
|
|
|
} |
390
|
|
|
}; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
private function runChunkProcessParallel( |
394
|
|
|
int $chunkNum, |
395
|
|
|
Process $process, |
396
|
|
|
Closure $callback) |
397
|
|
|
{ |
398
|
|
|
$this->output->writeln(sprintf('Starting chunk <info>#%s</info>', $chunkNum)); |
399
|
|
|
|
400
|
|
|
$process->start($callback); |
401
|
|
|
|
402
|
|
|
if (count($this->processes) >= $this->numParallelProcesses) { |
403
|
|
|
$this->waitForProcesses(); |
404
|
|
|
} |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
private function waitForProcesses() |
408
|
|
|
{ |
409
|
|
|
while (count($this->processes)) { |
410
|
|
|
foreach ($this->processes as $chunkNum => $process) { |
411
|
|
|
if ($process->isRunning()) { |
412
|
|
|
continue; |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
unset($this->processes[$chunkNum]); |
416
|
|
|
|
417
|
|
|
$this->codes[] = $code = $process->getExitCode(); |
418
|
|
|
|
419
|
|
|
if ($code > 0) { |
420
|
|
|
$this->numChunkFailures++; |
421
|
|
|
|
422
|
|
|
$this->output->writeln(sprintf('Chunk #%s <error>FAILED</error>', $chunkNum)); |
423
|
|
|
|
424
|
|
|
$this->output->writeln(''); |
425
|
|
|
$this->output->write($process->getOutput()); |
426
|
|
|
|
427
|
|
|
if ($this->input->getOption('stop')) { |
428
|
|
|
return $code; |
429
|
|
|
} |
430
|
|
View Code Duplication |
} else { |
|
|
|
|
431
|
|
|
$this->output->writeln(sprintf('Chunk #%s <info>PASSED</info>', $chunkNum)); |
432
|
|
|
|
433
|
|
|
if ($this->verbose) { |
434
|
|
|
$this->output->writeln(''); |
435
|
|
|
$this->output->write($process->getOutput()); |
436
|
|
|
} |
437
|
|
|
} |
438
|
|
|
} |
439
|
|
|
} |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
private function runChunkProcessSerial( |
443
|
|
|
int $chunkNum, |
444
|
|
|
Process $process, |
445
|
|
|
Closure $callback) |
446
|
|
|
{ |
447
|
|
|
if ($this->verbose) { |
448
|
|
|
$this->output->writeln(''); |
449
|
|
|
$this->output->writeln(sprintf('Running chunk <info>#%s</info>', $chunkNum)); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
$this->codes[] = $code = $process->run($callback); |
453
|
|
|
|
454
|
|
|
if ($code > 0) { |
455
|
|
|
$this->numChunkFailures++; |
456
|
|
|
|
457
|
|
|
if ($this->verbose) { |
458
|
|
|
$this->output->writeln(sprintf('Chunk #%s <error>FAILED</error>', $chunkNum)); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
if ($this->input->getOption('stop')) { |
462
|
|
|
$this->output->writeln(''); |
463
|
|
|
$this->output->writeln($process->getOutput()); |
464
|
|
|
|
465
|
|
|
return $code; |
466
|
|
|
} |
467
|
|
|
} |
468
|
|
|
|
469
|
|
|
if (!$this->verbose) { |
470
|
|
|
$this->progressBar->finish(); |
|
|
|
|
471
|
|
|
$this->output->writeln(''); |
472
|
|
|
} |
473
|
|
|
|
474
|
|
View Code Duplication |
if ($code > 0) { |
|
|
|
|
475
|
|
|
$this->output->writeln(''); |
476
|
|
|
|
477
|
|
|
if (!$this->verbose) { |
478
|
|
|
$this->output->writeln($process->getOutput()); |
479
|
|
|
} |
480
|
|
|
} |
481
|
|
|
} |
482
|
|
|
|
483
|
1 |
|
private function outputFooter() |
484
|
|
|
{ |
485
|
1 |
|
$chunks = $this->chunkedTests->getChunks(); |
486
|
|
|
|
487
|
1 |
|
$failed = $this->hasFailed(); |
488
|
|
|
|
489
|
1 |
|
$event = $this->stopwatch->stop('Tests'); |
490
|
|
|
|
491
|
1 |
|
$this->output->writeln(''); |
492
|
1 |
|
$this->output->writeln(sprintf('Time: %s seconds, Memory: %s', |
493
|
1 |
|
round($event->getDuration() / 1000, 2), |
494
|
1 |
|
$this->formatBytes($event->getMemory()) |
495
|
|
|
)); |
496
|
|
|
|
497
|
1 |
|
$this->output->writeln(''); |
498
|
1 |
|
$this->output->writeln(sprintf('%s (%s chunks, %s tests, %s assertions, %s failures%s)', |
499
|
1 |
|
$failed ? '<error>FAILED</error>' : '<info>PASSED</info>', |
500
|
|
|
count($chunks), |
501
|
1 |
|
$this->totalTestsRan, |
502
|
1 |
|
$this->numAssertions, |
503
|
1 |
|
$this->numFailures, |
504
|
1 |
|
$failed ? sprintf(', Failed chunks: %s', $this->numChunkFailures) : '' |
505
|
|
|
)); |
506
|
1 |
|
} |
507
|
|
|
|
508
|
1 |
|
private function hasFailed() : bool |
509
|
|
|
{ |
510
|
1 |
|
return array_sum($this->codes) ? true : false; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
private function extractDataFromPhpunitOutput(string $outputBuffer) : int |
514
|
|
|
{ |
515
|
|
|
preg_match_all('/([0-9]+) assertions/', $outputBuffer, $matches); |
516
|
|
|
|
517
|
|
View Code Duplication |
if (isset($matches[1][0])) { |
|
|
|
|
518
|
|
|
$this->numAssertions += (int) $matches[1][0]; |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
preg_match_all('/Assertions: ([0-9]+)/', $outputBuffer, $matches); |
522
|
|
|
|
523
|
|
View Code Duplication |
if (isset($matches[1][0])) { |
|
|
|
|
524
|
|
|
$this->numAssertions += (int) $matches[1][0]; |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
preg_match_all('/Failures: ([0-9]+)/', $outputBuffer, $matches); |
528
|
|
|
|
529
|
|
|
if (isset($matches[1][0])) { |
530
|
|
|
$this->numFailures += (int) $matches[1][0]; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
return 0; |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
private function countNumTestsInChunk(array $chunk) : int |
537
|
|
|
{ |
538
|
|
|
return array_sum(array_map(function(array $chunkFile) { |
539
|
|
|
return $chunkFile['numTests']; |
540
|
|
|
}, $chunk)); |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
private function buildFilesFromChunk(array $chunk) : array |
544
|
|
|
{ |
545
|
|
|
return array_map(function(array $chunkFile) { |
546
|
|
|
return $chunkFile['file']; |
547
|
|
|
}, $chunk); |
548
|
|
|
} |
549
|
|
|
|
550
|
|
|
private function createChunkProgressBar(int $numTests) : ProgressBar |
551
|
|
|
{ |
552
|
|
|
$progressBar = new ProgressBar($this->output, $numTests); |
553
|
|
|
$progressBar->setBarCharacter('<fg=green>=</>'); |
554
|
|
|
$progressBar->setProgressCharacter("\xF0\x9F\x8C\xAD"); |
555
|
|
|
|
556
|
|
|
return $progressBar; |
557
|
|
|
} |
558
|
|
|
|
559
|
1 |
|
private function formatBytes(int $size, int $precision = 2) : string |
560
|
|
|
{ |
561
|
1 |
|
if (!$size) { |
562
|
|
|
return 0; |
563
|
|
|
} |
564
|
|
|
|
565
|
1 |
|
$base = log($size, 1024); |
566
|
1 |
|
$suffixes = ['', 'KB', 'MB', 'GB', 'TB']; |
567
|
|
|
|
568
|
1 |
|
return round(pow(1024, $base - floor($base)), $precision).$suffixes[floor($base)]; |
569
|
|
|
} |
570
|
|
|
} |
571
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.