org.springsource.loaded.test.SpringLoadedTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springsource.loaded.test.SpringLoadedTests.java

Source

/*
 * Copyright 2010-2012 VMware and contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springsource.loaded.test;

import static org.junit.Assert.fail;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.springsource.loaded.ClassRenamer;
import org.springsource.loaded.Constants;
import org.springsource.loaded.ISMgr;
import org.springsource.loaded.MethodMember;
import org.springsource.loaded.NameRegistry;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.SSMgr;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils;
import org.springsource.loaded.agent.SpringLoadedPreProcessor;
import org.springsource.loaded.test.ReloadingJVM.JVMOutput;
import org.springsource.loaded.test.infra.ClassPrinter;
import org.springsource.loaded.test.infra.MethodPrinter;
import org.springsource.loaded.test.infra.Result;
import org.springsource.loaded.test.infra.ResultException;
import org.springsource.loaded.test.infra.TestClassLoader;

/**
 * Abstract root test class containing helper functions.
 * 
 * @author Andy Clement
 * @since 1.0
 */
public abstract class SpringLoadedTests implements Constants {

    /**
     * Classloader that can be used to see things in the bin directory, it is initialised ready for each test to use.
     */
    protected ClassLoader binLoader;

    protected String TestDataPath = TestUtils.getPathToClasses("../testdata");
    protected String TestDataAspectJPath = TestUtils.getPathToClasses("../testdata-aspectj");
    protected String GroovyTestDataPath = TestUtils.getPathToClasses("../testdata-groovy");
    protected String AspectjrtJar = "../testdata/aspectjrt.jar";
    protected String CodeJar = "../testdata/code.jar";
    // TODO [java8] replace this with project dependency when Java8 is out
    protected String Java8CodeJar = findJar("../testdata-java8/build/libs", "testdata-java8");
    protected String GroovyrtJar = "../testdata-groovy/groovy-all-1.8.6.jar";
    protected Result result;
    protected TypeRegistry registry;

    @Before
    public void setup() throws Exception {
        SpringLoadedPreProcessor.disabled = true;
        NameRegistry.reset();
        binLoader = new TestClassLoader(
                toURLs(TestDataPath, TestDataAspectJPath, AspectjrtJar, CodeJar, Java8CodeJar),
                this.getClass().getClassLoader());
    }

    @After
    public void teardown() throws Exception {
        SpringLoadedPreProcessor.disabled = false;
    }

    public void switchToGroovy() {
        binLoader = new TestClassLoader(toURLs(GroovyTestDataPath, GroovyrtJar), this.getClass().getClassLoader());
        //      Thread.currentThread().setContextClassLoader(binLoader);
    }

    /**
     * Convert an array of string paths to an array of URLs
     * 
     * @param paths the string paths
     * @return the converted URLs
     */
    public URL[] toURLs(String... paths) {
        URL[] urls = new URL[paths.length];
        int i = 0;
        for (String path : paths) {
            try {
                urls[i++] = new File(path).toURI().toURL();
            } catch (MalformedURLException e) {
                Assert.fail(e.toString());
            }
        }
        return urls;
    }

