Package eu.scape_project.planning.services.taverna.generator

Source Code of eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGeneratorTest

/*******************************************************************************
* Copyright 2006 - 2014 Vienna University of Technology,
* Department of Software Technology and Interactive Systems, IFS
*
* 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 eu.scape_project.planning.services.taverna.generator;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Node;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXException;

import eu.scape_project.planning.services.myexperiment.domain.ComponentConstants;
import eu.scape_project.planning.services.myexperiment.domain.MigrationPath;
import eu.scape_project.planning.services.myexperiment.domain.Port;
import eu.scape_project.planning.services.myexperiment.domain.WorkflowDescription;
import eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGenerator;
import eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGenerator.InputSource;
import eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGenerator.RelatedObject;

/**
* Unit tests for T2FlowExecutablePlanGenerator.
*/
public class T2FlowExecutablePlanGeneratorTest {

    private static final String MIGRATION_DATAFLOW_ID = "00000000-0000-0000-0000-000000000000";
    private static final String QA_DATAFLOW_ID = "11111111-1111-1111-1111-111111111111";
    private static final String CC_DATAFLOW_ID = "22222222-2222-2222-2222-222222222222";
    protected static final Map<String, String> T2FLOW_NAMESPACE_MAP = new HashMap<String, String>();
    private static final String TOP_WF = "/t2f:workflow/t2f:dataflow[@role='top']";
    private static final String NESTED_WF = "/t2f:workflow/t2f:dataflow[@role='nested']";

    private static final List<String> DEFAULT_MEASURES = new ArrayList<String>();
    protected static final Map<String, String> DEFAULT_PARAMETERS = new HashMap<String, String>();

    @BeforeClass
    public static void setup() {
        T2FLOW_NAMESPACE_MAP.put("t2f", "http://taverna.sf.net/2008/xml/t2flow");
        DEFAULT_MEASURES.add("http://purl.org/DP/quality/measures#1");
    }

    @Test
    public void createEmptyWorkflow() throws IOException, ParserConfigurationException, SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");
        Document doc = getDocument(planGenerator);

        assertThat(getContent(doc, TOP_WF + "/t2f:name"), is("Name"));

        assertThat(
            getContent(
                doc,
                TOP_WF
                    + "/t2f:annotations/t2f:annotation_chain/net.sf.taverna.t2.annotation.AnnotationChainImpl/annotationAssertions/net.sf.taverna.t2.annotation.AnnotationAssertionImpl/annotationBean[@class='net.sf.taverna.t2.annotation.annotationbeans.Author']/text"),
            is("Author"));

