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