/******************************************************************************* * Copyright (c) 2014 Markus Alexander Kuppe. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Markus Alexander Kuppe (ecf-dev_eclipse.org <at> lemmster <dot> de) - initial API and implementation ******************************************************************************/ package org.eclipse.ecf.tests.discovery; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import org.eclipse.ecf.core.util.StringUtils; import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestListener; import junit.framework.TestResult; import junit.framework.TestSuite; public abstract class RndStatsTestCase extends TestCase { // Run each test ITERATION multiple times private static final int ITERATIONS = Integer.getInteger("RndStatsTestCase.iterations" , 1).intValue(); // Seed (indirectly) determines test order private static final long SEED = Long.getLong("RndStatsTestCase.seed", System.currentTimeMillis()).longValue(); // An inclusion filter to only run testa matching this substring (case sensitive!) private static final String FILTER = System.getProperty("RndStatsTestCase.filter", ""); /** * Subclass AllTests calls this method (it differs from the regular static * {@link TestCase#suite()} method in the parameter {@link TestSuite}) prior * to returing the actual {@link TestSuite} in its standard method to set up * the {@link TestSuite}. * * @param suite * With test classes to be executed * @return A randomized {@link TestSuite} with all tests contained times * {@link RndStatsTestCase#ITERATIONS} */ public static Test suite(TestSuite suite) { // Add all tests of all test classes to the list final List/*<Test>*/ tests = new ArrayList(); final List/*<TestSuite>*/ suites = Collections.list(suite.tests()); for (int i = 0; i < ITERATIONS; i++) { for (final Iterator itr = suites.iterator(); itr.hasNext();) { final TestSuite aSuite = (TestSuite) itr.next(); // Callee just passes a TestSuite of classes. tests() returns // all real tests of current TestSuite. final List/*<Test>*/ testsOfClass = Collections.list(aSuite.tests()); if ("".equals(FILTER)) { tests.addAll(testsOfClass); } else { for (final Iterator itr2 = testsOfClass.iterator(); itr2 .hasNext();) { final Test t = (Test) itr2.next(); if (StringUtils.contains(t.toString(), FILTER)) { tests.add(t); } } } } } // shuffle the list to create randomized test order System.out.println("Seed used for test ordering: " + SEED); Collections.shuffle(tests, new Random(SEED)); // Create empty test suite and add tests in order of shuffeled list suite = new MyTestSuite(RndStatsTestCase.class.getName()); for (final Iterator itr = tests.iterator(); itr.hasNext();) { final Test t = (Test) itr.next(); suite.addTest(t); } return suite; } /** * Hooks into JUnit to register a {@link TestListener} and to print * statistics at end of {@link TestSuite} */ public static class MyTestSuite extends TestSuite { private StatisticalTestListener statisticalTestListener; public MyTestSuite(String name) { super(name); statisticalTestListener = new StatisticalTestListener(); } public void run(TestResult result) { result.addListener(statisticalTestListener); // This runs all the tests added in suite(TestSuite) super.run(result); // Finally, all tests have ended System.out.println("Executions/Failures/Errors/Test name"); final Collection values = statisticalTestListener.results.values(); for (Iterator itr = values.iterator(); itr.hasNext();) { StatisticalTestListener.Result v = (StatisticalTestListener.Result) itr.next(); System.out.println(v.cnt + "/" + v.failures.size() + "/" + v.errors.size() + " :" + v.t.toString() + (v.failures.size() > 0 ? " with predecessors: " + v.failures : "")); } System.out.println("Total: " + values.size()); } /** * Aggregates test results in {@link Result} per {@link Test} */ public static class StatisticalTestListener implements TestListener { // Use test's name (method + class) as key to aggregate multiple // executions of the same test. public final Map/*<String, Result>*/ results; private Test predecessor; public StatisticalTestListener() { results = new HashMap(); } public void addError(Test test, Throwable t) { final Result r = (Result) results.get(test.toString()); r.errors.add(t); } public void addFailure(Test test, AssertionFailedError t) { final Result r = (Result) results.get(test.toString()); r.failures.add(new Failure(t, predecessor)); } public void endTest(Test test) { predecessor = test; } public void startTest(Test test) { if (!results.containsKey(test.toString())) { final Result value = new Result(); value.t = test.toString(); value.tests.add(test); value.cnt += 1; results.put(test.toString(), value); } else { final Result val = (Result) results.get(test.toString()); val.cnt += 1; } } /** * Struct-like holder for aggregated test results */ public static class Result { public String t; public int cnt = 0; public final List tests = new ArrayList(); public final List errors = new ArrayList(); public final List failures = new ArrayList(); } public static class Failure { public AssertionFailedError afe; public Test predecessor; public Failure(AssertionFailedError t, Test predecessor) { this.afe = t; this.predecessor = predecessor; } public String toString() { return "Failure [afe=" + afe + ", predecessor=" + predecessor + "]"; } } } } }