1 package org.apache.lucene.util;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
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;
29 import java.util.Map.Entry;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.TimeUnit;
34 import org.apache.lucene.analysis.Analyzer;
35 import org.apache.lucene.document.Field.Index;
36 import org.apache.lucene.document.Field.Store;
37 import org.apache.lucene.document.Field.TermVector;
38 import org.apache.lucene.document.Field;
39 import org.apache.lucene.index.*;
40 import org.apache.lucene.search.AssertingIndexSearcher;
41 import org.apache.lucene.search.BooleanQuery;
42 import org.apache.lucene.search.FieldCache.CacheEntry;
43 import org.apache.lucene.search.FieldCache;
44 import org.apache.lucene.search.IndexSearcher;
45 import org.apache.lucene.store.Directory;
46 import org.apache.lucene.store.FSDirectory;
47 import org.apache.lucene.store.LockFactory;
48 import org.apache.lucene.store.MockDirectoryWrapper;
49 import org.apache.lucene.util.FieldCacheSanityChecker.Insanity;
50 import org.junit.After;
51 import org.junit.AfterClass;
52 import org.junit.Assert;
53 import org.junit.Assume;
54 import org.junit.Before;
55 import org.junit.BeforeClass;
56 import org.junit.Ignore;
57 import org.junit.Rule;
58 import org.junit.rules.MethodRule;
59 import org.junit.rules.TestWatchman;
60 import org.junit.runner.RunWith;
61 import org.junit.runners.model.FrameworkMethod;
62 import org.junit.runners.model.Statement;
65 * Base class for all Lucene unit tests, Junit3 or Junit4 variant.
70 * override either <code>setUp()</code> or
71 * <code>tearDown()</code> in your unit test, make sure you
72 * call <code>super.setUp()</code> and
73 * <code>super.tearDown()</code>
76 * <code>@After</code> - replaces setup
77 * <code>@Before</code> - replaces teardown
78 * <code>@Test</code> - any public method with this annotation is a test case, regardless
82 * See Junit4 <a href="http://junit.org/junit/javadoc/4.7/">documentation</a> for a complete list of features.
84 * Import from org.junit rather than junit.framework.
86 * You should be able to use this class anywhere you used LuceneTestCase
87 * if you annotate your derived class correctly with the annotations above
88 * @see #assertSaneFieldCaches(String)
91 @RunWith(LuceneTestCaseRunner.class)
92 public abstract class LuceneTestCase extends Assert {
95 * true iff tests are run in verbose mode. Note: if it is false, tests are not
96 * expected to print any messages.
98 public static final boolean VERBOSE = Boolean.getBoolean("tests.verbose");
100 /** Use this constant when creating Analyzers and any other version-dependent stuff.
101 * <p><b>NOTE:</b> Change this when development starts for new Lucene version:
103 public static final Version TEST_VERSION_CURRENT = Version.LUCENE_35;
106 * If this is set, it is the only method that should run.
108 static final String TEST_METHOD;
110 /** Create indexes in this directory, optimally use a subdir, named after the test */
111 public static final File TEMP_DIR;
113 String method = System.getProperty("testmethod", "").trim();
114 TEST_METHOD = method.length() == 0 ? null : method;
115 String s = System.getProperty("tempDir", System.getProperty("java.io.tmpdir"));
117 throw new RuntimeException("To run tests, you need to define system property 'tempDir' or 'java.io.tmpdir'.");
118 TEMP_DIR = new File(s);
122 /** set of directories we created, in afterclass we try to clean these up */
123 private static final Map<File, StackTraceElement[]> tempDirs = Collections.synchronizedMap(new HashMap<File, StackTraceElement[]>());
125 // by default we randomly pick a different codec for
126 // each test case (non-J4 tests) and each test class (J4
128 /** Gets the locale to run tests with */
129 public static final String TEST_LOCALE = System.getProperty("tests.locale", "random");
130 /** Gets the timezone to run tests with */
131 public static final String TEST_TIMEZONE = System.getProperty("tests.timezone", "random");
132 /** Gets the directory to run tests with */
133 public static final String TEST_DIRECTORY = System.getProperty("tests.directory", "random");
134 /** Get the number of times to run tests */
135 public static final int TEST_ITER = Integer.parseInt(System.getProperty("tests.iter", "1"));
136 /** Get the minimum number of times to run tests until a failure happens */
137 public static final int TEST_ITER_MIN = Integer.parseInt(System.getProperty("tests.iter.min", Integer.toString(TEST_ITER)));
138 /** Get the random seed for tests */
139 public static final String TEST_SEED = System.getProperty("tests.seed", "random");
140 /** whether or not nightly tests should run */
141 public static final boolean TEST_NIGHTLY = Boolean.parseBoolean(System.getProperty("tests.nightly", "false"));
142 /** the line file used by LineFileDocs */
143 public static final String TEST_LINE_DOCS_FILE = System.getProperty("tests.linedocsfile", "europarl.lines.txt.gz");
144 /** whether or not to clean threads between test invocations: "false", "perMethod", "perClass" */
145 public static final String TEST_CLEAN_THREADS = System.getProperty("tests.cleanthreads", "perClass");
148 * A random multiplier which you should use when writing random tests:
149 * multiply it by the number of iterations
151 public static final int RANDOM_MULTIPLIER = Integer.parseInt(System.getProperty("tests.multiplier", "1"));
153 private int savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
155 private volatile Thread.UncaughtExceptionHandler savedUncaughtExceptionHandler = null;
157 /** Used to track if setUp and tearDown are called correctly from subclasses */
158 private static State state = State.INITIAL;
160 private static enum State {
161 INITIAL, // no tests ran yet
162 SETUP, // test has called setUp()
163 RANTEST, // test is running
164 TEARDOWN // test has called tearDown()
167 private static class UncaughtExceptionEntry {
168 public final Thread thread;
169 public final Throwable exception;
171 public UncaughtExceptionEntry(Thread thread, Throwable exception) {
172 this.thread = thread;
173 this.exception = exception;
176 private List<UncaughtExceptionEntry> uncaughtExceptions = Collections.synchronizedList(new ArrayList<UncaughtExceptionEntry>());
178 private static Locale locale;
179 private static Locale savedLocale;
180 private static TimeZone timeZone;
181 private static TimeZone savedTimeZone;
183 protected static Map<MockDirectoryWrapper,StackTraceElement[]> stores;
185 /** @deprecated: until we fix no-fork problems in solr tests */
187 static List<String> testClassesRun = new ArrayList<String>();
189 private static void initRandom() {
190 assert !random.initialized;
191 staticSeed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l1;
192 random.setSeed(staticSeed);
193 random.initialized = true;
197 private static boolean icuTested = false;
200 public static void beforeClassLuceneTestCaseJ4() {
202 state = State.INITIAL;
204 stores = Collections.synchronizedMap(new IdentityHashMap<MockDirectoryWrapper,StackTraceElement[]>());
205 // enable this by default, for IDE consistency with ant tests (as its the default from ant)
206 // TODO: really should be in solr base classes, but some extend LTC directly.
207 // we do this in beforeClass, because some tests currently disable it
208 if (System.getProperty("solr.directoryFactory") == null) {
209 System.setProperty("solr.directoryFactory", "org.apache.solr.core.MockDirectoryFactory");
211 // this code consumes randoms where 4.0's lucenetestcase would: to make seeds work across both branches.
212 // TODO: doesn't completely work, because what if we get mockrandom codec?!
213 if (random.nextInt(4) != 0) {
214 random.nextInt(); // consume RandomCodecProvider's seed.
216 // end compatibility random-consumption
218 savedLocale = Locale.getDefault();
220 // START hack to init ICU safely before we randomize locales.
221 // ICU fails during classloading when a special Java7-only locale is the default
222 // see: http://bugs.icu-project.org/trac/ticket/8734
226 Locale.setDefault(Locale.US);
227 Class.forName("com.ibm.icu.util.ULocale");
228 } catch (ClassNotFoundException cnfe) {
229 // ignore if no ICU is in classpath
234 locale = TEST_LOCALE.equals("random") ? randomLocale(random) : localeForName(TEST_LOCALE);
235 Locale.setDefault(locale);
236 savedTimeZone = TimeZone.getDefault();
237 timeZone = TEST_TIMEZONE.equals("random") ? randomTimeZone(random) : TimeZone.getTimeZone(TEST_TIMEZONE);
238 TimeZone.setDefault(timeZone);
241 // verify assertions are enabled (do last, for smooth cleanup)
242 if (!Boolean.parseBoolean(System.getProperty("tests.asserts.gracious", "false"))) {
243 assertTrue("assertions are not enabled!", assertionsEnabled());
248 public static void afterClassLuceneTestCaseJ4() {
249 State oldState = state; // capture test execution state
250 state = State.INITIAL; // set the state for subsequent tests
252 Throwable problem = null;
255 assertTrue("ensure your setUp() calls super.setUp() and your tearDown() calls super.tearDown()!!!",
256 oldState == State.INITIAL || oldState == State.TEARDOWN);
258 } catch (Throwable t) {
259 if (problem == null) problem = t;
262 if (! "false".equals(TEST_CLEAN_THREADS)) {
263 int rogueThreads = threadCleanup("test class");
264 if (rogueThreads > 0) {
265 // TODO: fail here once the leaks are fixed.
266 System.err.println("RESOURCE LEAK: test class left " + rogueThreads + " thread(s) running");
270 Locale.setDefault(savedLocale);
271 TimeZone.setDefault(savedTimeZone);
272 System.clearProperty("solr.solr.home");
273 System.clearProperty("solr.data.dir");
276 // now look for unclosed resources
278 checkResourcesAfterClass();
280 } catch (Throwable t) {
281 if (problem == null) problem = t;
287 // clear out any temp directories if we can
289 clearTempDirectoriesAfterClass();
291 } catch (Throwable t) {
292 if (problem == null) problem = t;
295 // if we had afterClass failures, get some debugging information
296 if (problem != null) {
297 reportPartialFailureInfo();
300 // if verbose or tests failed, report some information back
301 if (VERBOSE || testsFailed || problem != null) {
302 printDebuggingInformation();
307 random.initialized = false;
309 if (problem != null) {
310 throw new RuntimeException(problem);
314 /** print some useful debugging information about the environment */
315 private static void printDebuggingInformation() {
316 System.err.println("NOTE: test params are: " +
318 ", timezone=" + (timeZone == null ? "(null)" : timeZone.getID()));
319 System.err.println("NOTE: all tests run in this JVM:");
320 System.err.println(Arrays.toString(testClassesRun.toArray()));
321 System.err.println("NOTE: " + System.getProperty("os.name") + " "
322 + System.getProperty("os.version") + " "
323 + System.getProperty("os.arch") + "/"
324 + System.getProperty("java.vendor") + " "
325 + System.getProperty("java.version") + " "
326 + (Constants.JRE_IS_64BIT ? "(64-bit)" : "(32-bit)") + "/"
327 + "cpus=" + Runtime.getRuntime().availableProcessors() + ","
328 + "threads=" + Thread.activeCount() + ","
329 + "free=" + Runtime.getRuntime().freeMemory() + ","
330 + "total=" + Runtime.getRuntime().totalMemory());
333 /** check that directories and their resources were closed */
334 private static void checkResourcesAfterClass() {
335 for (MockDirectoryWrapper d : stores.keySet()) {
337 StackTraceElement elements[] = stores.get(d);
338 // Look for the first class that is not LuceneTestCase that requested
339 // a Directory. The first two items are of Thread's, so skipping over
341 StackTraceElement element = null;
342 for (int i = 2; i < elements.length; i++) {
343 StackTraceElement ste = elements[i];
344 if (ste.getClassName().indexOf("LuceneTestCase") == -1) {
349 fail("directory of test was not closed, opened from: " + element);
354 /** clear temp directories: this will fail if its not successful */
355 private static void clearTempDirectoriesAfterClass() {
356 for (Entry<File, StackTraceElement[]> entry : tempDirs.entrySet()) {
358 _TestUtil.rmDir(entry.getKey());
359 } catch (IOException e) {
361 System.err.println("path " + entry.getKey() + " allocated from");
362 // first two STE's are Java's
363 StackTraceElement[] elements = entry.getValue();
364 for (int i = 2; i < elements.length; i++) {
365 StackTraceElement ste = elements[i];
366 // print only our code's stack information
367 if (ste.getClassName().indexOf("org.apache.lucene") == -1) break;
368 System.err.println("\t" + ste);
370 fail("could not remove temp dir: " + entry.getKey());
375 protected static boolean testsFailed; /* true if any tests failed */
377 // This is how we get control when errors occur.
378 // Think of this as start/end/success/failed
381 public final TestWatchman intercept = new TestWatchman() {
384 public void failed(Throwable e, FrameworkMethod method) {
385 // org.junit.internal.AssumptionViolatedException in older releases
386 // org.junit.Assume.AssumptionViolatedException in recent ones
387 if (e.getClass().getName().endsWith("AssumptionViolatedException")) {
388 if (e.getCause() instanceof _TestIgnoredException)
390 System.err.print("NOTE: Assume failed in '" + method.getName() + "' (ignored):");
392 System.err.println();
393 e.printStackTrace(System.err);
395 System.err.print(" ");
396 System.err.println(e.getMessage());
400 reportAdditionalFailureInfo();
402 super.failed(e, method);
406 public void starting(FrameworkMethod method) {
407 // set current method name for logging
408 LuceneTestCase.this.name = method.getName();
409 State s = state; // capture test execution state
410 state = State.RANTEST; // set the state for subsequent tests
412 assertTrue("ensure your setUp() calls super.setUp()!!!", s == State.SETUP);
414 super.starting(method);
419 * The thread executing the current test case.
420 * @see #isTestThread()
422 volatile Thread testCaseThread;
424 /** @see #testCaseThread */
426 public final MethodRule setTestThread = new MethodRule() {
427 public Statement apply(final Statement s, FrameworkMethod fm, Object target) {
428 return new Statement() {
429 public void evaluate() throws Throwable {
431 LuceneTestCase.this.testCaseThread = Thread.currentThread();
434 LuceneTestCase.this.testCaseThread = null;
442 public void setUp() throws Exception {
443 seed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l2;
444 random.setSeed(seed);
445 State s = state; // capture test execution state
446 state = State.SETUP; // set the state for subsequent tests
448 savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
449 Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
450 public void uncaughtException(Thread t, Throwable e) {
452 uncaughtExceptions.add(new UncaughtExceptionEntry(t, e));
453 if (savedUncaughtExceptionHandler != null)
454 savedUncaughtExceptionHandler.uncaughtException(t, e);
458 savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
461 assertTrue("ensure your tearDown() calls super.tearDown()!!!", (s == State.INITIAL || s == State.TEARDOWN));
466 * Forcible purges all cache entries from the FieldCache.
468 * This method will be called by tearDown to clean up FieldCache.DEFAULT.
469 * If a (poorly written) test has some expectation that the FieldCache
470 * will persist across test methods (ie: a static IndexReader) this
471 * method can be overridden to do nothing.
474 * @see FieldCache#purgeAllCaches()
476 protected void purgeFieldCache(final FieldCache fc) {
480 protected String getTestLabel() {
481 return getClass().getName() + "." + getName();
484 public static void setUseCompoundFile(MergePolicy mp, boolean useCompound) {
485 if (mp instanceof LogMergePolicy) {
486 ((LogMergePolicy) mp).setUseCompoundFile(useCompound);
487 } else if (mp instanceof TieredMergePolicy) {
488 ((TieredMergePolicy) mp).setUseCompoundFile(useCompound);
490 fail("MergePolicy (compound-file) not supported " + mp);
494 public static void setMergeFactor(MergePolicy mp, int mergeFactor) {
495 if (mp instanceof LogMergePolicy) {
496 ((LogMergePolicy) mp).setMergeFactor(mergeFactor);
497 } else if (mp instanceof TieredMergePolicy) {
498 ((TieredMergePolicy) mp).setMaxMergeAtOnce(mergeFactor);
499 ((TieredMergePolicy) mp).setMaxMergeAtOnceExplicit(mergeFactor);
501 fail("MergePolicy not supported " + mp);
506 * Returns true if and only if the calling thread is the primary thread
507 * executing the test case.
509 protected boolean isTestThread() {
510 assertNotNull("Test case thread not set?", testCaseThread);
511 return Thread.currentThread() == testCaseThread;
515 public void tearDown() throws Exception {
516 State oldState = state; // capture test execution state
517 state = State.TEARDOWN; // set the state for subsequent tests
519 // NOTE: with junit 4.7, we don't get a reproduceWith because our Watchman
520 // does not know if something fails in tearDown. so we ensure this happens ourselves for now.
521 // we can remove this if we upgrade to 4.8
522 Throwable problem = null;
526 // Note: we allow a test to go straight from SETUP -> TEARDOWN (without ever entering the RANTEST state)
527 // because if you assume() inside setUp(), it skips the test and the TestWatchman has no way to know...
528 assertTrue("ensure your setUp() calls super.setUp()!!!", oldState == State.RANTEST || oldState == State.SETUP);
530 } catch (Throwable t) {
531 if (problem == null) problem = t;
534 BooleanQuery.setMaxClauseCount(savedBoolMaxClauseCount);
536 // this won't throw any exceptions or fail the test
537 // if we change this, then change this logic
538 checkRogueThreadsAfter();
539 // restore the default uncaught exception handler
540 Thread.setDefaultUncaughtExceptionHandler(savedUncaughtExceptionHandler);
543 checkUncaughtExceptionsAfter();
544 } catch (Throwable t) {
545 if (problem == null) problem = t;
549 // calling assertSaneFieldCaches here isn't as useful as having test
550 // classes call it directly from the scope where the index readers
551 // are used, because they could be gc'ed just before this tearDown
554 // But it's better then nothing.
556 // If you are testing functionality that you know for a fact
557 // "violates" FieldCache sanity, then you should either explicitly
558 // call purgeFieldCache at the end of your test method, or refactor
559 // your Test class so that the inconsistant FieldCache usages are
560 // isolated in distinct test methods
561 assertSaneFieldCaches(getTestLabel());
562 } catch (Throwable t) {
563 if (problem == null) problem = t;
566 purgeFieldCache(FieldCache.DEFAULT);
568 if (problem != null) {
570 reportAdditionalFailureInfo();
571 throw new RuntimeException(problem);
575 /** check if the test still has threads running, we don't want them to
576 * fail in a subsequent test and pass the blame to the wrong test */
577 private void checkRogueThreadsAfter() {
578 if ("perMethod".equals(TEST_CLEAN_THREADS)) {
579 int rogueThreads = threadCleanup("test method: '" + getName() + "'");
580 if (!testsFailed && rogueThreads > 0) {
581 System.err.println("RESOURCE LEAK: test method: '" + getName()
582 + "' left " + rogueThreads + " thread(s) running");
583 // TODO: fail, but print seed for now
584 if (uncaughtExceptions.isEmpty()) {
585 reportAdditionalFailureInfo();
591 /** see if any other threads threw uncaught exceptions, and fail the test if so */
592 private void checkUncaughtExceptionsAfter() {
593 if (!uncaughtExceptions.isEmpty()) {
594 System.err.println("The following exceptions were thrown by threads:");
595 for (UncaughtExceptionEntry entry : uncaughtExceptions) {
596 System.err.println("*** Thread: " + entry.thread.getName() + " ***");
597 entry.exception.printStackTrace(System.err);
599 fail("Some threads threw uncaught exceptions!");
603 private final static int THREAD_STOP_GRACE_MSEC = 50;
604 // jvm-wide list of 'rogue threads' we found, so they only get reported once.
605 private final static IdentityHashMap<Thread,Boolean> rogueThreads = new IdentityHashMap<Thread,Boolean>();
608 // just a hack for things like eclipse test-runner threads
609 for (Thread t : Thread.getAllStackTraces().keySet()) {
610 rogueThreads.put(t, true);
614 System.out.println("WARNING: you are using -Dtests.iter=n where n > 1, not all tests support this option.");
615 System.out.println("Some may crash or fail: this is not a bug.");
620 * Looks for leftover running threads, trying to kill them off,
621 * so they don't fail future tests.
622 * returns the number of rogue threads that it found.
624 private static int threadCleanup(String context) {
626 Thread[] stillRunning = new Thread[Thread.activeCount()+1];
630 if ((threadCount = Thread.enumerate(stillRunning)) > 1) {
631 while (threadCount == stillRunning.length) {
632 // truncated response
633 stillRunning = new Thread[stillRunning.length*2];
634 threadCount = Thread.enumerate(stillRunning);
637 for (int i = 0; i < threadCount; i++) {
638 Thread t = stillRunning[i];
641 !rogueThreads.containsKey(t) &&
642 t != Thread.currentThread() &&
643 // TODO: TimeLimitingCollector starts a thread statically.... WTF?!
644 !t.getName().equals("TimeLimitedCollector timer thread") &&
645 /* its ok to keep your searcher across test cases */
646 (t.getName().startsWith("LuceneTestCase") && context.startsWith("test method")) == false) {
647 System.err.println("WARNING: " + context + " left thread running: " + t);
648 rogueThreads.put(t, true);
650 if (t.getName().startsWith("LuceneTestCase")) {
651 System.err.println("PLEASE CLOSE YOUR INDEXSEARCHERS IN YOUR TEST!!!!");
654 // wait on the thread to die of natural causes
656 t.join(THREAD_STOP_GRACE_MSEC);
657 } catch (InterruptedException e) { e.printStackTrace(); }
659 // try to stop the thread:
660 t.setUncaughtExceptionHandler(null);
661 Thread.setDefaultUncaughtExceptionHandler(null);
670 * Asserts that FieldCacheSanityChecker does not detect any
671 * problems with FieldCache.DEFAULT.
673 * If any problems are found, they are logged to System.err
674 * (allong with the msg) when the Assertion is thrown.
677 * This method is called by tearDown after every test method,
678 * however IndexReaders scoped inside test methods may be garbage
679 * collected prior to this method being called, causing errors to
680 * be overlooked. Tests are encouraged to keep their IndexReaders
681 * scoped at the class level, or to explicitly call this method
682 * directly in the same scope as the IndexReader.
685 * @see org.apache.lucene.util.FieldCacheSanityChecker
687 protected void assertSaneFieldCaches(final String msg) {
688 final CacheEntry[] entries = FieldCache.DEFAULT.getCacheEntries();
689 Insanity[] insanity = null;
692 insanity = FieldCacheSanityChecker.checkSanity(entries);
693 } catch (RuntimeException e) {
694 dumpArray(msg + ": FieldCache", entries, System.err);
698 assertEquals(msg + ": Insane FieldCache usage(s) found",
703 // report this in the event of any exception/failure
704 // if no failure, then insanity will be null anyway
705 if (null != insanity) {
706 dumpArray(msg + ": Insane FieldCache usage(s)", insanity, System.err);
713 * Returns a number of at least <code>i</code>
715 * The actual number returned will be influenced by whether {@link #TEST_NIGHTLY}
716 * is active and {@link #RANDOM_MULTIPLIER}, but also with some random fudge.
718 public static int atLeast(Random random, int i) {
719 int min = (TEST_NIGHTLY ? 3*i : i) * RANDOM_MULTIPLIER;
720 int max = min+(min/2);
721 return _TestUtil.nextInt(random, min, max);
724 public static int atLeast(int i) {
725 return atLeast(random, i);
729 * Returns true if something should happen rarely,
731 * The actual number returned will be influenced by whether {@link #TEST_NIGHTLY}
732 * is active and {@link #RANDOM_MULTIPLIER}.
734 public static boolean rarely(Random random) {
735 int p = TEST_NIGHTLY ? 10 : 5;
736 p += (p * Math.log(RANDOM_MULTIPLIER));
737 int min = 100 - Math.min(p, 50); // never more than 50
738 return random.nextInt(100) >= min;
741 public static boolean rarely() {
742 return rarely(random);
745 public static boolean usually(Random random) {
746 return !rarely(random);
749 public static boolean usually() {
750 return usually(random);
753 // These deprecated methods should be removed soon, when all tests using no Epsilon are fixed:
756 static public void assertEquals(double expected, double actual) {
757 assertEquals(null, expected, actual);
761 static public void assertEquals(String message, double expected, double actual) {
762 assertEquals(message, Double.valueOf(expected), Double.valueOf(actual));
766 static public void assertEquals(float expected, float actual) {
767 assertEquals(null, expected, actual);
771 static public void assertEquals(String message, float expected, float actual) {
772 assertEquals(message, Float.valueOf(expected), Float.valueOf(actual));
775 public static void assumeTrue(String msg, boolean b) {
776 Assume.assumeNoException(b ? null : new _TestIgnoredException(msg));
779 public static void assumeFalse(String msg, boolean b) {
783 public static void assumeNoException(String msg, Exception e) {
784 Assume.assumeNoException(e == null ? null : new _TestIgnoredException(msg, e));
788 * Convenience method for logging an iterator.
790 * @param label String logged before/after the items in the iterator
791 * @param iter Each next() is toString()ed and logged on it's own line. If iter is null this is logged differnetly then an empty iterator.
792 * @param stream Stream to log messages to.
794 public static void dumpIterator(String label, Iterator<?> iter,
795 PrintStream stream) {
796 stream.println("*** BEGIN " + label + " ***");
798 stream.println(" ... NULL ...");
800 while (iter.hasNext()) {
801 stream.println(iter.next().toString());
804 stream.println("*** END " + label + " ***");
808 * Convenience method for logging an array. Wraps the array in an iterator and delegates
810 * @see #dumpIterator(String,Iterator,PrintStream)
812 public static void dumpArray(String label, Object[] objs,
813 PrintStream stream) {
814 Iterator<?> iter = (null == objs) ? null : Arrays.asList(objs).iterator();
815 dumpIterator(label, iter, stream);
818 /** create a new index writer config with random defaults */
819 public static IndexWriterConfig newIndexWriterConfig(Version v, Analyzer a) {
820 return newIndexWriterConfig(random, v, a);
823 /** create a new index writer config with random defaults using the specified random */
824 public static IndexWriterConfig newIndexWriterConfig(Random r, Version v, Analyzer a) {
825 IndexWriterConfig c = new IndexWriterConfig(v, a);
826 if (r.nextBoolean()) {
827 c.setMergePolicy(newTieredMergePolicy());
828 } else if (r.nextBoolean()) {
829 c.setMergePolicy(newLogMergePolicy());
831 c.setMergePolicy(new MockRandomMergePolicy(r));
834 if (r.nextBoolean()) {
835 c.setMergeScheduler(new SerialMergeScheduler());
837 if (r.nextBoolean()) {
840 c.setMaxBufferedDocs(_TestUtil.nextInt(r, 2, 7));
843 c.setMaxBufferedDocs(_TestUtil.nextInt(r, 8, 1000));
846 if (r.nextBoolean()) {
849 c.setTermIndexInterval(r.nextBoolean() ? _TestUtil.nextInt(r, 1, 31) : _TestUtil.nextInt(r, 129, 1000));
852 c.setTermIndexInterval(_TestUtil.nextInt(r, 32, 128));
855 if (r.nextBoolean()) {
856 c.setMaxThreadStates(_TestUtil.nextInt(r, 1, 20));
859 if (r.nextBoolean()) {
860 c.setMergePolicy(new MockRandomMergePolicy(r));
862 c.setMergePolicy(newLogMergePolicy());
865 c.setReaderPooling(r.nextBoolean());
866 c.setReaderTermsIndexDivisor(_TestUtil.nextInt(r, 1, 4));
870 public static LogMergePolicy newLogMergePolicy() {
871 return newLogMergePolicy(random);
874 public static TieredMergePolicy newTieredMergePolicy() {
875 return newTieredMergePolicy(random);
878 public static LogMergePolicy newLogMergePolicy(Random r) {
879 LogMergePolicy logmp = r.nextBoolean() ? new LogDocMergePolicy() : new LogByteSizeMergePolicy();
880 logmp.setUseCompoundFile(r.nextBoolean());
881 logmp.setCalibrateSizeByDeletes(r.nextBoolean());
883 logmp.setMergeFactor(_TestUtil.nextInt(r, 2, 4));
885 logmp.setMergeFactor(_TestUtil.nextInt(r, 5, 50));
890 public static TieredMergePolicy newTieredMergePolicy(Random r) {
891 TieredMergePolicy tmp = new TieredMergePolicy();
893 tmp.setMaxMergeAtOnce(_TestUtil.nextInt(r, 2, 4));
894 tmp.setMaxMergeAtOnceExplicit(_TestUtil.nextInt(r, 2, 4));
896 tmp.setMaxMergeAtOnce(_TestUtil.nextInt(r, 5, 50));
897 tmp.setMaxMergeAtOnceExplicit(_TestUtil.nextInt(r, 5, 50));
899 tmp.setMaxMergedSegmentMB(0.2 + r.nextDouble() * 2.0);
900 tmp.setFloorSegmentMB(0.2 + r.nextDouble() * 2.0);
901 tmp.setForceMergeDeletesPctAllowed(0.0 + r.nextDouble() * 30.0);
902 tmp.setSegmentsPerTier(_TestUtil.nextInt(r, 2, 20));
903 tmp.setUseCompoundFile(r.nextBoolean());
904 tmp.setNoCFSRatio(0.1 + r.nextDouble()*0.8);
905 tmp.setReclaimDeletesWeight(r.nextDouble()*4);
909 public static LogMergePolicy newLogMergePolicy(boolean useCFS) {
910 LogMergePolicy logmp = newLogMergePolicy();
911 logmp.setUseCompoundFile(useCFS);
915 public static LogMergePolicy newLogMergePolicy(boolean useCFS, int mergeFactor) {
916 LogMergePolicy logmp = newLogMergePolicy();
917 logmp.setUseCompoundFile(useCFS);
918 logmp.setMergeFactor(mergeFactor);
922 public static LogMergePolicy newLogMergePolicy(int mergeFactor) {
923 LogMergePolicy logmp = newLogMergePolicy();
924 logmp.setMergeFactor(mergeFactor);
929 * Returns a new Directory instance. Use this when the test does not
930 * care about the specific Directory implementation (most tests).
932 * The Directory is wrapped with {@link MockDirectoryWrapper}.
933 * By default this means it will be picky, such as ensuring that you
934 * properly close it and all open files in your test. It will emulate
935 * some features of Windows, such as not allowing open files to be
938 public static MockDirectoryWrapper newDirectory() throws IOException {
939 return newDirectory(random);
943 * Returns a new Directory instance, using the specified random.
944 * See {@link #newDirectory()} for more information.
946 public static MockDirectoryWrapper newDirectory(Random r) throws IOException {
947 Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
948 MockDirectoryWrapper dir = new MockDirectoryWrapper(r, impl);
949 stores.put(dir, Thread.currentThread().getStackTrace());
954 * Returns a new Directory instance, with contents copied from the
955 * provided directory. See {@link #newDirectory()} for more
958 public static MockDirectoryWrapper newDirectory(Directory d) throws IOException {
959 return newDirectory(random, d);
962 /** Returns a new FSDirectory instance over the given file, which must be a folder. */
963 public static MockDirectoryWrapper newFSDirectory(File f) throws IOException {
964 return newFSDirectory(f, null);
967 /** Returns a new FSDirectory instance over the given file, which must be a folder. */
968 public static MockDirectoryWrapper newFSDirectory(File f, LockFactory lf) throws IOException {
969 String fsdirClass = TEST_DIRECTORY;
970 if (fsdirClass.equals("random")) {
971 fsdirClass = FS_DIRECTORIES[random.nextInt(FS_DIRECTORIES.length)];
974 if (fsdirClass.indexOf(".") == -1) {// if not fully qualified, assume .store
975 fsdirClass = "org.apache.lucene.store." + fsdirClass;
978 Class<? extends FSDirectory> clazz;
981 clazz = Class.forName(fsdirClass).asSubclass(FSDirectory.class);
982 } catch (ClassCastException e) {
983 // TEST_DIRECTORY is not a sub-class of FSDirectory, so draw one at random
984 fsdirClass = FS_DIRECTORIES[random.nextInt(FS_DIRECTORIES.length)];
986 if (fsdirClass.indexOf(".") == -1) {// if not fully qualified, assume .store
987 fsdirClass = "org.apache.lucene.store." + fsdirClass;
990 clazz = Class.forName(fsdirClass).asSubclass(FSDirectory.class);
992 MockDirectoryWrapper dir = new MockDirectoryWrapper(random, newFSDirectoryImpl(clazz, f));
994 dir.setLockFactory(lf);
996 stores.put(dir, Thread.currentThread().getStackTrace());
998 } catch (Exception e) {
999 throw new RuntimeException(e);
1004 * Returns a new Directory instance, using the specified random
1005 * with contents copied from the provided directory. See
1006 * {@link #newDirectory()} for more information.
1008 public static MockDirectoryWrapper newDirectory(Random r, Directory d) throws IOException {
1009 Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
1010 for (String file : d.listAll()) {
1011 d.copy(impl, file, file);
1013 MockDirectoryWrapper dir = new MockDirectoryWrapper(r, impl);
1014 stores.put(dir, Thread.currentThread().getStackTrace());
1018 /** Returns a new field instance.
1019 * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1020 public static Field newField(String name, String value, Index index) {
1021 return newField(random, name, value, index);
1024 /** Returns a new field instance.
1025 * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1026 public static Field newField(String name, String value, Store store, Index index) {
1027 return newField(random, name, value, store, index);
1031 * Returns a new Field instance. Use this when the test does not
1032 * care about some specific field settings (most tests)
1034 * <li>If the store value is set to Store.NO, sometimes the field will be randomly stored.
1035 * <li>More term vector data than you ask for might be indexed, for example if you choose YES
1036 * it might index term vectors with offsets too.
1039 public static Field newField(String name, String value, Store store, Index index, TermVector tv) {
1040 return newField(random, name, value, store, index, tv);
1043 /** Returns a new field instance, using the specified random.
1044 * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1045 public static Field newField(Random random, String name, String value, Index index) {
1046 return newField(random, name, value, Store.NO, index);
1049 /** Returns a new field instance, using the specified random.
1050 * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1051 public static Field newField(Random random, String name, String value, Store store, Index index) {
1052 return newField(random, name, value, store, index, TermVector.NO);
1055 /** Returns a new field instance, using the specified random.
1056 * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
1057 public static Field newField(Random random, String name, String value, Store store, Index index, TermVector tv) {
1058 if (usually(random)) {
1059 // most of the time, don't modify the params
1060 return new Field(name, value, store, index, tv);
1063 if (!index.isIndexed())
1064 return new Field(name, value, store, index, tv);
1066 if (!store.isStored() && random.nextBoolean())
1067 store = Store.YES; // randomly store it
1069 tv = randomTVSetting(random, tv);
1071 return new Field(name, value, store, index, tv);
1074 static final TermVector tvSettings[] = {
1075 TermVector.NO, TermVector.YES, TermVector.WITH_OFFSETS,
1076 TermVector.WITH_POSITIONS, TermVector.WITH_POSITIONS_OFFSETS
1079 private static TermVector randomTVSetting(Random random, TermVector minimum) {
1081 case NO: return tvSettings[_TestUtil.nextInt(random, 0, tvSettings.length-1)];
1082 case YES: return tvSettings[_TestUtil.nextInt(random, 1, tvSettings.length-1)];
1083 case WITH_OFFSETS: return random.nextBoolean() ? TermVector.WITH_OFFSETS
1084 : TermVector.WITH_POSITIONS_OFFSETS;
1085 case WITH_POSITIONS: return random.nextBoolean() ? TermVector.WITH_POSITIONS
1086 : TermVector.WITH_POSITIONS_OFFSETS;
1087 default: return TermVector.WITH_POSITIONS_OFFSETS;
1091 /** return a random Locale from the available locales on the system */
1092 public static Locale randomLocale(Random random) {
1093 Locale locales[] = Locale.getAvailableLocales();
1094 return locales[random.nextInt(locales.length)];
1097 /** return a random TimeZone from the available timezones on the system */
1098 public static TimeZone randomTimeZone(Random random) {
1099 String tzIds[] = TimeZone.getAvailableIDs();
1100 return TimeZone.getTimeZone(tzIds[random.nextInt(tzIds.length)]);
1103 /** return a Locale object equivalent to its programmatic name */
1104 public static Locale localeForName(String localeName) {
1105 String elements[] = localeName.split("\\_");
1106 switch(elements.length) {
1107 case 3: return new Locale(elements[0], elements[1], elements[2]);
1108 case 2: return new Locale(elements[0], elements[1]);
1109 case 1: return new Locale(elements[0]);
1110 default: throw new IllegalArgumentException("Invalid Locale: " + localeName);
1114 private static final String FS_DIRECTORIES[] = {
1115 "SimpleFSDirectory",
1120 private static final String CORE_DIRECTORIES[] = {
1122 FS_DIRECTORIES[0], FS_DIRECTORIES[1], FS_DIRECTORIES[2]
1125 public static String randomDirectory(Random random) {
1126 if (rarely(random)) {
1127 return CORE_DIRECTORIES[random.nextInt(CORE_DIRECTORIES.length)];
1129 return "RAMDirectory";
1133 private static Directory newFSDirectoryImpl(
1134 Class<? extends FSDirectory> clazz, File file)
1135 throws IOException {
1136 FSDirectory d = null;
1138 // Assuming every FSDirectory has a ctor(File), but not all may take a
1139 // LockFactory too, so setting it afterwards.
1140 Constructor<? extends FSDirectory> ctor = clazz.getConstructor(File.class);
1141 d = ctor.newInstance(file);
1142 } catch (Exception e) {
1143 d = FSDirectory.open(file);
1149 * Registers a temp directory that will be deleted when tests are done. This
1150 * is used by {@link _TestUtil#getTempDir(String)} and
1151 * {@link _TestUtil#unzip(File, File)}, so you should call these methods when
1154 static void registerTempDir(File tmpFile) {
1155 tempDirs.put(tmpFile.getAbsoluteFile(), Thread.currentThread().getStackTrace());
1158 static Directory newDirectoryImpl(Random random, String clazzName) {
1159 if (clazzName.equals("random"))
1160 clazzName = randomDirectory(random);
1161 if (clazzName.indexOf(".") == -1) // if not fully qualified, assume .store
1162 clazzName = "org.apache.lucene.store." + clazzName;
1164 final Class<? extends Directory> clazz = Class.forName(clazzName).asSubclass(Directory.class);
1165 // If it is a FSDirectory type, try its ctor(File)
1166 if (FSDirectory.class.isAssignableFrom(clazz)) {
1167 final File dir = _TestUtil.getTempDir("index");
1168 dir.mkdirs(); // ensure it's created so we 'have' it.
1169 return newFSDirectoryImpl(clazz.asSubclass(FSDirectory.class), dir);
1173 return clazz.newInstance();
1174 } catch (Exception e) {
1175 throw new RuntimeException(e);
1179 /** create a new searcher over the reader.
1180 * This searcher might randomly use threads. */
1181 public static IndexSearcher newSearcher(IndexReader r) throws IOException {
1182 return newSearcher(r, true);
1185 /** create a new searcher over the reader.
1186 * This searcher might randomly use threads.
1187 * if <code>maybeWrap</code> is true, this searcher might wrap the reader
1188 * with one that returns null for getSequentialSubReaders.
1190 public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) throws IOException {
1191 if (random.nextBoolean()) {
1192 if (maybeWrap && rarely()) {
1193 r = new SlowMultiReaderWrapper(r);
1195 return new AssertingIndexSearcher(r);
1198 final ExecutorService ex = (random.nextBoolean()) ? null
1199 : Executors.newFixedThreadPool(threads = _TestUtil.nextInt(random, 1, 8),
1200 new NamedThreadFactory("LuceneTestCase"));
1201 if (ex != null && VERBOSE) {
1202 System.out.println("NOTE: newSearcher using ExecutorService with " + threads + " threads");
1204 return new AssertingIndexSearcher(r, ex) {
1206 public void close() throws IOException {
1208 shutdownExecutorService(ex);
1214 static void shutdownExecutorService(ExecutorService ex) {
1218 ex.awaitTermination(1000, TimeUnit.MILLISECONDS);
1219 } catch (InterruptedException e) {
1220 e.printStackTrace();
1225 public String getName() {
1229 /** Gets a resource from the classpath as {@link File}. This method should only be used,
1230 * if a real file is needed. To get a stream, code should prefer
1231 * {@link Class#getResourceAsStream} using {@code this.getClass()}.
1234 protected File getDataFile(String name) throws IOException {
1236 return new File(this.getClass().getResource(name).toURI());
1237 } catch (Exception e) {
1238 throw new IOException("Cannot find resource: " + name);
1242 // We get here from InterceptTestCaseEvents on the 'failed' event....
1243 public static void reportPartialFailureInfo() {
1244 System.err.println("NOTE: reproduce with (hopefully): ant test -Dtestcase=" + testClassesRun.get(testClassesRun.size()-1)
1245 + " -Dtests.seed=" + new ThreeLongs(staticSeed, 0L, LuceneTestCaseRunner.runnerSeed)
1246 + reproduceWithExtraParams());
1249 // We get here from InterceptTestCaseEvents on the 'failed' event....
1250 public void reportAdditionalFailureInfo() {
1251 System.err.println("NOTE: reproduce with: ant test -Dtestcase=" + getClass().getSimpleName()
1252 + " -Dtestmethod=" + getName() + " -Dtests.seed=" + new ThreeLongs(staticSeed, seed, LuceneTestCaseRunner.runnerSeed)
1253 + reproduceWithExtraParams());
1256 // extra params that were overridden needed to reproduce the command
1257 private static String reproduceWithExtraParams() {
1258 StringBuilder sb = new StringBuilder();
1259 if (!TEST_LOCALE.equals("random")) sb.append(" -Dtests.locale=").append(TEST_LOCALE);
1260 if (!TEST_TIMEZONE.equals("random")) sb.append(" -Dtests.timezone=").append(TEST_TIMEZONE);
1261 if (!TEST_DIRECTORY.equals("random")) sb.append(" -Dtests.directory=").append(TEST_DIRECTORY);
1262 if (RANDOM_MULTIPLIER > 1) sb.append(" -Dtests.multiplier=").append(RANDOM_MULTIPLIER);
1263 if (TEST_NIGHTLY) sb.append(" -Dtests.nightly=true");
1264 // TODO we can't randomize this yet (it drives ant crazy) but this makes tests reproduceable
1265 // in case machines have different default charsets...
1266 sb.append(" -Dargs=\"-Dfile.encoding=" + System.getProperty("file.encoding") + "\"");
1267 return sb.toString();
1270 // recorded seed: for beforeClass
1271 private static long staticSeed;
1272 // seed for individual test methods, changed in @before
1275 static final Random seedRand = new Random();
1276 protected static final SmartRandom random = new SmartRandom(0);
1278 private String name = "<unknown>";
1281 * Annotation for tests that should only be run during nightly builds.
1285 @Retention(RetentionPolicy.RUNTIME)
1286 public @interface Nightly {}
1288 @Ignore("just a hack")
1289 public final void alwaysIgnoredTestMethod() {}
1291 /** check if assertions are enabled */
1292 private static boolean assertionsEnabled() {
1294 assert Boolean.FALSE.booleanValue();
1295 return false; // should never get here
1296 } catch (AssertionError e) {