Completed
Pull Request — master (#232)
by Daniel
03:09
created

PHPUnit   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Test Coverage

Coverage 56%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 32
c 1
b 0
f 0
lcom 2
cbo 6
dl 0
loc 225
ccs 56
cts 100
cp 0.56
rs 9.6

10 Methods

Rating   Name   Duplication   Size   Complexity  
A hasPath() 0 6 2
A hasConfig() 0 4 1
A configure() 0 17 1
C execute() 0 26 7
C getConfig() 0 36 7
A getRunnerOptions() 0 17 3
A requireBootstrap() 0 13 3
A scopedRequire() 0 6 1
A hasCoverage() 0 6 3
A getBootstrapFile() 0 15 4
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
        $runner->run();
84
        return $runner->getExitCode();
85
    }
86
87
    /**
88
     * Returns whether or not a test path has been supplied
89
     * via option or regular input
90
     *
91
     * @param InputInterface $input
92
     * @return bool
93
     */
94
    protected function hasPath(InputInterface $input)
95
    {
96
        $argument = $input->getArgument('path');
97
        $option = $input->getOption('path');
98
        return $argument || $option;
99
    }
100
101
    /**
102
     * Is there a PHPUnit xml configuration present
103
     *
104
     * @param InputInterface $input
105
     * @return bool
106
     */
107 4
    protected function hasConfig(InputInterface $input)
108
    {
109 4
        return (false !== $this->getConfig($input));
110
    }
111
112
    /**
113
     * @param \Symfony\Component\Console\Input\InputInterface $input
114
     * @return \ParaTest\Runners\PHPUnit\Configuration|boolean
115
     */
116 4
    protected function getConfig(InputInterface $input)
117
    {
118 4
        $cwd = getcwd() . DIRECTORY_SEPARATOR;
119
120 4
        if ($config = $input->getOption('configuration')) {
121
          // Make it possible to not specify the exact location but rather just
122
          // the folder.
123
          if (\is_dir($config)) {
124
            $configurationFile = $config . '/phpunit.xml';
125
            if (\file_exists($configurationFile)) {
126
              $configFilename = \realpath(
127
                $configurationFile
128
              );
129
            }
130
            elseif (\file_exists($configurationFile . '.dist')) {
131
              $configFilename = \realpath(
132
                $configurationFile . '.dist'
133
              );
134
            }
135
            else {
136
              throw new \InvalidArgumentException('No configuration file found in the specified directory.');
137
            }
138
          }
139
          else {
140
            $configFilename = $input->getOption('configuration');
141
          }
142 4
        } elseif (file_exists($cwd . 'phpunit.xml.dist')) {
143 4
            $configFilename = $cwd . 'phpunit.xml.dist';
144 4
        } elseif (file_exists($cwd . 'phpunit.xml')) {
145
            $configFilename = $cwd . 'phpunit.xml';
146
        } else {
147
            return false;
148
        }
149
150 4
        return new Configuration($configFilename);
151
    }
152
153
    /**
154
     * @param \Symfony\Component\Console\Input\InputInterface $input
155
     * @return array
156
     * @throws \RuntimeException
157
     */
158 4
    public function getRunnerOptions(InputInterface $input)
159
    {
160 4
        $path = $input->getArgument('path');
161 4
        $options = $this->getOptions($input);
162 4
        $bootstrap = $this->getBootstrapFile($input, $options);
163 4
        $this->requireBootstrap($bootstrap);
164
165 4
        if ($this->hasCoverage($options)) {
166 2
            $options['coverage-php'] = tempnam(sys_get_temp_dir(), 'paratest_');
167 2
        }
168
169 4
        if ($path) {
170 4
            $options = array_merge(array('path' => $path), $options);
171 4
        }
172
173 4
        return $options;
174
    }
175
176
    /**
177
     * Require the bootstrap. If the file is specified, but does not exist
178
     * then an exception will be raised.
179
     *
180
     * @param $file
181
     * @throws \RuntimeException
182
     */
183 5
    public function requireBootstrap($file)
184
    {
185 5
        if (! $file) {
186
            return;
187
        }
188
189 5
        if (! file_exists($file)) {
190
            $message = sprintf('Bootstrap specified but could not be found (%s)', $file);
191
            throw new \RuntimeException($message);
192
        }
193
194 5
        $this->scopedRequire($file);
195 5
    }
196
197
    /**
198
     * This function limits the scope of a required file
199
     * so that variables defined in it do not break
200
     * this object's configuration.
201
     */
202 5
    protected function scopedRequire($file)
203
    {
204 5
        $cwd = getcwd();
205 5
        require_once $file;
206 5
        chdir($cwd);
207 5
    }
208
209
    /**
210
     * Return whether or not code coverage information should be collected.
211
     *
212
     * @param $options
213
     * @return bool
214
     */
215 4
    protected function hasCoverage($options)
216
    {
217 4
        $isFileFormat = isset($options['coverage-html']) || isset($options['coverage-clover']);
218 4
        $isPHP = isset($options['coverage-php']);
219 4
        return $isFileFormat && ! $isPHP;
220
    }
221
222
    /**
223
     * Fetch the path to the bootstrap file.
224
     *
225
     * @param InputInterface $input
226
     * @param array $options
227
     * @return string
228
     */
229 4
    protected function getBootstrapFile(InputInterface $input, array $options)
230
    {
231 4
        if (isset($options['bootstrap'])) {
232
            return $options['bootstrap'];
233
        }
234
235 4
        if (! $this->hasConfig($input)) {
236
            return '';
237
        }
238
239 4
        $config = $this->getConfig($input);
240 4
        $bootstrap = $config->getBootstrap();
241
242 4
        return ($bootstrap) ? $config->getConfigDir() . $bootstrap : '';
243
    }
244
}
245