Issues (181)

src/Container.php (6 issues)

1
<?php
2
/**
3
 * This code is licensed under the BSD 3-Clause License.
4
 *
5
 * Copyright (c) 2017, Maks Rafalko
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions are met:
10
 *
11
 * * Redistributions of source code must retain the above copyright notice, this
12
 *   list of conditions and the following disclaimer.
13
 *
14
 * * Redistributions in binary form must reproduce the above copyright notice,
15
 *   this list of conditions and the following disclaimer in the documentation
16
 *   and/or other materials provided with the distribution.
17
 *
18
 * * Neither the name of the copyright holder nor the names of its
19
 *   contributors may be used to endorse or promote products derived from
20
 *   this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
declare(strict_types=1);
35
36
namespace Infection;
37
38
use function array_filter;
39
use function array_key_exists;
40
use Closure;
41
use Infection\AbstractTestFramework\TestFrameworkAdapter;
42
use Infection\CI\MemoizedCiDetector;
43
use Infection\CI\NullCiDetector;
44
use Infection\Configuration\Configuration;
45
use Infection\Configuration\ConfigurationFactory;
46
use Infection\Configuration\Schema\SchemaConfiguration;
47
use Infection\Configuration\Schema\SchemaConfigurationFactory;
48
use Infection\Configuration\Schema\SchemaConfigurationFileLoader;
49
use Infection\Configuration\Schema\SchemaConfigurationLoader;
50
use Infection\Configuration\Schema\SchemaValidator;
51
use Infection\Console\Input\MsiParser;
52
use Infection\Console\LogVerbosity;
53
use Infection\Console\OutputFormatter\FormatterFactory;
54
use Infection\Console\OutputFormatter\FormatterName;
55
use Infection\Console\OutputFormatter\OutputFormatter;
56
use Infection\Differ\DiffColorizer;
57
use Infection\Differ\Differ;
58
use Infection\Differ\DiffSourceCodeMatcher;
59
use Infection\Event\EventDispatcher\EventDispatcher;
60
use Infection\Event\EventDispatcher\SyncEventDispatcher;
61
use Infection\Event\Subscriber\ChainSubscriberFactory;
62
use Infection\Event\Subscriber\CleanUpAfterMutationTestingFinishedSubscriberFactory;
63
use Infection\Event\Subscriber\InitialTestsConsoleLoggerSubscriberFactory;
64
use Infection\Event\Subscriber\MutationGeneratingConsoleLoggerSubscriberFactory;
65
use Infection\Event\Subscriber\MutationTestingConsoleLoggerSubscriberFactory;
66
use Infection\Event\Subscriber\MutationTestingResultsLoggerSubscriberFactory;
67
use Infection\Event\Subscriber\PerformanceLoggerSubscriberFactory;
68
use Infection\Event\Subscriber\SubscriberRegisterer;
69
use Infection\ExtensionInstaller\GeneratedExtensionsConfig;
70
use Infection\FileSystem\DummyFileSystem;
71
use Infection\FileSystem\Finder\ComposerExecutableFinder;
72
use Infection\FileSystem\Finder\TestFrameworkFinder;
73
use Infection\FileSystem\Locator\RootsFileLocator;
74
use Infection\FileSystem\Locator\RootsFileOrDirectoryLocator;
75
use Infection\FileSystem\SourceFileCollector;
76
use Infection\FileSystem\SourceFileFilter;
77
use Infection\FileSystem\TmpDirProvider;
78
use Infection\Logger\LoggerFactory;
79
use Infection\Metrics\MetricsCalculator;
80
use Infection\Metrics\MinMsiChecker;
81
use Infection\Mutant\MutantCodeFactory;
82
use Infection\Mutant\MutantExecutionResultFactory;
83
use Infection\Mutant\MutantFactory;
84
use Infection\Mutation\FileMutationGenerator;
85
use Infection\Mutation\MutationAttributeKeys;
86
use Infection\Mutation\MutationGenerator;
87
use Infection\Mutator\MutatorFactory;
88
use Infection\Mutator\MutatorParser;
89
use Infection\Mutator\MutatorResolver;
90
use Infection\PhpParser\FileParser;
91
use Infection\PhpParser\NodeTraverserFactory;
92
use Infection\Process\Factory\InitialTestsRunProcessFactory;
93
use Infection\Process\Factory\MutantProcessFactory;
94
use Infection\Process\Runner\DryProcessRunner;
95
use Infection\Process\Runner\InitialTestsRunner;
96
use Infection\Process\Runner\MutationTestingRunner;
97
use Infection\Process\Runner\ParallelProcessRunner;
98
use Infection\Process\Runner\ProcessRunner;
99
use Infection\Resource\Memory\MemoryFormatter;
100
use Infection\Resource\Memory\MemoryLimiter;
101
use Infection\Resource\Memory\MemoryLimiterEnvironment;
102
use Infection\Resource\Time\Stopwatch;
103
use Infection\Resource\Time\TimeFormatter;
104
use Infection\TestFramework\AdapterInstallationDecider;
105
use Infection\TestFramework\AdapterInstaller;
106
use Infection\TestFramework\CommandLineBuilder;
107
use Infection\TestFramework\Config\TestFrameworkConfigLocator;
108
use Infection\TestFramework\Coverage\BufferedSourceFileFilter;
109
use Infection\TestFramework\Coverage\CoverageChecker;
110
use Infection\TestFramework\Coverage\CoveredTraceProvider;
111
use Infection\TestFramework\Coverage\JUnit\JUnitReportLocator;
112
use Infection\TestFramework\Coverage\JUnit\JUnitTestExecutionInfoAdder;
113
use Infection\TestFramework\Coverage\JUnit\JUnitTestFileDataProvider;
114
use Infection\TestFramework\Coverage\JUnit\MemoizedTestFileDataProvider;
115
use Infection\TestFramework\Coverage\JUnit\TestFileDataProvider;
116
use Infection\TestFramework\Coverage\LineRangeCalculator;
117
use Infection\TestFramework\Coverage\UncoveredTraceProvider;
118
use Infection\TestFramework\Coverage\UnionTraceProvider;
119
use Infection\TestFramework\Coverage\XmlReport\IndexXmlCoverageLocator;
120
use Infection\TestFramework\Coverage\XmlReport\IndexXmlCoverageParser;
121
use Infection\TestFramework\Coverage\XmlReport\PhpUnitXmlCoverageTraceProvider;
122
use Infection\TestFramework\Coverage\XmlReport\XmlCoverageParser;
123
use Infection\TestFramework\Factory;
124
use Infection\TestFramework\TestFrameworkExtraOptionsFilter;
125
use InvalidArgumentException;
126
use OndraM\CiDetector\CiDetector;
127
use function php_ini_loaded_file;
128
use PhpParser\Lexer;
129
use PhpParser\Parser;
130
use PhpParser\ParserFactory;
131
use PhpParser\PrettyPrinter\Standard;
132
use PhpParser\PrettyPrinterAbstract;
133
use Psr\Log\LoggerInterface;
134
use Psr\Log\NullLogger;
135
use function Safe\getcwd;
136
use function Safe\sprintf;
137
use SebastianBergmann\Diff\Differ as BaseDiffer;
138
use Symfony\Component\Console\Helper\QuestionHelper;
139
use Symfony\Component\Console\Output\NullOutput;
140
use Symfony\Component\Console\Output\OutputInterface;
141
use Symfony\Component\Filesystem\Filesystem;
142
use Webmozart\Assert\Assert;
143
use Webmozart\PathUtil\Path;
144
145
/**
146
 * @internal
147
 */
