/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.testdriver;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.asakusafw.runtime.core.Report;
import com.asakusafw.testdriver.compiler.CompilerConfiguration.DebugLevel;
import com.asakusafw.testdriver.compiler.CompilerConfiguration.OptimizeLevel;
import com.asakusafw.testdriver.core.TestDataToolProvider;
import com.asakusafw.testdriver.core.TestingEnvironmentConfigurator;
import com.asakusafw.trace.model.TraceSetting;
import com.asakusafw.trace.model.TraceSetting.Mode;
import com.asakusafw.trace.model.TraceSettingList;
import com.asakusafw.trace.model.Tracepoint;
import com.asakusafw.trace.model.Tracepoint.PortKind;
import com.asakusafw.vocabulary.flow.FlowDescription;
import com.asakusafw.vocabulary.flow.FlowPart;
/**
* An abstract super class of test-driver classes.
* @since 0.2.0
* @version 0.7.0
*/
public abstract class TestDriverBase extends DriverElementBase {
private static final String FLOW_OPERATOR_FACTORY_METHOD_NAME = "create"; //$NON-NLS-1$
static {
TestingEnvironmentConfigurator.initialize();
}
/**
* The internal test driver context object.
*/
protected final TestDriverContext driverContext;
/**
* Creates a new instance.
* @param callerClass the caller class
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public TestDriverBase(Class<?> callerClass) {
if (callerClass == null) {
throw new IllegalArgumentException("callerClass must not be null"); //$NON-NLS-1$
}
this.driverContext = new TestDriverContext(callerClass);
}
@Override
protected final Class<?> getCallerClass() {
return driverContext.getCallerClass();
}
@Override
protected final TestDataToolProvider getTestTools() {
return driverContext.getRepository();
}
/**
* Adds a runtime configuration item.
* This may customize behavior of some framework APIs (e.g. {@link Report report API}).
* @param key the configuration key name
* @param value the configuration value, or {@code null} to unset the target configuration
* @throws IllegalArgumentException if {@code key} is {@code null}
*/
public void configure(String key, String value) {
if (key == null) {
throw new IllegalArgumentException("key must not be null"); //$NON-NLS-1$
}
if (value != null) {
driverContext.getExtraConfigurations().put(key, value);
} else {
driverContext.getExtraConfigurations().remove(key);
}
}
/**
* Adds a batch argument.
* @param key the argument name
* @param value the argument value, or {@code null} to unset the target argument
* @throws IllegalArgumentException if {@code key} is {@code null}
*/
public void setBatchArg(String key, String value) {
if (key == null) {
throw new IllegalArgumentException("key must not be null"); //$NON-NLS-1$
}
if (value != null) {
driverContext.getBatchArgs().put(key, value);
} else {
driverContext.getBatchArgs().remove(key);
}
}
/**
* Configures the compiler optimization level.
* <ul>
* <li> 0: disables all optimizations </li>
* <li> 1: only enables default optimizations </li>
* <li> 2~: enables aggressive optimizations </li>
* </ul>
* @param level the compiler optimization level
*/
public void setOptimize(int level) {
if (level <= 0) {
driverContext.setCompilerOptimizeLevel(OptimizeLevel.DISABLED);
} else if (level == 1) {
driverContext.setCompilerOptimizeLevel(OptimizeLevel.NORMAL);
} else {
driverContext.setCompilerOptimizeLevel(OptimizeLevel.AGGRESSIVE);
}
}
/**
* Sets whether compiler should keep debugging information or not.
* @param enable {@code true} to keep debugging information, otherwise {@code false}
*/
public void setDebug(boolean enable) {
if (enable) {
driverContext.setCompilerDebugLevel(DebugLevel.NORMAL);
} else {
driverContext.setCompilerDebugLevel(DebugLevel.DISABLED);
}
}
/**
* Sets an extra compiler option.
* @param name the option name
* @param value the option value
* @since 0.7.3
*/
public void setExtraCompilerOption(String name, String value) {
Objects.requireNonNull(name);
if (value == null) {
driverContext.getCompilerOptions().remove(name);
} else {
driverContext.getCompilerOptions().put(name, value);
}
}
/**
* Sets the Asakusa framework installation path ({@literal a.k.a.} {@code $ASAKUSA_HOME}).
* If this is not set, the installation path will be computed from the environment variable.
* @param frameworkHomePath the framework installation path
*/
public void setFrameworkHomePath(File frameworkHomePath) {
driverContext.setFrameworkHomePath(frameworkHomePath);
}
/**
* Sets the search path of the external library files.
* If this is not set, the search path will be {@link TestDriverContext#EXTERNAL_LIBRARIES_PATH}.
* @param librariesPath the search path of the external library files
* @since 0.5.1
*/
public void setLibrariesPath(File librariesPath) {
driverContext.setLibrariesPath(librariesPath);
}
/**
* Sets the explicit compiler working directory.
* If this is not set, the compiler will create the working directory into the temporary area,
* and remove it after test was finished.
* @param path the explicit compiler working directory
* @since 0.5.2
*/
public void setCompilerWorkingDirectory(File path) {
driverContext.setCompilerWorkingDirectory(path);
}
/**
* Sets whether skips verifying test conditions.
* @param skip {@code true} if verifying test conditions, otherwise {@code false}
* @since 0.7.0
*/
public void skipValidateCondition(boolean skip) {
driverContext.setSkipValidateCondition(skip);
}
/**
* Sets whether skips truncating test input data.
* @param skip {@code true} if truncating test input data, otherwise {@code false}
*/
public void skipCleanInput(boolean skip) {
driverContext.setSkipCleanInput(skip);
}
/**
* Sets whether skips truncating test output data.
* @param skip {@code true} if truncating test output data, otherwise {@code false}
*/
public void skipCleanOutput(boolean skip) {
driverContext.setSkipCleanOutput(skip);
}
/**
* Sets whether skips preparing test input data.
* @param skip {@code true} if preparing test input data, otherwise {@code false}
*/
public void skipPrepareInput(boolean skip) {
driverContext.setSkipPrepareInput(skip);
}
/**
* Sets whether skips preparing test output data.
* @param skip {@code true} if preparing test output data, otherwise {@code false}
*/
public void skipPrepareOutput(boolean skip) {
driverContext.setSkipPrepareOutput(skip);
}
/**
* Sets whether skips executing jobflows.
* @param skip {@code true} if executing jobflows, otherwise {@code false}
*/
public void skipRunJobflow(boolean skip) {
driverContext.setSkipRunJobflow(skip);
}
/**
* Sets whether skips verifying test results.
* @param skip {@code true} if verifying test results, otherwise {@code false}
*/
public void skipVerify(boolean skip) {
driverContext.setSkipVerify(skip);
}
/**
* Adds a new trace-point to the target operator input.
* @param operatorClass target operator class
* @param methodName target operator method name
* @param portName target operator input port name
* @throws IllegalArgumentException if some parameters were {@code null}
* @since 0.5.1
*/
public void addInputTrace(Class<?> operatorClass, String methodName, String portName) {
if (operatorClass == null) {
throw new IllegalArgumentException("operatorClass must not be null"); //$NON-NLS-1$
}
if (methodName == null) {
throw new IllegalArgumentException("methodName must not be null"); //$NON-NLS-1$
}
if (portName == null) {
throw new IllegalArgumentException("portName must not be null"); //$NON-NLS-1$
}
TraceSetting setting = createTraceSetting(
operatorClass, methodName,
PortKind.INPUT, portName,
Collections.emptyMap());
appendTrace(setting);
}
/**
* Adds a new trace-point to the target operator output.
* @param operatorClass target operator class
* @param methodName target operator method name
* @param portName target operator input port name
* @throws IllegalArgumentException if some parameters were {@code null}
* @since 0.5.1
*/
public void addOutputTrace(Class<?> operatorClass, String methodName, String portName) {
if (operatorClass == null) {
throw new IllegalArgumentException("operatorClass must not be null"); //$NON-NLS-1$
}
if (methodName == null) {
throw new IllegalArgumentException("methodName must not be null"); //$NON-NLS-1$
}
if (portName == null) {
throw new IllegalArgumentException("portName must not be null"); //$NON-NLS-1$
}
TraceSetting setting = createTraceSetting(
operatorClass, methodName,
PortKind.OUTPUT, portName,
Collections.emptyMap());
appendTrace(setting);
}
/**
* Adds a new trace-point to the target operator input.
* @param flowpartClass target flow-part class
* @param portName target operator input port name
* @throws IllegalArgumentException if some parameters were {@code null}
* @deprecated some platform does not support tracing flow-part I/O;
* please use {@link #addInputTrace(Class, String, String)} instead
* @since 0.5.1
*/
@Deprecated
public void addInputTrace(Class<? extends FlowDescription> flowpartClass, String portName) {
if (flowpartClass == null) {
throw new IllegalArgumentException("operatorClass must not be null"); //$NON-NLS-1$
}
if (portName == null) {
throw new IllegalArgumentException("portName must not be null"); //$NON-NLS-1$
}
checkFlowpart(flowpartClass);
TraceSetting setting = createTraceSetting(
flowpartClass, FLOW_OPERATOR_FACTORY_METHOD_NAME,
PortKind.INPUT, portName,
Collections.emptyMap());
appendTrace(setting);
}
/**
* Adds a new trace-point to the target operator output.
* @param flowpartClass target flow-part class
* @param portName target operator input port name
* @throws IllegalArgumentException if some parameters were {@code null}
* @deprecated some platform does not support tracing flow-part I/O;
* please use {@link #addOutputTrace(Class, String, String)} instead
* @since 0.5.1
*/
@Deprecated
public void addOutputTrace(Class<? extends FlowDescription> flowpartClass, String portName) {
if (flowpartClass == null) {
throw new IllegalArgumentException("operatorClass must not be null"); //$NON-NLS-1$
}
if (portName == null) {
throw new IllegalArgumentException("portName must not be null"); //$NON-NLS-1$
}
checkFlowpart(flowpartClass);
TraceSetting setting = createTraceSetting(
flowpartClass, FLOW_OPERATOR_FACTORY_METHOD_NAME,
PortKind.OUTPUT, portName,
Collections.emptyMap());
appendTrace(setting);
}
private void checkFlowpart(Class<? extends FlowDescription> flowpartClass) {
if (flowpartClass.isAnnotationPresent(FlowPart.class) == false) {
throw new IllegalArgumentException(MessageFormat.format(
Messages.getString("TestDriverBase.errorInvalidFlowpartClass"), //$NON-NLS-1$
flowpartClass.getName()));
}
}
private void appendTrace(TraceSetting setting) {
assert setting != null;
List<TraceSetting> elements = new ArrayList<>();
TraceSettingList list = driverContext.getExtension(TraceSettingList.class);
if (list != null) {
elements.addAll(list.getElements());
}
elements.add(setting);
driverContext.putExtension(TraceSettingList.class, new TraceSettingList(elements));
}
static TraceSetting createTraceSetting(
Class<?> operatorClass,
String methodName,
PortKind portKind,
String portName,
Map<String, String> attributes) {
assert operatorClass != null;
assert methodName != null;
assert portKind != null;
assert portName != null;
assert attributes != null;
return new TraceSetting(
new Tracepoint(operatorClass.getName(), methodName, portKind, portName),
Mode.STRICT, attributes);
}
/**
* Sets the {@link JobExecutorFactory} for executing jobs in this test.
* @param factory the factory, or {@code null} to use a default implementation
* @see TestDriverContext#KEY_JOB_EXECUTOR_FACTORY
* @since 0.6.0
*/
public void setJobExecutorFactory(JobExecutorFactory factory) {
driverContext.setJobExecutorFactory(factory);
}
/**
* Returns the current test driver context (for internal use only).
* @return the current test driver context
* @since 0.6.1
*/
TestDriverContext getDriverContext() {
return driverContext;
}
}