--- jsr166/src/test/tck/JSR166TestCase.java 2013/01/21 19:32:19 1.94 +++ jsr166/src/test/tck/JSR166TestCase.java 2015/05/24 01:53:55 1.133 @@ -6,30 +6,52 @@ * Pat Fisher, Mike Judd. */ -import junit.framework.*; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + 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.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Policy; +import java.security.ProtectionDomain; +import java.security.SecurityPermission; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; +import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.PropertyPermission; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RecursiveAction; +import java.util.concurrent.RecursiveTask; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.NANOSECONDS; -import java.security.CodeSource; -import java.security.Permission; -import java.security.PermissionCollection; -import java.security.Permissions; -import java.security.Policy; -import java.security.ProtectionDomain; -import java.security.SecurityPermission; +import java.util.regex.Pattern; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestResult; +import junit.framework.TestSuite; /** * Base class for JSR166 Junit TCK tests. Defines some constants, @@ -112,6 +134,13 @@ public class JSR166TestCase extends Test Boolean.getBoolean("jsr166.expensiveTests"); /** + * If true, also run tests that are not part of the official tck + * because they test unspecified implementation details. + */ + protected static final boolean testImplementationDetails = + Boolean.getBoolean("jsr166.testImplementationDetails"); + + /** * If true, report on stdout all "slow" tests, that is, ones that * take more than profileThreshold milliseconds to execute. */ @@ -125,20 +154,50 @@ 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); + + /** + * The number of repetitions of the test suite (for finding leaks?). + */ + private static final int suiteRuns = + Integer.getInteger("jsr166.suiteRuns", 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 { + // Warmup run, notably to trigger all needed classloading. + super.runTest(); long t0 = System.nanoTime(); try { super.runTest(); } finally { - long elapsedMillis = - (System.nanoTime() - t0) / (1000L * 1000L); + long elapsedMillis = millisElapsedSince(t0); if (elapsedMillis >= profileThreshold) System.out.printf("%n%s: %d%n", toString(), elapsedMillis); } @@ -146,24 +205,28 @@ public class JSR166TestCase extends Test /** * 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) { + main(suite(), args); + } + + /** + * Runs all unit tests in the given test suite. + * Actual behavior influenced by jsr166.* system properties. + */ + static void main(Test suite, String[] args) { if (useSecurityManager) { System.err.println("Setting a permissive security manager"); Policy.setPolicy(permissivePolicy()); System.setSecurityManager(new SecurityManager()); } - int iters = (args.length == 0) ? 1 : Integer.parseInt(args[0]); - - Test s = suite(); - for (int i = 0; i < iters; ++i) { - junit.textui.TestRunner.run(s); + for (int i = 0; i < suiteRuns; i++) { + TestResult result = junit.textui.TestRunner.run(suite); + if (!result.wasSuccessful()) + System.exit(1); System.gc(); System.runFinalization(); } - System.exit(0); } public static TestSuite newTestSuite(Object... suiteOrClasses) { @@ -179,11 +242,48 @@ 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() { return JAVA_CLASS_VERSION >= 53.0; } + /** * 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(), @@ -248,15 +348,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. @@ -339,6 +469,28 @@ public class JSR166TestCase extends Test if (Thread.interrupted()) throw new AssertionFailedError("interrupt status set in main thread"); + + checkForkJoinPoolThreadLeaks(); + } + + /** + * Finds 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; + throw new AssertionFailedError + (String.format("Found leaked ForkJoinPool thread test=%s thread=%s%n", + toString(), name)); + } + } } /** @@ -419,11 +571,11 @@ public class JSR166TestCase extends Test public void threadAssertEquals(Object x, Object y) { try { assertEquals(x, y); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (AssertionFailedError fail) { + threadRecordFailure(fail); + throw fail; + } catch (Throwable fail) { + threadUnexpectedException(fail); } } @@ -435,9 +587,9 @@ public class JSR166TestCase extends Test public void threadAssertSame(Object x, Object y) { try { assertSame(x, y); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; + } catch (AssertionFailedError fail) { + threadRecordFailure(fail); + throw fail; } } @@ -502,16 +654,27 @@ public class JSR166TestCase extends Test void joinPool(ExecutorService exec) { try { exec.shutdown(); - assertTrue("ExecutorService did not terminate in a timely manner", - exec.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)); + if (!exec.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) + fail("ExecutorService " + exec + + " did not terminate in a timely manner"); } catch (SecurityException ok) { // Allowed in case test doesn't have privs - } catch (InterruptedException ie) { + } catch (InterruptedException fail) { fail("Unexpected InterruptedException"); } } /** + * 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()}. */ @@ -527,7 +690,7 @@ public class JSR166TestCase extends Test // No need to optimize the failing case via Thread.join. delay(millis); assertTrue(thread.isAlive()); - } catch (InterruptedException ie) { + } catch (InterruptedException fail) { fail("Unexpected InterruptedException"); } } @@ -549,7 +712,7 @@ public class JSR166TestCase extends Test delay(millis); for (Thread thread : threads) assertTrue(thread.isAlive()); - } catch (InterruptedException ie) { + } catch (InterruptedException fail) { fail("Unexpected InterruptedException"); } } @@ -571,8 +734,8 @@ public class JSR166TestCase extends Test future.get(timeoutMillis, MILLISECONDS); shouldThrow(); } catch (TimeoutException success) { - } catch (Exception e) { - threadUnexpectedException(e); + } catch (Exception fail) { + threadUnexpectedException(fail); } finally { future.cancel(true); } assertTrue(millisElapsedSince(startTime) >= timeoutMillis); } @@ -616,7 +779,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 @@ -729,10 +891,10 @@ public class JSR166TestCase extends Test void sleep(long millis) { try { delay(millis); - } catch (InterruptedException ie) { + } catch (InterruptedException fail) { AssertionFailedError afe = new AssertionFailedError("Unexpected InterruptedException"); - afe.initCause(ie); + afe.initCause(fail); throw afe; } } @@ -770,12 +932,42 @@ public class JSR166TestCase extends Test /** * Returns the number of milliseconds since time given by * startNanoTime, which must have been previously returned from a - * call to {@link System.nanoTime()}. + * call to {@link System#nanoTime()}. */ - long millisElapsedSince(long startNanoTime) { + static long millisElapsedSince(long startNanoTime) { return NANOSECONDS.toMillis(System.nanoTime() - startNanoTime); } +// void assertTerminatesPromptly(long timeoutMillis, Runnable r) { +// long startTime = System.nanoTime(); +// try { +// r.run(); +// } catch (Throwable fail) { threadUnexpectedException(fail); } +// if (millisElapsedSince(startTime) > timeoutMillis/2) +// throw new AssertionFailedError("did not return promptly"); +// } + +// void assertTerminatesPromptly(Runnable r) { +// assertTerminatesPromptly(LONG_DELAY_MS/2, r); +// } + + /** + * Checks that timed f.get() returns the expected value, and does not + * wait for the timeout to elapse before returning. + */ + void checkTimedGet(Future f, T expectedValue, long timeoutMillis) { + long startTime = System.nanoTime(); + try { + assertEquals(expectedValue, f.get(timeoutMillis, MILLISECONDS)); + } catch (Throwable fail) { threadUnexpectedException(fail); } + if (millisElapsedSince(startTime) > timeoutMillis/2) + throw new AssertionFailedError("timed get did not return promptly"); + } + + void checkTimedGet(Future f, T expectedValue) { + checkTimedGet(f, expectedValue, LONG_DELAY_MS); + } + /** * Returns a new started daemon Thread running the given runnable. */ @@ -794,8 +986,8 @@ public class JSR166TestCase extends Test void awaitTermination(Thread t, long timeoutMillis) { try { t.join(timeoutMillis); - } catch (InterruptedException ie) { - threadUnexpectedException(ie); + } catch (InterruptedException fail) { + threadUnexpectedException(fail); } finally { if (t.getState() != Thread.State.TERMINATED) { t.interrupt(); @@ -821,8 +1013,8 @@ public class JSR166TestCase extends Test public final void run() { try { realRun(); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); } } } @@ -876,8 +1068,8 @@ public class JSR166TestCase extends Test threadShouldThrow("InterruptedException"); } catch (InterruptedException success) { threadAssertFalse(Thread.interrupted()); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); } } } @@ -888,8 +1080,8 @@ public class JSR166TestCase extends Test public final T call() { try { return realCall(); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); return null; } } @@ -906,8 +1098,8 @@ public class JSR166TestCase extends Test return result; } catch (InterruptedException success) { threadAssertFalse(Thread.interrupted()); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); } return null; } @@ -947,16 +1139,16 @@ public class JSR166TestCase extends Test public void await(CountDownLatch latch) { try { assertTrue(latch.await(LONG_DELAY_MS, MILLISECONDS)); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); } } public void await(Semaphore semaphore) { try { assertTrue(semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); } } @@ -1147,11 +1339,11 @@ 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) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); } } } @@ -1162,11 +1354,11 @@ 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) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); return null; } } @@ -1190,12 +1382,12 @@ public class JSR166TestCase extends Test public int await() { try { return super.await(2 * LONG_DELAY_MS, MILLISECONDS); - } catch (TimeoutException e) { + } catch (TimeoutException timedOut) { throw new AssertionFailedError("timed out"); - } catch (Exception e) { + } catch (Exception fail) { AssertionFailedError afe = - new AssertionFailedError("Unexpected exception: " + e); - afe.initCause(e); + new AssertionFailedError("Unexpected exception: " + fail); + afe.initCause(fail); throw afe; } } @@ -1223,9 +1415,7 @@ public class JSR166TestCase extends Test q.remove(); shouldThrow(); } catch (NoSuchElementException success) {} - } catch (InterruptedException ie) { - threadUnexpectedException(ie); - } + } catch (InterruptedException fail) { threadUnexpectedException(fail); } } void assertSerialEquals(Object x, Object y) { @@ -1244,8 +1434,8 @@ public class JSR166TestCase extends Test oos.flush(); oos.close(); return bos.toByteArray(); - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); return new byte[0]; } } @@ -1258,9 +1448,38 @@ public class JSR166TestCase extends Test T clone = (T) ois.readObject(); assertSame(o.getClass(), clone.getClass()); return clone; - } catch (Throwable t) { - threadUnexpectedException(t); + } catch (Throwable fail) { + threadUnexpectedException(fail); 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()); + } + } + + public void assertIteratorExhausted(Iterator it) { + try { + it.next(); + shouldThrow(); + } catch (NoSuchElementException success) {} + assertFalse(it.hasNext()); + } }