/************************************************************************* * Copyright 2009-2012 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. ************************************************************************/ package com.eucalyptus.upgrade; import java.io.File; import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.OptionBuilder; import org.apache.commons.cli.Options; import org.apache.log4j.Logger; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runners.Parameterized.Parameters; import com.eucalyptus.bootstrap.ServiceJarDiscovery; import com.eucalyptus.component.ComponentDiscovery; import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; public class TestHarness { public static Integer LINE_BYTES = 0; static { String defaultLevel = System.getProperty("verbose"); String columns = System.getenv("COLUMNS"); System.setProperty("test.output.console", "INFO"); System.setProperty("test.output.terminal", "INFO"); if (columns != null && !"".equals(columns)) { LINE_BYTES = Integer.parseInt(System.getenv("COLUMNS")); System.setProperty("test.output.terminal", defaultLevel != null ? defaultLevel : "INFO"); } else { LINE_BYTES = 85; System.setProperty("test.output.console", "INFO"); } } public static int count = 1; private static Logger LOG = Logger.getLogger(TestHarness.class); private static List<Class<? extends Annotation>> testAnnotations = Lists .newArrayList(Before.class, After.class, Test.class, Parameters.class); public static void main(String[] args) { boolean anyFail = false; try { System.out.println(Arrays.asList(args)); final Options opts = getCliOptions(); final GnuParser cliParser = new GnuParser(); final CommandLine cmd = cliParser.parse(opts, args); try { anyFail = Iterables.any(Lists.newArrayList(Opts.values()), new Predicate<Opts>() { public boolean apply(Opts o) { return !o.run(cmd, opts); } }); } catch (Exception e) { e.printStackTrace(); } } catch (Exception e) { e.printStackTrace(); } System.exit(0); //This is necessary now; some shutdown process is hanging //return (anyFail) ? 1 : 0; } enum Opts { help { @Override public boolean run(CommandLine cmd, Options opts) { if (cmd.hasOption(Opts.help.toString()) || !cmd.getArgList().isEmpty() || cmd.getOptionValues("test") == null) { printHelp(opts); System.exit(0); } return true; } }, runTest { @Override public boolean run(CommandLine cmd, Options opts) { try { System.setProperty("euca.log.level", "TRACE"); System.setProperty("euca.log.appender", "console"); System.setProperty("euca.log.exhaustive.cc", "FATAL"); System.setProperty("euca.log.exhaustive.db", "FATAL"); System.setProperty("euca.log.exhaustive.external", "FATAL"); System.setProperty("euca.log.exhaustive.user", "FATAL"); System.setProperty("euca.var.dir", System.getProperty("euca.home") + "/var/lib/eucalyptus/"); System.setProperty("euca.conf.dir", System.getProperty("euca.home") + "/etc/eucalyptus/cloud.d/"); System.setProperty("euca.log.dir", System.getProperty("euca.home") + "/var/log/eucalyptus/"); System.setProperty("euca.lib.dir", System.getProperty("euca.home") + "/usr/share/eucalyptus/"); boolean doTrace = "TRACE".equals(System .getProperty("euca.log.level")); boolean doDebug = "DEBUG".equals(System .getProperty("euca.log.level")) || doTrace; // Logs.DEBUG = doDebug; // Logs.TRACE = doDebug; if ((StandalonePersistence.eucaDest = System .getProperty("euca.upgrade.destination")) == null) { throw new RuntimeException( "Failed to find required 'euca.upgrade.destination' property"); } ServiceJarDiscovery.processLibraries( ); ServiceJarDiscovery.runDiscovery( new ComponentDiscovery( ) ); StandalonePersistence.setupInitProviders(); StandalonePersistence.setupProviders(); StandalonePersistence.setupNewDatabase(); } catch (Exception e) { throw new RuntimeException( "Standalone persistence setup failed", e); } runMethods(cmd, opts); return true; } }; public abstract boolean run(CommandLine cmd, Options opts); } @SuppressWarnings("static-access") private static Options getCliOptions() { final Options opts = new Options(); opts.addOption(OptionBuilder .withLongOpt("test") .hasArgs() .withDescription( "Run the specified test; this option can appear multiple times. Parameters can be set as key=value pairs. e.g. ConcurrencyTest:threads=16") .withArgName("TEST_CLASS[:PARAM=VALUE]*").create("t")); opts.addOption(OptionBuilder.withLongOpt(Opts.help.name()) .withDescription("Show this help information.").create("h")); return opts; } @SuppressWarnings({"static-access"}) private static void printHelp(Options opts) { try { PrintWriter out = new PrintWriter(System.out); HelpFormatter help = new HelpFormatter(); help.setArgName("TESTS"); help.printHelp(out, LINE_BYTES, "java -jar test.jar", "Options controlling the test harness.", opts, 2, 4, "", true); Multimap<Class, Method> testMethods = getTestMethods(); help = new HelpFormatter(); help.setLongOptPrefix(""); help.setOptPrefix(""); help.setSyntaxPrefix(""); help.setLeftPadding(0); Options testOptions = new Options(); for (Class c : testMethods.keySet()) { testOptions.addOption(OptionBuilder .withDescription(getDescription(c)) .withLongOpt(c.getSimpleName()).create()); for (Method m : testMethods.get(c)) { testOptions.addOption(OptionBuilder .withDescription(getDescription(m)) .withLongOpt(c.getSimpleName() + "." + m.getName()) .create()); } } help.printHelp(out, LINE_BYTES, " ", "Tests:", testOptions, 0, 2, "", false); out.flush(); } catch (Exception e) { System.out.println(e); System.exit(1); } } @SuppressWarnings("unchecked") private static String getDescription(Object o) { Class c = null; Method m = null; if (o instanceof Class && ((c = (Class) o).getAnnotation(TestDescription.class)) != null) { return ((TestDescription) c.getAnnotation(TestDescription.class)) .value(); } else if (o instanceof Method && ((m = (Method) o).getAnnotation(TestDescription.class)) != null) { StringBuffer sb = new StringBuffer(); for (Class a : Lists.newArrayList(Before.class, After.class, Test.class, Ignore.class, Parameters.class)) { if (m.getAnnotation(a) != null) sb.append(" @") .append(String.format("%-9.9s", a.getSimpleName())) .append(" "); } return sb .append(" ") .append(((TestDescription) m .getAnnotation(TestDescription.class)).value()) .toString(); } return ""; } @SuppressWarnings("unchecked") private static Multimap<Class, Method> getTestMethods() throws Exception { final Multimap<Class, Method> testMethods = ArrayListMultimap.create( ); List<Class> classList = Lists.newArrayList(); for (File f : new File(System.getProperty("euca.home") + "/usr/share/eucalyptus").listFiles()) { if (f.getName().startsWith("eucalyptus") && f.getName().endsWith(".jar") && !f.getName().matches(".*-ext-.*")) { try { JarFile jar = new JarFile(f); Enumeration<JarEntry> jarList = jar.entries(); for( JarEntry j : Collections.list( jar.entries() ) ) { if (j.getName().matches(".*\\.class.{0,1}")) { String classGuess = j.getName() .replaceAll("/", ".") .replaceAll("\\.class.{0,1}", ""); try { Class candidate = ClassLoader .getSystemClassLoader().loadClass( classGuess); for (final Method m : candidate .getDeclaredMethods()) { if (Iterables .any(testAnnotations, new Predicate<Class<? extends Annotation>>() { public boolean apply( Class<? extends Annotation> arg0) { return m.getAnnotation(arg0) != null; } })) { System.out.println("Added test class: " + candidate.getCanonicalName()); testMethods.put(candidate, m); } } } catch (ClassNotFoundException e) { } } } jar.close(); } catch (Exception e) { System.out.println(e.getMessage()); continue; } } } return testMethods; } public static boolean runTests(List<Class> tests) { System.out.println("TEST LIST SIZE:" + tests.size()); for (Class testClass : tests) { System.out.println("Test:" + testClass); } JUnitCore core = new JUnitCore(); // core.run( Cleanup.class ); core.addListener(new TestListener()); boolean rv = true; for (Class clazz : tests) { Result res = core.run(clazz); if (res == null || !res.wasSuccessful()) { rv = false; } } return rv; // tests = Lists.newArrayList( tests ); // tests.add( Cleanup.class ); // Result res = core.run( ( Class<?>[] ) tests.toArray( new Class[] {}) // ); // return res != null ? res.wasSuccessful( ) : false; } @SuppressWarnings("unchecked") private static void runMethods(final CommandLine cmd, final Options opts) { String[] optVals = cmd.getOptionValues("test"); for (int i = 0; i < optVals.length; i++) { String[] argParts = optVals[i].split(":"); String className = argParts[0]; String methodName = argParts[1]; String[] methodArgsArray = new String[0]; if (argParts.length > 2) { methodArgsArray = argParts[2].split(","); System.out.printf("Executing class:%s method:%s args:%s\n", className, methodName, argParts[2]); } else { System.out.printf("Executing class:%s method:%s\n", className, methodName); } Class[] params = new Class[methodArgsArray.length]; for (int j=0; j < params.length; j++) { params[j] = String.class; } try { Class clazz = Class.forName(className); clazz.getDeclaredMethod(methodName, params).invoke(null, (Object[])methodArgsArray); } catch (Exception ex) { ex.printStackTrace(); } System.out.println("Executed method"); } // List<Class> testList = Lists.transform( Lists.newArrayList( // cmd.getOptionValues( "test" ) ), new Function<String, Class>( ) { // public Class apply( String arg ) { // String[] argParts = arg.split(":"); // String className = argParts[0]; // System.out.println("CLASS NAME:" + className); // Class targetClass = null; // try { // targetClass = Class.forName( className ); // } catch ( Exception e ) { // try { // targetClass = Class.forName( className + "Test" ); // } catch ( Exception e1 ) { // } // } // if( targetClass == null ) { // printHelp( opts ); // System.exit( 1 ); // } else { // for( int i = 1; i < argParts.length; i++ ) { // String property = argParts[i].replaceAll("=.*",""); // String value = argParts[i].replaceAll(".*=",""); // try { // targetClass.getDeclaredMethod( property, String.class ).invoke( null, // value ); // } catch ( Exception e ) { // System.out.println( e ); // System.exit( 1 ); // } // } // } // return targetClass; // } // } ); // return testList; } }