Completed
Push — develop ( 2faf16...be2583 )
by ANTHONIUS
15s queued 11s
created

src/Bridge/CodeCoverage/Session/Session.php (4 issues)

1
<?php
2
3
/*
4
 * This file is part of the doyo/behat-coverage-extension project.
5
 *
6
 * (c) Anthonius Munthi <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Doyo\Behat\Coverage\Bridge\CodeCoverage\Session;
15
16
use Doyo\Behat\Coverage\Bridge\CodeCoverage\Exception\SessionException;
17
use Doyo\Behat\Coverage\Bridge\CodeCoverage\Processor;
18
use Doyo\Behat\Coverage\Bridge\CodeCoverage\ProcessorInterface;
19
use Doyo\Behat\Coverage\Bridge\CodeCoverage\TestCase;
20
use SebastianBergmann\CodeCoverage\CodeCoverage;
21
use SebastianBergmann\CodeCoverage\Filter;
22
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
23
24
abstract class Session implements \Serializable, SessionInterface
25
{
26
    const CACHE_KEY = 'session';
27
28
    /**
29
     * @var string|null
30
     */
31
    protected $name;
32
33
    /**
34
     * @var TestCase
35
     */
36
    protected $testCase;
37
38
    /**
39
     * @var FilesystemAdapter|null
40
     */
41
    protected $adapter;
42
43
    /**
44
     * @var array
45
     */
46
    protected $data = [];
47
48
    /**
49
     * @var ProcessorInterface
50
     */
51
    protected $processor;
52
53
    /**
54
     * @var \Exception[]
55
     */
56
    protected $exceptions = [];
57
58
    /**
59
     * @var bool
60
     */
61
    protected $hasStarted = false;
62
63
    /**
64
     * Code coverage for this session.
65
     *
66
     * @var CodeCoverage
67
     */
68
    protected $codeCoverage;
69
70
    /**
71
     * @var array
72
     */
73
    protected $filterOptions = [];
74
75
    /**
76
     * @var array
77
     */
78
    protected $codeCoverageOptions = [];
79
80 14
    public function __construct($name)
81
    {
82 14
        $dir               = sys_get_temp_dir().'/doyo/behat-coverage-extension';
83 14
        $adapter           = new FilesystemAdapter($name, 0, $dir);
84 14
        $this->adapter     = $adapter;
85 14
        $this->name        = $name;
86 14
        $this->refresh();
87 1
    }
88
89
    /**
90
     * {@inheritdoc}
91
     */
92
    public function setCodeCoverageOptions(array $options)
93
    {
94
        $this->codeCoverageOptions = $options;
95
    }
96
97
    public function getCodeCoverageOptions()
