--- jsr166/src/test/tck/JSR166TestCase.java 2018/01/23 22:56:37 1.240 +++ jsr166/src/test/tck/JSR166TestCase.java 2019/09/05 21:26:24 1.262 @@ -66,11 +66,14 @@ 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.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; @@ -114,13 +117,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 @@ -277,8 +288,9 @@ public class JSR166TestCase extends Test // 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; } @@ -418,11 +430,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); } } } @@ -449,6 +460,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 +518,7 @@ public class JSR166TestCase extends Test ExecutorsTest.suite(), ExecutorCompletionServiceTest.suite(), FutureTaskTest.suite(), + HashtableTest.suite(), LinkedBlockingDequeTest.suite(), LinkedBlockingQueueTest.suite(), LinkedListTest.suite(), @@ -539,6 +558,7 @@ public class JSR166TestCase extends Test "HashMapTest", "LinkedBlockingDeque8Test", "LinkedBlockingQueue8Test", + "LinkedHashMapTest", "LongAccumulatorTest", "LongAdderTest", "SplittableRandomTest", @@ -599,8 +619,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 +636,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 +657,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 +691,20 @@ 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(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 +722,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 +761,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() { @@ -1288,22 +1329,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 +1363,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 +1374,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 +1382,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()}. @@ -1385,9 +1429,11 @@ public class JSR166TestCase extends Test */ 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"); } @@ -1411,15 +1457,21 @@ public class JSR166TestCase extends Test * 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,26 +1499,6 @@ 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); - } - } - } - public abstract class ThreadShouldThrow extends Thread { protected abstract void realRun() throws Throwable; @@ -1479,11 +1511,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 +1526,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 +1544,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"); } } @@ -1586,13 +1602,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 +1618,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 +1660,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 +1715,8 @@ public class JSR166TestCase extends Test return realCompute(); } catch (Throwable fail) { threadUnexpectedException(fail); - return null; } + throw new AssertionError("unreached"); } } @@ -1781,28 +1793,27 @@ public class JSR166TestCase extends Test } } - void assertImmutable(final Object o) { + 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 +1832,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 +1857,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) { @@ -2072,4 +2083,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]))); + } + } + } }