Passed
Push — master ( 5c79ff...2cf866 )
by Doug
08:50
created

Extension::initialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 0
dl 0
loc 2
ccs 0
cts 1
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
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\Driver\RemoteXdebug;
17
use DVDoug\Behat\CodeCoverage\Listener\EventListener;
18
use SebastianBergmann\CodeCoverage\CodeCoverage;
19
use SebastianBergmann\CodeCoverage\Driver\Driver;
20
use SebastianBergmann\CodeCoverage\Filter;
21
use SebastianBergmann\CodeCoverage\NoCodeCoverageDriverWithPathCoverageSupportAvailableException;
22
use SebastianBergmann\Environment\Runtime;
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
30
/**
31
 * Code coverage extension.
32
 *
33
 * @author Anthon Pang <[email protected]>
34
 */
35
class Extension implements ExtensionInterface
36
{
37
    /**
38
     * @var string
39
     */
40
    private $configFolder;
41
42
    /**
43
     * Constructor.
44
     *
45
     * @param string $configFolder
46
     */
47
    public function __construct($configFolder = null)
48
    {
49
        $this->configFolder = $configFolder ?: __DIR__ . '/Resources/config';
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function initialize(ExtensionManager $extensionManager): void
56
    {
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function load(ContainerBuilder $container, array $config): void
63
    {
64
        $loader = new XmlFileLoader($container, new FileLocator($this->configFolder));
65
66
        $servicesFile = 'services.xml';
67
        $loader->load($servicesFile);
68
69
        if (!isset($config['auth']['user']) || !isset($config['auth']['password'])) {
70
            $config['auth'] = null;
71
        }
72
73
        if (count($config['drivers'])) {
74
            $config['driver'] = $config['drivers'][0];
75
        }
76
77
        if (!count($config['report']['options'])) {
78
            $config['report']['options'] = [
79
                'target' => '/tmp',
80
            ];
81
        }
82
83
        if (!$container->hasParameter('mink.base_url')) {
84
            $container->setParameter('mink.base_url', null);
85
        }
86
87
        $container->setParameter('behat.code_coverage.config.auth', $config['auth']);
88
        $container->setParameter('behat.code_coverage.config.create', $config['create']);
89
        $container->setParameter('behat.code_coverage.config.read', $config['read']);
90
        $container->setParameter('behat.code_coverage.config.delete', $config['delete']);
91
        $container->setParameter('behat.code_coverage.config.driver', $config['driver']);
92
        $container->setParameter('behat.code_coverage.config.drivers', $config['drivers']);
93
        $container->setParameter('behat.code_coverage.config.filter', $config['filter']);
94
        $container->setParameter('behat.code_coverage.config.report', $config['report'] ?? []);
95
        $container->setParameter('behat.code_coverage.config.reports', $config['reports'] ?? []);
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function configure(ArrayNodeDefinition $builder): void
102
    {
103
        $builder
104
            ->children()
105
                ->arrayNode('auth')
106
                    ->children()
107
                        ->scalarNode('user')->end()
108
                        ->scalarNode('password')->end()
0 ignored issues
show
Bug introduced by
The method scalarNode() 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

108
                        ->/** @scrutinizer ignore-call */ scalarNode('password')->end()
Loading history...
109
                    ->end()
110
                ->end()
111
                ->arrayNode('create')
112
                    ->addDefaultsIfNotSet()
113
                    ->children()
114
                        ->scalarNode('method')->defaultValue('POST')->end()
115
                        ->scalarNode('path')->defaultValue('/')->end()
116
                    ->end()
117
                ->end()
118
                ->arrayNode('read')
119
                    ->addDefaultsIfNotSet()
120
                    ->children()
121
                        ->scalarNode('method')->defaultValue('GET')->end()
122
                        ->scalarNode('path')->defaultValue('/')->end()
123
                    ->end()
124
                ->end()
125
                ->arrayNode('delete')
126
                    ->addDefaultsIfNotSet()
127
                    ->children()
128
                        ->scalarNode('method')->defaultValue('DELETE')->end()
129
                        ->scalarNode('path')->defaultValue('/')->end()
130
                    ->end()
131
                ->end()
132
                ->arrayNode('drivers')
133
                    ->setDeprecated('The "drivers" option is deprecated. Use "driver" instead.')
134
                    ->prototype('scalar')->end()
135
                ->end()
136
                ->enumNode('driver')
137
                    ->values(['local', 'remote'])
138
                    ->defaultValue('local')
139
                ->end()
140
                ->arrayNode('filter')
141
                    ->addDefaultsIfNotSet()
142
                    ->children()
143
                        ->arrayNode('whitelist')
144
                            ->addDefaultsIfNotSet()
145
                            ->children()
146
                                ->scalarNode('addUncoveredFilesFromWhitelist')
147
                                    ->defaultTrue()
148
                                ->end()
149
                                ->scalarNode('processUncoveredFilesFromWhitelist')
150
                                    ->defaultFalse()
151
                                ->end()
152
                                ->arrayNode('include')
153
                                    ->addDefaultsIfNotSet()
154
                                    ->children()
155
                                        ->arrayNode('directories')
156
                                           ->useAttributeAsKey('name')
157
                                           ->normalizeKeys(false)
158
                                           ->prototype('array')
159
                                               ->children()
160
                                                   ->scalarNode('prefix')->defaultValue('')->end()
161
                                                   ->scalarNode('suffix')->defaultValue('.php')->end()
162
                                               ->end()
163
                                           ->end()
164
                                        ->end()
165
                                        ->arrayNode('files')
166
                                           ->prototype('scalar')->end()
167
                                        ->end()
168
                                    ->end()
169
                                ->end()
170
                                ->arrayNode('exclude')
171
                                    ->addDefaultsIfNotSet()
172
                                    ->children()
173
                                        ->arrayNode('directories')
174
                                           ->useAttributeAsKey('name')
175
                                           ->normalizeKeys(false)
176
                                           ->prototype('array')
177
                                               ->children()
178
                                                   ->scalarNode('prefix')->defaultValue('')->end()
179
                                                   ->scalarNode('suffix')->defaultValue('.php')->end()
180
                                               ->end()
181
                                           ->end()
182
                                        ->end()
183
                                        ->arrayNode('files')
184
                                           ->prototype('scalar')->end()
185
                                        ->end()
186
                                    ->end()
187
                                ->end()
188
                            ->end()
189
                        ->end()
190
                    ->end()
191
                ->end()
192
                ->arrayNode('report')
193
                    ->setDeprecated('The "report" option is deprecated. Use "reports" instead.')
194
                    ->children()
195
                        ->scalarNode('format')->defaultValue('html')->end()
196
                        ->arrayNode('options')
197
                            ->useAttributeAsKey('name')
198
                            ->prototype('scalar')->end()
199
                        ->end()
200
                    ->end()
201
                ->end()
202
                ->arrayNode('reports')
203
                    ->children()
204
                        ->arrayNode('clover')
205
                            ->children()
206
                                ->scalarNode('name')->end()
207
                                ->scalarNode('target')->end()
208
                            ->end()
209
                        ->end()
210
                        ->arrayNode('crap4j')
211
                            ->children()
212
                                ->scalarNode('name')->end()
213
                                ->scalarNode('target')->end()
214
                            ->end()
215
                        ->end()
216
                        ->arrayNode('html')
217
                            ->children()
218
                                ->scalarNode('target')->end()
219
                                ->scalarNode('lowUpperBound')->end()
220
                                ->scalarNode('highLowerBound')->end()
221
                            ->end()
222
                        ->end()
223
                        ->arrayNode('php')
224
                            ->children()
225
                                ->scalarNode('target')->end()
226
                            ->end()
227
                        ->end()
228
                        ->arrayNode('text')
229
                            ->children()
230
                                ->booleanNode('showColors')->end()
231
                                ->scalarNode('lowUpperBound')->end()
232
                                ->scalarNode('highLowerBound')->end()
233
                                ->booleanNode('showOnlySummary')->end()
234
                                ->booleanNode('showUncoveredFiles')->end()
235
                            ->end()
236
                        ->end()
237
                        ->arrayNode('xml')
238
                            ->children()
239
                                ->scalarNode('target')->end()
240
                            ->end()
241
                        ->end()
242
                    ->end()
243
                ->end()
244
            ->end()
245
        ->end();
246
    }
247
248
    /**
249
     * {@inheritdoc}
250
     */
251
    public function getConfigKey()
252
    {
253
        return 'code_coverage';
254
    }
255
256
    /**
257
     * {@inheritdoc}
258
     */
259
    public function process(ContainerBuilder $container): void
260
    {
261
        /** @var InputInterface $input */
262
        $input = $container->get('cli.input');
263
264
        /** @var OutputInterface $output */
265
        $output = $container->get('cli.output');
266
267
        $runtime = new Runtime();
268
        $canCollectCodeCoverage = $runtime->canCollectCodeCoverage();
269
270
        if (!$canCollectCodeCoverage) {
271
            $output->writeln('<comment>No code coverage driver is available</comment>');
272
        }
273
274
        if (!$canCollectCodeCoverage || $input->hasParameterOption('--no-coverage')) {
275
            $container->getDefinition(EventListener::class)->setArgument('$coverage', null);
276
        }
277
278
        $this->setupCodeCoverageFilter($container);
279
        $this->setupCodeCoverage($container);
280
    }
281
282
    private function setupCodeCoverage(ContainerBuilder $container): void
283
    {
284
        $codeCoverage = $container->getDefinition(CodeCoverage::class);
285
        $filter = $container->getDefinition(Filter::class);
286
        $driverChoice = $container->getParameter('behat.code_coverage.config.driver');
287
288
        if ($driverChoice === 'remote') {
289
            $remoteXdebug = $container->getDefinition(RemoteXdebug::class);
290
            $codeCoverage->setArguments([$remoteXdebug, $filter]);
291
        } else {
292
            $codeCoverage->setFactory([self::class, 'initCodeCoverage']);
293
            $codeCoverage->setArguments([$filter]);
294
        }
295
296
        $config = $container->getParameter('behat.code_coverage.config.filter');
297
298
        $codeCoverage->addMethodCall(
299
            'includeUncoveredFiles',
300
            [$config['whitelist']['addUncoveredFilesFromWhitelist']]
301
        );
302
        $codeCoverage->addMethodCall(
303
            'processUncoveredFiles',
304
            [$config['whitelist']['processUncoveredFilesFromWhitelist']]
305
        );
306
    }
307
308
    private function setupCodeCoverageFilter(ContainerBuilder $container): void
309
    {
310
        $filter = $container->getDefinition(Filter::class);
311
        $config = $container->getParameter('behat.code_coverage.config.filter');
312
313
        $dirs = [
314
            'includeDirectory' => ['whitelist', 'include', 'directories'],
315
            'excludeDirectory' => ['whitelist', 'exclude', 'directories'],
316
        ];
317
318
        foreach ($dirs as $method => $hiera) {
319
            foreach ($config[$hiera[0]][$hiera[1]][$hiera[2]] as $path => $dir) {
320
                $filter->addMethodCall($method, [$path, $dir['suffix'], $dir['prefix']]);
321
            }
322
        }
323
324
        $files = [
325
            'addFileToWhiteList' => ['whitelist', 'include', 'files'],
326
            'removeFileFromWhiteList' => ['whitelist', 'exclude', 'files'],
327
        ];
328
329
        foreach ($files as $method => $hiera) {
330
            foreach ($config[$hiera[0]][$hiera[1]][$hiera[2]] as $file) {
331
                $filter->addMethodCall($method, [$file]);
332
            }
333
        }
334
    }
335
336
    public static function initCodeCoverage(Filter $filter): CodeCoverage
337
    {
338
        try {
339
            $driver = Driver::forLineAndPathCoverage($filter);
340
        } catch (NoCodeCoverageDriverWithPathCoverageSupportAvailableException $e) {
341
            $driver = Driver::forLineCoverage($filter);
342
        }
343
344
        return new CodeCoverage($driver, $filter);
345
    }
346
}
347