Issues (4)

src/Extension.php (2 issues)

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 DVDoug\Behat\CodeCoverage\Subscriber\EventSubscriber;
15
use SebastianBergmann\CodeCoverage\CodeCoverage;
16
use SebastianBergmann\CodeCoverage\Driver\Selector;
17
use SebastianBergmann\CodeCoverage\Driver\XdebugNotAvailableException;
18
use SebastianBergmann\CodeCoverage\Driver\XdebugNotEnabledException;
19
use SebastianBergmann\CodeCoverage\Filter;
20
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverAvailableException;
21
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
22
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
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\ConsoleOutputInterface;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\DependencyInjection\ContainerBuilder;
29
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
30
use Symfony\Component\DependencyInjection\Reference;
31
32
use function sprintf;
33
use function sys_get_temp_dir;
34
use function realpath;
35
36
class Extension implements ExtensionInterface
37
{
38 37
    public function initialize(ExtensionManager $extensionManager): void
39
    {
40 37
    }
41
42 37
    public function load(ContainerBuilder $container, array $config): void
43
    {
44 37
        $container->registerForAutoconfiguration(Controller::class)->addTag(CliExtension::CONTROLLER_TAG);
45 37
        $container->registerForAutoconfiguration(EventSubscriber::class)->addTag(EventDispatcherExtension::SUBSCRIBER_TAG);
46
47 37
        $loader = new PhpFileLoader($container, new FileLocator(__DIR__ . '/../config'));
48 37
        $loader->load('services.php');
49
50 37
        $container->setParameter('behat.code_coverage.config.filter', $config['filter']);
51 37
        $container->setParameter('behat.code_coverage.config.branchAndPathCoverage', $config['branchAndPathCoverage']);
52 37
        $container->setParameter('behat.code_coverage.config.reports', $config['reports'] ?? []);
53 37
        $container->setParameter('behat.code_coverage.config.cache', $config['cache']);
54
    }
55
56 74
    public function configure(ArrayNodeDefinition $builder): void
57
    {
58 74
        $builder
59 74
            ->children()
60 74
                ->scalarNode('cache')
61 74
                    ->defaultValue(sys_get_temp_dir() . '/behat-code-coverage-cache')
62 74
                ->end()
63 74
                ->booleanNode('branchAndPathCoverage')
0 ignored issues
show
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

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