TestRunner::runTestCommand()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace PHPChunkit;
6
7
use RuntimeException;
8
use Symfony\Component\Console\Application;
9
use Symfony\Component\Console\Input\ArrayInput;
10
use Symfony\Component\Console\Input\InputInterface;
11
use Symfony\Component\Console\Output\Output;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Process\Process;
14
15
/**
16
 * @testClass PHPChunkit\Test\TestRunnerTest
17
 */
18
class TestRunner
19
{
20
    /**
21
     * @var Application
22
     */
23
    private $app;
24
25
    /**
26
     * @var InputInterface
27
     */
28
    private $input;
29
30
    /**
31
     * @var OutputInterface
32
     */
33
    private $output;
34
35
    /**
36
     * @var Configuration
37
     */
38
    private $configuration;
39
40 6
    public function __construct(
41
        Application $app,
42
        InputInterface $input,
43
        OutputInterface $output,
44
        Configuration $configuration)
45
    {
46 6
        $this->app = $app;
47 6
        $this->input = $input;
48 6
        $this->output = $output;
49 6
        $this->configuration = $configuration;
50 6
    }
51
52 1
    public function getRootDir() : string
53
    {
54 1
        return $this->configuration->getRootDir();
55
    }
56
57 1
    public function generatePhpunitXml(array &$files) : string
58
    {
59
        // load the config into memory
60 1
        $phpunitXmlDistPath = is_file($file = $this->getRootDir().'/phpunit.xml')
61
            ? $file
62 1
            : $this->getRootDir().'/phpunit.xml.dist'
63
        ;
64
65 1
        $src = file_get_contents($phpunitXmlDistPath);
66 1
        $xml = simplexml_load_string(str_replace('./', $this->getRootDir().'/', $src));
67
68
        // temp config file
69 1
        $config = tempnam($this->getRootDir(), 'phpunitxml');
70
71
        register_shutdown_function(function() use ($config) {
72
            unlink($config);
73 1
        });
74
75 1
        unset($xml->testsuites[0]->testsuite);
76 1
        $suite = $xml->testsuites[0]->addChild('testsuite');
77
78 1
        if (!empty($files)) {
79 1
            $files = array_unique($files);
80
81 1
            foreach ($files as $file) {
82 1
                $path = strpos($file, '/') === 0
83
                    ? $file
84 1
                    : $this->getRootDir().'/'.$file;
85
86 1
                $suite->addChild('file', $path);
87
            }
88
89 1
            file_put_contents($config, $xml->asXml());
90
91 1
            return $config;
92
        }
93
94
        return '';
95
    }
96
97 1
    public function runTestFiles(array $files, array $env = []) : int
98
    {
99 1
        $config = $this->generatePhpunitXml($files);
100
101 1
        if ($config !== null) {
102 1
            $this->output->writeln('');
103
104 1
            foreach ($files as $file) {
105 1
                $this->output->writeln(sprintf(' - Executing <comment>%s</comment>', $file));
106
            }
107
108 1
            $this->output->writeln('');
109
110 1
            return $this->runPhpunit(sprintf('-c %s', escapeshellarg($config)), $env);
111
        } else {
112
            $this->output->writeln('No tests to run.');
113
114
            return 1;
115
        }
116
    }
117
118 1
    public function runPhpunit(string $command, array $env = [], \Closure $callback = null) : int
119
    {
120 1
        $command = sprintf('%s %s %s',
121 1
            $this->configuration->getPhpunitPath(),
122
            $command,
123 1
            $this->flags()
124
        );
125
126 1
        return $this->run($command, false, $env, $callback);
127
    }
128
129
    public function getPhpunitProcess(string $command, array $env = []) : Process
130
    {
131
        $command = sprintf('%s %s %s',
132
            $this->configuration->getPhpunitPath(),
133
            $command,
134
            $this->flags()
135
        );
136
137
        return $this->getProcess($command, $env);
138
    }
139
140
    /**
141
     * @throws RuntimeException
142
     */
143 2
    public function run(string $command, bool $throw = true, array $env = [], \Closure $callback = null) : int
144
    {
145 2
        $process = $this->getProcess($command, $env);
146
147 2
        if ($callback === null) {
148
            $callback = function($output) {
149
                echo $output;
150 2
            };
151
        }
152
153 2
        $process->run(function($type, $output) use ($callback) {
154
            $callback($output);
155 2
        });
156
157 2
        if ($process->getExitCode() > 0 && $throw) {
158 1
            throw new RuntimeException('The command did not exit successfully.');
159
        }
160
161 1
        return $process->getExitCode();
162
    }
163
164 2
    public function getProcess(string $command, array $env = []) : Process
165
    {
166 2
        foreach ($env as $key => $value) {
167
            $command = sprintf('export %s=%s && %s', $key, $value, $command);
168
        }
169
170 2
        if ($this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
171
            $this->output->writeln('+ <comment>'.$command.'</comment>');
172
        }
173
174 2
        return $this->createProcess($command);
175
    }
176
177 1
    public function runTestCommand(string $command, array $input = []) : int
178
    {
179 1
        $input['command'] = $command;
180
181 1
        return $this->app->find($command)
182 1
            ->run(new ArrayInput($input), $this->output);
183
    }
184
185 1
    private function flags() : string
186
    {
187 1
        $memoryLimit = $this->input->getOption('memory-limit')
188 1
            ?: $this->configuration->getMemoryLimit();
189
190 1
        $flags = '-d memory_limit='.escapeshellarg($memoryLimit);
191
192 1
        if ($this->input->getOption('stop')) {
193
            $flags .= ' --stop-on-failure --stop-on-error';
194
        }
195
196 1
        if ($this->output->getVerbosity() > Output::VERBOSITY_NORMAL) {
197
            $flags .= ' --verbose';
198
        }
199
200 1
        if ($this->input->getOption('debug')) {
201
            $flags .= ' --debug';
202
        }
203
204 1
        if ($this->output->isDecorated()) {
205
            $flags .= ' --colors';
206
        }
207
208 1
        if ($this->input->hasOption('phpunit-opt')
209 1
            && $phpunitOptions = $this->input->getOption('phpunit-opt')) {
210
            $flags .= ' '.$phpunitOptions;
211
        }
212
213 1
        return $flags;
214
    }
215
216
    protected function createProcess(string $command) : Process
217
    {
218
        return new Process($command, null, null, null, null);
219
    }
220
}
221