/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.testutils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import de.rcenvironment.core.communication.common.LogicalNodeId;
import de.rcenvironment.core.communication.common.NodeIdentifierTestUtils;
import de.rcenvironment.core.component.datamanagement.api.ComponentHistoryDataItem;
import de.rcenvironment.core.component.execution.api.Component;
import de.rcenvironment.core.component.execution.api.ComponentContext;
import de.rcenvironment.core.datamodel.api.DataType;
import de.rcenvironment.core.datamodel.api.EndpointCharacter;
import de.rcenvironment.core.datamodel.api.TypedDatum;
import de.rcenvironment.core.datamodel.api.TypedDatumService;
import de.rcenvironment.core.datamodel.testutils.TypedDatumServiceDefaultStub;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.toolkit.utils.common.AutoCreationMap;
/**
* Placeholder implementation of {@link ComponentContext} for writing {@link Component} integration tests.
*
* Individual tests may subclass it to add additional featured. Code that can be useful for other tests should be merged into this base
* class.
*
* @author Robert Mischke
* @author Doreen Seider
*/
public class ComponentContextMock extends ComponentContextDefaultStub {
private static final long serialVersionUID = -8621047700219054161L;
private final Map<String, String> testConfig = new HashMap<>();
private final Map<String, SimulatedEndpoint> simulatedInputs = new HashMap<>();
private final Map<String, SimulatedEndpoint> simulatedOutputs = new HashMap<>();
private Map<String, TypedDatum> inputValues;
private AutoCreationMap<String, List<TypedDatum>> capturedOutputValues;
private List<String> capturedOutputClosings;
private List<String> capturedOutputResets;
private final TypedDatumService typedDatumService;
private final Map<Class<?>, Object> services = new HashMap<>();
private int executionCount;
private final LogicalNodeId node = NodeIdentifierTestUtils.createTestInstanceNodeSessionIdWithDisplayName("ComponentContextMock")
.convertToDefaultLogicalNodeId();
private ComponentHistoryDataItem historyDataItem;
private Set<String> inputsNotConnected = new HashSet<String>();
/**
* Defines a dynamic or static endpoint of the component.
*
* @author Robert Mischke
* @author Sascha Zur
*/
private final class SimulatedEndpoint {
private final String endpointId;
private final DataType dataType;
private final boolean isDynamic;
private final Map<String, String> metaData;
private final EndpointCharacter endpointCharacter;
SimulatedEndpoint(String endpointId, DataType dataType, boolean isDynamic, Map<String, String> metaData,
EndpointCharacter endpointCharacter) {
this.endpointId = endpointId;
this.dataType = dataType;
this.isDynamic = isDynamic;
if (metaData != null) {
this.metaData = metaData;
} else {
this.metaData = new HashMap<>();
}
this.endpointCharacter = endpointCharacter;
}
private String getEndpointId() {
return endpointId;
}
private DataType getDataType() {
return dataType;
}
private boolean isDynamic() {
return isDynamic;
}
private Map<String, String> getMetaData() {
return metaData;
}
private EndpointCharacter getEndpointCharacter() {
return endpointCharacter;
}
}
/**
* Utility class that automatically creates a List<TypedDatum> for every new output id to contain the captured {@link TypedDatum}s.
*
* @author Robert Mischke
*/
private final class OutputCaptureMap extends AutoCreationMap<String, List<TypedDatum>> {
@Override
protected List<TypedDatum> createNewEntry(String key) {
return new ArrayList<>();
}
}
public ComponentContextMock() {
// hard-coded common service
typedDatumService = new TypedDatumServiceDefaultStub();
services.put(TypedDatumService.class, typedDatumService);
// initialize holders
resetInputData();
resetOutputData();
}
/**
* Method for adding a needed service.
*
* @param <T> generic parameter
* @param clazz name of the original service
* @param serviceStub stub of the service
*/
public <T> void addService(Class<T> clazz, Object serviceStub) {
services.put(clazz, serviceStub);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getService(Class<T> clazz) {
return (T) services.get(clazz);
}
@Override
public LogicalNodeId getNodeId() {
return node;
}
@Override
public String getConfigurationValue(String key) {
return testConfig.get(key);
}
@Override
public Set<String> getConfigurationKeys() {
return testConfig.keySet();
}
@Override
public Set<String> getInputs() {
return new HashSet<>(simulatedInputs.keySet());
}
@Override
public Set<String> getOutputs() {
return new HashSet<>(simulatedOutputs.keySet());
}
@Override
public void closeAllOutputs() {
for (String outputName : simulatedOutputs.keySet()) {
capturedOutputClosings.add(outputName);
}
}
@Override
public void closeOutput(String outputName) {
capturedOutputClosings.add(outputName);
}
@Override
public void resetOutput(String outputName) {
capturedOutputResets.add(outputName);
}
@Override
public Set<String> getInputsWithDatum() {
return new HashSet<String>(inputValues.keySet());
}
@Override
public DataType getInputDataType(String inputName) {
return simulatedInputs.get(inputName).getDataType();
}
@Override
public DataType getOutputDataType(String outputName) {
return simulatedOutputs.get(outputName).getDataType();
}
@Override
public String getOutputMetaDataValue(String outputName, String metaDataKey) {
return simulatedOutputs.get(outputName).getMetaData().get(metaDataKey);
}
@Override
public String getInputMetaDataValue(String inputName, String metaDataKey) {
return simulatedInputs.get(inputName).getMetaData().get(metaDataKey);
}
@Override
public void writeOutput(String outputName, TypedDatum value) {
if (simulatedOutputs.get(outputName) == null) {
throw new RuntimeException(StringUtils.format("Output \"%s\" is not defined.", outputName));
}
if (value.getDataType() != simulatedOutputs.get(outputName).getDataType() && !value.getDataType().equals(DataType.NotAValue)
&& !typedDatumService.getConverter().isConvertibleTo(value, simulatedOutputs.get(outputName).getDataType())) {
throw new RuntimeException(StringUtils.format("DataType %s of given value not convertible to defined output DataType %s",
value.getDataType(), simulatedOutputs.get(outputName).getDataType()));
}
capturedOutputValues.get(outputName).add(value);
}
@Override
public boolean isDynamicInput(String inputName) {
return simulatedInputs.get(inputName).isDynamic();
}
@Override
public String getDynamicOutputIdentifier(String outputName) {
return simulatedOutputs.get(outputName).getEndpointId();
}
@Override
public String getDynamicInputIdentifier(String inputName) {
return simulatedInputs.get(inputName).getEndpointId();
}
@Override
public TypedDatum readInput(String key) {
return inputValues.get(key);
}
@Override
public int getExecutionCount() {
return executionCount;
}
/**
* Adds a key/value pair to the component's configuration. Should be called only before starting the component.
*
* @param key entry key
* @param value entry value
*/
public void setConfigurationValue(String key, String value) {
testConfig.put(key, value);
}
/**
* Defines/adds a simulated input endpoint for the component. Should be called only before starting the component.
*
* @param name the name of the input (as visible to the user in actual workflows)
* @param endpointId the internal endpoint ID
* @param dataType the {@link DataType} of the input
* @param isDynamic true for a dynamic input, false for a static one
* @param metaData the metadata map of the intput; can be null as a shortcut for an empty map
*/
public void addSimulatedInput(String name, String endpointId, DataType dataType, boolean isDynamic, Map<String, String> metaData) {
simulatedInputs.put(name,
new SimulatedEndpoint(endpointId, dataType, isDynamic, metaData, EndpointCharacter.SAME_LOOP));
}
/**
* Defines/adds a simulated input endpoint for the component. Should be called only before starting the component.
*
* @param name the name of the input (as visible to the user in actual workflows)
* @param endpointId the internal endpoint ID
* @param dataType the {@link DataType} of the input
* @param isDynamic true for a dynamic input, false for a static one
* @param metaData the metadata map of the intput; can be null as a shortcut for an empty map
* @param inputCharacter {@link EndpointCharacter} of the input
*/
public void addSimulatedInput(String name, String endpointId, DataType dataType, boolean isDynamic, Map<String, String> metaData,
EndpointCharacter inputCharacter) {
simulatedInputs.put(name, new SimulatedEndpoint(endpointId, dataType, isDynamic, metaData, inputCharacter));
}
/**
* Defines/adds a simulated output endpoint for the component. Should be called only before starting the component.
*
* @param name the name of the output (as visible to the user in actual workflows)
* @param endpointId the internal endpoint ID
* @param dataType the {@link DataType} of the input
* @param isDynamic true for a dynamic output, false for a static one
* @param metaData the metadata map of the output; can be null as a shortcut for an empty map
*/
public void addSimulatedOutput(String name, String endpointId, DataType dataType, boolean isDynamic, Map<String, String> metaData) {
simulatedOutputs.put(name,
new SimulatedEndpoint(endpointId, dataType, isDynamic, metaData, EndpointCharacter.SAME_LOOP));
}
/**
* Defines/adds a simulated output endpoint for the component. Should be called only before starting the component.
*
* @param name the name of the output (as visible to the user in actual workflows)
* @param endpointId the internal endpoint ID
* @param dataType the {@link DataType} of the input
* @param isDynamic true for a dynamic output, false for a static one
* @param metaData the metadata map of the output; can be null as a shortcut for an empty map
* @param outputCharacter {@link EndpointCharacter} of the input
*/
public void addSimulatedOutput(String name, String endpointId, DataType dataType, boolean isDynamic, Map<String, String> metaData,
EndpointCharacter outputCharacter) {
simulatedOutputs.put(name, new SimulatedEndpoint(endpointId, dataType, isDynamic, metaData, outputCharacter));
}
/**
* Clears all remaining input values and all captured output values.
*
* @deprecated Should not be needed anymore when using ComponentTestWrapper
*/
@Deprecated
public void resetEndpointData() {
resetInputData();
resetOutputData();
}
/**
* Sets an input value for future consumption by the component. Only one input can be set per input; existing values are overwritten.
*
* @param inputName the name of the input
* @param datum the TypedDatum to "send"
*/
public void setInputValue(String inputName, TypedDatum datum) {
inputValues.put(inputName, datum);
}
/**
* Returns a list of all captured {@link TypedDatum}s sent to the given output by the component. If no output was generated, an empty
* list is returned.
*
* @param outputName the name if the output
* @return a list of the captured outputs; may be empty, but not null
*/
public List<TypedDatum> getCapturedOutput(String outputName) {
return capturedOutputValues.get(outputName);
}
/**
* Returns a list of all output names, which were closed. If no output was closed, an empty list is returned.
*
* @return a list of the captured outputs, which were closed; may be empty, but not <code>null</code>
*/
public List<String> getCapturedOutputClosings() {
return capturedOutputClosings;
}
/**
* Returns a list of all output names, which were reset. If no output was reset, an empty list is returned.
*
* @return a list of the captured outputs, which were reset; may be empty, but not <code>null</code>
*/
public List<String> getCapturedOutputResets() {
return capturedOutputResets;
}
/**
* Clears all defined input data; automatically called by {@link ComponentTestWrapper}.
*/
protected void resetInputData() {
inputValues = new HashMap<>();
}
/**
* Clears all captured output data; automatically called by {@link ComponentTestWrapper}.
*/
protected void resetOutputData() {
capturedOutputValues = new OutputCaptureMap();
}
/**
* Clears all captured output closings; automatically called by {@link ComponentTestWrapper}.
*/
protected void resetOutputClosings() {
capturedOutputClosings = new ArrayList<>();
}
/**
* Clears all captured output resets; automatically called by {@link ComponentTestWrapper}.
*/
protected void resetOutputResets() {
capturedOutputResets = new ArrayList<>();
}
/**
* Increments the execution counter; automatically called by {@link ComponentTestWrapper}.
*/
protected void incrementExecutionCount() {
executionCount++;
}
@Override
public void writeFinalHistoryDataItem(ComponentHistoryDataItem newHistoryDataItem) {
this.historyDataItem = newHistoryDataItem;
}
public ComponentHistoryDataItem getHistoryDataItem() {
return historyDataItem;
}
@Override
public boolean isDynamicOutput(String outputName) {
return simulatedOutputs.get(outputName).isDynamic();
}
@Override
public Set<String> getInputsNotConnected() {
return inputsNotConnected;
}
@Override
public EndpointCharacter getInputCharacter(String inputName) {
return simulatedInputs.get(inputName).getEndpointCharacter();
}
@Override
public EndpointCharacter getOutputCharacter(String outputName) {
return simulatedOutputs.get(outputName).getEndpointCharacter();
}
/**
* Adds an input that should not be connected.
*
* @param name of input
*/
public void addInputNotConnected(String name) {
inputsNotConnected.add(name);
}
}