1 package org.hansel; 2 3 import java.io.File ; 4 import java.io.FileFilter ; 5 import java.io.FilenameFilter ; 6 import java.lang.reflect.Method ; 7 import java.lang.reflect.Modifier ; 8 import java.text.NumberFormat ; 9 import java.util.Comparator ; 10 import java.util.Enumeration ; 11 import java.util.HashSet ; 12 import java.util.Iterator ; 13 import java.util.Set ; 14 import java.util.Stack ; 15 import java.util.StringTokenizer ; 16 import java.util.TreeSet ; 17 18 import junit.framework.Test; 19 import junit.framework.TestSuite; 20 21 import org.apache.tools.ant.AntClassLoader; 22 import org.apache.tools.ant.BuildException; 23 import org.apache.tools.ant.Task; 24 import org.apache.tools.ant.types.Path; 25 import org.apache.tools.ant.types.Reference; 26 27 public class AntCoverageTask extends Task { 28 private static final String SUITE_METHOD = "suite"; 29 private static final Class [] SUITE_TYPES = new Class [0]; 30 private static final Object [] SUITE_PARAMS = new Object [0]; 31 32 private static final FileFilter DIR_FILTER = 33 new FileFilter () { 34 public boolean accept(File path) { 35 return path.isDirectory(); 36 } 37 }; 38 39 private static final FilenameFilter CLASS_FILTER = 40 new FilenameFilter () { 41 public boolean accept(File dir, String name) { 42 return (name.endsWith(".class") 43 && (name.indexOf("$") < 0)); 44 } 45 }; 46 47 private String packages; 48 49 private Path classpath; 50 private String classFilePath; 51 52 private String excludePackages; 53 54 private boolean printStats; 55 56 private ClassLoader cl; 57 private ClassLoader oldCl; 58 59 private int numTests; 60 private int numCoverageTests; 61 62 public AntCoverageTask() { 63 numTests = 0; 64 numCoverageTests = 0; 65 printStats = true; 66 } 67 68 73 public void setClasspath(Path s) { 74 createClasspath().append(s); 75 } 76 77 80 public Path createClasspath() { 81 classpath = new Path(getProject()); 82 return classpath; 83 } 84 85 88 public void setClasspathRef(Reference r) { 89 createClasspath().setRefid(r); 90 } 91 92 public void setStats(boolean printStats) { 93 this.printStats = printStats; 94 } 95 96 public void setClassFilePath(String classFilePath) { 97 this.classFilePath = classFilePath; 98 } 99 100 public void setPackages(String packages) { 101 this.packages = packages; 102 } 103 104 public void setExcludePackages(String excludePackages) { 105 this.excludePackages = excludePackages; 106 } 107 108 private void createClassloader() throws BuildException { 109 Path path = new Path(this.getProject()); 110 path.setLocation(new File (classFilePath)); 111 112 if (classpath != null) { 113 classpath.append(path); 114 } else { 115 classpath = path; 116 } 117 118 cl = new AntClassLoader(getClass().getClassLoader(), 119 getProject(), classpath, true); 120 121 oldCl = Thread.currentThread().getContextClassLoader(); 122 Thread.currentThread().setContextClassLoader(cl); 123 } 124 125 private void resetClassLoader() { 126 Thread.currentThread().setContextClassLoader(oldCl); 127 } 128 129 private Set createSortedSet() { 130 return new TreeSet (new Comparator () { 131 public int compare(Object o1, Object o2) { 132 return o1.toString().compareTo(o2.toString()); 133 } 134 135 public boolean equals(Object obj) { 136 return false; 137 } 138 }); 139 } 140 141 private String normalize(String packageName) { 142 while (packageName.endsWith("*")) { 143 packageName = packageName.substring(0, packageName.length() - 1); 144 } 145 146 if (packageName.endsWith(File.separator)) { 147 packageName = packageName.substring(0, packageName.length() - 1); 148 } 149 150 return packageName; 151 } 152 153 private File getPackageFile(String packageName) throws BuildException { 154 packageName = classFilePath + File.separator + packageName; 155 return testDirectory("Package", packageName); 156 } 157 158 private void findClasses(String packageName, Set <Class > addTo) { 159 File packageFile = getPackageFile(packageName); 160 File [] classes = packageFile.listFiles(CLASS_FILTER); 161 162 for (int i=0; i<classes.length; i++) { 163 String name = classes[i].getName(); 164 String classname = 165 packageName.replace(File.separatorChar, '.') 166 + "." 167 + name.substring(0, name.length() - 6); 168 try { 169 addTo.add(cl.loadClass(classname)); 170 } catch (ClassNotFoundException cnfe) { 171 throw new BuildException(cnfe); 172 } 173 } 174 175 } 176 177 private Set <String > createPackageSet(String path, 178 String packageNames) { 179 HashSet <String > result = new HashSet <String >(); 180 181 if (packageNames == null) { 182 return result; 183 } 184 185 packageNames = packageNames.replace('.', 186 File.separatorChar); 187 Stack <String > stack = new Stack <String >(); 188 StringTokenizer st = new StringTokenizer (packageNames, ","); 189 while (st.hasMoreTokens()) { 190 stack.push(st.nextToken().trim()); 191 } 192 193 while (!stack.isEmpty()) { 194 String packageName = (String ) stack.pop(); 195 boolean recurse = packageName.endsWith("*"); 196 197 packageName = normalize(packageName); 198 File packageFile = getPackageFile(packageName); 199 200 result.add(packageName); 201 202 if (recurse) { 203 File [] subdirs = packageFile.listFiles(DIR_FILTER); 204 for (int i=0; i<subdirs.length; i++) { 205 stack.push(packageName 206 + File.separator 207 + subdirs[i].getName() 208 + "*"); 209 } 210 } 211 } 212 213 return result; 214 } 215 216 217 private Set getClasses(Set packages) { 218 Set result = createSortedSet(); 219 220 Iterator it = packages.iterator(); 221 222 while (it.hasNext()) { 223 String packageName = (String ) it.next(); 224 findClasses(packageName, result); 225 } 226 227 return result; 228 } 229 230 private void addTest(Set <String > testClasses, 231 Set <Class > resultSet, 232 Test test) throws ClassNotFoundException { 233 String className = test.getClass().getName(); 234 if (testClasses.contains(className)) { 235 return; 236 } 237 238 if (!(className.startsWith(TestSuite.class.getName()) 241 || className.startsWith(CoverageDecorator.class.getName()))) { 242 numTests++; 243 testClasses.add(className); 244 } 245 246 if (test instanceof TestSuite) { 247 Enumeration e = ((TestSuite) test).tests(); 248 while (e.hasMoreElements()) { 249 addTest(testClasses, resultSet, 250 (Test) e.nextElement()); 251 } 252 } 253 254 if (test instanceof CoverageDecorator) { 255 numCoverageTests++; 256 Class [] classes = ((CoverageDecorator) test).getClassesCovered(); 257 for (int i=0; i<classes.length; i++) { 258 resultSet.add(classes[i]); 259 } 260 } 261 } 262 263 private Set getClassesCovered(Set <String > classSet) throws ClassNotFoundException { 264 Set result = createSortedSet(); 265 Iterator it = classSet.iterator(); 266 267 while (it.hasNext()) { 268 Set testClasses = new HashSet (); 269 270 Class next = (Class ) it.next(); 271 if (!next.isInterface() 272 && ((next.getModifiers() & Modifier.ABSTRACT) == 0) 273 && Test.class.isAssignableFrom(next)) { 274 275 try { 276 Method suitMethod = next.getMethod(SUITE_METHOD, 277 SUITE_TYPES); 278 try { 279 addTest(testClasses, 280 result, 281 (Test) suitMethod.invoke(null, SUITE_PARAMS)); 282 } catch (Exception e) { 283 throw new BuildException(e); 284 } 285 286 } catch (NoSuchMethodException nsme) { 287 Enumeration e = new TestSuite(next).tests(); 290 while (e.hasMoreElements()) { 291 addTest(testClasses, result, 292 (Test) e.nextElement()); 293 } 294 } 295 296 } 297 } 298 299 return result; 300 } 301 302 private Set getClassesToCover(Set <Class > classes) { 303 Set <Class > result = createSortedSet(); 304 305 Iterator <Class > it = classes.iterator(); 306 307 while (it.hasNext()) { 308 Class next = (Class ) it.next(); 309 310 if (!next.isInterface() 311 && !Test.class.isAssignableFrom(next)) { 312 result.add(next); 313 } 314 } 315 316 return result; 317 } 318 319 private File testDirectory(String name, 320 String path) throws BuildException { 321 if (path == null) { 322 throw new BuildException("'" + 323 name 324 + "' property has to be set."); 325 } 326 327 File dir = new File (path); 328 if (!(dir.exists() && dir.isDirectory())) { 329 throw new BuildException(name 330 + " directory: '" 331 + path 332 + "' does not exist."); 333 } 334 335 return dir; 336 } 337 338 public void execute() throws BuildException { 339 try { 340 testDirectory("ClassFilePath", classFilePath); 341 createClassloader(); 342 343 Set packageSet = createPackageSet(classFilePath, 344 packages); 345 Set excludePackageSet = createPackageSet(classFilePath, 346 excludePackages); 347 348 Set allClasses = getClasses(packageSet); 349 int numClasses = allClasses.size(); 350 351 Set classesCovered = getClassesCovered(allClasses); 352 353 packageSet.removeAll(excludePackageSet); 354 355 Set classes = getClasses(packageSet); 356 Set classesToCover = getClassesToCover(classes); 357 358 int numClassesToCover = classesToCover.size(); 359 360 classesToCover.removeAll(classesCovered); 361 362 Iterator it = classesToCover.iterator(); 363 while (it.hasNext()) { 364 handleErrorOutput("Class not covered by a coverage test: '" 365 + ((Class ) it.next()).getName() + "'"); 366 } 367 368 int numUncoveredClasses = classesToCover.size(); 369 int numCoveredClasses = (numClassesToCover - numUncoveredClasses); 370 371 if (printStats) { 372 handleOutput(""); 373 handleOutput("Coverage Statistics"); 374 handleOutput("--------------------"); 375 handleOutput("Classes: " 376 + Util.leftFill(5, "" + numClasses)); 377 handleOutput("Classes requiring coverage: " 378 + Util.leftFill(5, "" + numClassesToCover)); 379 handleOutput("Testcases: " 380 + Util.leftFill(5, "" + numTests)); 381 handleOutput("Coverage Testcases: " 382 + Util.leftFill(5, "" + numCoverageTests)); 383 handleOutput("Classes covered: " 384 + Util.leftFill(5, "" + numCoveredClasses)); 385 NumberFormat nf = NumberFormat.getPercentInstance(); 386 String p = nf.format((double) numCoveredClasses 387 / (double) numClassesToCover); 388 handleOutput("Percentage covered: " 389 + Util.leftFill(5, p)); 390 } 391 392 resetClassLoader(); 393 } catch (Throwable t) { 394 t.printStackTrace(); 395 throw new BuildException(t); 396 } 397 398 } 399 400 } 401 | Popular Tags |