ChunkRunner::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 11
cts 11
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 18
nc 2
nop 8
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace PHPChunkit;
6
7
use Closure;
8
use Symfony\Component\Console\Helper\ProgressBar;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Process\Process;
12
13
class ChunkRunner
14
{
15
    /**
16
     * @var ChunkedTests
17
     */
18
    private $chunkedTests;
19
20
    /**
21
     * @var ChunkResults
22
     */
23
    private $chunkResults;
24
25
    /**
26
     * @var TestRunner
27
     */
28
    private $testRunner;
29
30
    /**
31
     * @var Processes
32
     */
33
    private $processes;
34
35
    /**
36
     * @var InputInterface
37
     */
38
    private $input;
39
40
    /**
41
     * @var OutputInterface
42
     */
43
    private $output;
44
45
    /**
46
     * @var bool
47
     */
48
    private $verbose = false;
49
50
    /**
51
     * @var bool
52
     */
53
    private $parallel = false;
54
55
    /**
56
     * @var bool
57
     */
58
    private $showProgressBar = false;
59
60
    /**
61
     * @var ProgressBar|null
62
     */
63
    private $progressBar;
64
65 1
    public function __construct(
66
        ChunkedTests $chunkedTests,
67
        ChunkResults $chunkResults,
68
        TestRunner $testRunner,
69
        Processes $processes,
70
        InputInterface $input,
71
        OutputInterface $output,
72
        bool $verbose = false,
73
        bool $parallel = false)
74
    {
75 1
        $this->chunkedTests = $chunkedTests;
76 1
        $this->chunkResults = $chunkResults;
77 1
        $this->testRunner = $testRunner;
78 1
        $this->processes = $processes;
79 1
        $this->input = $input;
80 1
        $this->output = $output;
81 1
        $this->verbose = $verbose;
82 1
        $this->parallel = $parallel;
83 1
        $this->showProgressBar = !$this->verbose && !$this->parallel;
84 1
    }
85
86
    /**
87
     * @return null|integer
88
     */
89 1
    public function runChunks()
90
    {
91 1
        $chunks = $this->chunkedTests->getChunks();
92
93 1
        foreach ($chunks as $i => $chunk) {
94
            // drop and recreate dbs before running this chunk of tests
95
            if ($this->input->getOption('create-dbs')) {
96
                $this->testRunner->runTestCommand('create-dbs', [
97
                    '--quiet' => true,
98
                ]);
99
            }
100
101
            $chunkNum = $i + 1;
102
103
            $code = $this->runChunk($chunkNum, $chunk);
104
105
            if ($code > 0) {
106
                return $code;
107
            }
108
        }
109
110 1
        if ($this->parallel) {
111
            $this->processes->wait();
112
        }
113 1
    }
114
115
    private function runChunk(int $chunkNum, array $chunk)
116
    {
117
        $numTests = $this->countNumTestsInChunk($chunk);
118
119
        $this->chunkResults->incrementTotalTestsRan($numTests);
120
121
        $process = $this->getChunkProcess($chunk);
122
        $this->processes->addProcess($process);
123
124
        $callback = $this->createProgressCallback($numTests);
125
126
        if ($this->parallel) {
127
            return $this->runChunkProcessParallel(
128
                $chunkNum, $process, $callback
129
            );
130
        }
131
132
        return $this->runChunkProcessSerial(
133
            $chunkNum, $process, $callback
134
        );
135
    }
136
137
    private function getChunkProcess(array $chunk) : Process
138
    {
139
        $files = $this->buildFilesFromChunk($chunk);
140
141
        $config = $this->testRunner->generatePhpunitXml($files);
142
143
        $command = sprintf('-c %s', $config);
144
145
        return $this->testRunner->getPhpunitProcess($command);
146
    }
147
148
    private function createProgressCallback(int $numTests) : Closure
149
    {
150
        if ($this->showProgressBar) {
151
            $this->progressBar = $this->createChunkProgressBar($numTests);
152
153
            return $this->createProgressBarCallback($this->progressBar);
154
        }
155
156
        if ($this->verbose) {
157
            return function($type, $out) {
158
                $this->extractDataFromPhpunitOutput($out);
159
160
                $this->output->write($out);
161
            };
162
        }
163
164
        return function($type, $out) {
165
            $this->extractDataFromPhpunitOutput($out);
166
        };
167
    }
168
169
    private function createProgressBarCallback(ProgressBar $progressBar)
170
    {
171
        return function(string $type, string $buffer) use ($progressBar) {
172
            $this->extractDataFromPhpunitOutput($buffer);
173
174
            if ($progressBar) {
175
                if (in_array($buffer, ['F', 'E'])) {
176
                    $progressBar->setBarCharacter('<fg=red>=</>');
177
                }
178
179
                if (in_array($buffer, ['F', 'E', 'S', '.'])) {
180
                    $progressBar->advance();
181
                }
182
            }
183
        };
184
    }
185
186
    private function runChunkProcessParallel(
187
        int $chunkNum,
188
        Process $process,
189
        Closure $callback)
190
    {
191
        $this->output->writeln(sprintf('Starting chunk <info>#%s</info>', $chunkNum));
192
193
        $process->start($callback);
194
195
        $this->processes->wait();
196
    }
197
198
    private function runChunkProcessSerial(
199
        int $chunkNum,
200
        Process $process,
201
        Closure $callback)
202
    {
203
        if ($this->verbose) {
204
            $this->output->writeln('');
205
            $this->output->writeln(sprintf('Running chunk <info>#%s</info>', $chunkNum));
206
        }
207
208
        $this->chunkResults->addCode($code = $process->run($callback));
209
210
        if ($code > 0) {
211
            $this->chunkResults->incrementNumChunkFailures();
212
213
            if ($this->verbose) {
214
                $this->output->writeln(sprintf('Chunk #%s <error>FAILED</error>', $chunkNum));
215
            }
216
217
            if ($this->input->getOption('stop')) {
218
                $this->output->writeln('');
219
                $this->output->writeln($process->getOutput());
220
221
                return $code;
222
            }
223
        }
224
225
        if (!$this->verbose) {
226
            $this->progressBar->finish();
227
            $this->output->writeln('');
228
        }
229
230 View Code Duplication
        if ($code > 0) {
231
            $this->output->writeln('');
232
233
            if (!$this->verbose) {
234
                $this->output->writeln($process->getOutput());
235
            }
236
        }
237
    }
238
239
    private function countNumTestsInChunk(array $chunk) : int
240
    {
241
        return array_sum(array_map(function(array $chunkFile) {
242
            return $chunkFile['numTests'];
243
        }, $chunk));
244
    }
245
246
    private function buildFilesFromChunk(array $chunk) : array
247
    {
248
        return array_map(function(array $chunkFile) {
249
            return $chunkFile['file'];
250
        }, $chunk);
251
    }
252
253
    private function extractDataFromPhpunitOutput(string $outputBuffer) : int
254
    {
255
        preg_match_all('/([0-9]+) assertions/', $outputBuffer, $matches);
256
257 View Code Duplication
        if (isset($matches[1][0])) {
258
            $this->chunkResults->incrementNumAssertions((int) $matches[1][0]);
259
        }
260
261
        preg_match_all('/Assertions: ([0-9]+)/', $outputBuffer, $matches);
262
263 View Code Duplication
        if (isset($matches[1][0])) {
264
            $this->chunkResults->incrementNumAssertions((int) $matches[1][0]);
265
        }
266
267
        preg_match_all('/Failures: ([0-9]+)/', $outputBuffer, $matches);
268
269
        if (isset($matches[1][0])) {
270
            $this->chunkResults->incrementNumFailures((int) $matches[1][0]);
271
        }
272
273
        return 0;
274
    }
275
276
    private function createChunkProgressBar(int $numTests) : ProgressBar
277
    {
278
        $progressBar = new ProgressBar($this->output, $numTests);
279
        $progressBar->setBarCharacter('<fg=green>=</>');
280
        $progressBar->setProgressCharacter("\xF0\x9F\x8C\xAD");
281
282
        return $progressBar;
283
    }
284
}
285