Completed
Push — master ( dab6cf...e9b641 )
by Stéphane
13s queued 11s
created

CodeCoverageListener   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Test Coverage

Coverage 8.93%

Importance

Changes 0
Metric Value
eloc 61
dl 0
loc 157
ccs 5
cts 56
cp 0.0893
rs 10
c 0
b 0
f 0
wmc 24

7 Methods

Rating   Name   Duplication   Size   Complexity  
A setOptions() 0 3 1
A __construct() 0 15 1
A getSubscribedEvents() 0 7 1
A beforeExample() 0 20 3
A afterExample() 0 7 2
B afterSuite() 0 25 10
A beforeSuite() 0 22 6
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of the friends-of-phpspec/phpspec-code-coverage package.
7
 *
8
 * @author  ek9 <[email protected]>
9
 * @license MIT
10
 *
11
 * For the full copyright and license information, please see the LICENSE file
12
 * that was distributed with this source code.
13
 */
14
15
namespace FriendsOfPhpSpec\PhpSpec\CodeCoverage\Listener;
16
17
use PhpSpec\Console\ConsoleIO;
18
use PhpSpec\Event\ExampleEvent;
19
use PhpSpec\Event\SuiteEvent;
20
use SebastianBergmann\CodeCoverage\CodeCoverage;
21
use SebastianBergmann\CodeCoverage\Report;
22
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
23
24
/**
25
 * @author Henrik Bjornskov
26
 */
27
class CodeCoverageListener implements EventSubscriberInterface
28
{
29
    private $coverage;
30
31
    private $io;
32
33
    private $options;
34
35
    private $reports;
36
37
    /**
38
     * @var bool
39
     */
40
    private $skipCoverage;
41
42
    /**
43
     * @param ConsoleIO    $io
44
     * @param CodeCoverage $coverage
45
     * @param array        $reports
46
     * @param bool      $skipCoverage
47
     */
48
    public function __construct(ConsoleIO $io, CodeCoverage $coverage, array $reports, $skipCoverage = false)
49
    {
50
        $this->io = $io;
51
        $this->coverage = $coverage;
52
        $this->reports = $reports;
53
        $this->options = [
54
            'whitelist' => ['src', 'lib'],
55
            'blacklist' => ['test', 'vendor', 'spec'],
56
            'whitelist_files' => [],
57
            'blacklist_files' => [],
58
            'output' => ['html' => 'coverage'],
59
            'format' => ['html'],
60
        ];
61
62
        $this->skipCoverage = $skipCoverage;
63
    }
64
65
    /**
66
     * @param ExampleEvent $event
67
     */
68 5
    public function afterExample(ExampleEvent $event): void
69
    {
70 5
        if ($this->skipCoverage) {
71
            return;
72
        }
73
74 5
        $this->coverage->stop();
75
    }
76
77
    /**
78
     * @param SuiteEvent $event
79
     */
80
    public function afterSuite(SuiteEvent $event): void
81
    {
82
        if ($this->skipCoverage) {
83
            if ($this->io && $this->io->isVerbose()) {
84
                $this->io->writeln('Skipping code coverage generation');
85
            }
86
87
            return;
88
        }
89
90
        if ($this->io && $this->io->isVerbose()) {
91
            $this->io->writeln();
92
        }
93
94
        foreach ($this->reports as $format => $report) {
95
            if ($this->io && $this->io->isVerbose()) {
96
                $this->io->writeln(\sprintf('Generating code coverage report in %s format ...', $format));
97
            }
98
99
            if ($report instanceof Report\Text) {
100
                $this->io->writeln(
101
                    $report->process($this->coverage, $this->io->isDecorated())
102
                );
103
            } else {
104
                $report->process($this->coverage, $this->options['output'][$format]);
105
            }
106
        }
107
    }
108
109
    /**
110
     * @param ExampleEvent $event
111
     */
112 5
    public function beforeExample(ExampleEvent $event): void
113
    {
114
        if ($this->skipCoverage) {
115
            return;
116
        }
117
118
        $example = $event->getExample();
119
120
        $name = null;
121
122
        if (null !== $spec = $example->getSpecification()) {
123
            $name = $spec->getClassReflection()->getName();
124
        }
125
126
        $name = \strtr('%spec%::%example%', [
127
            '%spec%' => $name,
128
            '%example%' => $example->getFunctionReflection()->getName(),
129
        ]);
130
131
        $this->coverage->start($name);
132 5
    }
133
134
    /**
135
     * Note: We use array_map() instead of array_walk() because the latter expects
136
     * the callback to take the value as the first and the index as the seconds parameter.
137
     *
138
     * @param SuiteEvent $event
139
     */
140
    public function beforeSuite(SuiteEvent $event): void
141
    {
142
        if ($this->skipCoverage) {
143
            return;
144
        }
145
146
        $filter = $this->coverage->filter();
147
148
        foreach ($this->options['whitelist'] as $option) {
149
            $filter->addDirectoryToWhitelist($option);
150
        }
151
152
        foreach ($this->options['blacklist'] as $option) {
153
            $filter->removeDirectoryFromWhitelist($option);
154
        }
155
156
        foreach ($this->options['whitelist_files'] as $option) {
157
            $filter->addFilesToWhitelist($option);
158
        }
159
160
        foreach ($this->options['blacklist_files'] as $option) {
161
            $filter->removeFileFromWhitelist($option);
162
        }
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168
    public static function getSubscribedEvents(): array
169
    {
170
        return [
171
            'beforeExample' => ['beforeExample', -10],
172
            'afterExample' => ['afterExample', -10],
173
            'beforeSuite' => ['beforeSuite', -10],
174
            'afterSuite' => ['afterSuite', -10],
175
        ];
176
    }
177
178
    /**
179
     * @param array $options
180
     */
181
    public function setOptions(array $options): void
182
    {
183
        $this->options = $options + $this->options;
184
    }
185
}
186