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 }