package org.wildfly.core.testrunner; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.security.Provider; import java.security.Security; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.inject.Inject; import org.jboss.as.controller.client.ModelControllerClient; import org.jboss.dmr.ModelNode; import org.junit.runner.Result; import org.junit.runner.notification.RunListener; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkField; import org.junit.runners.model.InitializationError; import org.junit.runners.model.TestClass; import org.wildfly.security.WildFlyElytronProvider; /** * A lightweight test runner for running management based tests * * @author Stuart Douglas */ public class WildflyTestRunner extends BlockJUnit4ClassRunner { private static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider(); private final ServerController controller = new ServerController(); private final boolean automaticServerControl; private final List<ServerSetupTask> serverSetupTasks = new LinkedList<>(); /** * Creates a BlockJUnit4ClassRunner to run {@code klass} * * @throws org.junit.runners.model.InitializationError if the test class is malformed. */ public WildflyTestRunner(Class<?> klass) throws InitializationError { super(klass); if (klass.isAnnotationPresent(ServerControl.class)) { ServerControl serverControl = klass.getAnnotation(ServerControl.class); automaticServerControl = !serverControl.manual(); }else{ automaticServerControl = true; } startServerIfRequired(); doInject(getTestClass(), null); prepareSetupTasks(getTestClass()); } private void doInject(TestClass klass, Object instance) { try { for (FrameworkField frameworkField : klass.getAnnotatedFields(Inject.class)) { Field field = frameworkField.getField(); if ((instance == null && Modifier.isStatic(field.getModifiers()) || instance != null)) {//we want to do injection even on static fields before test run, so we make sure that client is correct for current state of server field.setAccessible(true); if (field.getType() == ManagementClient.class && controller.isStarted()) { field.set(instance, controller.getClient()); } else if (field.getType() == ModelControllerClient.class && controller.isStarted()) { field.set(instance, controller.getClient().getControllerClient()); } else if (field.getType() == ServerController.class) { field.set(instance, controller); } } } } catch (Exception e) { throw new RuntimeException("Failed to inject", e); } } @Override protected Object createTest() throws Exception { Object res = super.createTest(); doInject(getTestClass(), res); return res; } @Override public void run(final RunNotifier notifier){ notifier.addListener(new RunListener() { @Override public void testRunFinished(Result result) throws Exception { super.testRunFinished(result); if (automaticServerControl) { controller.stop(); } } }); startServerIfRequired(); if (!serverSetupTasks.isEmpty() && !automaticServerControl) { throw new RuntimeException("Can't run setup tasks with manual server control"); } boolean providerInstalled = false; if (Security.getProvider(ELYTRON_PROVIDER.getName()) == null) { Security.insertProviderAt(ELYTRON_PROVIDER, 0); providerInstalled = true; } if (automaticServerControl) { runSetupTasks(); } super.run(notifier); if (automaticServerControl) { runTearDownTasks(); } if (providerInstalled) { Security.removeProvider(ELYTRON_PROVIDER.getName()); } } private void runSetupTasks() { for (ServerSetupTask task : serverSetupTasks) { try { task.setup(controller.getClient()); } catch (Exception e) { throw new RuntimeException(String.format("Could not run setup task '%s'", task), e); } } } private void runTearDownTasks() { List<ServerSetupTask> reverseServerSetupTasks = new LinkedList<>(serverSetupTasks); Collections.reverse(reverseServerSetupTasks); for (ServerSetupTask task : reverseServerSetupTasks) { try { task.tearDown(controller.getClient()); } catch (Exception e) { throw new RuntimeException(String.format("Could not run tear down task '%s'", task), e); } } checkServerState(); } private void prepareSetupTasks(TestClass klass) throws InitializationError { try { if (klass.getJavaClass().isAnnotationPresent(ServerSetup.class)) { ServerSetup serverSetup = klass.getAnnotation(ServerSetup.class); for (Class<? extends ServerSetupTask> clazz : serverSetup.value()) { Constructor<? extends ServerSetupTask> ctor = clazz.getDeclaredConstructor(); ctor.setAccessible(true); serverSetupTasks.add(ctor.newInstance()); } } } catch (Exception e) { throw new InitializationError(e); } } private void startServerIfRequired() { if (automaticServerControl) { controller.start(); } } private void checkServerState() { ModelNode op = new ModelNode(); op.get("operation").set("read-attribute"); op.get("name").set("server-state"); try { ModelNode result = controller.getClient().executeForResult(op); if (!"running".equalsIgnoreCase(result.asString())) { throw new RuntimeException(String.format("Server state is '%s' following test completion; tests must complete with the server in 'running' state", result.asString())); } } catch (UnsuccessfulOperationException e) { throw new RuntimeException("Failed checking server-state", e); } } }