--- jsr166/src/test/tck/JSR166TestCase.java 2015/11/09 06:06:54 1.181 +++ jsr166/src/test/tck/JSR166TestCase.java 2017/05/13 19:13:09 1.223 @@ -1,11 +1,45 @@ /* - * 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. */ +/* + * @test + * @summary JSR-166 tck tests, in a number of variations. + * The first is the conformance testing variant, + * while others also test implementation details. + * @build * + * @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; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -20,8 +54,6 @@ 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; @@ -31,6 +63,8 @@ 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.Enumeration; import java.util.Iterator; @@ -50,11 +84,13 @@ import java.util.concurrent.RecursiveAct import java.util.concurrent.RecursiveTask; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.Semaphore; +import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadPoolExecutor; 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; @@ -176,10 +212,42 @@ public class JSR166TestCase extends Test Integer.getInteger("jsr166.suiteRuns", 1); /** - * The scaling factor to apply to standard delays used in tests. + * Returns the value of the system property, or NaN if not defined. */ - private static final int delayFactor = - Integer.getInteger("jsr166.delay.factor", 1); + private static float systemPropertyValue(String name) { + String floatString = System.getProperty(name); + if (floatString == null) + return Float.NaN; + try { + return Float.parseFloat(floatString); + } catch (NumberFormatException ex) { + throw new IllegalArgumentException( + String.format("Bad float value in system property %s=%s", + name, floatString)); + } + } + + /** + * The scaling factor to apply to standard delays used in tests. + * May be initialized from any of: + * - the "jsr166.delay.factor" system property + * - the "test.timeout.factor" system property (as used by jtreg) + * See: http://openjdk.java.net/jtreg/tag-spec.html + * - hard-coded fuzz factor when using a known slowpoke VM + */ + private static final float delayFactor = delayFactor(); + + private static float delayFactor() { + float x; + if (!Float.isNaN(x = systemPropertyValue("jsr166.delay.factor"))) + return x; + if (!Float.isNaN(x = systemPropertyValue("test.timeout.factor"))) + return x; + String prop = System.getProperty("java.vm.version"); + if (prop != null && prop.matches(".*debug.*")) + return 4.0f; // How much slower is fastdebug than product?! + return 1.0f; + } public JSR166TestCase() { super(); } public JSR166TestCase(String name) { super(name); } @@ -232,9 +300,11 @@ public class JSR166TestCase extends Test // 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; } @@ -401,6 +471,7 @@ public class JSR166TestCase extends Test AbstractQueuedLongSynchronizerTest.suite(), ArrayBlockingQueueTest.suite(), ArrayDequeTest.suite(), + ArrayListTest.suite(), AtomicBooleanTest.suite(), AtomicIntegerArrayTest.suite(), AtomicIntegerFieldUpdaterTest.suite(), @@ -423,6 +494,7 @@ public class JSR166TestCase extends Test CopyOnWriteArrayListTest.suite(), CopyOnWriteArraySetTest.suite(), CountDownLatchTest.suite(), + CountedCompleterTest.suite(), CyclicBarrierTest.suite(), DelayQueueTest.suite(), EntryTest.suite(), @@ -451,25 +523,30 @@ 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", + "LinkedBlockingDeque8Test", + "LinkedBlockingQueue8Test", "LongAccumulatorTest", "LongAdderTest", "SplittableRandomTest", "StampedLockTest", "SubmissionPublisherTest", "ThreadLocalRandom8Test", + "TimeUnit8Test", }; addNamedTestClasses(suite, java8TestClassNames); } @@ -477,7 +554,15 @@ public class JSR166TestCase extends Test // Java9+ test classes if (atLeastJava9()) { String[] java9TestClassNames = { - // Currently empty, but expecting varhandle tests + "AtomicBoolean9Test", + "AtomicInteger9Test", + "AtomicIntegerArray9Test", + "AtomicLong9Test", + "AtomicLongArray9Test", + "AtomicReference9Test", + "AtomicReferenceArray9Test", + "ExecutorCompletionService9Test", + "ForkJoinPool9Test", }; addNamedTestClasses(suite, java9TestClassNames); } @@ -488,7 +573,7 @@ public class JSR166TestCase extends Test /** 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()) @@ -555,10 +640,12 @@ public class JSR166TestCase extends Test /** * Returns the shortest timed delay. This can be scaled up for - * slow machines using the jsr166.delay.factor system property. + * slow machines using the jsr166.delay.factor system property, + * or via jtreg's -timeoutFactor: flag. + * http://openjdk.java.net/jtreg/command-help.html */ protected long getShortDelay() { - return 50 * delayFactor; + return (long) (50 * delayFactor); } /** @@ -571,12 +658,17 @@ public class JSR166TestCase extends Test LONG_DELAY_MS = SHORT_DELAY_MS * 200; } + 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; } /** @@ -592,7 +684,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 @@ -871,6 +963,14 @@ public class JSR166TestCase extends Test }}; } + PoolCleaner cleaner(ExecutorService pool, AtomicBoolean flag) { + return new PoolCleanerWithReleaser(pool, releaser(flag)); + } + + Runnable releaser(final AtomicBoolean flag) { + return new Runnable() { public void run() { flag.set(true); }}; + } + /** * Waits out termination of a thread pool or fails doing so. */ @@ -894,7 +994,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; } /** @@ -925,23 +1029,37 @@ public class JSR166TestCase extends Test * Uninteresting threads are filtered out. */ static void dumpTestThreads() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + System.setSecurityManager(null); + } catch (SecurityException giveUp) { + return; + } + } + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); System.err.println("------ stacktrace dump start ------"); for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { - String name = info.getThreadName(); + final String name = info.getThreadName(); + String lockName; if ("Signal Dispatcher".equals(name)) continue; if ("Reference Handler".equals(name) - && info.getLockName().startsWith("java.lang.ref.Reference$Lock")) + && (lockName = info.getLockName()) != null + && lockName.startsWith("java.lang.ref.Reference$Lock")) continue; if ("Finalizer".equals(name) - && info.getLockName().startsWith("java.lang.ref.ReferenceQueue$Lock")) + && (lockName = info.getLockName()) != null + && lockName.startsWith("java.lang.ref.ReferenceQueue$Lock")) continue; if ("checkForWedgedTest".equals(name)) continue; System.err.print(info); } System.err.println("------ stacktrace dump end ------"); + + if (sm != null) System.setSecurityManager(sm); } /** @@ -1025,6 +1143,12 @@ public class JSR166TestCase extends Test } /** + * The maximum number of consecutive spurious wakeups we should + * tolerate (from APIs like LockSupport.park) before failing a test. + */ + static final int MAX_SPURIOUS_WAKEUPS = 10; + + /** * The number of elements to place in collections, arrays, etc. */ public static final int SIZE = 20; @@ -1128,7 +1252,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; @@ -1158,7 +1282,7 @@ public class JSR166TestCase extends Test * Sleeps until the given time has elapsed. * Throws AssertionFailedError if interrupted. */ - void sleep(long millis) { + static void sleep(long millis) { try { delay(millis); } catch (InterruptedException fail) { @@ -1174,7 +1298,7 @@ public class JSR166TestCase extends Test * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING. */ void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) { - long startTime = System.nanoTime(); + long startTime = 0L; for (;;) { Thread.State s = thread.getState(); if (s == Thread.State.BLOCKED || @@ -1183,23 +1307,65 @@ public class JSR166TestCase extends Test return; 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()); - return; + fail("timed out waiting for thread to enter wait state"); } 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, + * and additionally satisfy the given condition. + */ + 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(); + } + } + + /** + * 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); } /** + * 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); + } + + /** * Returns the number of milliseconds since time given by * startNanoTime, which must have been previously returned from a * call to {@link System#nanoTime()}. @@ -1427,16 +1593,20 @@ public class JSR166TestCase extends Test return new LatchAwaiter(latch); } - public void await(CountDownLatch latch) { + public void await(CountDownLatch latch, long timeoutMillis) { try { - if (!latch.await(LONG_DELAY_MS, MILLISECONDS)) + if (!latch.await(timeoutMillis, MILLISECONDS)) fail("timed out waiting for CountDownLatch for " - + (LONG_DELAY_MS/1000) + " sec"); + + (timeoutMillis/1000) + " sec"); } catch (Throwable fail) { threadUnexpectedException(fail); } } + public void await(CountDownLatch latch) { + await(latch, LONG_DELAY_MS); + } + public void await(Semaphore semaphore) { try { if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)) @@ -1671,7 +1841,7 @@ public class JSR166TestCase extends Test * A CyclicBarrier that uses timed await and fails with * AssertionFailedErrors 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() { @@ -1735,12 +1905,22 @@ public class JSR166TestCase extends Test } } + void assertImmutable(final Object o) { + if (o instanceof Collection) { + assertThrows( + UnsupportedOperationException.class, + new Runnable() { public void run() { + ((Collection) o).add(null);}}); + } + } + @SuppressWarnings("unchecked") T serialClone(T o) { 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; } catch (Throwable fail) { @@ -1749,6 +1929,46 @@ public class JSR166TestCase extends Test } } + /** + * 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); + 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) { @@ -1777,4 +1997,22 @@ public class JSR166TestCase extends Test } catch (NoSuchElementException success) {} assertFalse(it.hasNext()); } + + public Callable callableThrowing(final Exception ex) { + return new Callable() { public T call() throws Exception { throw ex; }}; + } + + public Runnable runnableThrowing(final RuntimeException ex) { + return new Runnable() { public void run() { throw ex; }}; + } + + /** A reusable thread pool to be shared by tests. */ + static final ExecutorService cachedThreadPool = + new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 1000L, MILLISECONDS, + new SynchronousQueue()); + + static void shuffle(T[] array) { + Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current()); + } }