/******************************************************************************* * Copyright (c) 2012 itemis AG (http://www.itemis.eu) and others. * 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 *******************************************************************************/ package org.xpect.runner; import java.io.IOException; import java.util.Collection; import java.util.List; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.plugin.EcorePlugin; import org.eclipse.xtext.resource.IResourceServiceProvider; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.resource.XtextResourceFactory; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.util.Strings; import org.eclipse.xtext.validation.CheckMode; import org.eclipse.xtext.validation.IResourceValidator; import org.eclipse.xtext.validation.Issue; import org.junit.ComparisonFailure; import org.junit.runner.Description; import org.junit.runner.Runner; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.ParentRunner; import org.junit.runners.model.InitializationError; import org.xpect.XpectFile; import org.xpect.XpectImport; import org.xpect.XpectJavaModel; import org.xpect.XpectStandaloneSetup; import org.xpect.registry.ITestSuiteInfo; import org.xpect.runner.XpectTestFiles.Builder; import org.xpect.runner.XpectTestFiles.FileRoot; import org.xpect.state.Configuration; import org.xpect.state.ResolvedConfiguration; import org.xpect.state.StateContainer; import org.xpect.util.AnnotationUtil; import org.xpect.util.IssueVisualizer; import org.xpect.util.XpectJavaModelManager; import com.google.common.collect.Lists; import com.google.inject.Injector; /** * @author Moritz Eysholdt - Initial contribution and API */ @XpectImport(TestTitleProvider.class) public class XpectRunner extends ParentRunner<Runner> { public static XpectRunner INSTANCE = null; public static ClassLoader testClassloader = null; private List<Runner> children; private Collection<URI> files; private final StateContainer state; private final IXpectURIProvider uriProvider; private final Injector xpectInjector; private final XpectJavaModel xpectJavaModel; public XpectRunner(Class<?> testClass) throws InitializationError { super(testClass); INSTANCE = this; testClassloader = testClass.getClassLoader(); this.uriProvider = findUriProvider(testClass); this.xpectInjector = findXpectInjector(); this.xpectJavaModel = XpectJavaModelManager.createJavaModel(testClass); this.state = TestExecutor.createState(createRootConfiguration()); } protected Runner createChild(URI uri) { try { XtextResource resource = loadXpectResource(uri); XpectFile file = loadXpectFile(resource); Configuration cfg = createChildConfiguration(file); StateContainer childState = new StateContainer(state, new ResolvedConfiguration(state.getConfiguration(), cfg)); return childState.get(XpectFileRunner.class).get(); } catch (Throwable t) { return new ErrorReportingRunner(this, uri, t); } } protected Configuration createChildConfiguration(XpectFile file) { return TestExecutor.createFileConfiguration(file); } protected List<Runner> createChildren(Class<?> clazz) { List<Runner> result = Lists.newArrayList(); for (URI uri : getFiles()) result.add(createChild(uri)); return result; } protected Configuration createRootConfiguration() { Configuration config = TestExecutor.createRootConfiguration(this.xpectJavaModel); config.addDefaultValue(this); config.addDefaultValue(IXpectURIProvider.class, this.uriProvider); config.addFactory(XpectFileRunner.class); config.addFactory(XpectTestRunner.class); config.addFactory(TestRunner.class); return config; } @Override protected Description describeChild(Runner child) { return child.getDescription(); } protected IXpectURIProvider findUriProvider(Class<?> clazz) throws InitializationError { String baseDir = System.getProperty("xpectBaseDir"); String files = System.getProperty("xpectFiles"); if (!Strings.isEmpty(baseDir) || !Strings.isEmpty(files)) { Builder builder = new XpectTestFiles.Builder().relativeTo(FileRoot.PROJECT); if (!Strings.isEmpty(baseDir)) builder.withBaseDir(baseDir); if (files != null) for (String file : files.split(";")) { String trimmed = file.trim(); if (!"".equals(trimmed)) builder.addFile(trimmed); } return builder.create(clazz); } IXpectURIProvider provider = AnnotationUtil.newInstanceViaMetaAnnotation(clazz, XpectURIProvider.class, IXpectURIProvider.class); if (provider != null) return provider; return new XpectTestFiles.Builder().relativeTo(FileRoot.CLASS).create(clazz); } protected Injector findXpectInjector() { IResourceServiceProvider rssp = IResourceServiceProvider.Registry.INSTANCE.getResourceServiceProvider(URI.createURI("foo.xpect")); if (rssp != null) return rssp.get(Injector.class); if (!EcorePlugin.IS_ECLIPSE_RUNNING) return new XpectStandaloneSetup().createInjectorAndDoEMFRegistration(); throw new IllegalStateException("The language *.xpect is not activated"); } @Override public List<Runner> getChildren() { if (children == null) children = createChildren(getTestClass().getJavaClass()); return children; } protected Collection<URI> getFiles() { if (files == null) files = uriProvider.getAllURIs(); return files; } public StateContainer getState() { return state; } public IXpectURIProvider getUriProvider() { return uriProvider; } protected Injector getXpectInjector() { return xpectInjector; } public XpectJavaModel getXpectJavaModel() { return xpectJavaModel; } protected XpectFile loadXpectFile(XtextResource res) throws IOException { XpectFile file = !res.getContents().isEmpty() ? (XpectFile) res.getContents().get(0) : null; if (file == null) throw new IllegalStateException("Resource for " + res.getURI() + " is empty."); validate(file); validate(res); return file; } protected XtextResource loadXpectResource(URI uri) throws IOException { XtextResource resource = (XtextResource) getXpectInjector().getInstance(XtextResourceFactory.class).createResource(uri); getXpectJavaModel().eResource().getResourceSet().getResources().add(resource); resource.load(null); return resource; } @Override public void run(RunNotifier notifier) { if (getChildren().isEmpty()) { notifier.fireTestRunStarted(getDescription()); notifier.fireTestFailure(new Failure(getDescription(), new RuntimeException("No Tests found via " + getUriProvider()))); } else { try { super.run(notifier); } finally { try { state.invalidate(); } catch (Throwable t) { notifier.fireTestFailure(new Failure(getDescription(), t)); } } } } @Override protected void runChild(Runner child, RunNotifier notifier) { try { child.run(notifier); } catch (Throwable t) { notifier.fireTestFailure(new Failure(child.getDescription(), t)); } } protected void validate(XpectFile file) { XpectJavaModel model = file.getJavaModel(); if (model == null || model.eIsProxy()) { String fileName = file.eResource().getURI().lastSegment(); String registry = ITestSuiteInfo.Registry.INSTANCE.toString(); throw new IllegalStateException("Could not find test suite for " + fileName + ". Registry:\n" + registry); } } protected void validate(XtextResource res) { IResourceValidator validator = res.getResourceServiceProvider().get(IResourceValidator.class); List<Issue> issues = validator.validate(res, CheckMode.ALL, CancelIndicator.NullImpl); if (!issues.isEmpty()) { String document = res.getParseResult().getRootNode().getText(); String errors = new IssueVisualizer().visualize(document, issues); throw new ComparisonFailure("Errors in " + res.getURI(), document.trim(), errors.trim()); } } }