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