        String wfAnnotations = getSemanticAnnotation(doc, TOP_WF);
        assertThat(wfAnnotations, containsString("http://purl.org/DP/components#ExecutablePlan"));
        assertThat(wfAnnotations, containsString("image/tiff"));
        assertThat(wfAnnotations, containsString("image/jp2"));
    }

    @Test
    public void addSourcePort() throws IOException, ParserConfigurationException, SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");
        planGenerator.addSourcePort();

        Document doc = getDocument(planGenerator);

        assertThat(getContent(doc, TOP_WF + "/t2f:inputPorts/t2f:port/t2f:name"), is("source"));
        assertThat(getContent(doc, TOP_WF + "/t2f:inputPorts/t2f:port/t2f:depth"), is("0"));
        assertThat(getSemanticAnnotation(doc, TOP_WF + "/t2f:inputPorts/t2f:port"),
            containsString(ComponentConstants.VALUE_SOURCE_OBJECT));
    }

    @Test
    public void addSourcePort_depth() throws IOException, ParserConfigurationException, SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");
        planGenerator.addSourcePort(7);

        Document doc = getDocument(planGenerator);

        assertThat(getContent(doc, TOP_WF + "/t2f:inputPorts/t2f:port/t2f:name"), is("source"));
        assertThat(getContent(doc, TOP_WF + "/t2f:inputPorts/t2f:port/t2f:depth"), is("7"));
        assertThat(getSemanticAnnotation(doc, TOP_WF + "/t2f:inputPorts/t2f:port"),
            containsString(ComponentConstants.VALUE_SOURCE_OBJECT));
    }

    @Test
    public void addTargetPort() throws IOException, ParserConfigurationException, SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");
        planGenerator.addTargetPort();

        Document doc = getDocument(planGenerator);

        assertThat(getContent(doc, TOP_WF + "/t2f:outputPorts/t2f:port/t2f:name"), is("target"));
        assertThat(getSemanticAnnotation(doc, TOP_WF + "/t2f:outputPorts/t2f:port"),
            containsString(ComponentConstants.VALUE_TARGET_OBJECT));
    }

    @Test
    public void addMeasurePort_purlDp() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");
        planGenerator.addMeasurePort("http://purl.org/DP/quality/measures#1");

        Document doc = getDocument(planGenerator);

        String portName = getContent(doc, TOP_WF + "/t2f:outputPorts/t2f:port/t2f:name");
        assertThat(portName, is("measures_1"));

        String portAnnotations = getSemanticAnnotation(doc, TOP_WF + "/t2f:outputPorts/t2f:port");
        assertThat(portAnnotations, containsString("http://purl.org/DP/quality/measures#1"));
    }

    @Test
    public void addMeasurePort_generic() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");
        planGenerator.addMeasurePort("http://example.com/DP/measures#1");

        Document doc = getDocument(planGenerator);

        String portName = getContent(doc, TOP_WF + "/t2f:outputPorts/t2f:port/t2f:name");
        assertThat(portName, is("example_com_DP_measures_1"));

        String portAnnotations = getSemanticAnnotation(doc, TOP_WF + "/t2f:outputPorts/t2f:port");
        assertThat(portAnnotations, containsString("http://example.com/DP/measures#1"));
    }

    @Test
    public void addMigrationComponent() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);

        Document doc = getDocument(planGenerator);

        assertThat(getContent(doc, TOP_WF + "/t2f:processors/t2f:processor/t2f:name"), is("Migration"));
        assertThat(getContent(doc, NESTED_WF + "/t2f:name"), is("Migration component"));

        assertThat(
            getContent(doc, TOP_WF
                + "/t2f:processors/t2f:processor/t2f:activities/t2f:activity/t2f:configBean/t2f:dataflow/@ref"),
            is(MIGRATION_DATAFLOW_ID));

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='source']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='target']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(2));
    }

    @Test
    public void addQaComponent_noMigration() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        WorkflowDescription wf = mockQaAny("QA component");
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(getContent(doc, TOP_WF + "/t2f:processors/t2f:processor/t2f:name"), is("QA_component"));
        assertThat(getContent(doc, NESTED_WF + "/t2f:name"), is("QA component"));

        assertThat(
            getContent(doc, TOP_WF
                + "/t2f:processors/t2f:processor/t2f:activities/t2f:activity/t2f:configBean/t2f:dataflow/@ref"),
            is(QA_DATAFLOW_ID));

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(2));
    }

    @Test
    public void addQaComponent_set_sourceLeft_targetRight() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockQaAny("QA component");
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), InputSource.SOURCE_OBJECT,
            InputSource.TARGET_OBJECT, DEFAULT_PARAMETERS, DEFAULT_MEASURES, RelatedObject.RIGHT_OBJECT);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(5));
    }

    @Test
    public void addQaComponent_set_sourceRight_targetLeft() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);

        WorkflowDescription wf = mockQaAny("QA component");
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), InputSource.TARGET_OBJECT,
            InputSource.SOURCE_OBJECT, DEFAULT_PARAMETERS, DEFAULT_MEASURES, RelatedObject.RIGHT_OBJECT);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(5));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceLeft_targetRight() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);

        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsMimetypes("image/tiff", "image/jp2")).thenReturn(true);
        when(wf.acceptsLeftMimetype("image/tiff")).thenReturn(true);
        when(wf.acceptsRightMimetype("image/jp2")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(5));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceRight_targetLeft() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);

        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsMimetypes("image/jp2", "image/tiff")).thenReturn(true);
        when(wf.acceptsLeftMimetype("image/jp2")).thenReturn(true);
        when(wf.acceptsRightMimetype("image/tiff")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(5));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceBoth_targetBoth() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);

        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsMimetypes("image/jp2", "image/tiff")).thenReturn(true);
        when(wf.acceptsMimetypes("image/tiff", "image/jp2")).thenReturn(true);
        when(wf.acceptsLeftMimetype("image/jp2")).thenReturn(true);
        when(wf.acceptsLeftMimetype("image/tiff")).thenReturn(true);
        when(wf.acceptsRightMimetype("image/jp2")).thenReturn(true);
        when(wf.acceptsRightMimetype("image/tiff")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(5));
    }

    @Test
    public void addQaComponent_mimetypePair_sourcePair_targetPair() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsMimetypes("image/tiff", "image/tiff")).thenReturn(true);
        when(wf.acceptsMimetypes("image/jp2", "image/jp2")).thenReturn(true);
        when(wf.acceptsLeftMimetype("image/jp2")).thenReturn(true);
        when(wf.acceptsLeftMimetype("image/tiff")).thenReturn(true);
        when(wf.acceptsRightMimetype("image/jp2")).thenReturn(true);
        when(wf.acceptsRightMimetype("image/tiff")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(4));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceLeft_targetNone() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsLeftMimetype("image/tiff")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(4));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceRight_targetNone() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsRightMimetype("image/tiff")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(4));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceNone_targetLeft() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsLeftMimetype("image/jp2")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(4));
    }

    @Test
    public void addQaComponent_mimetypePair_sourceNone_targetRight() throws IOException, ParserConfigurationException,
        SAXException, DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockQa("QA component");
        when(wf.acceptsRightMimetype("image/jp2")).thenReturn(true);
        planGenerator.addQaComponent(wf, generateQaContent(QA_DATAFLOW_ID, "QA component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='left']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), nullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='right']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(4));
    }

    @Test
    public void addQaComponent_twoComponents() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf1 = mockQaAny("QA component 1");
        planGenerator.addQaComponent(wf1, generateQaContent(QA_DATAFLOW_ID, "QA component 1"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);
        WorkflowDescription wf2 = mockQaAny("QA component 2");
        planGenerator.addQaComponent(wf2, generateQaContent(QA_DATAFLOW_ID, "QA component 2"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES);

        Document doc = getDocument(planGenerator);

        assertThat(
            getNodes(
                doc,
                TOP_WF + "/t2f:datalinks/t2f:datalink" + "[t2f:source[@type='dataflow' and t2f:port/text()='source']"
                    + " and " + "t2f:sink[@type='processor' and t2f:port/text()='left']]").size(), is(2));
        assertThat(
            getNodes(
                doc,
                TOP_WF + "/t2f:datalinks/t2f:datalink" + "[t2f:source[@type='processor' and t2f:port/text()='target']"
                    + " and " + "t2f:sink[@type='processor' and t2f:port/text()='right']]").size(), is(2));
        assertThat(
            getNodes(
                doc,
                TOP_WF + "/t2f:datalinks/t2f:datalink"
                    + "[t2f:source[@type='processor' and t2f:port/text()='qa_output']" + " and "
                    + "t2f:sink[@type='merge' and t2f:port/text()='measures_1']]").size(), is(2));
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(8));
    }

    @Test
    public void addCcComponent_source() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        WorkflowDescription wf = mockCcAny("CC Component");
        planGenerator.addCcComponent(wf, generateCcContent(CC_DATAFLOW_ID, "CC Component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES, InputSource.SOURCE_OBJECT);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='source']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='cc_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(2));
    }

    @Test
    public void addCcComponent_target() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf = mockCcAny("CC Component");
        planGenerator.addCcComponent(wf, generateCcContent(CC_DATAFLOW_ID, "CC Component"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES, InputSource.TARGET_OBJECT);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='source']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='cc_output']" + " and "
                + "t2f:sink[@type='dataflow' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(4));
    }

    @Test
    public void addCcComponent_twoComponents() throws IOException, ParserConfigurationException, SAXException,
        DocumentException {
        T2FlowExecutablePlanGenerator planGenerator = new T2FlowExecutablePlanGenerator("Name", "Author", "image/tiff",
            "image/jp2");

        planGenerator.addSourcePort();
        planGenerator.addTargetPort();

        addMigrationMock(planGenerator);
        WorkflowDescription wf1 = mockCcAny("CC Component 1");
        planGenerator.addCcComponent(wf1, generateCcContent(CC_DATAFLOW_ID, "CC Component 1"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES, InputSource.SOURCE_OBJECT);
        WorkflowDescription wf2 = mockCcAny("CC Component 2");
        planGenerator.addCcComponent(wf2, generateCcContent(CC_DATAFLOW_ID, "CC Component 2"), DEFAULT_PARAMETERS,
            DEFAULT_MEASURES, InputSource.TARGET_OBJECT);

        Document doc = getDocument(planGenerator);

        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='dataflow' and t2f:port/text()='source']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='source' and t2f:processor/text()='CC_Component_1']]"),
            notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='cc_output' and t2f:processor/text()='CC_Component_1']"
                + " and " + "t2f:sink[@type='merge' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='target']" + " and "
                + "t2f:sink[@type='processor' and t2f:port/text()='source' and t2f:processor/text()='CC_Component_2']]"),
            notNullValue());
        assertThat(
            getSingleNode(doc, TOP_WF + "/t2f:datalinks/t2f:datalink"
                + "[t2f:source[@type='processor' and t2f:port/text()='cc_output' and t2f:processor/text()='CC_Component_2']"
                + " and " + "t2f:sink[@type='merge' and t2f:port/text()='measures_1']]"), notNullValue());
        assertThat(getNodes(doc, TOP_WF + "/t2f:datalinks/t2f:datalink").size(), is(6));
    }

    /**
     * Returns a document generated by the provided plan generator.
     *
     * @param planGenerator
     *            the plan generator
     * @return a document
     * @throws IOException
     *             if an exception occurred
     * @throws ParserConfigurationException
     *             if an exception occurred
     * @throws SAXException
     *             if an exception occurred
     * @throws DocumentException
     *             if an exception occurred
     */
    private Document getDocument(T2FlowExecutablePlanGenerator planGenerator) throws IOException,
        ParserConfigurationException, SAXException, DocumentException {
        StringWriter w = new StringWriter();
        planGenerator.generate(w);

        StringReader r = new StringReader(w.toString());
        SAXReader reader = new SAXReader();
        return reader.read(r);
    }

    /**
     * Returns nodes of the provided document identified by the xPath.
     *
     * @param document
     *            the document
     * @param xPath
     *            the xPath to search
     * @return a list of nodes
     */
    private List<Node> getNodes(Document document, String xPath) {
        XPath x = DocumentHelper.createXPath(xPath);
        x.setNamespaceURIs(T2FLOW_NAMESPACE_MAP);
        return x.selectNodes(document);
    }

    /**
     * Returns a single node of the provided document identified by the xPath.
     *
     * @param document
     *            the document
     * @param xPath
     *            the xPath to search
     * @return a single node or null if none found
     */
    private Node getSingleNode(Document document, String xPath) {
        XPath x = DocumentHelper.createXPath(xPath);
        x.setNamespaceURIs(T2FLOW_NAMESPACE_MAP);
        return x.selectSingleNode(document);
    }

    /**
     * Returns the content of a single node.
     *
     * @param document
     *            the document
     * @param xPath
     *            the xPath to search
     * @return content of a single node or null if none found
     */
    private String getContent(Document document, String xPath) {
        Node n = getSingleNode(document, xPath);
        if (n == null) {
            return null;
        } else {
            return n.getText();
        }
    }

    /**
     * Returns a semantic annotation of a node.
     *
     * @param document
     *            the document
     * @param xPathPrefix
     *            the xPath of the node to search
     * @return the semantic annotation of the node or null if none found
     */
    private String getSemanticAnnotation(Document document, String xPathPrefix) {
        return getContent(
            document,
            xPathPrefix
                + "/t2f:annotations/t2f:annotation_chain_2_2/net.sf.taverna.t2.annotation.AnnotationChainImpl/annotationAssertions/net.sf.taverna.t2.annotation.AnnotationAssertionImpl/annotationBean[@class='net.sf.taverna.t2.annotation.annotationbeans.SemanticAnnotation']/content");
    }

    /**
     * Generates a very simple migration workflow.
     *
     * @param uuid
     *            the UUID of the workflow
     * @param name
     *            the name of the workflow
     * @return the workflow as string
     */
    private String generateMigrationContent(String uuid, String name) {
        // @formatter:off
        return "<workflow xmlns=\"http://taverna.sf.net/2008/xml/t2flow\" version=\"1\" producedBy=\"taverna-2.4.0\">"
            + "<dataflow id=\"" + uuid + "\" role=\"top\">"
            + "<name>" + name + "</name>"
            + "<inputPorts><port><name>source</name><depth>0</depth><granularDepth>0</granularDepth><annotations/></port></inputPorts>"
            + "<outputPorts><port><name>target</name><annotations/></port></outputPorts>"
            + "<processors/>"
            + "<conditions/>"
            + "<datalinks><datalink><sink type=\"dataflow\"><port>target</port></sink><source type=\"dataflow\"><port>source</port></source></datalink></datalinks>"
            + "<annotations><annotation_chain_2_2 encoding=\"xstream\"><null/></annotation_chain_2_2></annotations>" + "</dataflow>" + "</workflow>";
        // @formatter:on
    }

    /**
     * Generates a very simple QA workflow.
     *
     * @param uuid
     *            the UUID of the workflow
     * @param name
     *            the name of the workflow
     * @return the workflow as string
     */
    private String generateQaContent(String uuid, String name) {
        // @formatter:off
        return "<workflow xmlns=\"http://taverna.sf.net/2008/xml/t2flow\" version=\"1\" producedBy=\"taverna-2.4.0\">"
            + "<dataflow id=\"" + uuid + "\" role=\"top\">"
            + "<name>" + name + "</name>"
            + "<inputPorts><port><name>left</name><depth>0</depth><granularDepth>0</granularDepth><annotations/></port><port><name>right</name><depth>0</depth><granularDepth>0</granularDepth><annotations/></port></inputPorts>"
            + "<outputPorts><port><name>qa_output</name><annotations/></port></outputPorts>"
            + "<processors/>"
            + "<conditions/>"
            + "<datalinks><datalink>"
            + "<sink type=\"merge\"><port>qa_output</port></sink><source type=\"dataflow\"><port>left</port></source>"
            + "</datalink><datalink>"
            + "<sink type=\"merge\"><port>qa_output</port></sink><source type=\"dataflow\"><port>right</port></source>"
            + "</datalink></datalinks>"
            + "<annotations><annotation_chain_2_2 encoding=\"xstream\"><null/></annotation_chain_2_2></annotations>"
            + "</dataflow>"
            + "</workflow>";
        // @formatter:on
    }

    /**
     * Generates a very simple CC workflow.
     *
     * @param uuid
     *            the UUID of the workflow
     * @param name
     *            the name of the workflow
     * @return the workflow as string
     */
    private String generateCcContent(String uuid, String name) {
        // @formatter:off
        return "<workflow xmlns=\"http://taverna.sf.net/2008/xml/t2flow\" version=\"1\" producedBy=\"taverna-2.4.0\">"
            + "<dataflow id=\"" + uuid + "\" role=\"top\">"
            + "<name>" + name + "</name>"
            + "<inputPorts><port><name>source</name><depth>0</depth><granularDepth>0</granularDepth><annotations/></port></inputPorts>"
            + "<outputPorts><port><name>cc_output</name><annotations/></port></outputPorts>"
            + "<processors/>"
            + "<conditions/>"
            + "<datalinks><datalink>"
            + "<sink type=\"dataflow\"><port>cc_output</port></sink><source type=\"dataflow\"><port>source</port></source>"
            + "</datalink></datalinks>"
            + "<annotations><annotation_chain_2_2 encoding=\"xstream\"><null/></annotation_chain_2_2></annotations>"
            + "</dataflow>"
            + "</workflow>";
        // @formatter:on
    }

    /**
     * Adds a mock of a migration component to the plan generator.
     *
     * @param planGenerator
     *            the plan generator to user
     */
    private void addMigrationMock(T2FlowExecutablePlanGenerator planGenerator) {
        WorkflowDescription wf = mock(WorkflowDescription.class);
        when(wf.getDataflowId()).thenReturn(MIGRATION_DATAFLOW_ID);

        List<Port> inputPorts = new ArrayList<Port>(1);
        inputPorts.add(new Port("source", "Description", ComponentConstants.VALUE_SOURCE_OBJECT));
        when(wf.getInputPorts()).thenReturn(inputPorts);

        List<Port> outputPorts = new ArrayList<Port>(1);
        outputPorts.add(new Port("target", "Description", ComponentConstants.VALUE_TARGET_OBJECT));
        when(wf.getOutputPorts()).thenReturn(outputPorts);

        List<MigrationPath> migrationPaths = new ArrayList<MigrationPath>(1);
        migrationPaths.add(new MigrationPath("image/tiff", "image/jp2"));
        when(wf.getMigrationPaths()).thenReturn(migrationPaths);

        planGenerator.setMigrationComponent(wf, generateMigrationContent(MIGRATION_DATAFLOW_ID, "Migration component"),
            new HashMap<String, String>(0));
    }

    /**
     * Mock a QA component without accepted mimetypes.
     *
     * @param name
     *            the component name
     * @return a mock of a QA component
     */
    private WorkflowDescription mockQa(String name) {
        WorkflowDescription wf = mock(WorkflowDescription.class);
        when(wf.getName()).thenReturn(name);
        when(wf.getDataflowId()).thenReturn(QA_DATAFLOW_ID);

        List<Port> inputPorts = new ArrayList<Port>(2);
        inputPorts.add(new Port("left", "Description", ComponentConstants.VALUE_LEFT_OBJECT));
        inputPorts.add(new Port("right", "Description", ComponentConstants.VALUE_RIGHT_OBJECT));
        when(wf.getInputPorts()).thenReturn(inputPorts);

        List<Port> outputPorts = new ArrayList<Port>(1);
        outputPorts.add(new Port("qa_output", "Description",
            "http://purl.org/DP/quality/measures#1"));
        when(wf.getOutputPorts()).thenReturn(outputPorts);

        List<String> measures = new ArrayList<String>(1);
        measures.add("http://purl.org/DP/quality/measures#1");

        return wf;
    }

    /**
     * Mock a QA component that accepts any mimetypes.
     *
     * @param name
     *            the component name
     * @return a mock of the QA component
     */
    private WorkflowDescription mockQaAny(String name) {
        WorkflowDescription wf = mockQa(name);
        when(wf.acceptsMimetypes(any(String.class), any(String.class))).thenReturn(true);
        when(wf.acceptsLeftMimetype(any(String.class))).thenReturn(true);
        when(wf.acceptsRightMimetype(any(String.class))).thenReturn(true);
        when(wf.handlesMimetype(any(String.class))).thenReturn(true);
        return wf;
    }

    /**
     * Adds a mock of a CC component to the plan generator.
     *
     * @param name
     *            the component name
     * @return a mock of the CC component
     */
    private WorkflowDescription mockCcAny(String name) {
        WorkflowDescription wf = mock(WorkflowDescription.class);
        when(wf.getName()).thenReturn(name);
        when(wf.getDataflowId()).thenReturn(CC_DATAFLOW_ID);

        List<Port> inputPorts = new ArrayList<Port>(1);
        inputPorts.add(new Port("source", "Description", ComponentConstants.VALUE_SOURCE_OBJECT));
        when(wf.getInputPorts()).thenReturn(inputPorts);

        List<Port> outputPorts = new ArrayList<Port>(1);
        outputPorts.add(new Port("cc_output", "Description",
            "http://purl.org/DP/quality/measures#1"));
        when(wf.getOutputPorts()).thenReturn(outputPorts);

        when(wf.handlesMimetype(any(String.class))).thenReturn(true);
        return wf;
    }
}
TOP

Related Classes of eu.scape_project.planning.services.taverna.generator.T2FlowExecutablePlanGeneratorTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.