001 /** 002 * www.jcoverage.com 003 * Copyright (C)2003 jcoverage ltd. 004 * 005 * This file is part of jcoverage. 006 * 007 * jcoverage 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 * jcoverage 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 jcoverage; 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 com.jcoverage.coverage; 024 025 import com.jcoverage.util.SetHelper; 026 027 import java.util.Collections; 028 import java.util.Iterator; 029 import java.util.Map; 030 import java.util.Set; 031 import java.util.TreeMap; 032 import java.util.TreeSet; 033 034 import org.apache.log4j.Logger; 035 036 037 class InstrumentationImpl implements InstrumentationInternal,HasBeenInstrumented { 038 static final long serialVersionUID=247748305779236308L; 039 040 static final transient Logger logger=Logger.getLogger(InstrumentationImpl.class); 041 static final transient Long ZERO=new Long(0); 042 043 final Map lineCounts=new TreeMap(); 044 final Set sourceLineNumbers=new TreeSet(); 045 final Map sourceLineNumbersByMethod=new TreeMap(); 046 final Map conditionalsByMethod=new TreeMap(); 047 final Set methodNamesAndSignatures=new TreeSet(); 048 049 int linesOfCode=0; 050 String sourceFileName; 051 052 053 InstrumentationImpl() { 054 } 055 056 public Map getCoverage() { 057 return Collections.unmodifiableMap(lineCounts); 058 } 059 060 public long getCoverage(int lineNumber) { 061 return getLineCount(lineNumber).longValue(); 062 } 063 064 public void touch(int lineNumber) { 065 Integer key=new Integer(lineNumber); 066 setLineCount(key,increment(getLineCount(key))); 067 } 068 069 public void merge(Instrumentation instrumentation) { 070 sourceLineNumbers.addAll(instrumentation.getSourceLineNumbers()); 071 methodNamesAndSignatures.addAll(instrumentation.getMethodNamesAndSignatures()); 072 sourceLineNumbersByMethod.putAll(((InstrumentationImpl)instrumentation).getSourceLineNumbersByMethod()); 073 conditionalsByMethod.putAll(((InstrumentationImpl)instrumentation).getConditionalsByMethod()); 074 075 Iterator i=instrumentation.getCoverage().entrySet().iterator(); 076 while(i.hasNext()) { 077 Map.Entry entry=(Map.Entry)i.next(); 078 079 if(lineCounts.containsKey(entry.getKey())) { 080 long total=((Long)entry.getValue()).longValue()+getLineCount((Integer)entry.getKey()).longValue(); 081 setLineCount((Integer)entry.getKey(),new Long(total)); 082 } else { 083 setLineCount((Integer)entry.getKey(),(Long)entry.getValue()); 084 } 085 } 086 087 if(getSourceFileName()==null) { 088 setSourceFileName(instrumentation.getSourceFileName()); 089 } 090 } 091 092 public Set getSourceLineNumbers() { 093 return Collections.unmodifiableSet(sourceLineNumbers); 094 } 095 096 public void setSourceLineNumbers(Set sourceLineNumbers) { 097 this.sourceLineNumbers.addAll(sourceLineNumbers); 098 } 099 100 public double getLineCoverageRate() { 101 if(sourceLineNumbers.size()==0) { 102 return 1d; 103 } 104 return ((double)lineCounts.keySet().size())/((double)sourceLineNumbers.size()); 105 } 106 107 public double getBranchCoverageRate() { 108 double sum=0d; 109 Iterator i=sourceLineNumbersByMethod.keySet().iterator(); 110 while(i.hasNext()) { 111 sum+=getBranchCoverageRate((String)i.next()); 112 } 113 return sum/(double)sourceLineNumbersByMethod.keySet().size(); 114 } 115 116 117 public double getLineCoverageRate(String methodNameAndSignature) { 118 if(!sourceLineNumbersByMethod.containsKey(methodNameAndSignature)) { 119 if(logger.isDebugEnabled()) { 120 logger.debug("sourceLineNumbersByMethod: "+sourceLineNumbersByMethod.keySet()); 121 } 122 123 throw new IllegalArgumentException(methodNameAndSignature); 124 } 125 126 Set lineNumbers=(Set)sourceLineNumbersByMethod.get(methodNameAndSignature); 127 if(lineNumbers.size()==0) { 128 return 1d; 129 } 130 131 int count=0; 132 Iterator i=lineNumbers.iterator(); 133 while(i.hasNext()) { 134 if(getLineCount((Integer)i.next()).longValue()>0) { 135 count++; 136 } 137 } 138 139 return ((double)count)/((double)lineNumbers.size()); 140 } 141 142 Set getTouchedLines(String methodNameAndSignature) { 143 Set results=new TreeSet(); 144 145 Iterator i=((Set)sourceLineNumbersByMethod.get(methodNameAndSignature)).iterator(); 146 while(i.hasNext()) { 147 Integer lineNumber=(Integer)i.next(); 148 if(getLineCount(lineNumber).longValue()>0) { 149 results.add(lineNumber); 150 } 151 } 152 return results; 153 } 154 155 public double getBranchCoverageRate(String methodNameAndSignature) { 156 if(!sourceLineNumbersByMethod.containsKey(methodNameAndSignature)) { 157 if(logger.isDebugEnabled()) { 158 logger.debug("sourceLineNumbersByMethod: "+sourceLineNumbersByMethod.keySet()); 159 } 160 161 throw new IllegalArgumentException(methodNameAndSignature); 162 } 163 164 Set conditionals=(Set)conditionalsByMethod.get(methodNameAndSignature); 165 if(conditionals.size()==0) { 166 // no conditional branches, therefore 100% branch coverage. 167 return 1d; 168 } 169 170 Set requiredHits=new TreeSet(); 171 Iterator i=conditionals.iterator(); 172 while(i.hasNext()) { 173 Conditional conditional=(Conditional)i.next(); 174 requiredHits.add(findNextSourceLineAfter(methodNameAndSignature,conditional.getLineNumber())); 175 requiredHits.add(new Integer(conditional.getTargetLineNumber())); 176 } 177 178 return ((double)SetHelper.intersection(requiredHits,getTouchedLines(methodNameAndSignature)).size())/((double)requiredHits.size()); 179 } 180 181 Integer findNextSourceLineAfter(String methodNameAndSignature,int thisOne) { 182 Iterator i=((Set)sourceLineNumbersByMethod.get(methodNameAndSignature)).iterator(); 183 Integer lineNumber=new Integer(0); 184 185 while(i.hasNext()&&(lineNumber.intValue()<thisOne)) { 186 lineNumber=(Integer)i.next(); 187 } 188 189 return lineNumber; 190 } 191 192 193 public void setSourceFileName(String sourceFileName) { 194 this.sourceFileName=sourceFileName; 195 } 196 197 public String getSourceFileName() { 198 return sourceFileName; 199 } 200 201 private Long increment(Long count) { 202 return new Long(count.longValue()+1); 203 } 204 205 private Long getLineCount(int lineNumber) { 206 return getLineCount(new Integer(lineNumber)); 207 } 208 209 private Long getLineCount(Integer lineNumber) { 210 if(!lineCounts.containsKey(lineNumber)) { 211 lineCounts.put(lineNumber,ZERO); 212 } 213 214 return (Long)lineCounts.get(lineNumber); 215 } 216 217 private void setLineCount(Integer lineNumber,Long lineCount) { 218 lineCounts.put(lineNumber,lineCount); 219 } 220 221 public Map getSourceLineNumbersByMethod() { 222 return sourceLineNumbersByMethod; 223 } 224 225 public void setSourceLineNumbersByMethod(Map sourceLineNumbersByMethod) { 226 this.sourceLineNumbersByMethod.putAll(sourceLineNumbersByMethod); 227 } 228 229 public Map getConditionalsByMethod() { 230 return conditionalsByMethod; 231 } 232 233 public void setConditionalsByMethod(Map conditionalsByMethod) { 234 this.conditionalsByMethod.putAll(conditionalsByMethod); 235 } 236 237 public Set getMethodNamesAndSignatures() { 238 return methodNamesAndSignatures; 239 } 240 241 public void setMethodNamesAndSignatures(Set x) { 242 if(logger.isDebugEnabled()) { 243 logger.debug("x: "+x); 244 } 245 246 this.methodNamesAndSignatures.addAll(x); 247 248 if(logger.isDebugEnabled()) { 249 logger.debug("methodNamesAndSignatures: "+methodNamesAndSignatures); 250 } 251 } 252 253 }