1
2
3
4
5 package mockit.coverage;
6
7 import edu.umd.cs.findbugs.annotations.NonNull;
8 import edu.umd.cs.findbugs.annotations.Nullable;
9
10 import java.io.File;
11 import java.io.IOException;
12 import java.nio.file.Path;
13 import java.util.ArrayList;
14 import java.util.List;
15 import java.util.regex.Pattern;
16
17 import mockit.coverage.data.CoverageData;
18
19 import org.checkerframework.checker.index.qual.NonNegative;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 final class CoverageCheck {
24
25
26 private static final Logger logger = LoggerFactory.getLogger(CoverageCheck.class);
27
28 private static final String configuration = Configuration.getProperty("check", "");
29
30 @Nullable
31 static CoverageCheck createIfApplicable() {
32 return configuration.isEmpty() ? null : new CoverageCheck();
33 }
34
35 private static final class Threshold {
36 private static final Pattern PARAMETER_SEPARATORS = Pattern.compile(":|=");
37
38 @Nullable
39 private final String sourceFilePrefix;
40 @NonNull
41 private final String scopeDescription;
42 @NonNegative
43 private int minPercentage;
44
45 Threshold(@NonNull String configurationParameter) {
46 String[] sourceFilePrefixAndMinPercentage = PARAMETER_SEPARATORS.split(configurationParameter);
47 String textualPercentage;
48
49 if (sourceFilePrefixAndMinPercentage.length == 1) {
50 sourceFilePrefix = null;
51 scopeDescription = "";
52 textualPercentage = sourceFilePrefixAndMinPercentage[0];
53 } else {
54 String scope = sourceFilePrefixAndMinPercentage[0].trim();
55
56 if (isPerFile(scope)) {
57 sourceFilePrefix = scope;
58 scopeDescription = " for some source files";
59 } else {
60 sourceFilePrefix = scope.replace('.', '/');
61 scopeDescription = " for " + scope;
62 }
63
64 textualPercentage = sourceFilePrefixAndMinPercentage[1];
65 }
66
67 try {
68 minPercentage = Integer.parseInt(textualPercentage.trim());
69 } catch (NumberFormatException ignore) {
70 }
71 }
72
73 private static boolean isPerFile(@Nullable String scope) {
74 return "perFile".equalsIgnoreCase(scope);
75 }
76
77 boolean verifyMinimum() {
78 CoverageData coverageData = CoverageData.instance();
79 int percentage;
80
81 if (isPerFile(sourceFilePrefix)) {
82 percentage = coverageData.getSmallestPerFilePercentage();
83 } else {
84 percentage = coverageData.getPercentage(sourceFilePrefix);
85 }
86
87 return percentage < 0 || verifyMinimum(percentage);
88 }
89
90 private boolean verifyMinimum(@NonNegative int percentage) {
91 if (percentage < minPercentage) {
92 logger.info("JMockit: coverage too low {}: {}% < {}%", scopeDescription, percentage, minPercentage);
93 return false;
94 }
95
96 return true;
97 }
98 }
99
100 @NonNull
101 private final List<Threshold> thresholds;
102 private boolean allThresholdsSatisfied;
103
104 private CoverageCheck() {
105 String[] configurationParameters = configuration.split(";");
106 int n = configurationParameters.length;
107 thresholds = new ArrayList<>(n);
108
109 for (String configurationParameter : configurationParameters) {
110 thresholds.add(new Threshold(configurationParameter));
111 }
112 }
113
114 void verifyThresholds() {
115 allThresholdsSatisfied = true;
116
117 for (Threshold threshold : thresholds) {
118 allThresholdsSatisfied &= threshold.verifyMinimum();
119 }
120
121 createOrDeleteIndicatorFile();
122
123 if (!allThresholdsSatisfied) {
124 throw new AssertionError("JMockit: minimum coverage percentages not reached; see previous messages.");
125 }
126 }
127
128 @SuppressWarnings("ResultOfMethodCallIgnored")
129 private void createOrDeleteIndicatorFile() {
130 String parentDir = Configuration.getOrChooseOutputDirectory("");
131 File indicatorFile = Path.of(parentDir, "coverage.check.failed").toFile();
132
133 if (indicatorFile.exists()) {
134 if (allThresholdsSatisfied) {
135 indicatorFile.delete();
136 } else {
137 indicatorFile.setLastModified(System.currentTimeMillis());
138 }
139 } else if (!allThresholdsSatisfied) {
140 try {
141 indicatorFile.createNewFile();
142 } catch (IOException e) {
143 throw new RuntimeException(e);
144 }
145 }
146 }
147 }