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 }