Passed
Push — master ( ddbcf3...47f4a6 )
by Maks
02:16
created

Mutation::getNominalTestExecutionTime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 7
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\Mutation;
37
38
use function array_intersect_key;
39
use function array_keys;
40
use function array_map;
41
use function array_sum;
42
use function implode;
43
use Infection\AbstractTestFramework\Coverage\TestLocation;
44
use Infection\Mutator\ProfileList;
45
use Infection\PhpParser\MutatedNode;
46
use function md5;
47
use PhpParser\Node;
48
use function Safe\array_flip;
49
use Webmozart\Assert\Assert;
50
51
/**
52
 * @internal
53
 * @final
54
 */
55
class Mutation
56
{
57
    private $originalFilePath;
58
    private $mutatorName;
59
    private $mutatedNodeClass;
60
    private $mutatedNode;
61
    private $mutationByMutatorIndex;
62
    private $attributes;
63
    private $originalFileAst;
64
    private $tests;
65
    private $coveredByTests;
66
    /**
67
     * @var float|null
68
     */
69
    private $nominalTimeToTest;
70
71
    /**
72
     * @var string|null
73
     */
74
    private $hash;
75
76
    /**
77
     * @param Node[] $originalFileAst
78
     * @param array<string|int|float> $attributes
79
     * @param TestLocation[] $tests
80
     */
81
    public function __construct(
82
        string $originalFilePath,
83
        array $originalFileAst,
84
        string $mutatorName,
85
        array $attributes,
86
        string $mutatedNodeClass,
87
        MutatedNode $mutatedNode,
88
        int $mutationByMutatorIndex,
89
        array $tests
90
    ) {
91
        Assert::oneOf($mutatorName, array_keys(ProfileList::ALL_MUTATORS));
92
93
        foreach (MutationAttributeKeys::ALL as $key) {
94
            Assert::keyExists($attributes, $key);
95
        }
96
97
        $this->originalFilePath = $originalFilePath;
98
        $this->originalFileAst = $originalFileAst;
99
        $this->mutatorName = $mutatorName;
100
        $this->attributes = array_intersect_key($attributes, array_flip(MutationAttributeKeys::ALL));
101
        $this->mutatedNodeClass = $mutatedNodeClass;
102
        $this->mutatedNode = $mutatedNode;
103
        $this->mutationByMutatorIndex = $mutationByMutatorIndex;
104
        $this->tests = $tests;
105
        $this->coveredByTests = $tests !== [];
106
    }
107
108
    public function getOriginalFilePath(): string
109
    {
110
        return $this->originalFilePath;
111
    }
112
113
    /**
114
     * @return Node[]
115
     */
116
    public function getOriginalFileAst(): array
117
    {
118
        return $this->originalFileAst;
119
    }
120
121
    public function getMutatorName(): string
122
    {
123
        return $this->mutatorName;
124
    }
125
126
    /**
127
     * @return (string|int|float)[]
128
     */
129
    public function getAttributes(): array
130
    {
131
        return $this->attributes;
132
    }
133
134
    public function getOriginalStartingLine(): int
135
    {
136
        return (int) $this->attributes['startLine'];
137
    }
138
139
    public function getMutatedNodeClass(): string
140
    {
141
        return $this->mutatedNodeClass;
142
    }
143
144
    public function getMutatedNode(): MutatedNode
145
    {
146
        return $this->mutatedNode;
147
    }
148
149
    // TODO: hasTest()?
150
    public function isCoveredByTest(): bool
151
    {
152
        return $this->coveredByTests;
153
    }
154
155
    /**
156
     * @return TestLocation[]
157
     */
158
    public function getAllTests(): array
159
    {
160
        return $this->tests;
161
    }
162
163
    /**
164
     * Overall time needed to run known tests for a mutation, excluding dependencies.
165
     */
166
    public function getNominalTestExecutionTime(): float
167
    {
168
        return $this->nominalTimeToTest ?? $this->nominalTimeToTest = array_sum(array_map(
169
            static function (TestLocation $testLocation) {
170
                return $testLocation->getExecutionTime();
171
            },
172
            $this->tests
173
        ));
174
    }
175
176
    public function getHash(): string
177
    {
178
        return $this->hash ?? $this->hash = $this->createHash();
179
    }
180
181
    private function createHash(): string
182
    {
183
        $hashKeys = [
184
            $this->originalFilePath,
185
            $this->mutatorName,
186
            $this->mutationByMutatorIndex,
187
        ];
188
189
        foreach ($this->attributes as $attribute) {
190
            $hashKeys[] = $attribute;
191
        }
192
193
        return md5(implode('_', $hashKeys));
194
    }
195
}
196