Index: src/test/org/apache/lucene/TestMergeSchedulerExternal.java =================================================================== --- src/test/org/apache/lucene/TestMergeSchedulerExternal.java (revision 1050438) +++ src/test/org/apache/lucene/TestMergeSchedulerExternal.java (working copy) @@ -105,6 +105,5 @@ assertTrue(mergeCalled); assertTrue(excCalled); dir.close(); - assertTrue(ConcurrentMergeScheduler.anyUnhandledExceptions()); } } Index: src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java =================================================================== --- src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java (revision 1050439) +++ src/test/org/apache/lucene/index/TestIndexWriterOnDiskFull.java (working copy) @@ -209,6 +209,7 @@ System.out.println("TEST: iter=" + iter); // Start with 100 bytes more than we are currently using: + // nocommit make eviler long diskFree = diskUsage+100; int method = iter; @@ -226,12 +227,16 @@ } while(!done) { + if (VERBOSE) { + System.out.println("TEST: cycle..."); + } // Make a new dir that will enforce disk usage: MockDirectoryWrapper dir = new MockDirectoryWrapper(random, new RAMDirectory(startDir)); writer = new IndexWriter(dir, newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer()).setOpenMode(OpenMode.APPEND)); IOException err = null; - + writer.setInfoStream(VERBOSE ? System.out : null); + MergeScheduler ms = writer.getConfig().getMergeScheduler(); for(int x=0;x<2;x++) { if (ms instanceof ConcurrentMergeScheduler) Index: src/test/org/apache/lucene/util/LuceneTestCase.java =================================================================== --- src/test/org/apache/lucene/util/LuceneTestCase.java (revision 1050438) +++ src/test/org/apache/lucene/util/LuceneTestCase.java (working copy) @@ -66,8 +66,9 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.Set; +import java.util.IdentityHashMap; import java.util.Collections; -import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -291,6 +292,7 @@ @Override public void starting(FrameworkMethod method) { // set current method name for logging + System.out.println("\nLTC: start " + method.getName()); LuceneTestCase.this.name = method.getName(); super.starting(method); } @@ -307,12 +309,12 @@ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { uncaughtExceptions.add(new UncaughtExceptionEntry(t, e)); + System.out.println("LTC: thread exc: " + Thread.currentThread().getName()); if (savedUncaughtExceptionHandler != null) savedUncaughtExceptionHandler.uncaughtException(t, e); } }); - ConcurrentMergeScheduler.setTestMode(); savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount(); } @@ -336,6 +338,8 @@ return getClass().getName() + "." + getName(); } + private final static int THREAD_STOP_GRACE_MSEC = 1000; + @After public void tearDown() throws Exception { assertTrue("ensure your setUp() calls super.setUp()!!!", setup); @@ -343,10 +347,68 @@ BooleanQuery.setMaxClauseCount(savedBoolMaxClauseCount); try { + // If test left any running threads, we wait a bit for + // them to complete, and mark the test as failed + final Thread[] stillRunning = new Thread[10]; + final Thread myThread = Thread.currentThread(); + + final long deadline = System.currentTimeMillis() + THREAD_STOP_GRACE_MSEC; + + boolean testLeftLiveThreads = false; + boolean allThreadsFinished = true; + + // nocommit there's no identity hash set? + final Map liveThreads = new IdentityHashMap(); + + // Must loop in case more than 10 threads are still running + while(System.currentTimeMillis() < deadline) { + // nocommit -- not clear from jdocs what happens if + // there are more than 10 threads still running; + // we may have to fallback to .enumerate() directly + // from thread group ourselves? + final int threadCount = Thread.enumerate(stillRunning); + if (threadCount != 1) { + allThreadsFinished = false; + testLeftLiveThreads = true; + for(int threadIdx=0;threadIdx 0) { + System.out.println("LTC: grace " + timeLeft + " msec for thread " + thread.getName()); + thread.join(timeLeft); + } else { + break; + } + } + } + } else { + allThreadsFinished = true; + break; + } + } + + if (!allThreadsFinished) { + // nocommit here we escalate interrupt, stop, + // destroy, ..., crashJVM!! + } + + // nocommit we should clean this up -- print all the + // failure reasons and then if any failure reasons + // call fail(), maybe? + + // nocommit: also, if the main thread failed, we end + // up double-reportign this, ie junit calls it 2 + // failures when it was only one method that failed; + // so somehow if main thread already marked failed, we + // should only print further details of failure, here, + // but not call fail()? + if (!uncaughtExceptions.isEmpty()) { testsFailed = true; - reportAdditionalFailureInfo(); - System.err.println("The following exceptions were thrown by threads:"); + reportAdditionalFailureInfo(); + System.err.println("FAIL: test " + getTestLabel() + " hit exceptions in threads:"); for (UncaughtExceptionEntry entry : uncaughtExceptions) { System.err.println("*** Thread: " + entry.thread.getName() + " ***"); entry.exception.printStackTrace(System.err); @@ -354,6 +416,16 @@ fail("Some threads threw uncaught exceptions!"); } + if (testLeftLiveThreads) { + // nocommit -- don't do this if the test is failing; + // not helpful then + System.err.println("FAIL: test " + getTestLabel() + " left these live threads:"); + for(Thread thread : liveThreads.keySet()) { + System.err.println(thread.getName()); + } + fail("Test left live threads:"); + } + // calling assertSaneFieldCaches here isn't as useful as having test // classes call it directly from the scope where the index readers // are used, because they could be gc'ed just before this tearDown @@ -368,12 +440,6 @@ // isolated in distinct test methods assertSaneFieldCaches(getTestLabel()); - if (ConcurrentMergeScheduler.anyUnhandledExceptions()) { - // Clear the failure so that we don't just keep - // failing subsequent test cases - ConcurrentMergeScheduler.clearUnhandledExceptions(); - fail("ConcurrentMergeScheduler hit unhandled exceptions"); - } } finally { purgeFieldCache(FieldCache.DEFAULT); } @@ -790,11 +856,16 @@ } } + private boolean additionalReported; + // We get here from InterceptTestCaseEvents on the 'failed' event.... public void reportAdditionalFailureInfo() { - System.err.println("NOTE: reproduce with: ant test -Dtestcase=" + getClass().getSimpleName() - + " -Dtestmethod=" + getName() + " -Dtests.seed=" + new TwoLongs(staticSeed, seed) - + reproduceWithExtraParams()); + if (!additionalReported) { + System.err.println("NOTE: reproduce with: ant test -Dtestcase=" + getClass().getSimpleName() + + " -Dtestmethod=" + getName() + " -Dtests.seed=" + new TwoLongs(staticSeed, seed) + + reproduceWithExtraParams()); + additionalReported = true; + } } // extra params that were overridden needed to reproduce the command Index: src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java =================================================================== --- src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java (revision 1050438) +++ src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java (working copy) @@ -69,13 +69,6 @@ protected IndexWriter writer; protected int mergeThreadCount; - public ConcurrentMergeScheduler() { - if (allInstances != null) { - // Only for testing - addMyself(); - } - } - /** Sets the max # simultaneous merge threads that should * be running at once. This must be <= {@link * #setMaxMergeCount}. */ @@ -431,7 +424,6 @@ if (!suppressExceptions) { // suppressExceptions is normally only set during // testing. - anyExceptions = true; handleMergeException(exc); } } @@ -471,48 +463,6 @@ throw new MergePolicy.MergeException(exc, dir); } - static boolean anyExceptions = false; - - /** Used for testing */ - public static boolean anyUnhandledExceptions() { - if (allInstances == null) { - throw new RuntimeException("setTestMode() was not called; often this is because your test case's setUp method fails to call super.setUp in LuceneTestCase"); - } - synchronized(allInstances) { - final int count = allInstances.size(); - // Make sure all outstanding threads are done so we see - // any exceptions they may produce: - for(int i=0;i allInstances; - public static void setTestMode() { - allInstances = new ArrayList(); - } }