/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.workflow.model.api;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
import de.rcenvironment.core.communication.api.PlatformService;
import de.rcenvironment.core.communication.common.InstanceNodeSessionId;
import de.rcenvironment.core.communication.common.LogicalNodeId;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.communication.common.NodeIdentifierUtils;
import de.rcenvironment.core.component.api.ComponentConstants;
import de.rcenvironment.core.component.api.DistributedComponentKnowledge;
import de.rcenvironment.core.component.api.DistributedComponentKnowledgeService;
import de.rcenvironment.core.component.model.api.ComponentDescription;
import de.rcenvironment.core.component.model.api.ComponentDescriptionFactoryService;
import de.rcenvironment.core.component.model.api.ComponentInstallation;
import de.rcenvironment.core.component.model.configuration.api.ConfigurationDescription;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDescriptionsManager;
import de.rcenvironment.core.component.testutils.ComponentDescriptionFactoryServiceDefaultStub;
import de.rcenvironment.core.component.testutils.DistributedComponentKnowledgeServiceDefaultStub;
import de.rcenvironment.core.component.workflow.ComponentInstallationMockFactory;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowFileException;
import de.rcenvironment.core.datamodel.api.DataType;
import de.rcenvironment.core.utils.common.TempFileServiceAccess;
/**
* Test cases for {@link WorkflowDescriptionPersistenceHandler}.
*
* @author Doreen Seider
* @author Robert Mischke
*/
public class WorkflowDescriptionPersistenceHandlerTest {
private static final String COMP_VERSION_INPUT_PROVIDER = "3.2";
private static final String COMP_ID_INPUT_PROVIDER =
"de.rcenvironment.inputprovider" + ComponentConstants.ID_SEPARATOR + COMP_VERSION_INPUT_PROVIDER;
private static final String COMP_VERSION_SCRIPT = "3.4";
private static final String COMP_ID_SCRIPT = "de.rcenvironment.script" + ComponentConstants.ID_SEPARATOR + COMP_VERSION_SCRIPT;
private static final String COMP_VERSION_OUTPUT_WRITER = "2.0";
private static final String COMP_ID_OUTPUT_WRITER =
"de.rcenvironment.outputwriter" + ComponentConstants.ID_SEPARATOR + COMP_VERSION_OUTPUT_WRITER;
private static final String LOCAL_INSTANCE_ID = "c8061f66333342c9a393c2184c75454f";
private static final String LOCAL_LOGICAL_NODE_ID = LOCAL_INSTANCE_ID + ":0";
private static final String REMOTE_INSTANCE_ID = "5f323616fcc4440d852074f737a9f297";
private static final String REMOTE_LOGICAL_NODE_ID = REMOTE_INSTANCE_ID + ":0";
private static final int TEST_TIMEOUT_MSEC = 2000;
/**
* Tests if a basic workflow is read properly by {@link WorkflowDescriptionPersistenceHandler}. It doesn't check each detail, but tries
* to cover most parts of the workflow.
*
* @throws IOException on test failure
* @throws WorkflowFileException on test failure
*/
@Test(timeout = TEST_TIMEOUT_MSEC)
public void testValidBasicWorkflow() throws IOException, WorkflowFileException {
WorkflowDescriptionPersistenceHandler handler =
WorkflowDescriptionPersistenceHandlerTestUtils.createWorkflowDescriptionPersistenceHandlerTestInstance();
handler.bindPlatformService(createPlatformServiceMock());
Map<ComponentInstallation, ComponentDescription> compInstToCompDescMapping = createCompInstToCompDescMapping();
Map<String, ComponentInstallation> wfNodeIdToCompInstMapping = createWfNodeIdToCompInstMapping(compInstToCompDescMapping.keySet());
handler.bindDistributedComponentKnowledgeService(createComponentKnowledgeServiceMock(compInstToCompDescMapping));
handler.bindComponentDescriptionFactoryService(createComponentDescriptionFactoryServiceMock(compInstToCompDescMapping));
WorkflowDescription workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream("Basic_test_workflow.wf"));
assertEquals("aebf6bb8-ad05-4b2e-a698-a6705f6dc0cf", workflowDescription.getIdentifier());
assertEquals(4, workflowDescription.getWorkflowVersion());
assertWorkflowNodes(workflowDescription, wfNodeIdToCompInstMapping);
assertConnections(workflowDescription);
assertBendpoints(workflowDescription);
assertWorkflowLabels(workflowDescription);
}
/**
* Test if loading a workflow file and saving it again produces the same file.
*
* @throws IOException on test failure
* @throws WorkflowFileException on test failure
* @throws SecurityException on failure setting the encoding
* @throws NoSuchFieldException on failure setting the encoding
* @throws IllegalAccessException on failure setting the encoding
* @throws IllegalArgumentException on failure setting the encoding
*/
@Test(timeout = TEST_TIMEOUT_MSEC)
public void testWritingWorkflow() throws IOException, WorkflowFileException, NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
setFileEncoding("Cp1252");
String filename = "Labels_old_style.wf";
TempFileServiceAccess.setupUnitTestEnvironment();
File tempFileOrig = TempFileServiceAccess.getInstance().createTempFileWithFixedFilename("Labels_orig.wf");
FileUtils.copyInputStreamToFile(getTestFileStream(filename), tempFileOrig);
WorkflowDescriptionPersistenceHandler handler =
WorkflowDescriptionPersistenceHandlerTestUtils.createWorkflowDescriptionPersistenceHandlerTestInstance();
WorkflowDescription workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream(filename));
ByteArrayOutputStream workflowStream = handler.writeWorkflowDescriptionToStream(workflowDescription);
File tempFileTest = TempFileServiceAccess.getInstance().createTempFileWithFixedFilename("Labels_test.wf");
try (OutputStream outputStream = new FileOutputStream(tempFileTest)) {
workflowStream.writeTo(outputStream);
}
setFileEncoding("UTF-8");
Assert.assertTrue(FileUtils.contentEqualsIgnoreEOL(tempFileOrig, tempFileTest, "Cp1252"));
TempFileServiceAccess.getInstance().disposeManagedTempDirOrFile(tempFileOrig);
TempFileServiceAccess.getInstance().disposeManagedTempDirOrFile(tempFileTest);
}
private static void setFileEncoding(String encoding) throws NoSuchFieldException, IllegalAccessException {
System.setProperty("file.encoding", encoding);
Field cs = Charset.class.getDeclaredField("defaultCharset");
cs.setAccessible(true);
cs.set(null, null);
}
private void assertWorkflowNodes(WorkflowDescription wd, Map<String, ComponentInstallation> wfNodeIdToCompInstMapping) {
assertEquals(3, wd.getWorkflowNodes().size());
for (WorkflowNode wfNode : wd.getWorkflowNodes()) {
assertTrue(wfNode.getComponentDescription().getComponentInstallation().getComponentRevision().getComponentInterface()
.getIdentifier().equals(wfNodeIdToCompInstMapping.get(wfNode.getIdentifier())
.getComponentRevision().getComponentInterface().getIdentifier()));
final String logicalNodeIdStringOfComponent = wfNode.getComponentDescription().getComponentInstallation().getNodeId();
assertEquals(logicalNodeIdStringOfComponent,
wfNodeIdToCompInstMapping.get(wfNode.getIdentifier()).getNodeId());
}
// spot check
WorkflowNode ouputWritterWfNode = wd.getWorkflowNode("361ff68d-04c7-4ef1-a1b7-aba1292b339d");
final int x = 571;
final int y = 265;
assertEquals(x, ouputWritterWfNode.getX());
assertEquals(y, ouputWritterWfNode.getY());
assertEquals("Combiner", ouputWritterWfNode.getName());
}
private void assertConnections(WorkflowDescription wd) {
assertEquals(2, wd.getConnections().size());
for (Connection cn : wd.getConnections()) {
assertTrue(cn.getSourceNode().getName().equals("Floater") || cn.getSourceNode().getName().equals("Texter"));
assertTrue(cn.getTargetNode().getName().equals("Combiner"));
// spot check
if (cn.getSourceNode().getName().equals("Texter")) {
assertEquals("e292472a-2b5e-4007-ab6c-2d2bd784d6f6", cn.getOutput().getIdentifier());
assertEquals("text_outp", cn.getOutput().getName());
assertEquals(DataType.ShortText, cn.getOutput().getDataType());
assertEquals("d4d41dc2-5586-423a-9f2e-cc0bd7f00d24", cn.getInput().getIdentifier());
assertEquals("text_inp", cn.getInput().getName());
assertEquals(DataType.ShortText, cn.getInput().getDataType());
assertEquals("default", cn.getInput().getDynamicEndpointIdentifier());
assertEquals("fcbbe1be-1fc2-45fc-9718-0c64d76c7bf8", cn.getInput().getParentGroupName());
}
}
}
private void assertBendpoints(WorkflowDescription wd) {
for (Connection cn : wd.getConnections()) {
// spot check
if (cn.getSourceNode().getName().equals("Floater")) {
assertEquals(1, cn.getBendpoints().size());
final int x = 590;
assertEquals(x, cn.getBendpoints().get(0).x);
final int y = 177;
assertEquals(y, cn.getBendpoints().get(0).y);
}
}
}
private void assertWorkflowLabels(WorkflowDescription wd) {
assertEquals(1, wd.getWorkflowLabels().size());
// spot check
WorkflowLabel wfLabel = wd.getWorkflowLabels().get(0);
assertEquals("2a16256d-4b49-421b-8bb0-9244008a7592", wfLabel.getIdentifier());
assertEquals("Workflow that covers a broad range of possible content in a workflow file.", wfLabel.getText());
final int height = 68;
assertEquals(height, wfLabel.getHeight());
final int width = 665;
assertEquals(width, wfLabel.getWidth());
final int x = 50;
assertEquals(x, wfLabel.getX());
final int y = 25;
assertEquals(y, wfLabel.getY());
final int textSize = 11;
assertEquals(textSize, wfLabel.getTextSize());
}
private PlatformService createPlatformServiceMock() {
PlatformService platformService = EasyMock.createNiceMock(PlatformService.class);
final InstanceNodeSessionId instanceSessionId = NodeIdentifierTestUtils.createTestInstanceNodeSessionId(LOCAL_INSTANCE_ID);
final LogicalNodeId defaultLogicalNodeId = instanceSessionId.convertToDefaultLogicalNodeId();
EasyMock.expect(platformService.getLocalInstanceNodeSessionId())
.andReturn(instanceSessionId)
.anyTimes();
EasyMock.expect(platformService.getLocalDefaultLogicalNodeId())
.andReturn(defaultLogicalNodeId)
.anyTimes();
EasyMock.replay(platformService);
return platformService;
}
private DistributedComponentKnowledgeService createComponentKnowledgeServiceMock(
Map<ComponentInstallation, ComponentDescription> compInstToCompDescMapping) {
DistributedComponentKnowledgeService compKnowledgeService = EasyMock.createStrictMock(DistributedComponentKnowledgeService.class);
EasyMock.expect(compKnowledgeService.getCurrentComponentKnowledge())
.andStubReturn(createComponentKnowledgeMock(compInstToCompDescMapping));
EasyMock.replay(compKnowledgeService);
return compKnowledgeService;
}
private DistributedComponentKnowledge createComponentKnowledgeMock(
Map<ComponentInstallation, ComponentDescription> compInstToCompDescMapping) {
DistributedComponentKnowledge compKnowledge = EasyMock.createStrictMock(DistributedComponentKnowledge.class);
EasyMock.expect(compKnowledge.getAllInstallations()).andStubReturn(compInstToCompDescMapping.keySet());
EasyMock.replay(compKnowledge);
return compKnowledge;
}
private Map<ComponentInstallation, ComponentDescription> createCompInstToCompDescMapping() {
Map<ComponentInstallation, ComponentDescription> compInstToCompDescMapping = new HashMap<>();
for (ComponentInstallation compInst : createComponentInstallationMocks()) {
ConfigurationDescription confDesc = EasyMock.createNiceMock(ConfigurationDescription.class);
EasyMock.replay(confDesc);
EndpointDescriptionsManager inpDescManager = EasyMock.createNiceMock(EndpointDescriptionsManager.class);
EasyMock.replay(inpDescManager);
EndpointDescriptionsManager outpDescManager = EasyMock.createNiceMock(EndpointDescriptionsManager.class);
EasyMock.replay(outpDescManager);
ComponentDescription compDesc = EasyMock.createNiceMock(ComponentDescription.class);
EasyMock.expect(compDesc.getComponentInstallation()).andStubReturn(compInst);
EasyMock.expect(compDesc.getIdentifier())
.andStubReturn(compInst.getComponentRevision().getComponentInterface().getIdentifier());
EasyMock.expect(compDesc.getConfigurationDescription()).andStubReturn(confDesc);
EasyMock.expect(compDesc.getInputDescriptionsManager()).andStubReturn(inpDescManager);
EasyMock.expect(compDesc.getOutputDescriptionsManager()).andStubReturn(outpDescManager);
EasyMock.replay(compDesc);
compInstToCompDescMapping.put(compInst, compDesc);
}
return compInstToCompDescMapping;
}
private Map<String, ComponentInstallation> createWfNodeIdToCompInstMapping(Set<ComponentInstallation> compInstallations) {
Map<String, ComponentInstallation> wfNodeIdToCompInstMapping = new HashMap<>();
for (ComponentInstallation compInst : compInstallations) {
if (compInst.getComponentRevision().getComponentInterface().getIdentifier().equals(COMP_ID_INPUT_PROVIDER)
&& (compInst.getNodeId() == null || isComponentInstallationLocatedOnInstance(compInst, LOCAL_INSTANCE_ID))) {
wfNodeIdToCompInstMapping.put("fbfcc614-b0e9-4a97-81db-26dd40964e15", compInst);
} else if (compInst.getComponentRevision().getComponentInterface().getIdentifier().equals(COMP_ID_SCRIPT)
&& isComponentInstallationLocatedOnInstance(compInst, REMOTE_INSTANCE_ID)) {
wfNodeIdToCompInstMapping.put("c5aa840d-68a8-456f-943b-569a1875933d", compInst);
} else if (compInst.getComponentRevision().getComponentInterface().getIdentifier().equals(COMP_ID_OUTPUT_WRITER)
&& (compInst.getNodeId() == null || isComponentInstallationLocatedOnInstance(compInst, LOCAL_INSTANCE_ID))) {
wfNodeIdToCompInstMapping.put("361ff68d-04c7-4ef1-a1b7-aba1292b339d", compInst);
}
}
return wfNodeIdToCompInstMapping;
}
private Collection<ComponentInstallation> createComponentInstallationMocks() {
Set<ComponentInstallation> compInstallations = new HashSet<>();
compInstallations.add(ComponentInstallationMockFactory.createComponentInstallationMock(
COMP_ID_INPUT_PROVIDER, COMP_VERSION_INPUT_PROVIDER, LOCAL_LOGICAL_NODE_ID));
compInstallations.add(ComponentInstallationMockFactory.createComponentInstallationMock(
COMP_ID_SCRIPT, COMP_VERSION_SCRIPT, LOCAL_LOGICAL_NODE_ID));
compInstallations.add(ComponentInstallationMockFactory.createComponentInstallationMock(
COMP_ID_SCRIPT, COMP_VERSION_SCRIPT, REMOTE_LOGICAL_NODE_ID));
compInstallations.add(ComponentInstallationMockFactory.createComponentInstallationMock(
COMP_ID_OUTPUT_WRITER, COMP_VERSION_OUTPUT_WRITER, LOCAL_LOGICAL_NODE_ID));
compInstallations.add(ComponentInstallationMockFactory.createComponentInstallationMock(
COMP_ID_OUTPUT_WRITER, COMP_VERSION_OUTPUT_WRITER, REMOTE_LOGICAL_NODE_ID));
return compInstallations;
}
private ComponentDescriptionFactoryService createComponentDescriptionFactoryServiceMock(
Map<ComponentInstallation, ComponentDescription> compInstToCompDescMapping) {
ComponentDescriptionFactoryService compKnowledgeService = EasyMock.createStrictMock(ComponentDescriptionFactoryService.class);
for (ComponentInstallation compInst : compInstToCompDescMapping.keySet()) {
EasyMock.expect(compKnowledgeService.createComponentDescription(compInst))
.andStubReturn(compInstToCompDescMapping.get(compInst));
}
EasyMock.replay(compKnowledgeService);
return compKnowledgeService;
}
/**
* Tests if {@link WorkflowDescriptionPersistenceHandler} can handle json arrays on top level, which key is not known.
*
* @throws IOException on test failure
* @throws WorkflowFileException on test failure
*/
@Test(timeout = TEST_TIMEOUT_MSEC)
public void testIfUnknownJsonArrayIsHandledProperly() throws IOException, WorkflowFileException {
WorkflowDescriptionPersistenceHandler handler =
WorkflowDescriptionPersistenceHandlerTestUtils.createWorkflowDescriptionPersistenceHandlerTestInstance();
WorkflowDescription workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream("UnknownJsonArray.wf"));
assertEquals("03c4b8e3-7238-43f3-8992-2c3956b737a9", workflowDescription.getIdentifier());
assertEquals(1, workflowDescription.getWorkflowVersion());
}
/**
* Tests if {@link WorkflowDescriptionPersistenceHandler} can parse {@link WorkflowLabel}s in both ways supported (json array as one
* single string and as normal node).
*
* @throws IOException on test failure
* @throws WorkflowFileException on test failure
*/
@Test(timeout = TEST_TIMEOUT_MSEC)
public void testParseLabelsEitherStyle() throws IOException, WorkflowFileException {
WorkflowDescriptionPersistenceHandler handler =
WorkflowDescriptionPersistenceHandlerTestUtils.createWorkflowDescriptionPersistenceHandlerTestInstance();
WorkflowDescription workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream("Labels_old_style.wf"));
assertLabelsParsedEitheStyle(workflowDescription);
workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream("Labels_new_style.wf"));
assertLabelsParsedEitheStyle(workflowDescription);
}
private void assertLabelsParsedEitheStyle(WorkflowDescription workflowDescription) {
assertEquals("697261b6-eaf5-44ab-af40-6c161a4f26f8", workflowDescription.getIdentifier());
assertEquals(4, workflowDescription.getWorkflowVersion());
assertEquals(1, workflowDescription.getWorkflowLabels().size());
assertEquals("A label with umlauts: öäüÖÄÜß", workflowDescription.getWorkflowLabels().get(0).getText());
assertEquals(7, workflowDescription.getWorkflowLabels().get(0).getTextSize());
}
/**
* Tests if {@link WorkflowDescriptionPersistenceHandler} can parse bendpoints in both ways supported (json array as one single string
* and as normal node).
*
* @throws IOException on test failure
* @throws WorkflowFileException on test failure
*/
@Test(timeout = TEST_TIMEOUT_MSEC)
public void testParseBendpointsEitherStyle() throws IOException, WorkflowFileException {
WorkflowDescriptionPersistenceHandler handler =
WorkflowDescriptionPersistenceHandlerTestUtils.createWorkflowDescriptionPersistenceHandlerTestInstance();
handler.bindDistributedComponentKnowledgeService(new DistributedComponentKnowledgeServiceDefaultStub());
handler.bindComponentDescriptionFactoryService(new ComponentDescriptionFactoryServiceDefaultStub());
WorkflowDescription workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream("Bendpoints_old_style.wf"));
assertBendpointsParsedEitherStyle(workflowDescription);
workflowDescription = handler.readWorkflowDescriptionFromStream(getTestFileStream("Bendpoints_new_style.wf"));
assertBendpointsParsedEitherStyle(workflowDescription);
}
private void assertBendpointsParsedEitherStyle(WorkflowDescription workflowDescription) {
assertEquals("a5018ce0-bfdd-4704-a1a7-32d8a3a739ad", workflowDescription.getIdentifier());
assertEquals(4, workflowDescription.getWorkflowVersion());
for (Connection connection : workflowDescription.getConnections()) {
if (connection.getSourceNode().getIdentifier().equals("d31c12aa-faab-4ed1-89ae-08eaa8c9a9af")) {
assertEquals(1, connection.getBendpoints().size());
final int x = 352;
final int y = 41;
assertEquals(x, connection.getBendpoint(0).x);
assertEquals(y, connection.getBendpoint(0).y);
}
}
}
/**
* Regression test for issue #0013377 that causes/caused the parser to hang on specific input.
*
* @throws IOException on test failure
* @throws WorkflowFileException on test failure
*/
@Test(timeout = TEST_TIMEOUT_MSEC)
public void specificRegressionTest() throws IOException, WorkflowFileException {
WorkflowDescriptionPersistenceHandler handler =
WorkflowDescriptionPersistenceHandlerTestUtils.createWorkflowDescriptionPersistenceHandlerTestInstance();
try {
handler.readWorkflowDescriptionFromStream(getTestFileStream("Bug_Demo.wf"));
} catch (WorkflowFileException e) {
WorkflowDescription workflowDescription = e.getParsedWorkflowDescription();
assertNotNull(workflowDescription);
assertEquals("697261b6-eaf5-44ab-af0f35-9445-40a3-bbe1-84b36cc6ab2f", workflowDescription.getIdentifier());
}
}
private InputStream getTestFileStream(String filename) {
return getClass().getResourceAsStream("/workflows_unit_test/" + filename);
}
private boolean isComponentInstallationLocatedOnInstance(ComponentInstallation compInst, String instanceId) {
// encapsulate comparison until component installations use id objects natively
return NodeIdentifierUtils.parseLogicalNodeIdStringWithExceptionWrapping(compInst.getNodeId()).getInstanceNodeIdString()
.equals(instanceId);
}
}