Passed
Push — master ( 75431b...319ac3 )
by Doug
06:12 queued 04:26
created

Extension::load()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 1
rs 10
1
<?php
2
/**
3
 * Behat Code Coverage
4
 */
5
declare(strict_types=1);
6
7
namespace DVDoug\Behat\CodeCoverage;
8
9
use function array_walk;
10
use Behat\Testwork\Cli\ServiceContainer\CliExtension;
11
use Behat\Testwork\ServiceContainer\Extension as ExtensionInterface;
12
use Behat\Testwork\ServiceContainer\ExtensionManager;
13
use Composer\InstalledVersions;
14
use Composer\Semver\VersionParser;
15
use DVDoug\Behat\CodeCoverage\Subscriber\EventSubscriber;
16
use SebastianBergmann\CodeCoverage\CodeCoverage;
17
use SebastianBergmann\CodeCoverage\Driver\Driver;
18
use SebastianBergmann\CodeCoverage\Driver\Selector;
19
use SebastianBergmann\CodeCoverage\Driver\Xdebug2NotEnabledException;
20
use SebastianBergmann\CodeCoverage\Driver\Xdebug3NotEnabledException;
21
use SebastianBergmann\CodeCoverage\Driver\XdebugNotAvailableException;
22
use SebastianBergmann\CodeCoverage\Driver\XdebugNotEnabledException;
0 ignored issues
show
Bug introduced by
The type SebastianBergmann\CodeCo...ebugNotEnabledException 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...
23
use SebastianBergmann\CodeCoverage\Filter;
24
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
25
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
26
use function sprintf;
27
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
28
use Symfony\Component\Config\FileLocator;
29
use Symfony\Component\Console\Input\InputInterface;
30
use Symfony\Component\Console\Output\OutputInterface;
31
use Symfony\Component\DependencyInjection\ContainerBuilder;
32
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
33
use Symfony\Component\DependencyInjection\Reference;
34
use function sys_get_temp_dir;
35
36
class Extension implements ExtensionInterface
37
{
38
    /**
39
     * {@inheritdoc}
40
     */
41 2
    public function initialize(ExtensionManager $extensionManager): void
42
    {
43 2
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48 3
    public function load(ContainerBuilder $container, array $config): void
49
    {
50 3
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/Resources/config'));
51
52 3
        $servicesFile = 'services.xml';
53 3
        $loader->load($servicesFile);
54
55 3
        $container->setParameter('behat.code_coverage.config.filter', $config['filter']);
56 3
        $container->setParameter('behat.code_coverage.config.branchAndPathCoverage', $config['branchAndPathCoverage']);
57 3
        $container->setParameter('behat.code_coverage.config.reports', $config['reports'] ?? []);
58 3
        $container->setParameter('behat.code_coverage.config.cache', $config['cache']);
59 2
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 6
    public function configure(ArrayNodeDefinition $builder): void
65
    {
66
        $builder
67 6
            ->children()
68 6
                ->scalarNode('cache')
69 6
                    ->defaultValue(sys_get_temp_dir() . '/behat-code-coverage-cache')
70 6
                ->end()
71 6
                ->booleanNode('branchAndPathCoverage')
0 ignored issues
show
Bug introduced by
The method booleanNode() 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

71
                ->/** @scrutinizer ignore-call */ booleanNode('branchAndPathCoverage')
Loading history...
72 6
                  ->defaultNull() // use null to mean auto
73 6
                ->end()
74 6
                ->arrayNode('filter')
75 6
                    ->addDefaultsIfNotSet()
76 6
                    ->children()
77 6
                        ->scalarNode('includeUncoveredFiles')
78 6
                            ->defaultTrue()
79 6
                        ->end()
80 6
                        ->scalarNode('processUncoveredFiles')
81 6
                            ->defaultFalse()
82 6
                            ->setDeprecated('dvdoug/behat-code-coverage', '5.3', 'the processUncoveredFiles setting is deprecated, it has been removed from php-code-coverage v10')
83 6
                        ->end()
84 6
                        ->arrayNode('include')
85 6
                            ->addDefaultsIfNotSet()
86 6
                            ->children()
87 6
                                ->arrayNode('directories')
88 6
                                   ->useAttributeAsKey('name')
89 6
                                   ->normalizeKeys(false)
90 6
                                   ->prototype('array')
91 6
                                       ->children()
92 6
                                           ->scalarNode('prefix')->defaultValue('')->end()
93 6
                                           ->scalarNode('suffix')->defaultValue('.php')->end()
94 6
                                       ->end()
95 6
                                   ->end()
96 6
                                ->end()
97 6
                                ->arrayNode('files')
98 6
                                   ->prototype('scalar')->end()
99 6
                                ->end()
100 6
                            ->end()
101 6
                        ->end()
102 6
                        ->arrayNode('exclude')
103 6
                            ->addDefaultsIfNotSet()
104 6
                            ->children()
105 6
                                ->arrayNode('directories')
106 6
                                   ->useAttributeAsKey('name')
107 6
                                   ->normalizeKeys(false)
108 6
                                   ->prototype('array')
109 6
                                       ->children()
110 6
                                           ->scalarNode('prefix')->defaultValue('')->end()
111 6
                                           ->scalarNode('suffix')->defaultValue('.php')->end()
112 6
                                       ->end()
113 6
                                   ->end()
114 6
                                ->end()
115 6
                                ->arrayNode('files')
116 6
                                   ->prototype('scalar')->end()
117 6
                                ->end()
118 6
                            ->end()
119 6
                        ->end()
120 6
                    ->end()
121 6
                ->end()
122 6
                ->arrayNode('reports')
123 6
                    ->children()
124 6
                        ->arrayNode('cobertura')
125 6
                            ->children()
126 6
                                ->scalarNode('name')->defaultNull()->end()
127 6
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
128 6
                            ->end()
129 6
                        ->end()
130 6
                        ->arrayNode('clover')
131 6
                            ->children()
132 6
                                ->scalarNode('name')->defaultNull()->end()
133 6
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
134 6
                            ->end()
135 6
                        ->end()
136 6
                        ->arrayNode('crap4j')
137 6
                            ->children()
138 6
                                ->scalarNode('name')->defaultNull()->end()
139 6
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
140 6
                            ->end()
141 6
                        ->end()
142 6
                        ->arrayNode('html')
143 6
                            ->children()
144 6
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
145 6
                                ->scalarNode('lowUpperBound')->defaultValue(50)->end()
146 6
                                ->scalarNode('highLowerBound')->defaultValue(90)->end()
147 6
                                ->arrayNode('colors')
148 6
                                    ->addDefaultsIfNotSet()
149 6
                                    ->children()
150 6
                                        ->scalarNode('successLow')->defaultValue('#dff0d8')->end()
151 6
                                        ->scalarNode('successMedium')->defaultValue('#c3e3b5')->end()
152 6
                                        ->scalarNode('successHigh')->defaultValue('#99cb84')->end()
153 6
                                        ->scalarNode('warning')->defaultValue('#fcf8e3')->end()
154 6
                                        ->scalarNode('danger')->defaultValue('#f2dede')->end()
155 6
                                    ->end()
156 6
                                ->end()
157 6
                                ->scalarNode('customCSSFile')->defaultNull()->end()
158 6
                            ->end()
159 6
                        ->end()
160 6
                        ->arrayNode('php')
161 6
                            ->children()
162 6
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
163 6
                            ->end()
164 6
                        ->end()
165 6
                        ->arrayNode('text')
166 6
                            ->children()
167 6
                                ->booleanNode('showColors')->defaultValue(false)->end()
168 6
                                ->scalarNode('lowUpperBound')->defaultValue(50)->end()
169 6
                                ->scalarNode('highLowerBound')->defaultValue(90)->end()
170 6
                                ->booleanNode('showOnlySummary')->defaultValue(false)->end()
171 6
                                ->booleanNode('showUncoveredFiles')->defaultValue(false)->end()
172 6
                            ->end()
173 6
                        ->end()
174 6
                        ->arrayNode('xml')
175 6
                            ->children()
176 6
                                ->scalarNode('target')->isRequired()->cannotBeEmpty()->end()
177 6
                            ->end()
178 6
                        ->end()
179 6
                    ->end()
180 6
                ->end()
181 6
            ->end()
182 6
        ->end();
183 4
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188 6
    public function getConfigKey()
189
    {
190 6
        return 'code_coverage';
191
    }
192
193
    /**
194
     * {@inheritdoc}
195
     */
196 12
    public function process(ContainerBuilder $container): void
197
    {
198
        /** @var InputInterface $input */
199 12
        $input = $container->get(CliExtension::INPUT_ID);
200
201
        /** @var OutputInterface $output */
202 12
        $output = $container->get(CliExtension::OUTPUT_ID);
203
204 12
        $filterConfig = $container->getParameter('behat.code_coverage.config.filter');
205 12
        $branchPathConfig = $container->getParameter('behat.code_coverage.config.branchAndPathCoverage');
206 12
        $cacheDir = $container->getParameter('behat.code_coverage.config.cache');
207
208 12
        $canCollectCodeCoverage = true;
209
        try {
210 12
            $this->initCodeCoverage(new Filter(), $filterConfig, null, $cacheDir, $output);
211
212 9
            $codeCoverageDefinition = $container->getDefinition(CodeCoverage::class);
213 9
            $filterDefinition = $container->getDefinition(Filter::class);
214 9
            $codeCoverageDefinition->setFactory([new Reference(self::class), 'initCodeCoverage']);
215 9
            $codeCoverageDefinition->setArguments([$filterDefinition, $filterConfig, $branchPathConfig, $cacheDir, $output]);
216 3
        } catch (NoCodeCoverageDriverAvailableException|Xdebug2NotEnabledException|Xdebug3NotEnabledException|XdebugNotEnabledException|XdebugNotAvailableException $e) {
217 3
            $output->writeln("<comment>No code coverage driver is available. {$e->getMessage()}</comment>");
218 3
            $canCollectCodeCoverage = false;
219
        }
220
221 12
        if (!$canCollectCodeCoverage || $input->hasParameterOption('--no-coverage')) {
222 6
            $container->getDefinition(EventSubscriber::class)->setArgument('$coverage', null);
223
        }
224 8
    }
225
226 9
    public function initCodeCoverage(Filter $filter, array $filterConfig, ?bool $branchPathConfig, string $cacheDir, OutputInterface $output): CodeCoverage
227
    {
228
        // set up filter
229 9
        array_walk($filterConfig['include']['directories'], static function (array $dir, string $path, Filter $filter): void {
230 9
            $filter->includeDirectory($path, $dir['suffix'], $dir['prefix']);
231 6
        }, $filter);
232
233 9
        array_walk($filterConfig['include']['files'], static function (string $file, string $key, Filter $filter): void {
234 9
            $filter->includeFile($file);
235 6
        }, $filter);
236
237 9
        array_walk($filterConfig['exclude']['directories'], static function (array $dir, string $path, Filter $filter): void {
238 9
            $filter->excludeDirectory($path, $dir['suffix'], $dir['prefix']);
239 6
        }, $filter);
240
241 9
        array_walk($filterConfig['exclude']['files'], static function (string $file, string $key, Filter $filter): void {
242 9
            $filter->excludeFile($file);
243 6
        }, $filter);
244
245
        // see if we can get a driver
246 9
        $selector = new Selector();
247 9
        $driver = $selector->forLineCoverage($filter);
248 9
        if ($branchPathConfig !== false) {
249
            try {
250 9
                $driver = $selector->forLineAndPathCoverage($filter);
251 3
            } catch (NoCodeCoverageDriverWithPathCoverageSupportAvailableException $e) {
252
                // fallback driver is already set
253 3
                if ($branchPathConfig === true) { // only warn if explicitly enabled
0 ignored issues
show
introduced by
The condition $branchPathConfig === true is always true.
Loading history...
254 1
                    $output->writeln(sprintf('<info>%s does not support collecting branch and path data</info>', $driver->nameAndVersion()));
255
                }
256
            }
257
        }
258
259
        // and init coverage
260 9
        $codeCoverage = new CodeCoverage($driver, $filter);
261 9
        $codeCoverage->cacheStaticAnalysis($cacheDir);
262
263 9
        if ($filterConfig['includeUncoveredFiles']) {
264 3
            $codeCoverage->includeUncoveredFiles();
265
        } else {
266 6
            $codeCoverage->excludeUncoveredFiles();
267
        }
268
269 9
        if (InstalledVersions::satisfies(new VersionParser(), 'phpunit/php-code-coverage', '^9.0')) {
270 9
            if ($filterConfig['processUncoveredFiles']) {
271 3
                $codeCoverage->processUncoveredFiles();
272
            } else {
273 6
                $codeCoverage->doNotProcessUncoveredFiles();
274
            }
275
        }
276
277 9
        return $codeCoverage;
278
    }
279
}
280