--- jsr166/src/test/tck/JSR166TestCase.java 2018/01/28 16:20:42 1.241 +++ jsr166/src/test/tck/JSR166TestCase.java 2019/08/22 22:27:57 1.259 @@ -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; } @@ -448,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. @@ -499,6 +518,7 @@ public class JSR166TestCase extends Test ExecutorsTest.suite(), ExecutorCompletionServiceTest.suite(), FutureTaskTest.suite(), + HashtableTest.suite(), LinkedBlockingDequeTest.suite(), LinkedBlockingQueueTest.suite(), LinkedListTest.suite(), @@ -538,6 +558,7 @@ public class JSR166TestCase extends Test "HashMapTest", "LinkedBlockingDeque8Test", "LinkedBlockingQueue8Test", + "LinkedHashMapTest", "LongAccumulatorTest", "LongAdderTest", "SplittableRandomTest", @@ -664,6 +685,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. @@ -1287,22 +1322,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(); } @@ -1310,32 +1356,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); } /** @@ -1343,7 +1367,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); } /** @@ -1351,12 +1375,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()}. @@ -1384,9 +1422,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"); } @@ -1415,11 +1455,12 @@ public class JSR166TestCase extends Test t.join(timeoutMillis); } catch (InterruptedException fail) { threadUnexpectedException(fail); - } finally { - if (t.getState() != Thread.State.TERMINATED) { - t.interrupt(); - threadFail("timed out waiting for thread to terminate"); - } + } + Thread.State state; + if ((state = t.getState()) != Thread.State.TERMINATED) { + t.interrupt(); + threadFail("timed out waiting for thread to terminate; " + + "state=" + state); } } @@ -1446,26 +1487,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; @@ -1478,11 +1499,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()); } } @@ -1492,12 +1514,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"); } } @@ -1509,26 +1532,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"); } } @@ -1585,13 +1590,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) { @@ -1599,13 +1606,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) { @@ -1639,14 +1648,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() { @@ -1702,8 +1703,8 @@ public class JSR166TestCase extends Test return realCompute(); } catch (Throwable fail) { threadUnexpectedException(fail); - return null; } + throw new AssertionError("unreached"); } } @@ -1780,28 +1781,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; } /** @@ -1820,7 +1820,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; } @@ -1845,8 +1845,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) { @@ -2071,4 +2071,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]))); + } + } + } }