Passed
Pull Request — master (#13)
by
unknown
12:40 queued 10:23
created

Extension::getConfigKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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