c75ec170d5bfc1c8cc0a0045a7729a550d9aa7e6
[idea/community.git] / plugins / testng / src / com / theoryinpractice / testng / ui / TestNGResults.java
1 /*
2  * Copyright 2000-2009 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 /*
18  * Created by IntelliJ IDEA.
19  * User: amrk
20  * Date: Jul 6, 2005
21  * Time: 10:49:05 PM
22  */
23 package com.theoryinpractice.testng.ui;
24
25 import com.intellij.codeInsight.AnnotationUtil;
26 import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
27 import com.intellij.execution.configurations.RunnerSettings;
28 import com.intellij.execution.testframework.*;
29 import com.intellij.execution.testframework.actions.ScrollToTestSourceAction;
30 import com.intellij.execution.testframework.ui.TestResultsPanel;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.progress.util.ColorProgressBar;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.util.Computable;
35 import com.intellij.openapi.util.Disposer;
36 import com.intellij.openapi.util.Pass;
37 import com.intellij.openapi.util.text.StringUtil;
38 import com.intellij.psi.*;
39 import com.intellij.psi.util.ClassUtil;
40 import com.intellij.ui.ScrollPaneFactory;
41 import com.intellij.ui.table.TableView;
42 import com.intellij.util.OpenSourceUtil;
43 import com.theoryinpractice.testng.configuration.TestNGConfiguration;
44 import com.theoryinpractice.testng.model.*;
45 import com.theoryinpractice.testng.util.TestNGUtil;
46 import org.jetbrains.annotations.NonNls;
47 import org.testng.remote.strprotocol.MessageHelper;
48 import org.testng.remote.strprotocol.TestResultMessage;
49
50 import javax.swing.*;
51 import javax.swing.event.TreeSelectionEvent;
52 import javax.swing.event.TreeSelectionListener;
53 import javax.swing.tree.DefaultMutableTreeNode;
54 import javax.swing.tree.TreePath;
55 import java.awt.*;
56 import java.awt.event.MouseAdapter;
57 import java.awt.event.MouseEvent;
58 import java.text.NumberFormat;
59 import java.util.*;
60 import java.util.List;
61
62 public class TestNGResults extends TestResultsPanel implements TestFrameworkRunningModel {
63   @NonNls private static final String TESTNG_SPLITTER_PROPERTY = "TestNG.Splitter.Proportion";
64
65   private final TableView resultsTable;
66
67   private final TestNGResultsTableModel model;
68   private TestNGTestTreeView tree;
69
70   private final Project project;
71   private int count;
72   private int total;
73   private final Set<TestProxy> failed = new HashSet<TestProxy>();
74   private final Map<TestResultMessage, List<TestProxy>> started = new HashMap<TestResultMessage, List<TestProxy>>();
75   private TestProxy failedToStart = null;
76   private long start;
77   private long end;
78   private TestTreeBuilder treeBuilder;
79   private Animator animator;
80
81   private final TreeRootNode rootNode;
82   private static final String NO_PACKAGE = "No Package";
83   private TestNGResults.OpenSourceSelectionListener openSourceListener;
84   private int myStatus = MessageHelper.PASSED_TEST;
85   private Set<String> startedMethods = new HashSet<String>();
86   private TestProxy myLastSelected;
87
88   public TestNGResults(final JComponent component,
89                        final TestNGConfiguration configuration,
90                        final TestNGConsoleView console,
91                        final RunnerSettings runnerSettings,
92                        final ConfigurationPerRunnerSettings configurationSettings) {
93     super(component, console.getConsole().createConsoleActions(), console.getProperties(),
94           runnerSettings, configurationSettings, TESTNG_SPLITTER_PROPERTY, 0.5f);
95     this.project = configuration.getProject();
96
97     model = new TestNGResultsTableModel(project);
98     resultsTable = new TableView(model);
99     resultsTable.addMouseListener(new MouseAdapter() {
100       public void mouseClicked(MouseEvent e) {
101         if (e.getClickCount() == 2) {
102           final Object result = resultsTable.getSelectedObject();
103           if (result instanceof TestResultMessage) {
104             final String testClass = ((TestResultMessage)result).getTestClass();
105             final PsiClass psiClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), testClass);
106             if (psiClass != null) {
107               final String method = ((TestResultMessage)result).getMethod();
108               if (method != null) {
109                 final PsiMethod[] psiMethods = psiClass.findMethodsByName(method, false);
110                 for (PsiMethod psiMethod : psiMethods) {
111                   psiMethod.navigate(true);
112                   return;
113                 }
114               }
115               psiClass.navigate(true);
116             }
117           }
118         }
119       }
120     });
121     rootNode = new TreeRootNode();
122     console.getUnboundOutput().addChild(rootNode);
123   }
124
125   protected JComponent createTestTreeView() {
126     tree = new TestNGTestTreeView();
127
128     final TestTreeStructure structure = new TestTreeStructure(project, rootNode);
129     tree.attachToModel(this);
130     treeBuilder = new TestTreeBuilder(tree, structure);
131     Disposer.register(this, treeBuilder);
132
133     animator = new Animator(this, treeBuilder);
134
135     openSourceListener = new OpenSourceSelectionListener();
136     tree.getSelectionModel().addTreeSelectionListener(openSourceListener);
137
138     TrackRunningTestUtil.installStopListeners(tree, this, new Pass<AbstractTestProxy>() {
139       @Override
140       public void pass(AbstractTestProxy abstractTestProxy) {
141         myLastSelected = (TestProxy)abstractTestProxy;
142       }
143     });
144
145     return tree;
146   }
147
148   @Override
149   protected ToolbarPanel createToolbarPanel() {
150     final ToolbarPanel panel = new ToolbarPanel(getProperties(), myRunnerSettings, myConfigurationSettings, this);
151     panel.setModel(this);
152     return panel;
153   }
154
155   public TestConsoleProperties getProperties() {
156     return myProperties;
157   }
158
159   protected JComponent createStatisticsPanel() {
160     final JPanel panel = new JPanel(new BorderLayout()); //do not remove wrapper panel 
161     panel.add(ScrollPaneFactory.createScrollPane(resultsTable), BorderLayout.CENTER);
162     return panel;
163   }
164
165   private void updateStatusLine() {
166     myStatusLine.setText(getStatusLine());
167   }
168
169   public int getStatus() {
170     return myStatus;
171   }
172
173   public String getStatusLine() {
174     StringBuffer sb = new StringBuffer();
175     if (end == 0 && start > 0) {
176       sb.append("Running: ");
177     }
178     else {
179       if (failed.size() > 0) sb.append("Failed: ").append(failed.size()).append("   ");
180       sb.append("Done: ");
181     }
182     sb.append(count).append(" of ").append(total);
183     if (end == 0) {
184       if (failed.size() > 0) {
185         sb.append("   Failed: ").append(failed.size());
186       }
187     }
188     else {
189       final long time = end - start;
190       sb.append(" (").append(time == 0 ? "0.0 s" : NumberFormat.getInstance().format((double)time / 1000.0) + " s").append(")  ");
191     }
192     return sb.toString();
193   }
194
195   public TestProxy testStarted(TestResultMessage result) {
196     return testStarted(result, true);
197   }
198
199   public TestProxy testStarted(TestResultMessage result, boolean registerDups) {
200     TestProxy classNode = getPackageClassNodeFor(result);
201     TestProxy proxy = new TestProxy();
202     proxy.setParent(classNode);
203     proxy.setResultMessage(result);
204     synchronized (started) {
205       if (registerDups) {
206         List<TestProxy> dups = started.get(result);
207         if (dups == null) {
208           dups = new ArrayList<TestProxy>();
209           started.put(result, dups);
210         }
211         dups.add(proxy);
212       }
213     }
214     final String testMethodDescriptor = result.getTestClass() + result.getMethod();
215     if (startedMethods.contains(testMethodDescriptor)) {
216       total++;
217     }
218     else {
219       startedMethods.add(testMethodDescriptor);
220     }
221     animator.setCurrentTestCase(proxy);
222     treeBuilder.addItem(classNode, proxy);
223     treeBuilder.repaintWithParents(proxy);
224     count++;
225     if (count > total) total = count;
226     if (myLastSelected == proxy) {
227       myLastSelected = null;
228     }
229     if (myLastSelected == null && TestConsoleProperties.TRACK_RUNNING_TEST.value(myProperties)) {
230       selectTest(proxy);
231     }
232     return proxy;
233   }
234
235   public void addTestResult(final TestResultMessage result, int exceptionMark) {
236     TestProxy testCase;
237     synchronized (started) {
238       final List<TestProxy> dups = started.get(result);
239       testCase = dups == null || dups.isEmpty() ? null : dups.remove(0);
240     }
241     if (testCase == null) {
242       final PsiElement element = getPackageClassNodeFor(result).getPsiElement();
243       if (element instanceof PsiClass) {
244         final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(
245           new Computable<PsiMethod[]>() {
246             public PsiMethod[] compute() {
247               return ((PsiClass)element).findMethodsByName(result.getMethod(), true);
248             }
249           }
250         );
251         if (methods.length > 0 &&
252             methods[0] != null &&
253             !AnnotationUtil.isAnnotated(methods[0], Arrays.asList(TestNGUtil.CONFIG_ANNOTATIONS_FQN))) {
254           for (List<TestProxy> proxies : started.values()) {
255             if (proxies != null) {
256               for (TestProxy proxy : proxies) {
257                 if (methods[0].equals(proxy.getPsiElement())) {
258                   testCase = proxy;
259                   break;
260                 }
261               }
262             }
263           }
264           if (testCase == null) {
265             testCase = testStarted(result, false);
266             testCase.appendStacktrace(result);
267           }
268         }
269       }
270     }
271
272     if (testCase != null) {
273       testCase.setResultMessage(result);
274       testCase.setTearDownFailure(failedToStart != null);
275       failedToStart = null;
276
277       if (result.getResult() == MessageHelper.FAILED_TEST) {
278         failed.add(testCase);
279       }
280       model.addTestResult(result);
281     }
282     else {
283       //do not remember testresultmessage: test hierarchy is not set
284       testCase = new TestProxy(TestProxy.toDisplayText(result, project));
285       testCase.appendStacktrace(result);
286       if (failedToStart != null) {
287         failedToStart.addChild(testCase);
288         failedToStart.setTearDownFailure(true);
289       }
290       else {
291         failedToStart = testCase;
292       }
293     }
294
295     testCase.setExceptionMark(exceptionMark);
296     AbstractTestProxy.flushOutput(testCase);
297
298     if (result.getResult() == MessageHelper.FAILED_TEST) {
299       myStatusLine.setStatusColor(ColorProgressBar.RED);
300       myStatus = MessageHelper.FAILED_TEST;
301     }
302     else if (result.getResult() == MessageHelper.SKIPPED_TEST && myStatus == MessageHelper.PASSED_TEST) {
303       myStatus = MessageHelper.SKIPPED_TEST;
304     }
305     myStatusLine.setFraction((double)count / total);
306     updateStatusLine();
307   }
308
309   private TestProxy getPackageClassNodeFor(final TestResultMessage result) {
310     TestProxy owner = treeBuilder.getRoot();
311     final String packageName1 = StringUtil.getPackageName(result.getTestClass());
312     String packageName = packageName1.length() == 0 ? NO_PACKAGE : packageName1;
313     owner = getChildNodeNamed(owner, packageName);
314     if (owner.getPsiElement() == null) {
315       owner.setPsiElement(JavaPsiFacade.getInstance(project).findPackage(packageName));
316     }
317     owner = getChildNodeNamed(owner, StringUtil.getShortName(result.getTestClass()));
318     //look up the psiclass now
319     if (owner.getPsiElement() == null) {
320       final TestProxy finalOwner = owner;
321       ApplicationManager.getApplication().runReadAction(new Runnable() {
322         public void run() {
323           finalOwner.setPsiElement(ClassUtil.findPsiClass(PsiManager.getInstance(project), result.getTestClass()));
324         }
325       });
326     }
327     return owner;
328   }
329
330   private TestProxy getChildNodeNamed(TestProxy currentNode, String node) {
331     for (TestProxy child : currentNode.getChildren()) {
332       if (child.getName().equals(node)) {
333         return child;
334       }
335     }
336
337     TestProxy child = new TestProxy(node);
338     treeBuilder.addItem(currentNode, child);
339     return child;
340   }
341
342   public void selectTest(TestProxy proxy) {
343     if (proxy == null) return;
344     treeBuilder.select(proxy, null);
345   }
346
347   public void setTotal(int total) {
348     this.total = total;
349   }
350
351   public void start() {
352     if (start == 0) {
353       start = System.currentTimeMillis();
354     }
355     treeBuilder.select(rootNode);
356     rootNode.setInProgress(true);
357     rootNode.setStarted(true);
358   }
359
360   public void finish() {
361     if (start > 0) {
362       end = System.currentTimeMillis();
363     }
364     LvcsHelper.addLabel(this);
365
366     SwingUtilities.invokeLater(new Runnable() {
367       public void run() {
368         animator.stopMovie();
369         updateStatusLine();
370         if (total > count) {
371           myStatusLine.setStatusColor(ColorProgressBar.YELLOW);
372         }
373         rootNode.setInProgress(false);
374         if (TestNGConsoleProperties.SELECT_FIRST_DEFECT.value(myProperties)) {
375           selectTest(rootNode.getFirstDefect());
376         }
377         else {
378           final DefaultMutableTreeNode node = treeBuilder.getNodeForElement(rootNode);
379           if (node != null && myLastSelected == null) {
380             tree.getSelectionModel().setSelectionPath(new TreePath(node));
381           }
382         }
383         tree.repaint();
384       }
385     });
386   }
387
388   public void setFilter(final Filter filter) {
389     getTreeStructure().setFilter(filter);
390     treeBuilder.updateFromRoot();
391   }
392
393   public boolean isRunning() {
394     return rootNode.isInProgress();
395   }
396
397   public TestTreeView getTreeView() {
398     return tree;
399   }
400
401   @Override
402   public TestTreeBuilder getTreeBuilder() {
403     return treeBuilder;
404   }
405
406   public boolean hasTestSuites() {
407     return rootNode.getChildren().size() > 0;
408   }
409
410   public TestProxy getRoot() {
411     return rootNode;
412   }
413
414   public void selectAndNotify(final AbstractTestProxy testProxy) {
415     selectTest((TestProxy)testProxy);
416   }
417
418   public TestTreeStructure getTreeStructure() {
419     return (TestTreeStructure)treeBuilder.getTreeStructure();
420   }
421
422   public void rebuildTree() {
423     treeBuilder.updateFromRoot();
424     tree.invalidate();
425   }
426
427   public void dispose() {
428     super.dispose();
429     tree.getSelectionModel().removeTreeSelectionListener(openSourceListener);
430   }
431
432   public TestProxy getFailedToStart() {
433     return failedToStart;
434   }
435
436   public void setFailedToStart(TestProxy failedToStart) {
437     this.failedToStart = failedToStart;
438   }
439
440   private class OpenSourceSelectionListener implements TreeSelectionListener {
441
442     public void valueChanged(TreeSelectionEvent e) {
443       TreePath path = e.getPath();
444       if (path == null) return;
445       TestProxy proxy = (TestProxy)tree.getSelectedTest();
446       if (proxy == null) return;
447       if (ScrollToTestSourceAction.isScrollEnabled(TestNGResults.this)) {
448         OpenSourceUtil.openSourcesFrom(tree, false);
449       }
450     }
451   }
452 }