Completed
Pull Request — master (#38)
by Jonathan
02:53
created

TestRunner::run()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

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