7c063b6439584a7baa2870bce1bd76601f6bc322
[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.openapi.Disposable;
40 import com.intellij.openapi.application.ApplicationManager;
41 import com.intellij.openapi.diagnostic.Logger;
42 import com.intellij.openapi.extensions.Extensions;
43 import com.intellij.openapi.module.Module;
44 import com.intellij.openapi.project.Project;
45 import com.intellij.openapi.projectRoots.JavaSdk;
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     if (myConfiguration.isAlternativeJrePathEnabled()){
183       if (myConfiguration.getAlternativeJrePath() == null ||
184           myConfiguration.getAlternativeJrePath().length() == 0 ||
185           !JavaSdk.checkForJre(myConfiguration.getAlternativeJrePath())){
186         throw new RuntimeConfigurationWarning(
187           ExecutionBundle.message("jre.path.is.not.valid.jre.home.error.mesage", myConfiguration.getAlternativeJrePath()));
188       }
189     }
190   }
191
192   public SourceScope getSourceScope() {
193     return SourceScope.modulesWithDependencies(myConfiguration.getModules());
194   }
195
196   protected void initialize() throws ExecutionException {
197     JavaParametersUtil.configureConfiguration(myJavaParameters, myConfiguration);
198     myJavaParameters.setMainClass(JUnitConfiguration.JUNIT_START_CLASS);
199     final Module module = myConfiguration.getConfigurationModule().getModule();
200     if (myJavaParameters.getJdk() == null){
201       myJavaParameters.setJdk(module != null
202                               ? ModuleRootManager.getInstance(module).getSdk()
203                               : ProjectRootManager.getInstance(myProject).getProjectSdk());
204     }
205
206     myJavaParameters.getClassPath().add(JavaSdkUtil.getIdeaRtJarPath());
207     myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(JUnitStarter.class));
208     myJavaParameters.getProgramParametersList().add(JUnitStarter.IDE_VERSION + JUnitStarter.VERSION);
209     for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
210       ext.updateJavaParameters(myConfiguration, myJavaParameters, myRunnerSettings);
211     }
212
213     final Object[] listeners = Extensions.getExtensions(IDEAJUnitListener.EP_NAME);
214     final StringBuilder buf = new StringBuilder();
215     for (final Object listener : listeners) {
216       boolean enabled = true;
217       for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
218         if (ext.isListenerDisabled(myConfiguration, listener, myRunnerSettings)) {
219           enabled = false;
220           break;
221         }
222       }
223       if (enabled) {
224         final Class classListener = listener.getClass();
225         buf.append(classListener.getName()).append("\n");
226         myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(classListener));
227       }
228     }
229     if (buf.length() > 0) {
230       try {
231         myListenersFile = FileUtil.createTempFile("junit_listeners_", "");
232         myListenersFile.deleteOnExit();
233         myJavaParameters.getProgramParametersList().add("@@" + myListenersFile.getPath());
234         FileUtil.writeToFile(myListenersFile, buf.toString().getBytes());
235       }
236       catch (IOException e) {
237         LOG.error(e);
238       }
239     }
240   }
241
242   public JavaParameters getJavaParameters() throws ExecutionException {
243     if (myJavaParameters == null) {
244       myJavaParameters = new JavaParameters();
245       initialize();
246       final Module module = myConfiguration.getConfigurationModule().getModule();
247       final Object[] patchers = Extensions.getExtensions(ExtensionPoints.JUNIT_PATCHER);
248       for (Object patcher : patchers) {
249         ((JUnitPatcher)patcher).patchJavaParameters(module, myJavaParameters);
250       }
251     }
252     return myJavaParameters;
253   }
254
255   public ExecutionResult execute(final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
256     final JUnitProcessHandler handler = createHandler();
257     final RunnerSettings runnerSettings = getRunnerSettings();
258     JavaRunConfigurationExtensionManager.getInstance().attachExtensionsToProcess(myConfiguration, handler, runnerSettings);
259     final TestProxy unboundOutputRoot = new TestProxy(new RootTestInfo());
260     final JUnitConsoleProperties consoleProperties = new JUnitConsoleProperties(myConfiguration, executor);
261     final JUnitTreeConsoleView consoleView = new JUnitTreeConsoleView(consoleProperties, runnerSettings, getConfigurationSettings(), unboundOutputRoot);
262     consoleView.initUI();
263     consoleView.attachToProcess(handler);
264     unboundOutputRoot.setPrinter(consoleView.getPrinter());
265     Disposer.register(consoleView, unboundOutputRoot);
266     final TestsPacketsReceiver packetsReceiver = new TestsPacketsReceiver(consoleView, unboundOutputRoot) {
267       @Override
268       public void notifyStart(TestProxy root) {
269         super.notifyStart(root);
270         unboundOutputRoot.addChild(root);
271         if (myConfiguration.isSaveOutputToFile()) {
272           unboundOutputRoot.setOutputFilePath(myConfiguration.getOutputFilePath());
273         }
274         final JUnitRunningModel model = getModel();
275         if (model != null) {
276           handler.getOut().setDispatchListener(model.getNotifier());
277           Disposer.register(model, new Disposable() {
278             public void dispose() {
279               handler.getOut().setDispatchListener(DispatchListener.DEAF);
280             }
281           });
282           consoleView.attachToModel(model);
283         }
284       }
285     };
286
287     final DeferredActionsQueue queue = new DeferredActionsQueueImpl();
288     handler.getOut().setPacketDispatcher(packetsReceiver, queue);
289     handler.getErr().setPacketDispatcher(packetsReceiver, queue);
290
291     handler.addProcessListener(new ProcessAdapter() {
292       @Override
293       public void processTerminated(ProcessEvent event) {
294         handler.removeProcessListener(this);
295         if (myTempFile != null) {
296           FileUtil.delete(myTempFile);
297         }
298         if (myListenersFile != null) {
299           FileUtil.delete(myListenersFile);
300         }
301         IJSwingUtilities.invoke(new Runnable() {
302           public void run() {
303             unboundOutputRoot.flush();
304             packetsReceiver.checkTerminated();
305             final JUnitRunningModel model = packetsReceiver.getModel();
306             notifyByBalloon(model, consoleProperties);
307           }
308         });
309       }
310
311       @Override
312       public void onTextAvailable(final ProcessEvent event, final Key outputType) {
313         final String text = event.getText();
314         final ConsoleViewContentType consoleViewType = ConsoleViewContentType.getConsoleViewType(outputType);
315         final Printable printable = new Printable() {
316           public void printOn(final Printer printer) {
317             printer.print(text, consoleViewType);
318           }
319         };
320         final Extractor extractor;
321         if (consoleViewType == ConsoleViewContentType.ERROR_OUTPUT ||
322             consoleViewType == ConsoleViewContentType.SYSTEM_OUTPUT) {
323           extractor = handler.getErr();
324         }
325         else {
326           extractor = handler.getOut();
327         }
328         extractor.getEventsDispatcher().processOutput(printable);
329       }
330     });
331
332     if (ApplicationManager.getApplication().isUnitTestMode()) {
333       return new DefaultExecutionResult(null, handler);
334     }
335
336     final RerunFailedTestsAction rerunFailedTestsAction = new RerunFailedTestsAction(consoleView.getComponent());
337     rerunFailedTestsAction.init(consoleProperties, myRunnerSettings, myConfigurationSettings);
338     rerunFailedTestsAction.setModelProvider(new Getter<TestFrameworkRunningModel>() {
339       public TestFrameworkRunningModel get() {
340         return packetsReceiver.getModel();
341       }
342     });
343
344     final DefaultExecutionResult result = new DefaultExecutionResult(consoleView, handler);
345     result.setRestartActions(rerunFailedTestsAction);
346     return result;
347   }
348
349   protected void notifyByBalloon(JUnitRunningModel model, JUnitConsoleProperties consoleProperties) {
350     TestsUIUtil.notifyByBalloon(myProject, model != null ? model.getRoot() : null, consoleProperties);
351   }
352
353   protected JUnitProcessHandler createHandler() throws ExecutionException {
354     appendForkInfo();
355     return JUnitProcessHandler.runCommandLine(CommandLineBuilder.createFromJavaParameters(myJavaParameters, myProject, true));
356   }
357
358   private void appendForkInfo() throws ExecutionException {
359     final String forkMode = myConfiguration.getForkMode();
360     if (Comparing.strEqual(forkMode, "none")) {
361       return;
362     }
363
364     if (myRunnerSettings.getData() instanceof DebuggingRunnerData) {
365       throw new CantRunException("Debug is disabled in fork mode.<br/>Please change fork mode to &lt;none&gt; to debug.");
366     }
367
368     final JavaParameters javaParameters = getJavaParameters();
369     final Sdk jdk = javaParameters.getJdk();
370     if (jdk == null) {
371       throw new ExecutionException(ExecutionBundle.message("run.configuration.error.no.jdk.specified"));
372     }
373
374     try {
375       final File tempFile = FileUtil.createTempFile("command.line", "", true);
376       final PrintWriter writer = new PrintWriter(tempFile, "UTF-8");
377       try {
378         writer.println(((JavaSdkType)jdk.getSdkType()).getVMExecutablePath(jdk));
379         for (String vmParameter : javaParameters.getVMParametersList().getList()) {
380           writer.println(vmParameter);
381         }
382         writer.println("-classpath");
383         writer.println(javaParameters.getClassPath().getPathsString());
384       }
385       finally {
386         writer.close();
387       }
388       
389       myJavaParameters.getProgramParametersList().add("@@@" + forkMode + ',' + tempFile.getAbsolutePath());
390     }
391     catch (Exception e) {
392       LOG.error(e);
393     }
394   }
395
396   protected <T> void addClassesListToJavaParameters(Collection<? extends T> elements, Function<T, String> nameFunction, String packageName,
397                                                 boolean createTempFile,
398                                                 boolean junit4) {
399     try {
400       if (createTempFile) {
401         myTempFile = FileUtil.createTempFile("idea_junit", ".tmp");
402         myTempFile.deleteOnExit();
403         myJavaParameters.getProgramParametersList().add("@" + myTempFile.getAbsolutePath());
404       }
405
406       final PrintWriter writer = new PrintWriter(myTempFile, "UTF-8");
407       try {
408         writer.println(junit4 ? JUnitStarter.JUNIT4_PARAMETER : "-junit3");
409         writer.println(packageName);
410         final List<String> testNames = new ArrayList<String>();
411         for (final T element : elements) {
412           final String name = nameFunction.fun(element);
413           if (name == null) {
414             LOG.error("invalid element " + element);
415             return;
416           }
417           testNames.add(name);
418         }
419         Collections.sort(testNames); //sort tests in FQN order
420         for (String testName : testNames) {
421           writer.println(testName);
422         }
423       }
424       finally {
425         writer.close();
426       }
427     }
428     catch (IOException e) {
429       LOG.error(e);
430     }
431   }
432
433   public void clear() {
434     myJavaParameters = null;
435   }
436 }