execution: extract check for alternative jre; check for working directory (IDEA-77754)
[idea/community.git] / plugins / junit / src / com / intellij / execution / junit / TestObject.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.execution.junit;
18
19 import com.intellij.ExtensionPoints;
20 import com.intellij.execution.*;
21 import com.intellij.execution.configurations.*;
22 import com.intellij.execution.junit2.TestProxy;
23 import com.intellij.execution.junit2.segments.DeferredActionsQueue;
24 import com.intellij.execution.junit2.segments.DeferredActionsQueueImpl;
25 import com.intellij.execution.junit2.segments.DispatchListener;
26 import com.intellij.execution.junit2.segments.Extractor;
27 import com.intellij.execution.junit2.ui.JUnitTreeConsoleView;
28 import com.intellij.execution.junit2.ui.TestsPacketsReceiver;
29 import com.intellij.execution.junit2.ui.actions.RerunFailedTestsAction;
30 import com.intellij.execution.junit2.ui.model.JUnitRunningModel;
31 import com.intellij.execution.junit2.ui.model.RootTestInfo;
32 import com.intellij.execution.junit2.ui.properties.JUnitConsoleProperties;
33 import com.intellij.execution.process.ProcessAdapter;
34 import com.intellij.execution.process.ProcessEvent;
35 import com.intellij.execution.runners.ProgramRunner;
36 import com.intellij.execution.testframework.*;
37 import com.intellij.execution.ui.ConsoleViewContentType;
38 import com.intellij.execution.util.JavaParametersUtil;
39 import com.intellij.execution.util.ProgramParametersUtil;
40 import com.intellij.openapi.Disposable;
41 import com.intellij.openapi.application.ApplicationManager;
42 import com.intellij.openapi.diagnostic.Logger;
43 import com.intellij.openapi.extensions.Extensions;
44 import com.intellij.openapi.module.Module;
45 import com.intellij.openapi.project.Project;
46 import com.intellij.openapi.projectRoots.JavaSdkType;
47 import com.intellij.openapi.projectRoots.Sdk;
48 import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
49 import com.intellij.openapi.roots.ModuleRootManager;
50 import com.intellij.openapi.roots.ProjectRootManager;
51 import com.intellij.openapi.util.Comparing;
52 import com.intellij.openapi.util.Disposer;
53 import com.intellij.openapi.util.Getter;
54 import com.intellij.openapi.util.Key;
55 import com.intellij.openapi.util.io.FileUtil;
56 import com.intellij.psi.PsiClass;
57 import com.intellij.psi.PsiElement;
58 import com.intellij.psi.PsiMethod;
59 import com.intellij.psi.PsiPackage;
60 import com.intellij.refactoring.listeners.RefactoringElementListener;
61 import com.intellij.rt.execution.junit.IDEAJUnitListener;
62 import com.intellij.rt.execution.junit.JUnitStarter;
63 import com.intellij.util.Function;
64 import com.intellij.util.IJSwingUtilities;
65 import com.intellij.util.PathUtil;
66 import org.jetbrains.annotations.NotNull;
67
68 import java.io.File;
69 import java.io.IOException;
70 import java.io.PrintWriter;
71 import java.util.ArrayList;
72 import java.util.Collection;
73 import java.util.Collections;
74 import java.util.List;
75
76 public abstract class TestObject implements JavaCommandLine {
77   protected static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit.TestObject");
78
79   private static final String MESSAGE = ExecutionBundle.message("configuration.not.speficied.message");
80
81   protected JavaParameters myJavaParameters;
82   private final Project myProject;
83   protected final JUnitConfiguration myConfiguration;
84   private final RunnerSettings myRunnerSettings;
85   private final ConfigurationPerRunnerSettings myConfigurationSettings;
86   protected File myTempFile = null;
87   public File myListenersFile;
88
89   public static TestObject fromString(final String id,
90                                       final Project project,
91                                       final JUnitConfiguration configuration,
92                                       RunnerSettings runnerSettings, ConfigurationPerRunnerSettings configurationSettings) {
93     if (JUnitConfiguration.TEST_METHOD.equals(id))
94       return new TestMethod(project, configuration, runnerSettings, configurationSettings);
95     if (JUnitConfiguration.TEST_CLASS.equals(id))
96       return new TestClass(project, configuration, runnerSettings, configurationSettings);
97     if (JUnitConfiguration.TEST_PACKAGE.equals(id))
98       return new TestPackage(project, configuration, runnerSettings, configurationSettings);
99     else if (JUnitConfiguration.TEST_DIRECTORY.equals(id)) {
100       return new TestDirectory(project, configuration, runnerSettings, configurationSettings);
101     }
102     if (JUnitConfiguration.TEST_PATTERN.equals(id)) {
103       return new TestsPattern(project, configuration, runnerSettings, configurationSettings);
104     }
105     return NOT_CONFIGURED;
106   }
107
108   public Module[] getModulesToCompile() {
109     final SourceScope sourceScope = getSourceScope();
110     return sourceScope != null ? sourceScope.getModulesToCompile() : Module.EMPTY_ARRAY;
111   }
112
113   protected TestObject(final Project project,
114                        final JUnitConfiguration configuration,
115                        RunnerSettings runnerSettings,
116                        ConfigurationPerRunnerSettings configurationSettings) {
117     myProject = project;
118     myConfiguration = configuration;
119     myRunnerSettings = runnerSettings;
120     myConfigurationSettings = configurationSettings;
121   }
122
123   public abstract String suggestActionName();
124
125   public RunnerSettings getRunnerSettings() {
126     return myRunnerSettings;
127   }
128
129   public ConfigurationPerRunnerSettings getConfigurationSettings() {
130     return myConfigurationSettings;
131   }
132
133   public abstract RefactoringElementListener getListener(PsiElement element, JUnitConfiguration configuration);
134
135   public abstract boolean isConfiguredByElement(JUnitConfiguration configuration,
136                                                 PsiClass testClass,
137                                                 PsiMethod testMethod,
138                                                 PsiPackage testPackage);
139
140   protected void configureModule(final JavaParameters parameters, final RunConfigurationModule configurationModule, final String mainClassName)
141     throws CantRunException {
142     int classPathType = JavaParametersUtil.getClasspathType(configurationModule, mainClassName, true);
143     JavaParametersUtil.configureModule(configurationModule, parameters, classPathType,
144                                        myConfiguration.isAlternativeJrePathEnabled() ? myConfiguration.getAlternativeJrePath() : null);
145   }
146
147   private static final TestObject NOT_CONFIGURED = new TestObject(null, null, null, null) {
148     public RefactoringElementListener getListener(final PsiElement element, final JUnitConfiguration configuration) {
149       return null;
150     }
151
152     @Override
153     public String suggestActionName() {
154       throw new RuntimeException(String.valueOf(myConfiguration));
155     }
156
157     @Override
158     public boolean isConfiguredByElement(final JUnitConfiguration configuration,
159                                          PsiClass testClass,
160                                          PsiMethod testMethod,
161                                          PsiPackage testPackage) {
162       return false;
163     }
164
165     @Override
166     public void checkConfiguration() throws RuntimeConfigurationException {
167       throw new RuntimeConfigurationError(MESSAGE);
168     }
169
170     @Override
171     public JavaParameters getJavaParameters() throws ExecutionException {
172       throw new ExecutionException(MESSAGE);
173     }
174
175     @Override
176     protected void initialize() throws ExecutionException {
177       throw new ExecutionException(MESSAGE);
178     }
179   };
180
181   public void checkConfiguration() throws RuntimeConfigurationException{
182     JavaParametersUtil.checkAlternativeJRE(myConfiguration);
183     ProgramParametersUtil.checkWorkingDirectoryExist(myConfiguration, myConfiguration.getProject(), myConfiguration.getConfigurationModule().getModule());
184   }
185
186   public SourceScope getSourceScope() {
187     return SourceScope.modulesWithDependencies(myConfiguration.getModules());
188   }
189
190   protected void initialize() throws ExecutionException {
191     JavaParametersUtil.configureConfiguration(myJavaParameters, myConfiguration);
192     myJavaParameters.setMainClass(JUnitConfiguration.JUNIT_START_CLASS);
193     final Module module = myConfiguration.getConfigurationModule().getModule();
194     if (myJavaParameters.getJdk() == null){
195       myJavaParameters.setJdk(module != null
196                               ? ModuleRootManager.getInstance(module).getSdk()
197                               : ProjectRootManager.getInstance(myProject).getProjectSdk());
198     }
199
200     myJavaParameters.getClassPath().add(JavaSdkUtil.getIdeaRtJarPath());
201     myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(JUnitStarter.class));
202     myJavaParameters.getProgramParametersList().add(JUnitStarter.IDE_VERSION + JUnitStarter.VERSION);
203     for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
204       ext.updateJavaParameters(myConfiguration, myJavaParameters, myRunnerSettings);
205     }
206
207     final Object[] listeners = Extensions.getExtensions(IDEAJUnitListener.EP_NAME);
208     final StringBuilder buf = new StringBuilder();
209     for (final Object listener : listeners) {
210       boolean enabled = true;
211       for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
212         if (ext.isListenerDisabled(myConfiguration, listener, myRunnerSettings)) {
213           enabled = false;
214           break;
215         }
216       }
217       if (enabled) {
218         final Class classListener = listener.getClass();
219         buf.append(classListener.getName()).append("\n");
220         myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(classListener));
221       }
222     }
223     if (buf.length() > 0) {
224       try {
225         myListenersFile = FileUtil.createTempFile("junit_listeners_", "");
226         myListenersFile.deleteOnExit();
227         myJavaParameters.getProgramParametersList().add("@@" + myListenersFile.getPath());
228         FileUtil.writeToFile(myListenersFile, buf.toString().getBytes());
229       }
230       catch (IOException e) {
231         LOG.error(e);
232       }
233     }
234   }
235
236   public JavaParameters getJavaParameters() throws ExecutionException {
237     if (myJavaParameters == null) {
238       myJavaParameters = new JavaParameters();
239       initialize();
240       final Module module = myConfiguration.getConfigurationModule().getModule();
241       final Object[] patchers = Extensions.getExtensions(ExtensionPoints.JUNIT_PATCHER);
242       for (Object patcher : patchers) {
243         ((JUnitPatcher)patcher).patchJavaParameters(module, myJavaParameters);
244       }
245     }
246     return myJavaParameters;
247   }
248
249   public ExecutionResult execute(final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
250     final JUnitProcessHandler handler = createHandler();
251     final RunnerSettings runnerSettings = getRunnerSettings();
252     JavaRunConfigurationExtensionManager.getInstance().attachExtensionsToProcess(myConfiguration, handler, runnerSettings);
253     final TestProxy unboundOutputRoot = new TestProxy(new RootTestInfo());
254     final JUnitConsoleProperties consoleProperties = new JUnitConsoleProperties(myConfiguration, executor);
255     final JUnitTreeConsoleView consoleView = new JUnitTreeConsoleView(consoleProperties, runnerSettings, getConfigurationSettings(), unboundOutputRoot);
256     consoleView.initUI();
257     consoleView.attachToProcess(handler);
258     unboundOutputRoot.setPrinter(consoleView.getPrinter());
259     Disposer.register(consoleView, unboundOutputRoot);
260     final TestsPacketsReceiver packetsReceiver = new TestsPacketsReceiver(consoleView, unboundOutputRoot) {
261       @Override
262       public void notifyStart(TestProxy root) {
263         super.notifyStart(root);
264         unboundOutputRoot.addChild(root);
265         if (myConfiguration.isSaveOutputToFile()) {
266           unboundOutputRoot.setOutputFilePath(myConfiguration.getOutputFilePath());
267         }
268         final JUnitRunningModel model = getModel();
269         if (model != null) {
270           handler.getOut().setDispatchListener(model.getNotifier());
271           Disposer.register(model, new Disposable() {
272             public void dispose() {
273               handler.getOut().setDispatchListener(DispatchListener.DEAF);
274             }
275           });
276           consoleView.attachToModel(model);
277         }
278       }
279     };
280
281     final DeferredActionsQueue queue = new DeferredActionsQueueImpl();
282     handler.getOut().setPacketDispatcher(packetsReceiver, queue);
283     handler.getErr().setPacketDispatcher(packetsReceiver, queue);
284
285     handler.addProcessListener(new ProcessAdapter() {
286       @Override
287       public void processTerminated(ProcessEvent event) {
288         handler.removeProcessListener(this);
289         if (myTempFile != null) {
290           FileUtil.delete(myTempFile);
291         }
292         if (myListenersFile != null) {
293           FileUtil.delete(myListenersFile);
294         }
295         IJSwingUtilities.invoke(new Runnable() {
296           public void run() {
297             unboundOutputRoot.flush();
298             packetsReceiver.checkTerminated();
299             final JUnitRunningModel model = packetsReceiver.getModel();
300             notifyByBalloon(model, consoleProperties);
301           }
302         });
303       }
304
305       @Override
306       public void onTextAvailable(final ProcessEvent event, final Key outputType) {
307         final String text = event.getText();
308         final ConsoleViewContentType consoleViewType = ConsoleViewContentType.getConsoleViewType(outputType);
309         final Printable printable = new Printable() {
310           public void printOn(final Printer printer) {
311             printer.print(text, consoleViewType);
312           }
313         };
314         final Extractor extractor;
315         if (consoleViewType == ConsoleViewContentType.ERROR_OUTPUT ||
316             consoleViewType == ConsoleViewContentType.SYSTEM_OUTPUT) {
317           extractor = handler.getErr();
318         }
319         else {
320           extractor = handler.getOut();
321         }
322         extractor.getEventsDispatcher().processOutput(printable);
323       }
324     });
325
326     if (ApplicationManager.getApplication().isUnitTestMode()) {
327       return new DefaultExecutionResult(null, handler);
328     }
329
330     final RerunFailedTestsAction rerunFailedTestsAction = new RerunFailedTestsAction(consoleView.getComponent());
331     rerunFailedTestsAction.init(consoleProperties, myRunnerSettings, myConfigurationSettings);
332     rerunFailedTestsAction.setModelProvider(new Getter<TestFrameworkRunningModel>() {
333       public TestFrameworkRunningModel get() {
334         return packetsReceiver.getModel();
335       }
336     });
337
338     final DefaultExecutionResult result = new DefaultExecutionResult(consoleView, handler);
339     result.setRestartActions(rerunFailedTestsAction);
340     return result;
341   }
342
343   protected void notifyByBalloon(JUnitRunningModel model, JUnitConsoleProperties consoleProperties) {
344     TestsUIUtil.notifyByBalloon(myProject, model != null ? model.getRoot() : null, consoleProperties);
345   }
346
347   protected JUnitProcessHandler createHandler() throws ExecutionException {
348     appendForkInfo();
349     return JUnitProcessHandler.runCommandLine(CommandLineBuilder.createFromJavaParameters(myJavaParameters, myProject, true));
350   }
351
352   private void appendForkInfo() throws ExecutionException {
353     final String forkMode = myConfiguration.getForkMode();
354     if (Comparing.strEqual(forkMode, "none")) {
355       return;
356     }
357
358     if (myRunnerSettings.getData() instanceof DebuggingRunnerData) {
359       throw new CantRunException("Debug is disabled in fork mode.<br/>Please change fork mode to &lt;none&gt; to debug.");
360     }
361
362     final JavaParameters javaParameters = getJavaParameters();
363     final Sdk jdk = javaParameters.getJdk();
364     if (jdk == null) {
365       throw new ExecutionException(ExecutionBundle.message("run.configuration.error.no.jdk.specified"));
366     }
367
368     try {
369       final File tempFile = FileUtil.createTempFile("command.line", "", true);
370       final PrintWriter writer = new PrintWriter(tempFile, "UTF-8");
371       try {
372         writer.println(((JavaSdkType)jdk.getSdkType()).getVMExecutablePath(jdk));
373         for (String vmParameter : javaParameters.getVMParametersList().getList()) {
374           writer.println(vmParameter);
375         }
376         writer.println("-classpath");
377         writer.println(javaParameters.getClassPath().getPathsString());
378       }
379       finally {
380         writer.close();
381       }
382       
383       myJavaParameters.getProgramParametersList().add("@@@" + forkMode + ',' + tempFile.getAbsolutePath());
384     }
385     catch (Exception e) {
386       LOG.error(e);
387     }
388   }
389
390   protected <T> void addClassesListToJavaParameters(Collection<? extends T> elements, Function<T, String> nameFunction, String packageName,
391                                                 boolean createTempFile,
392                                                 boolean junit4) {
393     try {
394       if (createTempFile) {
395         myTempFile = FileUtil.createTempFile("idea_junit", ".tmp");
396         myTempFile.deleteOnExit();
397         myJavaParameters.getProgramParametersList().add("@" + myTempFile.getAbsolutePath());
398       }
399
400       final PrintWriter writer = new PrintWriter(myTempFile, "UTF-8");
401       try {
402         writer.println(junit4 ? JUnitStarter.JUNIT4_PARAMETER : "-junit3");
403         writer.println(packageName);
404         final List<String> testNames = new ArrayList<String>();
405         for (final T element : elements) {
406           final String name = nameFunction.fun(element);
407           if (name == null) {
408             LOG.error("invalid element " + element);
409             return;
410           }
411           testNames.add(name);
412         }
413         Collections.sort(testNames); //sort tests in FQN order
414         for (String testName : testNames) {
415           writer.println(testName);
416         }
417       }
418       finally {
419         writer.close();
420       }
421     }
422     catch (IOException e) {
423       LOG.error(e);
424     }
425   }
426
427   public void clear() {
428     myJavaParameters = null;
429   }
430 }