test fixed
[fedora-idea.git] / platform / smRunner / src / com / intellij / execution / testframework / sm / runner / ui / SMTestRunnerResultsForm.java
blob4bc2cde9e12b54865f33b3b9c790dcad20d0d874
1 package com.intellij.execution.testframework.sm.runner.ui;
3 import com.intellij.execution.configurations.ConfigurationPerRunnerSettings;
4 import com.intellij.execution.configurations.RunConfigurationBase;
5 import com.intellij.execution.configurations.RunnerSettings;
6 import com.intellij.execution.testframework.*;
7 import com.intellij.execution.testframework.sm.SMRunnerUtil;
8 import com.intellij.execution.testframework.sm.runner.SMTRunnerEventsListener;
9 import com.intellij.execution.testframework.sm.runner.SMTRunnerTreeBuilder;
10 import com.intellij.execution.testframework.sm.runner.SMTRunnerTreeStructure;
11 import com.intellij.execution.testframework.sm.runner.SMTestProxy;
12 import com.intellij.execution.testframework.sm.runner.ui.statistics.StatisticsPanel;
13 import com.intellij.execution.testframework.ui.AbstractTestTreeBuilder;
14 import com.intellij.execution.testframework.ui.PrintableTestProxy;
15 import com.intellij.execution.testframework.ui.TestResultsPanel;
16 import com.intellij.execution.testframework.ui.TestsProgressAnimator;
17 import com.intellij.openapi.Disposable;
18 import com.intellij.openapi.actionSystem.AnAction;
19 import com.intellij.openapi.application.ModalityState;
20 import com.intellij.openapi.progress.util.ColorProgressBar;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.Disposer;
23 import com.intellij.openapi.wm.IdeFocusManager;
24 import org.jetbrains.annotations.NonNls;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
28 import javax.swing.*;
29 import javax.swing.event.TreeSelectionEvent;
30 import javax.swing.event.TreeSelectionListener;
31 import java.awt.*;
32 import java.awt.event.InputEvent;
33 import java.awt.event.KeyEvent;
34 import java.util.ArrayList;
35 import java.util.LinkedHashSet;
36 import java.util.List;
37 import java.util.Set;
39 /**
40 * @author: Roman Chernyatchik
42 public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFrameworkRunningModel, TestResultsViewer, SMTRunnerEventsListener {
43 @NonNls private static final String DEFAULT_SM_RUNNER_SPLITTER_PROPERTY = "SMTestRunner.Splitter.Proportion";
45 private SMTRunnerTestTreeView myTreeView;
47 private TestsProgressAnimator myAnimator;
49 /**
50 * Fake parent suite for all tests and suites
52 private final SMTestProxy myTestsRootNode;
53 private SMTRunnerTreeBuilder myTreeBuilder;
54 private final TestConsoleProperties myConsoleProperties;
56 private final List<EventsListener> myEventListeners = new ArrayList<EventsListener>();
58 private PropagateSelectionHandler myShowStatisticForProxyHandler;
60 private final Project myProject;
62 private int myTestsCurrentCount;
63 private int myTestsTotal = 0;
64 private int myTestsFailuresCount;
65 private long myStartTime;
66 private long myEndTime;
67 private StatisticsPanel myStatisticsPane;
69 // custom progress
70 private String myCurrentCustomProgressCategory;
71 private Set<String> myMentionedCategories = new LinkedHashSet<String>();
73 public SMTestRunnerResultsForm(final RunConfigurationBase runConfiguration,
74 @NotNull final JComponent console,
75 final TestConsoleProperties consoleProperties,
76 final RunnerSettings runnerSettings,
77 final ConfigurationPerRunnerSettings configurationSettings) {
78 this(runConfiguration, console, AnAction.EMPTY_ARRAY, consoleProperties, runnerSettings, configurationSettings, null);
81 public SMTestRunnerResultsForm(final RunConfigurationBase runConfiguration,
82 @NotNull final JComponent console,
83 AnAction[] consoleActions,
84 final TestConsoleProperties consoleProperties,
85 final RunnerSettings runnerSettings,
86 final ConfigurationPerRunnerSettings configurationSettings,
87 final String splitterPropertyName) {
88 super(console, consoleActions, consoleProperties, runnerSettings, configurationSettings,
89 splitterPropertyName != null ? DEFAULT_SM_RUNNER_SPLITTER_PROPERTY : splitterPropertyName, 0.5f);
90 myConsoleProperties = consoleProperties;
92 myProject = runConfiguration.getProject();
94 //Create tests common suite root
95 //noinspection HardCodedStringLiteral
96 myTestsRootNode = new SMTestProxy("[root]", true, null);
98 // Fire selection changed and move focus on SHIFT+ENTER
99 //TODO[romeo] improve
101 final ArrayList<Component> components = new ArrayList<Component>();
102 components.add(myTreeView);
103 components.add(myTabs.getComponent());
104 myContentPane.setFocusTraversalPolicy(new MyFocusTraversalPolicy(components));
105 myContentPane.setFocusCycleRoot(true);
109 @Override
110 public void initUI() {
111 super.initUI();
113 final KeyStroke shiftEnterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_MASK);
114 SMRunnerUtil.registerAsAction(shiftEnterKey, "show-statistics-for-test-proxy",
115 new Runnable() {
116 public void run() {
117 showStatisticsForSelectedProxy();
120 myTreeView);
123 protected ToolbarPanel createToolbarPanel() {
124 return new SMTRunnerToolbarPanel(myConsoleProperties, myRunnerSettings, myConfigurationSettings, this, this);
127 protected JComponent createTestTreeView() {
128 myTreeView = new SMTRunnerTestTreeView();
130 myTreeView.setLargeModel(true);
131 myTreeView.attachToModel(this);
132 myTreeView.setTestResultsViewer(this);
134 final SMTRunnerTreeStructure structure = new SMTRunnerTreeStructure(myProject, myTestsRootNode);
135 myTreeBuilder = new SMTRunnerTreeBuilder(myTreeView, structure);
136 Disposer.register(this, myTreeBuilder);
137 myAnimator = new MyAnimator(this, myTreeBuilder);
139 return myTreeView;
142 protected JComponent createStatisticsPanel() {
143 // Statistics tab
144 final StatisticsPanel statisticsPane = new StatisticsPanel(myProject, this);
145 // handler to select in results viewer by statistics pane events
146 statisticsPane.addPropagateSelectionListener(createSelectMeListener());
147 // handler to select test statistics pane by result viewer events
148 setShowStatisticForProxyHandler(statisticsPane.createSelectMeListener());
150 myStatisticsPane = statisticsPane;
151 return myStatisticsPane.getContentPane();
154 public StatisticsPanel getStatisticsPane() {
155 return myStatisticsPane;
158 public void addTestsTreeSelectionListener(final TreeSelectionListener listener) {
159 myTreeView.getSelectionModel().addTreeSelectionListener(listener);
163 * Is used for navigation from tree view to other UI components
164 * @param handler
166 public void setShowStatisticForProxyHandler(final PropagateSelectionHandler handler) {
167 myShowStatisticForProxyHandler = handler;
171 * Returns root node, fake parent suite for all tests and suites
172 * @return
173 * @param testsRoot
175 public void onTestingStarted(@NotNull SMTestProxy testsRoot) {
176 myAnimator.setCurrentTestCase(myTestsRootNode);
178 // Status line
179 myStatusLine.setStatusColor(ColorProgressBar.GREEN);
181 // Tests tree
182 selectAndNotify(myTestsRootNode);
184 myStartTime = System.currentTimeMillis();
185 updateStatusLabel();
187 fireOnTestingStarted();
190 public void onTestingFinished(@NotNull SMTestProxy testsRoot) {
191 myEndTime = System.currentTimeMillis();
193 if (myTestsTotal == 0) {
194 myTestsTotal = myTestsCurrentCount;
195 myStatusLine.setFraction(1);
197 updateStatusLabel();
200 myAnimator.stopMovie();
201 myTreeBuilder.updateFromRoot();
203 LvcsHelper.addLabel(this);
205 fireOnTestingFinished();
208 public void onTestsCountInSuite(final int count) {
209 updateCountersAndProgressOnTestCount(count, false);
213 * Adds test to tree and updates status line.
214 * Test proxy should be initialized, proxy parent must be some suite (already added to tree)
216 * @param testProxy Proxy
218 public void onTestStarted(@NotNull final SMTestProxy testProxy) {
219 updateCountersAndProgressOnTestStarted(false);
221 _addTestOrSuite(testProxy);
223 fireOnTestNodeAdded(testProxy);
226 public void onTestFailed(@NotNull final SMTestProxy test) {
227 updateCountersAndProgressOnTestFailed(false);
230 public void onTestIgnored(@NotNull final SMTestProxy test) {
231 //Do nothing
235 * Adds suite to tree
236 * Suite proxy should be initialized, proxy parent must be some suite (already added to tree)
237 * If parent is null, then suite will be added to tests root.
239 * @param newSuite Tests suite
241 public void onSuiteStarted(@NotNull final SMTestProxy newSuite) {
242 _addTestOrSuite(newSuite);
245 public void onCustomProgressTestsCategory(@Nullable String categoryName, int testCount) {
246 myCurrentCustomProgressCategory = categoryName;
247 updateCountersAndProgressOnTestCount(testCount, true);
250 public void onCustomProgressTestStarted() {
251 updateCountersAndProgressOnTestStarted(true);
254 public void onCustomProgressTestFailed() {
255 updateCountersAndProgressOnTestFailed(true);
258 public void onTestFinished(@NotNull final SMTestProxy test) {
259 //Do nothing
262 public void onSuiteFinished(@NotNull final SMTestProxy suite) {
263 //Do nothing
266 public SMTestProxy getTestsRootNode() {
267 return myTestsRootNode;
270 public TestConsoleProperties getProperties() {
271 return myConsoleProperties;
274 public void setFilter(final Filter filter) {
275 // is usded by Test Runner actions, e.g. hide passed, etc
276 final SMTRunnerTreeStructure treeStructure = myTreeBuilder.getRTestUnitTreeStructure();
277 treeStructure.setFilter(filter);
278 myTreeBuilder.updateFromRoot();
281 public boolean isRunning() {
282 return getRoot().isInProgress();
285 public TestTreeView getTreeView() {
286 return myTreeView;
289 public boolean hasTestSuites() {
290 return getRoot().getChildren().size() > 0;
293 @NotNull
294 public AbstractTestProxy getRoot() {
295 return myTestsRootNode;
299 * Manual test proxy selectio in tests tree. E.g. do select root node on
300 * testing started or do select current node if TRACK_RUNNING_TEST is enabled
303 * Will select proxy in Event Dispatch Thread. Invocation of this
304 * method may be not in event dispatch thread
305 * @param testProxy Test or suite
307 public void selectAndNotify(@Nullable final AbstractTestProxy testProxy) {
308 selectWithoutNotify(testProxy);
310 // Is used by Statistic tab to differ use selection in tree
311 // from manual selection from API (e.g. test runner events)
312 showStatisticsForSelectedProxy(testProxy, false);
315 public void addEventsListener(final EventsListener listener) {
316 myEventListeners.add(listener);
317 addTestsTreeSelectionListener(new TreeSelectionListener() {
318 public void valueChanged(final TreeSelectionEvent e) {
319 //We should fire event only if it was generated by this component,
320 //e.g. it is focused. Otherwise it is side effect of selecing proxy in
321 //try by other component
322 //if (myTreeView.isFocusOwner()) {
323 @Nullable final PrintableTestProxy selectedProxy = (PrintableTestProxy)getTreeView().getSelectedTest();
324 listener.onSelected(selectedProxy, SMTestRunnerResultsForm.this, SMTestRunnerResultsForm.this);
330 public void dispose() {
331 super.dispose();
332 myShowStatisticForProxyHandler = null;
333 myEventListeners.clear();
336 public void showStatisticsForSelectedProxy() {
337 TestConsoleProperties.SHOW_STATISTICS.set(myProperties, true);
338 final AbstractTestProxy selectedProxy = myTreeView.getSelectedTest();
339 showStatisticsForSelectedProxy(selectedProxy, true);
342 private void showStatisticsForSelectedProxy(final AbstractTestProxy selectedProxy,
343 final boolean requestFocus) {
344 if (selectedProxy instanceof SMTestProxy && myShowStatisticForProxyHandler != null) {
345 myShowStatisticForProxyHandler.handlePropagateSelectionRequest((SMTestProxy)selectedProxy, this, requestFocus);
349 protected int getTestsCurrentCount() {
350 return myTestsCurrentCount;
353 protected int getTestsFailuresCount() {
354 return myTestsFailuresCount;
357 protected int getTestsTotal() {
358 return myTestsTotal;
361 public Set<String> getMentionedCategories() {
362 return myMentionedCategories;
365 protected long getStartTime() {
366 return myStartTime;
369 protected long getEndTime() {
370 return myEndTime;
373 private void _addTestOrSuite(@NotNull final SMTestProxy newTestOrSuite) {
375 final SMTestProxy parentSuite = newTestOrSuite.getParent();
376 assert parentSuite != null;
378 // Tree
379 myTreeBuilder.updateTestsSubtree(parentSuite);
380 myTreeBuilder.repaintWithParents(newTestOrSuite);
382 myAnimator.setCurrentTestCase(newTestOrSuite);
385 private void fireOnTestNodeAdded(final SMTestProxy test) {
386 for (EventsListener eventListener : myEventListeners) {
387 eventListener.onTestNodeAdded(this, test);
391 private void fireOnTestingFinished() {
392 for (EventsListener eventListener : myEventListeners) {
393 eventListener.onTestingFinished(this);
397 private void fireOnTestingStarted() {
398 for (EventsListener eventListener : myEventListeners) {
399 eventListener.onTestingStarted(this);
403 private void selectWithoutNotify(final AbstractTestProxy testProxy) {
404 if (testProxy == null) {
405 return;
408 SMRunnerUtil.runInEventDispatchThread(new Runnable() {
409 public void run() {
410 //TODO remove manual update!
411 myTreeBuilder.performUpdate();
413 myTreeBuilder.select(testProxy, null);
415 }, ModalityState.NON_MODAL);
418 private void updateStatusLabel() {
419 if (myTestsFailuresCount > 0) {
420 myStatusLine.setStatusColor(ColorProgressBar.RED);
422 myStatusLine.setText(TestsPresentationUtil.getProgressStatus_Text(myStartTime, myEndTime,
423 myTestsTotal, myTestsCurrentCount,
424 myTestsFailuresCount, myMentionedCategories));
428 * for java unit tests
430 public void performUpdate() {
431 myTreeBuilder.performUpdate();
435 * On event change selection and probably requests focus. Is used when we want
436 * navigate from other component to this
437 * @return Listener
439 public PropagateSelectionHandler createSelectMeListener() {
440 return new PropagateSelectionHandler() {
441 public void handlePropagateSelectionRequest(@Nullable final SMTestProxy selectedTestProxy, @NotNull final Object sender,
442 final boolean requestFocus) {
443 SMRunnerUtil.addToInvokeLater(new Runnable() {
444 public void run() {
445 selectWithoutNotify(selectedTestProxy);
447 // Request focus if necessary
448 if (requestFocus) {
449 //myTreeView.requestFocusInWindow();
450 IdeFocusManager.getInstance(myProject).requestFocus(myTreeView, true);
459 private static class MyAnimator extends TestsProgressAnimator {
460 public MyAnimator(final Disposable parentDisposable, final AbstractTestTreeBuilder builder) {
461 super(parentDisposable);
462 init(builder);
466 private void updateCountersAndProgressOnTestCount(final int count, final boolean isCustomMessage) {
467 if (!isModeConsistent(isCustomMessage)) return;
469 //This is for beter support groups of TestSuites
470 //Each group notifies about it's size
471 myTestsTotal += count;
472 updateStatusLabel();
475 private void updateCountersAndProgressOnTestStarted(final boolean isCustomMessage) {
476 if (!isModeConsistent(isCustomMessage)) return;
478 // for mixed tests results : mention category only if it contained tests
479 myMentionedCategories.add(myCurrentCustomProgressCategory != null ? myCurrentCustomProgressCategory : TestsPresentationUtil.DEFAULT_TESTS_CATEGORY);
481 // Counters
482 myTestsCurrentCount++;
484 // fix total count if it is corrupted
485 // but if test count wasn't set at all let's process such case separately
486 if (myTestsCurrentCount > myTestsTotal && myTestsTotal != 0) {
487 myTestsTotal = myTestsCurrentCount;
490 // update progress
491 if (myTestsTotal != 0) {
492 // if total is set
493 myStatusLine.setFraction((double)myTestsCurrentCount / myTestsTotal);
494 } else {
495 // just set progress in the middle to show user that tests are running
496 myStatusLine.setFraction(0.5);
498 updateStatusLabel();
501 private void updateCountersAndProgressOnTestFailed(final boolean isCustomMessage) {
502 if (!isModeConsistent(isCustomMessage)) return;
504 myTestsFailuresCount++;
505 updateStatusLabel();
508 private boolean isModeConsistent(boolean isCustomMessage) {
509 // check that we are in consistent mode
510 return isCustomMessage != (myCurrentCustomProgressCategory == null);
514 private static class MyFocusTraversalPolicy extends FocusTraversalPolicy {
515 final List<Component> myComponents;
517 private MyFocusTraversalPolicy(final List<Component> components) {
518 myComponents = components;
521 public Component getComponentAfter(final Container container, final Component component) {
522 return myComponents.get((myComponents.indexOf(component) + 1) % myComponents.size());
525 public Component getComponentBefore(final Container container, final Component component) {
526 final int prevIndex = myComponents.indexOf(component) - 1;
527 final int normalizedIndex = prevIndex < 0 ? myComponents.size() - 1 : prevIndex;
529 return myComponents.get(normalizedIndex);
532 public Component getFirstComponent(final Container container) {
533 return myComponents.get(0);
536 public Component getLastComponent(final Container container) {
537 return myComponents.get(myComponents.size() - 1);
540 public Component getDefaultComponent(final Container container) {
541 return getFirstComponent(container);