001: /*
002: * @(#)BranchCountMeasure.java
003: *
004: * Copyright (C) 2003-2004 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.codecoverage.v2.module;
028:
029: import net.sourceforge.groboutils.codecoverage.v2.IAnalysisMetaData;
030: import net.sourceforge.groboutils.codecoverage.v2.IMethodCode;
031:
032: import org.apache.bcel.classfile.CodeException;
033: import org.apache.bcel.generic.IfInstruction;
034: import org.apache.bcel.generic.Instruction;
035: import org.apache.bcel.generic.InstructionHandle;
036: import org.apache.bcel.generic.JsrInstruction;
037: import org.apache.bcel.generic.Select;
038:
039: /**
040: * Processes methods for branch coverage analysis, where each branch
041: * instruction and its corresponding not-taken instruction are marked.
042: * Currently, this does not support localization.
043: * <P>
044: * This is more accurately called "Object Code Branch Coverage", since
045: * true branch coverage requires the originating source code to correctly
046: * discover the branches.
047: * <P>
048: * This measure can be superior to line coverage due to the Java construct
049: * of the <tt>?:</tt> operation. This hides a branch inside a single
050: * statement. Also, some developers may put an <tt>if</tt> statement and
051: * its one-line branch all on the same line, which will hide the branch
052: * that was taken.
053: *
054: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
055: * @author Juergen Kindler <a href="mailto:jkindler@freenet.de">jkindler@freenet.de</a>
056: * @version $Date: 2004/04/15 05:48:26 $
057: * @since January 26, 2003
058: * @see IAnalysisMetaData
059: */
060: public class BranchCountMeasure extends AbstractMeasure {
061: //private static final org.apache.log4j.Logger LOG =
062: // org.apache.log4j.Logger.getLogger( BranchCountMeasure.class );
063:
064: /**
065: * Returns the human-readable name of the measure.
066: */
067: public String getMeasureName() {
068: return "Branch";
069: }
070:
071: /**
072: * Returns the unit name for this particular coverage measure.
073: */
074: public String getMeasureUnit() {
075: return "branches";
076: }
077:
078: /**
079: * Returns the text format used in meta-data formatted text. This should
080: * be the mime encoding type, such as "text/plain" or "text/html".
081: */
082: public String getMimeEncoding() {
083: return "text/plain";
084: }
085:
086: /**
087: * Perform the analysis on the method.
088: */
089: public void analyze(IMethodCode method) {
090: BytecodeLineUtil blu = new BytecodeLineUtil(method);
091: InstructionHandle handles[] = blu.getHandles();
092:
093: // always mark the first instruction, even if there are no
094: // instructions.
095: markInstruction(method, 0, createAMD("Start of Method", blu
096: .getLineNumberForInstructionPos(0)), false);
097:
098: // find the positions of the instructions in the bytecode
099: for (int i = 0; i < handles.length; ++i) {
100: InstructionHandle h = handles[i];
101: Instruction instr = h.getInstruction();
102:
103: // Mark the if, select, and jsr instructions
104: if (instr instanceof IfInstruction) {
105: markIf(method, (IfInstruction) instr, i, blu);
106: } else if (instr instanceof Select) {
107: markSelect(method, (Select) instr, i, blu);
108: } else if (instr instanceof JsrInstruction) {
109: markJsr(method, (JsrInstruction) instr, i, blu);
110: }
111: }
112:
113: // Also need to mark all exception handlers.
114: CodeException exceptions[] = method.getOriginalMethod()
115: .getCode().getExceptionTable();
116: for (int i = 0; i < exceptions.length; ++i) {
117: markExceptionHandler(method, exceptions[i], blu);
118: }
119: }
120:
121: /**
122: * The target will be the "else" or "default" instruction.
123: */
124: private void markIf(IMethodCode method, IfInstruction instr, int i,
125: BytecodeLineUtil blu) {
126: int lineNo = blu.getLineNumberForInstructionPos(i + 1);
127:
128: // the mark on this part goes AFTER the branch instruction,
129: // not before it.
130: // Bug 906198
131: // Bug 906207
132: markInstruction(method, i + 1, createAMD("'True' branch",
133: lineNo), false);
134:
135: InstructionHandle target = instr.getTarget();
136: markTarget(method, target, blu, "'False' branch");
137: }
138:
139: /**
140: *
141: */
142: private void markSelect(IMethodCode method, Select instr, int i,
143: BytecodeLineUtil blu) {
144: int lineNo = blu.getLineNumberForInstructionPos(i + 1);
145: markInstruction(method, i + 1,
146: createAMD("After switch", lineNo), false);
147:
148: InstructionHandle targets[] = instr.getTargets();
149: for (int tIndex = 0; tIndex < targets.length; ++tIndex) {
150: markTarget(method, targets[tIndex], blu, "Case index "
151: + Integer.toString(tIndex + 1));
152: }
153: }
154:
155: /**
156: *
157: */
158: private void markJsr(IMethodCode method, JsrInstruction instr,
159: int i, BytecodeLineUtil blu) {
160: // For a JSR, the target indicates the location of a finally block.
161: InstructionHandle target = instr.getTarget();
162: markTarget(method, target, blu, "Finally block");
163: }
164:
165: /**
166: *
167: */
168: private void markExceptionHandler(IMethodCode method,
169: CodeException ce, BytecodeLineUtil blu) {
170: int pc = ce.getHandlerPC();
171:
172: // Bug 906207
173: markInstruction(method,
174: blu.getInstructionPosForBytecodePos(pc), createAMD(
175: "Exception handler", blu
176: .getLineNumberForBytecodePos(pc)),
177: false);
178: }
179:
180: private void markTarget(IMethodCode method,
181: InstructionHandle target, BytecodeLineUtil blu,
182: String branchDesc) {
183: if (target != null) {
184: // This does not relate to bug 906207 due to bug 906211
185: markInstruction(method, blu
186: .getInstructionPosForBytecodePos(target
187: .getPosition()), createAMD(branchDesc, blu
188: .getLineNumber(target)), false);
189: }
190: }
191:
192: /**
193: *
194: */
195: private IAnalysisMetaData createAMD(String type, int lineNo) {
196: return new DefaultAnalysisMetaData(type + " at line " + lineNo,
197: "Didn't cover " + type + " at line " + lineNo, (byte) 0);
198: }
199: }
|