1
2
3
4
5
6 package mockit.coverage.lines;
7
8 import edu.umd.cs.findbugs.annotations.NonNull;
9 import edu.umd.cs.findbugs.annotations.Nullable;
10
11 import java.io.IOException;
12 import java.io.ObjectInputStream;
13 import java.util.HashMap;
14 import java.util.Map;
15 import java.util.Map.Entry;
16
17 import mockit.coverage.CallPoint;
18 import mockit.coverage.CoveragePercentage;
19 import mockit.coverage.data.PerFileCoverage;
20
21 import org.checkerframework.checker.index.qual.NonNegative;
22
23 public final class PerFileLineCoverage implements PerFileCoverage {
24 private static final long serialVersionUID = 6318915843739466316L;
25 private static final int[] NO_EXECUTIONS_YET = {};
26
27 @NonNull
28 private final Map<Integer, LineCoverageData> lineToLineData = new HashMap<>(128);
29
30 @NonNull
31 private int[] executionCounts = NO_EXECUTIONS_YET;
32
33 @NonNull
34 private transient LineCoverageData sharedLineData;
35
36
37 @NonNegative
38 private int lastLine;
39 private transient int totalSegments;
40 private transient int coveredSegments;
41
42 public PerFileLineCoverage() {
43 sharedLineData = new LineCoverageData();
44 initializeCache();
45 }
46
47 private void initializeCache() {
48 totalSegments = coveredSegments = -1;
49 }
50
51 private void readObject(@NonNull ObjectInputStream in) throws IOException, ClassNotFoundException {
52 sharedLineData = new LineCoverageData();
53 initializeCache();
54 in.defaultReadObject();
55
56 if (executionCounts.length == 0) {
57 executionCounts = NO_EXECUTIONS_YET;
58 }
59 }
60
61 public void addLine(@NonNegative int line) {
62 if (!lineToLineData.containsKey(line)) {
63 lineToLineData.put(line, null);
64 }
65
66 if (line > lastLine) {
67
68
69 int[] initialExecutionCounts = executionCounts;
70
71 if (initialExecutionCounts != NO_EXECUTIONS_YET && line >= initialExecutionCounts.length) {
72 int[] newCounts = new int[line + 30];
73 System.arraycopy(initialExecutionCounts, 0, newCounts, 0, initialExecutionCounts.length);
74 executionCounts = newCounts;
75 }
76
77 lastLine = line;
78 }
79 }
80
81 @NonNull
82 public LineCoverageData getOrCreateLineData(@NonNegative int line) {
83 LineCoverageData lineData = lineToLineData.get(line);
84
85 if (lineData == null) {
86 lineData = new LineCoverageData();
87 lineToLineData.put(line, lineData);
88 }
89
90 return lineData;
91 }
92
93 @NonNull
94 public BranchCoverageData getBranchData(@NonNegative int line, @NonNegative int index) {
95 LineCoverageData lineData = lineToLineData.get(line);
96 return lineData.getBranchData(index);
97 }
98
99 public void markLastLineSegmentAsEmpty(@NonNegative int line) {
100 LineCoverageData lineData = lineToLineData.get(line);
101 lineData.markLastSegmentAsEmpty();
102 }
103
104 public boolean acceptsAdditionalCallPoints(@NonNegative int line) {
105 LineCoverageData lineData = getOrCreateLineData(line);
106 return lineData.acceptsAdditionalCallPoints();
107 }
108
109 @NonNegative
110 public int registerExecution(@NonNegative int line, @Nullable CallPoint callPoint) {
111 if (executionCounts == NO_EXECUTIONS_YET) {
112 executionCounts = new int[lastLine + 1];
113 }
114
115 int previousExecutionCount = executionCounts[line]++;
116
117 if (callPoint != null) {
118 LineCoverageData lineData = lineToLineData.get(line);
119 lineData.registerExecution(callPoint);
120 }
121
122 return previousExecutionCount;
123 }
124
125 public boolean hasValidBranch(@NonNegative int line, @NonNegative int branchIndex) {
126 LineCoverageData lineData = lineToLineData.get(line);
127 return lineData.isValidBranch(branchIndex);
128 }
129
130 public boolean acceptsAdditionalCallPoints(@NonNegative int line, @NonNegative int branchIndex) {
131 LineCoverageData lineData = lineToLineData.get(line);
132 return lineData.acceptsAdditionalCallPoints(branchIndex);
133 }
134
135 @NonNegative
136 public int registerExecution(@NonNegative int line, @NonNegative int branchIndex, @Nullable CallPoint callPoint) {
137 LineCoverageData lineData = lineToLineData.get(line);
138 return lineData.registerExecution(branchIndex, callPoint);
139 }
140
141 @NonNegative
142 public int getLineCount() {
143 return lastLine;
144 }
145
146 @NonNegative
147 public int getExecutableLineCount() {
148 return lineToLineData.size();
149 }
150
151 public boolean hasLineData(@NonNegative int line) {
152 return executionCounts != NO_EXECUTIONS_YET && lineToLineData.containsKey(line);
153 }
154
155 @NonNull
156 public LineCoverageData getLineData(@NonNegative int line) {
157 LineCoverageData data = lineToLineData.get(line);
158
159 if (data == null) {
160 data = sharedLineData;
161 }
162
163 data.setExecutionCount(executionCounts[line]);
164 return data;
165 }
166
167 public void markLineAsReachable(@NonNegative int line) {
168 LineCoverageData data = lineToLineData.get(line);
169
170 if (data != null) {
171 data.markAsReachable();
172 }
173 }
174
175 public int getExecutionCount(@NonNegative int line) {
176 return line < executionCounts.length ? executionCounts[line] : -1;
177 }
178
179 @Override
180 @NonNegative
181 public int getTotalItems() {
182 computeValuesIfNeeded();
183 return totalSegments;
184 }
185
186 @Override
187 @NonNegative
188 public int getCoveredItems() {
189 computeValuesIfNeeded();
190 return coveredSegments;
191 }
192
193 @Override
194 public int getCoveragePercentage() {
195 computeValuesIfNeeded();
196 return CoveragePercentage.calculate(coveredSegments, totalSegments);
197 }
198
199 private void computeValuesIfNeeded() {
200 if (totalSegments >= 0) {
201 return;
202 }
203 totalSegments = coveredSegments = 0;
204
205 for (int line = 1, n = lastLine; line <= n; line++) {
206 if (lineToLineData.containsKey(line)) {
207 LineCoverageData lineData = lineToLineData.get(line);
208 int executionCount = executionCounts == NO_EXECUTIONS_YET ? 0 : executionCounts[line];
209
210 if (lineData == null) {
211 totalSegments++;
212
213 if (executionCount > 0) {
214 coveredSegments++;
215 }
216 } else {
217 lineData.setExecutionCount(executionCount);
218 totalSegments += lineData.getNumberOfSegments();
219 coveredSegments += lineData.getNumberOfCoveredSegments();
220 }
221 }
222 }
223 }
224
225 @NonNegative
226 public int getNumberOfSegments(@NonNegative int line) {
227 if (!lineToLineData.containsKey(line)) {
228 return 0;
229 }
230
231 LineCoverageData lineData = lineToLineData.get(line);
232 return lineData == null ? 1 : lineData.getNumberOfSegments();
233 }
234
235 @NonNegative
236 public int getNumberOfBranchingSourcesAndTargets(@NonNegative int line) {
237 LineCoverageData lineData = lineToLineData.get(line);
238
239 if (lineData == null) {
240 return 0;
241 }
242
243 return lineData.getNumberOfBranchingSourcesAndTargets();
244 }
245
246 public void mergeInformation(@NonNull PerFileLineCoverage previousCoverage) {
247 Map<Integer, LineCoverageData> previousInfo = previousCoverage.lineToLineData;
248 boolean previousRunHadLinesExecuted = previousCoverage.executionCounts.length > 0;
249
250 for (Entry<Integer, LineCoverageData> lineAndInfo : lineToLineData.entrySet()) {
251 Integer line = lineAndInfo.getKey();
252 LineCoverageData previousLineInfo = previousInfo.get(line);
253
254 if (previousLineInfo != null) {
255 LineCoverageData lineInfo = lineAndInfo.getValue();
256
257 if (lineInfo == null) {
258 lineInfo = new LineCoverageData();
259 lineAndInfo.setValue(lineInfo);
260 }
261
262 lineInfo.addCountsFromPreviousTestRun(previousLineInfo);
263
264 if (previousRunHadLinesExecuted) {
265 createExecutionCountsArrayIfNeeded(previousCoverage);
266 executionCounts[line] += previousCoverage.executionCounts[line];
267 }
268 }
269 }
270
271 for (Entry<Integer, LineCoverageData> lineAndInfo : previousInfo.entrySet()) {
272 Integer line = lineAndInfo.getKey();
273
274 if (!lineToLineData.containsKey(line)) {
275 LineCoverageData previousLineInfo = lineAndInfo.getValue();
276 lineToLineData.put(line, previousLineInfo);
277
278 if (previousRunHadLinesExecuted) {
279 createExecutionCountsArrayIfNeeded(previousCoverage);
280 executionCounts[line] = previousCoverage.executionCounts[line];
281 }
282 }
283 }
284 }
285
286 private void createExecutionCountsArrayIfNeeded(@NonNull PerFileLineCoverage previousCoverage) {
287 if (executionCounts == NO_EXECUTIONS_YET) {
288 executionCounts = new int[previousCoverage.executionCounts.length];
289 }
290 }
291 }