148
final class Container
149
{
150
    public const DEFAULT_CONFIG_FILE = null;
151
    public const DEFAULT_MUTATORS_INPUT = '';
152
    public const DEFAULT_SHOW_MUTATIONS = false;
153
    public const DEFAULT_LOG_VERBOSITY = LogVerbosity::NORMAL;
154
    public const DEFAULT_DEBUG = false;
155
    public const DEFAULT_ONLY_COVERED = false;
156
    public const DEFAULT_FORMATTER_NAME = FormatterName::DOT;
157
    public const DEFAULT_NO_PROGRESS = false;
158
    public const DEFAULT_FORCE_PROGRESS = false;
159
    public const DEFAULT_EXISTING_COVERAGE_PATH = null;
160
    public const DEFAULT_INITIAL_TESTS_PHP_OPTIONS = null;
161
    public const DEFAULT_SKIP_INITIAL_TESTS = false;
162
    public const DEFAULT_IGNORE_MSI_WITH_NO_MUTATIONS = false;
163
    public const DEFAULT_MIN_MSI = null;
164
    public const DEFAULT_MIN_COVERED_MSI = null;
165
    public const DEFAULT_MSI_PRECISION = MsiParser::DEFAULT_PRECISION;
166
    public const DEFAULT_TEST_FRAMEWORK = null;
167
    public const DEFAULT_TEST_FRAMEWORK_EXTRA_OPTIONS = null;
168
    public const DEFAULT_FILTER = '';
169
    public const DEFAULT_THREAD_COUNT = 1;
170
    public const DEFAULT_DRY_RUN = false;
171
172
    /**
173
     * @var array<class-string<object>, true>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<object>, true> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<object>, true>.
Loading history...
174
     */
175
    private $keys = [];
176
177
    /**
178
     * @var array<class-string<object>, object>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<object>, object> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<object>, object>.
Loading history...
179
     */
180
    private $values = [];
181
182
    /**
183
     * @var array<class-string<object>, Closure(self): object>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<objec... Closure(self): object> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<object>, Closure(self): object>.
Loading history...
184
     */
185
    private $factories = [];
186
187
    /**
188
     * @var string|null
189
     */
190
    private $defaultJUnitPath;
191
192
    /**
193
     * @param array<class-string<object>, Closure(self): object> $values
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string<objec... Closure(self): object> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string<object>, Closure(self): object>.
Loading history...
194
     */
195
    public function __construct(array $values)
196
    {
197
        foreach ($values as $id => $value) {
198
            $this->offsetSet($id, $value);
199
        }
200
    }
201
202
    public static function create(): self
203
    {
204
        $container = new self([
205
            Filesystem::class => static function (): Filesystem {
206
                return new Filesystem();
207
            },
208
            TmpDirProvider::class => static function (): TmpDirProvider {
209
                return new TmpDirProvider();
210
            },
211
            IndexXmlCoverageParser::class => static function (): IndexXmlCoverageParser {
212
                return new IndexXmlCoverageParser();
213
            },
214
            XmlCoverageParser::class => static function (): XmlCoverageParser {
215
                // TODO XmlCoverageParser might want to notify ProcessRunner if it can't parse another file due to lack of RAM
216
                return new XmlCoverageParser();
217
            },
218
            CoveredTraceProvider::class => static function (self $container): CoveredTraceProvider {
219
                return new CoveredTraceProvider(
220
                    $container->getPhpUnitXmlCoverageTraceProvider(),
221
                    $container->getJUnitTestExecutionInfoAdder(),
222
                    $container->getBufferedSourceFileFilter(),
223
                );
224
            },
225
            UnionTraceProvider::class => static function (self $container): UnionTraceProvider {
226
                return new UnionTraceProvider(
227
                    $container->getCoveredTraceProvider(),
228
                    $container->getUncoveredTraceProvider(),
229
                    $container->getConfiguration()->mutateOnlyCoveredCode()
230
                );
231
            },
232
            BufferedSourceFileFilter::class => static function (self $container): BufferedSourceFileFilter {
233
                return new BufferedSourceFileFilter(
234
                    $container->getSourceFileFilter(),
235
                    $container->getConfiguration()->getSourceFiles(),
236
                );
237
            },
238
            UncoveredTraceProvider::class => static function (self $container): UncoveredTraceProvider {
239
                return new UncoveredTraceProvider(
240
                    $container->getBufferedSourceFileFilter()
241
                );
242
            },
243
            SourceFileFilter::class => static function (self $container): SourceFileFilter {
244
                return new SourceFileFilter(
245
                    $container->getConfiguration()->getSourceFilesFilter(),
246
                    $container->getConfiguration()->getSourceFilesExcludes()
247
                );
248
            },
249
            JUnitTestExecutionInfoAdder::class => static function (self $container): JUnitTestExecutionInfoAdder {
250
                return new JUnitTestExecutionInfoAdder(
251
                    $container->getTestFrameworkAdapter(),
252
                    $container->getMemoizedTestFileDataProvider()
253
                );
254
            },
255
            PhpUnitXmlCoverageTraceProvider::class => static function (self $container): PhpUnitXmlCoverageTraceProvider {
256
                return new PhpUnitXmlCoverageTraceProvider(
257
                    $container->getIndexXmlCoverageLocator(),
258
                    $container->getIndexXmlCoverageParser(),
259
                    $container->getXmlCoverageParser()
260
                );
261
            },
262
            IndexXmlCoverageLocator::class => static function (self $container): IndexXmlCoverageLocator {
263
                return new IndexXmlCoverageLocator(
264
                    $container->getConfiguration()->getCoveragePath()
265
                );
266
            },
267
            RootsFileOrDirectoryLocator::class => static function (self $container): RootsFileOrDirectoryLocator {
268
                return new RootsFileOrDirectoryLocator(
269
                    [$container->getProjectDir()],
270
                    $container->getFileSystem()
271
                );
272
            },
273
            Factory::class => static function (self $container): Factory {
274
                $config = $container->getConfiguration();
275
276
                return new Factory(
277
                    $config->getTmpDir(),
278
                    $container->getProjectDir(),
279
                    $container->getTestFrameworkConfigLocator(),
280
                    $container->getTestFrameworkFinder(),
281
                    $container->getDefaultJUnitFilePath(),
282
                    $config,
283
                    GeneratedExtensionsConfig::EXTENSIONS
284
                );
285
            },
286
            MutantCodeFactory::class => static function (self $container): MutantCodeFactory {
287
                return new MutantCodeFactory($container->getPrinter());
288
            },
289
            MutantFactory::class => static function (self $container): MutantFactory {
290
                return new MutantFactory(
291
                    $container->getConfiguration()->getTmpDir(),
292
                    $container->getDiffer(),
293
                    $container->getPrinter(),
294
                    $container->getMutantCodeFactory()
295
                );
296
            },
297
            Differ::class => static function (): Differ {
298
                return new Differ(new BaseDiffer());
299
            },
300
            SyncEventDispatcher::class => static function (): SyncEventDispatcher {
301
                return new SyncEventDispatcher();
302
            },
303
            ParallelProcessRunner::class => static function (self $container): ParallelProcessRunner {
304
                return new ParallelProcessRunner($container->getConfiguration()->getThreadCount());
305
            },
306
            DryProcessRunner::class => static function (): DryProcessRunner {
307
                return new DryProcessRunner();
308
            },
309
            TestFrameworkConfigLocator::class => static function (self $container): TestFrameworkConfigLocator {
310
                return new TestFrameworkConfigLocator(
311
                    (string) $container->getConfiguration()->getPhpUnit()->getConfigDir()
312
                );
313
            },
314
            DiffColorizer::class => static function (): DiffColorizer {
315
                return new DiffColorizer();
316
            },
317
            MemoizedTestFileDataProvider::class => static function (self $container): TestFileDataProvider {
318
                return new MemoizedTestFileDataProvider(
319
                    new JUnitTestFileDataProvider($container->getJUnitReportLocator())
320
                );
321
            },
322
            Lexer::class => static function (): Lexer {
323
                $attributes = MutationAttributeKeys::ALL;
324
                $attributes[] = 'comments';
325
326
                return new Lexer\Emulative(['usedAttributes' => $attributes]);
327
            },
328
            Parser::class => static function (self $container): Parser {
329
                $lexer = $container->getLexer();
330
331
                return (new ParserFactory())->create(ParserFactory::PREFER_PHP7, $lexer);
332
            },
333
            FileParser::class => static function (self $container): FileParser {
334
                return new FileParser($container->getParser());
335
            },
336
            PrettyPrinterAbstract::class => static function (): Standard {
337
                return new Standard();
338
            },
339
            MetricsCalculator::class => static function (self $container): MetricsCalculator {
340
                return new MetricsCalculator($container->getConfiguration()->getMsiPrecision());
341
            },
342
            Stopwatch::class => static function (): Stopwatch {
343
                return new Stopwatch();
344
            },
345
            TimeFormatter::class => static function (): TimeFormatter {
346
                return new TimeFormatter();
347
            },
348
            MemoryFormatter::class => static function (): MemoryFormatter {
349
                return new MemoryFormatter();
350
            },
351
            MemoryLimiter::class => static function (self $container): MemoryLimiter {
352
                return new MemoryLimiter(
353
                    $container->getFileSystem(),
354
                    (string) php_ini_loaded_file(),
355
                    new MemoryLimiterEnvironment()
356
                );
357
            },
358
            SchemaConfigurationLoader::class => static function (self $container): SchemaConfigurationLoader {
359
                return new SchemaConfigurationLoader(
360
                    $container->getRootsFileLocator(),
361
                    $container->getSchemaConfigurationFileLoader()
362
                );
363
            },
364
            RootsFileLocator::class => static function (self $container): RootsFileLocator {
365
                return new RootsFileLocator(
366
                    [$container->getProjectDir()],
367
                    $container->getFileSystem()
368
                );
369
            },
370
            SchemaConfigurationFileLoader::class => static function (self $container): SchemaConfigurationFileLoader {
371
                return new SchemaConfigurationFileLoader(
372
                    $container->getSchemaValidator(),
373
                    $container->getSchemaConfigurationFactory()
374
                );
375
            },
376
            SchemaValidator::class => static function (): SchemaValidator {
377
                return new SchemaValidator();
378
            },
379
            SchemaConfigurationFactory::class => static function (): SchemaConfigurationFactory {
380
                return new SchemaConfigurationFactory();
381
            },
382
            ConfigurationFactory::class => static function (self $container): ConfigurationFactory {
383
                return new ConfigurationFactory(
384
                    $container->getTmpDirProvider(),
385
                    $container->getMutatorResolver(),
386
                    $container->getMutatorFactory(),
387
                    $container->getMutatorParser(),
388
                    $container->getSourceFileCollector(),
389
                    $container->getCiDetector()
390
                );
391
            },
392
            MutatorResolver::class => static function (): MutatorResolver {
393
                return new MutatorResolver();
394
            },
395
            MutatorFactory::class => static function (): MutatorFactory {
396
                return new MutatorFactory();
397
            },
398
            MutatorParser::class => static function (): MutatorParser {
399
                return new MutatorParser();
400
            },
401
            CoverageChecker::class => static function (self $container): CoverageChecker {
402
                $config = $container->getConfiguration();
403
                $testFrameworkAdapter = $container->getTestFrameworkAdapter();
404
405
                return new CoverageChecker(
406
                    $config->shouldSkipCoverage(),
407
                    $config->shouldSkipInitialTests(),
408
                    $config->getInitialTestsPhpOptions() ?? '',
409
                    $config->getCoveragePath(),
410
                    $testFrameworkAdapter->hasJUnitReport(),
411
                    $container->getJUnitReportLocator(),
412
                    $testFrameworkAdapter->getName(),
413
                    $container->getIndexXmlCoverageLocator()
414
                );
415
            },
416
            JUnitReportLocator::class => static function (self $container): JUnitReportLocator {
417
                return new JUnitReportLocator(
418
                    $container->getConfiguration()->getCoveragePath(),
419
                    $container->getDefaultJUnitFilePath()
420
                );
421
            },
422
            MinMsiChecker::class => static function (self $container): MinMsiChecker {
423
                $config = $container->getConfiguration();
424
425
                return new MinMsiChecker(
426
                    $config->ignoreMsiWithNoMutations(),
427
                    (float) $config->getMinMsi(),
428
                    (float) $config->getMinCoveredMsi()
429
                );
430
            },
431
            SubscriberRegisterer::class => static function (self $container): SubscriberRegisterer {
432
                return new SubscriberRegisterer(
433
                    $container->getEventDispatcher(),
434
                    $container->getSubscriberFactoryRegistry()
435
                );
436
            },
437
            ChainSubscriberFactory::class => static function (self $container): ChainSubscriberFactory {
438
                return new ChainSubscriberFactory(
439
                    $container->getInitialTestsConsoleLoggerSubscriberFactory(),
440
                    $container->getMutationGeneratingConsoleLoggerSubscriberFactory(),
441
                    $container->getMutationTestingConsoleLoggerSubscriberFactory(),
442
                    $container->getMutationTestingResultsLoggerSubscriberFactory(),
443
                    $container->getPerformanceLoggerSubscriberFactory(),
444
                    $container->getCleanUpAfterMutationTestingFinishedSubscriberFactory()
445
                );
446
            },
447
            CleanUpAfterMutationTestingFinishedSubscriberFactory::class => static function (self $container): CleanUpAfterMutationTestingFinishedSubscriberFactory {
448
                $config = $container->getConfiguration();
449
450
                return new CleanUpAfterMutationTestingFinishedSubscriberFactory(
451
                    $config->isDebugEnabled(),
452
                    $container->getFileSystem(),
453
                    $config->getTmpDir()
454
                );
455
            },
456
            InitialTestsConsoleLoggerSubscriberFactory::class => static function (self $container): InitialTestsConsoleLoggerSubscriberFactory {
457
                $config = $container->getConfiguration();
458
459
                return new InitialTestsConsoleLoggerSubscriberFactory(
460
                    $config->noProgress(),
461
                    $container->getTestFrameworkAdapter(),
462
                    $config->isDebugEnabled()
463
                );
464
            },
465
            MutationGeneratingConsoleLoggerSubscriberFactory::class => static function (self $container): MutationGeneratingConsoleLoggerSubscriberFactory {
466
                return new MutationGeneratingConsoleLoggerSubscriberFactory(
467
                    $container->getConfiguration()->noProgress()
468
                );
469
            },
470
            MutationTestingConsoleLoggerSubscriberFactory::class => static function (self $container): MutationTestingConsoleLoggerSubscriberFactory {
471
                $config = $container->getConfiguration();
472
473
                return new MutationTestingConsoleLoggerSubscriberFactory(
474
                    $container->getMetricsCalculator(),
475
                    $container->getDiffColorizer(),
476
                    $config->showMutations(),
477
                    $container->getOutputFormatter()
478
                );
479
            },
480
            MutationTestingResultsLoggerSubscriberFactory::class => static function (self $container): MutationTestingResultsLoggerSubscriberFactory {
481
                return new MutationTestingResultsLoggerSubscriberFactory(
482
                    $container->getLoggerFactory(),
483
                    $container->getConfiguration()->getLogs()
484
                );
485
            },
486
            PerformanceLoggerSubscriberFactory::class => static function (self $container): PerformanceLoggerSubscriberFactory {
487
                return new PerformanceLoggerSubscriberFactory(
488
                    $container->getStopwatch(),
489
                    $container->getTimeFormatter(),
490
                    $container->getMemoryFormatter()
491
                );
492
            },
493
            CommandLineBuilder::class => static function (): CommandLineBuilder {
494
                return new CommandLineBuilder();
495
            },
496
            SourceFileCollector::class => static function (): SourceFileCollector {
497
                return new SourceFileCollector();
498
            },
499
            NodeTraverserFactory::class => static function (): NodeTraverserFactory {
500
                return new NodeTraverserFactory();
501
            },
502
            FileMutationGenerator::class => static function (self $container): FileMutationGenerator {
503
                return new FileMutationGenerator(
504
                    $container->getFileParser(),
505
                    $container->getNodeTraverserFactory(),
506
                    $container->getLineRangeCalculator()
507
                );
508
            },
509
            LoggerFactory::class => static function (self $container): LoggerFactory {
510
                $config = $container->getConfiguration();
511
512
                return new LoggerFactory(
513
                    $container->getMetricsCalculator(),
514
                    $container->getFileSystem(),
515
                    $config->getLogVerbosity(),
516
                    $config->isDebugEnabled(),
517
                    $config->mutateOnlyCoveredCode(),
518
                    $container->getCiDetector(),
519
                    $container->getLogger()
520
                );
521
            },
522
            TestFrameworkAdapter::class => static function (self $container): TestFrameworkAdapter {
523
                $config = $container->getConfiguration();
524
525
                return $container->getFactory()->create(
526
                    $config->getTestFramework(),
527
                    $config->shouldSkipCoverage()
528
                );
529
            },
530
            InitialTestsRunProcessFactory::class => static function (self $container): InitialTestsRunProcessFactory {
531
                return new InitialTestsRunProcessFactory(
532
                    $container->getTestFrameworkAdapter()
533
                );
534
            },
535
            InitialTestsRunner::class => static function (self $container): InitialTestsRunner {
536
                return new InitialTestsRunner(
537
                    $container->getInitialTestRunProcessFactory(),
538
                    $container->getEventDispatcher()
539
                );
540
            },
541
            MutantProcessFactory::class => static function (self $container): MutantProcessFactory {
542
                return new MutantProcessFactory(
543
                    $container->getTestFrameworkAdapter(),
544
                    $container->getConfiguration()->getProcessTimeout(),
545
                    $container->getEventDispatcher(),
546
                    $container->getMutantExecutionResultFactory()
547
                );
548
            },
549
            MutationGenerator::class => static function (self $container): MutationGenerator {
550
                $config = $container->getConfiguration();
551
552
                return new MutationGenerator(
553
                    $container->getUnionTraceProvider(),
554
                    $config->getMutators(),
555
                    $container->getEventDispatcher(),
556
                    $container->getFileMutationGenerator(),
557
                    $config->noProgress()
558
                );
559
            },
560
            MutationTestingRunner::class => static function (self $container): MutationTestingRunner {
561
                $configuration = $container->getConfiguration();
562
563
                return new MutationTestingRunner(
564
                    $container->getMutantProcessFactory(),
565
                    $container->getMutantFactory(),
566
                    $container->getProcessRunner(),
567
                    $container->getEventDispatcher(),
568
                    $configuration->isDryRun()
569
                        ? new DummyFileSystem()
570
                        : $container->getFileSystem(),
571
                    $container->getDiffSourceCodeMatcher(),
572
                    $configuration->noProgress(),
573
                    $configuration->getProcessTimeout(),
574
                    $configuration->getIgnoreSourceCodeMutatorsMap()
575
                );
576
            },
577
            LineRangeCalculator::class => static function (): LineRangeCalculator {
578
                return new LineRangeCalculator();
579
            },
580
            TestFrameworkFinder::class => static function (): TestFrameworkFinder {
581
                return new TestFrameworkFinder();
582
            },
583
            TestFrameworkExtraOptionsFilter::class => static function (): TestFrameworkExtraOptionsFilter {
584
                return new TestFrameworkExtraOptionsFilter();
585
            },
586
            AdapterInstallationDecider::class => static function (): AdapterInstallationDecider {
587
                return new AdapterInstallationDecider(new QuestionHelper());
588
            },
589
            AdapterInstaller::class => static function (): AdapterInstaller {
590
                return new AdapterInstaller(new ComposerExecutableFinder());
591
            },
592
            MutantExecutionResultFactory::class => static function (self $container): MutantExecutionResultFactory {
593
                return new MutantExecutionResultFactory($container->getTestFrameworkAdapter());
594
            },
595
            FormatterFactory::class => static function (self $container): FormatterFactory {
596
                return new FormatterFactory($container->getOutput());
597
            },
598
            DiffSourceCodeMatcher::class => static function (): DiffSourceCodeMatcher {
599
                return new DiffSourceCodeMatcher();
600
            },
601
        ]);
602
603
        return $container->withValues(
604
            new NullLogger(),
605
            new NullOutput(),
606
            self::DEFAULT_CONFIG_FILE,
607
            self::DEFAULT_MUTATORS_INPUT,
608
            self::DEFAULT_SHOW_MUTATIONS,
609
            self::DEFAULT_LOG_VERBOSITY,
610
            self::DEFAULT_DEBUG,
611
            self::DEFAULT_ONLY_COVERED,
612
            self::DEFAULT_FORMATTER_NAME,
613
            self::DEFAULT_NO_PROGRESS,
614
            self::DEFAULT_FORCE_PROGRESS,
615
            self::DEFAULT_EXISTING_COVERAGE_PATH,
616
            self::DEFAULT_INITIAL_TESTS_PHP_OPTIONS,
617
            self::DEFAULT_SKIP_INITIAL_TESTS,
618
            self::DEFAULT_IGNORE_MSI_WITH_NO_MUTATIONS,
619
            self::DEFAULT_MIN_MSI,
620
            self::DEFAULT_MIN_COVERED_MSI,
621
            self::DEFAULT_MSI_PRECISION,
622
            self::DEFAULT_TEST_FRAMEWORK,
623
            self::DEFAULT_TEST_FRAMEWORK_EXTRA_OPTIONS,
624
            self::DEFAULT_FILTER,
625
            self::DEFAULT_THREAD_COUNT,
626
            self::DEFAULT_DRY_RUN
627
        );
628
    }
629
630
    public function withValues(
631
        LoggerInterface $logger,
632
        OutputInterface $output,
633
        ?string $configFile,
634
        string $mutatorsInput,
635
        bool $showMutations,
636
        string $logVerbosity,
637
        bool $debug,
638
        bool $onlyCovered,
639
        string $formatterName,
640
        bool $noProgress,
641
        bool $forceProgress,
642
        ?string $existingCoveragePath,
643
        ?string $initialTestsPhpOptions,
644
        bool $skipInitialTests,
645
        bool $ignoreMsiWithNoMutations,
646
        ?float $minMsi,
647
        ?float $minCoveredMsi,
648
        int $msiPrecision,
649
        ?string $testFramework,
650
        ?string $testFrameworkExtraOptions,
651
        string $filter,
652
        int $threadCount,
653
        bool $dryRun
654
    ): self {
655
        $clone = clone $this;
656
657
        if ($forceProgress) {
658
            Assert::false($noProgress, 'Cannot force progress and set no progress at the same time');
659
        }
660
661
        $clone->offsetSet(
662
            CiDetector::class,
663
            static function () use ($forceProgress): CiDetector {
664
                return $forceProgress ? new NullCiDetector() : new MemoizedCiDetector();
665
            }
666
        );
667
668
        $clone->offsetSet(
669
            LoggerInterface::class,
670
            static function () use ($logger): LoggerInterface {
671
                return $logger;
672
            }
673
        );
674
675
        $clone->offsetSet(
676
            SchemaConfiguration::class,
677
            static function (self $container) use ($configFile): SchemaConfiguration {
678
                return $container->getSchemaConfigurationLoader()->loadConfiguration(
679
                    array_filter(
680
                        [
681
                            $configFile,
682
                            SchemaConfigurationLoader::DEFAULT_CONFIG_FILE,
683
                            SchemaConfigurationLoader::DEFAULT_DIST_CONFIG_FILE,
684
                        ]
685
                    )
686
                );
687
            }
688
        );
689
690
        $clone->offsetSet(
691
            OutputInterface::class,
692
            static function () use ($output): OutputInterface {
693
                return $output;
694
            }
695
        );
696
697
        $clone->offsetSet(
698
            OutputFormatter::class,
699
            static function (self $container) use ($formatterName): OutputFormatter {
700
                return $container->getFormatterFactory()->create($formatterName);
701
            }
702
        );
703
704
        $clone->offsetSet(
705
            Configuration::class,
706
            static function (self $container) use (
707
                $existingCoveragePath,
708
                $initialTestsPhpOptions,
709
                $skipInitialTests,
710
                $logVerbosity,
711
                $debug,
712
                $onlyCovered,
713
                $noProgress,
714
                $ignoreMsiWithNoMutations,
715
                $minMsi,
716
                $showMutations,
717
                $minCoveredMsi,
718
                $msiPrecision,
719
                $mutatorsInput,
720
                $testFramework,
721
                $testFrameworkExtraOptions,
722
                $filter,
723
                $threadCount,
724
                $dryRun
725
            ): Configuration {
726
                return $container->getConfigurationFactory()->create(
727
                    $container->getSchemaConfiguration(),
728
                    $existingCoveragePath,
729
                    $initialTestsPhpOptions,
730
                    $skipInitialTests,
731
                    $logVerbosity,
732
                    $debug,
733
                    $onlyCovered,
734
                    $noProgress,
735
                    $ignoreMsiWithNoMutations,
736
                    $minMsi,
737
                    $showMutations,
738
                    $minCoveredMsi,
739
                    $msiPrecision,
740
                    $mutatorsInput,
741
                    $testFramework,
742
                    $testFrameworkExtraOptions,
743
                    $filter,
744
                    $threadCount,
745
                    $dryRun
746
                );
747
            }
748
        );
749
750
        return $clone;
751
    }
752
753
    public function getProjectDir(): string
754
    {
755
        // TODO: cache that result
756
        return getcwd();
757
    }
758
759
    public function getFileSystem(): Filesystem
760
    {
761
        return $this->get(Filesystem::class);
762
    }
763
764
    public function getTmpDirProvider(): TmpDirProvider
765
    {
766
        return $this->get(TmpDirProvider::class);
767
    }
768
769
    public function getDefaultJUnitFilePath(): string
770
    {
771
        return $this->defaultJUnitPath ?? $this->defaultJUnitPath = sprintf(
772
            '%s/%s',
773
            Path::canonicalize(
774
                $this->getConfiguration()->getCoveragePath()
775
            ),
776
            'junit.xml'
777
        );
778
    }
779
780
    public function getJUnitReportLocator(): JUnitReportLocator
781
    {
782
        return $this->get(JUnitReportLocator::class);
783
    }
784
785
    public function getIndexXmlCoverageParser(): IndexXmlCoverageParser
786
    {
787
        return $this->get(IndexXmlCoverageParser::class);
788
    }
789
790
    public function getXmlCoverageParser(): XmlCoverageParser
791
    {
792
        return $this->get(XmlCoverageParser::class);
793
    }
794
795
    public function getCoveredTraceProvider(): CoveredTraceProvider
796
    {
797
        return $this->get(CoveredTraceProvider::class);
798
    }
799
800
    public function getUnionTraceProvider(): UnionTraceProvider
801
    {
802
        return $this->get(UnionTraceProvider::class);
803
    }
804
805
    public function getSourceFileFilter(): SourceFileFilter
806
    {
807
        return $this->get(SourceFileFilter::class);
808
    }
809
810
    public function getBufferedSourceFileFilter(): BufferedSourceFileFilter
811
    {
812
        return $this->get(BufferedSourceFileFilter::class);
813
    }
814
815
    public function getUncoveredTraceProvider(): UncoveredTraceProvider
816
    {
817
        return $this->get(UncoveredTraceProvider::class);
818
    }
819
820
    public function getJUnitTestExecutionInfoAdder(): JUnitTestExecutionInfoAdder
821
    {
822
        return $this->get(JUnitTestExecutionInfoAdder::class);
823
    }
824
825
    public function getPhpUnitXmlCoverageTraceProvider(): PhpUnitXmlCoverageTraceProvider
826
    {
827
        return $this->get(PhpUnitXmlCoverageTraceProvider::class);
828
    }
829
830
    public function getIndexXmlCoverageLocator(): IndexXmlCoverageLocator
831
    {
832
        return $this->get(IndexXmlCoverageLocator::class);
833
    }
834
835
    public function getRootsFileOrDirectoryLocator(): RootsFileOrDirectoryLocator
836
    {
837
        return $this->get(RootsFileOrDirectoryLocator::class);
838
    }
839
840
    public function getFactory(): Factory
841
    {
842
        return $this->get(Factory::class);
843
    }
844
845
    public function getMutantCodeFactory(): MutantCodeFactory
846
    {
847
        return $this->get(MutantCodeFactory::class);
848
    }
849
850
    public function getMutantFactory(): MutantFactory
851
    {
852
        return $this->get(MutantFactory::class);
853
    }
854
855
    public function getDiffer(): Differ
856
    {
857
        return $this->get(Differ::class);
858
    }
859
860
    public function getEventDispatcher(): EventDispatcher
861
    {
862
        return $this->get(SyncEventDispatcher::class);
863
    }
864
865
    public function getProcessRunner(): ProcessRunner
866
    {
867
        $config = $this->getConfiguration();
868
869
        return $config->isDryRun()
870
            ? $this->get(DryProcessRunner::class)
871
            : $this->get(ParallelProcessRunner::class)
872
        ;
873
    }
874
875
    public function getTestFrameworkConfigLocator(): TestFrameworkConfigLocator
876
    {
877
        return $this->get(TestFrameworkConfigLocator::class);
878
    }
879
880
    public function getDiffColorizer(): DiffColorizer
881
    {
882
        return $this->get(DiffColorizer::class);
883
    }
884
885
    public function getMemoizedTestFileDataProvider(): MemoizedTestFileDataProvider
886
    {
887
        return $this->get(MemoizedTestFileDataProvider::class);
888
    }
889
890
    public function getLexer(): Lexer
891
    {
892
        return $this->get(Lexer::class);
893
    }
894
895
    public function getParser(): Parser
896
    {
897
        return $this->get(Parser::class);
898
    }
899
900
    public function getFileParser(): FileParser
901
    {
902
        return $this->get(FileParser::class);
903
    }
904
905
    public function getPrinter(): PrettyPrinterAbstract
906
    {
907
        return $this->get(PrettyPrinterAbstract::class);
908
    }
909
910
    public function getMetricsCalculator(): MetricsCalculator
911
    {
912
        return $this->get(MetricsCalculator::class);
913
    }
914
915
    public function getStopwatch(): Stopwatch
916
    {
917
        return $this->get(Stopwatch::class);
918
    }
919
920
    public function getTimeFormatter(): TimeFormatter
921
    {
922
        return $this->get(TimeFormatter::class);
923
    }
924
925
    public function getMemoryFormatter(): MemoryFormatter
926
    {
927
        return $this->get(MemoryFormatter::class);
928
    }
929
930
    public function getMemoryLimiter(): MemoryLimiter
931
    {
932
        return $this->get(MemoryLimiter::class);
933
    }
934
935
    public function getSchemaConfigurationLoader(): SchemaConfigurationLoader
936
    {
937
        return $this->get(SchemaConfigurationLoader::class);
938
    }
939
940
    public function getRootsFileLocator(): RootsFileLocator
941
    {
942
        return $this->get(RootsFileLocator::class);
943
    }
944
945
    public function getSchemaConfigurationFileLoader(): SchemaConfigurationFileLoader
946
    {
947
        return $this->get(SchemaConfigurationFileLoader::class);
948
    }
949
950
    public function getSchemaValidator(): SchemaValidator
951
    {
952
        return $this->get(SchemaValidator::class);
953
    }
954
955
    public function getSchemaConfigurationFactory(): SchemaConfigurationFactory
956
    {
957
        return $this->get(SchemaConfigurationFactory::class);
958
    }
959
960
    public function getConfigurationFactory(): ConfigurationFactory
961
    {
962
        return $this->get(ConfigurationFactory::class);
963
    }
964
965
    public function getMutatorResolver(): MutatorResolver
966
    {
967
        return $this->get(MutatorResolver::class);
968
    }
969
970
    public function getMutatorFactory(): MutatorFactory
971
    {
972
        return $this->get(MutatorFactory::class);
973
    }
974
975
    public function getMutatorParser(): MutatorParser
976
    {
977
        return $this->get(MutatorParser::class);
978
    }
979
980
    public function getCoverageChecker(): CoverageChecker
981
    {
982
        return $this->get(CoverageChecker::class);
983
    }
984
985
    public function getMinMsiChecker(): MinMsiChecker
986
    {
987
        return $this->get(MinMsiChecker::class);
988
    }
989
990
    public function getSubscriberRegisterer(): SubscriberRegisterer
991
    {
992
        return $this->get(SubscriberRegisterer::class);
993
    }
994
995
    public function getSubscriberFactoryRegistry(): ChainSubscriberFactory
996
    {
997
        return $this->get(ChainSubscriberFactory::class);
998
    }
999
1000
    public function getCleanUpAfterMutationTestingFinishedSubscriberFactory(): CleanUpAfterMutationTestingFinishedSubscriberFactory
1001
    {
1002
        return $this->get(CleanUpAfterMutationTestingFinishedSubscriberFactory::class);
1003
    }
1004
1005
    public function getInitialTestsConsoleLoggerSubscriberFactory(): InitialTestsConsoleLoggerSubscriberFactory
1006
    {
1007
        return $this->get(InitialTestsConsoleLoggerSubscriberFactory::class);
1008
    }
1009
1010
    public function getMutationGeneratingConsoleLoggerSubscriberFactory(): MutationGeneratingConsoleLoggerSubscriberFactory
1011
    {
1012
        return $this->get(MutationGeneratingConsoleLoggerSubscriberFactory::class);
1013
    }
1014
1015
    public function getMutationTestingConsoleLoggerSubscriberFactory(): MutationTestingConsoleLoggerSubscriberFactory
1016
    {
1017
        return $this->get(MutationTestingConsoleLoggerSubscriberFactory::class);
1018
    }
1019
1020
    public function getMutationTestingResultsLoggerSubscriberFactory(): MutationTestingResultsLoggerSubscriberFactory
1021
    {
1022
        return $this->get(MutationTestingResultsLoggerSubscriberFactory::class);
1023
    }
1024
1025
    public function getPerformanceLoggerSubscriberFactory(): PerformanceLoggerSubscriberFactory
1026
    {
1027
        return $this->get(PerformanceLoggerSubscriberFactory::class);
1028
    }
1029
1030
    public function getSourceFileCollector(): SourceFileCollector
1031
    {
1032
        return $this->get(SourceFileCollector::class);
1033
    }
1034
1035
    public function getNodeTraverserFactory(): NodeTraverserFactory
1036
    {
1037
        return $this->get(NodeTraverserFactory::class);
1038
    }
1039
1040
    public function getFileMutationGenerator(): FileMutationGenerator
1041
    {
1042
        return $this->get(FileMutationGenerator::class);
1043
    }
1044
1045
    public function getLoggerFactory(): LoggerFactory
1046
    {
1047
        return $this->get(LoggerFactory::class);
1048
    }
1049
1050
    public function getTestFrameworkAdapter(): TestFrameworkAdapter
1051
    {
1052
        return $this->get(TestFrameworkAdapter::class);
1053
    }
1054
1055
    public function getInitialTestRunProcessFactory(): InitialTestsRunProcessFactory
1056
    {
1057
        return $this->get(InitialTestsRunProcessFactory::class);
1058
    }
1059
1060
    public function getInitialTestsRunner(): InitialTestsRunner
1061
    {
1062
        return $this->get(InitialTestsRunner::class);
1063
    }
1064
1065
    public function getMutantProcessFactory(): MutantProcessFactory
1066
    {
1067
        return $this->get(MutantProcessFactory::class);
1068
    }
1069
1070
    public function getMutationGenerator(): MutationGenerator
1071
    {
1072
        return $this->get(MutationGenerator::class);
1073
    }
1074
1075
    public function getMutationTestingRunner(): MutationTestingRunner
1076
    {
1077
        return $this->get(MutationTestingRunner::class);
1078
    }
1079
1080
    public function getSchemaConfiguration(): SchemaConfiguration
1081
    {
1082
        return $this->get(SchemaConfiguration::class);
1083
    }
1084
1085
    public function getConfiguration(): Configuration
1086
    {
1087
        return $this->get(Configuration::class);
1088
    }
1089
1090
    public function getLineRangeCalculator(): LineRangeCalculator
1091
    {
1092
        return $this->get(LineRangeCalculator::class);
1093
    }
1094
1095
    public function getTestFrameworkFinder(): TestFrameworkFinder
1096
    {
1097
        return $this->get(TestFrameworkFinder::class);
1098
    }
1099
1100
    public function getTestFrameworkExtraOptionsFilter(): TestFrameworkExtraOptionsFilter
1101
    {
1102
        return $this->get(TestFrameworkExtraOptionsFilter::class);
1103
    }
1104
1105
    public function getAdapterInstallationDecider(): AdapterInstallationDecider
1106
    {
1107
        return $this->get(AdapterInstallationDecider::class);
1108
    }
1109
1110
    public function getAdapterInstaller(): AdapterInstaller
1111
    {
1112
        return $this->get(AdapterInstaller::class);
1113
    }
1114
1115
    public function getMutantExecutionResultFactory(): MutantExecutionResultFactory
1116
    {
1117
        return $this->get(MutantExecutionResultFactory::class);
1118
    }
1119
1120
    public function getCiDetector(): CiDetector
1121
    {
1122
        return $this->get(CiDetector::class);
1123
    }
1124
1125
    public function getLogger(): LoggerInterface
1126
    {
1127
        return $this->get(LoggerInterface::class);
1128
    }
1129
1130
    public function getOutput(): OutputInterface
1131
    {
1132
        return $this->get(OutputInterface::class);
1133
    }
1134
1135
    public function getFormatterFactory(): FormatterFactory
1136
    {
1137
        return $this->get(FormatterFactory::class);
1138
    }
1139
1140
    public function getOutputFormatter(): OutputFormatter
1141
    {
1142
        return $this->get(OutputFormatter::class);
1143
    }
1144
1145
    public function getDiffSourceCodeMatcher(): DiffSourceCodeMatcher
1146
    {
1147
        return $this->get(DiffSourceCodeMatcher::class);
1148
    }
1149
1150
    /**
1151
     * @param class-string<object> $id
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<object> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<object>.
Loading history...
1152
     * @param Closure(self): object $value
1153
     */
1154
    private function offsetSet(string $id, Closure $value): void
1155
    {
1156
        $this->keys[$id] = true;
1157
        $this->factories[$id] = $value;
1158
        unset($this->values[$id]);
1159
    }
1160
1161
    /**
1162
     * @template T of object
1163
     *
1164
     * @param class-string<T> $id
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
1165
     * @phpstan-return T
1166
     */
1167
    private function get(string $id): object
1168
    {
1169
        if (!isset($this->keys[$id])) {
1170
            throw new InvalidArgumentException(sprintf('Unknown service "%s"', $id));
1171
        }
1172
1173
        if (array_key_exists($id, $this->values)) {
1174
            $value = $this->values[$id];
1175
        } else {
1176
            $value = $this->values[$id] = $this->factories[$id]($this);
1177
        }
1178
1179
        Assert::isInstanceOf($value, $id);
1180
1181
        return $value;
1182
    }
1183
}
1184