Passed
Push — master ( 5eb535...2a2a20 )
by Alexey
02:58
created

Engine::getFilteredExtraOptionsForMutant()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
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 explode;
39
use Infection\AbstractTestFramework\TestFrameworkAdapter;
40
use Infection\Configuration\Configuration;
41
use Infection\Console\ConsoleOutput;
42
use Infection\Event\ApplicationExecutionWasFinished;
43
use Infection\Event\EventDispatcher\EventDispatcher;
44
use Infection\Metrics\MetricsCalculator;
45
use Infection\Metrics\MinMsiChecker;
46
use Infection\Metrics\MinMsiCheckFailed;
47
use Infection\Mutation\MutationGenerator;
48
use Infection\PhpParser\Visitor\IgnoreNode\NodeIgnorer;
49
use Infection\Process\Runner\InitialTestsFailed;
50
use Infection\Process\Runner\InitialTestsRunner;
51
use Infection\Process\Runner\MutationTestingRunner;
52
use Infection\Resource\Memory\MemoryLimiter;
53
use Infection\TestFramework\Coverage\CoverageChecker;
54
use Infection\TestFramework\IgnoresAdditionalNodes;
55
use Infection\TestFramework\ProvidesInitialRunOnlyOptions;
56
use Infection\TestFramework\TestFrameworkExtraOptionsFilter;
57
58
/**
59
 * @internal
60
 */
61
final class Engine
62
{
63
    private $config;
64
    private $adapter;
65
    private $coverageChecker;
66
    private $eventDispatcher;
67
    private $initialTestsRunner;
68
    private $memoryLimiter;
69
    private $mutationGenerator;
70
    private $mutationTestingRunner;
71
    private $minMsiChecker;
72
    private $consoleOutput;
73
    private $metricsCalculator;
74
    private $testFrameworkExtraOptionsFilter;
75
76
    public function __construct(
77
        Configuration $config,
78
        TestFrameworkAdapter $adapter,
79
        CoverageChecker $coverageChecker,
80
        EventDispatcher $eventDispatcher,
81
        InitialTestsRunner $initialTestsRunner,
82
        MemoryLimiter $memoryLimiter,
83
        MutationGenerator $mutationGenerator,
84
        MutationTestingRunner $mutationTestingRunner,
85
        MinMsiChecker $minMsiChecker,
86
        ConsoleOutput $consoleOutput,
87
        MetricsCalculator $metricsCalculator,
88
        TestFrameworkExtraOptionsFilter $testFrameworkExtraOptionsFilter
89
    ) {
90
        $this->config = $config;
91
        $this->adapter = $adapter;
92
        $this->coverageChecker = $coverageChecker;
93
        $this->eventDispatcher = $eventDispatcher;
94
        $this->initialTestsRunner = $initialTestsRunner;
95
        $this->memoryLimiter = $memoryLimiter;
96
        $this->mutationGenerator = $mutationGenerator;
97
        $this->mutationTestingRunner = $mutationTestingRunner;
98
        $this->minMsiChecker = $minMsiChecker;
99
        $this->consoleOutput = $consoleOutput;
100
        $this->metricsCalculator = $metricsCalculator;
101
        $this->testFrameworkExtraOptionsFilter = $testFrameworkExtraOptionsFilter;
102
    }
103
104
    /**
105
     * @throws InitialTestsFailed
106
     * @throws MinMsiCheckFailed
107
     */
108
    public function execute(): void
109
    {
110
        $this->runInitialTestSuite();
111
        $this->runMutationAnalysis();
112
113
        $this->minMsiChecker->checkMetrics(
114
            $this->metricsCalculator->getTestedMutantsCount(),
115
            $this->metricsCalculator->getMutationScoreIndicator(),
116
            $this->metricsCalculator->getCoveredCodeMutationScoreIndicator(),
117
            $this->consoleOutput
118
        );
119
120
        $this->eventDispatcher->dispatch(new ApplicationExecutionWasFinished());
121
    }
122
123
    private function runInitialTestSuite(): void
124
    {
125
        if ($this->config->shouldSkipInitialTests()) {
126
            $this->consoleOutput->logSkippingInitialTests();
127
            $this->coverageChecker->checkCoverageExists();
128
129
            return;
130
        }
131
132
        $initialTestSuiteProcess = $this->initialTestsRunner->run(
133
            $this->config->getTestFrameworkExtraOptions(),
134
            $this->getInitialTestsPhpOptionsArray(),
135
            $this->config->shouldSkipCoverage()
136
        );
137
138
        if (!$initialTestSuiteProcess->isSuccessful()) {
139
            throw InitialTestsFailed::fromProcessAndAdapter($initialTestSuiteProcess, $this->adapter);
140
        }
141
142
        $this->coverageChecker->checkCoverageHasBeenGenerated(
143
            $initialTestSuiteProcess->getCommandLine(),
144
            $initialTestSuiteProcess->getOutput()
145
        );
146
147
        /*
148
         * Limit the memory used for the mutation processes based on the memory
149
         * used for the initial test run.
150
         */
151
        $this->memoryLimiter->limitMemory($initialTestSuiteProcess->getOutput(), $this->adapter);
152
    }
153
154
    /**
155
     * @return string[]
156
     */
157
    private function getInitialTestsPhpOptionsArray(): array
158
    {
159
        return explode(' ', (string) $this->config->getInitialTestsPhpOptions());
160
    }
161
162
    private function runMutationAnalysis(): void
163
    {
164
        $mutations = $this->mutationGenerator->generate(
165
            $this->config->mutateOnlyCoveredCode(),
166
            $this->getNodeIgnorers()
167
        );
168
169
        $this->mutationTestingRunner->run(
170
            $mutations,
171
            $this->getFilteredExtraOptionsForMutant()
172
        );
173
    }
174
175
    /**
176
     * @return NodeIgnorer[]
177
     */
178
    private function getNodeIgnorers(): array
179
    {
180
        if ($this->adapter instanceof IgnoresAdditionalNodes) {
181
            return $this->adapter->getNodeIgnorers();
0 ignored issues
show
Bug introduced by
The method getNodeIgnorers() does not exist on Infection\AbstractTestFr...rk\TestFrameworkAdapter. It seems like you code against a sub-type of Infection\AbstractTestFr...rk\TestFrameworkAdapter such as Infection\TestFramework\...\Adapter\PhpUnitAdapter. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

181
            return $this->adapter->/** @scrutinizer ignore-call */ getNodeIgnorers();
Loading history...
182
        }
183
184
        return [];
185
    }
186
187
    private function getFilteredExtraOptionsForMutant(): string
188
    {
189
        if ($this->adapter instanceof ProvidesInitialRunOnlyOptions) {
190
            return $this->testFrameworkExtraOptionsFilter->filterForMutantProcess(
191
                $this->config->getTestFrameworkExtraOptions(),
192
                $this->adapter->getInitialRunOnlyOptions()
193
            );
194
        }
195
196
        return $this->config->getTestFrameworkExtraOptions();
197
    }
198
}
199