--- jsr166/src/test/tck/JSR166TestCase.java 2016/05/23 18:19:48 1.193 +++ jsr166/src/test/tck/JSR166TestCase.java 2022/11/25 16:39:49 1.276 @@ -1,6 +1,7 @@ /* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at + * Written by Doug Lea and Martin Buchholz with assistance from + * members of JCP JSR-166 Expert Group and released to the public + * domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ * Other contributors include Andrew Wright, Jeffrey Hayes, * Pat Fisher, Mike Judd. @@ -8,10 +9,35 @@ /* * @test - * @summary JSR-166 tck tests - * @modules java.management + * @summary JSR-166 tck tests, in a number of variations. + * The first is the conformance testing variant, + * while others also test implementation details. * @build * - * @run junit/othervm/timeout=1000 -Djsr166.testImplementationDetails=true JSR166TestCase + * @modules java.management + * @run junit/othervm/timeout=1000 JSR166TestCase + * @run junit/othervm/timeout=1000 + * --add-opens java.base/java.util.concurrent=ALL-UNNAMED + * --add-opens java.base/java.lang=ALL-UNNAMED + * -Djsr166.testImplementationDetails=true + * JSR166TestCase + * @run junit/othervm/timeout=1000 + * --add-opens java.base/java.util.concurrent=ALL-UNNAMED + * --add-opens java.base/java.lang=ALL-UNNAMED + * -Djsr166.testImplementationDetails=true + * -Djava.util.concurrent.ForkJoinPool.common.parallelism=0 + * JSR166TestCase + * @run junit/othervm/timeout=1000 + * --add-opens java.base/java.util.concurrent=ALL-UNNAMED + * --add-opens java.base/java.lang=ALL-UNNAMED + * -Djsr166.testImplementationDetails=true + * -Djava.util.concurrent.ForkJoinPool.common.parallelism=1 + * -Djava.util.secureRandomSeed=true + * JSR166TestCase + * @run junit/othervm/timeout=1000/policy=tck.policy + * --add-opens java.base/java.util.concurrent=ALL-UNNAMED + * --add-opens java.base/java.lang=ALL-UNNAMED + * -Djsr166.testImplementationDetails=true + * JSR166TestCase */ import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -23,13 +49,12 @@ 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; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.nio.file.Files; -import java.nio.file.Paths; import java.security.CodeSource; import java.security.Permission; import java.security.PermissionCollection; @@ -39,35 +64,46 @@ import java.security.ProtectionDomain; import java.security.SecurityPermission; import java.util.ArrayList; 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; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; import java.util.concurrent.RecursiveAction; import java.util.concurrent.RecursiveTask; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.Semaphore; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestResult; @@ -83,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 @@ -107,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 @@ -146,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"); @@ -201,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: @@ -242,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; } @@ -267,16 +321,18 @@ public class JSR166TestCase extends Test } lastTestCase = currentTestCase; }}}; - Thread thread = new Thread(checkForWedgedTest, "checkForWedgedTest"); + Thread thread = new Thread(wedgedTestDetector, "WedgedTestDetector"); thread.setDaemon(true); thread.start(); } // public static String cpuModel() { // try { -// Matcher matcher = Pattern.compile("model name\\s*: (.*)") +// java.util.regex.Matcher matcher +// = Pattern.compile("model name\\s*: (.*)") // .matcher(new String( -// Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8")); +// java.nio.file.Files.readAllBytes( +// java.nio.file.Paths.get("/proc/cpuinfo")), "UTF-8")); // matcher.find(); // return matcher.group(1); // } catch (Exception ex) { return null; } @@ -309,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); } } @@ -352,11 +408,15 @@ 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"); Policy.setPolicy(permissivePolicy()); - System.setSecurityManager(new SecurityManager()); + try { + System.setSecurityManager(new SecurityManager()); + } catch (Throwable ok) { // failure OK during deprecation + } } for (int i = 0; i < suiteRuns; i++) { TestResult result = newPithyTestRunner().doRun(suite); @@ -385,11 +445,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); } } } @@ -398,31 +457,35 @@ 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); } } - 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 - // As of 2015-09, java9 still uses 52.0 class file version - || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?(9|[0-9][0-9])$"); - } - public static boolean atLeastJava10() { - return JAVA_CLASS_VERSION >= 54.0 - || JAVA_SPECIFICATION_VERSION.matches("^(1\\.)?[0-9][0-9]$"); - } + 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; } + 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. @@ -443,6 +506,7 @@ public class JSR166TestCase extends Test AbstractQueuedLongSynchronizerTest.suite(), ArrayBlockingQueueTest.suite(), ArrayDequeTest.suite(), + ArrayListTest.suite(), AtomicBooleanTest.suite(), AtomicIntegerArrayTest.suite(), AtomicIntegerFieldUpdaterTest.suite(), @@ -465,6 +529,7 @@ public class JSR166TestCase extends Test CopyOnWriteArrayListTest.suite(), CopyOnWriteArraySetTest.suite(), CountDownLatchTest.suite(), + CountedCompleterTest.suite(), CyclicBarrierTest.suite(), DelayQueueTest.suite(), EntryTest.suite(), @@ -472,6 +537,7 @@ public class JSR166TestCase extends Test ExecutorsTest.suite(), ExecutorCompletionServiceTest.suite(), FutureTaskTest.suite(), + HashtableTest.suite(), LinkedBlockingDequeTest.suite(), LinkedBlockingQueueTest.suite(), LinkedListTest.suite(), @@ -493,19 +559,25 @@ public class JSR166TestCase extends Test TreeMapTest.suite(), TreeSetTest.suite(), TreeSubMapTest.suite(), - TreeSubSetTest.suite()); + TreeSubSetTest.suite(), + VectorTest.suite()); // Java8+ test classes if (atLeastJava8()) { String[] java8TestClassNames = { + "ArrayDeque8Test", "Atomic8Test", "CompletableFutureTest", "ConcurrentHashMap8Test", - "CountedCompleterTest", + "CountedCompleter8Test", "DoubleAccumulatorTest", "DoubleAdderTest", "ForkJoinPool8Test", "ForkJoinTask8Test", + "HashMapTest", + "LinkedBlockingDeque8Test", + "LinkedBlockingQueue8Test", + "LinkedHashMapTest", "LongAccumulatorTest", "LongAdderTest", "SplittableRandomTest", @@ -520,18 +592,32 @@ public class JSR166TestCase extends Test // Java9+ test classes if (atLeastJava9()) { String[] java9TestClassNames = { + "AtomicBoolean9Test", + "AtomicInteger9Test", + "AtomicIntegerArray9Test", + "AtomicLong9Test", + "AtomicLongArray9Test", + "AtomicReference9Test", + "AtomicReferenceArray9Test", "ExecutorCompletionService9Test", + "ForkJoinPool9Test", }; addNamedTestClasses(suite, java9TestClassNames); } + if (atLeastJava17()) { + String[] java17TestClassNames = { + "ForkJoinPool19Test", + }; + addNamedTestClasses(suite, java17TestClassNames); + } return suite; } /** Returns list of junit-style test method names in given class. */ public static ArrayList testMethodNames(Class testClass) { Method[] methods = testClass.getDeclaredMethods(); - ArrayList names = new ArrayList(methods.length); + ArrayList names = new ArrayList<>(methods.length); for (Method method : methods) { if (method.getName().startsWith("test") && Modifier.isPublic(method.getModifiers()) @@ -558,8 +644,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); } } @@ -575,14 +661,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(); @@ -597,6 +683,61 @@ public class JSR166TestCase extends Test 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; + static { + ThreadLocalRandom rnd = ThreadLocalRandom.current(); + long[] timeouts = { Long.MIN_VALUE, -1, 0, 1, Long.MAX_VALUE }; + RANDOM_TIMEOUT = timeouts[rnd.nextInt(timeouts.length)]; + RANDOM_EXPIRED_TIMEOUT = timeouts[rnd.nextInt(3)]; + TimeUnit[] timeUnits = TimeUnit.values(); + RANDOM_TIMEUNIT = timeUnits[rnd.nextInt(timeUnits.length)]; + } + + /** + * Returns a timeout for use when any value at all will do. + */ + static long randomTimeout() { return RANDOM_TIMEOUT; } + + /** + * Returns a timeout that means "no waiting", i.e. not positive. + */ + static long randomExpiredTimeout() { return RANDOM_EXPIRED_TIMEOUT; } + + /** + * Returns a random non-null TimeUnit. + */ + 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. @@ -614,14 +755,20 @@ 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 + = (long) (12.0 * Math.cbrt(delayFactor)); + /** - * Returns a timeout in milliseconds to be used in tests that - * verify that operations block or time out. + * Returns a timeout in milliseconds to be used in tests that verify + * that operations block or time out. We want this to be longer + * than the OS scheduling quantum, but not too long, so don't scale + * linearly with delayFactor; we use "crazy" cube root instead. */ - long timeoutMillis() { - return SHORT_DELAY_MS / 4; + static long timeoutMillis() { + return TIMEOUT_DELAY_MS; } /** @@ -637,7 +784,7 @@ public class JSR166TestCase extends Test * The first exception encountered if any threadAssertXXX method fails. */ private final AtomicReference threadFailure - = new AtomicReference(null); + = new AtomicReference<>(null); /** * Records an exception so that it can be rethrown later in the test @@ -647,8 +794,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() { @@ -659,7 +806,7 @@ public class JSR166TestCase extends Test String msg = toString() + ": " + String.format(format, args); System.err.println(msg); dumpTestThreads(); - throw new AssertionFailedError(msg); + throw new AssertionError(msg); } /** @@ -680,12 +827,8 @@ public class JSR166TestCase extends Test throw (RuntimeException) t; else if (t instanceof Exception) throw (Exception) t; - else { - AssertionFailedError afe = - new AssertionFailedError(t.toString()); - afe.initCause(t); - throw afe; - } + else + throw new AssertionError(t.toString(), t); } if (Thread.interrupted()) @@ -719,83 +862,83 @@ public class JSR166TestCase extends Test /** * Just like fail(reason), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadFail(String reason) { try { fail(reason); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; + } catch (AssertionError fail) { + threadRecordFailure(fail); + throw fail; } } /** * Just like assertTrue(b), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadAssertTrue(boolean b) { try { assertTrue(b); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; + } catch (AssertionError fail) { + threadRecordFailure(fail); + throw fail; } } /** * Just like assertFalse(b), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadAssertFalse(boolean b) { try { assertFalse(b); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; + } catch (AssertionError fail) { + threadRecordFailure(fail); + throw fail; } } /** * Just like assertNull(x), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadAssertNull(Object x) { try { assertNull(x); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; + } catch (AssertionError fail) { + threadRecordFailure(fail); + throw fail; } } /** * Just like assertEquals(x, y), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadAssertEquals(long x, long y) { try { assertEquals(x, y); - } catch (AssertionFailedError t) { - threadRecordFailure(t); - throw t; + } catch (AssertionError fail) { + threadRecordFailure(fail); + throw fail; } } /** * Just like assertEquals(x, y), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadAssertEquals(Object x, Object y) { try { assertEquals(x, y); - } catch (AssertionFailedError fail) { + } catch (AssertionError fail) { threadRecordFailure(fail); throw fail; } catch (Throwable fail) { @@ -805,13 +948,13 @@ public class JSR166TestCase extends Test /** * Just like assertSame(x, y), but additionally recording (using - * threadRecordFailure) any AssertionFailedError thrown, so that - * the current testcase will fail. + * threadRecordFailure) any AssertionError thrown, so that the + * current testcase will fail. */ public void threadAssertSame(Object x, Object y) { try { assertSame(x, y); - } catch (AssertionFailedError fail) { + } catch (AssertionError fail) { threadRecordFailure(fail); throw fail; } @@ -833,8 +976,8 @@ public class JSR166TestCase extends Test /** * Records the given exception using {@link #threadRecordFailure}, - * then rethrows the exception, wrapping it in an - * AssertionFailedError if necessary. + * then rethrows the exception, wrapping it in an AssertionError + * if necessary. */ public void threadUnexpectedException(Throwable t) { threadRecordFailure(t); @@ -843,12 +986,8 @@ public class JSR166TestCase extends Test throw (RuntimeException) t; else if (t instanceof Error) throw (Error) t; - else { - AssertionFailedError afe = - new AssertionFailedError("unexpected exception: " + t); - afe.initCause(t); - throw afe; - } + else + throw new AssertionError("unexpected exception: " + t, t); } /** @@ -930,7 +1069,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"); @@ -947,7 +1086,11 @@ public class JSR166TestCase extends Test } } - /** Like Runnable, but with the freedom to throw anything */ + /** + * Like Runnable, but with the freedom to throw anything. + * junit folks had the same idea: + * http://junit.org/junit5/docs/snapshot/api/org/junit/gen5/api/Executable.html + */ interface Action { public void run() throws Throwable; } /** @@ -973,85 +1116,106 @@ 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() { - ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); - System.err.println("------ stacktrace dump start ------"); - for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { - String name = info.getThreadName(); - if ("Signal Dispatcher".equals(name)) - continue; - if ("Reference Handler".equals(name) - && info.getLockName().startsWith("java.lang.ref.Reference$Lock")) - continue; - if ("Finalizer".equals(name) - && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock")) - continue; - if ("checkForWedgedTest".equals(name)) - continue; - System.err.print(info); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + System.setSecurityManager(null); + } catch (SecurityException giveUp) { + return; + } } + + System.err.println("------ stacktrace dump start ------"); + for (ThreadInfo info : THREAD_MXBEAN.dumpAllThreads(true, true)) + if (threadOfInterest(info)) + System.err.print(info); System.err.println("------ stacktrace dump end ------"); - } - /** - * Checks that thread does not terminate within the default - * millisecond delay of {@code timeoutMillis()}. - */ - void assertThreadStaysAlive(Thread thread) { - assertThreadStaysAlive(thread, timeoutMillis()); + if (sm != null) System.setSecurityManager(sm); } /** - * Checks that thread does not terminate within the given millisecond delay. + * Checks that thread eventually enters the expected blocked thread state. */ - void assertThreadStaysAlive(Thread thread, long millis) { - try { - // No need to optimize the failing case via Thread.join. - delay(millis); - assertTrue(thread.isAlive()); - } catch (InterruptedException fail) { - threadFail("Unexpected InterruptedException"); + void assertThreadBlocks(Thread thread, Thread.State expected) { + // always sleep at least 1 ms, with high probability avoiding + // transitory states + for (long retries = LONG_DELAY_MS * 3 / 4; retries-->0; ) { + try { delay(1); } + catch (InterruptedException fail) { + throw new AssertionError("Unexpected InterruptedException", fail); + } + Thread.State s = thread.getState(); + if (s == expected) + return; + else if (s == Thread.State.TERMINATED) + fail("Unexpected thread termination"); } + fail("timed out waiting for thread to enter thread state " + expected); } /** - * Checks that the threads do not terminate within the default - * millisecond delay of {@code timeoutMillis()}. + * Returns the thread's blocker's class name, if any, else null. */ - void assertThreadsStayAlive(Thread... threads) { - assertThreadsStayAlive(timeoutMillis(), threads); - } - - /** - * Checks that the threads do not terminate within the given millisecond delay. - */ - void assertThreadsStayAlive(long millis, Thread... threads) { - try { - // No need to optimize the failing case via Thread.join. - delay(millis); - for (Thread thread : threads) - assertTrue(thread.isAlive()); - } catch (InterruptedException fail) { - threadFail("Unexpected InterruptedException"); - } + 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); @@ -1059,8 +1223,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()); } /** @@ -1078,29 +1243,142 @@ public class JSR166TestCase extends Test } /** - * The number of elements to place in collections, arrays, etc. + * The maximum number of consecutive spurious wakeups we should + * tolerate (from APIs like LockSupport.park) before failing a test. */ - public static final int SIZE = 20; + static final int MAX_SPURIOUS_WAKEUPS = 10; - // Some convenient Integer constants + /** + * The number of elements to place in collections, arrays, etc. + * Must be at least ten; + */ + 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 @@ -1109,6 +1387,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) { @@ -1124,8 +1403,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(); @@ -1133,9 +1414,13 @@ public class JSR166TestCase extends Test Policy.setPolicy(permissivePolicy()); System.setSecurityManager(new SecurityManager()); runWithSecurityManagerWithPermissions(r, permissions); + } catch (UnsupportedOperationException ok) { } finally { - System.setSecurityManager(null); - Policy.setPolicy(savedPolicy); + try { + System.setSecurityManager(null); + Policy.setPolicy(savedPolicy); + } catch (Exception ok) { + } } } else { Policy savedPolicy = Policy.getPolicy(); @@ -1162,6 +1447,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) { @@ -1181,7 +1467,7 @@ public class JSR166TestCase extends Test } public void refresh() {} public String toString() { - List ps = new ArrayList(); + List ps = new ArrayList<>(); for (Enumeration e = perms.elements(); e.hasMoreElements();) ps.add(e.nextElement()); return "AdjustablePolicy with permissions " + ps; @@ -1191,6 +1477,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 @@ -1209,47 +1496,89 @@ public class JSR166TestCase extends Test /** * Sleeps until the given time has elapsed. - * Throws AssertionFailedError if interrupted. + * Throws AssertionError if interrupted. */ - void sleep(long millis) { + static void sleep(long millis) { try { delay(millis); } catch (InterruptedException fail) { - AssertionFailedError afe = - new AssertionFailedError("Unexpected InterruptedException"); - afe.initCause(fail); - throw afe; + throw new AssertionError("Unexpected InterruptedException", fail); } } /** * 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 = System.nanoTime(); - 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"); + } + + if (startTime == 0L) + startTime = System.nanoTime(); else if (millisElapsedSince(startTime) > timeoutMillis) { - threadAssertTrue(thread.isAlive()); - return; + 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(); } } /** - * Waits up to LONG_DELAY_MS for the given thread to enter a wait - * state: BLOCKED, WAITING, or TIMED_WAITING. + * Spin-waits up to the specified number of milliseconds for the given + * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. + */ + void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) { + waitForThreadToEnterWaitState(thread, timeoutMillis, null); + } + + /** + * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to + * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. */ void waitForThreadToEnterWaitState(Thread thread) { - waitForThreadToEnterWaitState(thread, LONG_DELAY_MS); + waitForThreadToEnterWaitState(thread, LONG_DELAY_MS, null); + } + + /** + * Spin-waits up to LONG_DELAY_MS milliseconds for the given thread to + * enter a wait state: BLOCKED, WAITING, or TIMED_WAITING, + * and additionally satisfy the given condition. + */ + 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(); + } } /** @@ -1261,30 +1590,19 @@ 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 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(); + 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 AssertionFailedError("timed get did not return promptly"); + throw new AssertionError("timed get did not return promptly"); } void checkTimedGet(Future f, T expectedValue) { @@ -1302,19 +1620,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(); } } } @@ -1342,24 +1674,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 { @@ -1374,11 +1693,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()); } } @@ -1388,12 +1708,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"); } } @@ -1405,26 +1726,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"); } } @@ -1432,7 +1735,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; } } @@ -1481,13 +1784,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) { @@ -1495,10 +1800,20 @@ public class JSR166TestCase extends Test } public void await(Semaphore semaphore) { + boolean timedOut = false; + try { + 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) { try { - if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)) - fail("timed out waiting for Semaphore for " - + (LONG_DELAY_MS/1000) + " sec"); + barrier.await(LONG_DELAY_MS, MILLISECONDS); } catch (Throwable fail) { threadUnexpectedException(fail); } @@ -1518,7 +1833,7 @@ public class JSR166TestCase extends Test // long startTime = System.nanoTime(); // while (!flag.get()) { // if (millisElapsedSince(startTime) > timeoutMillis) -// throw new AssertionFailedError("timed out"); +// throw new AssertionError("timed out"); // Thread.yield(); // } // } @@ -1527,55 +1842,6 @@ public class JSR166TestCase extends Test public String call() { throw new NullPointerException(); } } - public static class CallableOne implements Callable { - public Integer call() { return one; } - } - - public class ShortRunnable extends CheckedRunnable { - protected void realRun() throws Throwable { - delay(SHORT_DELAY_MS); - } - } - - public class ShortInterruptedRunnable extends CheckedInterruptedRunnable { - protected void realRun() throws InterruptedException { - delay(SHORT_DELAY_MS); - } - } - - public class SmallRunnable extends CheckedRunnable { - protected void realRun() throws Throwable { - delay(SMALL_DELAY_MS); - } - } - - public class SmallPossiblyInterruptedRunnable extends CheckedRunnable { - protected void realRun() { - try { - delay(SMALL_DELAY_MS); - } catch (InterruptedException ok) {} - } - } - - public class SmallCallable extends CheckedCallable { - protected Object realCall() throws InterruptedException { - delay(SMALL_DELAY_MS); - return Boolean.TRUE; - } - } - - public class MediumRunnable extends CheckedRunnable { - protected void realRun() throws Throwable { - delay(MEDIUM_DELAY_MS); - } - } - - public class MediumInterruptedRunnable extends CheckedInterruptedRunnable { - protected void realRun() throws InterruptedException { - delay(MEDIUM_DELAY_MS); - } - } - public Runnable possiblyInterruptedRunnable(final long timeoutMillis) { return new CheckedRunnable() { protected void realRun() { @@ -1585,22 +1851,6 @@ public class JSR166TestCase extends Test }}; } - public class MediumPossiblyInterruptedRunnable extends CheckedRunnable { - protected void realRun() { - try { - delay(MEDIUM_DELAY_MS); - } catch (InterruptedException ok) {} - } - } - - public class LongPossiblyInterruptedRunnable extends CheckedRunnable { - protected void realRun() { - try { - delay(LONG_DELAY_MS); - } catch (InterruptedException ok) {} - } - } - /** * For use as ThreadFactory in constructors */ @@ -1614,59 +1864,6 @@ public class JSR166TestCase extends Test boolean isDone(); } - public static TrackedRunnable trackedRunnable(final long timeoutMillis) { - return new TrackedRunnable() { - private volatile boolean done = false; - public boolean isDone() { return done; } - public void run() { - try { - delay(timeoutMillis); - done = true; - } catch (InterruptedException ok) {} - } - }; - } - - public static class TrackedShortRunnable implements Runnable { - public volatile boolean done = false; - public void run() { - try { - delay(SHORT_DELAY_MS); - done = true; - } catch (InterruptedException ok) {} - } - } - - public static class TrackedSmallRunnable implements Runnable { - public volatile boolean done = false; - public void run() { - try { - delay(SMALL_DELAY_MS); - done = true; - } catch (InterruptedException ok) {} - } - } - - public static class TrackedMediumRunnable implements Runnable { - public volatile boolean done = false; - public void run() { - try { - delay(MEDIUM_DELAY_MS); - done = true; - } catch (InterruptedException ok) {} - } - } - - public static class TrackedLongRunnable implements Runnable { - public volatile boolean done = false; - public void run() { - try { - delay(LONG_DELAY_MS); - done = true; - } catch (InterruptedException ok) {} - } - } - public static class TrackedNoOpRunnable implements Runnable { public volatile boolean done = false; public void run() { @@ -1674,17 +1871,6 @@ public class JSR166TestCase extends Test } } - public static class TrackedCallable implements Callable { - public volatile boolean done = false; - public Object call() { - try { - delay(SMALL_DELAY_MS); - done = true; - } catch (InterruptedException ok) {} - return Boolean.TRUE; - } - } - /** * Analog of CheckedRunnable for RecursiveAction */ @@ -1711,8 +1897,8 @@ public class JSR166TestCase extends Test return realCompute(); } catch (Throwable fail) { threadUnexpectedException(fail); - return null; } + throw new AssertionError("unreached"); } } @@ -1726,32 +1912,29 @@ public class JSR166TestCase extends Test /** * A CyclicBarrier that uses timed await and fails with - * AssertionFailedErrors instead of throwing checked exceptions. + * AssertionErrors instead of throwing checked exceptions. */ - public class CheckedBarrier extends CyclicBarrier { + public static class CheckedBarrier extends CyclicBarrier { public CheckedBarrier(int parties) { super(parties); } public int await() { try { - return super.await(2 * LONG_DELAY_MS, MILLISECONDS); + return super.await(LONGER_DELAY_MS, MILLISECONDS); } catch (TimeoutException timedOut) { - throw new AssertionFailedError("timed out"); + throw new AssertionError("timed out"); } catch (Exception fail) { - AssertionFailedError afe = - new AssertionFailedError("Unexpected exception: " + fail); - afe.initCause(fail); - throw afe; + throw new AssertionError("Unexpected exception: " + fail, fail); } } } - void checkEmpty(BlockingQueue q) { + void checkEmpty(BlockingQueue q) { try { assertTrue(q.isEmpty()); assertEquals(0, q.size()); assertNull(q.peek()); assertNull(q.poll()); - assertNull(q.poll(0, MILLISECONDS)); + assertNull(q.poll(randomExpiredTimeout(), randomTimeUnit())); assertEquals(q.toString(), "[]"); assertTrue(Arrays.equals(q.toArray(), new Object[0])); assertFalse(q.iterator().hasNext()); @@ -1793,34 +1976,81 @@ public class JSR166TestCase extends Test } @SuppressWarnings("unchecked") + void assertImmutable(Object o) { + if (o instanceof Collection) { + assertThrows( + UnsupportedOperationException.class, + () -> ((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(); - assertSame(o.getClass(), clone.getClass()); - return clone; + clone = (T) ois.readObject(); } catch (Throwable fail) { threadUnexpectedException(fail); + } + if (o == clone) assertImmutable(o); + else assertSame(o.getClass(), clone.getClass()); + return clone; + } + + /** + * A version of serialClone that leaves error handling (for + * e.g. NotSerializableException) up to the caller. + */ + @SuppressWarnings("unchecked") + T serialClonePossiblyFailing(T o) + throws ReflectiveOperationException, java.io.IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(o); + oos.flush(); + oos.close(); + ObjectInputStream ois = new ObjectInputStream + (new ByteArrayInputStream(bos.toByteArray())); + T clone = (T) ois.readObject(); + if (o == clone) assertImmutable(o); + else assertSame(o.getClass(), clone.getClass()); + return clone; + } + + /** + * If o implements Cloneable and has a public clone method, + * returns a clone of o, else null. + */ + @SuppressWarnings("unchecked") + T cloneableClone(T o) { + if (!(o instanceof Cloneable)) return null; + final T clone; + try { + clone = (T) o.getClass().getMethod("clone").invoke(o); + } catch (NoSuchMethodException ok) { return null; + } catch (ReflectiveOperationException unexpected) { + throw new Error(unexpected); } + assertNotSame(o, clone); // not 100% guaranteed by spec + assertSame(o.getClass(), clone.getClass()); + return clone; } 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) { threw = true; - if (!expectedExceptionClass.isInstance(t)) { - AssertionFailedError afe = - new AssertionFailedError - ("Expected " + expectedExceptionClass.getName() + - ", got " + t.getClass().getName()); - afe.initCause(t); - threadUnexpectedException(afe); - } + if (!expectedExceptionClass.isInstance(t)) + throw new AssertionError( + "Expected " + expectedExceptionClass.getName() + + ", got " + t.getClass().getName(), + t); } if (!threw) shouldThrow(expectedExceptionClass.getName()); @@ -1849,4 +2079,229 @@ public class JSR166TestCase extends Test 1000L, MILLISECONDS, new SynchronousQueue()); + static void shuffle(T[] array) { + Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current()); + } + + /** + * Returns the same String as would be returned by {@link + * Object#toString}, whether or not the given object's class + * overrides toString(). + * + * @see System#identityHashCode + */ + static String identityString(Object x) { + return x.getClass().getName() + + "@" + Integer.toHexString(System.identityHashCode(x)); + } + + // --- Shared assertions for Executor tests --- + + /** + * Returns maximum number of tasks that can be submitted to given + * pool (with bounded queue) before saturation (when submission + * throws RejectedExecutionException). + */ + static final int saturatedSize(ThreadPoolExecutor pool) { + BlockingQueue q = pool.getQueue(); + return pool.getMaximumPoolSize() + q.size() + q.remainingCapacity(); + } + + @SuppressWarnings("FutureReturnValueIgnored") + void assertNullTaskSubmissionThrowsNullPointerException(Executor e) { + try { + e.execute((Runnable) null); + shouldThrow(); + } catch (NullPointerException success) {} + + if (! (e instanceof ExecutorService)) return; + ExecutorService es = (ExecutorService) e; + try { + es.submit((Runnable) null); + shouldThrow(); + } catch (NullPointerException success) {} + try { + es.submit((Runnable) null, Boolean.TRUE); + shouldThrow(); + } catch (NullPointerException success) {} + try { + es.submit((Callable) null); + shouldThrow(); + } catch (NullPointerException success) {} + + if (! (e instanceof ScheduledExecutorService)) return; + ScheduledExecutorService ses = (ScheduledExecutorService) e; + try { + ses.schedule((Runnable) null, + randomTimeout(), randomTimeUnit()); + shouldThrow(); + } catch (NullPointerException success) {} + try { + ses.schedule((Callable) null, + randomTimeout(), randomTimeUnit()); + shouldThrow(); + } catch (NullPointerException success) {} + try { + ses.scheduleAtFixedRate((Runnable) null, + randomTimeout(), LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + try { + ses.scheduleWithFixedDelay((Runnable) null, + randomTimeout(), LONG_DELAY_MS, MILLISECONDS); + shouldThrow(); + } catch (NullPointerException success) {} + } + + void setRejectedExecutionHandler( + ThreadPoolExecutor p, RejectedExecutionHandler handler) { + p.setRejectedExecutionHandler(handler); + assertSame(handler, p.getRejectedExecutionHandler()); + } + + void assertTaskSubmissionsAreRejected(ThreadPoolExecutor p) { + final RejectedExecutionHandler savedHandler = p.getRejectedExecutionHandler(); + final long savedTaskCount = p.getTaskCount(); + final long savedCompletedTaskCount = p.getCompletedTaskCount(); + final int savedQueueSize = p.getQueue().size(); + final boolean stock = (p.getClass().getClassLoader() == null); + + Runnable r = () -> {}; + Callable c = () -> Boolean.TRUE; + + class Recorder implements RejectedExecutionHandler { + public volatile Runnable r = null; + public volatile ThreadPoolExecutor p = null; + public void reset() { r = null; p = null; } + public void rejectedExecution(Runnable r, ThreadPoolExecutor p) { + assertNull(this.r); + assertNull(this.p); + this.r = r; + this.p = p; + } + } + + // check custom handler is invoked exactly once per task + Recorder recorder = new Recorder(); + setRejectedExecutionHandler(p, recorder); + for (int i = 2; i--> 0; ) { + recorder.reset(); + p.execute(r); + if (stock && p.getClass() == ThreadPoolExecutor.class) + assertSame(r, recorder.r); + assertSame(p, recorder.p); + + recorder.reset(); + assertFalse(p.submit(r).isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + + recorder.reset(); + assertFalse(p.submit(r, Boolean.TRUE).isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + + recorder.reset(); + assertFalse(p.submit(c).isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + + if (p instanceof ScheduledExecutorService) { + ScheduledExecutorService s = (ScheduledExecutorService) p; + ScheduledFuture future; + + recorder.reset(); + future = s.schedule(r, randomTimeout(), randomTimeUnit()); + assertFalse(future.isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + + recorder.reset(); + future = s.schedule(c, randomTimeout(), randomTimeUnit()); + assertFalse(future.isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + + recorder.reset(); + future = s.scheduleAtFixedRate(r, randomTimeout(), LONG_DELAY_MS, MILLISECONDS); + assertFalse(future.isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + + recorder.reset(); + future = s.scheduleWithFixedDelay(r, randomTimeout(), LONG_DELAY_MS, MILLISECONDS); + assertFalse(future.isDone()); + if (stock) assertTrue(!((FutureTask) recorder.r).isDone()); + assertSame(p, recorder.p); + } + } + + // Checking our custom handler above should be sufficient, but + // we add some integration tests of standard handlers. + final AtomicReference thread = new AtomicReference<>(); + final Runnable setThread = () -> thread.set(Thread.currentThread()); + + setRejectedExecutionHandler(p, new ThreadPoolExecutor.AbortPolicy()); + try { + p.execute(setThread); + shouldThrow(); + } catch (RejectedExecutionException success) {} + assertNull(thread.get()); + + setRejectedExecutionHandler(p, new ThreadPoolExecutor.DiscardPolicy()); + p.execute(setThread); + assertNull(thread.get()); + + setRejectedExecutionHandler(p, new ThreadPoolExecutor.CallerRunsPolicy()); + p.execute(setThread); + if (p.isShutdown()) + assertNull(thread.get()); + else + assertSame(Thread.currentThread(), thread.get()); + + setRejectedExecutionHandler(p, savedHandler); + + // check that pool was not perturbed by handlers + assertEquals(savedTaskCount, p.getTaskCount()); + 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]))); + } + } + } }