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