    public Object run(Class<?> clazz, String methodname, Object... params) {
        try {
            //         System.out.println("Calling method " + methodname + " on " + clazz.getName());
            Object o = clazz.newInstance();
            Method m = null;
            Method[] ms = clazz.getMethods();
            for (Method mm : ms) {
                if (mm.getName().equals(methodname)) {
                    m = mm;
                    break;
                }
            }
            // Method m = clazz.getDeclaredMethod(methodname);
            return m.invoke(o, params);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.toString());
            return null;
        }
    }

    public Object run(Class<?> clazz, Object o, String methodname, Object... params) {
        try {
            System.out.println("Calling method " + methodname + " on " + clazz.getName());
            Method m = null;
            Method[] ms = clazz.getMethods();
            for (Method mm : ms) {
                if (mm.getName().equals(methodname)) {
                    m = mm;
                    break;
                }
            }
            // Method m = clazz.getDeclaredMethod(methodname);
            return m.invoke(o, params);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.toString());
            return null;
        }
    }

    public static boolean capture = true;

    public Result runUnguardedWithCCL(Class<?> clazz, ClassLoader ccl, String methodname, Object... params)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, InvocationTargetException {
        ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(ccl);
            return runUnguarded(clazz, methodname, params);
        } finally {
            Thread.currentThread().setContextClassLoader(oldCCL);
        }
    }

    public Result runUnguarded(Class<?> clazz, String methodname, Object... params)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, InvocationTargetException {

        PrintStream oldo = System.out;
        PrintStream olde = System.err;
        Object result = null;
        ByteArrayOutputStream oso = new ByteArrayOutputStream();
        ByteArrayOutputStream ose = new ByteArrayOutputStream();
        try {
            if (capture) {
                System.setOut(new PrintStream(oso));
                System.setErr(new PrintStream(ose));
            }

            Object o = clazz.newInstance();
            Method m = null;
            Method[] ms = clazz.getMethods();
            for (Method mm : ms) {
                if (mm.getName().equals(methodname)) {
                    m = mm;
                    break;
                }
            }
            if (m == null) {
                Assert.fail("Invocation failure: could not find method '" + methodname + "' on type '"
                        + clazz.getName());
            }
            m.setAccessible(true);
            result = m.invoke(o, params);
        } finally {
            if (capture) {
                System.setOut(oldo);
                System.setErr(olde);
            }
        }
        return new Result(result, oso.toString().replace("\r", ""), ose.toString().replace("\r", ""));
    }

    public Result runStaticUnguarded(Class<?> clazz, String methodname, Object... params)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, InvocationTargetException {

        PrintStream oldo = System.out;
        PrintStream olde = System.err;
        Object result = null;
        ByteArrayOutputStream oso = new ByteArrayOutputStream();
        ByteArrayOutputStream ose = new ByteArrayOutputStream();
        try {
            if (capture) {
                System.setOut(new PrintStream(oso));
                System.setErr(new PrintStream(ose));
            }

            Method m = null;
            Method[] ms = clazz.getMethods();
            for (Method mm : ms) {
                if (mm.getName().equals(methodname)) {
                    m = mm;
                    break;
                }
            }
            if (m == null) {
                Assert.fail("Invocation failure: could not find method '" + methodname + "' on type '"
                        + clazz.getName());
            }
            m.setAccessible(true);
            result = m.invoke(null, params);
        } finally {
            if (capture) {
                System.setOut(oldo);
                System.setErr(olde);
            }
        }
        return new Result(result, oso.toString().replace("\r", ""), ose.toString().replace("\r", ""));
    }

    public Result runConstructor(Class<?> clazz, int whichConstructor, Object... params)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, InvocationTargetException {

        PrintStream oldo = System.out;
        PrintStream olde = System.err;
        Object result = null;
        ByteArrayOutputStream oso = new ByteArrayOutputStream();
        ByteArrayOutputStream ose = new ByteArrayOutputStream();
        Constructor<?> c = null;
        try {
            if (capture) {
                System.setOut(new PrintStream(oso));
                System.setErr(new PrintStream(ose));
            }

            Constructor<?>[] cs = clazz.getConstructors();
            c = cs[whichConstructor];
            System.out.println(c);
            if (c == null) {
                Assert.fail("Invocation failure: could not find constructor " + whichConstructor + " on type '"
                        + clazz.getName());
            }
            c.setAccessible(true);
            result = c.newInstance(params);
        } finally {
            if (capture) {
                System.setOut(oldo);
                System.setErr(olde);
            }
        }
        return new Result(result, oso.toString().replace("\r", ""), ose.toString().replace("\r", ""));
    }

    public Result runConstructor(Class<?> clazz, String paramDescriptor, Object... params)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, InvocationTargetException {

        PrintStream oldo = System.out;
        PrintStream olde = System.err;
        Object result = null;
        ByteArrayOutputStream oso = new ByteArrayOutputStream();
        ByteArrayOutputStream ose = new ByteArrayOutputStream();
        try {
            if (capture) {
                System.setOut(new PrintStream(oso));
                System.setErr(new PrintStream(ose));
            }

            Constructor<?>[] cs = clazz.getConstructors();
            Constructor<?> c = null;
            for (Constructor<?> ctor : cs) {
                Class<?>[] paramClazzes = ctor.getParameterTypes();
                String toParamDescriptorString = toParamDescriptorString(paramClazzes);
                //            System.out.println(toParamDescriptorString + "<<");
                if (paramDescriptor.equals(toParamDescriptorString)) {
                    c = ctor;
                    break;
                }
            }

            if (c == null) {
                Assert.fail("Invocation failure: could not find constructor with param descriptor "
                        + paramDescriptor + " on type '" + clazz.getName());
            }
            c.setAccessible(true);
            result = c.newInstance(params);
        } finally {
            if (capture) {
                System.setOut(oldo);
                System.setErr(olde);
            }
        }
        return new Result(result, oso.toString().replace("\r", ""), ose.toString().replace("\r", ""));
    }

    private String toParamDescriptorString(Class<?>[] paramClazzes) {
        if (paramClazzes == null) {
            return "";
        }
        StringBuilder s = new StringBuilder();
        for (Class<?> c : paramClazzes) {
            s.append(c.getName());
            s.append(" ");
        }
        return s.toString().trim();
    }

    public void runExpectNoSuchMethodException(Class<?> clazz, String methodname, Object... params)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, InvocationTargetException {
        try {
            runUnguarded(clazz, methodname, params);
            Assert.fail("should not work, NSME should occur for " + methodname);
        } catch (InvocationTargetException ite) {
            String cause = ite.getCause().toString();
            if (!cause.startsWith("java.lang.NoSuchMethodError")) {
                ite.printStackTrace();
                Assert.fail("Should be a NoSuchMethodError, but got " + ite);
            }
        }
    }

    public static boolean printOutput = false;

    /**
     * Proposed alternate version of runOnInstance that produces a wrapper Exception object similar to the Result object, but where
     * the "result" is an exception. This is done so as not to lose the grabbed output when exception is raised by the test case.
     */
    public static Result runOnInstance(Class<?> clazz, Object instance, String methodname, Object... params)
            throws ResultException {

        PrintStream oldo = System.out;
        PrintStream olde = System.err;
        Object result = null;
        Throwable exception = null;
        ByteArrayOutputStream oso = new ByteArrayOutputStream();
        ByteArrayOutputStream ose = new ByteArrayOutputStream();
        try {
            if (capture) {
                System.setOut(new PrintStream(oso));
                System.setErr(new PrintStream(ose));
            }

            Method m = null;
            Method[] ms = clazz.getMethods();
            for (Method mm : ms) {
                if (mm.getName().equals(methodname)) {
                    m = mm;
                    break;
                }
            }
            if (m == null) {
                throw new IllegalStateException("No method called " + methodname + " to call on type "
                        + (instance == null ? "null" : instance.getClass().getName()));
            }
            try {
                result = m.invoke(instance, params);
            } catch (Throwable e) {
                exception = e;
            }

        } finally {
            System.setOut(oldo);
            System.setErr(olde);
        }
        if (printOutput) {
            System.out.println("Collected output running: " + methodname);
            System.out.println(oso.toString());
            System.out.println(ose.toString());
        }
        if (exception != null) {
            throw new ResultException(exception, oso.toString().replace("\r", ""),
                    ose.toString().replace("\r", ""));
        } else {
            return new Result(result, oso.toString().replace("\r", ""), ose.toString().replace("\r", ""));
        }
    }

    // attempt definition - kind of a lightweight way to see if it is OK
    public Class<?> loadit(String name, byte[] bytes) {
        try {
            return ((TestClassLoader) binLoader).defineTheClass(name, bytes);
        } catch (RuntimeException t) {
            ClassPrinter.print(bytes);
            t.printStackTrace();
            throw t;
        }
    }

    public Class<?> loadClass(String name) {
        return loadit(name, loadBytesForClass(name));
    }

    protected byte[] retrieveRename(String newName, String name) {
        return ClassRenamer.rename(newName, loadBytesForClass(name));
    }

    /**
     * retargets are "from.this.thing:to.this.thing"
     * 
     * @param newName
     * @param name
     * @param retargets of the form "this.from:this.to"
     * @return
     */
    protected byte[] retrieveRename(String newName, String name, String... retargets) {
        return ClassRenamer.rename(newName, loadBytesForClass(name), retargets);
    }

    protected byte[] loadBytesForClass(String dottedClassName) {
        byte[] data = Utils.loadDottedClassAsBytes(binLoader, dottedClassName);
        Assert.assertNotNull(data);
        Assert.assertNotSame(0, data.length);
        return data;
    }

    protected String getStamp(String classname) {
        return getStamp(binLoader, classname);
    }

    public static byte[] retrieveClass(ClassLoader loader, String classname) {
        byte[] data = Utils.loadDottedClassAsBytes(loader, classname);
        Assert.assertNotNull(data);
        Assert.assertNotSame(0, data.length);
        return data;
    }

    protected void print(byte[] classdata) {
        ClassReader reader = new ClassReader(classdata);
        reader.accept(new ClassPrinter(System.out), 0);
    }

    protected String printItAndReturnIt(byte[] classdata) {
        return printItAndReturnIt(classdata, false);
    }

    protected String printItAndReturnIt(byte[] classdata, boolean quoted) {
        OutputStream os = new SimpleOutputStream();
        ClassReader reader = new ClassReader(classdata);
        reader.accept(new ClassPrinter(new PrintStream(os)), 0);
        StringBuffer sb = new StringBuffer(os.toString().replace("\r", ""));
        if (!quoted) {
            return sb.toString();
        }
        for (int i = 0; i < sb.length(); i++) {
            if (sb.charAt(i) == '\n') {
                sb.insert(i + 1, "\"");
                sb.insert(i, "\\n\"+");
                i += 4;
            }
        }
        sb.delete(sb.length() - 3, sb.length());
        sb.insert(0, "\"");
        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    private MethodNode getMethod(byte[] classbytes, String methodName) {
        ClassReader super_cr = new ClassReader(classbytes);
        ClassNode cn = new ClassNode();
        super_cr.accept(cn, 0);
        List<MethodNode> methods = cn.methods;
        if (methods != null) {
            for (MethodNode mn : methods) {
                if (mn.name.equals(methodName)) {
                    return mn;
                }
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private FieldNode getField(byte[] classbytes, String fieldName) {
        ClassReader super_cr = new ClassReader(classbytes);
        ClassNode cn = new ClassNode();
        super_cr.accept(cn, 0);
        List<FieldNode> fields = cn.fields;
        if (fields != null) {
            for (FieldNode fn : fields) {
                if (fn.name.equals(fieldName)) {
                    return fn;
                }
            }
        }
        return null;
    }

    protected ClassNode getClassNode(byte[] classdata) {
        ClassNode cn = new ClassNode();
        ClassReader cr = new ClassReader(classdata);
        cr.accept(cn, 0);
        return cn;
    }

    @SuppressWarnings("unchecked")
    protected List<MethodNode> getMethods(byte[] classdata) {
        return getClassNode(classdata).methods;
    }

    protected int countMethods(byte[] classdata) {
        ClassNode cn = getClassNode(classdata);
        return cn.methods == null ? 0 : cn.methods.size();
    }

    protected List<MethodNode> filter(List<MethodNode> methods, String nameSubstring) {
        if (methods == null) {
            return Collections.<MethodNode>emptyList();
        }
        List<MethodNode> subset = new ArrayList<MethodNode>();
        for (MethodNode methodNode : methods) {
            if (methodNode.name.contains(nameSubstring)) {
                subset.add(methodNode);
            }
        }
        return subset;
    }

    protected String toStringClass(byte[] classdata) {
        return toStringClass(classdata, false, false);
    }

    protected String toStringClass(byte[] classdata, boolean includeBytecode) {
        return toStringClass(classdata, includeBytecode, false);
    }

    protected String toStringClass(byte[] classdata, boolean includeBytecode, boolean quoted) {
        OutputStream os = new SimpleOutputStream();
        ClassPrinter.print(new PrintStream(os), classdata, includeBytecode);
        String s = os.toString();
        StringBuffer sb = new StringBuffer(s.replaceAll("\r", ""));
        if (!quoted) {
            return sb.toString();
        }
        for (int i = 0; i < sb.length(); i++) {
            if (sb.charAt(i) == '\n') {
                sb.insert(i + 1, "\"");
                sb.insert(i, "\\n\"+");
                i += 4;
            }
        }
        sb.insert(0, "\"");
        sb.delete(sb.length() - 3, sb.length());
        return sb.toString();
    }

    protected String toStringMethod(byte[] classdata, String methodname, boolean quoted) {
        OutputStream os = new SimpleOutputStream();
        // ClassReader reader = new ClassReader(classdata);
        MethodNode one = getMethod(classdata, methodname);
        one.instructions.accept(new MethodPrinter(new PrintStream(os)));
        String s = os.toString();
        StringBuffer sb = new StringBuffer(s.replaceAll("\r", ""));
        if (!quoted) {
            return sb.toString();
        }
        for (int i = 0; i < sb.length(); i++) {
            if (sb.charAt(i) == '\n') {
                sb.insert(i + 1, "\"");
                sb.insert(i, "\\n\"+");
                i += 4;
            }
        }
        sb.insert(0, "Method is " + methodname + "\n\"");
        sb.delete(sb.length() - 3, sb.length());
        return sb.toString();
    }

    public static String findJar(String whereToLook, String jarPrefix) {
        File dir = new File(whereToLook);
        File[] fs = dir.listFiles();
        for (File f : fs) {
            if (f.getName().startsWith(jarPrefix)) {
                return f.toString();
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    protected String toStringField(byte[] classdata, String fieldname) {
        StringBuilder sb = new StringBuilder();
        FieldNode fieldNode = getField(classdata, fieldname);
        if (fieldNode == null) {
            return null;
        }
        List<AnnotationNode> annos = fieldNode.visibleAnnotations;
        if (annos != null) {
            sb.append("vis(").append(toStringAnnotations(annos)).append(") ");
        }
        annos = fieldNode.invisibleAnnotations;
        if (annos != null) {
            sb.append("invis(").append(toStringAnnotations(annos)).append(") ");
        }
        // will need implementing at some point:
        //      List<Attribute> attrs = fieldNode.attrs;
        //      if (attrs = !null) {
        //         sb.append("attrs(").append(toStringAttributes(attrs)).append(") ");
        //      }
        sb.append("0x").append(Integer.toHexString(fieldNode.access)).append("(")
                .append(ClassPrinter.toAccessForMember(fieldNode.access)).append(") ");
        sb.append(fieldNode.name).append(" ");
        sb.append(fieldNode.desc).append(" ");
        if (fieldNode.signature != null) {
            sb.append(fieldNode.signature).append(" ");
        }
        if (fieldNode.value != null) {
            sb.append(fieldNode.value).append(" ");
        }
        return sb.toString().trim();
    }

    private String toStringAnnotations(List<AnnotationNode> annos) {
        StringBuilder sb = new StringBuilder();
        for (AnnotationNode anno : annos) {
            sb.append(toStringAnnotation(anno)).append(" ");
        }
        return sb.toString().trim();
    }

    private String toStringAnnotation(AnnotationNode anno) {
        StringBuilder sb = new StringBuilder();
        sb.append(anno.desc);
        if (anno.values != null) {
            for (int i = 0; i < anno.values.size(); i = i + 2) {
                if (i > 0) {
                    sb.append(" ");
                }
                sb.append(toStringAnnotationValue((String) anno.values.get(i), anno.values.get(i + 1)));
            }
        }

        return sb.toString().trim();
    }

    /**
     * From asm:
     * 
     * The name value pairs of this annotation. Each name value pair is stored as two consecutive elements in the list. The name is
     * a {@link String}, and the value may be a {@link Byte}, {@link Boolean}, {@link Character}, {@link Short}, {@link Integer},
     * {@link Long}, {@link Float}, {@link Double}, {@link String} or {@link org.objectweb.asm.Type}, or an two elements String
     * array (for enumeration values), a {@link AnnotationNode}, or a {@link List} of values of one of the preceding types. The list
     * may be <tt>null</tt> if there is no name value pair.
     */

    private String toStringAnnotationValue(String name, Object value) {
        StringBuilder sb = new StringBuilder();
        sb.append(name).append("=");
        if (value instanceof Byte) {
            sb.append(((Byte) value).byteValue());
        } else if (value instanceof Boolean) {
            sb.append(((Boolean) value).booleanValue());
        } else if (value instanceof Character) {
            sb.append(((Character) value).charValue());
        } else if (value instanceof Short) {
            sb.append(((Short) value).shortValue());
        } else if (value instanceof Integer) {
            sb.append(((Integer) value).intValue());
        } else if (value instanceof Long) {
            sb.append(((Long) value).longValue());
        } else if (value instanceof Float) {
            sb.append(((Float) value).floatValue());
        } else if (value instanceof Double) {
            sb.append(((Double) value).doubleValue());
        } else if (value instanceof String) {
            sb.append(((String) value));
        } else if (value instanceof Type) {
            sb.append(((Type) value).getClassName());
        } else if (value instanceof String[]) {
            String[] ss = (String[]) value;
            sb.append(ss[0]).append(ss[1]);
        } else if (value instanceof AnnotationNode) {
            sb.append(toStringAnnotation((AnnotationNode) value));
        } else if (value instanceof List) {
            throw new IllegalStateException("nyi");
        }
        return sb.toString().trim();
    }

    private static class SimpleOutputStream extends OutputStream {

        StringBuilder sb = new StringBuilder();

        @Override
        public void write(int b) throws IOException {
            sb.append((char) b);
        }

        @Override
        public String toString() {
            return sb.toString();
        }

    }

    protected static String getStamp(ClassLoader loader, String classname) {
        Assert.assertFalse(classname.endsWith(".class"));
        Assert.assertEquals(-1, classname.indexOf('/'));
        URL resourceURL = loader.getResource(classname.replace('.', '/') + ".class");
        System.out.println(resourceURL.getFile());
        return null;
    }

    /**
     * Look for a <name>.print file and check the printout of the bytes matches it, unless regenerate is true in which case the
     * print out is recorded in that file.
     */
    protected void checkIt(String name, byte[] bytes) {
        checkIt(name, bytes, shouldRegenerate());
    }

    /**
     * Look for a <name>.print file and check the printout of the bytes matches it, unless regenerate is true in which case the
     * print out is recorded in that file.
     */
    protected void checkIt(String name, byte[] bytes, boolean regenerate) {
        String filename = "src/test/java/" + name.replace('.', '/') + ".print";
        try {
            if (regenerate) {
                // create the file
                System.out.println("creating " + filename);
                File f = new File(filename);
                FileWriter fos = new FileWriter(f);
                BufferedWriter dos = new BufferedWriter(fos);
                dos.write(printItAndReturnIt(bytes));
                dos.flush();
                fos.close();
            } else {
                // compare the files
                List<String> expectedLines = new ArrayList<String>();
                File f = new File(filename);
                if (!f.exists()) {
                    Assert.fail(
                            "Must run with renegerate on once to create the expected output for '" + name + "'");
                }
                FileInputStream fis = new FileInputStream(f);
                BufferedReader dis = new BufferedReader(new FileReader(new File(filename)));
                String line = null;
                while ((line = dis.readLine()) != null) {
                    if (line.length() != 0) {
                        expectedLines.add(line);
                    }
                }
                dis.close();
                fis.close();
                List<String> actualLines = toLines(printItAndReturnIt(bytes));
                if (actualLines.size() != expectedLines.size()) {
                    System.out.println("actual lines=" + actualLines.size());
                    System.out.println("   exp lines=" + expectedLines.size());
                    Assert.assertEquals(expectedLines, actualLines);
                }
                for (int ln = 0; ln < expectedLines.size(); ln++) {
                    if (!expectedLines.get(ln).equals(actualLines.get(ln))) {
                        String expLine = (ln + 1) + " " + expectedLines.get(ln);
                        String actLine = (ln + 1) + " " + actualLines.get(ln);
                        Assert.assertEquals(expLine, actLine);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected List<String> toLines(String input) {
        StringTokenizer tokenizer = new StringTokenizer(input, "\n\r");
        List<String> output = new ArrayList<String>();
        while (tokenizer.hasMoreElements()) {
            output.add(tokenizer.nextToken());
        }
        return output;
    }

    protected boolean shouldRegenerate() {
        return true;
    }

    protected void copyFile(File from, File to) {
        try {
            FileInputStream fis = new FileInputStream(from);
            FileOutputStream fos = new FileOutputStream(to);
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            byte[] buffer = new byte[4096];
            int len;
            while ((len = bis.read(buffer, 0, 4096)) != -1) {
                bos.write(buffer, 0, len);
            }
            bis.close();
            bos.close();
        } catch (IOException ioe) {
            throw new RuntimeException("Copy file failed", ioe);
        }
    }

    protected void checkMethod(byte[] bytes, String name, String expected) {
        String actual = toStringMethod(bytes, name, false);
        if (!actual.equals(expected)) {
            // print it out for inclusion in the testcode
            System.out.println(toStringMethod(bytes, name, true));
        }
        Assert.assertEquals(expected, actual);
    }

    protected void checkType(byte[] bytes, String expected) {
        String actual = printItAndReturnIt(bytes, false);
        if (!actual.equals(expected)) {
            // print it out for inclusion in the testcode
            System.out.println(printItAndReturnIt(bytes, true));
        }
        Assert.assertEquals(expected, actual);
    }

    // ---

    // private void loadNewVersion(ReloadableType rtype, String version) throws InstantiationException,
    // IllegalAccessException {
    // loadNewVersion(rtype, version, true);
    // }
    //
    // /**
    // * Loads a new version of a type, given the name of the type, the version (suffix) and the name.
    // */
    // private void loadNewVersion(ReloadableType rtype, String version, boolean log) throws InstantiationException,
    // IllegalAccessException {
    // String name = rtype.getTypeName();
    // byte[] newclassbytes = retrieveClass(name + version);
    // newclassbytes = ClassRenamer.rename(name.replace('.', '/'), newclassbytes);
    // byte[] newdispatcher = DispatcherCreator.createFor(rtype, version);
    // if (log) {
    // System.out.println("DISPATCHER:");
    // print(newdispatcher);
    // }
    // Class<?> newdispatcherclass = loadit(name + "$D$" + version, newdispatcher);
    // byte[] newexecutorbytes = ExecutorCreator.createFor(rtype, version, newclassbytes);
    // if (log) {
    // System.out.println("EXECUTOR:");
    // print(newexecutorbytes);
    // }
    // loadit(name + "$E$" + version, newexecutorbytes);
    // TypeRegistry.poke(name.replace('.', '/'), newdispatcherclass.newInstance());
    // }
    //
    // private void checkvalue(Class<?> clazz, String methodname, Object expectedOutput) {
    // Object value = run(clazz, methodname);
    // if (!value.equals(expectedOutput)) {
    // Assert.fail("Expected " + expectedOutput + ", not " + value);
    // }
    // }

    protected void configureForTesting(TypeRegistry typeRegistry, String... includePatterns) {
        if (includePatterns != null) {
            Properties p = new Properties();
            StringBuilder s = new StringBuilder();
            for (int i = 0; i < includePatterns.length; i++) {
                if (i > 0) {
                    s.append(',');
                }
                s.append(includePatterns[i]);
            }
            p.setProperty(TypeRegistry.Key_Inclusions, s.toString());
            typeRegistry.configure(p);
        }
    }

    protected void checkCause(Exception e, Class<? extends Throwable> expectedCause) {
        Throwable t = e.getCause();
        if (t == null || !t.getClass().equals(expectedCause)) {
            e.printStackTrace();
            Assert.fail(
                    "Expected cause of " + expectedCause + " but it was " + (t == null ? "null" : t.getClass()));
        }
    }

    public MethodMember grabFrom(List<MethodMember> ms, String name) {
        for (MethodMember m : ms) {
            if (m.getName().equals(name)) {
                return m;
            }
        }
        return null;
    }

    public Method grabFrom(Method[] ms, String name) {
        for (Method m : ms) {
            if (m.getName().equals(name)) {
                return m;
            }
        }
        return null;
    }

    public Field grabFrom(Field[] fs, String name) {
        for (Field f : fs) {
            if (f.getName().equals(name)) {
                return f;
            }
        }
        return null;
    }

    protected MethodMember findMethod(String toSearchFor, TypeDescriptor typeDescriptor) {
        for (MethodMember method : typeDescriptor.getMethods()) {
            if (method.toString().equals(toSearchFor)) {
                return method;
            }
        }
        return null;
    }

    /**
     * Create a type registry, configure it with the specified reloadable type/packages and return it.
     * 
     * @return new TypeRegistry
     */
    protected TypeRegistry getTypeRegistry(String includePatterns) {
        TypeRegistry.reinitialize();
        TypeRegistry tr = TypeRegistry.getTypeRegistryFor(binLoader);
        Properties p = new Properties();
        if (includePatterns != null) {
            p.setProperty(TypeRegistry.Key_Inclusions, includePatterns);
        }
        if (tr == null) {
            throw new IllegalStateException(
                    "maybe you need to run with: -Dspringloaded=limit=false -Xmx512M -XX:MaxPermSize=256m -noverify");
        }
        tr.configure(p);
        return tr;
    }

    //   protected TypeRegistry getTypeRegistry(String... includePatterns) {
    //      StringBuilder s = new StringBuilder();
    //      for (int i = 0; i < includePatterns.length; i++) {
    //         if (i > 0) {
    //            s.append(',');
    //         }
    //         s.append(includePatterns[i]);
    //      }
    //      return getTypeRegistry(s.toString());
    //   }

    protected TypeRegistry getTypeRegistry() {
        return getTypeRegistry(null);
    }

    /**
     * Make a type reload itself - this does trigger creation of the dispatcher/executor.
     */
    protected void reload(ReloadableType reloadableType, String versionstamp) {
        reloadableType.loadNewVersion(versionstamp, reloadableType.bytesInitial);
    }

    @SuppressWarnings("unchecked")
    protected void checkLocalVariables(byte[] bytes, String methodNameAndDescriptor, String... expected) {
        ClassNode cn = new ClassNode();
        ClassReader cr = new ClassReader(bytes);
        cr.accept(cn, 0);

        boolean checked = false;
        List<MethodNode> methods = cn.methods;
        for (MethodNode mn : methods) {
            if (methodNameAndDescriptor.equals(mn.name + mn.desc)) {
                List<LocalVariableNode> localVariables = mn.localVariables;
                Assert.assertEquals(expected.length, localVariables.size());
                for (int i = 0; i < expected.length; i++) {
                    StringTokenizer tokenizer = new StringTokenizer(expected[i], ":");
                    String expectedName = tokenizer.nextToken();
                    String expectedDesc = tokenizer.nextToken();
                    LocalVariableNode localVariable = localVariables.get(i);
                    Assert.assertEquals(i, localVariable.index);
                    Assert.assertEquals(expectedName, localVariable.name);
                    Assert.assertEquals(expectedDesc, localVariable.desc);
                }
                checked = true;
            }
        }
        if (!checked) {
            for (MethodNode mn : methods) {
                System.out.println(mn.name + mn.desc);
            }
            Assert.fail("Unable to find method " + methodNameAndDescriptor);
        }
    }

    @SuppressWarnings("unchecked")
    protected void checkAnnotations(byte[] bytes, String methodNameAndDescriptor, String... expected) {
        ClassNode cn = new ClassNode();
        ClassReader cr = new ClassReader(bytes);
        cr.accept(cn, 0);
        if (expected == null) {
            expected = new String[0];
        }

        boolean checked = false;
        List<MethodNode> methods = cn.methods;
        for (MethodNode mn : methods) {
            if (methodNameAndDescriptor.equals(mn.name + mn.desc)) {
                List<AnnotationNode> annotations = mn.visibleAnnotations;
                if (annotations == null) {
                    annotations = Collections.emptyList();
                }
                Assert.assertEquals(expected.length, annotations.size());
                for (int i = 0; i < expected.length; i++) {
                    //               StringTokenizer tokenizer = new StringTokenizer(expected[i], ":");
                    //               String expectedName = tokenizer.nextToken();
                    //               String expectedDesc = tokenizer.nextToken();
                    AnnotationNode annotation = annotations.get(i);
                    Assert.assertEquals(expected[i], toString(annotation));
                }
                checked = true;
            }
        }
        if (!checked) {
            for (MethodNode mn : methods) {
                System.out.println(mn.name + mn.desc);
            }
            Assert.fail("Unable to find method " + methodNameAndDescriptor);
        }
    }

    @SuppressWarnings({ "rawtypes" })
    protected Object toString(AnnotationNode annotation) {
        StringBuilder s = new StringBuilder();
        s.append("@");
        String s2 = annotation.desc.substring(1, annotation.desc.length() - 1);
        s.append(s2.replace('/', '.'));
        s.append("(");
        List values = annotation.values;
        if (values != null) {
            int i = 0;
            while (i < values.size()) {
                String name = (String) values.get(i++);
                Object value = values.get(i++);
                s.append(name).append("=").append(value);
            }
        }
        s.append(")");
        return s.toString();
    }

    protected void assertStartsWith(String prefix, Object value) {
        String stringValue = (String) value;
        if (!stringValue.startsWith(prefix)) {
            Assert.fail("Expected 'regular' toString() but was " + result.returnValue);
        }
    }

    protected void checkDoesNotContain(TypeDescriptor typeDescriptor, String string) {
        if (typeDescriptor.toString().indexOf(string) != -1) {
            Assert.fail("Did not expect to find '" + string + "' in\n" + typeDescriptor);
        }
    }

    protected void checkDoesContain(TypeDescriptor typeDescriptor, String string) {
        if (typeDescriptor.toString().indexOf(string) == -1) {
            Assert.fail("Expected to find '" + string + "' in\n" + typeDescriptor);
        }
    }

    protected ReloadableType loadType(TypeRegistry typeRegistry, String dottedTypeName) {
        return typeRegistry.addType(dottedTypeName, loadBytesForClass(dottedTypeName));
    }

    public Class<?> classForName(String typeName) throws ClassNotFoundException {
        if (typeName.endsWith("[]")) {
            Class<?> element = classForName(typeName.substring(0, typeName.length() - 2));
            return Array.newInstance(element, 0).getClass();
        } else if (typeName.equals("int")) {
            return int.class;
        } else if (typeName.equals("void")) {
            return void.class;
        } else if (typeName.equals("boolean")) {
            return boolean.class;
        } else if (typeName.equals("byte")) {
            return byte.class;
        } else if (typeName.equals("char")) {
            return char.class;
        } else if (typeName.equals("short")) {
            return short.class;
        } else if (typeName.equals("double")) {
            return double.class;
        } else if (typeName.equals("float")) {
            return float.class;
        } else if (typeName.equals("long")) {
            return long.class;
        }
        return Class.forName(typeName, false, binLoader);
    }

    protected Object intArrayToString(Object value) {
        int[] intArray = (int[]) value;
        StringBuilder s = new StringBuilder();
        s.append("{");
        if (intArray != null) {
            for (int j = 0; j < intArray.length; j++) {
                if (j > 0) {
                    s.append(",");
                }
                s.append(intArray[j]);
            }
        }
        s.append("}");
        return s.toString();
    }

    protected String objectArrayToString(Object value) {
        Object[] array = (Object[]) value;
        StringBuilder s = new StringBuilder();
        s.append("{");
        if (array != null) {
            for (int j = 0; j < array.length; j++) {
                if (j > 0) {
                    s.append(",");
                }
                s.append(array[j]);
            }
        }
        s.append("}");
        return s.toString();
    }

    protected void assertContains(String expectedToBeContained, String actual) {
        if (actual.indexOf(expectedToBeContained) == -1) {
            fail("\nCould not find expected data:\n" + expectedToBeContained + "\n in actual output:\n" + actual);
        }
    }

    protected void assertUniqueContains(String expectedToBeContained, String actual) {
        if (actual.indexOf(expectedToBeContained) == -1
                || actual.indexOf(expectedToBeContained) != actual.lastIndexOf(expectedToBeContained)) {
            fail("Expected a unique occurrence of:\n" + expectedToBeContained + "\n in actual output:\n" + actual);
        }
    }

    protected void assertDoesNotContain(String expectedToBeContained, String actual) {
        if (actual.indexOf(expectedToBeContained) != -1) {
            fail("Did not expect to find data:\n" + expectedToBeContained + "\n in actual output:\n" + actual);
        }
    }

    protected ISMgr getFieldAccessor(Object o) {
        Class<?> clazz = o.getClass();
        try {
            Field f = clazz.getDeclaredField(Constants.fInstanceFieldsName);
            f.setAccessible(true);
            return (ISMgr) f.get(o);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    protected String getStaticFieldsMap(Class<?> clazz) {
        try {
            Field f = clazz.getDeclaredField(Constants.fStaticFieldsName);
            f.setAccessible(true);
            SSMgr m = (SSMgr) f.get(null);
            return m.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    PrintStream oldo, olde;
    ByteArrayOutputStream oso, ose;

    /**
     * Start intercepting the System.out/System.err streams
     */
    protected void captureOn() {
        oldo = System.out;
        olde = System.err;
        oso = new ByteArrayOutputStream();
        ose = new ByteArrayOutputStream();
        System.setOut(new PrintStream(oso));
        System.setErr(new PrintStream(ose));
    }

    protected static String toSlash(String dottedName) {
        return dottedName.replace('.', '/');
    }

    protected static String toDotted(String slashedName) {
        return slashedName.replace('/', '.');
    }

    /**
     * Stop intercepting the System.out/System.err streams and return any accumulated output since the captureOn.
     */
    protected String captureOff() {
        if (oldo == null) {
            throw new IllegalStateException("Turning capture off without having turned it on");
        }
        System.setOut(oldo);
        System.setErr(olde);
        oldo = null;
        olde = null;
        return new String("SYSOUT\n" + oso.toString().replace("\r", "") + "\nSYSERR\n"
                + ose.toString().replace("\r", "") + "\n");
    }

    protected String captureOffReturnStdout() {
        if (oldo == null) {
            throw new IllegalStateException("Turning capture off without having turned it on");
        }
        System.setOut(oldo);
        System.setErr(olde);
        oldo = null;
        olde = null;
        return new String(oso.toString());
    }

    /**
     * Called at the end of a test to tidy up in case a test crashed and failed to stop capturing.
     */
    protected void ensureCaptureOff() {
        if (oldo != null) {
            System.setOut(oldo);
            System.setErr(olde);
            oldo = null;
        }
    }

    /**
     * Execute a specific method, returning all output that occurred during the run to the caller.
     */
    public String runMethodAndCollectOutput(Class<?> clazz, String methodname) throws Exception {
        captureOn();
        Method m = clazz.getDeclaredMethod(methodname);
        if (!Modifier.isStatic(m.getModifiers())) {
            fail("Method should be static: " + m);
        }
        m.invoke(null);
        return captureOff();
    }

    protected String slashed(String dotted) {
        return dotted.replaceAll("\\.", "/");
    }

    protected final static void pause(int seconds) {
        try {
            Thread.sleep(seconds * 1000);
        } catch (Exception e) {
        }
    }

    protected void assertStdout(String expectedStdout, JVMOutput actualOutput) {
        if (!expectedStdout.equals(actualOutput.stdout)) {
            //         assertEquals(expectedStdout, actualOutput.stdout);
            fail("Expected stdout '" + expectedStdout + "' not found in \n" + actualOutput.toString());
        }
    }

    protected void assertStdoutContains(String expectedStdout, JVMOutput actualOutput) {
        if (!actualOutput.stdout.contains(expectedStdout)) {
            fail("Expected stdout:\n" + expectedStdout + "\nbut was:\n" + actualOutput.stdout.toString()
                    + "\nComplete output: \n" + actualOutput.toString());
        }
    }

    protected void assertStdoutContains(String expectedStdout, Result r) {
        // TODO Auto-generated method stub

    }

    protected void assertStderrContains(String expectedStderrContains, JVMOutput actualOutput) {
        if (actualOutput.stderr.indexOf(expectedStderrContains) == -1) {
            fail("Expected stderr to contain '" + expectedStderrContains + "'\n" + actualOutput.toString());
        }
    }

}