/* * 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); } }