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    }