001: /*
002: * @(#)RebuildClassIUTest.java
003: *
004: * Copyright (C) 2002-2003 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.compiler;
028:
029: import java.io.ByteArrayInputStream;
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.util.ArrayList;
033: import java.util.Iterator;
034: import java.util.List;
035:
036: import junit.framework.Test;
037: import junit.framework.TestCase;
038: import junit.framework.TestSuite;
039: import net.sourceforge.groboutils.autodoc.v1.AutoDoc;
040: import net.sourceforge.groboutils.codecoverage.v2.ArrayClassLoader;
041: import net.sourceforge.groboutils.codecoverage.v2.BytecodeLoaderUtil;
042: import net.sourceforge.groboutils.util.io.v1.ReadByteStream;
043:
044: /**
045: * Attempts to load a classfile, disassemble it, modify the class file with
046: * the logging, rebuild it to a new file, then load the new class file and
047: * run it to ensure that everything's kosher with it.
048: *
049: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
050: * @version $Date: 2004/04/15 05:48:27 $
051: * @since December 19, 2002
052: */
053: public class RebuildClassIUTest extends TestCase {
054: //-------------------------------------------------------------------------
055: // Standard JUnit Class-specific declarations
056:
057: private static final Class THIS_CLASS = RebuildClassIUTest.class;
058: private static final AutoDoc DOC = new AutoDoc(THIS_CLASS);
059:
060: public RebuildClassIUTest(String name) {
061: super (name);
062: }
063:
064: //-------------------------------------------------------------------------
065: // Tests
066:
067: public static class MarkData {
068: public String classSig;
069: public short measureIndex;
070: public short methodIndex;
071: public short markIndex;
072:
073: public MarkData(String sig, short meaI, short metI, short marI) {
074: this .classSig = sig;
075: this .measureIndex = meaI;
076: this .methodIndex = metI;
077: this .markIndex = marI;
078: }
079:
080: public String toString() {
081: return "[Class Sig=" + this .classSig + ";measure="
082: + this .measureIndex + ";method=" + this .methodIndex
083: + ";mark=" + this .markIndex + "]";
084: }
085: }
086:
087: public static class MyLogger {
088: public static List marks = new ArrayList();
089:
090: public synchronized static void cover(String classSig,
091: short methodIndex, short channel, short markIndex) {
092: DOC.getLog().info("Inside cover");
093: MarkData md = new MarkData(classSig, channel, methodIndex,
094: markIndex);
095: DOC.getLog().info("adding mark: " + md);
096: marks.add(md);
097: }
098:
099: public synchronized static void reset() {
100: marks.clear();
101: }
102: }
103:
104: public static class MyLogger2 {
105: public static void cover(String classSig, short methodIndex,
106: short channel, short markIndex) {
107: System.out.println("MyLogger2.cover");
108: DOC.getLog().info("!!cover!!");
109: }
110: }
111:
112: private static final String COVER_METHOD_NAME = "cover";
113: private static final String CLASSFILE_PATH = "net/sourceforge/groboutils/codecoverage/v2/compiler/testcode/";
114: private static final String CLASSNAME_PACKAGE = "net.sourceforge.groboutils.codecoverage.v2.compiler.testcode.";
115: private static final String MAIN_SIG = "main([Ljava/lang/String;)V";
116:
117: public void testRebuild1() throws Exception {
118: final String classFileName = CLASSFILE_PATH + "Main1.class";
119: final String className = CLASSNAME_PACKAGE + "Main1";
120: byte[] origClassBytes = loadBytecode(classFileName);
121: // ModifiedClass mc = new ModifiedClass( new ParseCoverageLogger(
122: // Main1.class, COVER_METHOD_NAME ), classFileName, origClassBytes );
123: ModifiedClass mc = new ModifiedClass(new ParseCoverageLogger(
124: MyLogger.class, COVER_METHOD_NAME), classFileName,
125: origClassBytes);
126: ModifiedMethod[] mmL = mc.getMethods();
127: assertNotNull("Returned null method list.", mmL);
128: ModifiedMethod mm = null;
129: for (int i = 0; i < mmL.length; ++i) {
130: assertNotNull("Method " + i + " is null.", mmL[i]);
131: DOC.getLog().info(
132: "Method " + i + ": " + mmL[i].getMethodName());
133: if (mmL[i].getMethodName().equals(MAIN_SIG)) {
134: mm = mmL[i];
135: }
136: }
137: assertNotNull("Did not find a main method.", mm);
138: DOC.getLog().info(
139: "Modifying method " + mm.getMethodName() + ".");
140: ModifiedInstructionList mil = mm.getInstructionList();
141: assertNotNull("Null instruction list returned.", mil);
142: DOC.getLog().info(
143: "Instruction count: " + mil.getInstructionCount());
144: assertTrue("Do not have any instructions to mark.", mil
145: .getInstructionCount() > 0);
146: MarkedInstruction mi = mil.getInstructionAt(0);
147: assertNotNull("Null instruction returned.", mi);
148: // set measure, mark, and modify method
149: mi.addMark((short) 0, (short) 1);
150:
151: // recompile!!!!
152: final byte[] newClassfile = mc.getModifiedClass();
153: assertNotNull("Returned null classfile array.", newClassfile);
154:
155: DOC.getLog().info("Original Classfile:");
156: debugClass(origClassBytes, classFileName);
157: DOC.getLog().info("Recompiled Classfile:");
158: debugClass(newClassfile, classFileName);
159:
160: // clean-up
161: mc = null;
162: mi = null;
163: mil = null;
164: mm = null;
165: mmL = null;
166:
167: // load new, modified class.
168: ArrayClassLoader acl = new ArrayClassLoader();
169: acl.addClass(className, newClassfile);
170: // acl.addClass( className, origClassBytes );
171: Class clazz = acl.loadClass(className);
172:
173: // run the class
174: MyLogger.reset();
175: runMain(clazz);
176:
177: // check results
178: Iterator iter = MyLogger.marks.iterator();
179: assertTrue("Did not record any marks in MyLogger.", iter
180: .hasNext());
181: MarkData md = (MarkData) iter.next();
182: assertNotNull("Returned null mark data entry.", md);
183: DOC.getLog().info("First mark is: " + md);
184: assertEquals("Did not set correct measure.", (short) 0,
185: md.measureIndex);
186: assertEquals("Did not set correct mark.", (short) 1,
187: md.markIndex);
188: }
189:
190: //-------------------------------------------------------------------------
191: // helpers
192:
193: protected byte[] loadBytecode(String classFileName)
194: throws IOException {
195: ClassLoader cl = this .getClass().getClassLoader();
196: InputStream is = cl.getSystemResourceAsStream(classFileName);
197: assertNotNull("resource '" + classFileName
198: + "' could not be found.", is);
199: return ReadByteStream.readByteStream(is);
200: }
201:
202: protected void runMain(Class cz) throws Exception {
203: String s[] = new String[0];
204: java.lang.reflect.Method m = cz.getMethod("main",
205: new Class[] { s.getClass() });
206: m.invoke(null, new Object[] { s });
207: }
208:
209: protected static void debugClass(byte[] classBytes, String filename)
210: throws Exception {
211: ByteArrayInputStream bais = new ByteArrayInputStream(classBytes);
212: org.apache.bcel.classfile.ClassParser cp = new org.apache.bcel.classfile.ClassParser(
213: bais, filename);
214: org.apache.bcel.classfile.JavaClass origClass = cp.parse();
215: String className = origClass.getClassName();
216: org.apache.bcel.generic.ClassGen modClass = new org.apache.bcel.generic.ClassGen(
217: origClass);
218: org.apache.bcel.generic.ConstantPoolGen constantPool = modClass
219: .getConstantPool();
220: org.apache.bcel.classfile.Method mL[] = modClass.getMethods();
221:
222: DOC.getLog().debug("-->> Class " + className + ":");
223: /*
224: DOC.getLog().debug( " ClassGen ["+modClass+"]" );
225: DOC.getLog().debug( " Class Version ["+modClass.getMajor()+"."+
226: modClass.getMinor()+"]" );
227: DOC.getLog().debug( " ConstantPoolGen:" );
228: for (int i = 0; i < constantPool.getSize(); ++i)
229: {
230: DOC.getLog().debug( " "+i+": ["+
231: printConstant( constantPool.getConstant( i ) )+"]" );
232: }
233: */
234: for (int i = 0; i < mL.length; ++i) {
235: BytecodeLoaderUtil.verifyMethod(mL[i], constantPool);
236: if (mL[i].getName().equals("main")) {
237: int nameIndex = mL[i].getNameIndex();
238: int sigIndex = mL[i].getSignatureIndex();
239: DOC.getLog().debug(
240: " main name["
241: + nameIndex
242: + "] = ["
243: + printConstant(constantPool
244: .getConstant(nameIndex)) + "]");
245: DOC.getLog().debug(
246: " main signature["
247: + sigIndex
248: + "] = ["
249: + printConstant(constantPool
250: .getConstant(sigIndex)) + "]");
251: org.apache.bcel.classfile.Attribute attr[] = mL[i]
252: .getCode().getAttributes();
253: DOC.getLog().debug(" code attributes:");
254: for (int j = 0; j < attr.length; ++j) {
255: nameIndex = attr[j].getNameIndex();
256: DOC.getLog().debug(
257: " "
258: + printConstant(constantPool
259: .getConstant(nameIndex))
260: + " (" + j + ") ["
261: + attr[j].getTag() + "] = ["
262: + attr[j] + "] ("
263: + attr[j].getClass().getName()
264: + ")");
265: }
266: }
267: org.apache.bcel.generic.MethodGen mg = new org.apache.bcel.generic.MethodGen(
268: mL[i], className, constantPool);
269: DOC.getLog().debug(" Method " + i + " [" + mg + "]");
270: /*
271: org.apache.bcel.generic.InstructionList il =
272: mg.getInstructionList();
273: DOC.getLog().debug( " Code [\n"+il+"]" );
274: */
275: }
276: DOC.getLog().debug("<<--");
277: }
278:
279: protected static String printConstant(
280: org.apache.bcel.classfile.Constant c) {
281: if (c == null) {
282: return null;
283: }
284: StringBuffer sb = new StringBuffer("Tag ");
285: sb.append(c.getTag()).append('\'').append(c.toString()).append(
286: '\'');
287: return sb.toString();
288: }
289:
290: //-------------------------------------------------------------------------
291: // Standard JUnit declarations
292:
293: public static Test suite() {
294: TestSuite suite = new TestSuite(THIS_CLASS);
295:
296: return suite;
297: }
298:
299: public static void main(String[] args) {
300: String[] name = { THIS_CLASS.getName() };
301:
302: // junit.textui.TestRunner.main( name );
303: // junit.swingui.TestRunner.main( name );
304:
305: junit.textui.TestRunner.main(name);
306: }
307:
308: /**
309: *
310: * @exception Exception thrown under any exceptional condition.
311: */
312: protected void setUp() throws Exception {
313: super .setUp();
314:
315: // set ourself up
316: }
317:
318: /**
319: *
320: * @exception Exception thrown under any exceptional condition.
321: */
322: protected void tearDown() throws Exception {
323: // tear ourself down
324:
325: super.tearDown();
326: }
327: }
|