/*******************************************************************************
* Copyright (c) 2004, 2010 BREDEX GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BREDEX GmbH - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.jubula.client.core.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.list.UnmodifiableList;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.eclipse.jubula.client.core.businessprocess.AbstractParamInterfaceBP;
import org.eclipse.jubula.client.core.businessprocess.ExternalTestDataBP;
import org.eclipse.jubula.client.core.businessprocess.TestExecution;
import org.eclipse.jubula.client.core.businessprocess.TestExecution.PauseMode;
import org.eclipse.jubula.client.core.i18n.Messages;
import org.eclipse.jubula.client.core.model.IAbstractContainerPO;
import org.eclipse.jubula.client.core.model.ICapPO;
import org.eclipse.jubula.client.core.model.ICommentPO;
import org.eclipse.jubula.client.core.model.ICondStructPO;
import org.eclipse.jubula.client.core.model.IConditionalStatementPO;
import org.eclipse.jubula.client.core.model.IControllerPO;
import org.eclipse.jubula.client.core.model.IDataSetPO;
import org.eclipse.jubula.client.core.model.IDoWhilePO;
import org.eclipse.jubula.client.core.model.IEventExecTestCasePO;
import org.eclipse.jubula.client.core.model.IEventStackModificationListener;
import org.eclipse.jubula.client.core.model.IExecStackModificationListener;
import org.eclipse.jubula.client.core.model.IExecTestCasePO;
import org.eclipse.jubula.client.core.model.IIteratePO;
import org.eclipse.jubula.client.core.model.INodePO;
import org.eclipse.jubula.client.core.model.IParamDescriptionPO;
import org.eclipse.jubula.client.core.model.IParamNodePO;
import org.eclipse.jubula.client.core.model.IParameterInterfacePO;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.model.ITDManager;
import org.eclipse.jubula.client.core.model.ITestSuitePO;
import org.eclipse.jubula.client.core.model.IWhileDoPO;
import org.eclipse.jubula.client.core.model.NodeMaker;
import org.eclipse.jubula.client.core.model.ReentryProperty;
import org.eclipse.jubula.client.core.model.TestResultNode;
import org.eclipse.jubula.tools.internal.constants.StringConstants;
import org.eclipse.jubula.tools.internal.exception.Assert;
import org.eclipse.jubula.tools.internal.exception.IncompleteDataException;
import org.eclipse.jubula.tools.internal.exception.InvalidDataException;
import org.eclipse.jubula.tools.internal.exception.JBException;
import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs;
import org.eclipse.jubula.tools.internal.objects.event.TestErrorEvent;
import org.eclipse.osgi.util.NLS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author BREDEX GmbH
* @created 11.04.2005
*/
public class Traverser {
/**
* Represents the exact location of a test step within the execution
* hierarchy.
*
* @author BREDEX GmbH
* @created 06.03.2008
*/
private static final class ExecStackMarker {
/** the execution stack at the time the marker was created */
private Vector<ExecObject> m_execStack;
/** the test step represented by the marker */
private ICapPO m_step;
/**
* Constructor
*
* @param execStack The current execution stack.
* @param step The current test step.
*/
ExecStackMarker(Stack<ExecObject> execStack, ICapPO step) {
m_execStack = new Vector<ExecObject>(execStack);
m_step = step;
}
/**
*
* @return the execution stack at the time this marker was created.
*/
@SuppressWarnings("unchecked")
List<ExecObject> getExecStack() {
return UnmodifiableList.decorate(m_execStack);
}
/**
*
* @return the test step represented by this marker.
*/
ICapPO getStep() {
return m_step;
}
/**
*
* {@inheritDoc}
*/
public boolean equals(Object obj) {
if (obj instanceof ExecStackMarker) {
ExecStackMarker marker = (ExecStackMarker)obj;
return new EqualsBuilder()
.append(m_execStack, marker.getExecStack())
.append(m_step, marker.getStep()).isEquals();
}
return super.equals(obj);
}
/**
*
* {@inheritDoc}
*/
public int hashCode() {
return new HashCodeBuilder().append(m_execStack).append(m_step)
.toHashCode();
}
}
/**
* <code>NO_DATASET</code> to use for node operations without
* consideration of datasets
*/
public static final int NO_DATASET = -1;
/**
* <code>NO_INDEX</code> placeholder for index to use, if childrenlist of
* a node isn't yet proceeded
*/
public static final int NO_INDEX = -1;
/** The logger */
private static final Logger LOG = LoggerFactory.getLogger(Traverser.class);
/**
* <code>m_root</code> root node
*/
private INodePO m_root;
/**
* <code>m_execStack</code> stack for executed objects
*/
private Stack<ExecObject> m_execStack = new Stack<ExecObject>();
/**
* list of execution stack listeners
*/
private List<IExecStackModificationListener> m_execListenerList =
new ArrayList<IExecStackModificationListener>();
/**
* list of event stack listeners
*/
private List<IEventStackModificationListener> m_eventListenerList =
new ArrayList<IEventStackModificationListener>();
/**
* <code>m_eventStack</code> stack for actual executed eventHandler
* stackobjects are from type EventObject
*/
private Stack<EventObject> m_eventStack = new Stack<EventObject>();
/**
* mapping from positions in the execution hierarchy to the number of times
* the test step at that position has been retried
*/
private Map<ExecStackMarker, Integer> m_markerToNumRetriesMap =
new HashMap<ExecStackMarker, Integer>();
/** business process for retrieving test data */
private ExternalTestDataBP m_externalTestDataBP;
/** Indicates that we are just building the test result tree - this is for the loops */
private boolean m_building = false;
/** Maximum number of iterations */
private int m_iterMax = 100;
/**
* @param root root node from tree
*/
public Traverser(INodePO root) {
m_root = root;
m_externalTestDataBP = new ExternalTestDataBP();
m_execStack.push(new ExecObject(root, 0));
executeLogging();
}
/**
* @return the next Cap, regarding to the actual position in tree
* @throws JBException in case of missing testdata
*/
@SuppressWarnings("unchecked")
public ICapPO next() throws JBException {
if (!m_execStack.isEmpty()) {
ExecObject stackObj = m_execStack.peek();
INodePO node = stackObj.getExecNode();
// next index
ITDManager tdManager = null;
if (node instanceof IParamNodePO) {
tdManager =
m_externalTestDataBP.getExternalCheckedTDManager(
(IParamNodePO)node);
}
if (stackObj.getIndex() < node.getNodeListSize() - 1) {
stackObj.incrementIndex();
List<INodePO> nodeList =
IteratorUtils.toList(node.getNodeListIterator());
INodePO childNode = nodeList.get(stackObj.getIndex());
if (!childNode.isActive()) {
return next();
}
if (childNode instanceof ICapPO) {
if (LOG.isDebugEnabled()) {
LOG.debug(Messages.ActualExecutedCap
+ StringConstants.COLON + StringConstants.SPACE
+ childNode.getName());
}
fireNextCap((ICapPO)childNode);
return (ICapPO)childNode;
}
if (childNode instanceof IExecTestCasePO) {
if (((IExecTestCasePO)childNode).getSpecTestCase()
== null) {
throw new IncompleteDataException(
Messages.ExecTestCasePOMissingReference,
MessageIDs.E_DATASOURCE_CONTAIN_EMPTY_DATA);
}
processExecTestCase(stackObj, (IExecTestCasePO)childNode);
return next();
}
if (childNode instanceof ICommentPO
|| childNode instanceof IControllerPO
|| childNode instanceof IAbstractContainerPO) {
m_execStack.push(new ExecObject(childNode, NO_DATASET));
fireExecStackIncremented(childNode);
if (childNode instanceof IIteratePO) {
// not very nice, but we handle the 0-times executed iteration this way
decrementIterate(((IIteratePO) childNode).
getDoBranch());
}
return next();
}
Assert.notReached(Messages.ErrorInTestExecutionTree);
return null;
// next dataset
} else if (!(stackObj.getExecNode() instanceof ITestSuitePO)
&& tdManager != null) {
int maxDsNumber = tdManager.getDataSetCount();
if (stackObj.getNumberDs() == Traverser.NO_DATASET) {
stackObj.incrementDataSetNumber();
}
if (stackObj.getNumberDs() + 1 < maxDsNumber) {
stackObj.incrementDataSetNumber();
stackObj.setIndex(NO_INDEX);
fireNextDataSetIteration();
return next();
}
ReentryProperty prop = decrementStack(node);
return (prop == null) ? next() : next(prop);
} else {
ReentryProperty prop = decrementStack(node);
return (prop == null) ? next() : next(prop);
}
}
// end
return null;
}
/**
* processes a childnode which is an ExecTestCasePO
* @param stackObj the stack object
* @param exec the child node
* @throws JBException an exception.
*/
private void processExecTestCase(ExecObject stackObj,
final IExecTestCasePO exec)
throws JBException {
final ITDManager tdManager = m_externalTestDataBP
.getExternalCheckedTDManager(exec);
IDataSetPO dataSet = null;
int dataSetSize = tdManager.getDataSetCount();
if (dataSetSize > 0) {
dataSet = tdManager.getDataSet(0);
}
String modelValue = null;
if (dataSet != null && dataSet.getColumnCount() > 0) {
modelValue = dataSet.getValueAt(0);
}
// own data sets - > always begin at 0
if (dataSetSize > 1) {
m_execStack.push(new ExecObject(exec,
getFirstDataSetNumber(exec)));
} else if (dataSetSize == 1) {
// exact one dataset with a reference ->
// begin at data set index of parent
String uniqueId = tdManager.getUniqueIds().get(0);
IParamDescriptionPO desc = exec.getParameterForUniqueId(
uniqueId);
ParamValueConverter conv = new ModelParamValueConverter(
modelValue, exec, desc);
if (modelValue != null && conv.containsReferences()) {
m_execStack.push(new ExecObject(exec,
stackObj.getNumberDs()));
// exact one dataset with a value or no value in runIncomplete-mode
// always use dataset number 0
} else if (modelValue != null) {
m_execStack.push(new ExecObject(exec, 0));
} else {
throw new IncompleteDataException(
NLS.bind(Messages.ErrorWhenBuildingTestExecutionTree,
exec.getSpecAncestor().getName()),
MessageIDs.E_DATASOURCE_CONTAIN_EMPTY_DATA);
}
// no data sets or fixed value
} else {
m_execStack.push(new ExecObject(exec, NO_DATASET));
}
fireExecStackIncremented(exec);
}
/**
* @param node node to pop from stack
* @return the associated reentryProperty in case of eventExecTestCaseNode
* or null in case of execTestCaseNode
*/
private ReentryProperty decrementStack(INodePO node)
throws IncompleteDataException {
ReentryProperty prop = null;
if (isEventHandler(node)) {
IEventExecTestCasePO eventExec = (IEventExecTestCasePO)node;
prop = eventExec.getReentryProp();
}
if (node instanceof IAbstractContainerPO) {
// a container is finished without a Check Fail being triggered
// what happens next depends on what the container's parent is and what is the container
// first we remove the container
m_execStack.pop();
fireExecStackDecremented();
if (m_execStack.isEmpty()) {
// this can only happen when adding a new branch to the Test Result Tree
// then the root is a ContainerPO
return prop;
}
// so here the top of the exec stack is the controller, that is, node.getParent
if (node.getParentNode() instanceof IConditionalStatementPO) {
decrementCondStatementCont(node);
} else if (node.getParentNode() instanceof IDoWhilePO
|| node.getParentNode() instanceof IWhileDoPO) {
decrementDoWhileDoCont(node);
} else if (node.getParentNode() instanceof IIteratePO) {
decrementIterate(node);
}
return prop;
}
if (!m_execStack.isEmpty()) {
m_execStack.pop();
fireExecStackDecremented();
}
return prop;
}
/**
* A sub-branch of a Conditional Statement is finished
* @param node the sub-branch
*/
private void decrementCondStatementCont(INodePO node)
throws IncompleteDataException {
IConditionalStatementPO par =
(IConditionalStatementPO) node.getParentNode();
if (node.equals(par.getCondition())) {
// the condition is finished, so we continue with the Then or Else branches
INodePO next = par.isNegate() ? par.getElseBranch()
: par.getThenBranch();
m_execStack.push(new ExecObject(next, NO_DATASET));
fireExecStackIncremented(next);
return;
}
// the Then or Else branches are finished, so the conditional statement is also
m_execStack.pop();
fireExecStackDecremented();
}
/**
* A sub-branch of a Do-While or While-Do is finished
* @param node the sub-branch
*/
private void decrementDoWhileDoCont(INodePO node)
throws IncompleteDataException {
ICondStructPO par =
(ICondStructPO) node.getParentNode();
if (node.equals(par.getCondition())) {
// the condition is TRUE
if (par.isNegate() || m_building) {
// negated Do-While-Do or only Test Result Tree building, so we have to stop now
m_execStack.pop();
fireExecStackDecremented();
} else {
// we continue with the do branch
// if the Do-While-Do is negated then we will simply stop
INodePO next = par.getDoBranch();
m_execStack.push(new ExecObject(next, NO_DATASET));
fireExecStackIncremented(next);
}
} else {
// the do branch is executed, here comes the condition
if (m_execStack.peek().getIncLoopCount() >= m_iterMax) {
// or rather not, this looks like an infinite loop...
fireInifiniteLoop();
m_execStack.pop();
fireExecStackDecremented();
LOG.error(Messages.ErrorInfiniteLoop);
} else {
INodePO next = par.getCondition();
m_execStack.push(new ExecObject(next, NO_DATASET));
fireExecStackIncremented(next);
}
}
}
/**
* The Do block of an Iterate is finished
* @param node the Do block
*/
public void decrementIterate(INodePO node) throws IncompleteDataException {
IIteratePO iter = (IIteratePO) node.getParentNode();
ExecObject top = m_execStack.peek();
int limit = 0;
if (!m_building) {
try {
limit = Math.min(Integer.parseInt(top.getParameterValue(
iter.getParameterList().get(0).getUniqueId())), m_iterMax);
} catch (NumberFormatException e) {
limit = 0;
}
}
if (limit < top.getIncLoopCount()) {
// end
if (limit == m_iterMax) {
fireInifiniteLoop();
}
m_execStack.pop();
fireExecStackDecremented();
LOG.error(Messages.ErrorInfiniteLoop);
} else {
// continue
INodePO next = iter.getDoBranch();
m_execStack.push(new ExecObject(next, NO_DATASET));
fireExecStackIncremented(next);
}
}
/**
* write information to log
*/
private void executeLogging() {
if (LOG.isDebugEnabled() && !m_execStack.isEmpty()) {
LOG.debug(Messages.ActualPeekObjectOnStack + StringConstants.COLON
+ StringConstants.SPACE
+ m_execStack.peek().getExecNode().getName());
}
}
/**
* @param node for which the number of dataset to find
* @return number of datasets for given node
* @throws JBException in case of missing testdata
* hint: each execTestCase with a dataManager has parameter(s) and must
* have at least one dataset
*/
private int getFirstDataSetNumber(INodePO node)
throws JBException {
int firstDs = NO_DATASET;
if (node instanceof IParamNodePO
&& ((IParamNodePO) node).getDataManager() != null) {
IParamNodePO paramNode = (IParamNodePO)node;
ITDManager tdManager =
m_externalTestDataBP.getExternalCheckedTDManager(paramNode);
int ds = tdManager.getDataSetCount();
if (ds > 0) {
firstDs = 0;
}
}
return firstDs;
}
/**
* @return Returns the execStack as unmodifiable List
*/
public List <ExecObject> getExecStackAsList() {
return Collections.unmodifiableList(new ArrayList<ExecObject>(
m_execStack));
}
/**
* @return The nodes of the current execution stack in a list
*/
public List<INodePO> getExecStackAsNodeList() {
List<INodePO> nodes = new ArrayList<INodePO>(m_execStack.size());
for (Iterator<ExecObject> it = m_execStack.iterator(); it.hasNext();) {
ExecObject execObject = it.next();
nodes.add(execObject.getExecNode());
}
return nodes;
}
/**
* @return Returns the root.
*/
public INodePO getRoot() {
return m_root;
}
/**
* register listener for stackModificationListener
* @param listener listener to register
*/
public void addExecStackModificationListener(
IExecStackModificationListener listener) {
if (!m_execListenerList.contains(listener)) {
m_execListenerList.add(listener);
}
}
/**
* remove listener from stackModificationListenerList
* @param listener listener to remove
*/
public void removeExecStackModificationListener(
IExecStackModificationListener listener) {
m_execListenerList.remove(listener);
}
/**
* register listener for stackModificationListener
* @param listener listener to register
*/
public void addEventStackModificationListener(
IEventStackModificationListener listener) {
if (!m_eventListenerList.contains(listener)) {
m_eventListenerList.add(listener);
}
}
/**
* remove listener from stackModificationListenerList
* @param listener listener to remove
*/
public void removeEventStackModificationListener(
IEventStackModificationListener listener) {
m_eventListenerList.remove(listener);
}
/**
* event for push-operation on execStack
* @param node node, which was added to stack
*/
private void fireExecStackIncremented(INodePO node)
throws IncompleteDataException {
addParameters(m_execStack.peek());
executeLogging();
Iterator<IExecStackModificationListener> it =
m_execListenerList.iterator();
while (it.hasNext()) {
IExecStackModificationListener l = it.next();
try {
l.stackIncremented(node);
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* event for pop-operation on execStack
*/
private void fireExecStackDecremented() {
executeLogging();
Iterator<IExecStackModificationListener> it =
m_execListenerList.iterator();
while (it.hasNext()) {
IExecStackModificationListener l = it.next();
try {
l.stackDecremented();
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* event for pop-operation on execStack
*/
private void fireInifiniteLoop() {
Iterator<IExecStackModificationListener> it =
m_execListenerList.iterator();
while (it.hasNext()) {
IExecStackModificationListener l = it.next();
try {
l.infiniteLoop();
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* event for push-operation on execStack
*/
private void fireEventStackIncremented() {
executeLogging();
Iterator<IEventStackModificationListener> it =
m_eventListenerList.iterator();
while (it.hasNext()) {
IEventStackModificationListener l = it.next();
try {
l.eventStackIncremented();
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* event for pop-operation on execStack
*/
private void fireEventStackDecremented() {
Iterator<IEventStackModificationListener> it =
m_eventListenerList.iterator();
while (it.hasNext()) {
IEventStackModificationListener l = it.next();
try {
l.eventStackDecremented();
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* event for next dataset iteration
*/
private void fireNextDataSetIteration() throws IncompleteDataException {
addParameters(m_execStack.peek());
Iterator<IExecStackModificationListener> it =
m_execListenerList.iterator();
while (it.hasNext()) {
IExecStackModificationListener l = it.next();
try {
l.nextDataSetIteration();
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* event for proceeding of next cap
*
* @param cap
* actual proceeded cap
*/
private void fireNextCap(ICapPO cap) {
Iterator<IExecStackModificationListener> it =
m_execListenerList.iterator();
while (it.hasNext()) {
IExecStackModificationListener l = it.next();
try {
l.nextCap(cap);
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* @return Returns the dataSetNumber.
*/
public int getDataSetNumber() {
int ds = 0;
if (!m_execStack.isEmpty()) {
ExecObject obj = m_execStack.peek();
ds = obj.getNumberDs();
}
return ds;
}
/**
* @param reentryProp reentryProperty
* @return next Cap to execute
* @throws JBException in case of incomplete testdata
*/
private ICapPO next(ReentryProperty reentryProp)
throws JBException {
if (LOG.isDebugEnabled()) {
LOG.debug("ReentryProperty: " + String.valueOf(reentryProp)); //$NON-NLS-1$
}
if (!m_execStack.isEmpty()) {
if (reentryProp.equals(ReentryProperty.CONTINUE)) {
popEventStack();
} else if (reentryProp.equals(ReentryProperty.REPEAT)) {
ExecObject obj = m_execStack.peek();
obj.decrementIndex();
popEventStack();
} else if (reentryProp.equals(ReentryProperty.BREAK)) {
for (int i = m_eventStack.size(); i > 0
&& !m_execStack.isEmpty(); i--) {
m_execStack.pop();
fireExecStackDecremented();
}
int pos = m_eventStack.peek().getStackPos();
popEventStack();
popEventStackNested(pos);
} else if (reentryProp.equals(ReentryProperty.RETURN)) {
int stackPos = m_eventStack.peek().getStackPos();
popEventStack();
stackPos = popEventStackNested(stackPos);
while (m_execStack.size() > stackPos) {
m_execStack.pop();
fireExecStackDecremented();
}
} else if (reentryProp.equals(ReentryProperty.EXIT)) {
for (int i = m_execStack.size() - 1; i >= 0; i--) {
m_execStack.pop();
fireExecStackDecremented();
}
for (int i = m_eventStack.size() - 1; i >= 0; i--) {
popEventStack();
}
} else if (reentryProp.equals(ReentryProperty.STOP)) {
TestExecution.getInstance().pauseExecution(PauseMode.PAUSE);
popEventStack();
} else if (reentryProp.equals(ReentryProperty.RETRY)) {
popEventStack();
return retryStep();
// default is EXIT
} else {
for (int i = m_execStack.size() - 1; i >= 0; i--) {
m_execStack.pop();
fireExecStackDecremented();
}
for (int i = m_eventStack.size() - 1; i >= 0; i--) {
popEventStack();
}
}
return next();
}
// end of testexecution
return null;
}
/**
* Repeatedly pops the event stack until either the event stack is empty
* or the top event on the stack has a lower execution stack position than
* the top event on the stack at the time this method was called.
* @param stackPos the stack position of the previous top element of the event stack
* @return the execution stack position of the event object at the top of
* the event stack at the time this method is called.
*/
private int popEventStackNested(int stackPos) {
// Remove all events that occurred higher on the execution stack
// than the currently processed event
while (!m_eventStack.isEmpty()
&& m_eventStack.peek().getStackPos() >= stackPos) {
popEventStack();
}
return stackPos;
}
/**
* Calls pop() on the event stack and fires a corresponding event.
*/
private void popEventStack() {
m_eventStack.pop();
fireEventStackDecremented();
}
/**
* @return the cap to be retried, or <code>null</code> if the cap cannot be
* determined. A return value of <code>null</code> indicates an
* error.
*/
@SuppressWarnings("unchecked")
private ICapPO retryStep() {
ExecObject execObj = m_execStack.peek();
// inform listeners that we are retrying the step
List<INodePO> nodeList =
IteratorUtils.toList(execObj.getExecNode().getNodeListIterator());
INodePO childNode = nodeList.get(execObj.getIndex());
if (childNode instanceof ICapPO) {
ICapPO cap = (ICapPO)childNode;
ExecStackMarker marker = new ExecStackMarker(m_execStack, cap);
if (m_markerToNumRetriesMap.containsKey(marker)) {
// increment number of retries
m_markerToNumRetriesMap.put(
marker, m_markerToNumRetriesMap.get(marker) + 1);
} else {
m_markerToNumRetriesMap.put(marker, 1);
}
fireRetryStep(cap);
return cap;
}
return null;
}
/**
* event for retrying a test step
*
* @param toRetry The step to retry.
*/
private void fireRetryStep(ICapPO toRetry) {
Iterator<IExecStackModificationListener> it =
m_execListenerList.iterator();
while (it.hasNext()) {
IExecStackModificationListener l = it.next();
try {
l.retryCap(toRetry);
} catch (Throwable t) {
LOG.error(Messages.ErrorWhileNotifyingListeners, t);
}
}
}
/**
* determines, if node of type EventExecTestCase
* @param node node to validate
* @return result of validation
*/
private boolean isEventHandler(INodePO node) {
return node instanceof IEventExecTestCasePO;
}
/**
*
* @param eventType The event type to be handled.
* @return the reentry property of the event handler for the given
* event type.
*/
public ReentryProperty getEventHandlerReentry(String eventType) {
return getEventObject(eventType, false)
.getEventExecTc().getReentryProp();
}
/**
* @param eventType eventType of eventhandler, which is to execute in next
* step
* @return next cap to execute
* @throws JBException in case of incomplete testdata
*/
public ICapPO next(String eventType)
throws JBException {
ExecObject execObj = m_execStack.peek();
EventObject eventObj = getEventObject(eventType, true);
IEventExecTestCasePO eventExecTC = eventObj.getEventExecTc();
if (eventExecTC.getReentryProp().equals(ReentryProperty.CONDITION)) {
// failed Condition - we need something similar to a RETURN reentry
// the event stack does not contain the event handler!
// the exec stack ends with the Condition container and its CondStruct parent
int stackPos = popEventStackNested(eventObj.getStackPos());
while (m_execStack.size() > stackPos + 1) {
m_execStack.pop();
fireExecStackDecremented();
}
ExecObject top = m_execStack.peek();
if (top.getExecNode() instanceof IConditionalStatementPO) {
// The CondStruct is an If / Then / Else
IConditionalStatementPO cond = (IConditionalStatementPO)
top.getExecNode();
// This is the time to insert the Else or Then branch
INodePO node = cond.isNegate() ? cond.getThenBranch()
: cond.getElseBranch();
m_execStack.push(new ExecObject(node, NO_DATASET));
fireExecStackIncremented(node);
} else if (top.getExecNode() instanceof ICondStructPO) {
// not very nice, but a CondStruct which is not an If
// is a Do-While or While-Do
ICondStructPO cond = (ICondStructPO) top.getExecNode();
if (cond.isNegate()) {
// failed, so we continue the iteration
INodePO node = cond.getDoBranch();
m_execStack.push(new ExecObject(node, NO_DATASET));
fireExecStackIncremented(node);
} else {
// failed, normal, so we stop the iteration
m_execStack.pop();
fireExecStackDecremented();
}
}
return next();
}
int dataSetIndex = 0;
final ITDManager mgr = eventExecTC.getDataManager();
if (mgr.getDataSetCount() > 0) {
IDataSetPO row = mgr.getDataSet(0);
for (int col = 0; col < row.getColumnCount(); col++) {
String td = row.getValueAt(col);
String uniqueId = mgr.getUniqueIds().get(col);
IParamDescriptionPO desc =
eventExecTC.getParameterForUniqueId(uniqueId);
// if EH uses params of parent, start at the iteration which failed!
ParamValueConverter conv = new ModelParamValueConverter(
td, eventExecTC, desc);
if (conv.containsReferences()) {
dataSetIndex = execObj.getNumberDs();
break;
}
}
}
m_execStack.push(new ExecObject(eventExecTC, dataSetIndex));
m_eventStack.push(eventObj);
fireEventStackIncremented();
fireExecStackIncremented(eventExecTC);
return next();
}
/**
* find the next eventHandler for given eventType
*
* @param eventType
* eventType for eventHandler to find
* @param resetRetryCount
* if this is set to <code>true</code> the retry count of the
* EventHandler will be reset, if the EventHandler was
* unsuccessful and has reached his max retry count.
* This is necessary for http://eclip.se/347275
* @return the next eventHandler for given eventType
*/
@SuppressWarnings("unchecked")
private EventObject getEventObject(String eventType,
boolean resetRetryCount) {
List<INodePO> nodeList = IteratorUtils.toList(
m_execStack.peek().getExecNode().getNodeListIterator());
ICapPO cap = (ICapPO)nodeList.get(m_execStack.peek().getIndex());
ExecStackMarker marker = new ExecStackMarker(m_execStack, cap);
EventObject eventObj = null;
int startIndex = m_execStack.size() - 1;
for (int i = startIndex; i > 0 && i < m_execStack.size(); --i) {
ExecObject obj = m_execStack.get(i);
if (eventType.equals(TestErrorEvent.ID.VERIFY_FAILED)
&& obj.getExecNode() instanceof IAbstractContainerPO) {
eventObj = handleContainer((IAbstractContainerPO) obj.
getExecNode(), i);
if (eventObj != null) {
return eventObj;
}
}
if (!(obj.getExecNode() instanceof IExecTestCasePO)) {
continue;
}
IExecTestCasePO execTc = (IExecTestCasePO)obj.getExecNode();
IEventExecTestCasePO eventExecTc = execTc.getEventExecTC(eventType);
if (!isHandlingError(i) && eventExecTc != null) {
if (!(eventExecTc.getReentryProp().equals(ReentryProperty.RETRY)
&& m_markerToNumRetriesMap.containsKey(marker)
&& m_markerToNumRetriesMap.get(marker)
>= eventExecTc.getMaxRetries())) {
eventObj = new EventObject(eventExecTc, i);
break;
}
eventExecTc = null;
}
}
// nothing found --> call defaultEventHandler
if (eventObj == null) {
// reset the marker of the EventHandler
if (m_markerToNumRetriesMap.containsKey(marker)
&& resetRetryCount) {
m_markerToNumRetriesMap.put(marker, 0);
}
IEventExecTestCasePO eventExecTc =
DefaultEventHandler.getDefaultEventHandler(eventType, m_root);
Validate.notNull(eventExecTc,
Messages.MissingDefaultEventHandlerForEventType + eventType
+ StringConstants.DOT);
eventObj = new EventObject(eventExecTc, 0);
}
return eventObj;
}
/**
* Handles container nodes for events
* @param cont the container
* @param i the stack position of the container
* @return the EventObject or null if the container doesn't care about the event
*/
private EventObject handleContainer(IAbstractContainerPO cont, int i) {
INodePO par = cont.getParentNode();
if (par instanceof ICondStructPO && cont.equals(
((ICondStructPO) par).getCondition())) {
// this Condition fails, consuming the error event
// the 'real' handler node is the parent, that's why i - 1
return new EventObject(NodeMaker.COND_EVENT_EXECTC, i - 1);
}
return null;
}
/**
* Tells whether the test case at the given index in the execution stack
* is currently handling an error. A test case is defined as "currently
* handling an error" if any event handler for the test case is currently
* on the execution stack.
*
* @param execStackIndex The index at which the test case to check can be
* found in the execution stack.
* @return <code>true</code> if the test case at the specified index in the
* execution stack is currently handling an error. Otherwise
* <code>false</code>.
*/
private boolean isHandlingError(int execStackIndex) {
for (EventObject event : m_eventStack) {
if (event.getStackPos() == execStackIndex) {
return true;
}
}
return false;
}
/**
*
* class to manage information about executed eventHandler
* @author BREDEX GmbH
* @created 29.04.2005
*/
private static class EventObject {
/** <code>m_eventExec</code> the EventExecTestCasePO */
private IEventExecTestCasePO m_eventExec;
/** <code>m_stackPos</code> place of discovery of eventExecTestCase in execStack */
private int m_stackPos;
/**
* @param eventExec the event exec PO
* @param stackPos place of discovery of eventExecTestCase in execStack
*/
private EventObject(IEventExecTestCasePO eventExec, int stackPos) {
m_eventExec = eventExec;
m_stackPos = stackPos;
}
/**
* Returns the node as an EventExecTC
* @return Returns the eventExecTc.
*/
public IEventExecTestCasePO getEventExecTc() {
return m_eventExec;
}
/**
* @return Returns the stackPos.
*/
public int getStackPos() {
return m_stackPos;
}
}
/**
*
* @return the result value to use when the most recently executed cap was
* successful. This may depend on previous events within the test,
* such as whether the step has been retried.
*/
public int getSuccessResult() {
INodePO currentNode = getCurrentNode();
if (currentNode instanceof ICapPO) {
ExecStackMarker marker =
new ExecStackMarker(m_execStack, (ICapPO)currentNode);
if (m_markerToNumRetriesMap.containsKey(marker)) {
m_markerToNumRetriesMap.put(marker, 0);
return TestResultNode.SUCCESS_RETRY;
}
}
return TestResultNode.SUCCESS;
}
/**
*
* @return the currently executing node.
*/
@SuppressWarnings("unchecked")
private INodePO getCurrentNode() {
List<INodePO> nodeList = IteratorUtils.toList(
m_execStack.peek().getExecNode().getNodeListIterator());
return nodeList.get(m_execStack.peek().getIndex());
}
/**
* Adds parameter values to the given execution object. If
* <code>execObject</code> already has parameters assigned, this method
* may overwrite them.
*
* @param execObject The execution object to which the parameters will
* be added.
*/
private void addParameters(ExecObject execObject)
throws IncompleteDataException {
INodePO execNode = execObject.getExecNode();
if (execNode instanceof IParamNodePO) {
IParamNodePO paramNode = (IParamNodePO)execNode;
List<IParamDescriptionPO> parameterList =
paramNode.getParameterList();
String value = null;
for (IParamDescriptionPO desc : parameterList) {
int column = 0;
String descriptionId = desc.getUniqueId();
ITDManager tdManager = null;
try {
tdManager =
m_externalTestDataBP.getExternalCheckedTDManager(
paramNode);
} catch (JBException e) {
LOG.error(
Messages.TestDataNotAvailable + StringConstants.DOT, e);
}
TestExecution te = TestExecution.getInstance();
List <ExecObject> stackList =
new ArrayList<ExecObject>(getExecStackAsList());
int dataSetIndex = getDataSetNumber();
// Special handling for Test Case References that are repeated
// via Data Set. The test data manager for such References only has
// information about a single Data Set, so we need to ignore the
// actual current Data Set number.
if (tdManager.getDataSetCount() <= 1) {
dataSetIndex = 0;
}
// Special handling for Test Steps. Their test data manager has
// information about multiple Data Sets, but we are only interested
// in the first one.
if (paramNode instanceof ICapPO) {
dataSetIndex = 0;
}
if (tdManager.findColumnForParam(desc.getUniqueId()) == -1) {
IParameterInterfacePO referencedDataCube = paramNode
.getReferencedDataCube();
if (referencedDataCube != null) {
desc = referencedDataCube.getParameterForName(desc
.getName());
}
}
String date =
getDataForExec(execNode, desc, tdManager, dataSetIndex);
if (StringUtils.isBlank(date)) {
throw new IncompleteDataException(
NLS.bind(Messages.MissingTestData,
execNode.getName()),
MessageIDs.E_MISSING_DATA);
}
ParamValueConverter conv = new ModelParamValueConverter(
date, paramNode, desc);
try {
value = conv.getExecutionString(stackList);
} catch (InvalidDataException e) {
LOG.info(e.getMessage());
value = MessageIDs.getMessageObject(e.getErrorId()).
getMessage(new String[] {e.getLocalizedMessage()});
}
// It's important to use 'descriptionId' here instead of
// 'desc.getUniqueId()', as 'desc' may have changed between
// its definition and here.
execObject.addParameter(descriptionId,
StringUtils.defaultString(value));
column++;
}
}
}
/**
* This method is also searching for default values if in the Exec there is only one CAP
* @param node if this is a {@link IExecTestCasePO} we are also searching if
* there are default values
* @param desc the {@link IParamDescriptionPO} to get the correct value for
* the parameter
* @param tdManager the data manger
* @param dataSetIndex the index for the data set
* @return the value (also default value) for the node
*/
private String getDataForExec(INodePO node, IParamDescriptionPO desc,
ITDManager tdManager, int dataSetIndex) {
String data = StringConstants.EMPTY;
boolean cellNotFound = false;
try {
data = tdManager.getCell(dataSetIndex, desc);
} catch (IndexOutOfBoundsException e) {
cellNotFound = true;
// ignored
}
if (cellNotFound && StringUtils.isBlank(data)
&& node instanceof IExecTestCasePO) {
INodePO specNode = ((IExecTestCasePO) node).getSpecTestCase();
if (specNode instanceof ISpecTestCasePO) {
data = AbstractParamInterfaceBP
.getValueForSpecNodeWithParamDesc(desc,
(ISpecTestCasePO) specNode);
}
}
return data;
}
/**
* Sets the building flag
* @param build the flag
*/
public void setBuilding(boolean build) {
m_building = build;
}
/**
* @param iterMax the maximum iterate count
*/
public void setIterMax(int iterMax) {
m_iterMax = iterMax;
}
}