blob: dcb68dd629f755536ebb2210bb9cd5a1a31d6c1b [file] [log] [blame]
/**
* Copyright (c) 2010 Henning Heitkoetter.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Henning Heitkoetter - initial API and implementation
*/
package org.eclipse.bpmn2.tests;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.bpmn2.Bpmn2Factory;
import org.eclipse.bpmn2.Bpmn2Package;
import org.eclipse.bpmn2.Category;
import org.eclipse.bpmn2.CategoryValue;
import org.eclipse.bpmn2.Collaboration;
import org.eclipse.bpmn2.ConditionalEventDefinition;
import org.eclipse.bpmn2.ConversationLink;
import org.eclipse.bpmn2.Definitions;
import org.eclipse.bpmn2.DocumentRoot;
import org.eclipse.bpmn2.Documentation;
import org.eclipse.bpmn2.FlowElement;
import org.eclipse.bpmn2.FormalExpression;
import org.eclipse.bpmn2.Import;
import org.eclipse.bpmn2.ItemDefinition;
import org.eclipse.bpmn2.Lane;
import org.eclipse.bpmn2.LaneSet;
import org.eclipse.bpmn2.Participant;
import org.eclipse.bpmn2.Process;
import org.eclipse.bpmn2.RootElement;
import org.eclipse.bpmn2.ScriptTask;
import org.eclipse.bpmn2.Task;
import org.eclipse.bpmn2.util.NamespaceHelper;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.DynamicEObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.ClassNotFoundException;
import org.eclipse.emf.ecore.xmi.FeatureNotFoundException;
import org.eclipse.emf.ecore.xml.type.SimpleAnyType;
import org.eclipse.emf.ecore.xml.type.XMLTypeFactory;
import org.eclipse.emf.ecore.xml.type.XMLTypePackage;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Tests serialization as XML.
* @author Henning Heitkoetter
*
*/
public class XMLSerializationTest extends Bpmn2SerializationTest {
protected Definitions model;
/**
* Prepares a test run by initializing all fields.
*
* A basic BPMN2 model is created in {@link #model}, thereby initializing the BPMN2 package.
*/
@Before
public void setUpModel() {
model = TestHelper.initBasicModel("urn:tns1");
}
/**
* The extension for all files that are created.
* @return File extension, i.e. {@code "bpmn2"}.
*/
@Override
protected String getFileExtension() {
return EXTENSION_BPMN2_XML;
}
@Override
protected String getSubDirectory() {
return "xml";
}
/**
* Checks if serialization works at all.
* @throws Exception
*/
@Test
public void testBasicSerialization() throws Exception {
Resource res = saveAndLoadModel("basic", model);
assertTrue("Resource loaded with errors", res.getErrors().isEmpty());
checkBasicSerialization(res);
}
/**
* Performs the actual checks if the basic serialization succeeded.
*
* Can be overriden by subclasses.
* @param res
*/
protected void checkBasicSerialization(Resource res) {
EObject root = res.getContents().get(0);
if (root instanceof DocumentRoot) {
DocumentRoot docRoot = (DocumentRoot) root;
assertTrue("Namespace prefix bpmn2 not present", docRoot.getXMLNSPrefixMap()
.containsKey(Bpmn2Package.eNS_PREFIX));
String NS_URI_EXPECTED = Bpmn2Package.eNS_URI.endsWith("-XMI") ? NamespaceHelper
.xmiToXsdNamespaceUri(Bpmn2Package.eNS_URI) : Bpmn2Package.eNS_URI;
assertEquals("Namespace URI of prefix bpmn2", NS_URI_EXPECTED, docRoot
.getXMLNSPrefixMap().get(Bpmn2Package.eNS_PREFIX));
assertNotNull("No definitions object in doc root", docRoot.getDefinitions());
} else
fail("Root element is not DocumentRoot");
}
/**
* Tests if an ID is generated upon save if necessary.
* @throws Exception
*/
@Test
public void testIdSerialization() throws Exception {
Collaboration c = Bpmn2Factory.eINSTANCE.createCollaboration();
c.setName("collab1");
Process p = Bpmn2Factory.eINSTANCE.createProcess();
p.setDefinitionalCollaborationRef(c);
model.getRootElements().add(c);
model.getRootElements().add(p);
Resource res = saveAndLoadModel("idOK", model);
Definitions d = TestHelper.getRootDefinitionElement(res);
// Technically, only collab1 needs to have an ID, because it is referenced by another element
for (RootElement cur : d.getRootElements())
if (cur instanceof Collaboration && ((Collaboration) cur).getName().equals("collab1")) {
assertNotNull(
"No id generated for element \"collab1\", although it is referenced by another element",
cur.getId());
break;
}
}
/**
* Asserts that no ID is generated for elements that don't have a corresponding feature.
* @throws IOException
*/
@Test
public void testNoIDForImport() throws IOException {
model.getImports().add(Bpmn2Factory.eINSTANCE.createImport());
try {
saveAndLoadModel("noIDForImport", model);
} catch (WrappedException e) {
if (e.exception() instanceof FeatureNotFoundException) {
FeatureNotFoundException fnfe = ((FeatureNotFoundException) e.exception());
if (fnfe.getName().equals("id")) {
fail("ID was generated for an import element (Import does not have an ID feature)");
}
} else
throw e;
}
}
@Test
public void testIDAlreadySet() throws Exception {
model.setId("id1");
Resource res = null;
try {
res = saveAndLoadModel("idAlreadySet", model);
} catch (WrappedException e) {
if (e.exception() instanceof SAXParseException)
fail("Duplicate attribute 'id'.");
else
throw e;
}
assertEquals("id1", TestHelper.getRootDefinitionElement(res).getId());
}
@Test
public void testDocumentationText() throws Exception {
final String docId = "doc1";
final String docText = "Documentation text";
Process p = Bpmn2Factory.eINSTANCE.createProcess();
p.setName("Name");
Documentation doc = Bpmn2Factory.eINSTANCE.createDocumentation();
doc.setText(docText);
doc.setId(docId);
p.getDocumentation().add(doc);
model.getRootElements().add(p);
Resource res = saveAndLoadModel("documentationText", model);
EObject docLoaded = res.getEObject(docId);
assertTrue(docLoaded instanceof Documentation);
assertEquals(docText, ((Documentation) docLoaded).getText());
checkSerializationDocText(res);
}
protected void checkSerializationDocText(Resource res) throws SAXException, IOException,
ParserConfigurationException {
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
Document xml = fact.newDocumentBuilder().parse(new File(res.getURI().toFileString()));
Node docNode = xml.getElementsByTagName("bpmn2:documentation").item(0);
assertNull("Documentation has an attribute 'text' (invalid acc. to XML schema)", docNode
.getAttributes().getNamedItem("text"));
}
@Test
public void testScript() throws Exception {
final String scriptId = "st1";
final String scriptContent = "Script content";
Process p = Bpmn2Factory.eINSTANCE.createProcess();
p.setName("Name");
ScriptTask st = Bpmn2Factory.eINSTANCE.createScriptTask();
st.setId(scriptId);
st.setName("Name");
st.setScript(scriptContent);
st.setScriptFormat("Script format");
p.getFlowElements().add(st);
model.getRootElements().add(p);
Resource res = saveAndLoadModel("scriptContent", model);
EObject stLoaded = res.getEObject(scriptId);
assertTrue(stLoaded instanceof ScriptTask);
assertEquals(scriptContent, ((ScriptTask) stLoaded).getScript());
checkSerializationScriptContent(res);
}
protected void checkSerializationScriptContent(Resource res) throws SAXException, IOException,
ParserConfigurationException {
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
Document xml = fact.newDocumentBuilder().parse(new File(res.getURI().toFileString()));
Node scriptNode = xml.getElementsByTagName("bpmn2:scriptTask").item(0);
assertNull("ScriptTask has an attribute 'script' (invalid acc. to XML schema)", scriptNode
.getAttributes().getNamedItem("script"));
}
@Test
public void testFormalExpressionBody() throws Exception {
final String feId = "fe1";
final String feBody = "${script}";
ConditionalEventDefinition ced = Bpmn2Factory.eINSTANCE.createConditionalEventDefinition();
FormalExpression fe = Bpmn2Factory.eINSTANCE.createFormalExpression();
fe.setBody(feBody);
fe.setId(feId);
ced.setCondition(fe);
model.getRootElements().add(ced);
Resource res = saveAndLoadModel("formalExpBody", model);
EObject docLoaded = res.getEObject(feId);
assertTrue(docLoaded instanceof FormalExpression);
assertEquals(feBody, ((FormalExpression) docLoaded).getBody());
checkSerializationFormalExpBody(res);
}
protected void checkSerializationFormalExpBody(Resource res) throws SAXException, IOException,
ParserConfigurationException {
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
Document xml = fact.newDocumentBuilder().parse(new File(res.getURI().toFileString()));
Node docNode = xml.getElementsByTagName("bpmn2:condition").item(0);
assertNull("FormalExpression has an attribute 'body' (invalid acc. to XML schema)", docNode
.getAttributes().getNamedItem("body"));
}
@Test
public void testItemDefinitionXSD() throws Exception {
/*
* TODO: This test FAILS currently (XSD&XMI).
* XSD-EMF does not support lookup by ID - wrong element (whole schema) resolved.
* We need to handle this to not lose the original reference.
*/
testItemDefinition("res/DataDefinitions.xsd", "http://www.example.org/Messages",
"reqForQuot", "xsd");
}
@Test
public void testItemDefinitionBPMN() throws Exception {
// should resolve to Definitions element
testItemDefinition("res/basic.bpmn2", "urn:basic", "_DqjcgKCwEd-AVfJ3FSSMwg", "bpmn");
}
@Test
public void testItemDefinitionElement() throws Exception {
// does not exist, should be handled gracefully
testItemDefinition("test.abc", "urn:test", "id", "element");
}
/**
* Tests the structureRef feature of ItemDefinition.
*
* @param location Location of the file in which the structure element is located.
* @param namespace Namespace for the import element pointing to the file.
* @param id ID of the structure.
* @param name Name of the test, used in the filename of the resource.
* @throws Exception
*/
public void testItemDefinition(String location, String namespace, String id, String name)
throws Exception {
Import xsdImport = Bpmn2Factory.eINSTANCE.createImport();
xsdImport.setNamespace(namespace);
xsdImport.setLocation("../../" + location); // Relative to resource during test run.
model.getImports().add(xsdImport);
ItemDefinition xsdItem = Bpmn2Factory.eINSTANCE.createItemDefinition();
final String xsdItemId = "xsdItemID";
xsdItem.setId(xsdItemId);
InternalEObject value = new DynamicEObjectImpl();
final URI uri = URI.createURI(location + "#" + id);
value.eSetProxyURI(uri);
xsdItem.setStructureRef(value);
model.getRootElements().add(xsdItem);
Resource res = saveAndLoadModel("itemDef_" + name, model);
ItemDefinition xsdItemNew = (ItemDefinition) res.getEObject(xsdItemId);
checkStructureRef(uri, xsdItemNew);
}
/**
* Checks if the structureRef feature of itemDef points (after resolution) to the given URI.
* @param uriExpected The expected URI.
* @param itemDef The Item Definition.
*/
protected void checkStructureRef(final URI uriExpected, ItemDefinition itemDef) {
final InternalEObject xsdStructure = (InternalEObject) itemDef.getStructureRef();
assertNotNull(xsdStructure);
if (xsdStructure.eIsProxy())
assertTrue(String.format("Proxy and expected URI differ (expected: %s, actual: %s)",
uriExpected, xsdStructure.eProxyURI()), xsdStructure.eProxyURI().toString()
.endsWith(uriExpected.toString()));
else {
final Resource res = xsdStructure.eResource();
URI actual = res.getURI().appendFragment(res.getURIFragment(xsdStructure));
assertTrue(String.format("Actual and expected URI differ (expected: %s, actual: %s)",
uriExpected, actual), actual.toString().endsWith(uriExpected.toString()));
}
}
@Test
public void testOppositeReferenceCategoryValue() throws Exception {
Category cat = Bpmn2Factory.eINSTANCE.createCategory();
CategoryValue catValue = Bpmn2Factory.eINSTANCE.createCategoryValue();
cat.getCategoryValue().add(catValue);
model.getRootElements().add(cat);
Process proc = Bpmn2Factory.eINSTANCE.createProcess();
Task task = Bpmn2Factory.eINSTANCE.createTask();
proc.getFlowElements().add(task);
model.getRootElements().add(proc);
task.getCategoryValueRef().add(catValue);
saveAndLoadModel("oppositeRefCategoryValue", model);
List<FlowElement> result = null;
try {
result = catValue.getCategorizedFlowElements();
} catch (UnsupportedOperationException e) {
fail("getCategorizedFlowElements not implemented");
}
assertTrue("Task not found in list", result.contains(task));
}
@Test
public void testOppositeReferenceInteractionNode() throws Exception {
Collaboration collab = Bpmn2Factory.eINSTANCE.createCollaboration();
Participant part1 = Bpmn2Factory.eINSTANCE.createParticipant();
collab.getParticipants().add(part1);
Participant part2 = Bpmn2Factory.eINSTANCE.createParticipant();
collab.getParticipants().add(part2);
model.getRootElements().add(collab);
ConversationLink link1 = Bpmn2Factory.eINSTANCE.createConversationLink();
link1.setSourceRef(part1);
link1.setTargetRef(part2);
collab.getConversationLinks().add(link1);
ConversationLink link2 = Bpmn2Factory.eINSTANCE.createConversationLink();
link2.setTargetRef(part1);
link2.setSourceRef(part2);
collab.getConversationLinks().add(link2);
saveAndLoadModel("oppositeRefInteractionNode", model);
try {
List<ConversationLink> tmp = part1.getIncomingConversationLinks();
assertTrue(tmp.size() == 1 && tmp.contains(link2));
tmp = part1.getOutgoingConversationLinks();
assertTrue(tmp.size() == 1 && tmp.contains(link1));
tmp = part2.getIncomingConversationLinks();
assertTrue(tmp.size() == 1 && tmp.contains(link1));
tmp = part2.getOutgoingConversationLinks();
assertTrue(tmp.size() == 1 && tmp.contains(link2));
} catch (UnsupportedOperationException e) {
fail("getIncoming/OutgoingConversationLinks not implemented");
}
}
@Test
public void testIDReferenceToAbstractType() throws Exception {
Process p = Bpmn2Factory.eINSTANCE.createProcess();
model.getRootElements().add(p);
Task t = Bpmn2Factory.eINSTANCE.createTask();
p.getFlowElements().add(t);
LaneSet ls = Bpmn2Factory.eINSTANCE.createLaneSet();
p.getLaneSets().add(ls);
Lane l = Bpmn2Factory.eINSTANCE.createLane();
String laneId = "laneId";
l.setId(laneId);
ls.getLanes().add(l);
l.getFlowNodeRefs().add(t);
try {
Resource res = saveAndLoadModel("idRefToAbstract", model);
Lane lNew = (Lane) res.getEObject(laneId);
assertTrue(lNew.getFlowNodeRefs().size() > 0);
assertFalse(lNew.getFlowNodeRefs().get(0).eIsProxy());
assertEquals(t.getId(), lNew.getFlowNodeRefs().get(0).getId());
} catch (WrappedException e) {
if (e.exception() instanceof ClassNotFoundException)
fail("Class EventDefinition was recognized as abstract.");
else
throw e;
} catch (IllegalArgumentException e) {
// different error in Eclipse 3.4
if (e.getMessage().endsWith("not a valid classifier"))
fail("Class EventDefinition was recognized as abstract.");
else
throw e;
}
}
}