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.
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
;
48 import javax
.swing
.event
.TreeSelectionEvent
;
49 import javax
.swing
.event
.TreeSelectionListener
;
50 import javax
.swing
.tree
.DefaultMutableTreeNode
;
51 import javax
.swing
.tree
.TreePath
;
53 import java
.awt
.event
.MouseAdapter
;
54 import java
.awt
.event
.MouseEvent
;
55 import java
.text
.NumberFormat
;
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
;
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;
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
);
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);
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
);
141 protected ToolbarPanel
createToolbarPanel() {
142 final ToolbarPanel panel
= new ToolbarPanel(getProperties(), myRunnerSettings
, myConfigurationSettings
, this);
143 panel
.setModel(this);
147 public TestConsoleProperties
getProperties() {
151 protected JComponent
createStatisticsPanel() {
152 final JPanel panel
= new JPanel(new BorderLayout()); //do not remove wrapper panel
153 panel
.add(resultsTable
, BorderLayout
.CENTER
);
157 private void updateStatusLine() {
158 myStatusLine
.setText(getStatusLine());
161 public int getStatus() {
165 public String
getStatusLine() {
166 StringBuffer sb
= new StringBuffer();
168 sb
.append("Running: ");
173 sb
.append(count
).append(" of ").append(total
);
174 if (failed
.size() > 0) sb
.append(" Failed: ").append(failed
.size()).append(' ');
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;
186 if (flattenPackages
) {
187 classNode
= getPackageClassNodeFor(result
);
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
);
200 if (count
> total
) total
= count
;
201 if (TestNGConsoleProperties
.TRACK_RUNNING_TEST
.value(myProperties
)) {
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
);
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
);
262 private String
packageNameFor(String fqnClassName
) {
263 Matcher matcher
= packagePattern
.matcher(fqnClassName
);
264 if (matcher
.matches()) {
265 return matcher
.group(1);
272 private String
classNameFor(String fqnClassName
) {
273 Matcher matcher
= packagePattern
.matcher(fqnClassName
);
274 if (matcher
.matches()) {
275 return matcher
.group(2);
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() {
295 finalOwner
.setPsiElement(ClassUtil
.findPsiClass(PsiManager
.getInstance(project
), result
.getTestClass()));
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
);
312 private TestProxy
getChildNodeNamed(TestProxy currentNode
, String node
) {
313 for (TestProxy child
: currentNode
.getChildren()) {
314 if (child
.getName().equals(node
)) {
319 TestProxy child
= new TestProxy(node
);
320 treeBuilder
.addItem(currentNode
, child
);
324 public void selectTest(TestProxy proxy
) {
325 if (proxy
== null) return;
326 treeBuilder
.select(proxy
, null);
329 public void setTotal(int 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() {
342 end
= System
.currentTimeMillis();
343 LvcsHelper
.addLabel(this);
345 SwingUtilities
.invokeLater(new Runnable() {
347 animator
.stopMovie();
350 myStatusLine
.setStatusColor(ColorProgressBar
.YELLOW
);
352 rootNode
.setInProgress(false);
353 if (TestNGConsoleProperties
.SELECT_FIRST_DEFECT
.value(myProperties
)) {
354 selectTest(rootNode
.getFirstDefect());
357 final DefaultMutableTreeNode node
= treeBuilder
.getNodeForElement(rootNode
);
359 tree
.getSelectionModel().setSelectionPath(new TreePath(node
));
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() {
380 public boolean hasTestSuites() {
381 return rootNode
.getChildren().size() > 0;
384 public TestProxy
getRoot() {
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();
401 public void 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()) {
434 .setView(proxy
.getOutput(), TestNGConsoleProperties
.SCROLL_TO_STACK_TRACE
.value(getProperties()) ? proxy
.getExceptionMark() : 0);