blob: 6cac511558d93557cf51753d4dc5ed8b2b6c163c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012-2016 Antonio Garcia-Dominguez.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* Antonio Garcia-Dominguez - initial API and implementation
******************************************************************************/
package org.eclipse.epsilon.eunit.junit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
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.eunit.EUnitModule;
import org.eclipse.epsilon.eunit.EUnitTest;
import org.eclipse.epsilon.eunit.EUnitTestListener;
import org.eclipse.epsilon.eunit.EUnitTestResultType;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.InitializationError;
/**
* <p>
* Bridge runner between EUnit and JUnit. Can be used to turn an EUnit script
* into a JUnit Plug-in Test, for continuous integration.
* </p>
*
* <p>
* This runner should only be used with test classes that implement the
* {@link IEUnitSuite} interface for specifying the URI to the .eunit script and
* reloading the models between tests.
* </p>
*
* <p>
* Unlike previous EUnit test runners that only supported a subset of EUnit,
* this runner supports every feature in EUnit except for those specific to Ant,
* such as runTarget, useVariable, exportVariable and loadHutn.
* </p>
*
* <p>
* Users can provide additional built-in operations for their tests by implementing
* the {@link IEUnitSuite#getOperationContributor()} method appropriately. This can
* be useful when invoking EVL code, for instance, as we can't access the Ant tasks
* from a JUnit plugin test.
* </p>
*
* TODO: integrate the EOL debugger somehow.
*
* TODO: add support for running Ant targets from a previously specified Ant
* script, in order to reuse the workflow tasks. We could get back most of the
* missing functionality this way. The main problem is that this would probably
* require copying over the original project to the runtime workspace, and that
* could complicate things quite a bit.
*/
public class EUnitTestRunner extends ParentRunner<EUnitTest> {
protected class JUnitEUnitTestListener implements EUnitTestListener {
private final RunNotifier notifier;
private JUnitEUnitTestListener(RunNotifier notifier) {
this.notifier = notifier;
}
@Override
public void beforeCase(EUnitModule module, EUnitTest test) {
final Description desc = describeChild(test);
notifier.fireTestStarted(desc);
if (test.getResult().equals(EUnitTestResultType.SKIPPED)) {
notifier.fireTestIgnored(desc);
}
if (test.isLeafTest()) {
final ModelRepository repository = module.getContext().getModelRepository();
repository.dispose();
try {
final List<IModel> models = suiteInstance.prepareModels();
if (models != null) {
for (IModel model : models) {
repository.addModel(model);
}
}
} catch (Exception e) {
test.setResult(EUnitTestResultType.ERROR);
test.setException(e);
}
}
}
@Override
public void afterCase(EUnitModule module, EUnitTest test) {
final Description desc = describeChild(test);
if (test.isLeafTest()) {
EUnitTestResultType res = test.getResult();
if (res.equals(EUnitTestResultType.FAILURE) || res.equals(EUnitTestResultType.ERROR))
notifier.fireTestFailure(new Failure(desc, test.getException()));
}
notifier.fireTestFinished(desc);
}
}
private Map<EUnitTest, Description> testDescriptions = new IdentityHashMap<>();
protected EUnitModule module;
protected IEUnitSuite suiteInstance;
public EUnitTestRunner(Class<? extends IEUnitSuite> testClass) throws InitializationError {
super(testClass);
try {
suiteInstance = testClass.newInstance();
module = new EUnitModule();
module.parse(suiteInstance.getModuleURI());
final OperationContributor contrib = suiteInstance.getOperationContributor();
if (contrib != null) {
module.getContext().getOperationContributorRegistry().add(contrib);
}
} catch (Exception e) {
e.printStackTrace();
throw new InitializationError(e);
}
}
@Override
protected List<EUnitTest> getChildren() {
try {
return Arrays.asList(module.getSuiteRoot());
} catch (EolRuntimeException e) {
e.printStackTrace();
return new ArrayList<>();
}
}
@Override
protected Description describeChild(EUnitTest child) {
if (testDescriptions.containsKey(child)) {
return testDescriptions.get(child);
}
Description desc;
if (child.isRootTest()) {
desc = Description.createSuiteDescription(this.getTestClass().getJavaClass());
} else {
desc = Description.createTestDescription(this.getTestClass().getJavaClass(), child.getCaseName());
}
for (EUnitTest c : child.getChildren()) {
desc.addChild(describeChild(c));
}
testDescriptions.put(child, desc);
return desc;
}
@Override
protected void runChild(final EUnitTest child, final RunNotifier notifier) {
final Description thisDesc = describeChild(child);
// We only need to launch the root EUnitTest from JUnit: EUnit will take
// care of the rest
if (child.isRootTest()) {
try {
final JUnitEUnitTestListener listener = new JUnitEUnitTestListener(notifier);
module.addTestListener(listener);
module.execute();
module.removeTestListener(listener);
} catch (EolRuntimeException e) {
notifier.fireTestFailure(new Failure(thisDesc, e));
}
}
}
}