98
    {
99
        return $this->codeCoverageOptions;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function setFilterOptions(array $options)
106
    {
107
        $this->filterOptions = $options;
108
    }
109
110
    public function getFilterOptions(): array
111
    {
112
        return $this->filterOptions;
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 4
    public function reset()
119
    {
120 4
        $this->testCase   = null;
121 4
        $this->exceptions = [];
122 4
        $this->processor->clear();
123
124 4
        $this->save();
125 1
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130 8
    public function serialize()
131
    {
132
        $data = [
133 8
            $this->testCase,
134 8
            $this->exceptions,
135 8
            $this->processor,
136
        ];
137
138 8
        return serialize($data);
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144 10
    public function unserialize($serialized)
145
    {
146
        list(
147 10
            $this->testCase,
148 10
            $this->exceptions,
149 10
            $this->processor
150 10
        ) = unserialize($serialized);
151
    }
152
153
    /**
154
     * {@inheritdoc}
155
     */
156 14
    public function refresh()
157
    {
158 14
        $adapter = $this->adapter;
159 14
        $cached  = $adapter->getItem(static::CACHE_KEY)->get();
160
161 14
        if ($cached instanceof self) {
162 4
            $this->testCase                   = $cached->getTestCase();
163 4
            $this->exceptions                 = $cached->getExceptions();
164 4
            $this->processor                  = $cached->getProcessor();
165
        }
166 1
    }
167
168
    /**
169
     * @return string|null
170
     */
171 2
    public function getName()
172
    {
173 2
        return $this->name;
174
    }
175
176
    /**
177
     * @return TestCase
178
     */
179 7
    public function getTestCase()
180
    {
181 7
        return $this->testCase;
182
    }
183
184
    /**
185
     * {@inheritdoc}
186
     */
187 8
    public function setTestCase(TestCase $testCase = null)
188
    {
189 8
        $this->testCase = $testCase;
190
191 8
        return $this;
192
    }
193
194
    /**
195
     * @return FilesystemAdapter|null
196
     */
197 1
    public function getAdapter(): FilesystemAdapter
198
    {
199 1
        return $this->adapter;
200
    }
201
202
    /**
203
     * @param FilesystemAdapter|null $adapter
204
     *
205
     * @return Cache
0 ignored issues
show
The type Doyo\Behat\Coverage\Brid...eCoverage\Session\Cache was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
206
     */
207 1
    public function setAdapter(FilesystemAdapter $adapter): self
208
    {
209 1
        $this->adapter = $adapter;
210
211 1
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Doyo\Behat\Coverage\Brid...overage\Session\Session which is incompatible with the documented return type Doyo\Behat\Coverage\Brid...eCoverage\Session\Cache.
Loading history...
212
    }
213
214
    /**
215
     * @param ProcessorInterface $processor
216
     */
217 13
    public function setProcessor(ProcessorInterface $processor = null)
218
    {
219 13
        $this->processor = $processor;
220 1
    }
221
222
    /**
223
     * @return ProcessorInterface|null
224
     */
225 4
    public function getProcessor()
226
    {
227 4
        return $this->processor;
228
    }
229
230
    /**
231
     * {@inheritdoc}
232
     */
233 2
    public function hasExceptions()
234
    {
235 2
        return \count($this->exceptions) > 0;
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241 5
    public function getExceptions()
242
    {
243 5
        return $this->exceptions;
244
    }
245
246
    /**
247
     * @param \Exception $e
248
     */
249 1
    public function addException(\Exception $e)
250
    {
251 1
        $this->exceptions[] = $e;
252
    }
253
254
    /**
255
     * @codeCoverageIgnore
256
     */
257
    public function xdebugPatch()
258
    {
259
        $options   = $this->filterOptions;
260
        $filterKey = 'whitelistedFiles';
261
262
        if (
263
            !\extension_loaded('xdebug')
264
            || !\function_exists('xdebug_set_filter')
265
            || !isset($options[$filterKey])
266
        ) {
267
            return;
268
        }
269
270
        $dirs = [];
271
        foreach ($options[$filterKey] as $fileName => $status) {
272
            $dir = \dirname($fileName);
273
            if (!\in_array($dir, $dirs, true)) {
274
                $dirs[] = $dir;
275
            }
276
        }
277
278
        xdebug_set_filter(
279
            XDEBUG_FILTER_CODE_COVERAGE,
1 ignored issue
show
The constant Doyo\Behat\Coverage\Brid...UG_FILTER_CODE_COVERAGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
280
            XDEBUG_PATH_WHITELIST,
1 ignored issue
show
The constant Doyo\Behat\Coverage\Brid...n\XDEBUG_PATH_WHITELIST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
281
            $dirs
282
        );
283
    }
284
285
    /**
286
     * {@inheritdoc}
287
     */
288 3
    final public function start($driver = null)
289
    {
290 1
        if (null === $this->testCase) {
291
            return;
292
        }
293
        try {
294 1
            $this->codeCoverage = $this->createCodeCoverage($driver);
295 1
            $this->xdebugPatch();
296 1
            $this->codeCoverage->start($this->testCase->getName());
297 3
            $this->hasStarted = true;
298
        } catch (\Exception $e) {
299
            $message = sprintf(
300
                "Can not start code coverage in namespace: %s :\n%s",
301
                $this->name,
302
                $e->getMessage()
303
            );
304
            $exception = new SessionException($message);
305
            throw $exception;
306
        }
307 1
    }
308
309 2
    public function stop()
310
    {
311
        try {
312 2
            $codeCoverage = $this->codeCoverage;
313 2
            $processor    = $this->processor;
314
315 2
            $codeCoverage->stop();
316
317 1
            $processor->merge($codeCoverage);
318
        } catch (\Exception $e) {
319
            throw new SessionException(
320
                sprintf(
321
                    'Failed to stop coverage for session %s: %s',
322
                    $this->name,
323
                    $e->getMessage()
324
                )
325
            );
326
        }
327 1
        $this->hasStarted = false;
328
    }
329
330
    /**
331
     * {@inheritdoc}
332
     */
333 8
    public function save()
334
    {
335 8
        $adapter = $this->adapter;
336 8
        $item    = $adapter->getItem(static::CACHE_KEY);
337
338 8
        $item->set($this);
339 8
        $adapter->save($item);
340 1
    }
341
342
    /**
343
     * @return Filter
344
     */
345
    protected function createCodeCoverageFilter()
346
    {
347
        $config = $this->filterOptions;
348
        $filter = new Filter();
349
        foreach ($config as $method => $value) {
350
            $method = 'set'.ucfirst($method);
351
            \call_user_func_array([$filter, $method], [$value]);
352
        }
353
354
        return $filter;
355
    }
356
357 1
    public function shutdown()
358
    {
359 1
        if ($this->hasStarted && null !== $this->processor) {
360
            try {
361 1
                $this->stop();
362
            } catch (\Exception $e) {
363
                $this->addException(new SessionException($e->getMessage()));
364
            }
365
        }
366
        $this->hasStarted   = false;
367
        $this->save();
368
    }
369
370
    /**
371
     * @param mixed|null $driver
372
     *
373
     * @return CodeCoverage
374
     */
375 1
    protected function createCodeCoverage($driver): CodeCoverage
376
    {
377 1
        $filter   = $this->processor->getCodeCoverageFilter();
378 1
        $options  = $this->processor->getCodeCoverageOptions();
379 1
        $coverage = new CodeCoverage($driver, $filter);
380 1
        foreach ($options as $method => $option) {
381
            $method = 'set'.ucfirst($method);
382
            \call_user_func_array([$coverage, $method], [$option]);
383
        }
384
385 1
        return $coverage;
386
    }
387
}
388