001 /* 002 * Cobertura - http://cobertura.sourceforge.net/ 003 * 004 * Copyright (C) 2003 jcoverage ltd. 005 * Copyright (C) 2005 Mark Doliner <thekingant@users.sourceforge.net> 006 * 007 * Cobertura is free software; you can redistribute it and/or modify 008 * it under the terms of the GNU General Public License as published 009 * by the Free Software Foundation; either version 2 of the License, 010 * or (at your option) any later version. 011 * 012 * Cobertura is distributed in the hope that it will be useful, but 013 * WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU General Public License 018 * along with Cobertura; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 020 * USA 021 */ 022 023 package net.sourceforge.cobertura.coverage; 024 025 import java.io.Serializable; 026 import java.util.Collections; 027 import java.util.HashMap; 028 import java.util.HashSet; 029 import java.util.Iterator; 030 import java.util.Map; 031 import java.util.Set; 032 033 /** 034 * <p> 035 * CoverageData information is typically serialized to a file. An 036 * instance of this class records coverage information for a single 037 * class that has been instrumented. 038 * </p> 039 * 040 * <p> 041 * This class implements HasBeenInstrumented so that when cobertura 042 * instruments itself, it will omit this class. It does this to 043 * avoid an infinite recursion problem because instrumented classes 044 * make use of this class. 045 * </p> 046 */ 047 public class CoverageData implements HasBeenInstrumented, Serializable 048 { 049 050 private static final long serialVersionUID = 2; 051 052 /** 053 * Each key is a line number in this class, stored as an Integer object. 054 * Each value is information about the line, stored as a LineInformation object. 055 */ 056 private Map conditionals = new HashMap(); 057 058 /** 059 * Each key is a line number in this class, stored as an Integer object. 060 * Each value is information about the line, stored as a LineInformation object. 061 */ 062 private Map lines = new HashMap(); 063 064 private Set methodNamesAndDescriptors = new HashSet(); 065 066 private String sourceFileName = null; 067 068 public void addLine(int lineNumber, String methodName, 069 String methodDescriptor) 070 { 071 LineInformation lineInformation = getLineInformation(lineNumber); 072 if (lineInformation == null) 073 { 074 lineInformation = new LineInformation(lineNumber); 075 lines.put(new Integer(lineNumber), lineInformation); 076 } 077 lineInformation.setMethodNameAndDescriptor(methodName, 078 methodDescriptor); 079 methodNamesAndDescriptors.add(methodName + methodDescriptor); 080 } 081 082 /** 083 * @return The branch coverage rate for the class. 084 */ 085 public double getBranchCoverageRate() 086 { 087 if (conditionals.size() == 0) 088 { 089 // no conditional branches, therefore 100% branch coverage. 090 return 1d; 091 } 092 return (double)getNumberOfCoveredBranches() / conditionals.size(); 093 } 094 095 /** 096 * @return The branch coverage rate for a particular method. 097 */ 098 public double getBranchCoverageRate(String methodNameAndDescriptor) 099 { 100 int total = 0; 101 int hits = 0; 102 103 Iterator iter = conditionals.values().iterator(); 104 while (iter.hasNext()) 105 { 106 LineInformation next = (LineInformation)iter.next(); 107 if (methodNameAndDescriptor.equals(next.getMethodName() 108 + next.getMethodDescriptor())) 109 { 110 total++; 111 if (next.getHits() > 0) 112 { 113 hits++; 114 } 115 } 116 } 117 return (double)hits / total; 118 } 119 120 public Set getConditionals() 121 { 122 return Collections.unmodifiableSet(conditionals.keySet()); 123 } 124 125 /** 126 * @param lineNumber The source code line number. 127 * @return The number of hits a particular line of code has. 128 */ 129 public long getHitCount(int lineNumber) 130 { 131 Integer lineNum = new Integer(lineNumber); 132 if (!lines.containsKey(lineNum)) 133 { 134 return 0; 135 } 136 137 return ((LineInformation)lines.get(lineNum)).getHits(); 138 } 139 140 /** 141 * @return The line coverage rate for the class 142 */ 143 public double getLineCoverageRate() 144 { 145 if (lines.size() == 0) 146 { 147 return 1d; 148 } 149 return (double)getNumberOfCoveredLines() / lines.size(); 150 } 151 152 /** 153 * @return The line coverage rate for particular method 154 */ 155 public double getLineCoverageRate(String methodNameAndDescriptor) 156 { 157 int total = 0; 158 int hits = 0; 159 160 Iterator iter = lines.values().iterator(); 161 while (iter.hasNext()) 162 { 163 LineInformation next = (LineInformation)iter.next(); 164 if (methodNameAndDescriptor.equals(next.getMethodName() 165 + next.getMethodDescriptor())) 166 { 167 total++; 168 if (next.getHits() > 0) 169 { 170 hits++; 171 } 172 } 173 } 174 return (double)hits / total; 175 } 176 177 private LineInformation getLineInformation(int lineNumber) 178 { 179 return (LineInformation)lines.get(new Integer(lineNumber)); 180 } 181 182 /** 183 * @return The method name and descriptor of each method found in the 184 * class represented by this instrumentation. 185 */ 186 public Set getMethodNamesAndDescriptors() 187 { 188 return methodNamesAndDescriptors; 189 } 190 191 /** 192 * @return The number of branches in this class covered by testing. 193 */ 194 public int getNumberOfCoveredBranches() 195 { 196 int num = 0; 197 198 Iterator iter = conditionals.values().iterator(); 199 while (iter.hasNext()) 200 { 201 if (((LineInformation)iter.next()).getHits() > 0) 202 num++; 203 } 204 205 return num; 206 } 207 208 /** 209 * @return The number of lines in this class covered by testing. 210 */ 211 public int getNumberOfCoveredLines() 212 { 213 int num = 0; 214 215 Iterator iter = lines.values().iterator(); 216 while (iter.hasNext()) 217 { 218 if (((LineInformation)iter.next()).getHits() > 0) 219 num++; 220 } 221 222 return num; 223 } 224 225 /** 226 * @return The number of branches in this class. 227 */ 228 public int getNumberOfValidBranches() 229 { 230 return conditionals.size(); 231 } 232 233 /** 234 * @return The number of lines in this class. 235 */ 236 public int getNumberOfValidLines() 237 { 238 return lines.size(); 239 } 240 241 public String getSourceFileName() 242 { 243 return sourceFileName; 244 } 245 246 /** 247 * @return The set of valid source line numbers 248 */ 249 public Set getValidLineNumbers() 250 { 251 return Collections.unmodifiableSet(lines.keySet()); 252 } 253 254 /** 255 * Determine if a given line number is a valid line of code. 256 * 257 * @return True if the line contains executable code. False 258 * if the line is empty, or a comment, etc. 259 */ 260 public boolean isValidSourceLineNumber(int lineNumber) 261 { 262 return lines.containsKey(new Integer(lineNumber)); 263 } 264 265 public void markLineAsConditional(int lineNumber) 266 { 267 LineInformation lineInformation = getLineInformation(lineNumber); 268 if (lineInformation != null) 269 { 270 lineInformation.setConditional(true); 271 this.conditionals.put(new Integer(lineNumber), lineInformation); 272 } 273 } 274 275 /** 276 * Merge some existing instrumentation with this instrumentation. 277 * 278 * @param coverageData Some existing coverage data. 279 */ 280 public void merge(CoverageData coverageData) 281 { 282 lines.putAll(coverageData.lines); 283 conditionals.putAll(coverageData.conditionals); 284 methodNamesAndDescriptors.addAll(coverageData 285 .getMethodNamesAndDescriptors()); 286 } 287 288 public void removeLine(int lineNumber) 289 { 290 lines.remove(new Integer(lineNumber)); 291 } 292 293 public void setSourceFileName(String sourceFileName) 294 { 295 this.sourceFileName = sourceFileName; 296 } 297 298 /** 299 * Increment the number of hits for a particular line of code. 300 * 301 * @param lineNumber the line of code to increment the number of hits. 302 */ 303 public void touch(int lineNumber) 304 { 305 LineInformation lineInformation = getLineInformation(lineNumber); 306 if (lineInformation != null) 307 lineInformation.touch(); 308 } 309 310 }