package org.xtest; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.validation.CheckMode; import org.eclipse.xtext.validation.CheckType; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.interpreter.IEvaluationContext; import org.eclipse.xtext.xbase.interpreter.IEvaluationResult; import org.eclipse.xtext.xbase.interpreter.impl.InterpreterCanceledException; import org.xtest.interpreter.XTestInterpreter; import org.xtest.interpreter.XtestEvaluationResult; import org.xtest.results.XTestResult; import org.xtest.xTest.Body; import com.google.common.collect.Sets; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; /** * Entry point for running an xtest script * * @author Michael Barry */ @Singleton @SuppressWarnings("restriction") public class XTestRunner { /** * {@link CheckMode} that validates the file without running tests */ public static final CheckMode CHECK_BUT_DONT_RUN = new DontRunCheck(); @Inject private Provider<IEvaluationContext> contextProvider; private Set<XExpression> executed = Sets.newHashSet(); @Inject private Provider<XTestInterpreter> interpreterProvider; /** * Returns the list of executed expressions * * @param main * The main body of the xtest file * @return The list of executed expressions */ public Set<XExpression> getUnexecutedExpressions(Body main) { Set<XExpression> unexecuted = Sets.newHashSet(); // Only mark unexecuted from list of expressions, otherwise will mark file properties as // unexecuted EList<XExpression> expressions = main.getExpressions(); for (XExpression expression : expressions) { TreeIterator<EObject> eAllContents = expression.eAllContents(); while (eAllContents.hasNext()) { EObject next = eAllContents.next(); if (next instanceof XExpression && !executed.contains(next)) { unexecuted.add((XExpression) next); } } } // remove contained EObjects, only mark warning on outer container Set<EObject> toRemove = Sets.newHashSet(); for (XExpression expression : unexecuted) { toRemove.addAll(expression.eContents()); } unexecuted.removeAll(toRemove); return unexecuted; } /** * Interprets an already linked xtest object model * * @param main * The linked xtest object model * @param monitor * The progress monitor to tell if canceled * @return The test result */ public XTestResult run(Body main, RunType weight, CancelIndicator monitor) { XTestInterpreter interpreter = getInterpreter(main.eResource()); try { IEvaluationResult resultObject = null; boolean failed = false; try { resultObject = interpreter.evaluate(main, contextProvider.get(), monitor); } catch (InterpreterCanceledException e) { } catch (Throwable e) { failed = true; } XTestResult result = new XTestResult(main); if (resultObject instanceof XtestEvaluationResult) { XtestEvaluationResult evalResult = (XtestEvaluationResult) resultObject; result = evalResult.getXtestResult(); executed = evalResult.getExpressions(); } if (failed) { result.fail(); } result.setResultObject(resultObject.getResult()); return result; } finally { cleanupInterpreter(interpreter); } } /** * Perform cleanup on interpreter after a test run has finished * * @param interpreter * The interpreter to clean up */ protected void cleanupInterpreter(XTestInterpreter interpreter) { } /** * Gets the xtest interpreter to use so that subclasses can change this behavior * * @param resource * The resource * @return the xtest interpreter to use */ protected XTestInterpreter getInterpreter(Resource resource) { XTestInterpreter interpreter = interpreterProvider.get(); return interpreter; } /** * CheckMode for validating the xtest script only without running the tests */ public static class DontRunCheck extends CheckMode { CheckMode mode = CheckMode.FAST_ONLY; @Override public boolean shouldCheck(CheckType type) { return mode.shouldCheck(type); } } }