/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.framework; import com.intellij.compiler.impl.CompileDriver; import com.intellij.execution.ExecutionException; import com.intellij.execution.Executor; import com.intellij.execution.application.ApplicationConfiguration; import com.intellij.execution.application.ApplicationConfigurationType; import com.intellij.execution.configurations.RunnerSettings; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessListener; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.compiler.CompileContext; import com.intellij.openapi.compiler.CompileStatusNotification; import com.intellij.openapi.compiler.CompilerFilter; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.util.concurrency.Semaphore; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.module.IModule; import gw.plugin.ij.compiler.GosuCompiler; import gw.plugin.ij.core.ModuleClasspathListener; import gw.plugin.ij.core.PluginFailureReason; import gw.plugin.ij.core.PluginLoaderUtil; import gw.plugin.ij.framework.core.DaemonAnalyzerTestCase; import gw.plugin.ij.sdk.GosuSdkUtils; import gw.plugin.ij.util.ExceptionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; abstract public class GosuTestCase extends DaemonAnalyzerTestCase { private static IModule pushedModule; @Override protected void beforeClass() throws Exception { boolean previousValue = ModuleClasspathListener.ENABLED; ModuleClasspathListener.ENABLED = false; try { // setup Modules for the test class super.beforeClass(); ExceptionUtil._inTestMode = true; } finally { ModuleClasspathListener.ENABLED = previousValue; } } @Override protected void runStartupActivities() { // reinitialize type system with new set modules for the test class if (PluginLoaderUtil.instance(getProject()).isStarted()) { throw new RuntimeException("The plugin should have been stopped by the previous test class."); } // start IJEditorPluginLoader try { startPlugin(); } catch (ClassNotFoundException e) { e.printStackTrace(); fail("Unable to initialize type system"); } catch (Exception e) { e.printStackTrace(); fail("Unable to start plugin"); } super.runStartupActivities(); } @Override protected void afterClass() throws Exception { boolean previousValue = ModuleClasspathListener.ENABLED; ModuleClasspathListener.ENABLED = false; try { if (PluginLoaderUtil.instance(getProject()).isStarted()) { deleteAllFiles(); stopPlugin(); } } finally { super.afterClass(); ModuleClasspathListener.ENABLED = previousValue; } } @Override protected void invokeTestRunnable(@NotNull final Runnable runnable) throws Exception { final IModule module = TypeSystem.getExecutionEnvironment(PluginLoaderUtil.getFrom(getProject())).getGlobalModule(); final Runnable runnable2 = new Runnable() { @Override public void run() { TypeSystem.pushModule( module ); try { runnable.run(); } finally { TypeSystem.popModule( module ); } } }; super.invokeTestRunnable(runnable2); } private void startPlugin() throws Exception { getProject().putUserData( PluginLoaderUtil.TEST_ROOT_DISPOSABLE, myTestRootDisposable ); PluginLoaderUtil.instance(getProject()).startPLugin(); System.out.println("Starting plugin on project " + getProject().getBasePath()); if (!PluginLoaderUtil.instance(getProject()).isStartupOk()) { throw new Exception("Unable to start Gosu plugin", PluginLoaderUtil.instance(getProject()).getStartupError()); } } private void stopPlugin() { PluginLoaderUtil.instance(getProject()).closeProject( PluginFailureReason.NONE ); } public ProcessHandler runProcess(String className, Module module, final Class<? extends Executor> executorClass, final ProcessListener listener, @NotNull final ProgramRunner runner) throws ExecutionException { final ApplicationConfiguration configuration = new ApplicationConfiguration("app", getProject(), ApplicationConfigurationType.getInstance()); configuration.setModule(module); configuration.setMainClassName(className); final Executor executor = Executor.EXECUTOR_EXTENSION_NAME.findExtension(executorClass); final ExecutionEnvironment environment = new ExecutionEnvironment(configuration, getProject(), new RunnerSettings<>(null, null), null, null); final Semaphore semaphore = new Semaphore(); semaphore.down(); final AtomicReference<ProcessHandler> processHandler = new AtomicReference<>(); runner.execute(executor, environment, new ProgramRunner.Callback() { @Override public void processStarted(@NotNull final RunContentDescriptor descriptor) { disposeOnTearDown(new Disposable() { @Override public void dispose() { descriptor.dispose(); } }); final ProcessHandler handler = descriptor.getProcessHandler(); assert handler != null; handler.addProcessListener(listener); processHandler.set(handler); semaphore.up(); } }); semaphore.waitFor(); return processHandler.get(); } public void rebuildProject(boolean onlyGosu) { final CompileDriver compileDriver = new CompileDriver(getProject()); if (onlyGosu) { compileDriver.setCompilerFilter(new CompilerFilter() { public boolean acceptCompiler(com.intellij.openapi.compiler.Compiler compiler) { return compiler instanceof GosuCompiler; } }); } compileDriver.rebuild(new CompileStatusNotification() { public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) { synchronized (compileDriver) { compileDriver.notifyAll(); } } }); synchronized (compileDriver) { try { compileDriver.wait(); } catch (InterruptedException e) { } } } @NotNull public static String removeHeaderComment(@NotNull String code) { final int beginComment = code.indexOf("/*"); final int endComment = code.indexOf("*/"); final String firstSegment = code.substring(0, beginComment); final String secondSegment = code.substring(endComment + 2, code.length()); return firstSegment + secondSegment; } @Nullable @Override protected Sdk getTestProjectJdk() { Sdk gdk = GosuSdkUtils.initDefaultGosuSDK(); if (gdk == null) { throw new RuntimeException("Default Gosu SDK could not be found!"); } List<File> extraPaths = getExtraSDKPaths(); if (extraPaths != null) { SdkModificator modificator = gdk.getSdkModificator(); GosuSdkUtils.addSdkElements(modificator, extraPaths); modificator.commitChanges(); } return gdk; } private List<File> getExtraSDKPaths( ) { List<File> extraClasspath = new ArrayList<>(); String classpath = System.getProperty("java.class.path", ""); for( String path: classpath.split( File.pathSeparator ) ) { if( !extraClasspath.contains( path ) ) { extraClasspath.add( new File(path) ); } } return extraClasspath; } public static void runWriteActionInDispatchThread(@NotNull final Runnable operation, boolean blocking) { final Application application = ApplicationManager.getApplication(); final Runnable action = new Runnable() { public void run() { application.runWriteAction(operation); } }; if (application.isDispatchThread()) { action.run(); } else if (blocking) { application.invokeAndWait(action, ModalityState.defaultModalityState()); } else { application.invokeLater(action); } } }