/* 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. */ package org.activiti.standalone.validation; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import org.activiti.bpmn.converter.BpmnXMLConverter; import org.activiti.bpmn.model.BpmnModel; import org.activiti.engine.test.util.TestProcessUtil; import org.activiti.validation.ProcessValidator; import org.activiti.validation.ProcessValidatorFactory; import org.activiti.validation.ValidationError; import org.activiti.validation.validator.Problems; import org.activiti.validation.validator.ValidatorSetNames; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * @author jbarrez */ public class DefaultProcessValidatorTest { protected ProcessValidator processValidator; @Before public void setupProcessValidator() { ProcessValidatorFactory processValidatorFactory = new ProcessValidatorFactory(); this.processValidator = processValidatorFactory.createDefaultProcessValidator(); } @Test public void verifyValidation() throws Exception { InputStream xmlStream = this.getClass().getClassLoader().getResourceAsStream("org/activiti/engine/test/validation/invalidProcess.bpmn20.xml"); XMLInputFactory xif = XMLInputFactory.newInstance(); InputStreamReader in = new InputStreamReader(xmlStream, "UTF-8"); XMLStreamReader xtr = xif.createXMLStreamReader(in); BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr); Assert.assertNotNull(bpmnModel); List<ValidationError> allErrors = processValidator.validate(bpmnModel); Assert.assertEquals(65, allErrors.size()); String setName = ValidatorSetNames.ACTIVITI_EXECUTABLE_PROCESS; // shortening it a bit // isExecutable should be true List<ValidationError> problems = findErrors(allErrors, setName, Problems.ALL_PROCESS_DEFINITIONS_NOT_EXECUTABLE, 1); Assert.assertNotNull(problems.get(0).getValidatorSetName()); Assert.assertNotNull(problems.get(0).getProblem()); Assert.assertNotNull(problems.get(0).getDefaultDescription()); // Event listeners problems = findErrors(allErrors, setName, Problems.EVENT_LISTENER_IMPLEMENTATION_MISSING, 1); assertProcessElementError(problems.get(0)); problems = findErrors(allErrors, setName, Problems.EVENT_LISTENER_INVALID_THROW_EVENT_TYPE, 1); assertProcessElementError(problems.get(0)); // Execution listeners problems = findErrors(allErrors, setName, Problems.EXECUTION_LISTENER_IMPLEMENTATION_MISSING, 2); assertProcessElementError(problems.get(0)); assertCommonProblemFieldForActivity(problems.get(1)); // Association problems = findErrors(allErrors, setName, Problems.ASSOCIATION_INVALID_SOURCE_REFERENCE, 1); assertProcessElementError(problems.get(0)); problems = findErrors(allErrors, setName, Problems.ASSOCIATION_INVALID_TARGET_REFERENCE, 1); assertProcessElementError(problems.get(0)); // Signals problems = findErrors(allErrors, setName, Problems.SIGNAL_MISSING_ID, 1); assertCommonErrorFields(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SIGNAL_MISSING_NAME, 2); assertCommonErrorFields(problems.get(0)); assertCommonErrorFields(problems.get(1)); problems = findErrors(allErrors, setName, Problems.SIGNAL_DUPLICATE_NAME, 2); assertCommonErrorFields(problems.get(0)); assertCommonErrorFields(problems.get(1)); problems = findErrors(allErrors, setName, Problems.SIGNAL_INVALID_SCOPE, 1); assertCommonErrorFields(problems.get(0)); // Start event problems = findErrors(allErrors, setName, Problems.START_EVENT_MULTIPLE_FOUND, 2); assertCommonProblemFieldForActivity(problems.get(0)); assertCommonProblemFieldForActivity(problems.get(1)); problems = findErrors(allErrors, setName, Problems.START_EVENT_INVALID_EVENT_DEFINITION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Sequence flow problems = findErrors(allErrors, setName, Problems.SEQ_FLOW_INVALID_SRC, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SEQ_FLOW_INVALID_TARGET, 2); assertCommonProblemFieldForActivity(problems.get(0)); // User task problems = findErrors(allErrors, setName, Problems.USER_TASK_LISTENER_IMPLEMENTATION_MISSING, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Service task problems = findErrors(allErrors, setName, Problems.SERVICE_TASK_RESULT_VAR_NAME_WITH_DELEGATE, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SERVICE_TASK_INVALID_TYPE, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SERVICE_TASK_MISSING_IMPLEMENTATION, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SERVICE_TASK_WEBSERVICE_INVALID_OPERATION_REF, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Send task problems = findErrors(allErrors, setName, Problems.SEND_TASK_INVALID_IMPLEMENTATION, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SEND_TASK_INVALID_TYPE, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SEND_TASK_WEBSERVICE_INVALID_OPERATION_REF, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Mail task problems = findErrors(allErrors, setName, Problems.MAIL_TASK_NO_RECIPIENT, 2); assertCommonProblemFieldForActivity(problems.get(0)); assertCommonProblemFieldForActivity(problems.get(1)); problems = findErrors(allErrors, setName, Problems.MAIL_TASK_NO_CONTENT, 4); assertCommonProblemFieldForActivity(problems.get(0)); assertCommonProblemFieldForActivity(problems.get(1)); // Shell task problems = findErrors(allErrors, setName, Problems.SHELL_TASK_NO_COMMAND, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Script task problems = findErrors(allErrors, setName, Problems.SCRIPT_TASK_MISSING_SCRIPT, 2); assertCommonProblemFieldForActivity(problems.get(0)); assertCommonProblemFieldForActivity(problems.get(1)); // Exclusive gateway problems = findErrors(allErrors, setName, Problems.EXCLUSIVE_GATEWAY_CONDITION_NOT_ALLOWED_ON_SINGLE_SEQ_FLOW, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.EXCLUSIVE_GATEWAY_CONDITION_ON_DEFAULT_SEQ_FLOW, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.EXCLUSIVE_GATEWAY_NO_OUTGOING_SEQ_FLOW, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.EXCLUSIVE_GATEWAY_SEQ_FLOW_WITHOUT_CONDITIONS, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Event gateway problems = findErrors(allErrors, setName, Problems.EVENT_GATEWAY_ONLY_CONNECTED_TO_INTERMEDIATE_EVENTS, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Subprocesses problems = findErrors(allErrors, setName, Problems.SUBPROCESS_MULTIPLE_START_EVENTS, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SUBPROCESS_START_EVENT_EVENT_DEFINITION_NOT_ALLOWED, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Event subprocesses problems = findErrors(allErrors, setName, Problems.EVENT_SUBPROCESS_INVALID_START_EVENT_DEFINITION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Boundary events problems = findErrors(allErrors, setName, Problems.BOUNDARY_EVENT_NO_EVENT_DEFINITION, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.BOUNDARY_EVENT_CANCEL_ONLY_ON_TRANSACTION, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.BOUNDARY_EVENT_MULTIPLE_CANCEL_ON_TRANSACTION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Intermediate catch event problems = findErrors(allErrors, setName, Problems.INTERMEDIATE_CATCH_EVENT_NO_EVENTDEFINITION, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.INTERMEDIATE_CATCH_EVENT_INVALID_EVENTDEFINITION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Intermediate throw event problems = findErrors(allErrors, setName, Problems.THROW_EVENT_INVALID_EVENTDEFINITION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Multi instance problems = findErrors(allErrors, setName, Problems.MULTI_INSTANCE_MISSING_COLLECTION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Message events problems = findErrors(allErrors, setName, Problems.MESSAGE_EVENT_MISSING_MESSAGE_REF, 2); assertCommonProblemFieldForActivity(problems.get(0)); assertCommonProblemFieldForActivity(problems.get(1)); // Signal events problems = findErrors(allErrors, setName, Problems.SIGNAL_EVENT_MISSING_SIGNAL_REF, 1); assertCommonProblemFieldForActivity(problems.get(0)); problems = findErrors(allErrors, setName, Problems.SIGNAL_EVENT_INVALID_SIGNAL_REF, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Compensate event problems = findErrors(allErrors, setName, Problems.COMPENSATE_EVENT_INVALID_ACTIVITY_REF, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Timer event problems = findErrors(allErrors, setName, Problems.EVENT_TIMER_MISSING_CONFIGURATION, 2); assertCommonProblemFieldForActivity(problems.get(0)); // Data association problems = findErrors(allErrors, setName, Problems.DATA_ASSOCIATION_MISSING_TARGETREF, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Data object problems = findErrors(allErrors, setName, Problems.DATA_OBJECT_MISSING_NAME, 2); assertCommonErrorFields(problems.get(0)); assertCommonErrorFields(problems.get(1)); // End event problems = findErrors(allErrors, setName, Problems.END_EVENT_CANCEL_ONLY_INSIDE_TRANSACTION, 1); assertCommonProblemFieldForActivity(problems.get(0)); // Messages problems = findErrors(allErrors, setName, Problems.MESSAGE_INVALID_ITEM_REF, 1); assertCommonErrorFields(problems.get(0)); } @Test public void testWarningError() throws UnsupportedEncodingException, XMLStreamException { String flowWithoutConditionNoDefaultFlow = "<?xml version='1.0' encoding='UTF-8'?>" + "<definitions id='definitions' xmlns='http://www.omg.org/spec/BPMN/20100524/MODEL' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:activiti='http://activiti.org/bpmn' targetNamespace='Examples'>" + " <process id='exclusiveGwDefaultSequenceFlow'> " + " <startEvent id='theStart' /> " + " <sequenceFlow id='flow1' sourceRef='theStart' targetRef='exclusiveGw' /> " + " <exclusiveGateway id='exclusiveGw' name='Exclusive Gateway' /> " + // no default = "flow3" !! " <sequenceFlow id='flow2' sourceRef='exclusiveGw' targetRef='theTask1'> " + " <conditionExpression xsi:type='tFormalExpression'>${input == 1}</conditionExpression> " + " </sequenceFlow> " + " <sequenceFlow id='flow3' sourceRef='exclusiveGw' targetRef='theTask2'/> " + // one would be OK " <sequenceFlow id='flow4' sourceRef='exclusiveGw' targetRef='theTask2'/> " + // but two unconditional not! " <userTask id='theTask1' name='Input is one' /> " + " <userTask id='theTask2' name='Default input' /> " + " </process>" + "</definitions>"; XMLInputFactory xif = XMLInputFactory.newInstance(); InputStreamReader in = new InputStreamReader(new ByteArrayInputStream(flowWithoutConditionNoDefaultFlow.getBytes()), "UTF-8"); XMLStreamReader xtr = xif.createXMLStreamReader(in); BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr); Assert.assertNotNull(bpmnModel); List<ValidationError> allErrors = processValidator.validate(bpmnModel); Assert.assertEquals(1, allErrors.size()); Assert.assertTrue(allErrors.get(0).isWarning()); } /* * Test for https://activiti.atlassian.net/browse/ACT-2071: * * If all processes in a deployment are not executable, throw an exception * as this doesn't make sense to do. */ @Test public void testAllNonExecutableProcesses() { BpmnModel bpmnModel = new BpmnModel(); for (int i=0; i<5; i++) { org.activiti.bpmn.model.Process process = TestProcessUtil.createOneTaskProcess(); process.setExecutable(false); bpmnModel.addProcess(process); } List<ValidationError> errors = processValidator.validate(bpmnModel); Assert.assertEquals(1, errors.size()); } /* * Test for https://activiti.atlassian.net/browse/ACT-2071: * * If there is at least one process definition which is executable, * and the deployment contains other process definitions which are not executable, * then add a warning for those non executable process definitions */ @Test public void testNonExecutableProcessDefinitionWarning() { BpmnModel bpmnModel = new BpmnModel(); // 3 non-executables for (int i=0; i<3; i++) { org.activiti.bpmn.model.Process process = TestProcessUtil.createOneTaskProcess(); process.setExecutable(false); bpmnModel.addProcess(process); } // 1 executables org.activiti.bpmn.model.Process process = TestProcessUtil.createOneTaskProcess(); process.setExecutable(true); bpmnModel.addProcess(process); List<ValidationError> errors = processValidator.validate(bpmnModel); Assert.assertEquals(3, errors.size()); for (ValidationError error : errors) { Assert.assertTrue(error.isWarning()); Assert.assertNotNull(error.getValidatorSetName()); Assert.assertNotNull(error.getProblem()); Assert.assertNotNull(error.getDefaultDescription()); } } protected void assertCommonProblemFieldForActivity(ValidationError error) { assertProcessElementError(error); Assert.assertNotNull(error.getActivityId()); Assert.assertNotNull(error.getActivityName()); Assert.assertTrue(error.getActivityId().length() > 0); Assert.assertTrue(error.getActivityName().length() > 0); } protected void assertCommonErrorFields(ValidationError error) { Assert.assertNotNull(error.getValidatorSetName()); Assert.assertNotNull(error.getProblem()); Assert.assertNotNull(error.getDefaultDescription()); Assert.assertTrue(error.getXmlLineNumber() > 0); Assert.assertTrue(error.getXmlColumnNumber() > 0); } protected void assertProcessElementError(ValidationError error) { assertCommonErrorFields(error); Assert.assertEquals("invalidProcess", error.getProcessDefinitionId()); Assert.assertEquals("The invalid process", error.getProcessDefinitionName()); } protected List<ValidationError> findErrors(List<ValidationError> errors, String validatorSetName, String problemName, int expectedNrOfProblems) { List<ValidationError> results = findErrors(errors, validatorSetName, problemName); Assert.assertEquals(expectedNrOfProblems, results.size()); for (ValidationError result : results) { Assert.assertEquals(validatorSetName, result.getValidatorSetName()); Assert.assertEquals(problemName, result.getProblem()); } return results; } protected List<ValidationError> findErrors(List<ValidationError> errors, String validatorSetName, String problemName) { List<ValidationError> results = new ArrayList<ValidationError>(); for (ValidationError error : errors) { if (error.getValidatorSetName().equals(validatorSetName) && error.getProblem().equals(problemName)) { results.add(error); } } return results; } }