package org.xpect.runner; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import org.eclipse.emf.common.util.EList; import org.xpect.XjmContribution; import org.xpect.XjmTestMethod; import org.xpect.XjmXpectMethod; import org.xpect.XpectArgument; import org.xpect.XpectFile; import org.xpect.XpectInvocation; import org.xpect.XpectJavaModel; import org.xpect.expectation.impl.TargetSyntaxSupport; import org.xpect.setup.ISetupInitializer; import org.xpect.setup.ThisArgumentType; import org.xpect.setup.ThisRootTestClass; import org.xpect.setup.ThisTestClass; import org.xpect.setup.ThisTestObject; import org.xpect.setup.XpectSetupFactory; import org.xpect.setup.ThisTestObject.TestObjectSetup; import org.xpect.state.Configuration; import org.xpect.state.Managed; import org.xpect.state.ResolvedConfiguration; import org.xpect.state.StateContainer; import org.xpect.util.EnvironmentUtil; import com.google.common.primitives.Primitives; import junit.framework.AssertionFailedError; public class TestExecutor { private static Object cast(Managed<?> value, XpectArgument expectedType) { if (value == null) throw new RuntimeException("Could not create value for " + expectedType.toString(true, true)); Class<?> exactType = expectedType.getJavaType(); Class<?> javaType = exactType.isPrimitive() ? Primitives.wrap(exactType) : exactType; if (javaType.isInstance(javaType)) return javaType; Object object = value.get(); if (object != null && !javaType.isInstance(object)) throw new RuntimeException("Object of type " + object.getClass().getName() + " is not assignable to argument " + expectedType.toString(true, true)); return object; } private static Configuration[] createArgumentConfigurations(XpectInvocation statement) { EList<XpectArgument> arguments = statement.getArguments(); Configuration[] result = new Configuration[arguments.size()]; for (int i = 0; i < arguments.size(); i++) { XpectArgument argument = arguments.get(i); Configuration configuration = new Configuration(argument.toString(false, true)); configuration.addDefaultValue(XpectArgument.class, argument); configuration.addValue(ThisArgumentType.class, argument.getJavaType()); result[i] = configuration; } return result; } private static StateContainer[] createArgumentStateContainers(StateContainer state, ResolvedConfiguration[] configurations) { StateContainer[] result = new StateContainer[configurations.length]; for (int i = 0; i < configurations.length; i++) result[i] = new StateContainer(state, configurations[i]); return result; } private static Object[] createArgumentValues(StateContainer[] stateContainers, XpectInvocation invocation) { EList<XpectArgument> arguments = invocation.getArguments(); Object[] result = new Object[stateContainers.length]; for (int i = 0; i < stateContainers.length; i++) { StateContainer state = stateContainers[i]; XpectArgument argument = arguments.get(i); try { Class<?> keyType = argument.getJavaType(); Annotation keyAnnotation = argument.getStateAnnotation(); Managed<?> managed = keyAnnotation != null ? state.get(keyType, keyAnnotation) : state.get(keyType); result[i] = cast(managed, argument); } catch (Throwable t) { throw new RuntimeException("Error creating value for argument " + argument.toString(true, true), t); } } return result; } public static Configuration createTestConfiguration(XjmTestMethod method) { Configuration config = new Configuration(method.getName() + "()"); config.addValue(ThisTestClass.class, Class.class, method.getTest().getJavaClass()); config.addDefaultValue(XjmTestMethod.class, method); return config; } public static Configuration createXpectConfiguration(XpectInvocation invocation) { Configuration config = new Configuration(invocation.getMethodName() + "(...)"); config.addValue(ThisTestClass.class, Class.class, invocation.getMethod().getTest().getJavaClass()); config.addDefaultValue(XpectInvocation.class, invocation); config.addDefaultValue(XjmXpectMethod.class, invocation.getMethod()); return config; } public static Configuration createFileConfiguration(XpectFile file) { Configuration config = new Configuration(file.eResource().getURI().lastSegment()); config.addDefaultValue(XpectFile.class, file); config.addDefaultValue(ISetupInitializer.class, file.createSetupInitializer()); return config; } public static Configuration createRootConfiguration(XpectJavaModel model) { Configuration config = new Configuration("Root"); config.addValue(ThisRootTestClass.class, model.getTestOrSuite().getJavaClass()); config.addFactory(TestObjectSetup.class); config.addFactory(ValidatingSetup.class); config.addFactory(TargetSyntaxSupport.class); config.addFactory(ArgumentContributor.class); config.addDefaultValue(XpectJavaModel.class, model); Iterable<XjmContribution> contributions = model.getContributions(XpectSetupFactory.class, EnvironmentUtil.ENVIRONMENT); for (XjmContribution contribution : contributions) { if (contribution.isActive()) { config.addFactory(contribution.getJavaClass()); } } return config; } public static StateContainer createState(Configuration config) { return new StateContainer(new ResolvedConfiguration(config)); } public static StateContainer createState(StateContainer state, Configuration config) { return new StateContainer(state, new ResolvedConfiguration(state.getConfiguration(), config)); } private static ResolvedConfiguration[] resolveArgumentConfiguration(StateContainer state, Configuration[] configurations) { ResolvedConfiguration[] result = new ResolvedConfiguration[configurations.length]; for (int i = 0; i < configurations.length; i++) result[i] = new ResolvedConfiguration(state.getConfiguration(), configurations[i]); return result; } public static void runTest(StateContainer state, XpectInvocation invocation) throws Throwable { Object test = state.get(Object.class, ThisTestObject.class).get(); boolean fixmeMessage = false; try { ArgumentContributor contributor = state.get(ArgumentContributor.class).get(); Configuration[] configurations = createArgumentConfigurations(invocation); contributor.contributeArguments(configurations); ResolvedConfiguration[] resolved = resolveArgumentConfiguration(state, configurations); StateContainer[] states = createArgumentStateContainers(state, resolved); try { Object[] args = createArgumentValues(states, invocation); invocation.getMethod().getJavaMethod().invoke(test, args); // reaching this point implies that no exception was thrown, hence the test passes. if (invocation.isFixme()) { fixmeMessage = true; throw new InvocationTargetException(new AssertionFailedError("Congrats, this FIXME test is suddenly fixed!")); } } finally { for (StateContainer s : states) { s.invalidate(); } } } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (invocation.isFixme() && !fixmeMessage) { // FIXME-tests pass when they throw an exception } else { throw cause; } } } }