--- jsr166/src/test/tck/JSR166TestCase.java 2015/10/04 00:59:09 1.160 +++ jsr166/src/test/tck/JSR166TestCase.java 2016/05/21 22:29:45 1.191 @@ -6,6 +6,14 @@ * Pat Fisher, Mike Judd. */ +/* + * @test + * @summary JSR-166 tck tests + * @modules java.management + * @build * + * @run junit/othervm/timeout=1000 -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,6 +28,8 @@ 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; @@ -51,7 +61,9 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadFactory; 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; @@ -109,8 +121,7 @@ import junit.framework.TestSuite; * methods as there are exceptions the method can throw. Sometimes * there are multiple tests per JSR166 method when the different * "normal" behaviors differ significantly. And sometimes testcases - * cover multiple methods when they cannot be tested in - * isolation. + * cover multiple methods when they cannot be tested in isolation. * *
  • The documentation style for testcases is to provide as javadoc * a simple sentence or two describing the property that the testcase @@ -173,6 +184,44 @@ public class JSR166TestCase extends Test private static final int suiteRuns = Integer.getInteger("jsr166.suiteRuns", 1); + /** + * Returns the value of the system property, or NaN if not defined. + */ + 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); } @@ -188,19 +237,32 @@ public class JSR166TestCase extends Test return (regex == null) ? null : Pattern.compile(regex); } + // Instrumentation to debug very rare, but very annoying hung test runs. static volatile TestCase currentTestCase; + // static volatile int currentRun = 0; static { Runnable checkForWedgedTest = new Runnable() { public void run() { - // avoid spurious reports with enormous runsPerTest - final int timeoutMinutes = Math.max(runsPerTest / 10, 1); + // 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)); for (TestCase lastTestCase = currentTestCase;;) { try { MINUTES.sleep(timeoutMinutes); } catch (InterruptedException unexpected) { break; } if (lastTestCase == currentTestCase) { - System.err.println - ("Looks like we're stuck running test: " - + lastTestCase); + System.err.printf( + "Looks like we're stuck running test: %s%n", + lastTestCase); +// System.err.printf( +// "Looks like we're stuck running test: %s (%d/%d)%n", +// lastTestCase, currentRun, runsPerTest); +// System.err.println("availableProcessors=" + +// Runtime.getRuntime().availableProcessors()); +// System.err.printf("cpu model = %s%n", cpuModel()); dumpTestThreads(); + // one stack dump is probably enough; more would be spam + break; } lastTestCase = currentTestCase; }}}; @@ -209,6 +271,16 @@ public class JSR166TestCase extends Test thread.start(); } +// public static String cpuModel() { +// try { +// Matcher matcher = Pattern.compile("model name\\s*: (.*)") +// .matcher(new String( +// Files.readAllBytes(Paths.get("/proc/cpuinfo")), "UTF-8")); +// matcher.find(); +// return matcher.group(1); +// } catch (Exception ex) { return null; } +// } + public void runBare() throws Throwable { currentTestCase = this; if (methodFilter == null @@ -218,6 +290,7 @@ public class JSR166TestCase extends Test protected void runTest() throws Throwable { for (int i = 0; i < runsPerTest; i++) { + // currentRun = i; if (profileTests) runTestProfiled(); else @@ -246,6 +319,34 @@ public class JSR166TestCase extends Test main(suite(), args); } + static class PithyResultPrinter extends junit.textui.ResultPrinter { + PithyResultPrinter(java.io.PrintStream writer) { super(writer); } + long runTime; + public void startTest(Test test) {} + protected void printHeader(long runTime) { + this.runTime = runTime; // defer printing for later + } + protected void printFooter(TestResult result) { + if (result.wasSuccessful()) { + getWriter().println("OK (" + result.runCount() + " tests)" + + " Time: " + elapsedTimeAsString(runTime)); + } else { + getWriter().println("Time: " + elapsedTimeAsString(runTime)); + super.printFooter(result); + } + } + } + + /** + * Returns a TestRunner that doesn't bother with unnecessary + * fluff, like printing a "." for each test case. + */ + static junit.textui.TestRunner newPithyTestRunner() { + junit.textui.TestRunner runner = new junit.textui.TestRunner(); + runner.setPrinter(new PithyResultPrinter(System.out)); + return runner; + } + /** * Runs all unit tests in the given test suite. * Actual behavior influenced by jsr166.* system properties. @@ -257,7 +358,7 @@ public class JSR166TestCase extends Test System.setSecurityManager(new SecurityManager()); } for (int i = 0; i < suiteRuns; i++) { - TestResult result = junit.textui.TestRunner.run(suite); + TestResult result = newPithyTestRunner().doRun(suite); if (!result.wasSuccessful()) System.exit(1); System.gc(); @@ -410,6 +511,7 @@ public class JSR166TestCase extends Test "StampedLockTest", "SubmissionPublisherTest", "ThreadLocalRandom8Test", + "TimeUnit8Test", }; addNamedTestClasses(suite, java8TestClassNames); } @@ -494,11 +596,13 @@ public class JSR166TestCase extends Test public static long LONG_DELAY_MS; /** - * Returns the shortest timed delay. This could - * be reimplemented to use for example a Property. + * 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. + * http://openjdk.java.net/jtreg/command-help.html */ protected long getShortDelay() { - return 50; + return (long) (50 * delayFactor); } /** @@ -590,7 +694,7 @@ public class JSR166TestCase extends Test } /** - * Finds missing try { ... } finally { joinPool(e); } + * Finds missing PoolCleaners */ void checkForkJoinPoolThreadLeaks() throws InterruptedException { Thread[] survivors = new Thread[7]; @@ -749,22 +853,20 @@ public class JSR166TestCase extends Test /** * Delays, via Thread.sleep, for the given millisecond delay, but * if the sleep is shorter than specified, may re-sleep or yield - * until time elapses. + * until time elapses. Ensures that the given time, as measured + * by System.nanoTime(), has elapsed. */ static void delay(long millis) throws InterruptedException { - long startTime = System.nanoTime(); - long ns = millis * 1000 * 1000; - for (;;) { + long nanos = millis * (1000 * 1000); + final long wakeupTime = System.nanoTime() + nanos; + do { if (millis > 0L) Thread.sleep(millis); else // too short to sleep Thread.yield(); - long d = ns - (System.nanoTime() - startTime); - if (d > 0L) - millis = d / (1000 * 1000); - else - break; - } + nanos = wakeupTime - System.nanoTime(); + millis = nanos / (1000 * 1000); + } while (nanos >= 0L); } /** @@ -776,10 +878,51 @@ public class JSR166TestCase extends Test public void close() { joinPool(pool); } } + /** + * An extension of PoolCleaner that has an action to release the pool. + */ + class PoolCleanerWithReleaser extends PoolCleaner { + private final Runnable releaser; + public PoolCleanerWithReleaser(ExecutorService pool, Runnable releaser) { + super(pool); + this.releaser = releaser; + } + public void close() { + try { + releaser.run(); + } finally { + super.close(); + } + } + } + PoolCleaner cleaner(ExecutorService pool) { return new PoolCleaner(pool); } + PoolCleaner cleaner(ExecutorService pool, Runnable releaser) { + return new PoolCleanerWithReleaser(pool, releaser); + } + + PoolCleaner cleaner(ExecutorService pool, CountDownLatch latch) { + return new PoolCleanerWithReleaser(pool, releaser(latch)); + } + + Runnable releaser(final CountDownLatch latch) { + return new Runnable() { public void run() { + do { latch.countDown(); } + while (latch.getCount() > 0); + }}; + } + + 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. */ @@ -793,7 +936,7 @@ public class JSR166TestCase extends Test } finally { // last resort, for the benefit of subsequent tests pool.shutdownNow(); - pool.awaitTermination(SMALL_DELAY_MS, MILLISECONDS); + pool.awaitTermination(MEDIUM_DELAY_MS, MILLISECONDS); } } } catch (SecurityException ok) { @@ -1170,7 +1313,7 @@ public class JSR166TestCase extends Test } finally { if (t.getState() != Thread.State.TERMINATED) { t.interrupt(); - fail("Test timed out"); + threadFail("timed out waiting for thread to terminate"); } } } @@ -1318,24 +1461,43 @@ public class JSR166TestCase extends Test }}; } - public Runnable awaiter(final CountDownLatch latch) { - return new CheckedRunnable() { - public void realRun() throws InterruptedException { - await(latch); - }}; + class LatchAwaiter extends CheckedRunnable { + static final int NEW = 0; + static final int RUNNING = 1; + static final int DONE = 2; + final CountDownLatch latch; + int state = NEW; + LatchAwaiter(CountDownLatch latch) { this.latch = latch; } + public void realRun() throws InterruptedException { + state = 1; + await(latch); + state = 2; + } } - public void await(CountDownLatch latch) { + public LatchAwaiter awaiter(CountDownLatch latch) { + return new LatchAwaiter(latch); + } + + public void await(CountDownLatch latch, long timeoutMillis) { try { - assertTrue(latch.await(LONG_DELAY_MS, MILLISECONDS)); + if (!latch.await(timeoutMillis, MILLISECONDS)) + fail("timed out waiting for CountDownLatch for " + + (timeoutMillis/1000) + " sec"); } catch (Throwable fail) { threadUnexpectedException(fail); } } + public void await(CountDownLatch latch) { + await(latch, LONG_DELAY_MS); + } + public void await(Semaphore semaphore) { try { - assertTrue(semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)); + if (!semaphore.tryAcquire(LONG_DELAY_MS, MILLISECONDS)) + fail("timed out waiting for Semaphore for " + + (LONG_DELAY_MS/1000) + " sec"); } catch (Throwable fail) { threadUnexpectedException(fail); } @@ -1671,4 +1833,12 @@ 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; }}; + } }