testng: test view optimization
[fedora-idea.git] / plugins / testng / src / com / theoryinpractice / testng / ui / TestNGResults.java
blobba53978c92263cbfaa90a6e2c5aaf866c2564e9d
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Created by IntelliJ IDEA.
19 * User: amrk
20 * Date: Jul 6, 2005
21 * Time: 10:49:05 PM
23 package com.theoryinpractice.testng.ui;
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.psi.*;
37 import com.intellij.psi.util.ClassUtil;
38 import com.intellij.ui.table.TableView;
39 import com.intellij.util.OpenSourceUtil;
40 import com.theoryinpractice.testng.configuration.TestNGConfiguration;
41 import com.theoryinpractice.testng.model.*;
42 import com.theoryinpractice.testng.util.TestNGUtil;
43 import org.jetbrains.annotations.NonNls;
44 import org.testng.remote.strprotocol.MessageHelper;
45 import org.testng.remote.strprotocol.TestResultMessage;
47 import javax.swing.*;
48 import javax.swing.event.TreeSelectionEvent;
49 import javax.swing.event.TreeSelectionListener;
50 import javax.swing.tree.DefaultMutableTreeNode;
51 import javax.swing.tree.TreePath;
52 import java.awt.*;
53 import java.awt.event.MouseAdapter;
54 import java.awt.event.MouseEvent;
55 import java.text.NumberFormat;
56 import java.util.*;
57 import java.util.List;
58 import java.util.regex.Matcher;
59 import java.util.regex.Pattern;
61 public class TestNGResults extends TestResultsPanel implements TestFrameworkRunningModel {
62 @NonNls private static final String TESTNG_SPLITTER_PROPERTY = "TestNG.Splitter.Proportion";
64 private final TableView resultsTable;
66 private final TestNGResultsTableModel model;
67 private TestNGTestTreeView tree;
69 private final Project project;
70 private int count;
71 private int total;
72 private final Set<TestProxy> failed = new HashSet<TestProxy>();
73 private final Map<TestResultMessage, TestProxy> started = new HashMap<TestResultMessage, TestProxy>();
74 private TestProxy failedToStart = null;
75 private long start;
76 private long end;
77 private TestTreeBuilder treeBuilder;
78 private Animator animator;
80 private final Pattern packagePattern = Pattern.compile("(.*)\\.(.*)");
81 private final TreeRootNode rootNode;
82 private static final String NO_PACKAGE = "No Package";
83 private TestNGResults.OpenSourceSelectionListener openSourceListener;
84 private final TestNGConsoleView myConsole;
85 private int myStatus = MessageHelper.PASSED_TEST;
87 public TestNGResults(final JComponent component,
88 final TestNGConfiguration configuration,
89 final TestNGConsoleView console,
90 final RunnerSettings runnerSettings,
91 final ConfigurationPerRunnerSettings configurationSettings) {
92 super(component, console.createConsoleActions(), console.getProperties(),
93 runnerSettings, configurationSettings, TESTNG_SPLITTER_PROPERTY, 0.5f);
94 myConsole = console;
95 this.project = configuration.getProject();
97 model = new TestNGResultsTableModel();
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;
115 psiClass.navigate(true);
121 rootNode = new TreeRootNode();
124 protected JComponent createTestTreeView() {
125 tree = new TestNGTestTreeView();
127 final TestTreeStructure structure = new TestTreeStructure(project, rootNode);
128 tree.attachToModel(this);
129 treeBuilder = new TestTreeBuilder(tree, structure);
130 Disposer.register(this, treeBuilder);
132 animator = new Animator(this, treeBuilder);
134 openSourceListener = new OpenSourceSelectionListener(structure, myConsole);
135 tree.getSelectionModel().addTreeSelectionListener(openSourceListener);
137 return tree;
140 @Override
141 protected ToolbarPanel createToolbarPanel() {
142 final ToolbarPanel panel = new ToolbarPanel(getProperties(), myRunnerSettings, myConfigurationSettings, this);
143 panel.setModel(this);
144 return panel;
147 public TestConsoleProperties getProperties() {
148 return myProperties;
151 protected JComponent createStatisticsPanel() {
152 final JPanel panel = new JPanel(new BorderLayout()); //do not remove wrapper panel
153 panel.add(resultsTable, BorderLayout.CENTER);
154 return panel;
157 private void updateStatusLine() {
158 myStatusLine.setText(getStatusLine());
161 public int getStatus() {
162 return myStatus;
165 public String getStatusLine() {
166 StringBuffer sb = new StringBuffer();
167 if (end == 0) {
168 sb.append("Running: ");
170 else {
171 sb.append("Done: ");
173 sb.append(count).append(" of ").append(total);
174 if (failed.size() > 0) sb.append(" Failed: ").append(failed.size()).append(' ');
175 if (end != 0) {
176 final long time = end - start;
177 sb.append(" (").append(time == 0 ? "0.0 s" : NumberFormat.getInstance().format((double)time / 1000.0) + " s").append(") ");
179 return sb.toString();
182 public TestProxy testStarted(TestResultMessage result) {
183 // TODO This should be an action button which rebuilds the tree when toggled.
184 boolean flattenPackages = true;
185 TestProxy classNode;
186 if (flattenPackages) {
187 classNode = getPackageClassNodeFor(result);
189 else {
190 classNode = getClassNodeFor(result);
192 TestProxy proxy = new TestProxy();
193 proxy.setParent(classNode);
194 proxy.setResultMessage(result);
195 started.put(result, proxy);
196 animator.setCurrentTestCase(proxy);
197 treeBuilder.addItem(classNode, proxy);
198 treeBuilder.repaintWithParents(proxy);
199 count++;
200 if (count > total) total = count;
201 if (TestNGConsoleProperties.TRACK_RUNNING_TEST.value(myProperties)) {
202 selectTest(proxy);
204 return proxy;
207 public boolean wasTestStarted(TestResultMessage resultMessage) {
208 return started.get(resultMessage) != null;
211 public void addTestResult(final TestResultMessage result, List<Printable> output, int exceptionMark) {
212 if (failedToStart != null) {
213 output.addAll(failedToStart.getOutput());
214 exceptionMark += failedToStart.getExceptionMark();
217 TestProxy testCase = started.get(result);
218 if (testCase == null) {
219 final PsiElement element = getPackageClassNodeFor(result).getPsiElement();
220 if (element instanceof PsiClass) {
221 final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(
222 new Computable<PsiMethod[]>() {
223 public PsiMethod[] compute() {
224 return ((PsiClass)element).findMethodsByName(result.getMethod(), true);
228 if (methods.length > 0 && !AnnotationUtil.isAnnotated(methods[0], Arrays.asList(TestNGUtil.CONFIG_ANNOTATIONS_FQN))) {
229 testCase = testStarted(result);
234 if (testCase != null) {
235 testCase.setResultMessage(result);
236 failedToStart = null;
238 if (result.getResult() == MessageHelper.FAILED_TEST) {
239 failed.add(testCase);
241 model.addTestResult(result);
243 else {
244 //do not remember testresultmessage: test hierarchy is not set
245 testCase = new TestProxy();
246 failedToStart = testCase;
249 testCase.setOutput(output);
250 testCase.setExceptionMark(exceptionMark);
252 if (result.getResult() == MessageHelper.FAILED_TEST) {
253 myStatusLine.setStatusColor(ColorProgressBar.RED);
254 myStatus = MessageHelper.FAILED_TEST;
255 } else if (result.getResult() == MessageHelper.SKIPPED_TEST && myStatus == MessageHelper.PASSED_TEST) {
256 myStatus = MessageHelper.SKIPPED_TEST;
258 myStatusLine.setFraction((double)count / total);
259 updateStatusLine();
262 private String packageNameFor(String fqnClassName) {
263 Matcher matcher = packagePattern.matcher(fqnClassName);
264 if (matcher.matches()) {
265 return matcher.group(1);
267 else {
268 return NO_PACKAGE;
272 private String classNameFor(String fqnClassName) {
273 Matcher matcher = packagePattern.matcher(fqnClassName);
274 if (matcher.matches()) {
275 return matcher.group(2);
277 else {
278 return fqnClassName;
282 private TestProxy getPackageClassNodeFor(final TestResultMessage result) {
283 TestProxy owner = treeBuilder.getRoot();
284 String packageName = packageNameFor(result.getTestClass());
285 owner = getChildNodeNamed(owner, packageName);
286 if (owner.getPsiElement() == null) {
287 owner.setPsiElement(JavaPsiFacade.getInstance(project).findPackage(packageName));
289 owner = getChildNodeNamed(owner, classNameFor(result.getTestClass()));
290 //look up the psiclass now
291 if (owner.getPsiElement() == null) {
292 final TestProxy finalOwner = owner;
293 ApplicationManager.getApplication().runReadAction(new Runnable() {
294 public void run() {
295 finalOwner.setPsiElement(ClassUtil.findPsiClass(PsiManager.getInstance(project), result.getTestClass()));
299 return owner;
302 private TestProxy getClassNodeFor(TestResultMessage result) {
304 String[] nodes = result.getTestClass().split("\\.");
305 TestProxy owner = treeBuilder.getRoot();
306 for (String node : nodes) {
307 owner = getChildNodeNamed(owner, node);
309 return owner;
312 private TestProxy getChildNodeNamed(TestProxy currentNode, String node) {
313 for (TestProxy child : currentNode.getChildren()) {
314 if (child.getName().equals(node)) {
315 return child;
319 TestProxy child = new TestProxy(node);
320 treeBuilder.addItem(currentNode, child);
321 return child;
324 public void selectTest(TestProxy proxy) {
325 if (proxy == null) return;
326 treeBuilder.select(proxy, null);
329 public void setTotal(int total) {
330 this.total = total;
333 public void start() {
334 start = System.currentTimeMillis();
335 tree.getSelectionModel().setSelectionPath(new TreePath(treeBuilder.getNodeForElement(rootNode)));
336 rootNode.setInProgress(true);
337 rootNode.setStarted(true);
340 public void finish() {
341 if (end > 0) return;
342 end = System.currentTimeMillis();
343 LvcsHelper.addLabel(this);
345 SwingUtilities.invokeLater(new Runnable() {
346 public void run() {
347 animator.stopMovie();
348 updateStatusLine();
349 if (total > count) {
350 myStatusLine.setStatusColor(ColorProgressBar.YELLOW);
352 rootNode.setInProgress(false);
353 if (TestNGConsoleProperties.SELECT_FIRST_DEFECT.value(myProperties)) {
354 selectTest(rootNode.getFirstDefect());
356 else {
357 final DefaultMutableTreeNode node = treeBuilder.getNodeForElement(rootNode);
358 if (node != null) {
359 tree.getSelectionModel().setSelectionPath(new TreePath(node));
362 tree.repaint();
367 public void setFilter(final Filter filter) {
368 getTreeStructure().setFilter(filter);
369 treeBuilder.updateFromRoot();
372 public boolean isRunning() {
373 return rootNode.isInProgress();
376 public TestTreeView getTreeView() {
377 return tree;
380 public boolean hasTestSuites() {
381 return rootNode.getChildren().size() > 0;
384 public TestProxy getRoot() {
385 return rootNode;
388 public void selectAndNotify(final AbstractTestProxy testProxy) {
389 selectTest((TestProxy)testProxy);
392 public TestTreeStructure getTreeStructure() {
393 return (TestTreeStructure)treeBuilder.getTreeStructure();
396 public void rebuildTree() {
397 treeBuilder.updateFromRoot();
398 tree.invalidate();
401 public void dispose() {
402 super.dispose();
403 openSourceListener.structure = null;
404 openSourceListener.console = null;
405 tree.getSelectionModel().removeTreeSelectionListener(openSourceListener);
408 public TestProxy getFailedToStart() {
409 return failedToStart;
412 private class OpenSourceSelectionListener implements TreeSelectionListener {
413 private TestTreeStructure structure;
414 private TestNGConsoleView console;
416 public OpenSourceSelectionListener(TestTreeStructure structure, TestNGConsoleView console) {
417 this.structure = structure;
418 this.console = console;
421 public void valueChanged(TreeSelectionEvent e) {
422 TreePath path = e.getPath();
423 if (path == null) return;
424 TestProxy proxy = (TestProxy)tree.getSelectedTest();
425 if (proxy == null) return;
426 if (ScrollToTestSourceAction.isScrollEnabled(TestNGResults.this)) {
427 OpenSourceUtil.openSourcesFrom(tree, false);
429 if (proxy == structure.getRootElement()) {
430 console.reset();
432 else {
433 console
434 .setView(proxy.getOutput(), TestNGConsoleProperties.SCROLL_TO_STACK_TRACE.value(getProperties()) ? proxy.getExceptionMark() : 0);