blob: 2895c83f88c5a618da45a3342b8aaedba148bab9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Sebastian Davids - sdavids@gmx.de bugs 26754, 41228
*******************************************************************************/
package org.eclipse.jdt.internal.junit.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Vector;
import org.eclipse.jdt.junit.ITestRunListener;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
/*
* A view that shows the contents of a test suite
* as a tree.
*/
class HierarchyRunView implements ITestRunView, IMenuListener {
private Tree fTree;
private TreeItem fCachedParent;
private TreeItem[] fCachedItems;
private TreeItem fLastParent;
private List fExecutionPath;
private boolean fMoveSelection= false;
/**
* Helper used to resurrect test hierarchy
*/
private static class SuiteInfo {
public int fTestCount;
public TreeItem fTreeItem;
public SuiteInfo(TreeItem treeItem, int testCount){
fTreeItem= treeItem;
fTestCount= testCount;
}
}
/**
* Vector of SuiteInfo items
*/
private Vector fSuiteInfos= new Vector();
/**
* Maps test Ids to TreeItems.
*/
private Map fTreeItemMap= new HashMap();
private TestRunnerViewPart fTestRunnerPart;
private final Image fOkIcon= TestRunnerViewPart.createImage("obj16/testok.gif"); //$NON-NLS-1$
private final Image fErrorIcon= TestRunnerViewPart.createImage("obj16/testerr.gif"); //$NON-NLS-1$
private final Image fFailureIcon= TestRunnerViewPart.createImage("obj16/testfail.gif"); //$NON-NLS-1$
private final Image fHierarchyIcon= TestRunnerViewPart.createImage("obj16/testhier.gif"); //$NON-NLS-1$
private final Image fSuiteIcon= TestRunnerViewPart.createImage("obj16/tsuite.gif"); //$NON-NLS-1$
private final Image fSuiteErrorIcon= TestRunnerViewPart.createImage("obj16/tsuiteerror.gif"); //$NON-NLS-1$
private final Image fSuiteFailIcon= TestRunnerViewPart.createImage("obj16/tsuitefail.gif"); //$NON-NLS-1$
private final Image fTestIcon= TestRunnerViewPart.createImage("obj16/test.gif"); //$NON-NLS-1$
private final Image fTestRunningIcon= TestRunnerViewPart.createImage("obj16/testrun.gif"); //$NON-NLS-1$
private final Image fSuiteRunningIcon= TestRunnerViewPart.createImage("obj16/tsuiterun.gif"); //$NON-NLS-1$
private class ExpandAllAction extends Action {
public ExpandAllAction() {
setText(JUnitMessages.getString("ExpandAllAction.text")); //$NON-NLS-1$
setToolTipText(JUnitMessages.getString("ExpandAllAction.tooltip")); //$NON-NLS-1$
}
public void run(){
expandAll();
}
}
public HierarchyRunView(CTabFolder tabFolder, TestRunnerViewPart runner) {
fTestRunnerPart= runner;
CTabItem hierarchyTab= new CTabItem(tabFolder, SWT.NONE);
hierarchyTab.setText(getName());
hierarchyTab.setImage(fHierarchyIcon);
Composite testTreePanel= new Composite(tabFolder, SWT.NONE);
GridLayout gridLayout= new GridLayout();
gridLayout.marginHeight= 0;
gridLayout.marginWidth= 0;
testTreePanel.setLayout(gridLayout);
GridData gridData= new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
testTreePanel.setLayoutData(gridData);
hierarchyTab.setControl(testTreePanel);
hierarchyTab.setToolTipText(JUnitMessages.getString("HierarchyRunView.tab.tooltip")); //$NON-NLS-1$
fTree= new Tree(testTreePanel, SWT.V_SCROLL | SWT.SINGLE);
gridData= new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
fTree.setLayoutData(gridData);
initMenu();
addListeners();
}
void disposeIcons() {
fErrorIcon.dispose();
fFailureIcon.dispose();
fOkIcon.dispose();
fHierarchyIcon.dispose();
fTestIcon.dispose();
fTestRunningIcon.dispose();
fSuiteRunningIcon.dispose();
fSuiteIcon.dispose();
fSuiteErrorIcon.dispose();
fSuiteFailIcon.dispose();
}
private void initMenu() {
MenuManager menuMgr= new MenuManager();
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(this);
Menu menu= menuMgr.createContextMenu(fTree);
fTree.setMenu(menu);
}
private String getTestMethod() {
return getTestInfo().getTestMethodName();
}
private TestRunInfo getTestInfo() {
TreeItem[] treeItems= fTree.getSelection();
if(treeItems.length == 0)
return null;
return ((TestRunInfo)treeItems[0].getData());
}
private boolean isSuiteSelected() {
TreeItem[] treeItems= fTree.getSelection();
if(treeItems.length != 1)
return false;
return treeItems[0].getItemCount() > 0;
}
private String getClassName() {
return getTestInfo().getClassName();
}
public String getSelectedTestId() {
TestRunInfo testInfo= getTestInfo();
if (testInfo == null)
return null;
return testInfo.getTestId();
}
public String getName() {
return JUnitMessages.getString("HierarchyRunView.tab.title"); //$NON-NLS-1$
}
public void setSelectedTest(String testId) {
TreeItem treeItem= findTreeItem(testId);
if (treeItem != null)
fTree.setSelection(new TreeItem[]{treeItem});
}
public void startTest(String testId) {
TreeItem treeItem= findTreeItem(testId);
if (treeItem == null)
return;
TreeItem parent= treeItem.getParentItem();
if (fLastParent != parent) {
updatePath(parent);
fLastParent= parent;
}
setCurrentItem(treeItem);
}
private void updatePath(TreeItem parent) {
List newPath= new ArrayList();
while (parent != null) {
newPath.add(parent);
parent= parent.getParentItem();
}
Collections.reverse(newPath);
// common path
ListIterator old= fExecutionPath.listIterator();
ListIterator np= newPath.listIterator();
int c= 0;
while (old.hasNext() && np.hasNext()) {
if (old.next() != np.next())
break;
c++;
}
// clear old path
for (ListIterator iter= fExecutionPath.listIterator(c); iter.hasNext(); )
refreshItem((TreeItem) iter.next(), false);
// update new path
for (ListIterator iter= newPath.listIterator(c); iter.hasNext(); )
refreshItem((TreeItem) iter.next(), true);
fExecutionPath= newPath;
}
private void refreshItem(TreeItem item, boolean onPath) {
if (onPath)
item.setImage(fSuiteRunningIcon);
else {
TestRunInfo info= getTestRunInfo(item);
switch (info.getStatus()) {
case ITestRunListener.STATUS_ERROR:
item.setImage(fSuiteErrorIcon);
break;
case ITestRunListener.STATUS_FAILURE:
item.setImage(fSuiteFailIcon);
break;
default:
item.setImage(fSuiteIcon);
}
}
}
private void setCurrentItem(TreeItem treeItem) {
treeItem.setImage(fTestRunningIcon);
TreeItem parent= treeItem.getParentItem();
if (fTestRunnerPart.isAutoScroll()) {
fTree.showItem(treeItem);
while (parent != null) {
if (parent.getExpanded())
break;
parent.setExpanded(true);
parent= parent.getParentItem();
}
}
}
public void endTest(String testId) {
TreeItem treeItem= findTreeItem(testId);
if (treeItem == null)
return;
TestRunInfo testInfo= fTestRunnerPart.getTestInfo(testId);
updateItem(treeItem, testInfo);
if (fTestRunnerPart.isAutoScroll()) {
fTree.showItem(treeItem);
cacheItems(treeItem);
collapsePassedTests(treeItem);
}
}
private void cacheItems(TreeItem treeItem) {
TreeItem parent= treeItem.getParentItem();
if (parent == fCachedParent)
return;
fCachedItems= parent.getItems();
fCachedParent= parent;
}
private void collapsePassedTests(TreeItem treeItem) {
TreeItem parent= treeItem.getParentItem();
if (parent != null) {
TreeItem[] items= null;
if (parent == fCachedParent)
items= fCachedItems;
else
items= parent.getItems();
if (isLast(treeItem, items)) {
boolean ok= true;
for (int i= 0; i < items.length; i++) {
if (isFailure(items[i])) {
ok= false;
break;
}
}
if (ok) {
parent.setExpanded(false);
collapsePassedTests(parent);
}
}
}
}
private boolean isLast(TreeItem treeItem, TreeItem[] items) {
return items[items.length-1] == treeItem;
}
private void updateItem(TreeItem treeItem, TestRunInfo testInfo) {
treeItem.setData(testInfo);
if(testInfo.getStatus() == ITestRunListener.STATUS_OK) {
treeItem.setImage(fOkIcon);
return;
}
if (testInfo.getStatus() == ITestRunListener.STATUS_FAILURE)
treeItem.setImage(fFailureIcon);
else if (testInfo.getStatus() == ITestRunListener.STATUS_ERROR)
treeItem.setImage(fErrorIcon);
propagateStatus(treeItem, testInfo.getStatus());
}
private void propagateStatus(TreeItem item, int status) {
TreeItem parent= item.getParentItem();
TestRunInfo testRunInfo= getTestRunInfo(item);
if (parent == null)
return;
TestRunInfo parentInfo= getTestRunInfo(parent);
int parentStatus= parentInfo.getStatus();
if (status == ITestRunListener.STATUS_FAILURE) {
if (parentStatus == ITestRunListener.STATUS_ERROR || parentStatus == ITestRunListener.STATUS_FAILURE)
return;
parentInfo.setStatus(ITestRunListener.STATUS_FAILURE);
testRunInfo.setStatus(ITestRunListener.STATUS_FAILURE);
} else {
if (parentStatus == ITestRunListener.STATUS_ERROR)
return;
parentInfo.setStatus(ITestRunListener.STATUS_ERROR);
testRunInfo.setStatus(ITestRunListener.STATUS_ERROR);
}
propagateStatus(parent, status);
}
private TestRunInfo getTestRunInfo(TreeItem item) {
return (TestRunInfo)item.getData();
}
public void activate() {
fMoveSelection= false;
testSelected();
}
public void setFocus() {
fTree.setFocus();
}
public void aboutToStart() {
fTree.removeAll();
fSuiteInfos.removeAllElements();
fTreeItemMap= new HashMap();
fCachedParent= null;
fCachedItems= null;
fMoveSelection= false;
fExecutionPath= new ArrayList();
}
private void testSelected() {
fTestRunnerPart.handleTestSelected(getSelectedTestId());
}
private void addListeners() {
fTree.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
activate();
}
public void widgetDefaultSelected(SelectionEvent e) {
activate();
}
});
fTree.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
disposeIcons();
}
});
fTree.addMouseListener(new MouseAdapter() {
public void mouseDoubleClick(MouseEvent e) {
handleDoubleClick(e);
}
});
}
void handleDoubleClick(MouseEvent e) {
TestRunInfo testInfo= getTestInfo();
if (testInfo == null)
return;
String testLabel= testInfo.getTestName();
OpenTestAction action= null;
if (isSuiteSelected())
action= new OpenTestAction(fTestRunnerPart, testLabel);
else
action= new OpenTestAction(fTestRunnerPart, getClassName(), getTestMethod());
if (action != null && action.isEnabled())
action.run();
}
public void menuAboutToShow(IMenuManager manager) {
if (fTree.getSelectionCount() > 0) {
TreeItem treeItem= fTree.getSelection()[0];
TestRunInfo testInfo= (TestRunInfo) treeItem.getData();
String testLabel= testInfo.getTestName();
if (isSuiteSelected()) {
manager.add(new OpenTestAction(fTestRunnerPart, testLabel));
} else {
manager.add(new OpenTestAction(fTestRunnerPart, getClassName(), getTestMethod()));
manager.add(new RerunAction(fTestRunnerPart, getSelectedTestId(), getClassName(), getTestMethod()));
}
manager.add(new Separator());
manager.add(new ExpandAllAction());
}
}
public void newTreeEntry(String treeEntry) {
// format: testId","testName","isSuite","testcount
int index0= treeEntry.indexOf(',');
StringBuffer testStringBuffer= new StringBuffer(100);
int index1= scanTestName(treeEntry, index0+1, testStringBuffer);
int index2= treeEntry.indexOf(',', index1+1);
String testString= testStringBuffer.toString().trim();
String id= treeEntry.substring(0, index0);
TestRunInfo testInfo= new TestRunInfo(id, testString);
String isSuite= treeEntry.substring(index1+1, index2);
int testCount= Integer.parseInt(treeEntry.substring(index2+1));
TreeItem treeItem;
while((fSuiteInfos.size() > 0) && (((SuiteInfo) fSuiteInfos.lastElement()).fTestCount == 0)) {
fSuiteInfos.removeElementAt(fSuiteInfos.size()-1);
}
if(fSuiteInfos.size() == 0){
treeItem= new TreeItem(fTree, SWT.NONE);
treeItem.setImage(fSuiteIcon);
fSuiteInfos.addElement(new SuiteInfo(treeItem, testCount));
} else if(isSuite.equals("true")) { //$NON-NLS-1$
treeItem= new TreeItem(((SuiteInfo) fSuiteInfos.lastElement()).fTreeItem, SWT.NONE);
treeItem.setImage(fSuiteIcon);
((SuiteInfo)fSuiteInfos.lastElement()).fTestCount -= 1;
fSuiteInfos.addElement(new SuiteInfo(treeItem, testCount));
} else {
treeItem= new TreeItem(((SuiteInfo) fSuiteInfos.lastElement()).fTreeItem, SWT.NONE);
treeItem.setImage(fTestIcon);
((SuiteInfo)fSuiteInfos.lastElement()).fTestCount -= 1;
mapTest(testInfo, treeItem);
}
treeItem.setText(testInfo.getTestMethodName());
treeItem.setData(testInfo);
}
private int scanTestName(String s, int start, StringBuffer testName) {
boolean inQuote= false;
int i= start;
for (; i < s.length(); i++) {
char c= s.charAt(i);
if (c == '\\' && !inQuote) {
inQuote= true;
continue;
} else if (inQuote) {
inQuote= false;
testName.append(c);
} else if (c == ',')
break;
else
testName.append(c);
}
return i;
}
private void mapTest(TestRunInfo info, TreeItem item) {
fTreeItemMap.put(info.getTestId(), item);
}
private TreeItem findTreeItem(String testId) {
Object o= fTreeItemMap.get(testId);
if (o instanceof TreeItem)
return (TreeItem)o;
return null;
}
/*
* @see ITestRunView#testStatusChanged(TestRunInfo, int)
*/
public void testStatusChanged(TestRunInfo newInfo) {
Object o= fTreeItemMap.get(newInfo.getTestId());
if (o instanceof TreeItem) {
updateItem((TreeItem)o, newInfo);
return;
}
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.junit.ui.ITestRunView#selectNext()
*/
public void selectNext() {
TreeItem selection= getInitialSearchSelection();
if (!moveSelection(selection))
return;
TreeItem failure= findFailure(selection, true, !isLeafFailure(selection));
if (failure != null)
selectTest(failure);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.junit.ui.ITestRunView#selectPrevious()
*/
public void selectPrevious() {
TreeItem selection= getInitialSearchSelection();
if (!moveSelection(selection))
return;
TreeItem failure= findFailure(selection, false, !isLeafFailure(selection));
if (failure != null)
selectTest(failure);
}
private boolean moveSelection(TreeItem selection) {
if (!fMoveSelection) {
fMoveSelection= true;
if (isLeafFailure(selection)) {
selectTest(selection);
return false;
}
}
return true;
}
private TreeItem getInitialSearchSelection() {
TreeItem[] treeItems= fTree.getSelection();
TreeItem selection= null;
if (treeItems.length == 0)
selection= fTree.getItems()[0];
else
selection= treeItems[0];
return selection;
}
private boolean isFailure(TreeItem selection) {
return !(getTestRunInfo(selection).getStatus() == ITestRunListener.STATUS_OK);
}
private boolean isLeafFailure(TreeItem selection) {
boolean isLeaf= selection.getItemCount() == 0;
return isLeaf && isFailure(selection);
}
private void selectTest(TreeItem selection) {
fTestRunnerPart.showTest(getTestRunInfo(selection));
}
private TreeItem findFailure(TreeItem start, boolean next, boolean includeNode) {
TreeItem[] sib= findSiblings(start, next, includeNode);
if (next) {
for (int i= 0; i < sib.length; i++) {
TreeItem failure= findFailureInTree(sib[i]);
if (failure != null)
return failure;
}
} else {
for (int i= sib.length-1; i >= 0; i--) {
TreeItem failure= findFailureInTree(sib[i]);
if (failure != null)
return failure;
}
}
TreeItem parent= start.getParentItem();
if (parent == null)
return null;
return findFailure(parent, next, false);
}
private TreeItem[] findSiblings(TreeItem item, boolean next, boolean includeNode) {
TreeItem parent= item.getParentItem();
TreeItem[] children= null;
if (parent == null)
children= item.getParent().getItems();
else
children= parent.getItems();
for (int i= 0; i < children.length; i++) {
TreeItem item2= children[i];
if (item2 == item) {
TreeItem[] result= null;
if (next) {
if (!includeNode) {
result= new TreeItem[children.length-i-1];
System.arraycopy(children, i+1, result, 0, children.length-i-1);
} else {
result= new TreeItem[children.length-i];
System.arraycopy(children, i, result, 0, children.length-i);
}
} else {
if (!includeNode) {
result= new TreeItem[i];
System.arraycopy(children, 0, result, 0, i);
} else {
result= new TreeItem[i+1];
System.arraycopy(children, 0, result, 0, i+1);
}
}
return result;
}
}
return new TreeItem[0];
}
private TreeItem findFailureInTree(TreeItem item) {
if (item.getItemCount() == 0) {
if (isFailure(item))
return item;
}
TreeItem[] children= item.getItems();
for (int i= 0; i < children.length; i++) {
TreeItem item2= findFailureInTree(children[i]);
if (item2 != null)
return item2;
}
return null;
}
protected void expandAll() {
TreeItem[] treeItems= fTree.getSelection();
fTree.setRedraw(false);
for (int i= 0; i < treeItems.length; i++) {
expandAll(treeItems[i]);
}
fTree.setRedraw(true);
}
private void expandAll(TreeItem item) {
item.setExpanded(true);
TreeItem[] items= item.getItems();
for (int i= 0; i < items.length; i++) {
expandAll(items[i]);
}
}
public void aboutToEnd() {
for (int i= 0; i < fExecutionPath.size(); i++) {
refreshItem((TreeItem) fExecutionPath.get(i), false);
}
}
}