PhpUnit::__construct()   D
last analyzed

Complexity

Conditions 9
Paths 128

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 33
ccs 0
cts 25
cp 0
rs 4.6666
cc 9
eloc 17
nc 128
nop 3
crap 90
1
<?php
2
/**
3
 * PHPCI - Continuous Integration for PHP.
4
 *
5
 * @copyright    Copyright 2014, Block 8 Limited.
6
 * @license      https://github.com/Block8/PHPCI/blob/master/LICENSE.md
7
 *
8
 * @link         https://www.phptesting.org/
9
 */
10
11
namespace PHPCI\Plugin;
12
13
use PHPCI;
14
use PHPCI\Builder;
15
use PHPCI\Model\Build;
16
use PHPCI\Plugin\Util\TapParser;
17
18
/**
19
 * PHP Unit Plugin - Allows PHP Unit testing.
20
 *
21
 * @author       Dan Cryer <[email protected]>
22
 */
23
class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
24
{
25
    protected $args;
26
    protected $phpci;
27
    protected $build;
28
29
    /**
30
     * @var string|string[] The directory (or array of dirs) to run PHPUnit on
31
     */
32
    protected $directory;
33
34
    /**
35
     * @var string When running PHPUnit with an XML config, the command is run from this directory
36
     */
37
    protected $runFrom;
38
39
    /**
40
     * @var string, in cases where tests files are in a sub path of the /tests path,
41
     *              allows this path to be set in the config.
42
     */
43
    protected $path;
44
45
    protected $coverage = '';
46
47
    /**
48
     * @var string|string[] The path (or array of paths) of an xml config for PHPUnit
49
     */
50
    protected $xmlConfigFile;
51
52
    /**
53
     * Check if this plugin can be executed.
54
     *
55
     * @param $stage
56
     * @param Builder $builder
57
     * @param Build   $build
58
     *
59
     * @return bool
60
     */
61
    public static function canExecute($stage, Builder $builder, Build $build)
62
    {
63
        if ($stage == 'test' && !is_null(self::findConfigFile($builder->buildPath))) {
64
            return true;
65
        }
66
67
        return false;
68
    }
69
70
    /**
71
     * Try and find the phpunit XML config file.
72
     *
73
     * @param $buildPath
74
     *
75
     * @return null|string
76
     */
77
    public static function findConfigFile($buildPath)
78
    {
79
        if (file_exists($buildPath.'phpunit.xml')) {
80
            return 'phpunit.xml';
81
        }
82
83
        if (file_exists($buildPath.'tests'.DIRECTORY_SEPARATOR.'phpunit.xml')) {
84
            return 'tests'.DIRECTORY_SEPARATOR.'phpunit.xml';
85
        }
86
87
        if (file_exists($buildPath.'phpunit.xml.dist')) {
88
            return 'phpunit.xml.dist';
89
        }
90
91
        if (file_exists($buildPath.'tests/phpunit.xml.dist')) {
92
            return 'tests'.DIRECTORY_SEPARATOR.'phpunit.xml.dist';
93
        }
94
95
        return;
96
    }
97
98
    /**
99
     * Standard Constructor.
100
     *
101
     * $options['directory'] Output Directory. Default: %BUILDPATH%
102
     * $options['filename']  Phar Filename. Default: build.phar
103
     * $options['regexp']    Regular Expression Filename Capture. Default: /\.php$/
104
     * $options['stub']      Stub Content. No Default Value
105
     *
106
     * @param Builder $phpci
107
     * @param Build   $build
108
     * @param array   $options
109
     */
110
    public function __construct(Builder $phpci, Build $build, array $options = array())
111
    {
112
        $this->phpci = $phpci;
113
        $this->build = $build;
114
115
        if (empty($options['config']) && empty($options['directory'])) {
116
            $this->xmlConfigFile = self::findConfigFile($phpci->buildPath);
117
        }
118
119
        if (isset($options['directory'])) {
120
            $this->directory = $options['directory'];
121
        }
122
123
        if (isset($options['config'])) {
124
            $this->xmlConfigFile = $options['config'];
125
        }
126
127
        if (isset($options['run_from'])) {
128
            $this->runFrom = $options['run_from'];
129
        }
130
131
        if (isset($options['args'])) {
132
            $this->args = $this->phpci->interpolate($options['args']);
133
        }
134
135
        if (isset($options['path'])) {
136
            $this->path = $options['path'];
137
        }
138
139
        if (isset($options['coverage'])) {
140
            $this->coverage = ' --coverage-html '.$this->phpci->interpolate($options['coverage']).' ';
141
        }
142
    }
143
144
    /**
145
     * Runs PHP Unit tests in a specified directory, optionally using specified config file(s).
146
     */
147
    public function execute()
148
    {
149
        if (empty($this->xmlConfigFile) && empty($this->directory)) {
150
            $this->phpci->logFailure('Neither configuration file nor test directory found.');
151
152
            return false;
153
        }
154
155
        $success = true;
156
157
        $this->phpci->logExecOutput(false);
158
159
        // Run any config files first. This can be either a single value or an array.
160
        if ($this->xmlConfigFile !== null) {
161
            $success &= $this->runConfigFile($this->xmlConfigFile);
162
        }
163
164
        // Run any dirs next. Again this can be either a single value or an array.
165
        if ($this->directory !== null) {
166
            $success &= $this->runDir($this->directory);
167
        }
168
169
        $tapString = $this->phpci->getLastOutput();
170
        $tapString = mb_convert_encoding($tapString, 'UTF-8', 'ISO-8859-1');
171
172
        try {
173
            $tapParser = new TapParser($tapString);
174
            $output = $tapParser->parse();
175
        } catch (\Exception $ex) {
176
            $this->phpci->logFailure($tapString);
177
            throw $ex;
178
        }
179
180
        $failures = $tapParser->getTotalFailures();
181
182
        $this->build->storeMeta('phpunit-errors', $failures);
183
        $this->build->storeMeta('phpunit-data', $output);
184
185
        $this->phpci->logExecOutput(true);
186
187
        return $success;
188
    }
189
190
    /**
191
     * Run the tests defined in a PHPUnit config file.
192
     *
193
     * @param $configPath
194
     *
195
     * @return bool|mixed
196
     */
197
    protected function runConfigFile($configPath)
198
    {
199
        if (is_array($configPath)) {
200
            return $this->recurseArg($configPath, array($this, 'runConfigFile'));
201
        } else {
202
            if ($this->runFrom) {
203
                $curdir = getcwd();
204
                chdir($this->phpci->buildPath.DIRECTORY_SEPARATOR.$this->runFrom);
205
            }
206
207
            $phpunit = $this->phpci->findBinary('phpunit');
208
209
            $cmd = $phpunit.' --tap %s -c "%s" '.$this->coverage.$this->path;
210
            $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath.$configPath);
211
212
            if ($this->runFrom) {
213
                chdir($curdir);
0 ignored issues
show
Bug introduced by
The variable $curdir does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
214
            }
215
216
            return $success;
217
        }
218
    }
219
220
    /**
221
     * Run the PHPUnit tests in a specific directory or array of directories.
222
     *
223
     * @param $directory
224
     *
225
     * @return bool|mixed
226
     */
227
    protected function runDir($directory)
228
    {
229
        if (is_array($directory)) {
230
            return $this->recurseArg($directory, array($this, 'runDir'));
231
        } else {
232
            $curdir = getcwd();
233
            chdir($this->phpci->buildPath);
234
235
            $phpunit = $this->phpci->findBinary('phpunit');
236
237
            $cmd = $phpunit.' --tap %s "%s"';
238
            $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath.$directory);
239
            chdir($curdir);
240
241
            return $success;
242
        }
243
    }
244
245
    /**
246
     * @param $array
247
     * @param $callable
248
     *
249
     * @return bool|mixed
250
     */
251
    protected function recurseArg($array, $callable)
252
    {
253
        $success = true;
254
        foreach ($array as $subItem) {
255
            $success &= call_user_func($callable, $subItem);
256
        }
257
258
        return $success;
259
    }
260
}
261