/*******************************************************************************
* Copyright (c) 2016 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.businessprocess.compcheck;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jubula.client.core.businessprocess.CompNameManager;
import org.eclipse.jubula.client.core.businessprocess.problems.IProblem;
import org.eclipse.jubula.client.core.businessprocess.problems.ProblemFactory;
import org.eclipse.jubula.client.core.model.IAUTMainPO;
import org.eclipse.jubula.client.core.model.ICapPO;
import org.eclipse.jubula.client.core.model.ICompNamesPairPO;
import org.eclipse.jubula.client.core.model.IConditionalStatementPO;
import org.eclipse.jubula.client.core.model.IExecTestCasePO;
import org.eclipse.jubula.client.core.model.INodePO;
import org.eclipse.jubula.client.core.model.ISpecTestCasePO;
import org.eclipse.jubula.client.core.model.ITestSuitePO;
import org.eclipse.jubula.client.core.model.LogicComponentNotManagedException;
import org.eclipse.jubula.tools.internal.objects.IComponentIdentifier;
import org.eclipse.jubula.tools.internal.xml.businessmodell.Component;
import org.eclipse.jubula.tools.internal.xml.businessmodell.ConcreteComponent;
/**
* Class responsible for collecting completeness information for AUTs
* @author BREDEX GmbH
* @created Aug 18, 2016
*/
public class CompCheck {
/** Map Node Id => Set of Component Name guids which must be mapped in order to use the node */
private Map<Long, Set<String>> m_mustMap;
/** The original Test Suites */
private List<ITestSuitePO> m_suites;
/** The current AUT for Problem Collecting */
private IAUTMainPO m_aut;
/** A unique problem for the current AUT */
private IProblem m_autProblem;
/** The ID => unique problem for the AUTs */
private Map<Long, IProblem> m_autProblems;
/**
* Constructor
* @param suites the suites
**/
public CompCheck(List<ITestSuitePO> suites) {
m_mustMap = new HashMap<>();
m_suites = suites;
}
/**
* Collects the Component Name usage information for a list of Test Suites
*/
public void traverse() {
long start = System.currentTimeMillis();
IAUTMainPO aut;
IComponentIdentifier id;
Set<String> problems;
for (ITestSuitePO ts : m_suites) {
aut = ts.getAut();
if (aut != null) {
traverseImpl(ts);
problems = getProblematicGuids(ts);
}
}
}
/**
* The traverse implementation, bottom - up
* @param node the current node, can be ITestSuitePO or IExecTestCasePO
*/
private void traverseImpl(INodePO node) {
// first we collect all used Component Names for all children of a node
// and after this we calculate the usage for the node's SpecTC (not the node itself!)
INodePO next;
for (Iterator<INodePO> it = node.getAllNodeIter(); it.hasNext();) {
next = it.next();
// if m_mustMap contains the Id, that means we have already traversed the child
if (getId(next) != null && !m_mustMap.containsKey(getId(next))
&& !(next instanceof ICapPO)) {
// if we have not yet dealt with the node (in case of an ExecTC the corresponding SpecTC!)
// then we deal with it
traverseImpl(next);
}
}
// Finished with all children (Node and Event), next step is to fill the node's guid set
Set<String> nodeGuids = new HashSet<>();
Long id = getId(node);
String guid;
for (Iterator<INodePO> it = node.getAllNodeIter(); it.hasNext();) {
handleNext(nodeGuids, it.next());
}
m_mustMap.put(id, nodeGuids);
}
/**
* Puts the used guids to the given set
* @param nodeGuids the set of used Guids
* @param child the child of the SpecTC
*/
private void handleNext(Set<String> nodeGuids, INodePO child) {
if (!child.isActive()) {
return;
}
String guid;
if (child instanceof IExecTestCasePO) {
handleExecTestCasePO(nodeGuids, (IExecTestCasePO) child);
} else if (child instanceof ICapPO && isRelevant((ICapPO) child)) {
guid = ((ICapPO) child).getComponentName();
if (guid != null) {
nodeGuids.add(CompNameManager.getInstance().resolveGuid(guid));
}
} else if (child instanceof IConditionalStatementPO) {
for (Iterator<INodePO> it = child.getAllNodeIter(); it.hasNext();) {
handleNext(nodeGuids, it.next());
}
}
}
/**
* Handles an ExecTestCase child: we have to alter the Comp Names by the child's Comp Names Pairs
* @param guids the guids
* @param child the child
*/
private void handleExecTestCasePO(Set<String> guids,
IExecTestCasePO child) {
// We designed the traverse such that the corresponding SpecTC must have been traversed before
ISpecTestCasePO childSpecTC = child.getSpecTestCase();
if (childSpecTC != null) {
Set<String> childSpecGuids = m_mustMap.get(childSpecTC.getId());
ICompNamesPairPO pair;
for (String guid : childSpecGuids) {
pair = child.getCompNamesPair(guid);
if (pair != null) {
guids.add(CompNameManager.getInstance().
resolveGuid(pair.getSecondName()));
} else {
guids.add(guid);
}
}
}
}
/**
* Returns the Id of the SpecTC for ExecTCs and the normal id for other nodes
* @param node the node
* @return the id or null if
*/
private Long getId(INodePO node) {
if (node instanceof IExecTestCasePO) {
ISpecTestCasePO specTC = ((IExecTestCasePO) node).getSpecTestCase();
if (specTC != null) {
return specTC.getId();
}
return null;
}
return node.getId();
}
/**
* Adds Problem markers to Nodes: exactly those paths are marked
* which start in a problematic TS and end at the last node where there
* is still a chance to correct the map by changing a corresponding CNPair
*/
public void addProblems() {
m_autProblems = new HashMap<>();
for (ITestSuitePO ts : m_suites) {
m_aut = ts.getAut();
if (m_aut != null) {
m_autProblem = m_autProblems.get(m_aut.getId());
if (m_autProblem == null) {
m_autProblem = ProblemFactory.
createIncompleteObjectMappingProblem(m_aut);
m_autProblems.put(m_aut.getId(), m_autProblem);
}
Set<String> problems = getProblematicGuids(ts);
if (!problems.isEmpty()) {
addProblemsImpl(ts, problems);
}
}
}
}
/**
* The recursive method adding the problems to the nodes
* @param node the starting node
* @param problemGuids the problematic guids at this node
* - these are not mapped, should be, and still has a chance to be corrected
*/
private void addProblemsImpl(INodePO node, Set<String> problemGuids) {
// ExecTestCasePOs are marked problematic even if the problemGuids is empty
if (problemGuids.size() == 0 || !node.isActive()) {
return;
}
node.addProblem(m_autProblem);
INodePO child;
for (Iterator<INodePO> it = node.getAllNodeIter(); it.hasNext();) {
// adding problematic guids to Node children
child = it.next();
addProblemsImpl(child, problemHandleChild(child, problemGuids));
}
}
/**
* Calculates the problematic guids for a node
* @param child the node
* @param guids the problematic guids of the parent
* (these are not mapped by the AUT at the top of the current tree path)
* @return the Set of problematic guids for the Node
*/
private Set<String> problemHandleChild(INodePO child, Set<String> guids) {
Set<String> result = new HashSet<String>(guids.size());
if (child instanceof ICapPO && isRelevant((ICapPO) child)) {
String guid = CompNameManager.getInstance().
resolveGuid(((ICapPO) child).getComponentName());
if (guids.contains(guid)) {
result.add(guid);
}
} else if (child instanceof IExecTestCasePO) {
problemHandleExecTC((IExecTestCasePO) child, guids, result);
} else if (child instanceof IConditionalStatementPO) {
for (Iterator<INodePO> iterator = child.getAllNodeIter(); iterator
.hasNext();) {
INodePO node = iterator.next();
if (node instanceof IExecTestCasePO) {
problemHandleExecTC((IExecTestCasePO) node, guids, result);
}
}
}
return result;
}
/**
* Collecting the problematic guids for an ExecTC
* @param child the ExecTC
* @param problemGuids the problematic guids of the parent
* (these are not mapped by the AUT at the top of the current tree path)
* @param result the problematic guids for the ExecTC
* these are guids that are mapped to the problemGuids Set by the CompName
* pairs of the ExecTC
*/
private void problemHandleExecTC(IExecTestCasePO child,
Set<String> problemGuids, Set<String> result) {
ISpecTestCasePO spec = child.getSpecTestCase();
if (spec == null) {
return;
}
for (String guid : m_mustMap.get(spec.getId())) {
ICompNamesPairPO pair = child.getCompNamesPair(guid);
if (pair == null
&& problemGuids.contains(guid)
&& !specTCHasAutoGenPair(spec, guid)) {
result.add(guid);
} else if (pair != null
&& problemGuids.contains(pair.getSecondName())) {
child.addProblem(m_autProblem);
}
}
}
/**
* Decides whether the SpecTestCasePO has an auto-generated Comp Names Pair for the guid
* @param spec the SpecTestCasePO
* @param guid the guid
* @return whether there is a Comp Names Pair
*/
private boolean specTCHasAutoGenPair(ISpecTestCasePO spec, String guid) {
for (Iterator<INodePO> it = spec.getAllNodeIter(); it.hasNext(); ) {
INodePO node = it.next();
if (node instanceof ICapPO) {
String cNGuid = ((ICapPO) node).getComponentName();
if (cNGuid != null && cNGuid.equals(guid)) {
return true;
}
} else if (node instanceof IExecTestCasePO) {
for (ICompNamesPairPO pair : ((IExecTestCasePO) node)
.getCompNamesPairs()) {
if (pair.isPropagated() && guid.equals(
pair.getSecondName())) {
return true;
}
}
}
}
return false;
}
/**
* Collecting those guids of a TS which should be mapped, but aren't
* @param ts the TS
* @return the set of unmapped guids
*/
private Set<String> getProblematicGuids(ITestSuitePO ts) {
Set<String> problemGuids = new HashSet<>();
IComponentIdentifier id;
IAUTMainPO aut = ts.getAut();
for (String guid : m_mustMap.get(ts.getId())) {
id = null;
try {
id = aut.getObjMap().getTechnicalName(guid);
} catch (LogicComponentNotManagedException e) {
// Nothing
}
if (id == null) {
problemGuids.add(guid);
}
}
return problemGuids;
}
/**
* Decides if the Component Name of a CAP needs mapping
* @param cap the cap
* @return the result
*/
private boolean isRelevant(ICapPO cap) {
Component metaComponentType = cap.getMetaComponentType();
if (metaComponentType instanceof ConcreteComponent
&& ((ConcreteComponent) metaComponentType)
.hasDefaultMapping()) {
return false;
}
return true;
}
/**
* Returns the Suit ID => Set of guids of Component Names to map Map
* @return the Map
*/
public Map<Long, Set<String>> getCompNamesToMap() {
Map<Long, Set<String>> result = new HashMap<>(m_suites.size());
for (ITestSuitePO ts : m_suites) {
if (ts.getAut() != null) {
result.put(ts.getId(), m_mustMap.get(ts.getId()));
}
}
return result;
}
}