| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Created by IntelliJ IDEA. |
| * User: amrk |
| * Date: Jul 6, 2005 |
| * Time: 10:49:05 PM |
| */ |
| package com.theoryinpractice.testng.ui; |
| |
| import com.intellij.codeInsight.AnnotationUtil; |
| import com.intellij.execution.ExecutionBundle; |
| import com.intellij.execution.runners.ExecutionEnvironment; |
| import com.intellij.execution.testframework.*; |
| import com.intellij.execution.testframework.actions.ScrollToTestSourceAction; |
| import com.intellij.execution.testframework.ui.TestResultsPanel; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.DefaultActionGroup; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.progress.util.ColorProgressBar; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Pass; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.ClassUtil; |
| import com.intellij.ui.DoubleClickListener; |
| import com.intellij.ui.ScrollPaneFactory; |
| import com.intellij.ui.table.TableView; |
| import com.intellij.util.OpenSourceUtil; |
| import com.intellij.util.config.ToggleBooleanProperty; |
| import com.theoryinpractice.testng.configuration.TestNGConfiguration; |
| import com.theoryinpractice.testng.model.*; |
| import com.theoryinpractice.testng.util.TestNGUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.testng.remote.strprotocol.MessageHelper; |
| import org.testng.remote.strprotocol.TestResultMessage; |
| |
| import javax.swing.*; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.TreePath; |
| import java.awt.*; |
| import java.awt.event.MouseEvent; |
| import java.text.NumberFormat; |
| import java.util.*; |
| import java.util.List; |
| |
| public class TestNGResults extends TestResultsPanel implements TestFrameworkRunningModel { |
| @NonNls private static final String TESTNG_SPLITTER_PROPERTY = "TestNG.Splitter.Proportion"; |
| |
| private final TableView resultsTable; |
| |
| private final TestNGResultsTableModel model; |
| private TestNGTestTreeView tree; |
| |
| private final Project project; |
| private int count; |
| private int total = 0; |
| private final Set<TestProxy> failed = new HashSet<TestProxy>(); |
| private final Map<TestResultMessage, List<TestProxy>> started = new HashMap<TestResultMessage, List<TestProxy>>(); |
| private TestProxy failedToStart = null; |
| private long start; |
| private long end; |
| private TestTreeBuilder treeBuilder; |
| private Animator animator; |
| |
| private final TreeRootNode rootNode; |
| private static final String NO_PACKAGE = "No Package"; |
| private TestNGResults.OpenSourceSelectionListener openSourceListener; |
| private int myStatus = MessageHelper.PASSED_TEST; |
| private Set<String> startedMethods = new HashSet<String>(); |
| private TestProxy myLastSelected; |
| |
| public TestNGResults(final JComponent component, |
| final TestNGConfiguration configuration, |
| final TestNGConsoleView console, |
| final ExecutionEnvironment environment) { |
| super(component, console.getConsole().createConsoleActions(), console.getProperties(), |
| environment, TESTNG_SPLITTER_PROPERTY, 0.5f); |
| this.project = configuration.getProject(); |
| |
| model = new TestNGResultsTableModel(project); |
| resultsTable = new TableView(model); |
| new DoubleClickListener() { |
| @Override |
| protected boolean onDoubleClick(MouseEvent e) { |
| final Object result = resultsTable.getSelectedObject(); |
| if (result instanceof TestResultMessage) { |
| final String testClass = ((TestResultMessage)result).getTestClass(); |
| final PsiClass psiClass = ClassUtil.findPsiClass(PsiManager.getInstance(project), testClass); |
| if (psiClass != null) { |
| final String method = ((TestResultMessage)result).getMethod(); |
| if (method != null) { |
| final PsiMethod[] psiMethods = psiClass.findMethodsByName(method, false); |
| if (psiMethods.length > 0) { |
| psiMethods[0].navigate(true); |
| } |
| } |
| psiClass.navigate(true); |
| return true; |
| } |
| } |
| return false; |
| } |
| }.installOn(resultsTable); |
| |
| rootNode = new TreeRootNode(); |
| console.getUnboundOutput().addChild(rootNode); |
| } |
| |
| protected JComponent createTestTreeView() { |
| tree = new TestNGTestTreeView(); |
| |
| final TestTreeStructure structure = new TestTreeStructure(project, rootNode); |
| tree.attachToModel(this); |
| treeBuilder = new TestTreeBuilder(tree, structure); |
| Disposer.register(this, treeBuilder); |
| |
| animator = new Animator(this, treeBuilder); |
| |
| openSourceListener = new OpenSourceSelectionListener(); |
| tree.getSelectionModel().addTreeSelectionListener(openSourceListener); |
| |
| TrackRunningTestUtil.installStopListeners(tree, this, new Pass<AbstractTestProxy>() { |
| @Override |
| public void pass(AbstractTestProxy abstractTestProxy) { |
| myLastSelected = (TestProxy)abstractTestProxy; |
| } |
| }); |
| |
| return tree; |
| } |
| |
| @Override |
| protected ToolbarPanel createToolbarPanel() { |
| final ToolbarPanel panel = new ToolbarPanel(getProperties(), myEnvironment, this){ |
| @Override |
| protected void appendAdditionalActions(DefaultActionGroup actionGroup, |
| TestConsoleProperties properties, |
| ExecutionEnvironment environment, JComponent parent) { |
| super.appendAdditionalActions(actionGroup, properties, environment, parent); |
| actionGroup.addAction(new ToggleBooleanProperty( |
| ExecutionBundle.message("junit.runing.info.include.non.started.in.rerun.failed.action.name"), |
| null, |
| AllIcons.RunConfigurations.IncludeNonStartedTests_Rerun, |
| properties, TestConsoleProperties.INCLUDE_NON_STARTED_IN_RERUN_FAILED)).setAsSecondary(true); |
| } |
| }; |
| panel.setModel(this); |
| return panel; |
| } |
| |
| public TestConsoleProperties getProperties() { |
| return myProperties; |
| } |
| |
| protected JComponent createStatisticsPanel() { |
| final JPanel panel = new JPanel(new BorderLayout()); //do not remove wrapper panel |
| panel.add(ScrollPaneFactory.createScrollPane(resultsTable), BorderLayout.CENTER); |
| return panel; |
| } |
| |
| private void updateStatusLine() { |
| myStatusLine.setText(getStatusLine()); |
| } |
| |
| public int getStatus() { |
| return myStatus; |
| } |
| |
| public String getStatusLine() { |
| StringBuffer sb = new StringBuffer(); |
| if (end == 0 && start > 0) { |
| sb.append("Running: "); |
| } |
| else { |
| if (failed.size() > 0) sb.append("Failed: ").append(failed.size()).append(" "); |
| sb.append("Done: "); |
| } |
| sb.append(count).append(" of ").append(total); |
| if (end == 0) { |
| if (failed.size() > 0) { |
| sb.append(" Failed: ").append(failed.size()); |
| } |
| } |
| else { |
| final long time = end - start; |
| sb.append(" (").append(time == 0 ? "0.0 s" : NumberFormat.getInstance().format((double)time / 1000.0) + " s").append(") "); |
| } |
| return sb.toString(); |
| } |
| |
| public String getTime() { |
| final long time = end - start; |
| return time == 0 ? "0.0 s" : NumberFormat.getInstance().format((double)time / 1000.0) + " s"; |
| } |
| |
| public TestProxy testStarted(TestResultMessage result) { |
| return testStarted(result, true); |
| } |
| |
| public TestProxy testStarted(TestResultMessage result, boolean registerDups) { |
| TestProxy classNode = getPackageClassNodeFor(result); |
| TestProxy proxy = new TestProxy(); |
| proxy.setParent(classNode); |
| proxy.setResultMessage(result); |
| synchronized (started) { |
| if (registerDups) { |
| List<TestProxy> dups = started.get(result); |
| if (dups == null) { |
| dups = new ArrayList<TestProxy>(); |
| started.put(result, dups); |
| } |
| dups.add(proxy); |
| } |
| } |
| final String testMethodDescriptor = result.getTestClass() + TestProxy.toDisplayText(result, project); |
| if (startedMethods.contains(testMethodDescriptor)) { |
| total++; |
| } |
| else { |
| startedMethods.add(testMethodDescriptor); |
| } |
| animator.setCurrentTestCase(proxy); |
| treeBuilder.addItem(classNode, proxy); |
| //treeBuilder.repaintWithParents(proxy); |
| count++; |
| if (count > total) total = count; |
| if (myLastSelected == proxy) { |
| myLastSelected = null; |
| } |
| if (myLastSelected == null && TestConsoleProperties.TRACK_RUNNING_TEST.value(myProperties)) { |
| selectTest(proxy); |
| } |
| return proxy; |
| } |
| |
| public void addTestResult(final TestResultMessage result, int exceptionMark) { |
| TestProxy testCase; |
| synchronized (started) { |
| final List<TestProxy> dups = started.get(result); |
| testCase = dups == null || dups.isEmpty() ? null : dups.remove(0); |
| } |
| if (testCase == null) { |
| final PsiElement element = getPackageClassNodeFor(result).getPsiElement(); |
| if (element instanceof PsiClass) { |
| final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction( |
| new Computable<PsiMethod[]>() { |
| public PsiMethod[] compute() { |
| return ((PsiClass)element).findMethodsByName(result.getMethod(), true); |
| } |
| } |
| ); |
| if (methods.length > 0 && |
| methods[0] != null && |
| !AnnotationUtil.isAnnotated(methods[0], Arrays.asList(TestNGUtil.CONFIG_ANNOTATIONS_FQN))) { |
| for (List<TestProxy> proxies : started.values()) { |
| if (proxies != null) { |
| for (TestProxy proxy : proxies) { |
| if (methods[0].equals(proxy.getPsiElement())) { |
| testCase = proxy; |
| break; |
| } |
| } |
| } |
| } |
| if (testCase == null) { |
| testCase = testStarted(result, false); |
| testCase.appendStacktrace(result); |
| } |
| } |
| } |
| } |
| |
| if (testCase != null) { |
| testCase.setResultMessage(result); |
| testCase.setTearDownFailure(failedToStart != null); |
| failedToStart = null; |
| |
| if (result.getResult() == MessageHelper.FAILED_TEST) { |
| failed.add(testCase); |
| } |
| model.addTestResult(result); |
| } |
| else { |
| //do not remember testresultmessage: test hierarchy is not set |
| testCase = new TestProxy(TestProxy.toDisplayText(result, project)); |
| testCase.appendStacktrace(result); |
| if (failedToStart != null) { |
| failedToStart.addChild(testCase); |
| failedToStart.setTearDownFailure(true); |
| } |
| else { |
| failedToStart = testCase; |
| } |
| } |
| |
| testCase.setExceptionMark(exceptionMark); |
| AbstractTestProxy.flushOutput(testCase); |
| |
| if (result.getResult() == MessageHelper.FAILED_TEST) { |
| myStatusLine.setStatusColor(ColorProgressBar.RED); |
| myStatus = MessageHelper.FAILED_TEST; |
| } |
| else if (result.getResult() == MessageHelper.SKIPPED_TEST && myStatus == MessageHelper.PASSED_TEST) { |
| myStatus = MessageHelper.SKIPPED_TEST; |
| } |
| myStatusLine.setFraction((double)count / total); |
| updateStatusLine(); |
| TestsUIUtil.showIconProgress(project, count, total, failed.size()); |
| } |
| |
| private TestProxy getPackageClassNodeFor(final TestResultMessage result) { |
| TestProxy owner = treeBuilder.getRoot(); |
| final String packageName1 = StringUtil.getPackageName(result.getTestClass()); |
| String packageName = packageName1.length() == 0 ? NO_PACKAGE : packageName1; |
| owner = getChildNodeNamed(owner, packageName); |
| if (owner.getPsiElement() == null) { |
| owner.setPsiElement(JavaPsiFacade.getInstance(project).findPackage(packageName)); |
| } |
| owner = getChildNodeNamed(owner, StringUtil.getShortName(result.getTestClass())); |
| //look up the psiclass now |
| if (owner.getPsiElement() == null) { |
| final TestProxy finalOwner = owner; |
| ApplicationManager.getApplication().runReadAction(new Runnable() { |
| public void run() { |
| finalOwner.setPsiElement(ClassUtil.findPsiClass(PsiManager.getInstance(project), result.getTestClass())); |
| } |
| }); |
| } |
| return owner; |
| } |
| |
| private TestProxy getChildNodeNamed(TestProxy currentNode, String node) { |
| for (TestProxy child : currentNode.getChildren()) { |
| if (child.getName().equals(node)) { |
| return child; |
| } |
| } |
| |
| TestProxy child = new TestProxy(node); |
| treeBuilder.addItem(currentNode, child); |
| return child; |
| } |
| |
| public void selectTest(TestProxy proxy) { |
| if (proxy == null) return; |
| treeBuilder.select(proxy, null); |
| } |
| |
| public void setTotal(int total) { |
| this.total += total; |
| } |
| |
| public void start() { |
| if (start == 0) { |
| start = System.currentTimeMillis(); |
| } |
| treeBuilder.select(rootNode); |
| rootNode.setInProgress(true); |
| rootNode.setStarted(true); |
| } |
| |
| public void finish(final boolean started) { |
| if (start > 0) { |
| end = System.currentTimeMillis(); |
| } |
| LvcsHelper.addLabel(this); |
| |
| SwingUtilities.invokeLater(new Runnable() { |
| public void run() { |
| animator.stopMovie(); |
| updateStatusLine(); |
| if (total > count || myStatus == MessageHelper.SKIPPED_TEST) { |
| myStatusLine.setStatusColor(ColorProgressBar.YELLOW); |
| } |
| else if (myStatus == MessageHelper.FAILED_TEST) { |
| myStatusLine.setStatusColor(ColorProgressBar.RED); |
| } |
| else { |
| myStatusLine.setStatusColor(ColorProgressBar.GREEN); |
| } |
| rootNode.setInProgress(false); |
| TestStatusListener.notifySuiteFinished(rootNode, project); |
| if (TestNGConsoleProperties.SELECT_FIRST_DEFECT.value(myProperties)) { |
| selectTest(rootNode.getFirstDefect()); |
| } |
| else { |
| final DefaultMutableTreeNode node = treeBuilder.getNodeForElement(rootNode); |
| if (node != null && myLastSelected == null) { |
| tree.getSelectionModel().setSelectionPath(new TreePath(node)); |
| } |
| } |
| tree.repaint(); |
| TestsUIUtil.notifyByBalloon(project, started, rootNode, getProperties(), "in " + getTime()); |
| } |
| }); |
| } |
| |
| public void setFilter(final Filter filter) { |
| getTreeStructure().setFilter(filter); |
| treeBuilder.updateFromRoot(); |
| } |
| |
| public boolean isRunning() { |
| return rootNode.isInProgress(); |
| } |
| |
| public TestTreeView getTreeView() { |
| return tree; |
| } |
| |
| @Override |
| public TestTreeBuilder getTreeBuilder() { |
| return treeBuilder; |
| } |
| |
| public boolean hasTestSuites() { |
| return rootNode.getChildren().size() > 0; |
| } |
| |
| public TestProxy getRoot() { |
| return rootNode; |
| } |
| |
| public void selectAndNotify(final AbstractTestProxy testProxy) { |
| selectTest((TestProxy)testProxy); |
| } |
| |
| public TestTreeStructure getTreeStructure() { |
| return (TestTreeStructure)treeBuilder.getTreeStructure(); |
| } |
| |
| public void rebuildTree() { |
| treeBuilder.updateFromRoot(); |
| tree.invalidate(); |
| } |
| |
| public void dispose() { |
| super.dispose(); |
| tree.getSelectionModel().removeTreeSelectionListener(openSourceListener); |
| TestsUIUtil.clearIconProgress(project); |
| } |
| |
| public TestProxy getFailedToStart() { |
| return failedToStart; |
| } |
| |
| public void setFailedToStart(TestProxy failedToStart) { |
| this.failedToStart = failedToStart; |
| } |
| |
| public boolean hasFinishedTests() { |
| return count > 0; |
| } |
| |
| private class OpenSourceSelectionListener implements TreeSelectionListener { |
| |
| public void valueChanged(TreeSelectionEvent e) { |
| TreePath path = e.getPath(); |
| if (path == null) return; |
| TestProxy proxy = (TestProxy)tree.getSelectedTest(); |
| if (proxy == null) return; |
| if (ScrollToTestSourceAction.isScrollEnabled(TestNGResults.this)) { |
| OpenSourceUtil.openSourcesFrom(tree, false); |
| } |
| } |
| } |
| } |