Passed
Push — php8_build ( 83c998...34cfcd )
by Doug
05:13
created

Extension::initCodeCoverage()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.3149

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 4
dl 0
loc 8
ccs 4
cts 7
cp 0.5714
crap 2.3149
rs 10
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * Code Coverage Extension for Behat.
6
 *
7
 * @copyright 2013 Anthon Pang
8
 *
9
 * @license BSD-2-Clause
10
 */
11
12
namespace DVDoug\Behat\CodeCoverage;
13
14
use Behat\Testwork\ServiceContainer\Extension as ExtensionInterface;
15
use Behat\Testwork\ServiceContainer\ExtensionManager;
16
use DVDoug\Behat\CodeCoverage\Subscriber\EventSubscriber;
17
use SebastianBergmann\CodeCoverage\CodeCoverage;
18
use SebastianBergmann\CodeCoverage\Driver\Driver;
19
use SebastianBergmann\CodeCoverage\Filter;
20
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
0 ignored issues
show
Bug introduced by
The type SebastianBergmann\CodeCo...riverAvailableException 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...
21
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
0 ignored issues
show
Bug introduced by
The type SebastianBergmann\CodeCo...pportAvailableException 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...
22
use SebastianBergmann\CodeCoverage\RuntimeException;
23
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
24
use Symfony\Component\Config\FileLocator;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Output\OutputInterface;
27
use Symfony\Component\DependencyInjection\ContainerBuilder;
28
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
29
use Symfony\Component\DependencyInjection\Reference;
30
31
/**
32
 * Code coverage extension.
33
 *
34
 * @author Anthon Pang <[email protected]>
35
 */
