/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.framework.core; import com.intellij.openapi.project.Project; import com.intellij.util.ui.UIUtil; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.module.IModule; import gw.plugin.ij.core.PluginLoaderUtil; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; public abstract class StatefulTestCase extends TestCase { @Nullable private static IModule rootModule = null; private static StatefulTestCase statefulTestInstance = new MyStatefulTestCase(); private static final Map<Class, Set<String>> allTests = new HashMap(); private static final Map<Class, Set<String>> runTests = new HashMap(); protected enum ExecutionState { PENDING, EXECUTING, COMPLETE, } public static final AtomicReference<ExecutionState> _afterClassExecuting = new AtomicReference<>(ExecutionState.PENDING); public static final Object _afterClassMonitor = new Object(); static { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { try { _afterClassExecuting.set(ExecutionState.EXECUTING); SwingUtilities.invokeAndWait(new Runnable() { public void run() { if (statefulTestInstance != null) { try { statefulTestInstance.afterClass(); } catch (Exception e) { e.printStackTrace(); } } } }); } catch (Exception e) { e.printStackTrace(); } _afterClassExecuting.set(ExecutionState.COMPLETE); synchronized (_afterClassMonitor) { _afterClassMonitor.notifyAll(); } } }); } @Nullable public abstract Project getProject(); @Override public void setName(String name) { addTestName(getClass(), name, allTests); super.setName(name); } private void addTestName(Class aClass, String name, @NotNull Map<Class, Set<String>> map){ Set<String> strings = map.get(aClass); if (strings == null) { strings = new HashSet<>(); map.put(aClass, strings); } strings.add(name); } protected boolean runInDispatchThread() { return true; } protected void invokeInUIThread(@NotNull final VoidCallback runnable) throws Throwable { final Throwable[] exception = {null}; UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { Project project = getProject(); IModule root = null; if( project != null ) { root = TypeSystem.getExecutionEnvironment( PluginLoaderUtil.getFrom( project ) ).getGlobalModule(); if (root != null) { TypeSystem.pushModule(root); } } try { runnable.run(); } catch (Throwable tearingDown) { if (exception[0] == null) exception[0] = tearingDown; } finally { if (root != null) { TypeSystem.popModule(root); } } } }); if (exception[0] != null) throw exception[0]; } public void runBare() throws Throwable { if (runTests.get(getClass()) == null) { invokeInUIThread(new VoidCallback( getProject() ) { public void run() throws Throwable { statefulTestInstance = StatefulTestCase.this.getClass().newInstance(); statefulTestInstance.beforeClass(); } }); } statefulTestInstance.setName(this.getName()); invokeInUIThread(new VoidCallback( getProject() ) { public void run() throws Throwable { statefulTestInstance.beforeMethod(); } }); Throwable exception = null; try { if (runInDispatchThread()) { invokeInUIThread(new VoidCallback( getProject() ) { public void run() throws Throwable { statefulTestInstance.runTest(); } }); } else { final IModule rootModule = TypeSystem.getGlobalModule(); TypeSystem.pushModule(rootModule); try { statefulTestInstance.runTest(); } finally { TypeSystem.popModule(rootModule); } } } catch (Throwable running) { exception = running; } finally { try { invokeInUIThread(new VoidCallback( getProject() ) { public void run() throws Throwable { statefulTestInstance.afterMethod(); } }); } catch (Throwable tearingDown) { if (exception == null) exception = tearingDown; } finally { addTestName(getClass(), getName(), runTests); if (runTests.get(getClass()).equals(allTests.get(getClass()))) { try { invokeInUIThread(new VoidCallback( getProject() ) { public void run() throws Throwable { statefulTestInstance.afterClass(); } }); } catch (Throwable tearingDown) { if (exception == null) exception = tearingDown; } } } } if (exception != null) throw exception; } protected void beforeClass() throws Exception { } protected void afterClass() throws Exception { } protected void beforeMethod() throws Exception { } protected void afterMethod() throws Exception { } private static class MyStatefulTestCase extends StatefulTestCase { @NotNull public Project getProject() { throw new UnsupportedOperationException(); } } }