001 /*
002 * Cobertura - http://cobertura.sourceforge.net/
003 *
004 * Copyright (C) 2003 jcoverage ltd.
005 * Copyright (C) 2005 Mark Doliner
006 * Copyright (C) 2005 Nathan Wilson
007 *
008 * Cobertura is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License as published
010 * by the Free Software Foundation; either version 2 of the License,
011 * or (at your option) any later version.
012 *
013 * Cobertura is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with Cobertura; if not, write to the Free Software
020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
021 * USA
022 */
023
024 package net.sourceforge.cobertura.check;
025
026 import java.io.File;
027 import java.math.BigDecimal;
028 import java.util.HashMap;
029 import java.util.Iterator;
030 import java.util.Map;
031 import java.util.StringTokenizer;
032
033 import net.sourceforge.cobertura.coveragedata.ClassData;
034 import net.sourceforge.cobertura.coveragedata.CoverageDataFileHandler;
035 import net.sourceforge.cobertura.coveragedata.ProjectData;
036 import net.sourceforge.cobertura.util.Header;
037
038 import org.apache.log4j.Logger;
039 import org.apache.oro.text.regex.MalformedPatternException;
040 import org.apache.oro.text.regex.Pattern;
041 import org.apache.oro.text.regex.Perl5Compiler;
042 import org.apache.oro.text.regex.Perl5Matcher;
043
044 public class Main
045 {
046
047 private static final Logger logger = Logger.getLogger(Main.class);
048
049 final Perl5Matcher pm = new Perl5Matcher();
050
051 final Perl5Compiler pc = new Perl5Compiler();
052
053 /**
054 * The default CoverageRate needed for a class to pass the check.
055 */
056 CoverageRate minimumCoverageRate;
057
058 /**
059 * The keys of this map contain regular expression Patterns that
060 * match against classes. The values of this map contain
061 * CoverageRate objects that specify the minimum coverage rates
062 * needed for a class that matches the pattern.
063 */
064 Map minimumCoverageRates = new HashMap();
065
066 /**
067 * The keys of this map contain package names. The values of this
068 * map contain PackageCoverage objects that track the line and
069 * branch coverage values for a package.
070 */
071 Map packageCoverageMap = new HashMap();
072
073 double inRangeAndDivideByOneHundred(String coverageRateAsPercentage)
074 {
075 return inRangeAndDivideByOneHundred(Integer.valueOf(
076 coverageRateAsPercentage).intValue());
077 }
078
079 double inRangeAndDivideByOneHundred(int coverageRateAsPercentage)
080 {
081 if ((coverageRateAsPercentage >= 0)
082 && (coverageRateAsPercentage <= 100))
083 {
084 return (double)coverageRateAsPercentage / 100;
085 }
086 throw new IllegalArgumentException("The value "
087 + coverageRateAsPercentage
088 + "% is invalid. Percentages must be between 0 and 100.");
089 }
090
091 void setMinimumCoverageRate(String minimumCoverageRate)
092 throws MalformedPatternException
093 {
094 StringTokenizer tokenizer = new StringTokenizer(minimumCoverageRate,
095 ":");
096 this.minimumCoverageRates.put(pc.compile(tokenizer.nextToken()),
097 new CoverageRate(inRangeAndDivideByOneHundred(tokenizer
098 .nextToken()), inRangeAndDivideByOneHundred(tokenizer
099 .nextToken())));
100 }
101
102 /**
103 * This method returns the CoverageRate object that
104 * applies to the given class. If checks if there is a
105 * pattern that matches the class name, and returns that
106 * if it finds one. Otherwise it uses the global minimum
107 * rates that were passed in.
108 */
109 CoverageRate findMinimumCoverageRate(String classname)
110 {
111 Iterator iter = this.minimumCoverageRates.entrySet().iterator();
112 while (iter.hasNext())
113 {
114 Map.Entry entry = (Map.Entry)iter.next();
115
116 if (pm.matches(classname, (Pattern)entry.getKey()))
117 {
118 return (CoverageRate)entry.getValue();
119 }
120 }
121 return this.minimumCoverageRate;
122 }
123
124 public Main(String[] args) throws MalformedPatternException
125 {
126 int exitStatus = 0;
127
128 Header.print(System.out);
129
130 File dataFile = CoverageDataFileHandler.getDefaultDataFile();
131 double branchCoverageRate = 0.0;
132 double lineCoverageRate = 0.0;
133 double packageBranchCoverageRate = 0.0;
134 double packageLineCoverageRate = 0.0;
135 double totalBranchCoverageRate = 0.0;
136 double totalLineCoverageRate = 0.0;
137
138 for (int i = 0; i < args.length; i++)
139 {
140 if (args[i].equals("--branch"))
141 {
142 branchCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
143 }
144 else if (args[i].equals("--datafile"))
145 {
146 dataFile = new File(args[++i]);
147 }
148 else if (args[i].equals("--line"))
149 {
150 lineCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
151 }
152 else if (args[i].equals("--regex"))
153 {
154 setMinimumCoverageRate(args[++i]);
155 }
156 else if (args[i].equals("--packagebranch"))
157 {
158 packageBranchCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
159 }
160 else if (args[i].equals("--packageline"))
161 {
162 packageLineCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
163 }
164 else if (args[i].equals("--totalbranch"))
165 {
166 totalBranchCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
167 }
168 else if (args[i].equals("--totalline"))
169 {
170 totalLineCoverageRate = inRangeAndDivideByOneHundred(args[++i]);
171 }
172 }
173
174 ProjectData projectData = CoverageDataFileHandler
175 .loadCoverageData(dataFile);
176
177 if (projectData == null)
178 {
179 System.err.println("Error: Unable to read from data file "
180 + dataFile.getAbsolutePath());
181 System.exit(1);
182 }
183
184 // If they didn't specify any thresholds, then use some defaults
185 if ((branchCoverageRate == 0) && (lineCoverageRate == 0)
186 && (packageLineCoverageRate == 0)
187 && (packageBranchCoverageRate == 0)
188 && (totalLineCoverageRate == 0)
189 && (totalBranchCoverageRate == 0)
190 && (this.minimumCoverageRates.size() == 0))
191 {
192 branchCoverageRate = 0.5;
193 lineCoverageRate = 0.5;
194 packageBranchCoverageRate = 0.5;
195 packageLineCoverageRate = 0.5;
196 totalBranchCoverageRate = 0.5;
197 totalLineCoverageRate = 0.5;
198 }
199
200 this.minimumCoverageRate = new CoverageRate(lineCoverageRate,
201 branchCoverageRate);
202
203 double totalLines = 0;
204 double totalLinesCovered = 0;
205 double totalBranches = 0;
206 double totalBranchesCovered = 0;
207
208 Iterator iter = projectData.getClasses().iterator();
209 while (iter.hasNext())
210 {
211 ClassData classData = (ClassData)iter.next();
212 CoverageRate coverageRate = findMinimumCoverageRate(classData
213 .getName());
214
215 if (totalBranchCoverageRate > 0.0)
216 {
217 totalBranches += classData.getNumberOfValidBranches();
218 totalBranchesCovered += classData.getNumberOfCoveredBranches();
219 }
220
221 if (totalLineCoverageRate > 0.0)
222 {
223 totalLines += classData.getNumberOfValidLines();
224 totalLinesCovered += classData.getNumberOfCoveredLines();
225 }
226
227 PackageCoverage packageCoverage = getPackageCoverage(classData
228 .getPackageName());
229 if (packageBranchCoverageRate > 0.0)
230 {
231 packageCoverage.addBranchCount(classData
232 .getNumberOfValidBranches());
233 packageCoverage.addBranchCoverage(classData
234 .getNumberOfCoveredBranches());
235 }
236
237 if (packageLineCoverageRate > 0.0)
238 {
239 packageCoverage.addLineCount(classData.getNumberOfValidLines());
240 packageCoverage.addLineCoverage(classData
241 .getNumberOfCoveredLines());
242 }
243
244 logger.debug("Class " + classData.getName()
245 + ", line coverage rate: "
246 + percentage(classData.getLineCoverageRate())
247 + "%, branch coverage rate: "
248 + percentage(classData.getBranchCoverageRate()) + "%");
249
250 if (classData.getBranchCoverageRate() < coverageRate
251 .getBranchCoverageRate())
252 {
253 System.err.println(classData.getName()
254 + " failed check. Branch coverage rate of "
255 + percentage(classData.getBranchCoverageRate())
256 + "% is below "
257 + percentage(coverageRate.getBranchCoverageRate())
258 + "%");
259 exitStatus |= 2;
260 }
261
262 if (classData.getLineCoverageRate() < coverageRate
263 .getLineCoverageRate())
264 {
265 System.err.println(classData.getName()
266 + " failed check. Line coverage rate of "
267 + percentage(classData.getLineCoverageRate())
268 + "% is below "
269 + percentage(coverageRate.getLineCoverageRate()) + "%");
270 exitStatus |= 4;
271 }
272 }
273
274 exitStatus |= checkPackageCoverageLevels(packageBranchCoverageRate,
275 packageLineCoverageRate);
276
277 // Check the rates for the overal project
278 if ((totalBranches > 0)
279 && (totalBranchCoverageRate > (totalBranchesCovered / totalBranches)))
280 {
281 System.err
282 .println("Project failed check. "
283 + "Total branch coverage rate of "
284 + percentage(totalBranchesCovered / totalBranches)
285 + "% is below "
286 + percentage(totalBranchCoverageRate) + "%");
287 exitStatus |= 8;
288 }
289
290 if ((totalLines > 0)
291 && (totalLineCoverageRate > (totalLinesCovered / totalLines)))
292 {
293 System.err.println("Project failed check. "
294 + "Total line coverage rate of "
295 + percentage(totalLinesCovered / totalLines)
296 + "% is below " + percentage(totalLineCoverageRate) + "%");
297 exitStatus |= 16;
298 }
299
300 System.exit(exitStatus);
301 }
302
303 private PackageCoverage getPackageCoverage(String packageName)
304 {
305 PackageCoverage packageCoverage = (PackageCoverage)packageCoverageMap
306 .get(packageName);
307 if (packageCoverage == null)
308 {
309 packageCoverage = new PackageCoverage();
310 packageCoverageMap.put(packageName, packageCoverage);
311 }
312 return packageCoverage;
313 }
314
315 private int checkPackageCoverageLevels(double packageBranchCoverageRate,
316 double packageLineCoverageRate)
317 {
318 int exitStatus = 0;
319 Iterator iter = packageCoverageMap.entrySet().iterator();
320 while (iter.hasNext())
321 {
322 Map.Entry entry = (Map.Entry)iter.next();
323 String packageName = (String)entry.getKey();
324 PackageCoverage packageCoverage = (PackageCoverage)entry.getValue();
325
326 exitStatus |= checkPackageCoverage(packageBranchCoverageRate,
327 packageLineCoverageRate, packageName, packageCoverage);
328 }
329 return exitStatus;
330 }
331
332 private int checkPackageCoverage(double packageBranchCoverageRate,
333 double packageLineCoverageRate, String packageName,
334 PackageCoverage packageCoverage)
335 {
336 int exitStatus = 0;
337 double branchCoverage = packageCoverage.getBranchCoverage()
338 / packageCoverage.getBranchCount();
339 if ((packageCoverage.getBranchCount() > 0)
340 && (packageBranchCoverageRate > branchCoverage))
341 {
342 System.err.println("Package " + packageName
343 + " failed check. Package branch coverage rate of "
344 + percentage(branchCoverage) + "% is below "
345 + percentage(packageBranchCoverageRate) + "%");
346 exitStatus |= 32;
347 }
348
349 double lineCoverage = packageCoverage.getLineCoverage()
350 / packageCoverage.getLineCount();
351 if ((packageCoverage.getLineCount() > 0)
352 && (packageLineCoverageRate > lineCoverage))
353 {
354 System.err.println("Package " + packageName
355 + " failed check. Package line coverage rate of "
356 + percentage(lineCoverage) + "% is below "
357 + percentage(packageLineCoverageRate) + "%");
358 exitStatus |= 64;
359 }
360
361 return exitStatus;
362 }
363
364 private String percentage(double coverateRate)
365 {
366 BigDecimal decimal = new BigDecimal(coverateRate * 100);
367 return decimal.setScale(1, BigDecimal.ROUND_DOWN).toString();
368 }
369
370 public static void main(String[] args) throws MalformedPatternException
371 {
372 new Main(args);
373 }
374
375 }