36
class Extension implements ExtensionInterface
37
{
38
    /**
39
     * {@inheritdoc}
40
     */
41 26
    public function initialize(ExtensionManager $extensionManager): void
42
    {
43 26
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 26
    public function load(ContainerBuilder $container, array $config): void
49
    {
50 26
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/Resources/config'));
51
52 26
        $servicesFile = 'services.xml';
53 26
        $loader->load($servicesFile);
54
55 26
        $container->setParameter('behat.code_coverage.config.filter', $config['filter']);
56 26
        $container->setParameter('behat.code_coverage.config.branchAndPathCoverage', $config['branchAndPathCoverage']);
57 26
        $container->setParameter('behat.code_coverage.config.reports', $config['reports'] ?? []);
58 26
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 52
    public function configure(ArrayNodeDefinition $builder): void
64
    {
65
        $builder
66 52
            ->children()
67 52
                ->booleanNode('branchAndPathCoverage')
68 52
                  ->defaultNull() // use null to mean auto
69 52
                ->end()
70 52
                ->arrayNode('filter')
0 ignored issues
show
Bug introduced by
The method arrayNode() does not exist on Symfony\Component\Config...der\NodeParentInterface. It seems like you code against a sub-type of Symfony\Component\Config...der\NodeParentInterface such as Symfony\Component\Config...ion\Builder\NodeBuilder. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

70
                ->/** @scrutinizer ignore-call */ arrayNode('filter')
Loading history...
71 52
                    ->addDefaultsIfNotSet()
72 52
                    ->children()
73 52
                        ->scalarNode('includeUncoveredFiles')
74 52
                            ->defaultTrue()
75 52
                        ->end()
76 52
                        ->scalarNode('processUncoveredFiles')
77 52
                            ->defaultFalse()
78 52
                        ->end()
79 52
                        ->arrayNode('include')
80 52
                            ->addDefaultsIfNotSet()
81 52
                            ->children()
82 52
                                ->arrayNode('directories')
83 52
                                   ->useAttributeAsKey('name')
84 52
                                   ->normalizeKeys(false)
85 52
                                   ->prototype('array')
86 52
                                       ->children()
87 52
                                           ->scalarNode('prefix')->defaultValue('')->end()
88 52
                                           ->scalarNode('suffix')->defaultValue('.php')->end()
89 52
                                       ->end()
90 52
                                   ->end()
91 52
                                ->end()
92 52
                                ->arrayNode('files')
93 52
                                   ->prototype('scalar')->end()
94 52
                                ->end()
95 52
                            ->end()
96 52
                        ->end()
97 52
                        ->arrayNode('exclude')
98 52
                            ->addDefaultsIfNotSet()
99 52
                            ->children()
100 52
                                ->arrayNode('directories')
101 52
                                   ->useAttributeAsKey('name')
102 52
                                   ->normalizeKeys(false)
103 52
                                   ->prototype('array')
104 52
                                       ->children()
105 52
                                           ->scalarNode('prefix')->defaultValue('')->end()
106 52
                                           ->scalarNode('suffix')->defaultValue('.php')->end()
107 52
                                       ->end()
108 52
                                   ->end()
109 52
                                ->end()
110 52
                                ->arrayNode('files')
111 52
                                   ->prototype('scalar')->end()
112 52
                                ->end()
113 52
                            ->end()
114 52
                        ->end()
115 52
                    ->end()
116 52
                ->end()
117 52
                ->arrayNode('reports')
118 52
                    ->children()
119 52
                        ->arrayNode('clover')
120 52
                            ->children()
121 52
                                ->scalarNode('name')->defaultNull()->end()
122 52
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
123 52
                            ->end()
124 52
                        ->end()
125 52
                        ->arrayNode('crap4j')
126 52
                            ->children()
127 52
                                ->scalarNode('name')->defaultNull()->end()
128 52
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
129 52
                            ->end()
130 52
                        ->end()
131 52
                        ->arrayNode('html')
132 52
                            ->children()
133 52
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
134 52
                                ->scalarNode('lowUpperBound')->defaultValue(50)->end()
135 52
                                ->scalarNode('highLowerBound')->defaultValue(90)->end()
136 52
                            ->end()
137 52
                        ->end()
138 52
                        ->arrayNode('php')
139 52
                            ->children()
140 52
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
141 52
                            ->end()
142 52
                        ->end()
143 52
                        ->arrayNode('text')
144 52
                            ->children()
145 52
                                ->booleanNode('showColors')->defaultValue(false)->end()
146 52
                                ->scalarNode('lowUpperBound')->defaultValue(50)->end()
147 52
                                ->scalarNode('highLowerBound')->defaultValue(90)->end()
148 52
                                ->booleanNode('showOnlySummary')->defaultValue(false)->end()
149 52
                                ->booleanNode('showUncoveredFiles')->defaultValue(false)->end()
150 52
                            ->end()
151 52
                        ->end()
152 52
                        ->arrayNode('xml')
153 52
                            ->children()
154 52
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
155 52
                            ->end()
156 52
                        ->end()
157 52
                    ->end()
158 52
                ->end()
159 52
            ->end()
160 52
        ->end();
161 52
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166 52
    public function getConfigKey()
167
    {
168 52
        return 'code_coverage';
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174 104
    public function process(ContainerBuilder $container): void
175
    {
176
        /** @var InputInterface $input */
177 104
        $input = $container->get('cli.input');
178
179
        /** @var OutputInterface $output */
180 104
        $output = $container->get('cli.output');
181
182 104
        $filterConfig = $container->getParameter('behat.code_coverage.config.filter');
183 104
        $branchPathConfig = $container->getParameter('behat.code_coverage.config.branchAndPathCoverage');
184
185 104
        $canCollectCodeCoverage = true;
186
        try {
187 104
            $this->initCodeCoverage(new Filter(), $filterConfig, null, $output);
188
189 78
            $codeCoverageDefinition = $container->getDefinition(CodeCoverage::class);
190 78
            $filterDefinition = $container->getDefinition(Filter::class);
191 78
            $codeCoverageDefinition->setFactory([new Reference(self::class), 'initCodeCoverage']);
192 78
            $codeCoverageDefinition->setArguments([$filterDefinition, $filterConfig, $branchPathConfig, $output]);
193 26
        } catch (NoCodeCoverageDriverAvailableException | RuntimeException $e) {
194 26
            $output->writeln('<comment>No code coverage driver is available</comment>');
195 26
            $canCollectCodeCoverage = false;
196
        }
197
198 104
        if (!$canCollectCodeCoverage || $input->hasParameterOption('--no-coverage')) {
199 52
            $container->getDefinition(EventSubscriber::class)->setArgument('$coverage', null);
200
        }
201 104
    }
202
203 78
    public function initCodeCoverage(Filter $filter, array $filterConfig, ?bool $branchPathConfig, OutputInterface $output): CodeCoverage
204
    {
205 78
        $driverClassReflection = new \ReflectionClass(Driver::class);
206 78
        if ($driverClassReflection->isInterface()) {
207 78
            return $this->initCodeCoverageV678($filter, $filterConfig, $branchPathConfig, $output);
208
        }
209
210
        return $this->initCodeCoverageV9($filter, $filterConfig, $branchPathConfig, $output);
211
    }
212
213
    public function initCodeCoverageV9(Filter $filter, array $filterConfig, ?bool $branchPathConfig, OutputInterface $output): CodeCoverage
214
    {
215
        // set up filter
216
        array_walk($filterConfig['include']['directories'], static function (array $dir, string $path, Filter $filter): void {
217
            $filter->includeDirectory($path, $dir['suffix'], $dir['prefix']);
0 ignored issues
show
Bug introduced by
The method includeDirectory() does not exist on SebastianBergmann\CodeCoverage\Filter. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

217
            $filter->/** @scrutinizer ignore-call */ 
218
                     includeDirectory($path, $dir['suffix'], $dir['prefix']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
218
        }, $filter);
219
220
        array_walk($filterConfig['include']['files'], static function (string $file, string $key, Filter $filter): void {
221
            $filter->includeFile($file);
0 ignored issues
show
Bug introduced by
The method includeFile() does not exist on SebastianBergmann\CodeCoverage\Filter. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

221
            $filter->/** @scrutinizer ignore-call */ 
222
                     includeFile($file);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
222
        }, $filter);
223
224
        array_walk($filterConfig['exclude']['directories'], static function (array $dir, string $path, Filter $filter): void {
225
            $filter->excludeDirectory($path, $dir['suffix'], $dir['prefix']);
0 ignored issues
show
Bug introduced by
The method excludeDirectory() does not exist on SebastianBergmann\CodeCoverage\Filter. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

225
            $filter->/** @scrutinizer ignore-call */ 
226
                     excludeDirectory($path, $dir['suffix'], $dir['prefix']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
226
        }, $filter);
227
228
        array_walk($filterConfig['exclude']['files'], static function (string $file, string $key, Filter $filter): void {
229
            $filter->excludeFile($file);
0 ignored issues
show
Bug introduced by
The method excludeFile() does not exist on SebastianBergmann\CodeCoverage\Filter. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

229
            $filter->/** @scrutinizer ignore-call */ 
230
                     excludeFile($file);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
230
        }, $filter);
231
232
        // see if we can get a driver
233
        $driver = Driver::forLineCoverage($filter);
0 ignored issues
show
Bug introduced by
The method forLineCoverage() does not exist on SebastianBergmann\CodeCoverage\Driver\Driver. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

233
        /** @scrutinizer ignore-call */ 
234
        $driver = Driver::forLineCoverage($filter);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
234
        if ($branchPathConfig !== false) {
235
            try {
236
                $driver = Driver::forLineAndPathCoverage($filter);
0 ignored issues
show
Bug introduced by
The method forLineAndPathCoverage() does not exist on SebastianBergmann\CodeCoverage\Driver\Driver. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

236
                /** @scrutinizer ignore-call */ 
237
                $driver = Driver::forLineAndPathCoverage($filter);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
237
            } catch (NoCodeCoverageDriverWithPathCoverageSupportAvailableException $e) {
238
                // fallback driver is already set
239
                if ($branchPathConfig === true) { //only warn if explicitly enabled
0 ignored issues
show
introduced by
The condition $branchPathConfig === true is always true.
Loading history...
240
                    $output->writeln('<info>Your coverage driver does not support collecting branch and path data</info>');
241
                }
242
            }
243
        }
244
245
        // and init coverage
246
        $codeCoverage = new CodeCoverage($driver, $filter);
247
248
        if ($filterConfig['includeUncoveredFiles']) {
249
            $codeCoverage->includeUncoveredFiles();
0 ignored issues
show
Bug introduced by
The method includeUncoveredFiles() does not exist on SebastianBergmann\CodeCoverage\CodeCoverage. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

249
            $codeCoverage->/** @scrutinizer ignore-call */ 
250
                           includeUncoveredFiles();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
250
        } else {
251
            $codeCoverage->excludeUncoveredFiles();
0 ignored issues
show
Bug introduced by
The method excludeUncoveredFiles() does not exist on SebastianBergmann\CodeCoverage\CodeCoverage. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

251
            $codeCoverage->/** @scrutinizer ignore-call */ 
252
                           excludeUncoveredFiles();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
252
        }
253
254
        if ($filterConfig['processUncoveredFiles']) {
255
            $codeCoverage->processUncoveredFiles();
0 ignored issues
show
Bug introduced by
The method processUncoveredFiles() does not exist on SebastianBergmann\CodeCoverage\CodeCoverage. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

255
            $codeCoverage->/** @scrutinizer ignore-call */ 
256
                           processUncoveredFiles();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
256
        } else {
257
            $codeCoverage->doNotProcessUncoveredFiles();
0 ignored issues
show
Bug introduced by
The method doNotProcessUncoveredFiles() does not exist on SebastianBergmann\CodeCoverage\CodeCoverage. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

257
            $codeCoverage->/** @scrutinizer ignore-call */ 
258
                           doNotProcessUncoveredFiles();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
258
        }
259
260
        return $codeCoverage;
261
    }
262
263 78
    public function initCodeCoverageV678(Filter $filter, array $config, ?bool $branchPathConfig, OutputInterface $output): CodeCoverage
264
    {
265 78
        if ($branchPathConfig === true) { //only warn if explicitly enabled
266 26
            $output->writeln('<info>php-code-coverage v9+ is needed to support collecting branch and path data</info>');
267
        }
268
269
        // set up filter
270 18
        array_walk($config['include']['directories'], static function (array $dir, string $path, Filter $filter): void {
271 78
            $filter->addDirectoryToWhitelist($path, $dir['suffix'], $dir['prefix']);
272 78
        }, $filter);
273
274 18
        array_walk($config['include']['files'], static function (string $file, string $key, Filter $filter): void {
275 78
            $filter->addFileToWhitelist($file);
276 78
        }, $filter);
277
278 18
        array_walk($config['exclude']['directories'], static function (array $dir, string $path, Filter $filter): void {
279 78
            $filter->removeDirectoryFromWhitelist($path, $dir['suffix'], $dir['prefix']);
280 78
        }, $filter);
281
282 18
        array_walk($config['exclude']['files'], static function (string $file, string $key, Filter $filter): void {
283 78
            $filter->removeFileFromWhitelist($file);
284 78
        }, $filter);
285
286
        // and init coverage
287 78
        $codeCoverage = new CodeCoverage(null, $filter);
288
289 78
        $codeCoverage->setAddUncoveredFilesFromWhitelist($config['includeUncoveredFiles']);
290 78
        $codeCoverage->setProcessUncoveredFilesFromWhitelist($config['processUncoveredFiles']);
291
292 78
        return $codeCoverage;
293
    }
294
}
295