View Javadoc
1   /*
2    * Copyright (c) 2006 JMockit developers
3    * This file is subject to the terms of the MIT license (see LICENSE.txt).
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      // Computed on demand:
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              // Happens for source files with multiple types, where one is only loaded
67              // after another has already executed some code.
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 }