View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.coverage.lines;
7   
8   import static java.util.Collections.emptyList;
9   
10  import edu.umd.cs.findbugs.annotations.NonNull;
11  import edu.umd.cs.findbugs.annotations.Nullable;
12  
13  import java.util.ArrayList;
14  import java.util.Collections;
15  import java.util.List;
16  
17  import mockit.asm.controlFlow.Label;
18  import mockit.coverage.CallPoint;
19  
20  import org.checkerframework.checker.index.qual.NonNegative;
21  
22  /**
23   * Coverage data gathered for a single executable line of code in a source file.
24   */
25  public final class LineCoverageData extends LineSegmentData {
26      private static final long serialVersionUID = -6233980722802474992L;
27  
28      // Static data:
29      @NonNull
30      private List<BranchCoverageData> branches;
31      @NonNegative
32      private transient int segments;
33  
34      LineCoverageData() {
35          branches = emptyList();
36      }
37  
38      @NonNegative
39      public int addBranchingPoint(@NonNull Label jumpSource, @NonNull Label jumpTarget) {
40          int initialIndex = branches.size();
41  
42          if (initialIndex == 0) {
43              branches = new ArrayList<>(4);
44          }
45  
46          branches.add(new BranchCoverageData(jumpSource));
47          branches.add(new BranchCoverageData(jumpTarget));
48          return initialIndex;
49      }
50  
51      void markLastSegmentAsEmpty() {
52          BranchCoverageData lastBranch = branches.get(branches.size() - 1);
53          lastBranch.markAsEmpty();
54      }
55  
56      private boolean noBranchesYet() {
57          return branches == Collections.<BranchCoverageData> emptyList();
58      }
59  
60      @NonNull
61      public BranchCoverageData getBranchData(@NonNegative int index) {
62          return branches.get(index);
63      }
64  
65      boolean acceptsAdditionalCallPoints(@NonNegative int branchIndex) {
66          BranchCoverageData data = branches.get(branchIndex);
67          return data.acceptsAdditionalCallPoints();
68      }
69  
70      @NonNegative
71      int registerExecution(@NonNegative int branchIndex, @Nullable CallPoint callPoint) {
72          BranchCoverageData data = branches.get(branchIndex);
73          return data.registerExecution(callPoint);
74      }
75  
76      public boolean containsBranches() {
77          return !noBranchesYet();
78      }
79  
80      @NonNull
81      public List<BranchCoverageData> getBranches() {
82          return branches;
83      }
84  
85      boolean isValidBranch(@NonNegative int branchIndex) {
86          return branches.get(branchIndex) != BranchCoverageData.INVALID;
87      }
88  
89      @NonNegative
90      public int getNumberOfSegments() {
91          int previouslyCounted = segments;
92  
93          if (previouslyCounted > 0) {
94              return previouslyCounted;
95          }
96  
97          int n = branches.size();
98          int count = 1;
99  
100         for (int targetBranchIndex = 1; targetBranchIndex < n; targetBranchIndex += 2) {
101             BranchCoverageData targetBranch = branches.get(targetBranchIndex);
102             int targetLine = targetBranch.getLine();
103 
104             if (targetLine > 0) {
105                 BranchCoverageData sourceBranch = branches.get(targetBranchIndex - 1);
106                 int sourceLine = sourceBranch.getLine();
107 
108                 if (targetLine == sourceLine) {
109                     count++;
110                 }
111 
112                 if (!targetBranch.isEmpty()) {
113                     count++;
114                 }
115             }
116         }
117 
118         segments = count;
119         return count;
120     }
121 
122     @NonNegative
123     public int getNumberOfCoveredSegments() {
124         int segmentsCovered = isCovered() ? 1 : 0;
125         int n = branches.size();
126 
127         if (n == 0) {
128             return segmentsCovered;
129         }
130 
131         for (int sourceBranchIndex = 0; sourceBranchIndex < n; sourceBranchIndex += 2) {
132             BranchCoverageData sourceBranch = branches.get(sourceBranchIndex);
133             BranchCoverageData targetBranch = branches.get(sourceBranchIndex + 1);
134 
135             if (sourceBranch.isCovered() && !targetBranch.isEmpty()) {
136                 segmentsCovered++;
137             }
138 
139             if (targetBranch.isCovered()) {
140                 int targetLine = targetBranch.getLine();
141 
142                 if (targetLine == sourceBranch.getLine()) {
143                     segmentsCovered++;
144                 }
145             }
146         }
147 
148         return segmentsCovered;
149     }
150 
151     @NonNegative
152     public int getNumberOfBranchingSourcesAndTargets() {
153         int n = branches.size();
154 
155         if (n == 0) {
156             return 0;
157         }
158 
159         int count = 0;
160 
161         for (int sourceBranchIndex = 0; sourceBranchIndex < n; sourceBranchIndex += 2) {
162             BranchCoverageData sourceBranch = branches.get(sourceBranchIndex);
163 
164             if (!sourceBranch.isEmpty()) {
165                 count++;
166             }
167 
168             count++;
169         }
170 
171         return count;
172     }
173 
174     @NonNegative
175     public int getNumberOfCoveredBranchingSourcesAndTargets() {
176         int n = branches.size();
177 
178         if (n == 0) {
179             return 0;
180         }
181 
182         int sourcesAndTargetsCovered = 0;
183 
184         for (int sourceBranchIndex = 0; sourceBranchIndex < n; sourceBranchIndex += 2) {
185             BranchCoverageData sourceBranch = branches.get(sourceBranchIndex);
186             BranchCoverageData targetBranch = branches.get(sourceBranchIndex + 1);
187 
188             if (sourceBranch.isCovered()) {
189                 sourcesAndTargetsCovered++;
190             }
191 
192             if (targetBranch.isCovered()) {
193                 int targetLine = targetBranch.getLine();
194 
195                 if (targetLine == sourceBranch.getLine()) {
196                     sourcesAndTargetsCovered++;
197                 }
198             }
199         }
200 
201         return sourcesAndTargetsCovered;
202     }
203 
204     void addCountsFromPreviousTestRun(@NonNull LineCoverageData previousData) {
205         addExecutionCountAndCallPointsFromPreviousTestRun(previousData);
206 
207         if (containsBranches()) {
208             for (int i = 0, n = branches.size(); i < n; i++) {
209                 BranchCoverageData segmentData = branches.get(i);
210                 BranchCoverageData previousSegmentData = previousData.branches.get(i);
211 
212                 segmentData.addExecutionCountAndCallPointsFromPreviousTestRun(previousSegmentData);
213             }
214         }
215     }
216 }