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.dataItems;
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.ArrayList;
14  import java.util.LinkedHashMap;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Map.Entry;
18  
19  import mockit.coverage.CoveragePercentage;
20  import mockit.coverage.data.PerFileCoverage;
21  
22  import org.checkerframework.checker.index.qual.NonNegative;
23  
24  public final class PerFileDataCoverage implements PerFileCoverage {
25      private static final long serialVersionUID = -4561686103982673490L;
26  
27      @NonNull
28      public final List<String> allFields = new ArrayList<>(2);
29      @NonNull
30      public final Map<String, StaticFieldData> staticFieldsData = new LinkedHashMap<>();
31      @NonNull
32      public final Map<String, InstanceFieldData> instanceFieldsData = new LinkedHashMap<>();
33  
34      private transient int coveredDataItems = -1;
35  
36      private void readObject(@NonNull ObjectInputStream in) throws IOException, ClassNotFoundException {
37          coveredDataItems = -1;
38          in.defaultReadObject();
39      }
40  
41      public void addField(@NonNull String className, @NonNull String fieldName, boolean isStatic) {
42          String classAndField = className + '.' + fieldName;
43  
44          if (!allFields.contains(classAndField)) {
45              allFields.add(classAndField);
46          }
47  
48          if (isStatic) {
49              staticFieldsData.put(classAndField, new StaticFieldData());
50          } else {
51              instanceFieldsData.put(classAndField, new InstanceFieldData());
52          }
53      }
54  
55      public boolean isFieldWithCoverageData(@NonNull String classAndFieldNames) {
56          return instanceFieldsData.containsKey(classAndFieldNames) || staticFieldsData.containsKey(classAndFieldNames);
57      }
58  
59      public void registerAssignmentToStaticField(@NonNull String classAndFieldNames) {
60          StaticFieldData staticData = getStaticFieldData(classAndFieldNames);
61  
62          if (staticData != null) {
63              staticData.registerAssignment();
64          }
65      }
66  
67      @Nullable
68      public StaticFieldData getStaticFieldData(@NonNull String classAndFieldNames) {
69          return staticFieldsData.get(classAndFieldNames);
70      }
71  
72      public void registerReadOfStaticField(@NonNull String classAndFieldNames) {
73          StaticFieldData staticData = getStaticFieldData(classAndFieldNames);
74  
75          if (staticData != null) {
76              staticData.registerRead();
77          }
78      }
79  
80      public void registerAssignmentToInstanceField(@NonNull Object instance, @NonNull String classAndFieldNames) {
81          InstanceFieldData instanceData = getInstanceFieldData(classAndFieldNames);
82  
83          if (instanceData != null) {
84              instanceData.registerAssignment(instance);
85          }
86      }
87  
88      @Nullable
89      public InstanceFieldData getInstanceFieldData(@NonNull String classAndFieldNames) {
90          return instanceFieldsData.get(classAndFieldNames);
91      }
92  
93      public void registerReadOfInstanceField(@NonNull Object instance, @NonNull String classAndFieldNames) {
94          InstanceFieldData instanceData = getInstanceFieldData(classAndFieldNames);
95  
96          if (instanceData != null) {
97              instanceData.registerRead(instance);
98          }
99      }
100 
101     public boolean hasFields() {
102         return !allFields.isEmpty();
103     }
104 
105     public boolean isCovered(@NonNull String classAndFieldNames) {
106         InstanceFieldData instanceData = getInstanceFieldData(classAndFieldNames);
107 
108         if (instanceData != null && instanceData.isCovered()) {
109             return true;
110         }
111 
112         StaticFieldData staticData = getStaticFieldData(classAndFieldNames);
113 
114         return staticData != null && staticData.isCovered();
115     }
116 
117     @Override
118     @NonNegative
119     public int getTotalItems() {
120         return staticFieldsData.size() + instanceFieldsData.size();
121     }
122 
123     @Override
124     @NonNegative
125     public int getCoveredItems() {
126         if (coveredDataItems >= 0) {
127             return coveredDataItems;
128         }
129 
130         coveredDataItems = 0;
131 
132         for (StaticFieldData staticData : staticFieldsData.values()) {
133             if (staticData.isCovered()) {
134                 coveredDataItems++;
135             }
136         }
137 
138         for (InstanceFieldData instanceData : instanceFieldsData.values()) {
139             if (instanceData.isCovered()) {
140                 coveredDataItems++;
141             }
142         }
143 
144         return coveredDataItems;
145     }
146 
147     @Override
148     public int getCoveragePercentage() {
149         int totalFields = getTotalItems();
150 
151         if (totalFields == 0) {
152             return -1;
153         }
154 
155         int coveredFields = getCoveredItems();
156         return CoveragePercentage.calculate(coveredFields, totalFields);
157     }
158 
159     public void mergeInformation(@NonNull PerFileDataCoverage previousInfo) {
160         addInfoFromPreviousTestRun(staticFieldsData, previousInfo.staticFieldsData);
161         addFieldsFromPreviousTestRunIfAbsent(staticFieldsData, previousInfo.staticFieldsData);
162 
163         addInfoFromPreviousTestRun(instanceFieldsData, previousInfo.instanceFieldsData);
164         addFieldsFromPreviousTestRunIfAbsent(instanceFieldsData, previousInfo.instanceFieldsData);
165     }
166 
167     private static <FI extends FieldData> void addInfoFromPreviousTestRun(@NonNull Map<String, FI> currentInfo,
168             @NonNull Map<String, FI> previousInfo) {
169         for (Entry<String, FI> nameAndInfo : currentInfo.entrySet()) {
170             String fieldName = nameAndInfo.getKey();
171             FieldData previousFieldInfo = previousInfo.get(fieldName);
172 
173             if (previousFieldInfo != null) {
174                 FieldData fieldInfo = nameAndInfo.getValue();
175                 fieldInfo.addCountsFromPreviousTestRun(previousFieldInfo);
176             }
177         }
178     }
179 
180     private static <FI extends FieldData> void addFieldsFromPreviousTestRunIfAbsent(
181             @NonNull Map<String, FI> currentInfo, @NonNull Map<String, FI> previousInfo) {
182         for (Entry<String, FI> nameAndInfo : previousInfo.entrySet()) {
183             String fieldName = nameAndInfo.getKey();
184             currentInfo.putIfAbsent(fieldName, previousInfo.get(fieldName));
185         }
186     }
187 }