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;
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      /** The logger. */
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 }