/*
 * 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.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.test.infra.ClassPrinter;
import org.springsource.loaded.test.infra.TestClassloaderWithRewriting;


public class GroovyTests extends SpringLoadedTests {

	@Before
	public void setUp() throws Exception {
		switchToGroovy();
	}

	// Accessing a field from another type, which just turns into property
	// access via a method
	@Test
	public void fields() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String a = "simple.Front";
		String b = "simple.Back";
		TypeRegistry r = getTypeRegistry(a + "," + b);
		ReloadableType rtypea = r.addType(a, loadBytesForClass(a));
		ReloadableType rtypeb = r.addType(b, loadBytesForClass(b));
		result = runUnguarded(rtypea.getClazz(), "run");
		assertEquals(35, result.returnValue);

		try {
			result = runUnguarded(rtypea.getClazz(), "run2");
			fail();
		} catch (InvocationTargetException ite) {
			// success - var2 doesn't exist yet
		}

		rtypeb.loadNewVersion("2", retrieveRename(b, b + "2"));

		result = runUnguarded(rtypea.getClazz(), "run2");
		assertEquals(3355, result.returnValue);
	}

	// test is too sensitive to changes between groovy compiler versions
	@Ignore
	@Test
	public void reflection() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String a = "simple.SelfReflector";
		TypeRegistry r = getTypeRegistry(a);
		ReloadableType rtypea = r.addType(a, loadBytesForClass(a));
		result = runUnguarded(rtypea.getClazz(), "run");

		assertEquals(
				"14 $callSiteArray $class$java$lang$String $class$java$lang$StringBuilder $class$java$lang$reflect$Field $class$java$util$ArrayList $class$java$util$Collections $class$java$util$Iterator $class$java$util$List $class$simple$SelfReflector $staticClassInfo __$stMC array$$class$java$lang$reflect$Field i metaClass",
				result.returnValue);
	}

	// TODO why doesn't this need swapInit? Is that only required for static
	// field constants?
	@Test
	public void localVariables() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String a = "simple.LFront";
		TypeRegistry r = getTypeRegistry(a);
		ReloadableType rtypea = r.addType(a, loadBytesForClass(a));
		result = runUnguarded(rtypea.getClazz(), "run");
		assertEquals("abc", result.returnValue);
		result = runUnguarded(rtypea.getClazz(), "run2");
		assertEquals(99, result.returnValue);

		rtypea.loadNewVersion("2", retrieveRename(a, a + "2"));

		result = runUnguarded(rtypea.getClazz(), "run");
		assertEquals("xxx", result.returnValue);
		result = runUnguarded(rtypea.getClazz(), "run2");
		assertEquals(88, result.returnValue);
	}

	@Test
	public void fieldsOnInstance() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String a = "simple.Front";
		String b = "simple.Back";
		TypeRegistry r = getTypeRegistry(a + "," + b);
		ReloadableType rtypea = r.addType(a, loadBytesForClass(a));
		ReloadableType rtypeb = r.addType(b, loadBytesForClass(b));
		Object instance = rtypea.getClazz().newInstance();
		result = runOnInstance(rtypea.getClazz(), instance, "run");
		assertEquals(35, result.returnValue);
		try {
			result = runOnInstance(rtypea.getClazz(), instance, "run2");
			fail();
		} catch (Exception e) {
			// success - var2 doesn't exist yet
		}
		// rtypea.fixupGroovyType();
		rtypeb.loadNewVersion("2", retrieveRename(b, b + "2"));

		result = runOnInstance(rtypea.getClazz(), instance, "run2");
		// The field will not be initialized, so will contain 0
		assertEquals(0, result.returnValue);
	}

	// Changing the return value within a method
	@Test
	public void basic() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.Basic";
		TypeRegistry r = getTypeRegistry(t);
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("hello", result.returnValue);
		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("goodbye", result.returnValue);
	}

	@Test
	public void basicInstance() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.Basic";
		TypeRegistry r = getTypeRegistry(t);
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		Object instance = null;
		instance = rtype.getClazz().newInstance();

		// First method call to 'run' should return "hello"
		result = runOnInstance(rtype.getClazz(), instance, "run");
		assertEquals("hello", result.returnValue);

		// Version 3 makes run() call another local method to get the string
		// "abc"
		rtype.loadNewVersion("3", retrieveRename(t, t + "3"));

		// Field f = rtype.getClazz().getDeclaredField("metaClass");
		// f.setAccessible(true);
		// Object mc = f.get(instance);
		// System.out.println("Metaclass is currently " + mc);
		//
		// f.set(instance, null);
		//
		// f =
		// rtype.getClazz().getDeclaredField("$class$groovy$lang$MetaClass");
		// f.setAccessible(true);
		// f.set(instance, null);
		//
		// Method m = rtype.getClazz().getDeclaredMethod("getMetaClass");
		// m.setAccessible(true);
		// m.invoke(instance);
		// f.setAccessible(true);
		// mc = f.get(instance);

		// 9: invokevirtual #23; //Method
		// $getStaticMetaClass:()Lgroovy/lang/MetaClass;
		// 12: dup
		// 13: invokestatic #27; //Method
		// $get$$class$groovy$lang$MetaClass:()Ljava/lang/Class;
		// 16: invokestatic #33; //Method
		// org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType:(Ljava/lang/Object;Ljav
		// a/lang/Class;)Ljava/lang/Object;
		// 19: checkcast #35; //class groovy/lang/MetaClass
		// 22: aload_0
		// 23: swap
		// 24: putfield #37; //Field metaClass:Lgroovy/lang/MetaClass;
		// 27: pop
		// 28: return

		// Method m = rtype.getClazz().getDeclaredMethod("$getStaticMetaClass");
		// m.setAccessible(true);
		// Object o = m.invoke(instance);
		// m =
		// rtype.getClazz().getDeclaredMethod("$get$$class$groovy$lang$MetaClass");
		// m.setAccessible(true);
		// Object p = m.invoke(null);
		// m =
		// rtype.getClazz().getClassLoader().loadClass("org.codehaus.groovy.runtime.ScriptBytecodeAdapter")
		// .getDeclaredMethod("castToType", Object.class, Class.class);
		// m.setAccessible(true);
		//
		// Object mc = m.invoke(null, o, p);
		// Field f = rtype.getClazz().getDeclaredField("metaClass");
		// f.setAccessible(true);
		// f.set(instance, null);

		// instance = rtype.getClazz().newInstance();

		// System.out.println("Metaclass is currently " + mc);
		// Let's reinitialize the instance meta class by duplicating

		result = runOnInstance(rtype.getClazz(), instance, "run");
		// result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("abc", result.returnValue);
	}

	// The method calls another method to get the return string, test
	// that when the method we are calling changes, we do call the new
	// one (simply checking the callsite cache is reset)
	@Test
	public void basic2() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.BasicB";
		TypeRegistry r = getTypeRegistry(t);
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("hello", result.returnValue);
		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("goodbye", result.returnValue);
	}

	// Similar to BasicB but now using non-static methods
	@Test
	public void basic3() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.BasicC";
		TypeRegistry r = getTypeRegistry(t);
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("hello", result.returnValue);
		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("goodbye", result.returnValue);
	}

	// Now calling between two different types to check if we have
	// to clear more than 'our' state on a reload. In this scenario
	// the method being called is static
	@Test
	public void basic4() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.BasicD";
		String target = "simple.BasicDTarget";
		TypeRegistry r = getTypeRegistry(t + "," + target);
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		ReloadableType rtypeTarget = r.addType(target, loadBytesForClass(target));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("hello", result.returnValue);
		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		rtypeTarget.loadNewVersion("2", retrieveRename(target, target + "2"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("abc", result.returnValue);
	}

	// Calling from one type to another, now the methods are non-static
	@Test
	public void basic5() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.BasicE";
		String target = "simple.BasicETarget";

		TypeRegistry r = getTypeRegistry(t + "," + target);

		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		ReloadableType rtypeTarget = r.addType(target, loadBytesForClass(target));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("hello", result.returnValue);

		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		rtypeTarget.loadNewVersion("2", retrieveRename(target, target + "2"));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foobar", result.returnValue);
	}

	// Now call a method on the target, then load a version where it is gone,
	// what happens?
	// I'm looking to determine what in the caller needs clearing out based on
	// what it has
	// cached about the target
	@Test
	public void basic6() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.BasicF";
		String target = "simple.BasicFTarget";
		// GlobalConfiguration.logging = true;
		// GlobalConfiguration.isRuntimeLogging = true;

		TypeRegistry r = getTypeRegistry(t + "," + target);

		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		ReloadableType rtypeTarget = r.addType(target, loadBytesForClass(target));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("123", result.returnValue);

		// rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		rtypeTarget.loadNewVersion("2", retrieveRename(target, target + "2"));

		result = null;

		// The target method has been removed, should now fail to call it
		try {
			runUnguarded(rtype.getClazz(), "run");
			fail();
		} catch (InvocationTargetException ite) {
			// success
		}

		// Load the original back in, should work again
		rtypeTarget.loadNewVersion("3", retrieveRename(target, target));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("123", result.returnValue);

		// rtype.loadNewVersion("2", rtype.bytesInitial); //reload yourself

		// Load a new version that now returns an int
		rtypeTarget.loadNewVersion("4", retrieveRename(target, target + "4"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("4456", result.returnValue);

	}

	@Ignore // test has intermittent problems
	// checking call site caching in the caller - do we need to clear it out?
	// Are we just not having to because isCompilable is off?
	@Test
	public void methodCallTargetComingAndGoing() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.BasicG";
		String target = "simple.BasicGTarget";
		TypeRegistry r = getTypeRegistry(t + "," + target);
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		ReloadableType rtypeTarget = r.addType(target, loadBytesForClass(target));

		// At this point BasicG.run() is calling a method that does not yet
		// exist on BasicGTarget
		try {
			result = runUnguarded(rtype.getClazz(), "run");
			fail();
		} catch (InvocationTargetException ite) {
			ite.printStackTrace();
			assertCause(ite, "MissingMethodException");
		}

		// Now a version of BasicGTarget is loaded that does define the method
		rtypeTarget.loadNewVersion("2", retrieveRename(target, target + "2"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("hw", result.returnValue);

		// Now load the original version so the method is gone again
		rtypeTarget.loadNewVersion("3", rtypeTarget.bytesInitial);// retrieveRename(target,
																	// target +
																	// "2"));
		try {
			result = runUnguarded(rtype.getClazz(), "run");
			fail();
		} catch (InvocationTargetException ite) {
			ite.printStackTrace();
			assertCause(ite, "MissingMethodException");
		}
		// Here is the stack when it fails with a NSME:
		// java.lang.reflect.InvocationTargetException
		// at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
		// at
		// sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
		// at
		// sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
		// at java.lang.reflect.Method.invoke(Method.java:597)
		// at
		// org.springsource.loaded.test.SpringLoadedTests.runUnguarded(SpringLoadedTests.java:201)
		// at
		// org.springsource.loaded.test.GroovyTests.methodCallTargetComingAndGoing(GroovyTests.java:340)
		// at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
		// at
		// sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
		// at
		// sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
		// at java.lang.reflect.Method.invoke(Method.java:597)
		// at
		// org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
		// at
		// org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
		// at
		// org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
		// at
		// org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
		// at
		// org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
		// at
		// org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
		// at
		// org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
		// at
		// org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
		// at
		// org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
		// at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
		// at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
		// at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
		// at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
		// at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
		// at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
		// at
		// org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
		// at
		// org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
		// at
		// org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
		// at
		// org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
		// at
		// org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
		// at
		// org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
		// Caused by: java.lang.NoSuchMethodError:
		// BasicGTarget.foo()Ljava/lang/String;
		// at
		// org.springsource.loaded.TypeRegistry.istcheck(TypeRegistry.java:1090)
		// at simple.BasicGTarget$foo.call(Unknown Source)
		// at simple.BasicG.run(BasicG.groovy:6)
		// ... 31 more
	}

	private void assertCause(Exception e, String string) {
		String s = e.getCause().toString();
		if (!s.contains(string)) {
			fail("Did not find string '" + string + "' in exception text:\n" + s);
		}
	}

	// Needs groovy 1.7.8
	@Test
	public void simpleValues() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "simple.Values";
		String target = "simple.BasicFTarget";
		// GlobalConfiguration.logging = true;
		// GlobalConfiguration.isRuntimeLogging = true;

		TypeRegistry r = getTypeRegistry(t + "," + target);

		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		// ReloadableType rtypeTarget = r.addType(target,
		// loadBytesForClass(target));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals(new Integer(123), result.returnValue);

		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		// rtypeTarget.loadNewVersion("2", retrieveRename(target, target +
		// "2"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals(new Integer(456), result.returnValue);
		//
		// result = null;
		//
		// // The target method has been removed, should now fail to call it
		// try {
		// runUnguarded(rtype.getClazz(), "run");
		// fail();
		// } catch (InvocationTargetException ite) {
		// // success
		// }
		//
		// // Load the original back in, should work again
		// rtypeTarget.loadNewVersion("3", retrieveRename(target, target));
		// result = runUnguarded(rtype.getClazz(), "run");
		// assertEquals("123", result.returnValue);
		//
		// // rtype.loadNewVersion("2", rtype.bytesInitial); //reload yourself
		//
		// // Load a new version that now returns an int
		// rtypeTarget.loadNewVersion("4", retrieveRename(target, target +
		// "4"));
		// result = runUnguarded(rtype.getClazz(), "run");
		// assertEquals("4456", result.returnValue);

	}

	@Ignore // something has changed in closure structure and the tests need debugging to get to the bottom of it
	/**
	 * Reloading code that is using a closure within a method body - no real changes, just checking it hangs together!
	 */
	@Test
	public void closure1() throws Exception {
		String t = "simple.BasicWithClosure";
		String c = "simple.BasicWithClosure$_run_closure1";
		TypeRegistry r = getTypeRegistry(t + "," + c);
		ReloadableType ctype = r.addType(c, loadBytesForClass(c));

		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:hello!", result.stdout);

		rtype.loadNewVersion("2",
				retrieveRename(t, t + "2", "simple.BasicWithClosure2$_run_closure1:simple.BasicWithClosure$_run_closure1"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:hello!", result.stdout);

		// Change what the closure does and reload it
		ctype.loadNewVersion("3", retrieveRename(c, "simple.BasicWithClosure3$_run_closure1"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:goodbye!", result.stdout);

		// reload an unchanged version - should behave as before
		rtype.loadNewVersion("3",
				retrieveRename(t, t + "3", "simple.BasicWithClosure3$_run_closure1:simple.BasicWithClosure$_run_closure1"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:goodbye!", result.stdout);
	}

	@Ignore // something has changed in closure structure and the tests need debugging to get to the bottom of it
	/**
	 * Now closure is initialized as a field (so in ctor) rather than inside a method
	 */
	@Test
	public void closure2() throws Exception {
		String t = "simple.BasicWithClosureB";
		String c = "simple.BasicWithClosureB$_closure1";
		TypeRegistry r = getTypeRegistry(t + "," + c);
		ReloadableType ctype = r.addType(c, loadBytesForClass(c));
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:hello!", result.stdout);

		rtype.loadNewVersion("2",
				retrieveRename(t, t + "2", "simple.BasicWithClosureB2$_closure1:simple.BasicWithClosureB$_closure1"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:hello!", result.stdout);

		// code in closure changes
		ctype.loadNewVersion("3", retrieveRename(c, "simple.BasicWithClosureB3$_closure1"));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("Executing:goodbye!", result.stdout);
	}

	@Ignore // something has changed in closure structure and the tests need debugging to get to the bottom of it
	/**
	 * Double nested closure - a method that is invoked on the owners owner.
	 */
	@Test
	public void closure3() throws Exception {
		String t = "simple.BasicWithClosureC";
		String c = "simple.BasicWithClosureC$_run_closure1";
		String c2 = "simple.BasicWithClosureC$_run_closure1_closure2";
		TypeRegistry r = getTypeRegistry(t + "," + c + "," + c2);
		ReloadableType ctype = r.addType(c, loadBytesForClass(c));
		ReloadableType c2type = r.addType(c2, loadBytesForClass(c2));
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foo() running\nin closure", result.stdout);

		rtype.loadNewVersion(
				"2",
				retrieveRename(t, t + "2", "simple.BasicWithClosureC2$_run_closure1:simple.BasicWithClosureC$_run_closure1",
						"simple.BasicWithClosureC2:simple.BasicWithClosureC"));

		c2type.loadNewVersion(
				"2",
				retrieveRename(c2, "simple.BasicWithClosureC2$_run_closure1_closure2",
						"simple.BasicWithClosureC2$_run_closure1:simple.BasicWithClosureC$_run_closure1",
						"simple.BasicWithClosureC2:simple.BasicWithClosureC"));

		ctype.loadNewVersion(
				"2",
				retrieveRename(c, "simple.BasicWithClosureC2$_run_closure1",
						"simple.BasicWithClosureC2$_run_closure1_closure2:simple.BasicWithClosureC$_run_closure1_closure2",
						"simple.BasicWithClosureC2:simple.BasicWithClosureC"));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("in closure\nfoo() running", result.stdout);
		//
		// // code in closure changes
		// ctype.loadNewVersion("3", retrieveRename(c,
		// "simple.BasicWithClosureB3$_closure1"));
		// result = runUnguarded(rtype.getClazz(), "run");
		// assertEquals("Executing:goodbye!", result.stdout);
	}

	@Ignore // something has changed in closure structure and the tests need debugging to get to the bottom of it
	/**
	 * Closures with references.
	 * 
	 * This is the testcase that shows the remaining limitation of constructor reloading (that can be fixed, with a bit of work).<br>
	 * 
	 * In the first version of the type the closure references a field 'sone' in outer scope. This means the ctor has an extra
	 * parameter of type Reference that is stuffed into a field in the closure that has the same name as the externally referenced
	 * thing (sone).
	 * 
	 * In the second version of the type the closure is changed to refer to a different field in the outer scope. Now called 'stwo'.
	 * This makes the constructor appear to have changed and so we run the funky ctor instead of the regular ctor. This wouldn't
	 * necessarily be a problem except it means we failed to call the super constructor with the right fields.
	 * 
	 * There are two solutions, the expensive general solution and the cheap solution for groovy closures. For now I may go with the
	 * latter. We know the shape of these things, and we know that a characteristic of generated closures if that they call the
	 * super ctor (Closure.<init>) passing in two parameters (owner,this). For Closure based types we can just do a special and
	 * instead of our funky ctor, we generate a special one that takes these two arguments and we use that one.
	 */
	@Test
	public void closure4_oneReference() throws Exception {
		String t = "simple.BasicWithClosureD";
		String c = "simple.BasicWithClosureD$_run_closure1";
		String c2 = "simple.BasicWithClosureD$_run_closure1_closure2";
		TypeRegistry r = getTypeRegistry(t + "," + c + "," + c2);
		ReloadableType ctype = r.addType(c, loadBytesForClass(c));
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foo() running\nstring is abc\nowner is not null", result.stdout);

		rtype.loadNewVersion(
				"2",
				retrieveRename(t, t + "2", "simple.BasicWithClosureD2$_run_closure1:simple.BasicWithClosureD$_run_closure1",
						"simple.BasicWithClosureD2:simple.BasicWithClosureD"));

		ctype.loadNewVersion(
				"2",
				retrieveRename(c, "simple.BasicWithClosureD2$_run_closure1",
						"simple.BasicWithClosureD2$_run_closure1_closure2:simple.BasicWithClosureD$_run_closure1_closure2",
						"simple.BasicWithClosureD2:simple.BasicWithClosureD"));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foo() running\nstring is def\nowner is not null", result.stdout);
	}

	@Ignore // something has changed in closure structure and the tests need debugging to get to the bottom of it
	/**
	 * Variation of testcase above, but now we move between 1/2/3 references on reload (to exercise new/old constructors
	 * 
	 */
	@Test
	public void closure5_multipleReferences() throws Exception {
		String t = "simple.BasicWithClosureE";
		String c = "simple.BasicWithClosureE$_run_closure1";
		TypeRegistry r = getTypeRegistry(t + "," + c);
		ReloadableType ctype = r.addType(c, loadBytesForClass(c));
		ReloadableType rtype = r.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foo() running\nstring is abc\nowner is not null", result.stdout);

		rtype.loadNewVersion(
				"2",
				retrieveRename(t, t + "2", "simple.BasicWithClosureE2$_run_closure1:simple.BasicWithClosureE$_run_closure1",
						"simple.BasicWithClosureE2:simple.BasicWithClosureE"));

		ctype.loadNewVersion("2",
				retrieveRename(c, "simple.BasicWithClosureE2$_run_closure1", "simple.BasicWithClosureE2:simple.BasicWithClosureE"));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foo() running\nstring is abc def\nowner is not null", result.stdout);

		rtype.loadNewVersion(
				"3",
				retrieveRename(t, t + "3", "simple.BasicWithClosureE3$_run_closure1:simple.BasicWithClosureE$_run_closure1",
						"simple.BasicWithClosureE3:simple.BasicWithClosureE"));

		ctype.loadNewVersion("3",
				retrieveRename(c, "simple.BasicWithClosureE3$_run_closure1", "simple.BasicWithClosureE3:simple.BasicWithClosureE"));

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("foo() running\nstring is xyz\nowner is not null", result.stdout);
	}

	// TODO testcases:
	// multiple external references
	// external reference is to a primitive type?

	// TODO run these tests without -noverify, why are they failing
	// verification?

	@Ignore // something has changed in closure structure and the tests need debugging to get to the bottom of it
	/**
	 * Testing the grails controller usage of closure fields.
	 * 
	 * Ok. Closure names look like this for controller fields: Controller$_closureN where N is the number down the file.
	 * 
	 * if you introduce a new earlier one, that will 'replace' the one you were using before, and as the constructor is not re-run,
	 * you don't see the change. Two options:
	 * <ul>
	 * <li>repairing the damage
	 * <li>rerunning the ctor
	 * </ul>
	 */
	@Test
	public void testControllers1() throws Exception {
		String t = "controller.Controller";
		String closure = "controller.Controller$_closure1";
		TypeRegistry r = getTypeRegistry(t + "," + closure);
		ReloadableType ttype = r.addType(t, loadBytesForClass(t));
		ReloadableType closuretype = r.addType(closure, loadBytesForClass(closure));

		result = runUnguarded(ttype.getClazz(), "execute");
		assertEquals("[action:list, params:2]", result.stdout);

		// Change the body of the 'index' closure
		closuretype.loadNewVersion("2", retrieveRename(closure, "controller.Controller2$_closure1"));
		result = runUnguarded(ttype.getClazz(), "execute");
		assertEquals("[action:custard, params:345]", result.stdout);

		// Introduced a new closure, left the index one unchanged...
		closuretype.loadNewVersion("3", retrieveRename(closure, "controller.Controller3$_closure1"));

		result = runUnguarded(ttype.getClazz(), "execute");
		System.out.println(result);
		// assertEquals("[action:custard, params:345]", result.stdout);

	}

	@Test
	public void staticInitializerReloading1() throws Exception {
		String t = "clinitg.One";
		TypeRegistry typeRegistry = getTypeRegistry(t);
		ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("5", result.returnValue);
		rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
		rtype.runStaticInitializer(); // call is made on reloadable type
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("7", result.returnValue);
	}

	@Test
	public void staticInitializerReloading2() throws Exception {
		String t = "clinitg.One";
		TypeRegistry typeRegistry = getTypeRegistry(t);
		ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("5", result.returnValue);
		rtype.loadNewVersion("39", retrieveRename(t, t + "2"));

		// use the 'new' ___clinit___ method to drive the static initializer
		Method staticInitializer = rtype.getClazz().getMethod("___clinit___");
		assertNotNull(staticInitializer);
		staticInitializer.invoke(null);

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("7", result.returnValue);
	}

	/**
	 * Dealing with final fields. This test was passing until groovy started really inlining the final fields. After doing
	 * so it isn't sufficient to run the static initializer to get them set to the new values.
	 */
	@Ignore
	@Test
	public void staticInitializerReloading3() throws Exception {
		String t = "clinitg.Two";
		TypeRegistry typeRegistry = getTypeRegistry(t);
		ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("55", result.returnValue);
		rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
		rtype.runStaticInitializer();
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("99", result.returnValue);
	}

	/**
	 * Reloading enums written in groovy - very simple enum
	 */
	@Test
	public void enums() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String enumtype = "enums.WhatAnEnum";
		String intface = "enums.ExtensibleEnum";
		String runner = "enums.RunnerA";
		TypeRegistry typeRegistry = getTypeRegistry(enumtype + "," + intface + "," + runner);
//		ReloadableType rtypeIntface = 
		typeRegistry.addType(intface, loadBytesForClass(intface));
		ReloadableType rtypeEnum = typeRegistry.addType(enumtype, loadBytesForClass(enumtype));
		ReloadableType rtypeRunner = typeRegistry.addType(runner, loadBytesForClass(runner));
		result = runUnguarded(rtypeRunner.getClazz(), "run");
		// ClassPrinter.print(rtypeEnum.bytesInitial);
		assertContains("[RED GREEN BLUE]", result.stdout);
		System.out.println(result);
		byte[] bs = retrieveRename(enumtype, enumtype + "2",
				"enums.WhatAnEnum2$__clinit__closure1:enums.WhatAnEnum$__clinit__closure1",
				"[Lenums/WhatAnEnum2;:[Lenums/WhatAnEnum;",
				"Lenums/WhatAnEnum2;:Lenums/WhatAnEnum;",
				"enums/WhatAnEnum2:enums/WhatAnEnum");
		ClassPrinter.print(bs);
		rtypeEnum.loadNewVersion(bs);
		result = runUnguarded(rtypeRunner.getClazz(), "run");
		System.out.println(result);
		assertContains(
				"[RED GREEN BLUE YELLOW]",
				result.stdout);

		// assertEquals("55", result.returnValue);
		// rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
		// rtype.runStaticInitializer();
		// result = runUnguarded(rtype.getClazz(), "run");
		// assertEquals("99", result.returnValue);
	}

	/**
	 * Reloading enums - more complex enum (grails-7776)
	 */
	@Test
	public void enums2() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String enumtype = "enums.WhatAnEnumB";
		String intface = "enums.ExtensibleEnumB";
		String runner = "enums.RunnerB";
		String closure = "enums.WhatAnEnumB$__clinit__closure1";
		TypeRegistry typeRegistry = getTypeRegistry(enumtype + "," + intface + "," + runner + "," + closure);
//		ReloadableType rtypeIntface =
		typeRegistry.addType(intface, loadBytesForClass(intface));
		ReloadableType rtypeClosure = typeRegistry.addType(closure, loadBytesForClass(closure));
		ReloadableType rtypeEnum = typeRegistry.addType(enumtype, loadBytesForClass(enumtype));
		ReloadableType rtypeRunner = typeRegistry.addType(runner, loadBytesForClass(runner));
		result = runUnguarded(rtypeRunner.getClazz(), "run");
		assertContains(
				"[PETS_AT_THE_DISCO 1 JUMPING_INTO_A_HOOP 2 HAVING_A_NICE_TIME 3 LIVING_ON_A_LOG 4 WHAT_DID_YOU_DO 5 UNKNOWN 0]",
				result.stdout);

		byte[] cs = retrieveRename(closure, "enums.WhatAnEnumB2$__clinit__closure1", "enums.WhatAnEnumB2:enums.WhatAnEnumB");
		rtypeClosure.loadNewVersion(cs);
		byte[] bs = retrieveRename(enumtype, enumtype + "2",
				"enums.WhatAnEnumB2$__clinit__closure1:enums.WhatAnEnumB$__clinit__closure1",
				"[Lenums/WhatAnEnumB2;:[Lenums/WhatAnEnumB;","enums/WhatAnEnumB2:enums/WhatAnEnumB");
		rtypeEnum.loadNewVersion(bs);
		result = runUnguarded(rtypeRunner.getClazz(), "run");
		System.out.println(result);
		assertContains(
				"[PETS_AT_THE_DISCO 1 JUMPING_INTO_A_HOOP 2 HAVING_A_NICE_TIME 3 LIVING_ON_A_LOG 4 WHAT_DID_YOU_DO 5 WOBBLE 6 UNKNOWN 0]",
				result.stdout);
	}

	/**
	 * Type that doesn't really have a clinit
	 */
	@Ignore // Needs investigating...likely a change in groovy bytecode format tripping us up
	@Test
	public void staticInitializerReloading4() throws Exception {
		String t = "clinitg.Three";
		TypeRegistry typeRegistry = getTypeRegistry(t);
		ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("1", result.returnValue);
		rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
		rtype.runStaticInitializer();
		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("1", result.returnValue);
		rtype.loadNewVersion("3", retrieveRename(t, t + "3"));
		// Dont need to do this - think that is because of some of the constant
		// reinit stuff we already
		// drive in groovy
		rtype.runStaticInitializer();
		result = runUnguarded(rtype.getClazz(), "run");
		// ClassPrinter.print(rtype.getLatestExecutorBytes());
		assertEquals("4", result.returnValue);
	}

	/**
	 * Loading a type and not immediately running the clinit.
	 * 
	 * <p>
	 * This is to cover the problem where some type is loaded but not immediately initialized, it is then reloaded before
	 * initialization (i.e. before the clinit has run). If it is a groovy type then we are going to poke at it during reloading (to
	 * clear some caches). This poking may trigger the clinit to run. Now, the helper methods (like those that setup the
	 * callsitecache) will be using the 'new version' but the clinit hasn't been redirected to the reloaded version and so it
	 * indexes into the callsite cache using wrong indices.
	 */
	@Ignore // until I can find time
	@Test
	public void staticInitializerReloading5() throws Exception {
		binLoader = new TestClassloaderWithRewriting();
		String t = "clinitg.Four";
		String t2 = "clinitg.FourHelper";
		TypeRegistry typeRegistry = getTypeRegistry(t);
		ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
		// ReloadableType rtype2 =
		typeRegistry.addType(t2, loadBytesForClass(t2));
		typeRegistry.getClassLoader().loadClass(t); // load it but do not initialize it
		captureOn();
		byte[] renamed = retrieveRename(t, t + "2");
		rtype.loadNewVersion("2", renamed); // reload it, this will trigger initialization
		String s = captureOffReturnStdout();
		assertEquals("1a", s);

		result = runUnguarded(rtype.getClazz(), "run");
		assertEquals("1312", result.stdout);
	}

}