Passed
Push — master ( 1d8e46...0e8320 )
by Théo
02:15
created

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