Modified: jmeter/trunk/test/src/org/apache/jorphan/test/AllTests.java URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jorphan/test/AllTests.java?rev=1817424&r1=1817423&r2=1817424&view=diff ============================================================================== --- jmeter/trunk/test/src/org/apache/jorphan/test/AllTests.java (original) +++ jmeter/trunk/test/src/org/apache/jorphan/test/AllTests.java Thu Dec 7 21:31:04 2017 @@ -13,44 +13,50 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.apache.jorphan.test; +import javax.crypto.Cipher; import java.awt.GraphicsEnvironment; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Modifier; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; -import javax.crypto.Cipher; +import junit.framework.TestCase; +import org.apache.commons.lang3.time.StopWatch; import org.apache.jmeter.junit.categories.ExcludeCategoryFilter; import org.apache.jmeter.junit.categories.NeedGuiTests; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.reflect.ClassFilter; import org.apache.jorphan.reflect.ClassFinder; import org.apache.jorphan.util.JOrphanUtils; -import org.junit.internal.RealSystem; +import org.junit.experimental.ParallelComputer; import org.junit.internal.TextListener; import org.junit.runner.Computer; +import org.junit.runner.Description; import org.junit.runner.JUnitCore; import org.junit.runner.Request; import org.junit.runner.Result; +import org.junit.runner.notification.RunListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spock.lang.Specification; -import junit.framework.TestCase; - /** - * Provides a quick and easy way to run all <a href="http://http://junit.org">junit</a> + * Provides a quick and easy way to run all <a href="http://http://junit.org">junit</a> * unit tests (including Spock tests) in your Java project. * It will find all unit test classes and run all their test methods. * There is no need to configure it in any way to find these classes except to @@ -59,9 +65,9 @@ import junit.framework.TestCase; * Here is an example Ant target (See Ant at <a * href="http://ant.apache.org">Apache Ant</a>) that runs all your unit * tests: - * + * * <pre> - * + * * <target name="test" depends="compile"> * <java classname="org.apache.jorphan.test.AllTests" fork="yes"> * <classpath> @@ -73,34 +79,34 @@ import junit.framework.TestCase; * <arg value="NAME_OF_UNITTESTMANAGER_CLASS"/> * </java> * </target> - * + * * </pre> - * + * * <dl> * <dt>YOUR_CLASSPATH</dt> * <dd>Refers to the classpath that includes all jars and libraries need to run * your unit tests</dd> - * + * * <dt>ROOT_DIR_OF_YOUR_COMPILED_CLASSES</dt> * <dd>The classpath should include the directory where all your project's * classes are compiled to, if it doesn't already.</dd> - * + * * <dt>SEARCH_PATH</dt> * <dd>The first argument tells AllTests where to look for unit test classes to * execute. In most cases, it is identical to ROOT_DIR_OF_YOUR_COMPILED_CLASSES. * You can specify multiple directories or jars to search by providing a * comma-delimited list.</dd> - * + * * <dt>PROPERTY_FILE</dt> * <dd>A simple property file that sets logging parameters. It is optional and * is only relevant if you use the same logging packages that JOrphan uses.</dd> - * + * * <dt>NAME_OF_UNITTESTMANAGER_CLASS</dt> * <dd>If your system requires some configuration to run correctly, you can * implement the {@link UnitTestManager} interface and be given an opportunity * to initialize your system from a configuration file.</dd> * </dl> - * + * * @see UnitTestManager */ public final class AllTests { @@ -132,16 +138,15 @@ public final class AllTests { * implements the UnitTestManager interface. This provides a means of * initializing your application with a configuration file prior to the * start of any unit tests. - * - * @param args - * the command line arguments + * + * @param args the command line arguments */ public static void main(String[] args) { if (args.length < 1) { System.out.println("You must specify a comma-delimited list of paths to search " + "for unit tests"); return; } - + String home = new File(System.getProperty("user.dir")).getParent(); System.out.println("Setting JMeterHome: "+home); JMeterUtils.setJMeterHome(home); @@ -169,7 +174,7 @@ public final class AllTests { logprop("os.version", true); logprop("os.arch"); logprop("java.class.version"); - + String cp = System.getProperty("java.class.path"); String[] cpe = JOrphanUtils.split(cp, java.io.File.pathSeparator); StringBuilder sb = new StringBuilder(3000); @@ -194,52 +199,111 @@ public final class AllTests { System.out.println("+++++++++++"); logprop("java.awt.headless", true); logprop("java.awt.graphicsenv", true); - + System.out.println("------------"); + JUnitCore jUnitCore = new JUnitCore(); + // this listener is in the internal junit package + // if it breaks, replace it with a custom text listener + jUnitCore.addListener(new TextListener(System.out)); + // this will time each unit test and then print to file + // TODO: put behind a flag + jUnitCore.addListener(new TimePrinter()); + + System.out.println("Searching junit tests in : "+args[0]); + try { - System.out.println("Searching junit tests in : "+args[0]); List<String> tests = findJMeterJUnitTests(args[0]); - Class<?>[] classes = asClasses(tests); - JUnitCore jUnitCore = new JUnitCore(); - - // this listener is in the internal junit package - // if it breaks, replace it with a custom text listener - jUnitCore.addListener(new TextListener(new RealSystem())); - - Request request = Request.classes(new Computer(), classes); - if(GraphicsEnvironment.isHeadless()) { - request = request.filterWith(new ExcludeCategoryFilter(NeedGuiTests.class)); - } - Result result = jUnitCore.run(request); - - System.exit(result.wasSuccessful() ? 0 : 1); + List<Class<?>> classes = asClasses(tests); + + Result parallelResults = jUnitCore.run(getParallelTests(classes)); + Result serialResults = jUnitCore.run(getSerialTests(classes)); + + boolean allTestsSuccessful = + parallelResults.wasSuccessful() && serialResults.wasSuccessful(); + System.exit(allTestsSuccessful ? 0 : 1); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } - private static Class<?>[] asClasses(List<String> tests) throws ClassNotFoundException { - Class<?>[] classes = new Class<?>[tests.size()]; - for (int i = 0; i < classes.length; i++) { - String test = tests.get(i); - classes[i] = Class.forName(test, true, Thread.currentThread().getContextClassLoader()); + private static Request getParallelTests(List<Class<?>> classes) { + Request parallelRequest = Request.classes( + ParallelComputer.methods(), // ParallelComputer.classes() causes failures + classes.stream() + .filter(c -> !JMeterSerialTest.class.isAssignableFrom(c)) + .toArray(Class<?>[]::new)); + return filterGUITests(parallelRequest); + } + + private static Request getSerialTests(List<Class<?>> classes) { + Request serialRequest = Request.classes(Computer.serial(), + classes.stream() + .filter(JMeterSerialTest.class::isAssignableFrom) + .toArray(Class<?>[]::new)); + return filterGUITests(serialRequest); + } + + private static Request filterGUITests(Request request) { + if (GraphicsEnvironment.isHeadless()) { + return request.filterWith(new ExcludeCategoryFilter(NeedGuiTests.class)); + } else { + return request; + } + } + + private static List<Class<?>> asClasses(List<String> tests) { + return tests.stream() + .map(AllTests::asClass) + .collect(Collectors.toList()); + } + + private static Class<?> asClass(String test) { + try { + return Class.forName(test, true, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); } + } - return classes; + /** + * Used to time each unit test and then write the results to file + */ + private static class TimePrinter extends RunListener { + private ConcurrentHashMap<Description, StopWatch> testTimers = new ConcurrentHashMap<>(); + private List<String> logLines = new ArrayList<>(); + + @Override + public void testStarted(Description description) { + StopWatch sw = new StopWatch(); + sw.start(); + testTimers.put(description, sw); + } + + @Override + public void testFinished(Description desc) { + StopWatch sw = testTimers.get(desc); + sw.stop(); + logLines.add(desc.getClassName() + "." + desc.getMethodName() + "\t" + sw.getNanoTime()); + } + + @Override + public void testRunFinished(Result result) throws Exception { + Files.write(Paths.get("unit-test-perf.log"), logLines); + } } /** * An overridable method that instantiates a UnitTestManager (if one * was specified in the command-line arguments), and hands it the name of * the properties file to use to configure the system. - * + * * @param args arguments with the initialization parameter * arg[0] - not used * arg[1] - relative name of properties file * arg[2] - used as label */ - protected static void initializeManager(String[] args) { + private static void initializeManager(String[] args) { if (args.length >= 3) { try { System.out.println("Using initializeProperties() from " + args[2]);
Added: jmeter/trunk/test/src/org/apache/jorphan/test/JMeterSerialTest.java URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jorphan/test/JMeterSerialTest.java?rev=1817424&view=auto ============================================================================== --- jmeter/trunk/test/src/org/apache/jorphan/test/JMeterSerialTest.java (added) +++ jmeter/trunk/test/src/org/apache/jorphan/test/JMeterSerialTest.java Thu Dec 7 21:31:04 2017 @@ -0,0 +1,8 @@ +package org.apache.jorphan.test; + +/** + * Used to tag tests which need to be run on their own (in serial) because + * either, they cause other tests to fail, or they fail when run in parallel. + */ +public interface JMeterSerialTest { +} Propchange: jmeter/trunk/test/src/org/apache/jorphan/test/JMeterSerialTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/test/src/org/apache/jorphan/test/JMeterSerialTest.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: jmeter/trunk/xdocs/changes.xml URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1817424&r1=1817423&r2=1817424&view=diff ============================================================================== --- jmeter/trunk/xdocs/changes.xml [utf-8] (original) +++ jmeter/trunk/xdocs/changes.xml [utf-8] Thu Dec 7 21:31:04 2017 @@ -202,6 +202,7 @@ Summary <li><pr>334</pr>Enable running of JUnit tests from within IntelliJ with default config. Contributed by Graham Russell (graham at ham1.co.uk)</li> <li><pr>335</pr>Removed <code>functions.util.*</code> as they don't seem to be used (for many years). Contributed by Graham Russell (graham at ham1.co.uk)</li> <li><bug>61867</bug><pr>345</pr>Updated to latest checkstyle (v8.5), Added many more rules to checkstyle, Included checking of test files and more file types. Contributed by Graham Russell (graham at ham1.co.uk)</li> + <li><pr>350</pr> Parallelised unit tests</li> </ul> <!-- =================== Bug fixes =================== -->
