Completed
Pull Request — master (#217)
by
unknown
03:02
created

PHPUnit::execute()   D

Complexity

Conditions 10
Paths 96

Size

Total Lines 37
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 37
ccs 0
cts 26
cp 0
rs 4.8196
cc 10
eloc 21
nc 96
nop 2
crap 110

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php 
2
namespace ParaTest\Console\Testers;
3
4
use Symfony\Component\Console\Command\Command;
5
use Symfony\Component\Console\Input\InputOption;
6
use Symfony\Component\Console\Input\InputArgument;
7
use Symfony\Component\Console\Input\InputInterface;
8
use Symfony\Component\Console\Output\OutputInterface;
9
use ParaTest\Runners\PHPUnit\Configuration;
10
use ParaTest\Runners\PHPUnit\Runner;
11
use ParaTest\Runners\PHPUnit\WrapperRunner;
12
13
/**
14
 * Class PHPUnit
15
 *
16
 * Creates the interface for PHPUnit testing
17
 *
18
 * @package ParaTest\Console\Testers
19
 */
20
class PHPUnit extends Tester
21
{
22
    /**
23
     * @var \ParaTest\Console\Commands\ParaTestCommand
24
     */
25
    protected $command;
26
27
    /**
28
     * Configures the ParaTestCommand with PHPUnit specific
29
     * definitions
30
     *
31
     * @param Command $command
32
     * @return mixed
33
     */
34 8
    public function configure(Command $command)
35
    {
36
        $command
37 8
            ->addOption('phpunit', null, InputOption::VALUE_REQUIRED, 'The PHPUnit binary to execute. <comment>(default: vendor/bin/phpunit)</comment>')
38 8
            ->addOption('runner', null, InputOption::VALUE_REQUIRED, 'Runner or WrapperRunner. <comment>(default: Runner)</comment>')
39 8
            ->addOption('bootstrap', null, InputOption::VALUE_REQUIRED, 'The bootstrap file to be used by PHPUnit.')
40 8
            ->addOption('configuration', 'c', InputOption::VALUE_REQUIRED, 'The PHPUnit configuration file to use.')
41 8
            ->addOption('group', 'g', InputOption::VALUE_REQUIRED, 'Only runs tests from the specified group(s).')
42 8
            ->addOption('exclude-group', null, InputOption::VALUE_REQUIRED, 'Don\'t run tests from the specified group(s).')
43 8
            ->addOption('stop-on-failure', null, InputOption::VALUE_NONE, 'Don\'t start any more processes after a failure.')
44 8
            ->addOption('log-junit', null, InputOption::VALUE_REQUIRED, 'Log test execution in JUnit XML format to file.')
45 8
            ->addOption('colors', null, InputOption::VALUE_NONE, 'Displays a colored bar as a test result.')
46 8
            ->addOption('testsuite', null, InputOption::VALUE_OPTIONAL, 'Filter which testsuite to run')
47 8
            ->addArgument('path', InputArgument::OPTIONAL, 'The path to a directory or file containing tests. <comment>(default: current directory)</comment>')
48 8
            ->addOption('path', null, InputOption::VALUE_REQUIRED, 'An alias for the path argument.');
49 8
        $this->command = $command;
0 ignored issues
show
Documentation Bug introduced by
$command is of type object<Symfony\Component\Console\Command\Command>, but the property $command was declared to be of type object<ParaTest\Console\Commands\ParaTestCommand>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
50 8
    }
51
52
    /**
53
     * Executes the PHPUnit Runner. Will Display help if no config and no path
54
     * supplied
55
     *
56
     * @param InputInterface $input
57
     * @param OutputInterface $output
58
     * @return int|mixed
59
     */
60
    public function execute(InputInterface $input, OutputInterface $output)
61
    {
62
        if (!$this->hasConfig($input) && !$this->hasPath($input)) {
63
            $this->displayHelp($input, $output);
64
        }
65
66
        if ($input->getOption('runner') === 'WrapperRunner') {
67
            $runner = new WrapperRunner($this->getRunnerOptions($input));
68
        } else {
69
            if ($input->getOption('runner') !== '') {
70
                // because we want to have to bootstrap script inherited before check/initialization
71
                $runnerOption = $this->getRunnerOptions($input);
72
                $runnerClass = $input->getOption('runner');
73
                if (class_exists($runnerClass)) {
74
                    $runner = new $runnerClass($runnerOption);
75
                }
76
            }
77
        }
78
79
        if (!isset($runner)) {
80
            $runner = new Runner($this->getRunnerOptions($input));
81
        }
82
83
        // Allow the tests to be repeated.
84
        $statusCode = 0;
85
        $repeat = $input->getOption('repeat') ?: 1;
86
        for ($i = 0; $i < $repeat; $i++) {
87
            $runner->run();
88
            $status = $runner->getExitCode();
89
            $statusCode = $status ? $status : $statusCode;
90
        }
91
92
        // Print all messages at the end.
93
        $runner->complete();
94
95
        return $statusCode;
96
    }
97
98
    /**
99
     * Returns whether or not a test path has been supplied
100
     * via option or regular input
101
     *
102
     * @param InputInterface $input
103
     * @return bool
104
     */
105
    protected function hasPath(InputInterface $input)
106
    {
107
        $argument = $input->getArgument('path');
108
        $option = $input->getOption('path');
109
        return $argument || $option;
110
    }
111
112
    /**
113
     * Is there a PHPUnit xml configuration present
114
     *
115
     * @param InputInterface $input
116
     * @return bool
117
     */
118 4
    protected function hasConfig(InputInterface $input)
119
    {
120 4
        return (false !== $this->getConfig($input));
121
    }
122
123
    /**
124
     * @param \Symfony\Component\Console\Input\InputInterface $input
125
     * @return \ParaTest\Runners\PHPUnit\Configuration|boolean
126
     */
127 4
    protected function getConfig(InputInterface $input)
128
    {
129 4
        $cwd = getcwd() . DIRECTORY_SEPARATOR;
130
131 4
        if ($input->getOption('configuration')) {
132
            $configFilename = $input->getOption('configuration');
133 4
        } elseif (file_exists($cwd . 'phpunit.xml.dist')) {
134 4
            $configFilename = $cwd . 'phpunit.xml.dist';
135 4
        } elseif (file_exists($cwd . 'phpunit.xml')) {
136
            $configFilename = $cwd . 'phpunit.xml';
137
        } else {
138
            return false;
139
        }
140
141 4
        return new Configuration($configFilename);
142
    }
143
144
    /**
145
     * @param \Symfony\Component\Console\Input\InputInterface $input
146
     * @return array
147
     * @throws \RuntimeException
148
     */
149 4
    public function getRunnerOptions(InputInterface $input)
150
    {
151 4
        $path = $input->getArgument('path');
152 4
        $options = $this->getOptions($input);
153 4
        $bootstrap = $this->getBootstrapFile($input, $options);
154 4
        $this->requireBootstrap($bootstrap);
155
156 4
        if ($this->hasCoverage($options)) {
157 2
            $options['coverage-php'] = tempnam(sys_get_temp_dir(), 'paratest_');
158 2
        }
159
160 4
        if ($path) {
161 4
            $options = array_merge(array('path' => $path), $options);
162 4
        }
163
164 4
        return $options;
165
    }
166
167
    /**
168
     * Require the bootstrap. If the file is specified, but does not exist
169
     * then an exception will be raised.
170
     *
171
     * @param $file
172
     * @throws \RuntimeException
173
     */
174 5
    public function requireBootstrap($file)
175
    {
176 5
        if (! $file) {
177
            return;
178
        }
179
180 5
        if (! file_exists($file)) {
181
            $message = sprintf('Bootstrap specified but could not be found (%s)', $file);
182
            throw new \RuntimeException($message);
183
        }
184
185 5
        $this->scopedRequire($file);
186 5
    }
187
188
    /**
189
     * This function limits the scope of a required file
190
     * so that variables defined in it do not break
191
     * this object's configuration.
192
     */
193 5
    protected function scopedRequire($file)
194
    {
195 5
        $cwd = getcwd();
196 5
        require_once $file;
197 5
        chdir($cwd);
198 5
    }
199
200
    /**
201
     * Return whether or not code coverage information should be collected.
202
     *
203
     * @param $options
204
     * @return bool
205
     */
206 4
    protected function hasCoverage($options)
207
    {
208 4
        $isFileFormat = isset($options['coverage-html']) || isset($options['coverage-clover']);
209 4
        $isPHP = isset($options['coverage-php']);
210 4
        return $isFileFormat && ! $isPHP;
211
    }
212
213
    /**
214
     * Fetch the path to the bootstrap file.
215
     *
216
     * @param InputInterface $input
217
     * @param array $options
218
     * @return string
219
     */
220 4
    protected function getBootstrapFile(InputInterface $input, array $options)
221
    {
222 4
        if (isset($options['bootstrap'])) {
223
            return $options['bootstrap'];
224
        }
225
226 4
        if (! $this->hasConfig($input)) {
227
            return '';
228
        }
229
230 4
        $config = $this->getConfig($input);
231 4
        $bootstrap = $config->getBootstrap();
232
233 4
        return ($bootstrap) ? $config->getConfigDir() . $bootstrap : '';
234
    }
235
}
236