View Javadoc
1 /* StmtRegistry.java */ 2 package org.quilt.cover.stmt; 3 4 import java.lang.reflect.Field; 5 import java.util.Iterator; 6 import java.util.Hashtable; 7 import java.util.Map; 8 import java.util.Set; 9 import org.quilt.cl.*; 10 import org.quilt.reg.*; 11 12 /*** 13 * <p>Registry for statement coverage information. As Quilt-instrumented 14 * classes are loaded, they register their <code>q$$q</code> hit count 15 * arrays and are assigned an ID unique in the life of the registry. 16 * The registry maintains: </p> 17 * <ul> 18 * <li><b>hit counts</b>, keyed on class name</li> 19 * <li><b>method end counter indexes</b>, keyed on class and method name</li> 20 * <li><b>line number ranges</b>, keyed on class and counter index</li> 21 * </ul> 22 * <p>This and other information in the registry allows it the generate 23 * a number of reports summarizing coverage at</p> 24 * <ul> 25 * <li><b>package</b> level (soon)</li> 26 * <li><b>class</b> level (now)</li> 27 * <li><b>method</b> level (now)</li> 28 * <li><b>line</b> level (soonish)</li> 29 * </ul> 30 * 31 * <p>The registry is associated with the Quilt class loader when it 32 * is created. Information can been retrieved from the registry at 33 * any time. It will be accumulated as new instances of Quilt-instrumented 34 * classes are run.</p> 35 * 36 * @author <a href="mailto:jddixon@users.sourceforge.net">Jim Dixon</a> 37 */ 38 public class StmtRegistry extends QuiltRegistry { 39 40 /*** XXX */ 41 private static StmtRegistry INSTANCE = null; 42 43 /*** Returns a reference to the latest instance to announce itself. */ 44 public static StmtRegistry getInstance () { 45 return INSTANCE; 46 } 47 48 /*** Maps class name to array of names of instrumented methods */ 49 private Map methodNames = new Hashtable(); // key className, value String[] 50 /*** Maps class name to array of index of last counters for each method */ 51 private Map methodEnds = new Hashtable(); // key className, value int[] 52 53 /*** 54 * Constructor specifying Quilt class loader the registry is 55 * associated with. 56 */ 57 public StmtRegistry (QuiltClassLoader qcl) { 58 super(qcl); 59 INSTANCE = this; // XXX the horror 60 ClassAction classAct = new ClassAction(this); 61 62 cxf = new ClassXformer[] { classAct }; 63 mxf = new MethodXformer[] { new MethodAction(this) }; 64 gxf = new GraphXformer[] { new GraphAction(this, classAct) }; 65 setTransformers(); 66 } 67 68 /*** 69 * Clear counters associated with registry entries. 70 */ 71 public void reset() { 72 // XXX DO NOTHING FOR NOW XXX 73 } 74 75 /*** 76 * Dump the registry as plain text. 77 * @todo More elaborate reports. 78 */ 79 public String getReport() { 80 StringBuffer sb = new StringBuffer() 81 .append("\n=========================\n") 82 .append( " QUILT COVERAGE REPORT \n"); 83 if (isEmpty()) { 84 sb.append("* the registry is empty *\n"); 85 } else { 86 Set keys = keySet(); 87 Iterator i = keys.iterator(); 88 while (i.hasNext()) { 89 // class --> hit count arrays 90 String[] name = (String[]) i.next(); 91 int [] counts = (int[])get(name); 92 int count = counts.length; 93 String className = name[0]; 94 95 sb.append(className + ": " + count + " counters, " 96 + getClassCoverage(className) + "% coverage\n "); 97 // // DEBUG ONLY: DUMP COUNTER ARRAY 98 // for (int k = 0; k < count; k++) { 99 // sb.append(" " + counts[k]); 100 // } 101 // sb.append("\n"); 102 // // END 103 104 String [] methods = (String[])methodNames.get(className); 105 if (methods == null) { 106 sb.append(" NULL\n"); 107 } else { 108 // there can be more than one <init> method :-( 109 for (int k = 0; k < methods.length; k++) { 110 sb.append(" ").append(methods[k]) 111 .append(" ").append(getMethodCoverage(className,k)) 112 .append("% coverage\n"); 113 } 114 } 115 } 116 } 117 sb.append("=========================\n"); 118 return sb.toString(); 119 } 120 121 /*** 122 * Get the percentage of counters in the class that have counts 123 * greater than zero. The percentage is rounded down. If no 124 * class information is found, returns zero. 125 * 126 * @return an integer between 0 and 100, zero if class not found 127 */ 128 int getClassCoverage (String className) { 129 int nonZero = 0; 130 int[] hitCounts = (int[]) get(new String[] {className}); 131 if (hitCounts != null) { 132 for (int k = 0; k < hitCounts.length; k++) { 133 if (hitCounts[k] > 0) { 134 nonZero++; 135 } 136 } 137 nonZero = (nonZero*100)/hitCounts.length; 138 } 139 return nonZero; 140 } 141 142 /*** 143 * Get the percentage of counters in the Nth method that have counts 144 * greater than zero. The percentage is rounded down. If no 145 * class or method information is found, returns zero. 146 * 147 * XXX The 'methodEnds' array actually contains cumulative counts, 148 * so values must be reduced by one. 149 * 150 * @return an integer between 0 and 100, zero if class not found 151 */ 152 int getMethodCoverage (String className, int n) { 153 int nonZero = 0; 154 // XXX should report if either of these two is null or if 155 // cardinalities differ 156 int[] hitCounts = (int[]) get(new String[] {className}); 157 int[] ends = (int[]) methodEnds.get(className); 158 if ( n < 0 || n >= hitCounts.length ) { 159 throw new IllegalArgumentException("index out of range"); 160 } 161 int counterCount = 0; 162 int lastCounter = ends[n] -1 ; 163 int firstCounter = n == 0 ? 0 : ends[n - 1]; 164 if (hitCounts != null && ends != null) { 165 for (int k = firstCounter; k <= lastCounter; k++) { 166 counterCount++; 167 if (hitCounts[k] > 0) { 168 nonZero++; 169 } 170 } 171 if (counterCount > 0) { 172 nonZero = (nonZero*100)/counterCount; 173 } 174 } 175 return nonZero; 176 } 177 178 // GET/PUT METHODS ////////////////////////////////////////////// 179 // CLASSID //////////////////////////////////////////// 180 private static int nextClassID = 0; 181 182 public int getClassID (String className) { 183 int classID = -1; 184 try { 185 Field qField = Class.forName(className).getField("q$$qID"); 186 // a trifle uncertain about the argument 187 classID = qField.getInt(qField); 188 } catch (Exception e) { 189 // just ignore any errors 190 // DEBUG 191 System.out.println("StmtRegistry.getClassID(" 192 + className + ") failed - " + e); 193 // END 194 } 195 return classID; 196 } 197 // HIT COUNTS ///////////////////////////////////////// 198 /*** 199 * Get a reference to the hit count array for a class. 200 */ 201 public int [] getCounts (String className) { 202 int[] counts = null; 203 try { 204 counts = (int[])get ( new String[] {className} ); 205 } catch (Exception e) { 206 // just ignore any errors 207 System.out.println("StmtRegistry.getCounts (" 208 + className + ") failed - " + e); 209 } 210 return counts; 211 } 212 /*** 213 * Register a class by passing a reference to its integer hit count 214 * array. Returns a class ID which is unique within this run. The 215 * ID will be stored in a static in the class, public static int q$$qID 216 * 217 * XXX So far absolutely no value to using String array as key; could 218 * just be String. 219 * 220 * @param className Name of the class being registered. 221 * @param counts Reference to the class's public static hit count array. 222 * @return A unique class ID on success, -1 on failure. 223 */ 224 225 public int registerCounts (String className, int [] counts) { 226 int classID = -1; 227 try { 228 put ( new String[] {className} , counts ); 229 classID = nextClassID++; 230 } catch (Exception e) { 231 // just ignore any errors 232 System.out.println("StmtRegistry.registerCounts for " 233 + className + ", q$$q) failed - " + e); 234 } 235 return classID; 236 } 237 238 public void registerMethods (String className, String [] methods, 239 int [] endCounts) { 240 if (className == null || methods == null || endCounts == null) { 241 throw new IllegalArgumentException("null parameter"); 242 } 243 methodNames.put ( className, methods ); 244 methodEnds.put ( className, endCounts ); 245 } 246 // VERSION //////////////////////////////////////////// 247 public int getQuiltVersion (String className) { 248 int quiltVersion = -1; 249 try { 250 Field qField = Class.forName(className).getField("q$$qVer"); 251 // a trifle uncertain about the argument 252 quiltVersion = qField.getInt(qField); 253 } catch (Exception e) { 254 // just ignore any errors 255 // DEBUG 256 System.out.println("StmtRegistry.getClassID(" 257 + className + ") failed - " + e); 258 // END 259 } 260 return quiltVersion; 261 } 262 // EPHEMERA /////////////////////////////////////////// 263 /*** Maps class name to temporary data structure */ 264 private Map ephemera = new Hashtable(); // key className 265 266 /*** get reference to temporary data for class */ 267 Ephemera getEphemera (String className) { 268 if (ephemera.containsKey(className)) { 269 return (Ephemera) ephemera.get(className); 270 } else { 271 return null; 272 } 273 } 274 /*** add temporary data for class to registry */ 275 boolean putEphemera (String className, Ephemera eph) { 276 if (ephemera.containsKey(className)) { 277 return false; // operation failed 278 } 279 // XXX Should allow for failure 280 ephemera.put(className, eph); 281 return true; 282 } 283 /*** 284 * Remove the reference from the registry. 285 * 286 * @param className Name of the class we are storing information about 287 * @return The reference or null if none was found or null was the 288 * stored value. 289 */ 290 Ephemera removeEphemera (String className) { 291 return (Ephemera) ephemera.remove(className); 292 } 293 // EXPERIMENT /////////////////////////////////////////////////// 294 /*** STUB */ 295 public int registerClass(String name, org.quilt.cover.stmt.QIC junk) { 296 System.out.println( 297 "**************************" 298 + "\nQCL.registerClass " + name 299 + "\n**************************" ); 300 return 52; // <================================= 301 } 302 303 }

This page was automatically generated by Maven