--- jsr166/src/test/tck/JSR166TestCase.java 2012/11/18 18:03:11 1.92 +++ jsr166/src/test/tck/JSR166TestCase.java 2014/05/14 21:06:38 1.115 @@ -11,8 +11,14 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.Enumeration; +import java.util.List; import java.util.NoSuchElementException; import java.util.PropertyPermission; import java.util.concurrent.*; @@ -20,6 +26,7 @@ import java.util.concurrent.atomic.Atomi import java.util.concurrent.atomic.AtomicReference; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; +import java.util.regex.Pattern; import java.security.CodeSource; import java.security.Permission; import java.security.PermissionCollection; @@ -122,11 +129,34 @@ public class JSR166TestCase extends Test private static final long profileThreshold = Long.getLong("jsr166.profileThreshold", 100); + /** + * The number of repetitions per test (for tickling rare bugs). + */ + private static final int runsPerTest = + Integer.getInteger("jsr166.runsPerTest", 1); + + /** + * A filter for tests to run, matching strings of the form + * methodName(className), e.g. "testInvokeAll5(ForkJoinPoolTest)" + * Usefully combined with jsr166.runsPerTest. + */ + private static final Pattern methodFilter = methodFilter(); + + private static Pattern methodFilter() { + String regex = System.getProperty("jsr166.methodFilter"); + return (regex == null) ? null : Pattern.compile(regex); + } + protected void runTest() throws Throwable { - if (profileTests) - runTestProfiled(); - else - super.runTest(); + if (methodFilter == null + || methodFilter.matcher(toString()).find()) { + for (int i = 0; i < runsPerTest; i++) { + if (profileTests) + runTestProfiled(); + else + super.runTest(); + } + } } protected void runTestProfiled() throws Throwable { @@ -142,7 +172,9 @@ public class JSR166TestCase extends Test } /** - * Runs all JSR166 unit tests using junit.textui.TestRunner + * Runs all JSR166 unit tests using junit.textui.TestRunner. + * Optional command line arg provides the number of iterations to + * repeat running the tests. */ public static void main(String[] args) { if (useSecurityManager) { @@ -174,11 +206,51 @@ public class JSR166TestCase extends Test return suite; } + public static void addNamedTestClasses(TestSuite suite, + String... testClassNames) { + for (String testClassName : testClassNames) { + try { + Class testClass = Class.forName(testClassName); + Method m = testClass.getDeclaredMethod("suite", + new Class[0]); + suite.addTest(newTestSuite((Test)m.invoke(null))); + } catch (Exception e) { + throw new Error("Missing test class", e); + } + } + } + + public static final double JAVA_CLASS_VERSION; + public static final String JAVA_SPECIFICATION_VERSION; + static { + try { + JAVA_CLASS_VERSION = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public Double run() { + return Double.valueOf(System.getProperty("java.class.version"));}}); + JAVA_SPECIFICATION_VERSION = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + return System.getProperty("java.specification.version");}}); + } catch (Throwable t) { + throw new Error(t); + } + } + + public static boolean atLeastJava6() { return JAVA_CLASS_VERSION >= 50.0; } + public static boolean atLeastJava7() { return JAVA_CLASS_VERSION >= 51.0; } + public static boolean atLeastJava8() { return JAVA_CLASS_VERSION >= 52.0; } + public static boolean atLeastJava9() { + // As of 2014-05, java9 still uses 52.0 class file version + return JAVA_SPECIFICATION_VERSION.startsWith("1.9"); + } + /** * Collects all JSR166 unit tests as one suite. */ public static Test suite() { - return newTestSuite( + // Java7+ test classes + TestSuite suite = newTestSuite( ForkJoinPoolTest.suite(), ForkJoinTaskTest.suite(), RecursiveActionTest.suite(), @@ -243,15 +315,45 @@ public class JSR166TestCase extends Test TreeSetTest.suite(), TreeSubMapTest.suite(), TreeSubSetTest.suite()); + + // Java8+ test classes + if (atLeastJava8()) { + String[] java8TestClassNames = { + "Atomic8Test", + "CompletableFutureTest", + "ConcurrentHashMap8Test", + "CountedCompleterTest", + "DoubleAccumulatorTest", + "DoubleAdderTest", + "ForkJoinPool8Test", + "ForkJoinTask8Test", + "LongAccumulatorTest", + "LongAdderTest", + "SplittableRandomTest", + "StampedLockTest", + "ThreadLocalRandom8Test", + }; + addNamedTestClasses(suite, java8TestClassNames); + } + + // Java9+ test classes + if (atLeastJava9()) { + String[] java9TestClassNames = { + "ThreadPoolExecutor9Test", + }; + addNamedTestClasses(suite, java9TestClassNames); + } + + return suite; } + // Delays for timing-dependent tests, in milliseconds. public static long SHORT_DELAY_MS; public static long SMALL_DELAY_MS; public static long MEDIUM_DELAY_MS; public static long LONG_DELAY_MS; - /** * Returns the shortest timed delay. This could * be reimplemented to use for example a Property. @@ -334,6 +436,29 @@ public class JSR166TestCase extends Test if (Thread.interrupted()) throw new AssertionFailedError("interrupt status set in main thread"); + + checkForkJoinPoolThreadLeaks(); + } + + /** + * Find missing try { ... } finally { joinPool(e); } + */ + void checkForkJoinPoolThreadLeaks() throws InterruptedException { + Thread[] survivors = new Thread[5]; + int count = Thread.enumerate(survivors); + for (int i = 0; i < count; i++) { + Thread thread = survivors[i]; + String name = thread.getName(); + if (name.startsWith("ForkJoinPool-")) { + // give thread some time to terminate + thread.join(LONG_DELAY_MS); + if (!thread.isAlive()) continue; + thread.stop(); + throw new AssertionFailedError + (String.format("Found leaked ForkJoinPool thread test=%s thread=%s%n", + toString(), name)); + } + } } /** @@ -507,6 +632,16 @@ public class JSR166TestCase extends Test } /** + * A debugging tool to print all stack traces, as jstack does. + */ + static void printAllStackTraces() { + for (ThreadInfo info : + ManagementFactory.getThreadMXBean() + .dumpAllThreads(true, true)) + System.err.print(info); + } + + /** * Checks that thread does not terminate within the default * millisecond delay of {@code timeoutMillis()}. */ @@ -611,7 +746,6 @@ public class JSR166TestCase extends Test public static final Integer m6 = new Integer(-6); public static final Integer m10 = new Integer(-10); - /** * Runs Runnable r with a security policy that permits precisely * the specified permissions. If there is no current security @@ -623,11 +757,26 @@ public class JSR166TestCase extends Test SecurityManager sm = System.getSecurityManager(); if (sm == null) { r.run(); + } + runWithSecurityManagerWithPermissions(r, permissions); + } + + /** + * Runs Runnable r with a security policy that permits precisely + * the specified permissions. If there is no current security + * manager, a temporary one is set for the duration of the + * Runnable. We require that any security manager permit + * getPolicy/setPolicy. + */ + public void runWithSecurityManagerWithPermissions(Runnable r, + Permission... permissions) { + SecurityManager sm = System.getSecurityManager(); + if (sm == null) { Policy savedPolicy = Policy.getPolicy(); try { Policy.setPolicy(permissivePolicy()); System.setSecurityManager(new SecurityManager()); - runWithPermissions(r, permissions); + runWithSecurityManagerWithPermissions(r, permissions); } finally { System.setSecurityManager(null); Policy.setPolicy(savedPolicy); @@ -675,6 +824,12 @@ public class JSR166TestCase extends Test return perms.implies(p); } public void refresh() {} + public String toString() { + List ps = new ArrayList(); + for (Enumeration e = perms.elements(); e.hasMoreElements();) + ps.add(e.nextElement()); + return "AdjustablePolicy with permissions " + ps; + } } /** @@ -1121,7 +1276,7 @@ public class JSR166TestCase extends Test public abstract class CheckedRecursiveAction extends RecursiveAction { protected abstract void realCompute() throws Throwable; - public final void compute() { + @Override protected final void compute() { try { realCompute(); } catch (Throwable t) { @@ -1136,7 +1291,7 @@ public class JSR166TestCase extends Test public abstract class CheckedRecursiveTask extends RecursiveTask { protected abstract T realCompute() throws Throwable; - public final T compute() { + @Override protected final T compute() { try { return realCompute(); } catch (Throwable t) { @@ -1237,4 +1392,25 @@ public class JSR166TestCase extends Test return null; } } + + public void assertThrows(Class expectedExceptionClass, + Runnable... throwingActions) { + for (Runnable throwingAction : throwingActions) { + boolean threw = false; + try { throwingAction.run(); } + catch (Throwable t) { + threw = true; + if (!expectedExceptionClass.isInstance(t)) { + AssertionFailedError afe = + new AssertionFailedError + ("Expected " + expectedExceptionClass.getName() + + ", got " + t.getClass().getName()); + afe.initCause(t); + threadUnexpectedException(afe); + } + } + if (!threw) + shouldThrow(expectedExceptionClass.getName()); + } + } }