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