001: package com.reeltwo.jumble.mutation;
002:
003: import junit.framework.Test;
004: import junit.framework.TestCase;
005: import junit.framework.TestSuite;
006:
007: import org.apache.bcel.Repository;
008: import org.apache.bcel.classfile.Attribute;
009: import org.apache.bcel.classfile.ClassParser;
010: import org.apache.bcel.classfile.Code;
011: import org.apache.bcel.classfile.ConstantPool;
012: import org.apache.bcel.classfile.ExceptionTable;
013: import org.apache.bcel.classfile.Field;
014: import org.apache.bcel.classfile.JavaClass;
015: import org.apache.bcel.classfile.LineNumber;
016: import org.apache.bcel.classfile.LocalVariable;
017: import org.apache.bcel.classfile.Method;
018: import org.apache.bcel.generic.ConstantPoolGen;
019: import org.apache.bcel.generic.IADD;
020: import org.apache.bcel.generic.IDIV;
021: import org.apache.bcel.generic.IMUL;
022: import org.apache.bcel.generic.IOR;
023: import org.apache.bcel.generic.ISHL;
024: import org.apache.bcel.generic.ISHR;
025: import org.apache.bcel.generic.ISUB;
026: import org.apache.bcel.generic.Instruction;
027: import org.apache.bcel.generic.InstructionComparator;
028: import org.apache.bcel.generic.Type;
029: import org.apache.bcel.util.ByteSequence;
030:
031: /**
032: * Tests the corresponding class.
033: *
034: * @author Tin Pavlinic
035: * @version $Revision: 500 $
036: */
037: public class MutatingClassLoaderTest extends TestCase {
038:
039: static final String CLASSPATH = System
040: .getProperty("java.class.path");
041:
042: public void testNoMutation() throws Exception {
043: JavaClass original = new ClassParser(getClass()
044: .getClassLoader().getResourceAsStream(
045: "experiments/JumblerExperiment.class"),
046: "JumblerExperiment.class").parse();
047:
048: MutatingClassLoader j = new MutatingClassLoader(
049: "experiments.JumblerExperiment", new Mutater(-1),
050: CLASSPATH);
051: JavaClass a = j.modifyClass(original);
052: compareJavaClasses(original, a);
053: JavaClass b = j.modifyClass(original);
054: compareJavaClasses(b, a);
055: compareJavaClasses(b, original);
056: JavaClass c = j.modifyClass(a);
057: compareJavaClasses(c, b);
058: }
059:
060: public void testClassLoading() throws Exception {
061: MutatingClassLoader j = new MutatingClassLoader(
062: "experiments.JumblerExperiment", new Mutater(-1),
063: CLASSPATH);
064: Class clazz = j.loadClass("java.lang.Object");
065: assertNotNull(clazz);
066: clazz = j.loadClass("java.util.ArrayList");
067: assertNotNull(clazz);
068: clazz = j.loadClass("com.reeltwo.jumble.fast.TimingTestSuite");
069: assertNotNull(clazz);
070:
071: j = new MutatingClassLoader("experiments.JumblerExperiment",
072: new Mutater(-1), "");
073: clazz = j.loadClass("java.lang.Object");
074: assertNotNull(clazz);
075: clazz = j.loadClass("java.util.ArrayList");
076: assertNotNull(clazz);
077: // Jumble classes are also available, because we always load them from the
078: // parent classloader
079: clazz = j.loadClass("com.reeltwo.jumble.fast.TimingTestSuite");
080: assertNotNull(clazz);
081:
082: // Should have a test here where we load from a new explicit
083: // classpath. Perhaps copy a class file to a temporary directory
084: // and use that dir as the classpath.
085:
086: // try {
087: // clazz = j.loadClass("com.reeltwo.jumble.fast.TimingTestSuite");
088: // fail("Expected ClassNotFoundException");
089: // } catch (ClassNotFoundException e) {
090: // ; // Expected
091: // }
092: }
093:
094: public void testListAllModifications()
095: throws ClassNotFoundException {
096: String className = "experiments.JumblerExperiment";
097: Mutater mutater = new Mutater(-1);
098: mutater.setMutateIncrements(true);
099: mutater.setMutateInlineConstants(true);
100: mutater.setMutateReturnValues(true);
101: // System.err.println(CLASSPATH);
102:
103: MutatingClassLoader jumbler = new MutatingClassLoader(
104: className, mutater, CLASSPATH);
105: final int mutationCount = jumbler
106: .countMutationPoints(className);
107: assertEquals(13, mutationCount);
108:
109: // First list things when not performing any modifications
110: for (int i = 0; i < mutationCount; i++) {
111: mutater.setMutationPoint(i);
112: String methodName = mutater.getMutatedMethodName(className);
113: assertNotNull(methodName);
114: int mutPoint = mutater
115: .getMethodRelativeMutationPoint(className);
116: assertTrue(mutPoint != -1);
117: String modification = mutater.getModification();
118: assertNull(modification);
119: // System.err.println(methodName + "##" + mutPoint + "##" + modification);
120: }
121:
122: // Now list when performing modifications
123: for (int i = 0; i < mutationCount; i++) {
124: mutater.setMutationPoint(i);
125: jumbler = new MutatingClassLoader(className, mutater,
126: CLASSPATH);
127: jumbler.loadClass(className);
128: String methodName = mutater.getMutatedMethodName(className);
129: assertNotNull(methodName);
130: int mutPoint = mutater
131: .getMethodRelativeMutationPoint(className);
132: assertTrue(mutPoint != -1);
133: String modification = mutater.getModification();
134: assertNotNull(modification);
135: // System.err.println(methodName + "##" + mutPoint + "##" + modification);
136: }
137: }
138:
139: public void testLVTTError() throws Exception {
140: Mutater m = new Mutater();
141: m.setMutationPoint(5);
142: MutatingClassLoader cl = new MutatingClassLoader(
143: "experiments.LVTT", m, System
144: .getProperty("java.class.path"));
145: cl.loadClass("experiments.LVTT");
146: }
147:
148: public final void testJumbler() throws Exception {
149: JavaClass original = Repository.lookupClass("jumble.X2");
150:
151: MutatingClassLoader fj = new MutatingClassLoader("jumble.X2",
152: new Mutater(0), CLASSPATH);
153:
154: JavaClass c1 = fj.modifyClass(original);
155: // printClass(original);
156: // System.out.println("-------------------");
157: // printClass(c1);
158: compareModification(original, c1, 3, new IDIV());
159:
160: fj = new MutatingClassLoader("jumble.X2", new Mutater(1),
161: CLASSPATH);
162: c1 = fj.modifyClass(original);
163: compareModification(original, c1, 5, new IMUL());
164:
165: fj = new MutatingClassLoader("jumble.X2", new Mutater(2),
166: CLASSPATH);
167: c1 = fj.modifyClass(original);
168: compareModification(original, c1, 6, new ISUB());
169:
170: fj = new MutatingClassLoader("jumble.X2", new Mutater(3),
171: CLASSPATH);
172: c1 = fj.modifyClass(original);
173: compareModification(original, c1, 9, new IMUL());
174:
175: fj = new MutatingClassLoader("jumble.X2", new Mutater(4),
176: CLASSPATH);
177: c1 = fj.modifyClass(original);
178: compareModification(original, c1, 11, new IMUL());
179:
180: fj = new MutatingClassLoader("jumble.X2", new Mutater(5),
181: CLASSPATH);
182: c1 = fj.modifyClass(original);
183: compareModification(original, c1, 12, new IADD());
184:
185: fj = new MutatingClassLoader("jumble.X2", new Mutater(6),
186: CLASSPATH);
187: c1 = fj.modifyClass(original);
188: compareModification(original, c1, 14, new ISHL());
189:
190: fj = new MutatingClassLoader("jumble.X2", new Mutater(7),
191: CLASSPATH);
192: c1 = fj.modifyClass(original);
193: compareModification(original, c1, 16, new ISHR());
194:
195: fj = new MutatingClassLoader("jumble.X2", new Mutater(8),
196: CLASSPATH);
197: c1 = fj.modifyClass(original);
198: compareModification(original, c1, 18, new IOR());
199:
200: fj = new MutatingClassLoader("jumble.X2", new Mutater(9),
201: CLASSPATH);
202: c1 = fj.modifyClass(original);
203: compareModification(original, c1, 25, null);
204: }
205:
206: private void compareModification(JavaClass orig, JavaClass mod,
207: int mutationPoint, Instruction expected) throws Exception {
208: int point = 0;
209:
210: InstructionComparator comp = Instruction.getComparator();
211: Method[] methods = orig.getMethods();
212:
213: for (int i = 0; i < methods.length; i++) {
214: ByteSequence origCode = new ByteSequence(methods[i]
215: .getCode().getCode());
216: ByteSequence modCode = new ByteSequence(mod.getMethods()[i]
217: .getCode().getCode());
218:
219: while (origCode.available() > 0) {
220: final Instruction i1 = Instruction
221: .readInstruction(origCode);
222: final Instruction i2 = Instruction
223: .readInstruction(modCode);
224: if (point == mutationPoint) {
225: assertFalse(i1 + "==" + i2, comp.equals(i1, i2));
226: assertTrue(i1 + "!=" + i2, comp
227: .equals(i2, expected));
228: } else {
229: assertTrue(i1 + "!=" + i2, comp.equals(i1, i2));
230: }
231: point++;
232: }
233: }
234: }
235:
236: /**
237: * Asserts that the classes a and b are equal
238: *
239: * @param a
240: * first array
241: * @param b
242: * second array
243: */
244: private void compareJavaClasses(JavaClass a, JavaClass b)
245: throws Exception {
246: JavaClass[] i1 = a.getAllInterfaces();
247: JavaClass[] i2 = b.getAllInterfaces();
248:
249: compareClassArrays(i1, i2);
250:
251: Attribute[] a1 = a.getAttributes();
252: Attribute[] a2 = b.getAttributes();
253:
254: compareAttributeArrays(a1, a2);
255:
256: assertEquals(a.getAccessFlags(), b.getAccessFlags());
257: assertEquals(a.getClassName(), b.getClassName());
258: assertEquals(a.getClassNameIndex(), b.getClassNameIndex());
259:
260: compareConstantPools(a.getConstantPool(), b.getConstantPool());
261:
262: Field[] f1 = a.getFields();
263: Field[] f2 = b.getFields();
264:
265: compareFieldArrays(f1, f2);
266:
267: int[] int1 = a.getInterfaceIndices();
268: int[] int2 = b.getInterfaceIndices();
269: compareIntArrays(int1, int2);
270:
271: assertEquals(a.getMajor(), b.getMajor());
272:
273: Method[] meth1 = a.getMethods();
274: Method[] meth2 = b.getMethods();
275:
276: compareMethodArrays(meth1, meth2, a, b);
277:
278: assertEquals(a.getMinor(), b.getMinor());
279: assertEquals(a.getPackageName(), b.getPackageName());
280: assertEquals(a.getSource(), b.getSource());
281:
282: JavaClass[] sc1 = a.getSuperClasses();
283: JavaClass[] sc2 = b.getSuperClasses();
284: compareClassArrays(sc1, sc2);
285: assertEquals(a.getSuperclassNameIndex(), b
286: .getSuperclassNameIndex());
287: assertEquals(a.isClass(), b.isClass());
288: assertEquals(a.isSuper(), b.isSuper());
289: }
290:
291: /**
292: * Asserts that the two arrays are equal.
293: *
294: * @param a1
295: * first array.
296: * @param a2
297: * second array.
298: */
299: private void compareClassArrays(JavaClass[] a1, JavaClass[] a2)
300: throws Exception {
301: assertEquals(a1.length, a2.length);
302: for (int i = 0; i < a1.length; i++) {
303: compareJavaClasses(a1[i], a2[i]);
304: }
305: }
306:
307: /**
308: * Asserts that the two arrays are equal.
309: *
310: * @param a1
311: * first array.
312: * @param a2
313: * second array.
314: */
315: private void compareAttributeArrays(Attribute[] a1, Attribute[] a2) {
316: assertEquals(a1.length, a2.length);
317: for (int i = 0; i < a1.length; i++) {
318: compareAttributes(a1[i], a2[i]);
319: }
320: }
321:
322: /**
323: * Compares attributes
324: *
325: * @param a
326: * first attribute
327: * @param b
328: * second attribute
329: */
330: private void compareAttributes(Attribute a, Attribute b) {
331: assertEquals(a.getLength(), b.getLength());
332: assertEquals(a.getNameIndex(), b.getNameIndex());
333: assertEquals(a.getTag(), b.getTag());
334:
335: compareConstantPools(a.getConstantPool(), b.getConstantPool());
336: }
337:
338: /**
339: * Compares constant pools.
340: *
341: * @param a
342: * first pool
343: * @param b
344: * second pool
345: */
346: private void compareConstantPools(ConstantPool a, ConstantPool b) {
347: assertEquals(a.getLength(), b.getLength());
348:
349: for (int i = 0; i < a.getLength(); i++) {
350: if (a.getConstant(i) != null) {
351: // Probably shouldn't use toString but equals was not defined properly
352: assertEquals(a.getConstant(i).toString(), b
353: .getConstant(i).toString());
354: assertEquals(a.getConstant(i).getClass(), b
355: .getConstant(i).getClass());
356: } else {
357: assertEquals(null, b.getConstant(i));
358: }
359: }
360: }
361:
362: /**
363: * Asserts that the two arrays are equal.
364: *
365: * @param a1
366: * first array.
367: * @param a2
368: * second array.
369: */
370: private void compareFieldArrays(Field[] a1, Field[] a2) {
371: assertEquals(a1.length, a2.length);
372: for (int i = 0; i < a1.length; i++) {
373: compareFields(a1[i], a2[i]);
374: }
375: }
376:
377: /**
378: * Asserts two fields the same.
379: *
380: * @param a
381: * first field
382: * @param b
383: * second field
384: */
385: private void compareFields(Field a, Field b) {
386: assertEquals(a.getAccessFlags(), b.getAccessFlags());
387: assertEquals(a.getName(), b.getName());
388: compareConstantPools(a.getConstantPool(), b.getConstantPool());
389: assertEquals(a.toString(), b.toString());
390: compareTypes(a.getType(), b.getType());
391: }
392:
393: /**
394: * Asserts two Type objects the same.
395: *
396: * @param a
397: * first
398: * @param b
399: * second
400: */
401: private void compareTypes(Type a, Type b) {
402: assertEquals(a.getSignature(), b.getSignature());
403: assertEquals(a.getSize(), b.getSize());
404: assertEquals(a.getType(), b.getType());
405: assertEquals(a.toString(), b.toString());
406: }
407:
408: /**
409: * Asserts that the two arrays are equal.
410: *
411: * @param a1
412: * first array.
413: * @param a2
414: * second array.
415: */
416: private void compareIntArrays(int[] a1, int[] a2) {
417: assertEquals(a1.length, a2.length);
418: for (int i = 0; i < a1.length; i++) {
419: assertEquals(a1[i], a2[i]);
420: }
421: }
422:
423: /**
424: * Asserts that the two arrays are equal.
425: *
426: * @param a1
427: * first array.
428: * @param a2
429: * second array.
430: */
431: private void compareMethodArrays(Method[] a1, Method[] a2,
432: JavaClass classA, JavaClass classB) throws Exception {
433: assertEquals(a1.length, a2.length);
434: for (int i = 0; i < a1.length; i++) {
435: compareMethods(a1[i], a2[i], classA.getClassName(),
436: new ConstantPoolGen(classA.getConstantPool()));
437: }
438: }
439:
440: private void compareMethods(Method a, Method b, String className,
441: ConstantPoolGen cp) throws Exception {
442: compareTypeArrays(a.getArgumentTypes(), b.getArgumentTypes());
443: assertEquals(a.getName(), b.getName());
444: assertEquals(a.getSignature(), b.getSignature());
445: assertEquals(a.toString(), b.toString());
446: assertEquals(a.getAccessFlags(), b.getAccessFlags());
447:
448: compareCode(a.getCode(), b.getCode());
449: compareExceptionTable(a.getExceptionTable(), b
450: .getExceptionTable());
451:
452: if (a.getLineNumberTable() == null) {
453: assertEquals(null, b.getLineNumberTable());
454: } else {
455: compareLineNumberArray(a.getLineNumberTable()
456: .getLineNumberTable(), b.getLineNumberTable()
457: .getLineNumberTable());
458: }
459:
460: if (a.getLocalVariableTable() == null) {
461: assertEquals(null, b.getLocalVariableTable());
462: } else {
463: compareLocalVariableArray(a.getLocalVariableTable()
464: .getLocalVariableTable(), b.getLocalVariableTable()
465: .getLocalVariableTable());
466: }
467: compareTypes(a.getReturnType(), b.getReturnType());
468: }
469:
470: /**
471: * Asserts that the two arrays are equal.
472: *
473: * @param a1
474: * first array.
475: * @param a2
476: * second array.
477: */
478: private void compareTypeArrays(Type[] a1, Type[] a2) {
479: assertEquals(a1.length, a2.length);
480: for (int i = 0; i < a1.length; i++) {
481: compareTypes(a1[i], a2[i]);
482: }
483: }
484:
485: /**
486: * Compares exception tables.
487: *
488: * @param a
489: * first
490: * @param b
491: * second
492: */
493: private void compareExceptionTable(ExceptionTable a,
494: ExceptionTable b) {
495: if (a == null) {
496: assertEquals(null, b);
497: return;
498: }
499: compareIntArrays(a.getExceptionIndexTable(), b
500: .getExceptionIndexTable());
501: compareStringArrays(a.getExceptionNames(), b
502: .getExceptionNames());
503: assertEquals(a.getNumberOfExceptions(), b
504: .getNumberOfExceptions());
505: assertEquals(a.toString(), b.toString());
506: compareAttributes(a, b);
507: }
508:
509: /**
510: * Asserts that the two arrays are equal.
511: *
512: * @param a
513: * first array.
514: * @param b
515: * second array.
516: */
517: private void compareLineNumberArray(LineNumber[] a, LineNumber[] b) {
518: assertEquals(a.length, a.length);
519: for (int i = 0; i < a.length; i++) {
520: compareLineNumber(a[i], b[i]);
521: }
522: }
523:
524: /**
525: * Compares line numbers.
526: *
527: * @param a
528: * first
529: * @param b
530: * second
531: */
532: private void compareLineNumber(LineNumber a, LineNumber b) {
533: assertEquals(a.getLineNumber(), b.getLineNumber());
534: assertEquals(a.getStartPC(), b.getStartPC());
535: }
536:
537: /**
538: * Asserts that the two arrays are equal.
539: *
540: * @param a1
541: * first array.
542: * @param a2
543: * second array.
544: */
545: private void compareStringArrays(String[] a1, String[] a2) {
546: assertEquals(a1.length, a2.length);
547: for (int i = 0; i < a1.length; i++) {
548: assertEquals(a1[i], a2[i]);
549: }
550: }
551:
552: /**
553: * Asserts that the two arrays are equal.
554: *
555: * @param a1
556: * first array.
557: * @param a2
558: * second array.
559: */
560: private void compareLocalVariableArray(LocalVariable[] a1,
561: LocalVariable[] a2) {
562: assertEquals(a1.length, a2.length);
563: for (int i = 0; i < a1.length; i++) {
564: compareLocalVariables(a1[i], a2[i]);
565: }
566: }
567:
568: private void compareLocalVariables(LocalVariable a, LocalVariable b) {
569: assertEquals(a.getIndex(), b.getIndex());
570: assertEquals(a.getName(), b.getName());
571: assertEquals(a.getLength(), b.getLength());
572: assertEquals(a.getSignature(), b.getSignature());
573: assertEquals(a.getStartPC(), b.getStartPC());
574: }
575:
576: private void compareCode(Code a, Code b) throws Exception {
577: if (a == null) {
578: assertEquals(null, b);
579: return;
580: }
581: InstructionComparator comp = Instruction.getComparator();
582: compareAttributeArrays(a.getAttributes(), b.getAttributes());
583:
584: ByteSequence bsa = new ByteSequence(a.getCode());
585: ByteSequence bsb = new ByteSequence(b.getCode());
586:
587: assertEquals(bsa.available(), bsb.available());
588:
589: while (bsa.available() > 0) {
590: Instruction ia = Instruction.readInstruction(bsa);
591: Instruction ib = Instruction.readInstruction(bsb);
592: assertEquals(bsa.available(), bsb.available());
593: assertTrue("ia=" + ia + " ib=" + ib, comp.equals(ia, ib));
594: }
595: }
596:
597: public static Test suite() {
598: TestSuite suite = new TestSuite(MutatingClassLoaderTest.class);
599: return suite;
600: }
601:
602: public static void main(String[] args) {
603: junit.textui.TestRunner.run(suite());
604: }
605:
606: /**
607: * Method used for debugging - prints out the bytecode instructions for each
608: * method in the class.
609: *
610: * @param c
611: * the class to display.
612: * @throws Exception
613: * if something goes wrong
614: */
615: protected static void printClass(JavaClass c) throws Exception {
616: Method[] m = c.getMethods();
617:
618: for (int i = 0; i < m.length; i++) {
619: System.out.println(m[i].getName());
620:
621: ByteSequence code = new ByteSequence(m[i].getCode()
622: .getCode());
623:
624: while (code.available() > 0) {
625: System.out.println("\t"
626: + Instruction.readInstruction(code));
627: }
628: }
629: }
630: }
|