/* * JBoss, Home of Professional Open Source. * Copyright ${year}, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.wildfly.client.old.server.util; import static org.wildfly.core.testrunner.Server.LEGACY_JAVA_HOME; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.inject.Inject; import org.jboss.as.controller.client.ModelControllerClient; import org.junit.runner.Result; import org.junit.runner.Runner; import org.junit.runner.notification.RunListener; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.Suite; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.wildfly.core.testrunner.ManagementClient; import org.wildfly.core.testrunner.ServerController; /** * @author Kabir Khan */ public class OldVersionTestRunner extends Suite { private static final String LARGE_DEPLOYMENT_NAME = "large-deployment.xml"; static final String DEPLOYMENT_SIZE = "jboss.test.client.old.server.size"; static String OLD_VERSIONS_DIR = OldVersionCopier.OLD_VERSIONS_DIR; //On OS X we seem to need to specify the JRE, i.e. (on my machine): // /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre //rather than // /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home //The latter variety causes problems like: // "Cannot run program "/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/bin/java": error=2, No such file or directory" static final String JDK7_LOCATION = "jboss.test.client.old.server.jdk7"; static final String JDK6_LOCATION = "jboss.test.client.old.server.jdk6"; /** * Annotation for a method which provides parameters to be injected into the * test class constructor by <code>Parameterized</code> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public static @interface OldVersionParameter { /** * <p> * Optional pattern to derive the test's name from the parameters. Use * numbers in braces to refer to the parameters or the additional data * as follows: * </p> * * <pre> * {index} - the current parameter index * {0} - the first parameter value * {1} - the second parameter value * etc... * </pre> * <p> * Default value is "{index}" for compatibility with previous JUnit * versions. * </p> * * @return {@link MessageFormat} pattern string, except the index * placeholder. * @see MessageFormat */ String name() default "{index}"; } private class TestClassRunnerForParameters extends BlockJUnit4ClassRunner { private final OldVersionTestParameter parameter; private final String fName; private final ServerController controller = new ServerController(); private final LargeDeploymentFile largeDeploymentFile; TestClassRunnerForParameters(Class<?> klass, OldVersionTestParameter parameters, String name) throws InitializationError { super(klass); fName = name; this.parameter = parameters; try { this.largeDeploymentFile = createTestFile(); } catch (IOException e) { throw new InitializationError(e); } } private void doInject(Class<?> klass, Object instance) { Class c = klass; try { while (c != null && c != Object.class) { for (Field field : c.getDeclaredFields()) { if ((instance == null && Modifier.isStatic(field.getModifiers()) || instance != null && !Modifier.isStatic(field.getModifiers()))) { if (field.isAnnotationPresent(Inject.class)) { 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); } else if (field.getType() == LargeDeploymentFile.class) { field.set(instance, largeDeploymentFile); } } } } c = c.getSuperclass(); } } catch (Exception e) { throw new RuntimeException("Failed to inject", e); } } @Override public Object createTest() throws Exception { return createTestUsingConstructorInjection(); } private Object createTestUsingConstructorInjection() throws Exception { Object test = getTestClass().getOnlyConstructor().newInstance(parameter); doInject(getTestClass().getJavaClass(), test); return test; } @Override protected String getName() { return fName; } @Override protected String testName(FrameworkMethod method) { return method.getName() + getName(); } @Override protected void validateConstructor(List<Throwable> errors) { validateOnlyOneConstructor(errors); } @Override protected void validateFields(List<Throwable> errors) { super.validateFields(errors); } @Override protected Statement classBlock(RunNotifier notifier) { return childrenInvoker(notifier); } @Override protected Annotation[] getRunnerAnnotations() { return new Annotation[0]; } @Override public void run(final RunNotifier notifier){ if (controller.isStarted()) { controller.stop(); } setupServer(); try { controller.start(); } catch (RuntimeException e) { throw new RuntimeException(parameter.getAsVersion() + ": " + e.getMessage(), e); } try { doInject(super.getTestClass().getJavaClass(), null); notifier.addListener(new RunListener() { @Override public void testRunFinished(Result result) throws Exception { super.testRunFinished(result); controller.stop(); largeDeploymentFile.getFile().delete(); } }); super.run(notifier); } finally { controller.stop(); } } private void setupServer() { Version.AsVersion version = parameter.getAsVersion(); File file = OldVersionCopier.expandOldVersion(version); System.setProperty("jboss.home", file.getAbsolutePath()); System.setProperty("management.protocol", version.getManagementProtocol()); System.setProperty("management.port", version.getManagementPort()); if (version.getJdk() != Version.JDK.JDK8) { final String prop; switch (version.getJdk()){ case JDK6: prop = JDK6_LOCATION; break; case JDK7: prop = JDK7_LOCATION; break; default: throw new IllegalStateException("Unknown jdk"); } String jdkHome = System.getProperty(prop); if (jdkHome == null) { throw new IllegalStateException("Could not determine " + version.getJdk() + " home from either -D" + prop); } System.setProperty(LEGACY_JAVA_HOME, jdkHome); //Server has this // private final String jvmArgs = System.getProperty("jvm.args", "-Xmx512m -XX:MaxMetaspaceSize=256m"); //-XX:MaxMetaspaceSize is not available < JDK 8, so remove it: System.setProperty("jvm.args", "-Xmx512m"); } else { System.clearProperty(LEGACY_JAVA_HOME); } } } private static final List<Runner> NO_RUNNERS = Collections .<Runner>emptyList(); private final ArrayList<Runner> runners = new ArrayList<Runner>(); /** * Only called reflectively. Do not use programmatically. */ public OldVersionTestRunner(Class<?> klass) throws Throwable { super(klass, NO_RUNNERS); OldVersionParameter parameters = getParametersMethod().getAnnotation( OldVersionParameter.class); createRunnersForParameters(allParameters(), parameters.name()); } @Override protected List<Runner> getChildren() { return runners; } @SuppressWarnings("unchecked") private Iterable<OldVersionTestParameter> allParameters() throws Throwable { Object parameters = getParametersMethod().invokeExplosively(null); if (parameters instanceof Iterable) { return (Iterable<OldVersionTestParameter>) parameters; } else { throw parametersMethodReturnedWrongType(); } } private FrameworkMethod getParametersMethod() throws Exception { List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods( OldVersionParameter.class); for (FrameworkMethod each : methods) { if (each.isStatic() && each.isPublic()) { return each; } } throw new Exception("No public static parameters method on class " + getTestClass().getName()); } private void createRunnersForParameters(Iterable<OldVersionTestParameter> allParameters, String namePattern) throws Exception { try { int i = 0; for (OldVersionTestParameter parametersOfSingleTest : allParameters) { String name = nameFor(namePattern, i, parametersOfSingleTest); TestClassRunnerForParameters runner = new TestClassRunnerForParameters( getTestClass().getJavaClass(), parametersOfSingleTest, name); runners.add(runner); ++i; } } catch (ClassCastException e) { throw parametersMethodReturnedWrongType(); } } private String nameFor(String namePattern, int index, OldVersionTestParameter parameters) { String finalPattern = namePattern.replaceAll("\\{index\\}", Integer.toString(index)); String name = MessageFormat.format(finalPattern, parameters); return "[" + name + "]"; } private Exception parametersMethodReturnedWrongType() throws Exception { String className = getTestClass().getName(); String methodName = getParametersMethod().getName(); String message = MessageFormat.format( "{0}.{1}() must return an Iterable of arrays.", className, methodName); return new Exception(message); } private LargeDeploymentFile createTestFile() throws IOException { //Default to a 1GB deployment int size = Integer.getInteger(DEPLOYMENT_SIZE, 1024 * 1024 * 1024); Path path = Paths.get(new File(".").getAbsolutePath(), "target", "deployment"); File dir = path.toFile(); if (!dir.exists()) { Files.createDirectories(path); } path = Paths.get(path.toString(), LARGE_DEPLOYMENT_NAME); File file = path.toFile(); if (!file.exists()) { byte[] bytes = new byte[1024]; for (int i = 0 ; i < 1024 ; i++) { bytes[i] = (byte)i; } try (BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(file))) { int max = size/1024; for (int i = 0 ; i < max ; i++) { writer.write(bytes); } } } return new LargeDeploymentFile(file); } }