--- jsr166/src/test/tck/JSR166TestCase.java 2019/07/28 18:09:25 1.255 +++ jsr166/src/test/tck/JSR166TestCase.java 2021/01/27 02:13:22 1.273 @@ -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; @@ -73,6 +74,7 @@ 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; @@ -149,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. + * *
  • 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 @@ -243,6 +250,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: @@ -284,7 +294,7 @@ 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 ... @@ -310,7 +320,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(); } @@ -354,7 +364,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); } } @@ -657,6 +667,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; @@ -685,6 +701,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. @@ -702,6 +740,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 @@ -740,8 +779,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() { @@ -1062,6 +1101,39 @@ 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. @@ -1076,25 +1148,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); @@ -1121,17 +1178,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); @@ -1139,8 +1207,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()); } /** @@ -1165,28 +1234,137 @@ 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) { + Item[] items = defaultItems; + Item x = (i >= 0 && i < items.length) ? items[i] : new Item(i); + assertFalse(c.remove(x)); + } + 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 @@ -1367,6 +1545,20 @@ public class JSR166TestCase extends Test } /** + * 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()}. @@ -1375,19 +1567,6 @@ 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. @@ -1418,19 +1597,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(); } } } @@ -1458,6 +1651,13 @@ public class JSR166TestCase extends Test } } + Runnable checkedRunnable(Action action) { + return new CheckedRunnable() { + public void realRun() throws Throwable { + action.run(); + }}; + } + public abstract class ThreadShouldThrow extends Thread { protected abstract void realRun() throws Throwable; @@ -1512,7 +1712,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; } } @@ -1696,7 +1896,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) { @@ -1705,7 +1905,7 @@ public class JSR166TestCase extends Test } } - void checkEmpty(BlockingQueue q) { + void checkEmpty(BlockingQueue q) { try { assertTrue(q.isEmpty()); assertEquals(0, q.size()); @@ -1752,6 +1952,7 @@ public class JSR166TestCase extends Test } } + @SuppressWarnings("unchecked") void assertImmutable(Object o) { if (o instanceof Collection) { assertThrows( @@ -1901,7 +2102,7 @@ public class JSR166TestCase extends Test shouldThrow(); } catch (NullPointerException success) {} try { - es.submit((Callable) null); + es.submit((Callable) null); shouldThrow(); } catch (NullPointerException success) {} @@ -1913,7 +2114,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,7 +2273,7 @@ public class JSR166TestCase extends Test else { assertEquals(x.isEmpty(), y.isEmpty()); assertEquals(x.size(), y.size()); - assertEquals(new HashSet(x), new HashSet(y)); + 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]),