package org.camunda.bpm.extension.process_test_coverage.listeners; import java.util.logging.Logger; import org.camunda.bpm.engine.RepositoryService; import org.camunda.bpm.engine.delegate.DelegateExecution; import org.camunda.bpm.engine.delegate.ExecutionListener; import org.camunda.bpm.engine.repository.ProcessDefinition; import org.camunda.bpm.extension.process_test_coverage.junit.rules.CoverageTestRunState; import org.camunda.bpm.extension.process_test_coverage.model.CoveredFlowNode; import org.camunda.bpm.extension.process_test_coverage.model.CoveredSequenceFlow; import org.camunda.bpm.model.bpmn.BpmnModelInstance; import org.camunda.bpm.model.bpmn.instance.FlowNode; import org.camunda.bpm.model.bpmn.instance.IntermediateThrowEvent; import org.camunda.bpm.model.bpmn.instance.SequenceFlow; import org.camunda.bpm.model.xml.instance.ModelElementInstance; /** * Listener taking note of covered sequence flows. * * @author grossax * @author z0rbas * */ public class PathCoverageExecutionListener implements ExecutionListener { private Logger logger = Logger.getLogger(this.getClass().getCanonicalName()); /** * The state of the currently running coverage test. */ private CoverageTestRunState coverageTestRunState; public PathCoverageExecutionListener(CoverageTestRunState coverageTestRunState) { this.coverageTestRunState = coverageTestRunState; } @Override public void notify(DelegateExecution execution) throws Exception { if (coverageTestRunState == null) { logger.warning("Coverage execution listener in use but no coverage run state assigned!"); return; } final RepositoryService repositoryService = execution.getProcessEngineServices().getRepositoryService(); // Get the process definition in order to obtain the key final ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId( execution.getProcessDefinitionId()).singleResult(); final String transitionId = execution.getCurrentTransitionId(); // Record sequence flow coverage final CoveredSequenceFlow coveredSequenceFlow = new CoveredSequenceFlow(processDefinition.getKey(), transitionId); coverageTestRunState.addCoveredElement(coveredSequenceFlow); // Record possible event coverage handleEvent(transitionId, processDefinition, repositoryService); } /** * Events aren't reported like SequenceFlows and Activities, so we need * special handling. If a sequence flow has an event as the source or the * target, we add it to the coverage. It's pretty straight forward if a * sequence flow is active, then it's source has been covered anyway and it * will most definitely arrive at its target. * * @param transitionId * @param processDefinition * @param repositoryService */ private void handleEvent(String transitionId, ProcessDefinition processDefinition, RepositoryService repositoryService) { final BpmnModelInstance modelInstance = repositoryService.getBpmnModelInstance(processDefinition.getId()); final ModelElementInstance modelElement = modelInstance.getModelElementById(transitionId); if (modelElement.getElementType().getInstanceType() == SequenceFlow.class) { final SequenceFlow sequenceFlow = (SequenceFlow) modelElement; // If there is an event at the sequence flow source add it to the // coverage final FlowNode source = sequenceFlow.getSource(); addEventToCoverage(processDefinition, source); // If there is an event at the sequence flow target add it to the // coverage final FlowNode target = sequenceFlow.getTarget(); addEventToCoverage(processDefinition, target); } } private void addEventToCoverage(ProcessDefinition processDefinition, FlowNode node) { if (node instanceof IntermediateThrowEvent) { final CoveredFlowNode coveredElement = new CoveredFlowNode(processDefinition.getKey(), node.getId()); // We consider entered throw elements as also ended coveredElement.setEnded(true); coverageTestRunState.addCoveredElement(coveredElement); } } }