Passed
Pull Request — master (#22)
by
unknown
14:48 queued 12:08
created

Extension::initialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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

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

269
                $codeCoverage->/** @scrutinizer ignore-call */ 
270
                               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...
270
            } else {
271 44
                $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

271
                $codeCoverage->/** @scrutinizer ignore-call */ 
272
                               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...
272
            }
273
        }
274
275 105
        return $codeCoverage;
276
    }
277
}
278