Completed
Push — master ( e546fc...9f6d8c )
by Julian
11s
created

Options::__construct()   C

Complexity

Conditions 8
Paths 64

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 23
cts 23
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 22
nc 64
nop 1
crap 8
1
<?php
2
3
namespace ParaTest\Runners\PHPUnit;
4
5
/**
6
 * Class Options.
7
 *
8
 * An object containing all configurable information used
9
 * to run PHPUnit via ParaTest
10
 */
11
class Options
12
{
13
    /**
14
     * The number of processes to run at a time.
15
     *
16
     * @var int
17
     */
18
    protected $processes;
19
20
    /**
21
     * The test path pointing to tests that will
22
     * be run.
23
     *
24
     * @var string
25
     */
26
    protected $path;
27
28
    /**
29
     * The path to the PHPUnit binary that will be run.
30
     *
31
     * @var string
32
     */
33
    protected $phpunit;
34
35
    /**
36
     * Determines whether or not ParaTest runs in
37
     * functional mode. If enabled, ParaTest will run
38
     * every test method in a separate process.
39
     *
40
     * @var string
41
     */
42
    protected $functional;
43
44
    /**
45
     * Prevents starting new tests after a test has failed.
46
     *
47
     * @var bool
48
     */
49
    protected $stopOnFailure;
50
51
    /**
52
     * A collection of post-processed option values. This is the collection
53
     * containing ParaTest specific options.
54
     *
55
     * @var array
56
     */
57
    protected $filtered;
58
59
    /**
60
     * @var string
61
     */
62
    protected $runner;
63
64
    /**
65
     * @var bool
66
     */
67
    protected $noTestTokens;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $colors;
73
74
    /**
75
     * Filters which tests to run.
76
     *
77
     * @var string|null
78
     */
79
    protected $testsuite;
80
81
    /**
82
     * @var int|null
83
     */
84
    protected $maxBatchSize;
85
86
    /**
87
     * @var string
88
     */
89
    protected $filter;
90
91
    /**
92
     * @var string[]
93
     */
94
    protected $groups;
95
96
    /**
97
     * @var string[]
98
     */
99
    protected $excludeGroups;
100
101
    /**
102
     * A collection of option values directly corresponding
103
     * to certain annotations - i.e group.
104
     *
105
     * @var array
106
     */
107
    protected $annotations = [];
108
109 42
    public function __construct($opts = [])
110
    {
111 42
        foreach (self::defaults() as $opt => $value) {
112 42
            $opts[$opt] = $opts[$opt] ?? $value;
113
        }
114
115 42
        $this->processes = $opts['processes'];
116 42
        $this->path = $opts['path'];
117 42
        $this->phpunit = $opts['phpunit'];
118 42
        $this->functional = $opts['functional'];
119 42
        $this->stopOnFailure = $opts['stop-on-failure'];
120 42
        $this->runner = $opts['runner'];
121 42
        $this->noTestTokens = $opts['no-test-tokens'];
122 42
        $this->colors = $opts['colors'];
123 42
        $this->testsuite = $opts['testsuite'];
124 42
        $this->maxBatchSize = $opts['max-batch-size'];
125 42
        $this->filter = $opts['filter'];
126
127
        // we need to register that options if they are blank but do not get them as
128
        // key with null value in $this->filtered as it will create problems for
129
        // phpunit command line generation (it will add them in command line with no value
130
        // and it's wrong because group and exclude-group options require value when passed
131
        // to phpunit)
132 42
        $this->groups = isset($opts['group']) && $opts['group'] !== ''
133 14
                      ? explode(',', $opts['group'])
134 30
                      : [];
135 42
        $this->excludeGroups = isset($opts['exclude-group']) && $opts['exclude-group'] !== ''
136
                             ? explode(',', $opts['exclude-group'])
137 42
                             : [];
138
139 42
        if (strlen($opts['filter']) > 0 && !$this->functional) {
140
            throw new \RuntimeException('Option --filter is not implemented for non functional mode');
141
        }
142
143 42
        $this->filtered = $this->filterOptions($opts);
144 42
        $this->initAnnotations();
145 42
    }
146
147
    /**
148
     * Public read accessibility.
149
     *
150
     * @param string $var
151
     *
152
     * @return mixed
153
     */
154 40
    public function __get($var)
155
    {
156 40
        return $this->$var;
157
    }
158
159
    /**
160
     * Public read accessibility
161
     * (e.g. to make empty($options->property) work as expected).
162
     *
163
     * @param string $var
164
     *
165
     * @return mixed
166
     */
167 23
    public function __isset($var)
168
    {
169 23
        return isset($this->$var);
170
    }
171
172
    /**
173
     * Returns a collection of ParaTest's default
174
     * option values.
175
     *
176
     * @return array
177
     */
178 42
    protected static function defaults()
179
    {
180
        return [
181 42
            'processes' => 5,
182 42
            'path' => '',
183 42
            'phpunit' => static::phpunit(),
184
            'functional' => false,
185
            'stop-on-failure' => false,
186 42
            'runner' => 'Runner',
187
            'no-test-tokens' => false,
188
            'colors' => false,
189 42
            'testsuite' => '',
190 42
            'max-batch-size' => 0,
191
            'filter' => null,
192
        ];
193
    }
194
195
    /**
196
     * Get the path to phpunit
197
     * First checks if a Windows batch script is in the composer vendors directory.
198
     * Composer automatically handles creating a .bat file, so if on windows this should be the case.
199
     * Second look for the phpunit binary under nix
200
     * Defaults to phpunit on the users PATH.
201
     *
202
     * @return string $phpunit the path to phpunit
203
     */
204 42
    protected static function phpunit()
205
    {
206 42
        $vendor = static::vendorDir();
207 42
        $phpunit = $vendor . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . 'phpunit';
208 42
        $batch = $phpunit . '.bat';
209
210 42
        if (file_exists($batch)) {
211
            return $batch;
212
        }
213
214 42
        if (file_exists($phpunit)) {
215 42
            return $phpunit;
216
        }
217
218
        return 'phpunit';
219
    }
220
221
    /**
222
     * Get the path to the vendor directory
223
     * First assumes vendor directory is accessible from src (i.e development)
224
     * Second assumes vendor directory is accessible within src.
225
     */
226 42
    protected static function vendorDir()
227
    {
228 42
        $vendor = dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR . 'vendor';
229 42
        if (!file_exists($vendor)) {
230
            $vendor = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
231
        }
232
233 42
        return $vendor;
234
    }
235
236
    /**
237
     * Filter options to distinguish between paratest
238
     * internal options and any other options.
239
     */
240 42
    protected function filterOptions(array $options): array
241
    {
242 42
        $filtered = array_diff_key($options, [
243 42
            'processes' => $this->processes,
244 42
            'path' => $this->path,
245 42
            'phpunit' => $this->phpunit,
246 42
            'functional' => $this->functional,
247 42
            'stop-on-failure' => $this->stopOnFailure,
248 42
            'runner' => $this->runner,
249 42
            'no-test-tokens' => $this->noTestTokens,
250 42
            'colors' => $this->colors,
251 42
            'testsuite' => $this->testsuite,
252 42
            'max-batch-size' => $this->maxBatchSize,
253 42
            'filter' => $this->filter,
254
        ]);
255 42
        if ($configuration = $this->getConfigurationPath($filtered)) {
256 42
            $filtered['configuration'] = new Configuration($configuration);
257
        }
258
259 42
        return $filtered;
260
    }
261
262
    /**
263
     * Take an array of filtered options and return a
264
     * configuration path.
265
     *
266
     * @param $filtered
267
     *
268
     * @return string|null
269
     */
270 42
    protected function getConfigurationPath($filtered)
271
    {
272 42
        if (isset($filtered['configuration'])) {
273 18
            return $this->getDefaultConfigurationForPath($filtered['configuration'], $filtered['configuration']);
274
        }
275
276 28
        return $this->getDefaultConfigurationForPath();
277
    }
278
279
    /**
280
     * Retrieve the default configuration given a path (directory or file).
281
     * This will search into the directory, if a directory is specified.
282
     *
283
     * @param string $path    The path to search into
284
     * @param string $default The default value to give back
285
     *
286
     * @return string|null
287
     */
288 42
    private function getDefaultConfigurationForPath($path = '.', $default = null)
289
    {
290 42
        if ($this->isFile($path)) {
291 15
            return realpath($path);
292
        }
293
294 28
        $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
295 28
        $suffixes = ['phpunit.xml', 'phpunit.xml.dist'];
296
297 28
        foreach ($suffixes as $suffix) {
298 28
            if ($this->isFile($path . $suffix)) {
299 28
                return realpath($path . $suffix);
300
            }
301
        }
302
303 4
        return $default;
304
    }
305
306
    /**
307
     * Load options that are represented by annotations
308
     * inside of tests i.e @group group1 = --group group1.
309
     */
310 42
    protected function initAnnotations()
311
    {
312 42
        $annotatedOptions = ['group'];
313 42
        foreach ($this->filtered as $key => $value) {
314 42
            if (array_search($key, $annotatedOptions, true) !== false) {
315 42
                $this->annotations[$key] = $value;
316
            }
317
        }
318 42
    }
319
320
    /**
321
     * @param $file
322
     *
323
     * @return bool
324
     */
325 42
    private function isFile($file)
326
    {
327 42
        return file_exists($file) && !is_dir($file);
328
    }
329
}
330