--- jsr166/src/test/tck/JSR166TestCase.java 2018/01/23 22:56:37 1.240 +++ jsr166/src/test/tck/JSR166TestCase.java 2022/03/22 16:26:19 1.274 @@ -49,6 +49,7 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.management.ManagementFactory; +import java.lang.management.LockInfo; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.reflect.Constructor; @@ -66,11 +67,15 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.Deque; import java.util.Enumeration; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.PropertyPermission; +import java.util.Queue; +import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -114,13 +119,21 @@ import junit.framework.TestSuite; * *
    * - *
  1. All assertions in code running in generated threads must use - * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link - * #threadAssertEquals}, or {@link #threadAssertNull}, (not - * {@code fail}, {@code assertTrue}, etc.) It is OK (but not - * particularly recommended) for other code to use these forms too. - * Only the most typically used JUnit assertion methods are defined - * this way, but enough to live with. + *
  2. All code not running in the main test thread (manually spawned threads + * or the common fork join pool) must be checked for failure (and completion!). + * Mechanisms that can be used to ensure this are: + *
      + *
    1. Signalling via a synchronizer like AtomicInteger or CountDownLatch + * that the task completed normally, which is checked before returning from + * the test method in the main thread. + *
    2. Using the forms {@link #threadFail}, {@link #threadAssertTrue}, + * or {@link #threadAssertNull}, (not {@code fail}, {@code assertTrue}, etc.) + * Only the most typically used JUnit assertion methods are defined + * this way, but enough to live with. + *
    3. Recording failure explicitly using {@link #threadUnexpectedException} + * or {@link #threadRecordFailure}. + *
    4. Using a wrapper like CheckedRunnable that uses one the mechanisms above. + *
    * *
  3. If you override {@link #setUp} or {@link #tearDown}, make sure * to invoke {@code super.setUp} and {@code super.tearDown} within @@ -138,6 +151,11 @@ import junit.framework.TestSuite; * but even so, if there is ever any doubt, they can all be increased * in one spot to rerun tests on slower platforms. * + * Class Item is used for elements of collections and related + * purposes. Many tests rely on their keys being equal to ints. To + * check these, methods mustEqual, mustContain, etc adapt the JUnit + * assert methods to intercept ints. + * *
  4. All threads generated must be joined inside each test case * method (or {@code fail} to do so) before returning from the * method. The {@code joinPool} method can be used to do this when @@ -177,8 +195,9 @@ import junit.framework.TestSuite; * */ public class JSR166TestCase extends TestCase { + // No longer run with custom securityManagers private static final boolean useSecurityManager = - Boolean.getBoolean("jsr166.useSecurityManager"); + Boolean.getBoolean("jsr166.useSecurityManager"); protected static final boolean expensiveTests = Boolean.getBoolean("jsr166.expensiveTests"); @@ -232,6 +251,9 @@ public class JSR166TestCase extends Test } } + private static final ThreadMXBean THREAD_MXBEAN + = ManagementFactory.getThreadMXBean(); + /** * The scaling factor to apply to standard delays used in tests. * May be initialized from any of: @@ -273,12 +295,13 @@ public class JSR166TestCase extends Test static volatile TestCase currentTestCase; // static volatile int currentRun = 0; static { - Runnable checkForWedgedTest = new Runnable() { public void run() { + Runnable wedgedTestDetector = new Runnable() { public void run() { // Avoid spurious reports with enormous runsPerTest. // A single test case run should never take more than 1 second. // But let's cap it at the high end too ... - final int timeoutMinutes = - Math.min(15, Math.max(runsPerTest / 60, 1)); + final int timeoutMinutesMin = Math.max(runsPerTest / 60, 1) + * Math.max((int) delayFactor, 1); + final int timeoutMinutes = Math.min(15, timeoutMinutesMin); for (TestCase lastTestCase = currentTestCase;;) { try { MINUTES.sleep(timeoutMinutes); } catch (InterruptedException unexpected) { break; } @@ -298,7 +321,7 @@ public class JSR166TestCase extends Test } lastTestCase = currentTestCase; }}}; - Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest"); + Thread thread = new Thread(wedgedTestDetector, "WedgedTestDetector"); thread.setDaemon(true); thread.start(); } @@ -342,7 +365,7 @@ public class JSR166TestCase extends Test // Never report first run of any test; treat it as a // warmup run, notably to trigger all needed classloading, if (i > 0) - System.out.printf("%n%s: %d%n", toString(), elapsedMillis); + System.out.printf("%s: %d%n", toString(), elapsedMillis); } } @@ -385,6 +408,7 @@ public class JSR166TestCase extends Test * Runs all unit tests in the given test suite. * Actual behavior influenced by jsr166.* system properties. */ + @SuppressWarnings("removal") static void main(Test suite, String[] args) { if (useSecurityManager) { System.err.println("Setting a permissive security manager"); @@ -418,11 +442,10 @@ public class JSR166TestCase extends Test for (String testClassName : testClassNames) { try { Class testClass = Class.forName(testClassName); - Method m = testClass.getDeclaredMethod("suite", - new Class[0]); + Method m = testClass.getDeclaredMethod("suite"); suite.addTest(newTestSuite((Test)m.invoke(null))); - } catch (Exception e) { - throw new Error("Missing test class", e); + } catch (ReflectiveOperationException e) { + throw new AssertionError("Missing test class", e); } } } @@ -431,14 +454,18 @@ public class JSR166TestCase extends Test public static final String JAVA_SPECIFICATION_VERSION; static { try { - JAVA_CLASS_VERSION = java.security.AccessController.doPrivileged( + @SuppressWarnings("removal") double jcv = + 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( + JAVA_CLASS_VERSION = jcv; + @SuppressWarnings("removal") String jsv = + java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public String run() { return System.getProperty("java.specification.version");}}); + JAVA_SPECIFICATION_VERSION = jsv; } catch (Throwable t) { throw new Error(t); } @@ -449,6 +476,13 @@ public class JSR166TestCase extends Test public static boolean atLeastJava8() { return JAVA_CLASS_VERSION >= 52.0; } public static boolean atLeastJava9() { return JAVA_CLASS_VERSION >= 53.0; } public static boolean atLeastJava10() { return JAVA_CLASS_VERSION >= 54.0; } + public static boolean atLeastJava11() { return JAVA_CLASS_VERSION >= 55.0; } + public static boolean atLeastJava12() { return JAVA_CLASS_VERSION >= 56.0; } + public static boolean atLeastJava13() { return JAVA_CLASS_VERSION >= 57.0; } + public static boolean atLeastJava14() { return JAVA_CLASS_VERSION >= 58.0; } + public static boolean atLeastJava15() { return JAVA_CLASS_VERSION >= 59.0; } + public static boolean atLeastJava16() { return JAVA_CLASS_VERSION >= 60.0; } + public static boolean atLeastJava17() { return JAVA_CLASS_VERSION >= 61.0; } /** * Collects all JSR166 unit tests as one suite. @@ -500,6 +534,7 @@ public class JSR166TestCase extends Test ExecutorsTest.suite(), ExecutorCompletionServiceTest.suite(), FutureTaskTest.suite(), + HashtableTest.suite(), LinkedBlockingDequeTest.suite(), LinkedBlockingQueueTest.suite(), LinkedListTest.suite(), @@ -539,6 +574,7 @@ public class JSR166TestCase extends Test "HashMapTest", "LinkedBlockingDeque8Test", "LinkedBlockingQueue8Test", + "LinkedHashMapTest", "LongAccumulatorTest", "LongAdderTest", "SplittableRandomTest", @@ -566,6 +602,12 @@ public class JSR166TestCase extends Test addNamedTestClasses(suite, java9TestClassNames); } + if (atLeastJava17()) { + String[] java17TestClassNames = { + "ForkJoinPool19Test", + }; + addNamedTestClasses(suite, java17TestClassNames); + } return suite; } @@ -599,8 +641,8 @@ public class JSR166TestCase extends Test for (String methodName : testMethodNames(testClass)) suite.addTest((Test) c.newInstance(data, methodName)); return suite; - } catch (Exception e) { - throw new Error(e); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); } } @@ -616,14 +658,14 @@ public class JSR166TestCase extends Test if (atLeastJava8()) { String name = testClass.getName(); String name8 = name.replaceAll("Test$", "8Test"); - if (name.equals(name8)) throw new Error(name); + if (name.equals(name8)) throw new AssertionError(name); try { return (Test) Class.forName(name8) - .getMethod("testSuite", new Class[] { dataClass }) + .getMethod("testSuite", dataClass) .invoke(null, data); - } catch (Exception e) { - throw new Error(e); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); } } else { return new TestSuite(); @@ -637,6 +679,12 @@ public class JSR166TestCase extends Test public static long MEDIUM_DELAY_MS; public static long LONG_DELAY_MS; + /** + * A delay significantly longer than LONG_DELAY_MS. + * Use this in a thread that is waited for via awaitTermination(Thread). + */ + public static long LONGER_DELAY_MS; + private static final long RANDOM_TIMEOUT; private static final long RANDOM_EXPIRED_TIMEOUT; private static final TimeUnit RANDOM_TIMEUNIT; @@ -665,6 +713,28 @@ public class JSR166TestCase extends Test static TimeUnit randomTimeUnit() { return RANDOM_TIMEUNIT; } /** + * Returns a random boolean; a "coin flip". + */ + static boolean randomBoolean() { + return ThreadLocalRandom.current().nextBoolean(); + } + + /** + * Returns a random element from given choices. + */ + T chooseRandomly(List choices) { + return choices.get(ThreadLocalRandom.current().nextInt(choices.size())); + } + + /** + * Returns a random element from given choices. + */ + @SuppressWarnings("unchecked") + T chooseRandomly(T... choices) { + return choices[ThreadLocalRandom.current().nextInt(choices.length)]; + } + + /** * Returns the shortest timed delay. This can be scaled up for * slow machines using the jsr166.delay.factor system property, * or via jtreg's -timeoutFactor: flag. @@ -682,6 +752,7 @@ public class JSR166TestCase extends Test SMALL_DELAY_MS = SHORT_DELAY_MS * 5; MEDIUM_DELAY_MS = SHORT_DELAY_MS * 10; LONG_DELAY_MS = SHORT_DELAY_MS * 200; + LONGER_DELAY_MS = 2 * LONG_DELAY_MS; } private static final long TIMEOUT_DELAY_MS @@ -720,8 +791,8 @@ public class JSR166TestCase extends Test */ public void threadRecordFailure(Throwable t) { System.err.println(t); - dumpTestThreads(); - threadFailure.compareAndSet(null, t); + if (threadFailure.compareAndSet(null, t)) + dumpTestThreads(); } public void setUp() { @@ -995,7 +1066,7 @@ public class JSR166TestCase extends Test void joinPool(ExecutorService pool) { try { pool.shutdown(); - if (!pool.awaitTermination(2 * LONG_DELAY_MS, MILLISECONDS)) { + if (!pool.awaitTermination(20 * LONG_DELAY_MS, MILLISECONDS)) { try { threadFail("ExecutorService " + pool + " did not terminate in a timely manner"); @@ -1042,10 +1113,44 @@ public class JSR166TestCase extends Test } } + /** Returns true if thread info might be useful in a thread dump. */ + static boolean threadOfInterest(ThreadInfo info) { + final String name = info.getThreadName(); + String lockName; + if (name == null) + return true; + if (name.equals("Signal Dispatcher") + || name.equals("WedgedTestDetector")) + return false; + if (name.equals("Reference Handler")) { + // Reference Handler stacktrace changed in JDK-8156500 + StackTraceElement[] stackTrace; String methodName; + if ((stackTrace = info.getStackTrace()) != null + && stackTrace.length > 0 + && (methodName = stackTrace[0].getMethodName()) != null + && methodName.equals("waitForReferencePendingList")) + return false; + // jdk8 Reference Handler stacktrace + if ((lockName = info.getLockName()) != null + && lockName.startsWith("java.lang.ref")) + return false; + } + if ((name.equals("Finalizer") || name.equals("Common-Cleaner")) + && (lockName = info.getLockName()) != null + && lockName.startsWith("java.lang.ref")) + return false; + if (name.startsWith("ForkJoinPool.commonPool-worker") + && (lockName = info.getLockName()) != null + && lockName.startsWith("java.util.concurrent.ForkJoinPool")) + return false; + return true; + } + /** * A debugging tool to print stack traces of most threads, as jstack does. * Uninteresting threads are filtered out. */ + @SuppressWarnings("removal") static void dumpTestThreads() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { @@ -1056,25 +1161,10 @@ public class JSR166TestCase extends Test } } - ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); System.err.println("------ stacktrace dump start ------"); - for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { - final String name = info.getThreadName(); - String lockName; - if ("Signal Dispatcher".equals(name)) - continue; - if ("Reference Handler".equals(name) - && (lockName = info.getLockName()) != null - && lockName.startsWith("java.lang.ref.Reference$Lock")) - continue; - if ("Finalizer".equals(name) - && (lockName = info.getLockName()) != null - && lockName.startsWith("java.lang.ref.ReferenceQueue$Lock")) - continue; - if ("checkForWedgedTest".equals(name)) - continue; - System.err.print(info); - } + for (ThreadInfo info : THREAD_MXBEAN.dumpAllThreads(true, true)) + if (threadOfInterest(info)) + System.err.print(info); System.err.println("------ stacktrace dump end ------"); if (sm != null) System.setSecurityManager(sm); @@ -1101,17 +1191,28 @@ public class JSR166TestCase extends Test } /** + * Returns the thread's blocker's class name, if any, else null. + */ + String blockerClassName(Thread thread) { + ThreadInfo threadInfo; LockInfo lockInfo; + if ((threadInfo = THREAD_MXBEAN.getThreadInfo(thread.getId(), 0)) != null + && (lockInfo = threadInfo.getLockInfo()) != null) + return lockInfo.getClassName(); + return null; + } + + /** * Checks that future.get times out, with the default timeout of * {@code timeoutMillis()}. */ - void assertFutureTimesOut(Future future) { + void assertFutureTimesOut(Future future) { assertFutureTimesOut(future, timeoutMillis()); } /** * Checks that future.get times out, with the given millisecond timeout. */ - void assertFutureTimesOut(Future future, long timeoutMillis) { + void assertFutureTimesOut(Future future, long timeoutMillis) { long startTime = System.nanoTime(); try { future.get(timeoutMillis, MILLISECONDS); @@ -1119,8 +1220,9 @@ public class JSR166TestCase extends Test } catch (TimeoutException success) { } catch (Exception fail) { threadUnexpectedException(fail); - } finally { future.cancel(true); } + } assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + assertFalse(future.isDone()); } /** @@ -1145,28 +1247,135 @@ public class JSR166TestCase extends Test /** * The number of elements to place in collections, arrays, etc. + * Must be at least ten; */ - public static final int SIZE = 20; - - // Some convenient Integer constants + public static final int SIZE = 32; - public static final Integer zero = new Integer(0); - public static final Integer one = new Integer(1); - public static final Integer two = new Integer(2); - public static final Integer three = new Integer(3); - public static final Integer four = new Integer(4); - public static final Integer five = new Integer(5); - public static final Integer six = new Integer(6); - public static final Integer seven = new Integer(7); - public static final Integer eight = new Integer(8); - public static final Integer nine = new Integer(9); - public static final Integer m1 = new Integer(-1); - public static final Integer m2 = new Integer(-2); - public static final Integer m3 = new Integer(-3); - public static final Integer m4 = new Integer(-4); - public static final Integer m5 = new Integer(-5); - public static final Integer m6 = new Integer(-6); - public static final Integer m10 = new Integer(-10); + static Item[] seqItems(int size) { + Item[] s = new Item[size]; + for (int i = 0; i < size; ++i) + s[i] = new Item(i); + return s; + } + static Item[] negativeSeqItems(int size) { + Item[] s = new Item[size]; + for (int i = 0; i < size; ++i) + s[i] = new Item(-i); + return s; + } + + // Many tests rely on defaultItems all being sequential nonnegative + public static final Item[] defaultItems = seqItems(SIZE); + + static Item itemFor(int i) { // check cache for defaultItems + Item[] items = defaultItems; + return (i >= 0 && i < items.length) ? items[i] : new Item(i); + } + + public static final Item zero = defaultItems[0]; + public static final Item one = defaultItems[1]; + public static final Item two = defaultItems[2]; + public static final Item three = defaultItems[3]; + public static final Item four = defaultItems[4]; + public static final Item five = defaultItems[5]; + public static final Item six = defaultItems[6]; + public static final Item seven = defaultItems[7]; + public static final Item eight = defaultItems[8]; + public static final Item nine = defaultItems[9]; + public static final Item ten = defaultItems[10]; + + public static final Item[] negativeItems = negativeSeqItems(SIZE); + + public static final Item minusOne = negativeItems[1]; + public static final Item minusTwo = negativeItems[2]; + public static final Item minusThree = negativeItems[3]; + public static final Item minusFour = negativeItems[4]; + public static final Item minusFive = negativeItems[5]; + public static final Item minusSix = negativeItems[6]; + public static final Item minusSeven = negativeItems[7]; + public static final Item minusEight = negativeItems[8]; + public static final Item minusNone = negativeItems[9]; + public static final Item minusTen = negativeItems[10]; + + // elements expected to be missing + public static final Item fortytwo = new Item(42); + public static final Item eightysix = new Item(86); + public static final Item ninetynine = new Item(99); + + // Interop across Item, int + + static void mustEqual(Item x, Item y) { + if (x != y) + assertEquals(x.value, y.value); + } + static void mustEqual(Item x, int y) { + assertEquals(x.value, y); + } + static void mustEqual(int x, Item y) { + assertEquals(x, y.value); + } + static void mustEqual(int x, int y) { + assertEquals(x, y); + } + static void mustEqual(Object x, Object y) { + if (x != y) + assertEquals(x, y); + } + static void mustEqual(int x, Object y) { + if (y instanceof Item) + assertEquals(x, ((Item)y).value); + else fail(); + } + static void mustEqual(Object x, int y) { + if (x instanceof Item) + assertEquals(((Item)x).value, y); + else fail(); + } + static void mustEqual(boolean x, boolean y) { + assertEquals(x, y); + } + static void mustEqual(long x, long y) { + assertEquals(x, y); + } + static void mustEqual(double x, double y) { + assertEquals(x, y); + } + static void mustContain(Collection c, int i) { + assertTrue(c.contains(itemFor(i))); + } + static void mustContain(Collection c, Item i) { + assertTrue(c.contains(i)); + } + static void mustNotContain(Collection c, int i) { + assertFalse(c.contains(itemFor(i))); + } + static void mustNotContain(Collection c, Item i) { + assertFalse(c.contains(i)); + } + static void mustRemove(Collection c, int i) { + assertTrue(c.remove(itemFor(i))); + } + static void mustRemove(Collection c, Item i) { + assertTrue(c.remove(i)); + } + static void mustNotRemove(Collection c, int i) { + assertFalse(c.remove(itemFor(i))); + } + static void mustNotRemove(Collection c, Item i) { + assertFalse(c.remove(i)); + } + static void mustAdd(Collection c, int i) { + assertTrue(c.add(itemFor(i))); + } + static void mustAdd(Collection c, Item i) { + assertTrue(c.add(i)); + } + static void mustOffer(Queue c, int i) { + assertTrue(c.offer(itemFor(i))); + } + static void mustOffer(Queue c, Item i) { + assertTrue(c.offer(i)); + } /** * Runs Runnable r with a security policy that permits precisely @@ -1175,6 +1384,7 @@ public class JSR166TestCase extends Test * security manager. We require that any security manager permit * getPolicy/setPolicy. */ + @SuppressWarnings("removal") public void runWithPermissions(Runnable r, Permission... permissions) { SecurityManager sm = System.getSecurityManager(); if (sm == null) { @@ -1190,8 +1400,10 @@ public class JSR166TestCase extends Test * Runnable. We require that any security manager permit * getPolicy/setPolicy. */ + @SuppressWarnings("removal") public void runWithSecurityManagerWithPermissions(Runnable r, Permission... permissions) { + if (!useSecurityManager) return; SecurityManager sm = System.getSecurityManager(); if (sm == null) { Policy savedPolicy = Policy.getPolicy(); @@ -1228,6 +1440,7 @@ public class JSR166TestCase extends Test * A security policy where new permissions can be dynamically added * or all cleared. */ + @SuppressWarnings("removal") public static class AdjustablePolicy extends java.security.Policy { Permissions perms = new Permissions(); AdjustablePolicy(Permission... permissions) { @@ -1257,6 +1470,7 @@ public class JSR166TestCase extends Test /** * Returns a policy containing all the permissions we ever need. */ + @SuppressWarnings("removal") public static Policy permissivePolicy() { return new AdjustablePolicy // Permissions j.u.c. needs directly @@ -1288,22 +1502,33 @@ public class JSR166TestCase extends Test /** * Spin-waits up to the specified number of milliseconds for the given * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. + * @param waitingForGodot if non-null, an additional condition to satisfy */ - void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) { - long startTime = 0L; - for (;;) { - Thread.State s = thread.getState(); - if (s == Thread.State.BLOCKED || - s == Thread.State.WAITING || - s == Thread.State.TIMED_WAITING) - return; - else if (s == Thread.State.TERMINATED) + void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis, + Callable waitingForGodot) { + for (long startTime = 0L;;) { + switch (thread.getState()) { + default: break; + case BLOCKED: case WAITING: case TIMED_WAITING: + try { + if (waitingForGodot == null || waitingForGodot.call()) + return; + } catch (Throwable fail) { threadUnexpectedException(fail); } + break; + case TERMINATED: fail("Unexpected thread termination"); - else if (startTime == 0L) + } + + if (startTime == 0L) startTime = System.nanoTime(); else if (millisElapsedSince(startTime) > timeoutMillis) { - threadAssertTrue(thread.isAlive()); - fail("timed out waiting for thread to enter wait state"); + assertTrue(thread.isAlive()); + if (waitingForGodot == null + || thread.getState() == Thread.State.RUNNABLE) + fail("timed out waiting for thread to enter wait state"); + else + fail("timed out waiting for condition, thread state=" + + thread.getState()); } Thread.yield(); } @@ -1311,32 +1536,10 @@ public class JSR166TestCase extends Test /** * Spin-waits up to the specified number of milliseconds for the given - * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING, - * and additionally satisfy the given condition. + * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. */ - void waitForThreadToEnterWaitState( - Thread thread, long timeoutMillis, Callable waitingForGodot) { - long startTime = 0L; - for (;;) { - Thread.State s = thread.getState(); - if (s == Thread.State.BLOCKED || - s == Thread.State.WAITING || - s == Thread.State.TIMED_WAITING) { - try { - if (waitingForGodot.call()) - return; - } catch (Throwable fail) { threadUnexpectedException(fail); } - } - else if (s == Thread.State.TERMINATED) - fail("Unexpected thread termination"); - else if (startTime == 0L) - startTime = System.nanoTime(); - else if (millisElapsedSince(startTime) > timeoutMillis) { - threadAssertTrue(thread.isAlive()); - fail("timed out waiting for thread to enter wait state"); - } - Thread.yield(); - } + void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) { + waitForThreadToEnterWaitState(thread, timeoutMillis, null); } /** @@ -1344,7 +1547,7 @@ public class JSR166TestCase extends Test * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. */ void waitForThreadToEnterWaitState(Thread thread) { - waitForThreadToEnterWaitState(thread, LONG_DELAY_MS); + waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, null); } /** @@ -1352,12 +1555,26 @@ public class JSR166TestCase extends Test * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING, * and additionally satisfy the given condition. */ - void waitForThreadToEnterWaitState( - Thread thread, Callable waitingForGodot) { + void waitForThreadToEnterWaitState(Thread thread, + Callable waitingForGodot) { waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, waitingForGodot); } /** + * Spin-waits up to LONG_DELAY_MS milliseconds for the current thread to + * be interrupted. Clears the interrupt status before returning. + */ + void awaitInterrupted() { + for (long startTime = 0L; !Thread.interrupted(); ) { + if (startTime == 0L) + startTime = System.nanoTime(); + else if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out waiting for thread interrupt"); + Thread.yield(); + } + } + + /** * Returns the number of milliseconds since time given by * startNanoTime, which must have been previously returned from a * call to {@link System#nanoTime()}. @@ -1366,28 +1583,17 @@ public class JSR166TestCase extends Test 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 AssertionError("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(); + T actual = null; try { - assertEquals(expectedValue, f.get(timeoutMillis, MILLISECONDS)); + actual = f.get(timeoutMillis, MILLISECONDS); } catch (Throwable fail) { threadUnexpectedException(fail); } + assertEquals(expectedValue, actual); if (millisElapsedSince(startTime) > timeoutMillis/2) throw new AssertionError("timed get did not return promptly"); } @@ -1407,19 +1613,33 @@ public class JSR166TestCase extends Test } /** + * Returns a new started daemon Thread running the given action, + * wrapped in a CheckedRunnable. + */ + Thread newStartedThread(Action action) { + return newStartedThread(checkedRunnable(action)); + } + + /** * Waits for the specified time (in milliseconds) for the thread * to terminate (using {@link Thread#join(long)}), else interrupts * the thread (in the hope that it may terminate later) and fails. */ - void awaitTermination(Thread t, long timeoutMillis) { + void awaitTermination(Thread thread, long timeoutMillis) { try { - t.join(timeoutMillis); + thread.join(timeoutMillis); } catch (InterruptedException fail) { threadUnexpectedException(fail); - } finally { - if (t.getState() != Thread.State.TERMINATED) { - t.interrupt(); - threadFail("timed out waiting for thread to terminate"); + } + if (thread.getState() != Thread.State.TERMINATED) { + String detail = String.format( + "timed out waiting for thread to terminate, thread=%s, state=%s" , + thread, thread.getState()); + try { + threadFail(detail); + } finally { + // Interrupt thread __after__ having reported its stack trace + thread.interrupt(); } } } @@ -1447,24 +1667,11 @@ public class JSR166TestCase extends Test } } - public abstract class RunnableShouldThrow implements Runnable { - protected abstract void realRun() throws Throwable; - - final Class exceptionClass; - - RunnableShouldThrow(Class exceptionClass) { - this.exceptionClass = exceptionClass; - } - - public final void run() { - try { - realRun(); - threadShouldThrow(exceptionClass.getSimpleName()); - } catch (Throwable t) { - if (! exceptionClass.isInstance(t)) - threadUnexpectedException(t); - } - } + Runnable checkedRunnable(Action action) { + return new CheckedRunnable() { + public void realRun() throws Throwable { + action.run(); + }}; } public abstract class ThreadShouldThrow extends Thread { @@ -1479,11 +1686,12 @@ public class JSR166TestCase extends Test public final void run() { try { realRun(); - threadShouldThrow(exceptionClass.getSimpleName()); } catch (Throwable t) { if (! exceptionClass.isInstance(t)) threadUnexpectedException(t); + return; } + threadShouldThrow(exceptionClass.getSimpleName()); } } @@ -1493,12 +1701,13 @@ public class JSR166TestCase extends Test public final void run() { try { realRun(); - threadShouldThrow("InterruptedException"); } catch (InterruptedException success) { threadAssertFalse(Thread.interrupted()); + return; } catch (Throwable fail) { threadUnexpectedException(fail); } + threadShouldThrow("InterruptedException"); } } @@ -1510,26 +1719,8 @@ public class JSR166TestCase extends Test return realCall(); } catch (Throwable fail) { threadUnexpectedException(fail); - return null; - } - } - } - - public abstract class CheckedInterruptedCallable - implements Callable { - protected abstract T realCall() throws Throwable; - - public final T call() { - try { - T result = realCall(); - threadShouldThrow("InterruptedException"); - return result; - } catch (InterruptedException success) { - threadAssertFalse(Thread.interrupted()); - } catch (Throwable fail) { - threadUnexpectedException(fail); } - return null; + throw new AssertionError("unreached"); } } @@ -1537,7 +1728,7 @@ public class JSR166TestCase extends Test public void run() {} } - public static class NoOpCallable implements Callable { + public static class NoOpCallable implements Callable { public Object call() { return Boolean.TRUE; } } @@ -1586,13 +1777,15 @@ public class JSR166TestCase extends Test } public void await(CountDownLatch latch, long timeoutMillis) { + boolean timedOut = false; try { - if (!latch.await(timeoutMillis, MILLISECONDS)) - fail("timed out waiting for CountDownLatch for " - + (timeoutMillis/1000) + " sec"); + timedOut = !latch.await(timeoutMillis, MILLISECONDS); } catch (Throwable fail) { threadUnexpectedException(fail); } + if (timedOut) + fail("timed out waiting for CountDownLatch for " + + (timeoutMillis/1000) + " sec"); } public void await(CountDownLatch latch) { @@ -1600,13 +1793,15 @@ public class JSR166TestCase extends Test } public void await(Semaphore semaphore) { + boolean timedOut = false; try { - if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)) - fail("timed out waiting for Semaphore for " - + (LONG_DELAY_MS/1000) + " sec"); + timedOut = !semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS); } catch (Throwable fail) { threadUnexpectedException(fail); } + if (timedOut) + fail("timed out waiting for Semaphore for " + + (LONG_DELAY_MS/1000) + " sec"); } public void await(CyclicBarrier barrier) { @@ -1640,14 +1835,6 @@ public class JSR166TestCase extends Test public String call() { throw new NullPointerException(); } } - public class SmallPossiblyInterruptedRunnable extends CheckedRunnable { - protected void realRun() { - try { - delay(SMALL_DELAY_MS); - } catch (InterruptedException ok) {} - } - } - public Runnable possiblyInterruptedRunnable(final long timeoutMillis) { return new CheckedRunnable() { protected void realRun() { @@ -1703,8 +1890,8 @@ public class JSR166TestCase extends Test return realCompute(); } catch (Throwable fail) { threadUnexpectedException(fail); - return null; } + throw new AssertionError("unreached"); } } @@ -1725,7 +1912,7 @@ public class JSR166TestCase extends Test public int await() { try { - return super.await(2 * LONG_DELAY_MS, MILLISECONDS); + return super.await(LONGER_DELAY_MS, MILLISECONDS); } catch (TimeoutException timedOut) { throw new AssertionError("timed out"); } catch (Exception fail) { @@ -1734,7 +1921,7 @@ public class JSR166TestCase extends Test } } - void checkEmpty(BlockingQueue q) { + void checkEmpty(BlockingQueue q) { try { assertTrue(q.isEmpty()); assertEquals(0, q.size()); @@ -1781,28 +1968,28 @@ public class JSR166TestCase extends Test } } - void assertImmutable(final Object o) { + @SuppressWarnings("unchecked") + void assertImmutable(Object o) { if (o instanceof Collection) { assertThrows( UnsupportedOperationException.class, - new Runnable() { public void run() { - ((Collection) o).add(null);}}); + () -> ((Collection) o).add(null)); } } @SuppressWarnings("unchecked") T serialClone(T o) { + T clone = null; try { ObjectInputStream ois = new ObjectInputStream (new ByteArrayInputStream(serialBytes(o))); - T clone = (T) ois.readObject(); - if (o == clone) assertImmutable(o); - assertSame(o.getClass(), clone.getClass()); - return clone; + clone = (T) ois.readObject(); } catch (Throwable fail) { threadUnexpectedException(fail); - return null; } + if (o == clone) assertImmutable(o); + else assertSame(o.getClass(), clone.getClass()); + return clone; } /** @@ -1821,7 +2008,7 @@ public class JSR166TestCase extends Test (new ByteArrayInputStream(bos.toByteArray())); T clone = (T) ois.readObject(); if (o == clone) assertImmutable(o); - assertSame(o.getClass(), clone.getClass()); + else assertSame(o.getClass(), clone.getClass()); return clone; } @@ -1846,8 +2033,8 @@ public class JSR166TestCase extends Test } public void assertThrows(Class expectedExceptionClass, - Runnable... throwingActions) { - for (Runnable throwingAction : throwingActions) { + Action... throwingActions) { + for (Action throwingAction : throwingActions) { boolean threw = false; try { throwingAction.run(); } catch (Throwable t) { @@ -1931,7 +2118,7 @@ public class JSR166TestCase extends Test shouldThrow(); } catch (NullPointerException success) {} try { - es.submit((Callable) null); + es.submit((Callable) null); shouldThrow(); } catch (NullPointerException success) {} @@ -1943,7 +2130,7 @@ public class JSR166TestCase extends Test shouldThrow(); } catch (NullPointerException success) {} try { - ses.schedule((Callable) null, + ses.schedule((Callable) null, randomTimeout(), randomTimeUnit()); shouldThrow(); } catch (NullPointerException success) {} @@ -2072,4 +2259,42 @@ public class JSR166TestCase extends Test assertEquals(savedCompletedTaskCount, p.getCompletedTaskCount()); assertEquals(savedQueueSize, p.getQueue().size()); } + + void assertCollectionsEquals(Collection x, Collection y) { + assertEquals(x, y); + assertEquals(y, x); + assertEquals(x.isEmpty(), y.isEmpty()); + assertEquals(x.size(), y.size()); + if (x instanceof List) { + assertEquals(x.toString(), y.toString()); + } + if (x instanceof List || x instanceof Set) { + assertEquals(x.hashCode(), y.hashCode()); + } + if (x instanceof List || x instanceof Deque) { + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + assertTrue(Arrays.equals(x.toArray(new Object[0]), + y.toArray(new Object[0]))); + } + } + + /** + * A weaker form of assertCollectionsEquals which does not insist + * that the two collections satisfy Object#equals(Object), since + * they may use identity semantics as Deques do. + */ + void assertCollectionsEquivalent(Collection x, Collection y) { + if (x instanceof List || x instanceof Set) + assertCollectionsEquals(x, y); + else { + assertEquals(x.isEmpty(), y.isEmpty()); + assertEquals(x.size(), y.size()); + assertEquals(new HashSet(x), new HashSet(y)); + if (x instanceof Deque) { + assertTrue(Arrays.equals(x.toArray(), y.toArray())); + assertTrue(Arrays.equals(x.toArray(new Object[0]), + y.toArray(new Object[0]))); + } + } + } }