blob: bf124ddea758c8e53619c752c88ac88f86c02e90 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2011 The University of York.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Dimitris Kolovos - initial API and implementation
* Antonio Garcia-Dominguez - rewrite with nested Ant task support
******************************************************************************/
package org.eclipse.epsilon.workflow.tasks;
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
import org.eclipse.epsilon.eol.IEolExecutableModule;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.execute.context.IEolContext;
import org.eclipse.epsilon.eol.execute.context.Variable;
import org.eclipse.epsilon.eol.execute.operations.contributors.OperationContributor;
import org.eclipse.epsilon.eol.models.IModel;
import org.eclipse.epsilon.eol.models.ModelRepository;
import org.eclipse.epsilon.eol.types.EolAnyType;
import org.eclipse.epsilon.eol.types.EolClasspathNativeTypeDelegate;
import org.eclipse.epsilon.eol.types.EolNoType;
import org.eclipse.epsilon.eunit.EUnitModule;
import org.eclipse.epsilon.eunit.EUnitTest;
import org.eclipse.epsilon.eunit.EUnitTestListener;
import org.eclipse.epsilon.eunit.EUnitTestResultType;
import org.eclipse.epsilon.workflow.tasks.hosts.HostManager;
/**
* Ant task for running EUnit test suites.
*
* @author Antonio Garcia-Dominguez
* @version 1.0
*/
public class EUnitTask extends ExecutableModuleTask implements EUnitTestListener {
/**
* Class for a nested element which simply contains tasks.
*/
public class TaskCollection implements TaskContainer {
private List<Task> tasks = new ArrayList<Task>();
public void addTask(Task task) {
tasks.add(task);
if (task instanceof ExecutableModuleTask) {
ExecutableModuleTask moduleTask = (ExecutableModuleTask)task;
// The gui attribute of the EUnit Ant task is inherited by all nested tasks
moduleTask.setGUI(isGUI());
}
}
public void run() {
// We trick tasks into using the EUnit model repository instead of the project's
for (Task task : tasks) {
task.perform();
}
}
}
public class RunTargetOperationContributor extends OperationContributor {
@Override
public boolean contributesTo(Object target) {
return EolNoType.NoInstance.equals(target);
}
/**
* Operation which can call a series of Ant tasks described inside a
* "script" nested element of this Ant task.
*/
public void runTarget(String targetName) throws EolRuntimeException {
// Check that the name of the target is not null
if (targetName == null) {
throw new EolRuntimeException("The name of the target to be run cannot be null");
}
// Run tasks, ensuring they manipulate our model repository instead of the project's
getProject().executeTarget(targetName);
}
/**
* EUnit-specific operation which is equivalent to the "exports" nested element.
*/
public void exportVariable(String varName) {
EUnitTask.this.exportVariable(varName, varName, false, false);
}
/**
* EUnit-specific operation which is equivalent to the "imports" nested element.
*/
public void useVariable(String varName) {
EUnitTask.this.useVariable(varName, varName, false, false);
}
/**
* EUnit-specific operation for loading models inside the .eunit file
* from HUTN fragments.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public void loadHutn(String modelName, String hutnContent) throws EolModelLoadingException {
IModel hutnModel = null;
try {
final Class hutnModelClass = Class.forName("org.eclipse.epsilon.emc.hutn.HutnModel");
final Constructor constructor = hutnModelClass.getConstructor(String.class, String.class);
hutnModel = (IModel)constructor.newInstance(modelName, hutnContent);
hutnModel.load();
ModelRepository modelRepository = module.getContext().getModelRepository();
modelRepository.addModel(hutnModel);
} catch (Exception ex) {
throw new EolModelLoadingException(ex, hutnModel);
}
}
}
private File fReportDirectory;
private String fPackage = EUnitModule.DEFAULT_PACKAGE;
private boolean fGenerateReport = true;
private TaskCollection modelLoadingTasks;
private ModelRepository oldProjectRepository;
public EUnitTask() {
// By default, the EUnit Ant task disables JFace-based user input,
// which hinders automated testing
setGUI(false);
}
@Override
protected void initialize() throws Exception {
final EUnitModule eunitModule = (EUnitModule)module;
eunitModule.addTestListener(this);
eunitModule.setPackage(getPackage());
if (getToDir() != null) {
eunitModule.setReportDirectory(getToDir());
}
else if (isReport()) {
eunitModule.setReportDirectory(getProject().getBaseDir());
}
else {
eunitModule.setReportDirectory(null);
}
HostManager.getHost().addNativeTypeDelegates(eunitModule);
final List<EUnitTestListener> testListeners
= HostManager.getHost().getExtensionsOfType(EUnitTestListener.class);
for (EUnitTestListener listener : testListeners) {
eunitModule.addTestListener(listener);
}
}
@Override
protected void examine() throws Exception {
final EUnitTest test = ((EUnitModule)createModule()).getSuiteRoot();
final PrintStream out = module.getContext().getOutputStream();
out.println("Global result: " + test.getResult());
if (test.getResult() == EUnitTestResultType.FAILURE || test.getResult() == EUnitTestResultType.ERROR) {
fail("At least one test case had a failure or an error: " + test.getException().getMessage(), test.getException());
}
}
@Override
public IEolExecutableModule createModule() {
// We store the created module, so the EUnit view can call this,
// register itself as a listener, and then let EUnitTask configure
// it as usual
if (module == null) {
module = new EUnitModule();
final IEolContext context = module.getContext();
context.getOperationContributorRegistry().add(new RunTargetOperationContributor());
context.getFrameStack().put(
new Variable("antProject", getProject(), new EolAnyType(), true));
// Replace the default native type delegate (which uses the Eclipse class loader) with
// one which uses the Ant classpath, as customized by the user
final ClassLoader classLoaderAnt = getProject().createClassLoader(org.apache.tools.ant.types.Path.systemClasspath);
context.getNativeTypeDelegates().clear();
context.getNativeTypeDelegates().add(new EolClasspathNativeTypeDelegate(classLoaderAnt));
}
return module;
}
// TEST LISTENER METHODS
public void beforeCase(EUnitModule module, EUnitTest test) {
if (test.isRootTest()) {
// Disable notification through dialogs: it's bad for automated test cases.
// Use the console instead.
HostManager.getHost().configureUserInput(module, false);
}
if (test.isLeafTest()) {
try {
// Dispose all models in this module's model repository, and reload them from the <model> references
populateModelRepository(true);
} catch (Exception e) {
fail("Exception while repopulating the model repository", e);
}
// We need to trick the other Ant tasks into loading models into this module's repository.
// These may be run from <modelTasks> or from the @model operations
oldProjectRepository = getProjectRepository();
setProjectRepository(module.getContext().getModelRepository());
// Run the <modelTasks>
if (modelLoadingTasks != null) {
modelLoadingTasks.run();
}
}
}
public void afterCase(EUnitModule module, EUnitTest test) {
// Restore the original model repository for the project after running the test
if (test.isLeafTest()) {
setProjectRepository(oldProjectRepository);
module.getContext().getModelRepository().dispose();
}
final PrintStream out = module.getContext().getOutputStream();
final PrintStream err = module.getContext().getErrorStream();
final String sMillis = String.format(" [cpu: %d ms, wallclock: %d ms]", test.getCpuTimeMillis(), test.getWallclockTimeMillis());
final String testDescription = "Test " + test.getMethodName() + " {" + test.explainAllBindings() + "}";
if (test.getResult() == EUnitTestResultType.SUCCESS) {
out.println(testDescription + " passed" + sMillis);
} else if (test.getResult() == EUnitTestResultType.SKIPPED){
out.println(testDescription + " skipped" + sMillis);
} else {
err.print(testDescription + " failed with status " + test.getResult());
final Exception testException = test.getException();
if (testException != null) {
err.println(": " + testException.getMessage());
}
else {
err.println();
}
}
}
// NESTED ELEMENTS
public TaskCollection createModelTasks() {
if (modelLoadingTasks == null) {
modelLoadingTasks = new TaskCollection();
}
return modelLoadingTasks;
}
// TEST REPORT METHODS
/**
* Returns the destination directory for the JUnit-like report. By default,
* it is the base directory of the Ant project.
*/
public File getToDir() {
return fReportDirectory;
}
/**
* Changes the destination directory for the JUnit-like report. See {@link #getToDir()} for the default value.
*/
public void setToDir(File f) {
fReportDirectory = f;
}
/**
* Returns the package in which all tests will be contained. By default, it
* is set to {@link EUnitModule#DEFAULT_PACKAGE}.
*/
public String getPackage() {
return fPackage ;
}
/**
* Changes the package in which all tests will be contained. Empty or null arguments are <b>ignored</b>.
*/
public void setPackage(String packageName) {
fPackage = packageName;
}
/**
* Returns <code>true</code> if a XML report compatible with the &lt;junit&gt; Ant task should be generated.
*/
public boolean isReport() {
return fGenerateReport;
}
/**
* Changes whether an XML report compatible with the &lt;junit&gt; Ant task should be generated. By default,
* it will be generated.
* @param generate If <code>true</code>, the XML report will be generated. Otherwise, it will not be generated.
*/
public void setReport(boolean generate) {
this.fGenerateReport = generate;
}
}