pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test-framework / java / org / apache / lucene / util / LuceneTestCase.java
1 package org.apache.lucene.util;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.PrintStream;
23 import java.lang.annotation.Documented;
24 import java.lang.annotation.Inherited;
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.lang.reflect.Constructor;
28 import java.util.*;
29 import java.util.Map.Entry;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.TimeUnit;
33
34 import org.apache.lucene.analysis.Analyzer;
35 import org.apache.lucene.document.Field.Index;
36 import org.apache.lucene.document.Field.Store;
37 import org.apache.lucene.document.Field.TermVector;
38 import org.apache.lucene.document.Field;
39 import org.apache.lucene.index.*;
40 import org.apache.lucene.search.AssertingIndexSearcher;
41 import org.apache.lucene.search.BooleanQuery;
42 import org.apache.lucene.search.FieldCache.CacheEntry;
43 import org.apache.lucene.search.FieldCache;
44 import org.apache.lucene.search.IndexSearcher;
45 import org.apache.lucene.store.Directory;
46 import org.apache.lucene.store.FSDirectory;
47 import org.apache.lucene.store.LockFactory;
48 import org.apache.lucene.store.MockDirectoryWrapper;
49 import org.apache.lucene.util.FieldCacheSanityChecker.Insanity;
50 import org.junit.After;
51 import org.junit.AfterClass;
52 import org.junit.Assert;
53 import org.junit.Assume;
54 import org.junit.Before;
55 import org.junit.BeforeClass;
56 import org.junit.Ignore;
57 import org.junit.Rule;
58 import org.junit.rules.MethodRule;
59 import org.junit.rules.TestWatchman;
60 import org.junit.runner.RunWith;
61 import org.junit.runners.model.FrameworkMethod;
62 import org.junit.runners.model.Statement;
63
64 /**
65  * Base class for all Lucene unit tests, Junit3 or Junit4 variant.
66  * <p>
67  * </p>
68  * <p>
69  * If you
70  * override either <code>setUp()</code> or
71  * <code>tearDown()</code> in your unit test, make sure you
72  * call <code>super.setUp()</code> and
73  * <code>super.tearDown()</code>
74  * </p>
75  *
76  * <code>@After</code> - replaces setup
77  * <code>@Before</code> - replaces teardown
78  * <code>@Test</code> - any public method with this annotation is a test case, regardless
79  * of its name
80  * <p>
81  * <p>
82  * See Junit4 <a href="http://junit.org/junit/javadoc/4.7/">documentation</a> for a complete list of features.
83  * <p>
84  * Import from org.junit rather than junit.framework.
85  * <p>
86  * You should be able to use this class anywhere you used LuceneTestCase
87  * if you annotate your derived class correctly with the annotations above
88  * @see #assertSaneFieldCaches(String)
89  */
90
91 @RunWith(LuceneTestCaseRunner.class)
92 public abstract class LuceneTestCase extends Assert {
93
94   /**
95    * true iff tests are run in verbose mode. Note: if it is false, tests are not
96    * expected to print any messages.
97    */
98   public static final boolean VERBOSE = Boolean.getBoolean("tests.verbose");
99
100   /** Use this constant when creating Analyzers and any other version-dependent stuff.
101    * <p><b>NOTE:</b> Change this when development starts for new Lucene version:
102    */
103   public static final Version TEST_VERSION_CURRENT = Version.LUCENE_35;
104
105   /**
106    * If this is set, it is the only method that should run.
107    */
108   static final String TEST_METHOD;
109   
110   /** Create indexes in this directory, optimally use a subdir, named after the test */
111   public static final File TEMP_DIR;
112   static {
113     String method = System.getProperty("testmethod", "").trim();
114     TEST_METHOD = method.length() == 0 ? null : method;
115     String s = System.getProperty("tempDir", System.getProperty("java.io.tmpdir"));
116     if (s == null)
117       throw new RuntimeException("To run tests, you need to define system property 'tempDir' or 'java.io.tmpdir'.");
118     TEMP_DIR = new File(s);
119     TEMP_DIR.mkdirs();
120   }
121   
122   /** set of directories we created, in afterclass we try to clean these up */
123   private static final Map<File, StackTraceElement[]> tempDirs = Collections.synchronizedMap(new HashMap<File, StackTraceElement[]>());
124
125   // by default we randomly pick a different codec for
126   // each test case (non-J4 tests) and each test class (J4
127   // tests)
128   /** Gets the locale to run tests with */
129   public static final String TEST_LOCALE = System.getProperty("tests.locale", "random");
130   /** Gets the timezone to run tests with */
131   public static final String TEST_TIMEZONE = System.getProperty("tests.timezone", "random");
132   /** Gets the directory to run tests with */
133   public static final String TEST_DIRECTORY = System.getProperty("tests.directory", "random");
134   /** Get the number of times to run tests */
135   public static final int TEST_ITER = Integer.parseInt(System.getProperty("tests.iter", "1"));
136   /** Get the minimum number of times to run tests until a failure happens */
137   public static final int TEST_ITER_MIN = Integer.parseInt(System.getProperty("tests.iter.min", Integer.toString(TEST_ITER)));
138   /** Get the random seed for tests */
139   public static final String TEST_SEED = System.getProperty("tests.seed", "random");
140   /** whether or not nightly tests should run */
141   public static final boolean TEST_NIGHTLY = Boolean.parseBoolean(System.getProperty("tests.nightly", "false"));
142   /** the line file used by LineFileDocs */
143   public static final String TEST_LINE_DOCS_FILE = System.getProperty("tests.linedocsfile", "europarl.lines.txt.gz");
144   /** whether or not to clean threads between test invocations: "false", "perMethod", "perClass" */
145   public static final String TEST_CLEAN_THREADS = System.getProperty("tests.cleanthreads", "perClass");
146
147   /**
148    * A random multiplier which you should use when writing random tests:
149    * multiply it by the number of iterations
150    */
151   public static final int RANDOM_MULTIPLIER = Integer.parseInt(System.getProperty("tests.multiplier", "1"));
152   
153   private int savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
154
155   private volatile Thread.UncaughtExceptionHandler savedUncaughtExceptionHandler = null;
156   
157   /** Used to track if setUp and tearDown are called correctly from subclasses */
158   private static State state = State.INITIAL;
159
160   private static enum State {
161     INITIAL, // no tests ran yet
162     SETUP,   // test has called setUp()
163     RANTEST, // test is running
164     TEARDOWN // test has called tearDown()
165   }
166   
167   private static class UncaughtExceptionEntry {
168     public final Thread thread;
169     public final Throwable exception;
170     
171     public UncaughtExceptionEntry(Thread thread, Throwable exception) {
172       this.thread = thread;
173       this.exception = exception;
174     }
175   }
176   private List<UncaughtExceptionEntry> uncaughtExceptions = Collections.synchronizedList(new ArrayList<UncaughtExceptionEntry>());
177   
178   private static Locale locale;
179   private static Locale savedLocale;
180   private static TimeZone timeZone;
181   private static TimeZone savedTimeZone;
182   
183   protected static Map<MockDirectoryWrapper,StackTraceElement[]> stores;
184   
185   /** @deprecated: until we fix no-fork problems in solr tests */
186   @Deprecated
187   static List<String> testClassesRun = new ArrayList<String>();
188   
189   private static void initRandom() {
190     assert !random.initialized;
191     staticSeed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l1;
192     random.setSeed(staticSeed);
193     random.initialized = true;
194   }
195   
196   @Deprecated
197   private static boolean icuTested = false;
198
199   @BeforeClass
200   public static void beforeClassLuceneTestCaseJ4() {
201     initRandom();
202     state = State.INITIAL;
203     tempDirs.clear();
204     stores = Collections.synchronizedMap(new IdentityHashMap<MockDirectoryWrapper,StackTraceElement[]>());
205     // enable this by default, for IDE consistency with ant tests (as its the default from ant)
206     // TODO: really should be in solr base classes, but some extend LTC directly.
207     // we do this in beforeClass, because some tests currently disable it
208     if (System.getProperty("solr.directoryFactory") == null) {
209       System.setProperty("solr.directoryFactory", "org.apache.solr.core.MockDirectoryFactory");
210     }
211     // this code consumes randoms where 4.0's lucenetestcase would: to make seeds work across both branches.
212     // TODO: doesn't completely work, because what if we get mockrandom codec?!
213     if (random.nextInt(4) != 0) {
214       random.nextInt(); // consume RandomCodecProvider's seed.
215     }
216     // end compatibility random-consumption
217     
218     savedLocale = Locale.getDefault();
219     
220     // START hack to init ICU safely before we randomize locales.
221     // ICU fails during classloading when a special Java7-only locale is the default
222     // see: http://bugs.icu-project.org/trac/ticket/8734
223     if (!icuTested) {
224       icuTested = true;
225       try {
226         Locale.setDefault(Locale.US);
227         Class.forName("com.ibm.icu.util.ULocale");
228       } catch (ClassNotFoundException cnfe) {
229         // ignore if no ICU is in classpath
230       }
231     }
232     // END hack
233     
234     locale = TEST_LOCALE.equals("random") ? randomLocale(random) : localeForName(TEST_LOCALE);
235     Locale.setDefault(locale);
236     savedTimeZone = TimeZone.getDefault();
237     timeZone = TEST_TIMEZONE.equals("random") ? randomTimeZone(random) : TimeZone.getTimeZone(TEST_TIMEZONE);
238     TimeZone.setDefault(timeZone);
239     testsFailed = false;
240     
241     // verify assertions are enabled (do last, for smooth cleanup)
242     if (!Boolean.parseBoolean(System.getProperty("tests.asserts.gracious", "false"))) {
243       assertTrue("assertions are not enabled!", assertionsEnabled());
244     }
245   }
246   
247   @AfterClass
248   public static void afterClassLuceneTestCaseJ4() {
249     State oldState = state; // capture test execution state
250     state = State.INITIAL; // set the state for subsequent tests
251     
252     Throwable problem = null;
253     try {
254       if (!testsFailed) {
255         assertTrue("ensure your setUp() calls super.setUp() and your tearDown() calls super.tearDown()!!!", 
256           oldState == State.INITIAL || oldState == State.TEARDOWN);
257       }
258     } catch (Throwable t) {
259       if (problem == null) problem = t;
260     }
261     
262     if (! "false".equals(TEST_CLEAN_THREADS)) {
263       int rogueThreads = threadCleanup("test class");
264       if (rogueThreads > 0) {
265         // TODO: fail here once the leaks are fixed.
266         System.err.println("RESOURCE LEAK: test class left " + rogueThreads + " thread(s) running");
267       }
268     }
269
270     Locale.setDefault(savedLocale);
271     TimeZone.setDefault(savedTimeZone);
272     System.clearProperty("solr.solr.home");
273     System.clearProperty("solr.data.dir");
274     
275     try {
276       // now look for unclosed resources
277       if (!testsFailed) {
278         checkResourcesAfterClass();
279       }
280     } catch (Throwable t) {
281       if (problem == null) problem = t;
282     }
283     
284     stores = null;
285
286     try {
287       // clear out any temp directories if we can
288       if (!testsFailed) {
289         clearTempDirectoriesAfterClass();
290       }
291     } catch (Throwable t) {
292       if (problem == null) problem = t;
293     }
294
295     // if we had afterClass failures, get some debugging information
296     if (problem != null) {
297       reportPartialFailureInfo();      
298     }
299     
300     // if verbose or tests failed, report some information back
301     if (VERBOSE || testsFailed || problem != null) {
302       printDebuggingInformation();
303     }
304     
305     // reset seed
306     random.setSeed(0L);
307     random.initialized = false;
308     
309     if (problem != null) {
310       throw new RuntimeException(problem);
311     }
312   }
313   
314   /** print some useful debugging information about the environment */
315   private static void printDebuggingInformation() {
316     System.err.println("NOTE: test params are: " +
317         "locale=" + locale +
318         ", timezone=" + (timeZone == null ? "(null)" : timeZone.getID()));
319     System.err.println("NOTE: all tests run in this JVM:");
320     System.err.println(Arrays.toString(testClassesRun.toArray()));
321     System.err.println("NOTE: " + System.getProperty("os.name") + " "
322         + System.getProperty("os.version") + " "
323         + System.getProperty("os.arch") + "/"
324         + System.getProperty("java.vendor") + " "
325         + System.getProperty("java.version") + " "
326         + (Constants.JRE_IS_64BIT ? "(64-bit)" : "(32-bit)") + "/"
327         + "cpus=" + Runtime.getRuntime().availableProcessors() + ","
328         + "threads=" + Thread.activeCount() + ","
329         + "free=" + Runtime.getRuntime().freeMemory() + ","
330         + "total=" + Runtime.getRuntime().totalMemory());
331   }
332   
333   /** check that directories and their resources were closed */
334   private static void checkResourcesAfterClass() {
335     for (MockDirectoryWrapper d : stores.keySet()) {
336       if (d.isOpen()) {
337         StackTraceElement elements[] = stores.get(d);
338         // Look for the first class that is not LuceneTestCase that requested
339         // a Directory. The first two items are of Thread's, so skipping over
340         // them.
341         StackTraceElement element = null;
342         for (int i = 2; i < elements.length; i++) {
343           StackTraceElement ste = elements[i];
344           if (ste.getClassName().indexOf("LuceneTestCase") == -1) {
345             element = ste;
346             break;
347           }
348         }
349         fail("directory of test was not closed, opened from: " + element);
350       }
351     }
352   }
353   
354   /** clear temp directories: this will fail if its not successful */
355   private static void clearTempDirectoriesAfterClass() {
356     for (Entry<File, StackTraceElement[]> entry : tempDirs.entrySet()) {
357       try {
358         _TestUtil.rmDir(entry.getKey());
359       } catch (IOException e) {
360         e.printStackTrace();
361         System.err.println("path " + entry.getKey() + " allocated from");
362         // first two STE's are Java's
363         StackTraceElement[] elements = entry.getValue();
364         for (int i = 2; i < elements.length; i++) {
365           StackTraceElement ste = elements[i];            
366           // print only our code's stack information
367           if (ste.getClassName().indexOf("org.apache.lucene") == -1) break; 
368           System.err.println("\t" + ste);
369         }
370         fail("could not remove temp dir: " + entry.getKey());
371       }
372     }
373   }
374
375   protected static boolean testsFailed; /* true if any tests failed */
376   
377   // This is how we get control when errors occur.
378   // Think of this as start/end/success/failed
379   // events.
380   @Rule
381   public final TestWatchman intercept = new TestWatchman() {
382
383     @Override
384     public void failed(Throwable e, FrameworkMethod method) {
385       // org.junit.internal.AssumptionViolatedException in older releases
386       // org.junit.Assume.AssumptionViolatedException in recent ones
387       if (e.getClass().getName().endsWith("AssumptionViolatedException")) {
388         if (e.getCause() instanceof _TestIgnoredException)
389           e = e.getCause();
390         System.err.print("NOTE: Assume failed in '" + method.getName() + "' (ignored):");
391         if (VERBOSE) {
392           System.err.println();
393           e.printStackTrace(System.err);
394         } else {
395           System.err.print(" ");
396           System.err.println(e.getMessage());
397         }
398       } else {
399         testsFailed = true;
400         reportAdditionalFailureInfo();
401       }
402       super.failed(e, method);
403     }
404
405     @Override
406     public void starting(FrameworkMethod method) {
407       // set current method name for logging
408       LuceneTestCase.this.name = method.getName();
409       State s = state; // capture test execution state
410       state = State.RANTEST; // set the state for subsequent tests
411       if (!testsFailed) {
412         assertTrue("ensure your setUp() calls super.setUp()!!!", s == State.SETUP);
413       }
414       super.starting(method);
415     }
416   };
417   
418   /** 
419    * The thread executing the current test case.
420    * @see #isTestThread()
421    */
422   volatile Thread testCaseThread;
423
424   /** @see #testCaseThread */
425   @Rule
426   public final MethodRule setTestThread = new MethodRule() {
427     public Statement apply(final Statement s, FrameworkMethod fm, Object target) {
428       return new Statement() {
429         public void evaluate() throws Throwable {
430           try {
431             LuceneTestCase.this.testCaseThread = Thread.currentThread();
432             s.evaluate();
433           } finally {
434             LuceneTestCase.this.testCaseThread = null;
435           }
436         }
437       };
438     }
439   };
440
441   @Before
442   public void setUp() throws Exception {
443     seed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l2;
444     random.setSeed(seed);
445     State s = state; // capture test execution state
446     state = State.SETUP; // set the state for subsequent tests
447    
448     savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
449     Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
450       public void uncaughtException(Thread t, Throwable e) {
451         testsFailed = true;
452         uncaughtExceptions.add(new UncaughtExceptionEntry(t, e));
453         if (savedUncaughtExceptionHandler != null)
454           savedUncaughtExceptionHandler.uncaughtException(t, e);
455         }
456     });
457     
458     savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
459
460     if (!testsFailed) {
461       assertTrue("ensure your tearDown() calls super.tearDown()!!!", (s == State.INITIAL || s == State.TEARDOWN));
462     }
463   }
464
465   /**
466    * Forcible purges all cache entries from the FieldCache.
467    * <p>
468    * This method will be called by tearDown to clean up FieldCache.DEFAULT.
469    * If a (poorly written) test has some expectation that the FieldCache
470    * will persist across test methods (ie: a static IndexReader) this
471    * method can be overridden to do nothing.
472    * </p>
473    *
474    * @see FieldCache#purgeAllCaches()
475    */
476   protected void purgeFieldCache(final FieldCache fc) {
477     fc.purgeAllCaches();
478   }
479
480   protected String getTestLabel() {
481     return getClass().getName() + "." + getName();
482   }
483
484   public static void setUseCompoundFile(MergePolicy mp, boolean useCompound) {
485     if (mp instanceof LogMergePolicy) {
486       ((LogMergePolicy) mp).setUseCompoundFile(useCompound);
487     } else if (mp instanceof TieredMergePolicy) {
488       ((TieredMergePolicy) mp).setUseCompoundFile(useCompound);
489     } else {
490       fail("MergePolicy (compound-file) not supported " + mp);
491     }
492   }
493
494   public static void setMergeFactor(MergePolicy mp, int mergeFactor) {
495     if (mp instanceof LogMergePolicy) {
496       ((LogMergePolicy) mp).setMergeFactor(mergeFactor);
497     } else if (mp instanceof TieredMergePolicy) {
498       ((TieredMergePolicy) mp).setMaxMergeAtOnce(mergeFactor);
499       ((TieredMergePolicy) mp).setMaxMergeAtOnceExplicit(mergeFactor);
500     } else {
501       fail("MergePolicy not supported " + mp);
502     }
503   }
504
505   /**
506    * Returns true if and only if the calling thread is the primary thread 
507    * executing the test case. 
508    */
509   protected boolean isTestThread() {
510     assertNotNull("Test case thread not set?", testCaseThread);
511     return Thread.currentThread() == testCaseThread;
512   }
513
514   @After
515   public void tearDown() throws Exception {
516     State oldState = state; // capture test execution state
517     state = State.TEARDOWN; // set the state for subsequent tests
518     
519     // NOTE: with junit 4.7, we don't get a reproduceWith because our Watchman
520     // does not know if something fails in tearDown. so we ensure this happens ourselves for now.
521     // we can remove this if we upgrade to 4.8
522     Throwable problem = null;
523     
524     try {
525       if (!testsFailed) {
526         // Note: we allow a test to go straight from SETUP -> TEARDOWN (without ever entering the RANTEST state)
527         // because if you assume() inside setUp(), it skips the test and the TestWatchman has no way to know...
528         assertTrue("ensure your setUp() calls super.setUp()!!!", oldState == State.RANTEST || oldState == State.SETUP);
529       }
530     } catch (Throwable t) {
531       if (problem == null) problem = t;
532     }
533
534     BooleanQuery.setMaxClauseCount(savedBoolMaxClauseCount);
535
536     // this won't throw any exceptions or fail the test
537     // if we change this, then change this logic
538     checkRogueThreadsAfter();
539     // restore the default uncaught exception handler
540     Thread.setDefaultUncaughtExceptionHandler(savedUncaughtExceptionHandler);
541     
542     try {
543       checkUncaughtExceptionsAfter();
544     } catch (Throwable t) {
545       if (problem == null) problem = t;
546     }
547     
548     try {
549       // calling assertSaneFieldCaches here isn't as useful as having test 
550       // classes call it directly from the scope where the index readers 
551       // are used, because they could be gc'ed just before this tearDown 
552       // method is called.
553       //
554       // But it's better then nothing.
555       //
556       // If you are testing functionality that you know for a fact 
557       // "violates" FieldCache sanity, then you should either explicitly 
558       // call purgeFieldCache at the end of your test method, or refactor
559       // your Test class so that the inconsistant FieldCache usages are 
560       // isolated in distinct test methods  
561       assertSaneFieldCaches(getTestLabel());
562     } catch (Throwable t) {
563       if (problem == null) problem = t;
564     }
565     
566     purgeFieldCache(FieldCache.DEFAULT);
567     
568     if (problem != null) {
569       testsFailed = true;
570       reportAdditionalFailureInfo();
571       throw new RuntimeException(problem);
572     }
573   }
574   
575   /** check if the test still has threads running, we don't want them to 
576    *  fail in a subsequent test and pass the blame to the wrong test */
577   private void checkRogueThreadsAfter() {
578     if ("perMethod".equals(TEST_CLEAN_THREADS)) {
579       int rogueThreads = threadCleanup("test method: '" + getName() + "'");
580       if (!testsFailed && rogueThreads > 0) {
581         System.err.println("RESOURCE LEAK: test method: '" + getName()
582             + "' left " + rogueThreads + " thread(s) running");
583         // TODO: fail, but print seed for now
584         if (uncaughtExceptions.isEmpty()) {
585           reportAdditionalFailureInfo();
586         }
587       }
588     }
589   }
590   
591   /** see if any other threads threw uncaught exceptions, and fail the test if so */
592   private void checkUncaughtExceptionsAfter() {
593     if (!uncaughtExceptions.isEmpty()) {
594       System.err.println("The following exceptions were thrown by threads:");
595       for (UncaughtExceptionEntry entry : uncaughtExceptions) {
596         System.err.println("*** Thread: " + entry.thread.getName() + " ***");
597         entry.exception.printStackTrace(System.err);
598       }
599       fail("Some threads threw uncaught exceptions!");
600     }
601   }
602
603   private final static int THREAD_STOP_GRACE_MSEC = 50;
604   // jvm-wide list of 'rogue threads' we found, so they only get reported once.
605   private final static IdentityHashMap<Thread,Boolean> rogueThreads = new IdentityHashMap<Thread,Boolean>();
606   
607   static {
608     // just a hack for things like eclipse test-runner threads
609     for (Thread t : Thread.getAllStackTraces().keySet()) {
610       rogueThreads.put(t, true);
611     }
612     
613     if (TEST_ITER > 1) {
614       System.out.println("WARNING: you are using -Dtests.iter=n where n > 1, not all tests support this option.");
615       System.out.println("Some may crash or fail: this is not a bug.");
616     }
617   }
618   
619   /**
620    * Looks for leftover running threads, trying to kill them off,
621    * so they don't fail future tests.
622    * returns the number of rogue threads that it found.
623    */
624   private static int threadCleanup(String context) {
625     // educated guess
626     Thread[] stillRunning = new Thread[Thread.activeCount()+1];
627     int threadCount = 0;
628     int rogueCount = 0;
629     
630     if ((threadCount = Thread.enumerate(stillRunning)) > 1) {
631       while (threadCount == stillRunning.length) {
632         // truncated response
633         stillRunning = new Thread[stillRunning.length*2];
634         threadCount = Thread.enumerate(stillRunning);
635       }
636       
637       for (int i = 0; i < threadCount; i++) {
638         Thread t = stillRunning[i];
639           
640         if (t.isAlive() && 
641             !rogueThreads.containsKey(t) && 
642             t != Thread.currentThread() &&
643             // TODO: TimeLimitingCollector starts a thread statically.... WTF?!
644             !t.getName().equals("TimeLimitedCollector timer thread") &&
645             /* its ok to keep your searcher across test cases */
646             (t.getName().startsWith("LuceneTestCase") && context.startsWith("test method")) == false) {
647           System.err.println("WARNING: " + context  + " left thread running: " + t);
648           rogueThreads.put(t, true);
649           rogueCount++;
650           if (t.getName().startsWith("LuceneTestCase")) {
651             System.err.println("PLEASE CLOSE YOUR INDEXSEARCHERS IN YOUR TEST!!!!");
652             continue;
653           } else {
654             // wait on the thread to die of natural causes
655             try {
656               t.join(THREAD_STOP_GRACE_MSEC);
657             } catch (InterruptedException e) { e.printStackTrace(); }
658           }
659           // try to stop the thread:
660           t.setUncaughtExceptionHandler(null);
661           Thread.setDefaultUncaughtExceptionHandler(null);
662           t.interrupt();
663         }
664       }
665     }
666     return rogueCount;
667   }
668   
669   /**
670    * Asserts that FieldCacheSanityChecker does not detect any
671    * problems with FieldCache.DEFAULT.
672    * <p>
673    * If any problems are found, they are logged to System.err
674    * (allong with the msg) when the Assertion is thrown.
675    * </p>
676    * <p>
677    * This method is called by tearDown after every test method,
678    * however IndexReaders scoped inside test methods may be garbage
679    * collected prior to this method being called, causing errors to
680    * be overlooked. Tests are encouraged to keep their IndexReaders
681    * scoped at the class level, or to explicitly call this method
682    * directly in the same scope as the IndexReader.
683    * </p>
684    *
685    * @see org.apache.lucene.util.FieldCacheSanityChecker
686    */
687   protected void assertSaneFieldCaches(final String msg) {
688     final CacheEntry[] entries = FieldCache.DEFAULT.getCacheEntries();
689     Insanity[] insanity = null;
690     try {
691       try {
692         insanity = FieldCacheSanityChecker.checkSanity(entries);
693       } catch (RuntimeException e) {
694         dumpArray(msg + ": FieldCache", entries, System.err);
695         throw e;
696       }
697
698       assertEquals(msg + ": Insane FieldCache usage(s) found",
699               0, insanity.length);
700       insanity = null;
701     } finally {
702
703       // report this in the event of any exception/failure
704       // if no failure, then insanity will be null anyway
705       if (null != insanity) {
706         dumpArray(msg + ": Insane FieldCache usage(s)", insanity, System.err);
707       }
708
709     }
710   }
711   
712   /**
713    * Returns a number of at least <code>i</code>
714    * <p>
715    * The actual number returned will be influenced by whether {@link #TEST_NIGHTLY}
716    * is active and {@link #RANDOM_MULTIPLIER}, but also with some random fudge.
717    */
718   public static int atLeast(Random random, int i) {
719     int min = (TEST_NIGHTLY ? 3*i : i) * RANDOM_MULTIPLIER;
720     int max = min+(min/2);
721     return _TestUtil.nextInt(random, min, max);
722   }
723   
724   public static int atLeast(int i) {
725     return atLeast(random, i);
726   }
727   
728   /**
729    * Returns true if something should happen rarely,
730    * <p>
731    * The actual number returned will be influenced by whether {@link #TEST_NIGHTLY}
732    * is active and {@link #RANDOM_MULTIPLIER}.
733    */
734   public static boolean rarely(Random random) {
735     int p = TEST_NIGHTLY ? 10 : 5;
736     p += (p * Math.log(RANDOM_MULTIPLIER));
737     int min = 100 - Math.min(p, 50); // never more than 50
738     return random.nextInt(100) >= min;
739   }
740   
741   public static boolean rarely() {
742     return rarely(random);
743   }
744   
745   public static boolean usually(Random random) {
746     return !rarely(random);
747   }
748   
749   public static boolean usually() {
750     return usually(random);
751   }
752
753   // These deprecated methods should be removed soon, when all tests using no Epsilon are fixed:
754   
755   @Deprecated
756   static public void assertEquals(double expected, double actual) {
757     assertEquals(null, expected, actual);
758   }
759    
760   @Deprecated
761   static public void assertEquals(String message, double expected, double actual) {
762     assertEquals(message, Double.valueOf(expected), Double.valueOf(actual));
763   }
764
765   @Deprecated
766   static public void assertEquals(float expected, float actual) {
767     assertEquals(null, expected, actual);
768   }
769
770   @Deprecated
771   static public void assertEquals(String message, float expected, float actual) {
772     assertEquals(message, Float.valueOf(expected), Float.valueOf(actual));
773   }
774   
775   public static void assumeTrue(String msg, boolean b) {
776     Assume.assumeNoException(b ? null : new _TestIgnoredException(msg));
777   }
778  
779   public static void assumeFalse(String msg, boolean b) {
780     assumeTrue(msg, !b);
781   }
782   
783   public static void assumeNoException(String msg, Exception e) {
784     Assume.assumeNoException(e == null ? null : new _TestIgnoredException(msg, e));
785   }
786  
787   /**
788    * Convenience method for logging an iterator.
789    *
790    * @param label  String logged before/after the items in the iterator
791    * @param iter   Each next() is toString()ed and logged on it's own line. If iter is null this is logged differnetly then an empty iterator.
792    * @param stream Stream to log messages to.
793    */
794   public static void dumpIterator(String label, Iterator<?> iter,
795                                   PrintStream stream) {
796     stream.println("*** BEGIN " + label + " ***");
797     if (null == iter) {
798       stream.println(" ... NULL ...");
799     } else {
800       while (iter.hasNext()) {
801         stream.println(iter.next().toString());
802       }
803     }
804     stream.println("*** END " + label + " ***");
805   }
806
807   /**
808    * Convenience method for logging an array.  Wraps the array in an iterator and delegates
809    *
810    * @see #dumpIterator(String,Iterator,PrintStream)
811    */
812   public static void dumpArray(String label, Object[] objs,
813                                PrintStream stream) {
814     Iterator<?> iter = (null == objs) ? null : Arrays.asList(objs).iterator();
815     dumpIterator(label, iter, stream);
816   }
817
818   /** create a new index writer config with random defaults */
819   public static IndexWriterConfig newIndexWriterConfig(Version v, Analyzer a) {
820     return newIndexWriterConfig(random, v, a);
821   }
822   
823   /** create a new index writer config with random defaults using the specified random */
824   public static IndexWriterConfig newIndexWriterConfig(Random r, Version v, Analyzer a) {
825     IndexWriterConfig c = new IndexWriterConfig(v, a);
826     if (r.nextBoolean()) {
827       c.setMergePolicy(newTieredMergePolicy());
828     } else if (r.nextBoolean()) {
829       c.setMergePolicy(newLogMergePolicy());
830     } else {
831       c.setMergePolicy(new MockRandomMergePolicy(r));
832     }
833     
834     if (r.nextBoolean()) {
835       c.setMergeScheduler(new SerialMergeScheduler());
836     }
837     if (r.nextBoolean()) {
838       if (rarely(r)) {
839         // crazy value
840         c.setMaxBufferedDocs(_TestUtil.nextInt(r, 2, 7));
841       } else {
842         // reasonable value
843         c.setMaxBufferedDocs(_TestUtil.nextInt(r, 8, 1000));
844       }
845     }
846     if (r.nextBoolean()) {
847       if (rarely(r)) {
848         // crazy value
849         c.setTermIndexInterval(r.nextBoolean() ? _TestUtil.nextInt(r, 1, 31) : _TestUtil.nextInt(r, 129, 1000));
850       } else {
851         // reasonable value
852         c.setTermIndexInterval(_TestUtil.nextInt(r, 32, 128));
853       }
854     }
855     if (r.nextBoolean()) {
856       c.setMaxThreadStates(_TestUtil.nextInt(r, 1, 20));
857     }
858     
859     if (r.nextBoolean()) {
860       c.setMergePolicy(new MockRandomMergePolicy(r));
861     } else {
862       c.setMergePolicy(newLogMergePolicy());
863     }
864     
865     c.setReaderPooling(r.nextBoolean());
866     c.setReaderTermsIndexDivisor(_TestUtil.nextInt(r, 1, 4));
867     return c;
868   }
869
870   public static LogMergePolicy newLogMergePolicy() {
871     return newLogMergePolicy(random);
872   }
873
874   public static TieredMergePolicy newTieredMergePolicy() {
875     return newTieredMergePolicy(random);
876   }
877
878   public static LogMergePolicy newLogMergePolicy(Random r) {
879     LogMergePolicy logmp = r.nextBoolean() ? new LogDocMergePolicy() : new LogByteSizeMergePolicy();
880     logmp.setUseCompoundFile(r.nextBoolean());
881     logmp.setCalibrateSizeByDeletes(r.nextBoolean());
882     if (rarely(r)) {
883       logmp.setMergeFactor(_TestUtil.nextInt(r, 2, 4));
884     } else {
885       logmp.setMergeFactor(_TestUtil.nextInt(r, 5, 50));
886     }
887     return logmp;
888   }
889
890   public static TieredMergePolicy newTieredMergePolicy(Random r) {
891     TieredMergePolicy tmp = new TieredMergePolicy();
892     if (rarely(r)) {
893       tmp.setMaxMergeAtOnce(_TestUtil.nextInt(r, 2, 4));
894       tmp.setMaxMergeAtOnceExplicit(_TestUtil.nextInt(r, 2, 4));
895     } else {
896       tmp.setMaxMergeAtOnce(_TestUtil.nextInt(r, 5, 50));
897       tmp.setMaxMergeAtOnceExplicit(_TestUtil.nextInt(r, 5, 50));
898     }
899     tmp.setMaxMergedSegmentMB(0.2 + r.nextDouble() * 2.0);
900     tmp.setFloorSegmentMB(0.2 + r.nextDouble() * 2.0);
901     tmp.setForceMergeDeletesPctAllowed(0.0 + r.nextDouble() * 30.0);
902     tmp.setSegmentsPerTier(_TestUtil.nextInt(r, 2, 20));
903     tmp.setUseCompoundFile(r.nextBoolean());
904     tmp.setNoCFSRatio(0.1 + r.nextDouble()*0.8);
905     tmp.setReclaimDeletesWeight(r.nextDouble()*4);
906     return tmp;
907   }
908
909   public static LogMergePolicy newLogMergePolicy(boolean useCFS) {
910     LogMergePolicy logmp = newLogMergePolicy();
911     logmp.setUseCompoundFile(useCFS);
912     return logmp;
913   }
914
915   public static LogMergePolicy newLogMergePolicy(boolean useCFS, int mergeFactor) {
916     LogMergePolicy logmp = newLogMergePolicy();
917     logmp.setUseCompoundFile(useCFS);
918     logmp.setMergeFactor(mergeFactor);
919     return logmp;
920   }
921
922   public static LogMergePolicy newLogMergePolicy(int mergeFactor) {
923     LogMergePolicy logmp = newLogMergePolicy();
924     logmp.setMergeFactor(mergeFactor);
925     return logmp;
926   }
927
928   /**
929    * Returns a new Directory instance. Use this when the test does not
930    * care about the specific Directory implementation (most tests).
931    * <p>
932    * The Directory is wrapped with {@link MockDirectoryWrapper}.
933    * By default this means it will be picky, such as ensuring that you
934    * properly close it and all open files in your test. It will emulate
935    * some features of Windows, such as not allowing open files to be
936    * overwritten.
937    */
938   public static MockDirectoryWrapper newDirectory() throws IOException {
939     return newDirectory(random);
940   }
941   
942   /**
943    * Returns a new Directory instance, using the specified random.
944    * See {@link #newDirectory()} for more information.
945    */
946   public static MockDirectoryWrapper newDirectory(Random r) throws IOException {
947     Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
948     MockDirectoryWrapper dir = new MockDirectoryWrapper(r, impl);
949     stores.put(dir, Thread.currentThread().getStackTrace());
950     return dir;
951   }
952   
953   /**
954    * Returns a new Directory instance, with contents copied from the
955    * provided directory. See {@link #newDirectory()} for more
956    * information.
957    */
958   public static MockDirectoryWrapper newDirectory(Directory d) throws IOException {
959     return newDirectory(random, d);
960   }
961   
962   /** Returns a new FSDirectory instance over the given file, which must be a folder. */
963   public static MockDirectoryWrapper newFSDirectory(File f) throws IOException {
964     return newFSDirectory(f, null);
965   }
966   
967   /** Returns a new FSDirectory instance over the given file, which must be a folder. */
968   public static MockDirectoryWrapper newFSDirectory(File f, LockFactory lf) throws IOException {
969     String fsdirClass = TEST_DIRECTORY;
970     if (fsdirClass.equals("random")) {
971       fsdirClass = FS_DIRECTORIES[random.nextInt(FS_DIRECTORIES.length)];
972     }
973     
974     if (fsdirClass.indexOf(".") == -1) {// if not fully qualified, assume .store
975       fsdirClass = "org.apache.lucene.store." + fsdirClass;
976     }
977     
978     Class<? extends FSDirectory> clazz;
979     try {
980       try {
981         clazz = Class.forName(fsdirClass).asSubclass(FSDirectory.class);
982       } catch (ClassCastException e) {
983         // TEST_DIRECTORY is not a sub-class of FSDirectory, so draw one at random
984         fsdirClass = FS_DIRECTORIES[random.nextInt(FS_DIRECTORIES.length)];
985         
986         if (fsdirClass.indexOf(".") == -1) {// if not fully qualified, assume .store
987           fsdirClass = "org.apache.lucene.store." + fsdirClass;
988         }
989         
990         clazz = Class.forName(fsdirClass).asSubclass(FSDirectory.class);
991       }
992       MockDirectoryWrapper dir = new MockDirectoryWrapper(random, newFSDirectoryImpl(clazz, f));
993       if (lf != null) {
994         dir.setLockFactory(lf);
995       }
996       stores.put(dir, Thread.currentThread().getStackTrace());
997       return dir;
998     } catch (Exception e) {
999       throw new RuntimeException(e);
1000     }
1001   }
1002   
1003   /**
1004    * Returns a new Directory instance, using the specified random
1005    * with contents copied from the provided directory. See 
1006    * {@link #newDirectory()} for more information.
1007    */
1008   public static MockDirectoryWrapper newDirectory(Random r, Directory d) throws IOException {
1009     Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
1010     for (String file : d.listAll()) {
1011      d.copy(impl, file, file);
1012     }
1013     MockDirectoryWrapper dir = new MockDirectoryWrapper(r, impl);
1014     stores.put(dir, Thread.currentThread().getStackTrace());
1015     return dir;
1016   }
1017   
1018   /** Returns a new field instance. 
1019    * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1020   public static Field newField(String name, String value, Index index) {
1021     return newField(random, name, value, index);
1022   }
1023   
1024   /** Returns a new field instance. 
1025    * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1026   public static Field newField(String name, String value, Store store, Index index) {
1027     return newField(random, name, value, store, index);
1028   }
1029   
1030   /**
1031    * Returns a new Field instance. Use this when the test does not
1032    * care about some specific field settings (most tests)
1033    * <ul>
1034    *  <li>If the store value is set to Store.NO, sometimes the field will be randomly stored.
1035    *  <li>More term vector data than you ask for might be indexed, for example if you choose YES
1036    *      it might index term vectors with offsets too.
1037    * </ul>
1038    */
1039   public static Field newField(String name, String value, Store store, Index index, TermVector tv) {
1040     return newField(random, name, value, store, index, tv);
1041   }
1042   
1043   /** Returns a new field instance, using the specified random. 
1044    * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1045   public static Field newField(Random random, String name, String value, Index index) {
1046     return newField(random, name, value, Store.NO, index);
1047   }
1048   
1049   /** Returns a new field instance, using the specified random. 
1050    * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1051   public static Field newField(Random random, String name, String value, Store store, Index index) {
1052     return newField(random, name, value, store, index, TermVector.NO);
1053   }
1054   
1055   /** Returns a new field instance, using the specified random. 
1056    * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1057   public static Field newField(Random random, String name, String value, Store store, Index index, TermVector tv) {
1058     if (usually(random)) {
1059       // most of the time, don't modify the params
1060       return new Field(name, value, store, index, tv);
1061     }
1062
1063     if (!index.isIndexed())
1064       return new Field(name, value, store, index, tv);
1065     
1066     if (!store.isStored() && random.nextBoolean())
1067       store = Store.YES; // randomly store it
1068     
1069     tv = randomTVSetting(random, tv);
1070     
1071     return new Field(name, value, store, index, tv);
1072   }
1073   
1074   static final TermVector tvSettings[] = { 
1075     TermVector.NO, TermVector.YES, TermVector.WITH_OFFSETS, 
1076     TermVector.WITH_POSITIONS, TermVector.WITH_POSITIONS_OFFSETS 
1077   };
1078   
1079   private static TermVector randomTVSetting(Random random, TermVector minimum) {
1080     switch(minimum) {
1081       case NO: return tvSettings[_TestUtil.nextInt(random, 0, tvSettings.length-1)];
1082       case YES: return tvSettings[_TestUtil.nextInt(random, 1, tvSettings.length-1)];
1083       case WITH_OFFSETS: return random.nextBoolean() ? TermVector.WITH_OFFSETS 
1084           : TermVector.WITH_POSITIONS_OFFSETS;
1085       case WITH_POSITIONS: return random.nextBoolean() ? TermVector.WITH_POSITIONS 
1086           : TermVector.WITH_POSITIONS_OFFSETS;
1087       default: return TermVector.WITH_POSITIONS_OFFSETS;
1088     }
1089   }
1090   
1091   /** return a random Locale from the available locales on the system */
1092   public static Locale randomLocale(Random random) {
1093     Locale locales[] = Locale.getAvailableLocales();
1094     return locales[random.nextInt(locales.length)];
1095   }
1096   
1097   /** return a random TimeZone from the available timezones on the system */
1098   public static TimeZone randomTimeZone(Random random) {
1099     String tzIds[] = TimeZone.getAvailableIDs();
1100     return TimeZone.getTimeZone(tzIds[random.nextInt(tzIds.length)]);
1101   }
1102   
1103   /** return a Locale object equivalent to its programmatic name */
1104   public static Locale localeForName(String localeName) {
1105     String elements[] = localeName.split("\\_");
1106     switch(elements.length) {
1107       case 3: return new Locale(elements[0], elements[1], elements[2]);
1108       case 2: return new Locale(elements[0], elements[1]);
1109       case 1: return new Locale(elements[0]);
1110       default: throw new IllegalArgumentException("Invalid Locale: " + localeName);
1111     }
1112   }
1113
1114   private static final String FS_DIRECTORIES[] = {
1115     "SimpleFSDirectory",
1116     "NIOFSDirectory",
1117     "MMapDirectory"
1118   };
1119
1120   private static final String CORE_DIRECTORIES[] = {
1121     "RAMDirectory",
1122     FS_DIRECTORIES[0], FS_DIRECTORIES[1], FS_DIRECTORIES[2]
1123   };
1124   
1125   public static String randomDirectory(Random random) {
1126     if (rarely(random)) {
1127       return CORE_DIRECTORIES[random.nextInt(CORE_DIRECTORIES.length)];
1128     } else {
1129       return "RAMDirectory";
1130     }
1131   }
1132
1133   private static Directory newFSDirectoryImpl(
1134       Class<? extends FSDirectory> clazz, File file)
1135       throws IOException {
1136     FSDirectory d = null;
1137     try {
1138       // Assuming every FSDirectory has a ctor(File), but not all may take a
1139       // LockFactory too, so setting it afterwards.
1140       Constructor<? extends FSDirectory> ctor = clazz.getConstructor(File.class);
1141       d = ctor.newInstance(file);
1142     } catch (Exception e) {
1143       d = FSDirectory.open(file);
1144     }
1145     return d;
1146   }
1147
1148   /**
1149    * Registers a temp directory that will be deleted when tests are done. This
1150    * is used by {@link _TestUtil#getTempDir(String)} and
1151    * {@link _TestUtil#unzip(File, File)}, so you should call these methods when
1152    * possible.
1153    */
1154   static void registerTempDir(File tmpFile) {
1155     tempDirs.put(tmpFile.getAbsoluteFile(), Thread.currentThread().getStackTrace());
1156   }
1157   
1158   static Directory newDirectoryImpl(Random random, String clazzName) {
1159     if (clazzName.equals("random"))
1160       clazzName = randomDirectory(random);
1161     if (clazzName.indexOf(".") == -1) // if not fully qualified, assume .store
1162       clazzName = "org.apache.lucene.store." + clazzName;
1163     try {
1164       final Class<? extends Directory> clazz = Class.forName(clazzName).asSubclass(Directory.class);
1165       // If it is a FSDirectory type, try its ctor(File)
1166       if (FSDirectory.class.isAssignableFrom(clazz)) {
1167         final File dir = _TestUtil.getTempDir("index");
1168         dir.mkdirs(); // ensure it's created so we 'have' it.
1169         return newFSDirectoryImpl(clazz.asSubclass(FSDirectory.class), dir);
1170       }
1171
1172       // try empty ctor
1173       return clazz.newInstance();
1174     } catch (Exception e) {
1175       throw new RuntimeException(e);
1176     } 
1177   }
1178   
1179   /** create a new searcher over the reader.
1180    * This searcher might randomly use threads. */
1181   public static IndexSearcher newSearcher(IndexReader r) throws IOException {
1182     return newSearcher(r, true);
1183   }
1184   
1185   /** create a new searcher over the reader.
1186    * This searcher might randomly use threads.
1187    * if <code>maybeWrap</code> is true, this searcher might wrap the reader
1188    * with one that returns null for getSequentialSubReaders.
1189    */
1190   public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) throws IOException {
1191     if (random.nextBoolean()) {
1192       if (maybeWrap && rarely()) {
1193         r = new SlowMultiReaderWrapper(r);
1194       }
1195       return new AssertingIndexSearcher(r);
1196     } else {
1197       int threads = 0;
1198       final ExecutorService ex = (random.nextBoolean()) ? null 
1199           : Executors.newFixedThreadPool(threads = _TestUtil.nextInt(random, 1, 8), 
1200                       new NamedThreadFactory("LuceneTestCase"));
1201       if (ex != null && VERBOSE) {
1202         System.out.println("NOTE: newSearcher using ExecutorService with " + threads + " threads");
1203       }
1204       return new AssertingIndexSearcher(r, ex) {
1205         @Override
1206         public void close() throws IOException {
1207           super.close();
1208           shutdownExecutorService(ex);
1209         }
1210       };
1211     }
1212   }
1213   
1214   static void shutdownExecutorService(ExecutorService ex) {
1215     if (ex != null) {
1216       ex.shutdown();
1217       try {
1218         ex.awaitTermination(1000, TimeUnit.MILLISECONDS);
1219       } catch (InterruptedException e) {
1220         e.printStackTrace();
1221       }
1222     }
1223   }
1224
1225   public String getName() {
1226     return this.name;
1227   }
1228   
1229   /** Gets a resource from the classpath as {@link File}. This method should only be used,
1230    * if a real file is needed. To get a stream, code should prefer
1231    * {@link Class#getResourceAsStream} using {@code this.getClass()}.
1232    */
1233   
1234   protected File getDataFile(String name) throws IOException {
1235     try {
1236       return new File(this.getClass().getResource(name).toURI());
1237     } catch (Exception e) {
1238       throw new IOException("Cannot find resource: " + name);
1239     }
1240   }
1241
1242   // We get here from InterceptTestCaseEvents on the 'failed' event....
1243   public static void reportPartialFailureInfo() {
1244     System.err.println("NOTE: reproduce with (hopefully): ant test -Dtestcase=" + testClassesRun.get(testClassesRun.size()-1)
1245         + " -Dtests.seed=" + new ThreeLongs(staticSeed, 0L, LuceneTestCaseRunner.runnerSeed)
1246         + reproduceWithExtraParams());
1247   }
1248   
1249   // We get here from InterceptTestCaseEvents on the 'failed' event....
1250   public void reportAdditionalFailureInfo() {
1251     System.err.println("NOTE: reproduce with: ant test -Dtestcase=" + getClass().getSimpleName() 
1252         + " -Dtestmethod=" + getName() + " -Dtests.seed=" + new ThreeLongs(staticSeed, seed, LuceneTestCaseRunner.runnerSeed)
1253         + reproduceWithExtraParams());
1254   }
1255   
1256   // extra params that were overridden needed to reproduce the command
1257   private static String reproduceWithExtraParams() {
1258     StringBuilder sb = new StringBuilder();
1259     if (!TEST_LOCALE.equals("random")) sb.append(" -Dtests.locale=").append(TEST_LOCALE);
1260     if (!TEST_TIMEZONE.equals("random")) sb.append(" -Dtests.timezone=").append(TEST_TIMEZONE);
1261     if (!TEST_DIRECTORY.equals("random")) sb.append(" -Dtests.directory=").append(TEST_DIRECTORY);
1262     if (RANDOM_MULTIPLIER > 1) sb.append(" -Dtests.multiplier=").append(RANDOM_MULTIPLIER);
1263     if (TEST_NIGHTLY) sb.append(" -Dtests.nightly=true");
1264     // TODO we can't randomize this yet (it drives ant crazy) but this makes tests reproduceable
1265     // in case machines have different default charsets...
1266     sb.append(" -Dargs=\"-Dfile.encoding=" + System.getProperty("file.encoding") + "\"");
1267     return sb.toString();
1268   }
1269
1270   // recorded seed: for beforeClass
1271   private static long staticSeed;
1272   // seed for individual test methods, changed in @before
1273   private long seed;
1274   
1275   static final Random seedRand = new Random();
1276   protected static final SmartRandom random = new SmartRandom(0);
1277
1278   private String name = "<unknown>";
1279   
1280   /**
1281    * Annotation for tests that should only be run during nightly builds.
1282    */
1283   @Documented
1284   @Inherited
1285   @Retention(RetentionPolicy.RUNTIME)
1286   public @interface Nightly {}
1287   
1288   @Ignore("just a hack")
1289   public final void alwaysIgnoredTestMethod() {}
1290   
1291   /** check if assertions are enabled */
1292   private static boolean assertionsEnabled() {
1293     try {
1294       assert Boolean.FALSE.booleanValue();
1295       return false; // should never get here
1296     } catch (AssertionError e) {
1297       return true;
1298     }
1299   }
1300   
1301 }