+++ /dev/null
-package org.apache.lucene.util;
-
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Inherited;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Constructor;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.document.Field.Index;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.Field.TermVector;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.LogByteSizeMergePolicy;
-import org.apache.lucene.index.LogDocMergePolicy;
-import org.apache.lucene.index.LogMergePolicy;
-import org.apache.lucene.index.MergePolicy;
-import org.apache.lucene.index.MockRandomMergePolicy;
-import org.apache.lucene.index.SerialMergeScheduler;
-import org.apache.lucene.index.SlowMultiReaderWrapper;
-import org.apache.lucene.index.TieredMergePolicy;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.FieldCache.CacheEntry;
-import org.apache.lucene.search.AssertingIndexSearcher;
-import org.apache.lucene.search.FieldCache;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.FSDirectory;
-import org.apache.lucene.store.LockFactory;
-import org.apache.lucene.store.MockDirectoryWrapper;
-import org.apache.lucene.util.FieldCacheSanityChecker.Insanity;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Assert;
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.rules.TestWatchman;
-import org.junit.runner.RunWith;
-import org.junit.runners.model.FrameworkMethod;
-
-/**
- * Base class for all Lucene unit tests, Junit3 or Junit4 variant.
- * <p>
- * </p>
- * <p>
- * If you
- * override either <code>setUp()</code> or
- * <code>tearDown()</code> in your unit test, make sure you
- * call <code>super.setUp()</code> and
- * <code>super.tearDown()</code>
- * </p>
- *
- * <code>@After</code> - replaces setup
- * <code>@Before</code> - replaces teardown
- * <code>@Test</code> - any public method with this annotation is a test case, regardless
- * of its name
- * <p>
- * <p>
- * See Junit4 <a href="http://junit.org/junit/javadoc/4.7/">documentation</a> for a complete list of features.
- * <p>
- * Import from org.junit rather than junit.framework.
- * <p>
- * You should be able to use this class anywhere you used LuceneTestCase
- * if you annotate your derived class correctly with the annotations above
- * @see #assertSaneFieldCaches(String)
- */
-
-@RunWith(LuceneTestCaseRunner.class)
-public abstract class LuceneTestCase extends Assert {
-
- /**
- * true iff tests are run in verbose mode. Note: if it is false, tests are not
- * expected to print any messages.
- */
- public static final boolean VERBOSE = Boolean.getBoolean("tests.verbose");
-
- /** Use this constant when creating Analyzers and any other version-dependent stuff.
- * <p><b>NOTE:</b> Change this when development starts for new Lucene version:
- */
- public static final Version TEST_VERSION_CURRENT = Version.LUCENE_34;
-
- /**
- * If this is set, it is the only method that should run.
- */
- static final String TEST_METHOD;
-
- /** Create indexes in this directory, optimally use a subdir, named after the test */
- public static final File TEMP_DIR;
- static {
- String method = System.getProperty("testmethod", "").trim();
- TEST_METHOD = method.length() == 0 ? null : method;
- String s = System.getProperty("tempDir", System.getProperty("java.io.tmpdir"));
- if (s == null)
- throw new RuntimeException("To run tests, you need to define system property 'tempDir' or 'java.io.tmpdir'.");
- TEMP_DIR = new File(s);
- TEMP_DIR.mkdirs();
- }
-
- /** set of directories we created, in afterclass we try to clean these up */
- private static final Map<File, StackTraceElement[]> tempDirs = Collections.synchronizedMap(new HashMap<File, StackTraceElement[]>());
-
- // by default we randomly pick a different codec for
- // each test case (non-J4 tests) and each test class (J4
- // tests)
- /** Gets the locale to run tests with */
- public static final String TEST_LOCALE = System.getProperty("tests.locale", "random");
- /** Gets the timezone to run tests with */
- public static final String TEST_TIMEZONE = System.getProperty("tests.timezone", "random");
- /** Gets the directory to run tests with */
- public static final String TEST_DIRECTORY = System.getProperty("tests.directory", "random");
- /** Get the number of times to run tests */
- public static final int TEST_ITER = Integer.parseInt(System.getProperty("tests.iter", "1"));
- /** Get the minimum number of times to run tests until a failure happens */
- public static final int TEST_ITER_MIN = Integer.parseInt(System.getProperty("tests.iter.min", Integer.toString(TEST_ITER)));
- /** Get the random seed for tests */
- public static final String TEST_SEED = System.getProperty("tests.seed", "random");
- /** whether or not nightly tests should run */
- public static final boolean TEST_NIGHTLY = Boolean.parseBoolean(System.getProperty("tests.nightly", "false"));
- /** the line file used by LineFileDocs */
- public static final String TEST_LINE_DOCS_FILE = System.getProperty("tests.linedocsfile", "europarl.lines.txt.gz");
- /** whether or not to clean threads between test invocations: "false", "perMethod", "perClass" */
- public static final String TEST_CLEAN_THREADS = System.getProperty("tests.cleanthreads", "perClass");
-
- /**
- * A random multiplier which you should use when writing random tests:
- * multiply it by the number of iterations
- */
- public static final int RANDOM_MULTIPLIER = Integer.parseInt(System.getProperty("tests.multiplier", "1"));
-
- private int savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
-
- private volatile Thread.UncaughtExceptionHandler savedUncaughtExceptionHandler = null;
-
- /** Used to track if setUp and tearDown are called correctly from subclasses */
- private static State state = State.INITIAL;
-
- private static enum State {
- INITIAL, // no tests ran yet
- SETUP, // test has called setUp()
- RANTEST, // test is running
- TEARDOWN // test has called tearDown()
- }
-
- private static class UncaughtExceptionEntry {
- public final Thread thread;
- public final Throwable exception;
-
- public UncaughtExceptionEntry(Thread thread, Throwable exception) {
- this.thread = thread;
- this.exception = exception;
- }
- }
- private List<UncaughtExceptionEntry> uncaughtExceptions = Collections.synchronizedList(new ArrayList<UncaughtExceptionEntry>());
-
- private static Locale locale;
- private static Locale savedLocale;
- private static TimeZone timeZone;
- private static TimeZone savedTimeZone;
-
- protected static Map<MockDirectoryWrapper,StackTraceElement[]> stores;
-
- /** @deprecated: until we fix no-fork problems in solr tests */
- @Deprecated
- static List<String> testClassesRun = new ArrayList<String>();
-
- private static void initRandom() {
- assert !random.initialized;
- staticSeed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l1;
- random.setSeed(staticSeed);
- random.initialized = true;
- }
-
- @Deprecated
- private static boolean icuTested = false;
-
- @BeforeClass
- public static void beforeClassLuceneTestCaseJ4() {
- initRandom();
- state = State.INITIAL;
- tempDirs.clear();
- stores = Collections.synchronizedMap(new IdentityHashMap<MockDirectoryWrapper,StackTraceElement[]>());
- // enable this by default, for IDE consistency with ant tests (as its the default from ant)
- // TODO: really should be in solr base classes, but some extend LTC directly.
- // we do this in beforeClass, because some tests currently disable it
- if (System.getProperty("solr.directoryFactory") == null) {
- System.setProperty("solr.directoryFactory", "org.apache.solr.core.MockDirectoryFactory");
- }
- // this code consumes randoms where 4.0's lucenetestcase would: to make seeds work across both branches.
- // TODO: doesn't completely work, because what if we get mockrandom codec?!
- if (random.nextInt(4) != 0) {
- random.nextInt(); // consume RandomCodecProvider's seed.
- }
- // end compatibility random-consumption
-
- savedLocale = Locale.getDefault();
-
- // START hack to init ICU safely before we randomize locales.
- // ICU fails during classloading when a special Java7-only locale is the default
- // see: http://bugs.icu-project.org/trac/ticket/8734
- if (!icuTested) {
- icuTested = true;
- try {
- Locale.setDefault(Locale.US);
- Class.forName("com.ibm.icu.util.ULocale");
- } catch (ClassNotFoundException cnfe) {
- // ignore if no ICU is in classpath
- }
- }
- // END hack
-
- locale = TEST_LOCALE.equals("random") ? randomLocale(random) : localeForName(TEST_LOCALE);
- Locale.setDefault(locale);
- savedTimeZone = TimeZone.getDefault();
- timeZone = TEST_TIMEZONE.equals("random") ? randomTimeZone(random) : TimeZone.getTimeZone(TEST_TIMEZONE);
- TimeZone.setDefault(timeZone);
- testsFailed = false;
- }
-
- @AfterClass
- public static void afterClassLuceneTestCaseJ4() {
- State oldState = state; // capture test execution state
- state = State.INITIAL; // set the state for subsequent tests
-
- Throwable problem = null;
- try {
- if (!testsFailed) {
- assertTrue("ensure your setUp() calls super.setUp() and your tearDown() calls super.tearDown()!!!",
- oldState == State.INITIAL || oldState == State.TEARDOWN);
- }
- } catch (Throwable t) {
- if (problem == null) problem = t;
- }
-
- if (! "false".equals(TEST_CLEAN_THREADS)) {
- int rogueThreads = threadCleanup("test class");
- if (rogueThreads > 0) {
- // TODO: fail here once the leaks are fixed.
- System.err.println("RESOURCE LEAK: test class left " + rogueThreads + " thread(s) running");
- }
- }
-
- Locale.setDefault(savedLocale);
- TimeZone.setDefault(savedTimeZone);
- System.clearProperty("solr.solr.home");
- System.clearProperty("solr.data.dir");
-
- try {
- // now look for unclosed resources
- if (!testsFailed) {
- checkResourcesAfterClass();
- }
- } catch (Throwable t) {
- if (problem == null) problem = t;
- }
-
- stores = null;
-
- try {
- // clear out any temp directories if we can
- if (!testsFailed) {
- clearTempDirectoriesAfterClass();
- }
- } catch (Throwable t) {
- if (problem == null) problem = t;
- }
-
- // if we had afterClass failures, get some debugging information
- if (problem != null) {
- reportPartialFailureInfo();
- }
-
- // if verbose or tests failed, report some information back
- if (VERBOSE || testsFailed || problem != null) {
- printDebuggingInformation();
- }
-
- // reset seed
- random.setSeed(0L);
- random.initialized = false;
-
- if (problem != null) {
- throw new RuntimeException(problem);
- }
- }
-
- /** print some useful debugging information about the environment */
- private static void printDebuggingInformation() {
- System.err.println("NOTE: test params are: " +
- "locale=" + locale +
- ", timezone=" + (timeZone == null ? "(null)" : timeZone.getID()));
- System.err.println("NOTE: all tests run in this JVM:");
- System.err.println(Arrays.toString(testClassesRun.toArray()));
- System.err.println("NOTE: " + System.getProperty("os.name") + " "
- + System.getProperty("os.version") + " "
- + System.getProperty("os.arch") + "/"
- + System.getProperty("java.vendor") + " "
- + System.getProperty("java.version") + " "
- + (Constants.JRE_IS_64BIT ? "(64-bit)" : "(32-bit)") + "/"
- + "cpus=" + Runtime.getRuntime().availableProcessors() + ","
- + "threads=" + Thread.activeCount() + ","
- + "free=" + Runtime.getRuntime().freeMemory() + ","
- + "total=" + Runtime.getRuntime().totalMemory());
- }
-
- /** check that directories and their resources were closed */
- private static void checkResourcesAfterClass() {
- for (MockDirectoryWrapper d : stores.keySet()) {
- if (d.isOpen()) {
- StackTraceElement elements[] = stores.get(d);
- // Look for the first class that is not LuceneTestCase that requested
- // a Directory. The first two items are of Thread's, so skipping over
- // them.
- StackTraceElement element = null;
- for (int i = 2; i < elements.length; i++) {
- StackTraceElement ste = elements[i];
- if (ste.getClassName().indexOf("LuceneTestCase") == -1) {
- element = ste;
- break;
- }
- }
- fail("directory of test was not closed, opened from: " + element);
- }
- }
- }
-
- /** clear temp directories: this will fail if its not successful */
- private static void clearTempDirectoriesAfterClass() {
- for (Entry<File, StackTraceElement[]> entry : tempDirs.entrySet()) {
- try {
- _TestUtil.rmDir(entry.getKey());
- } catch (IOException e) {
- e.printStackTrace();
- System.err.println("path " + entry.getKey() + " allocated from");
- // first two STE's are Java's
- StackTraceElement[] elements = entry.getValue();
- for (int i = 2; i < elements.length; i++) {
- StackTraceElement ste = elements[i];
- // print only our code's stack information
- if (ste.getClassName().indexOf("org.apache.lucene") == -1) break;
- System.err.println("\t" + ste);
- }
- fail("could not remove temp dir: " + entry.getKey());
- }
- }
- }
-
- protected static boolean testsFailed; /* true if any tests failed */
-
- // This is how we get control when errors occur.
- // Think of this as start/end/success/failed
- // events.
- @Rule
- public final TestWatchman intercept = new TestWatchman() {
-
- @Override
- public void failed(Throwable e, FrameworkMethod method) {
- // org.junit.internal.AssumptionViolatedException in older releases
- // org.junit.Assume.AssumptionViolatedException in recent ones
- if (e.getClass().getName().endsWith("AssumptionViolatedException")) {
- if (e.getCause() instanceof _TestIgnoredException)
- e = e.getCause();
- System.err.print("NOTE: Assume failed in '" + method.getName() + "' (ignored):");
- if (VERBOSE) {
- System.err.println();
- e.printStackTrace(System.err);
- } else {
- System.err.print(" ");
- System.err.println(e.getMessage());
- }
- } else {
- testsFailed = true;
- reportAdditionalFailureInfo();
- }
- super.failed(e, method);
- }
-
- @Override
- public void starting(FrameworkMethod method) {
- // set current method name for logging
- LuceneTestCase.this.name = method.getName();
- State s = state; // capture test execution state
- state = State.RANTEST; // set the state for subsequent tests
- if (!testsFailed) {
- assertTrue("ensure your setUp() calls super.setUp()!!!", s == State.SETUP);
- }
- super.starting(method);
- }
- };
-
- @Before
- public void setUp() throws Exception {
- seed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l2;
- random.setSeed(seed);
- State s = state; // capture test execution state
- state = State.SETUP; // set the state for subsequent tests
-
- savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- public void uncaughtException(Thread t, Throwable e) {
- testsFailed = true;
- uncaughtExceptions.add(new UncaughtExceptionEntry(t, e));
- if (savedUncaughtExceptionHandler != null)
- savedUncaughtExceptionHandler.uncaughtException(t, e);
- }
- });
-
- savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
-
- if (!testsFailed) {
- assertTrue("ensure your tearDown() calls super.tearDown()!!!", (s == State.INITIAL || s == State.TEARDOWN));
- }
- }
-
- /**
- * Forcible purges all cache entries from the FieldCache.
- * <p>
- * This method will be called by tearDown to clean up FieldCache.DEFAULT.
- * If a (poorly written) test has some expectation that the FieldCache
- * will persist across test methods (ie: a static IndexReader) this
- * method can be overridden to do nothing.
- * </p>
- *
- * @see FieldCache#purgeAllCaches()
- */
- protected void purgeFieldCache(final FieldCache fc) {
- fc.purgeAllCaches();
- }
-
- protected String getTestLabel() {
- return getClass().getName() + "." + getName();
- }
-
- public static void setUseCompoundFile(MergePolicy mp, boolean useCompound) {
- if (mp instanceof LogMergePolicy) {
- ((LogMergePolicy) mp).setUseCompoundFile(useCompound);
- } else if (mp instanceof TieredMergePolicy) {
- ((TieredMergePolicy) mp).setUseCompoundFile(useCompound);
- } else {
- fail("MergePolicy (compound-file) not supported " + mp);
- }
- }
-
- public static void setMergeFactor(MergePolicy mp, int mergeFactor) {
- if (mp instanceof LogMergePolicy) {
- ((LogMergePolicy) mp).setMergeFactor(mergeFactor);
- } else if (mp instanceof TieredMergePolicy) {
- ((TieredMergePolicy) mp).setMaxMergeAtOnce(mergeFactor);
- ((TieredMergePolicy) mp).setMaxMergeAtOnceExplicit(mergeFactor);
- } else {
- fail("MergePolicy not supported " + mp);
- }
- }
-
- @After
- public void tearDown() throws Exception {
- State oldState = state; // capture test execution state
- state = State.TEARDOWN; // set the state for subsequent tests
-
- // NOTE: with junit 4.7, we don't get a reproduceWith because our Watchman
- // does not know if something fails in tearDown. so we ensure this happens ourselves for now.
- // we can remove this if we upgrade to 4.8
- Throwable problem = null;
-
- try {
- if (!testsFailed) {
- // Note: we allow a test to go straight from SETUP -> TEARDOWN (without ever entering the RANTEST state)
- // because if you assume() inside setUp(), it skips the test and the TestWatchman has no way to know...
- assertTrue("ensure your setUp() calls super.setUp()!!!", oldState == State.RANTEST || oldState == State.SETUP);
- }
- } catch (Throwable t) {
- if (problem == null) problem = t;
- }
-
- BooleanQuery.setMaxClauseCount(savedBoolMaxClauseCount);
-
- // this won't throw any exceptions or fail the test
- // if we change this, then change this logic
- checkRogueThreadsAfter();
- // restore the default uncaught exception handler
- Thread.setDefaultUncaughtExceptionHandler(savedUncaughtExceptionHandler);
-
- try {
- checkUncaughtExceptionsAfter();
- } catch (Throwable t) {
- if (problem == null) problem = t;
- }
-
- try {
- // calling assertSaneFieldCaches here isn't as useful as having test
- // classes call it directly from the scope where the index readers
- // are used, because they could be gc'ed just before this tearDown
- // method is called.
- //
- // But it's better then nothing.
- //
- // If you are testing functionality that you know for a fact
- // "violates" FieldCache sanity, then you should either explicitly
- // call purgeFieldCache at the end of your test method, or refactor
- // your Test class so that the inconsistant FieldCache usages are
- // isolated in distinct test methods
- assertSaneFieldCaches(getTestLabel());
- } catch (Throwable t) {
- if (problem == null) problem = t;
- }
-
- purgeFieldCache(FieldCache.DEFAULT);
-
- if (problem != null) {
- testsFailed = true;
- reportAdditionalFailureInfo();
- throw new RuntimeException(problem);
- }
- }
-
- /** check if the test still has threads running, we don't want them to
- * fail in a subsequent test and pass the blame to the wrong test */
- private void checkRogueThreadsAfter() {
- if ("perMethod".equals(TEST_CLEAN_THREADS)) {
- int rogueThreads = threadCleanup("test method: '" + getName() + "'");
- if (!testsFailed && rogueThreads > 0) {
- System.err.println("RESOURCE LEAK: test method: '" + getName()
- + "' left " + rogueThreads + " thread(s) running");
- // TODO: fail, but print seed for now
- if (uncaughtExceptions.isEmpty()) {
- reportAdditionalFailureInfo();
- }
- }
- }
- }
-
- /** see if any other threads threw uncaught exceptions, and fail the test if so */
- private void checkUncaughtExceptionsAfter() {
- if (!uncaughtExceptions.isEmpty()) {
- System.err.println("The following exceptions were thrown by threads:");
- for (UncaughtExceptionEntry entry : uncaughtExceptions) {
- System.err.println("*** Thread: " + entry.thread.getName() + " ***");
- entry.exception.printStackTrace(System.err);
- }
- fail("Some threads threw uncaught exceptions!");
- }
- }
-
- private final static int THREAD_STOP_GRACE_MSEC = 50;
- // jvm-wide list of 'rogue threads' we found, so they only get reported once.
- private final static IdentityHashMap<Thread,Boolean> rogueThreads = new IdentityHashMap<Thread,Boolean>();
-
- static {
- // just a hack for things like eclipse test-runner threads
- for (Thread t : Thread.getAllStackTraces().keySet()) {
- rogueThreads.put(t, true);
- }
-
- if (TEST_ITER > 1) {
- System.out.println("WARNING: you are using -Dtests.iter=n where n > 1, not all tests support this option.");
- System.out.println("Some may crash or fail: this is not a bug.");
- }
- }
-
- /**
- * Looks for leftover running threads, trying to kill them off,
- * so they don't fail future tests.
- * returns the number of rogue threads that it found.
- */
- private static int threadCleanup(String context) {
- // educated guess
- Thread[] stillRunning = new Thread[Thread.activeCount()+1];
- int threadCount = 0;
- int rogueCount = 0;
-
- if ((threadCount = Thread.enumerate(stillRunning)) > 1) {
- while (threadCount == stillRunning.length) {
- // truncated response
- stillRunning = new Thread[stillRunning.length*2];
- threadCount = Thread.enumerate(stillRunning);
- }
-
- for (int i = 0; i < threadCount; i++) {
- Thread t = stillRunning[i];
-
- if (t.isAlive() &&
- !rogueThreads.containsKey(t) &&
- t != Thread.currentThread() &&
- // TODO: TimeLimitingCollector starts a thread statically.... WTF?!
- !t.getName().equals("TimeLimitedCollector timer thread") &&
- /* its ok to keep your searcher across test cases */
- (t.getName().startsWith("LuceneTestCase") && context.startsWith("test method")) == false) {
- System.err.println("WARNING: " + context + " left thread running: " + t);
- rogueThreads.put(t, true);
- rogueCount++;
- if (t.getName().startsWith("LuceneTestCase")) {
- System.err.println("PLEASE CLOSE YOUR INDEXSEARCHERS IN YOUR TEST!!!!");
- continue;
- } else {
- // wait on the thread to die of natural causes
- try {
- t.join(THREAD_STOP_GRACE_MSEC);
- } catch (InterruptedException e) { e.printStackTrace(); }
- }
- // try to stop the thread:
- t.setUncaughtExceptionHandler(null);
- Thread.setDefaultUncaughtExceptionHandler(null);
- t.interrupt();
- }
- }
- }
- return rogueCount;
- }
-
- /**
- * Asserts that FieldCacheSanityChecker does not detect any
- * problems with FieldCache.DEFAULT.
- * <p>
- * If any problems are found, they are logged to System.err
- * (allong with the msg) when the Assertion is thrown.
- * </p>
- * <p>
- * This method is called by tearDown after every test method,
- * however IndexReaders scoped inside test methods may be garbage
- * collected prior to this method being called, causing errors to
- * be overlooked. Tests are encouraged to keep their IndexReaders
- * scoped at the class level, or to explicitly call this method
- * directly in the same scope as the IndexReader.
- * </p>
- *
- * @see org.apache.lucene.util.FieldCacheSanityChecker
- */
- protected void assertSaneFieldCaches(final String msg) {
- final CacheEntry[] entries = FieldCache.DEFAULT.getCacheEntries();
- Insanity[] insanity = null;
- try {
- try {
- insanity = FieldCacheSanityChecker.checkSanity(entries);
- } catch (RuntimeException e) {
- dumpArray(msg + ": FieldCache", entries, System.err);
- throw e;
- }
-
- assertEquals(msg + ": Insane FieldCache usage(s) found",
- 0, insanity.length);
- insanity = null;
- } finally {
-
- // report this in the event of any exception/failure
- // if no failure, then insanity will be null anyway
- if (null != insanity) {
- dumpArray(msg + ": Insane FieldCache usage(s)", insanity, System.err);
- }
-
- }
- }
-
- /**
- * Returns a number of at least <code>i</code>
- * <p>
- * The actual number returned will be influenced by whether {@link #TEST_NIGHTLY}
- * is active and {@link #RANDOM_MULTIPLIER}, but also with some random fudge.
- */
- public static int atLeast(Random random, int i) {
- int min = (TEST_NIGHTLY ? 3*i : i) * RANDOM_MULTIPLIER;
- int max = min+(min/2);
- return _TestUtil.nextInt(random, min, max);
- }
-
- public static int atLeast(int i) {
- return atLeast(random, i);
- }
-
- /**
- * Returns true if something should happen rarely,
- * <p>
- * The actual number returned will be influenced by whether {@link #TEST_NIGHTLY}
- * is active and {@link #RANDOM_MULTIPLIER}.
- */
- public static boolean rarely(Random random) {
- int p = TEST_NIGHTLY ? 10 : 5;
- p += (p * Math.log(RANDOM_MULTIPLIER));
- int min = 100 - Math.min(p, 50); // never more than 50
- return random.nextInt(100) >= min;
- }
-
- public static boolean rarely() {
- return rarely(random);
- }
-
- public static boolean usually(Random random) {
- return !rarely(random);
- }
-
- public static boolean usually() {
- return usually(random);
- }
-
- // These deprecated methods should be removed soon, when all tests using no Epsilon are fixed:
-
- @Deprecated
- static public void assertEquals(double expected, double actual) {
- assertEquals(null, expected, actual);
- }
-
- @Deprecated
- static public void assertEquals(String message, double expected, double actual) {
- assertEquals(message, Double.valueOf(expected), Double.valueOf(actual));
- }
-
- @Deprecated
- static public void assertEquals(float expected, float actual) {
- assertEquals(null, expected, actual);
- }
-
- @Deprecated
- static public void assertEquals(String message, float expected, float actual) {
- assertEquals(message, Float.valueOf(expected), Float.valueOf(actual));
- }
-
- public static void assumeTrue(String msg, boolean b) {
- Assume.assumeNoException(b ? null : new _TestIgnoredException(msg));
- }
-
- public static void assumeFalse(String msg, boolean b) {
- assumeTrue(msg, !b);
- }
-
- public static void assumeNoException(String msg, Exception e) {
- Assume.assumeNoException(e == null ? null : new _TestIgnoredException(msg, e));
- }
-
- /**
- * Convenience method for logging an iterator.
- *
- * @param label String logged before/after the items in the iterator
- * @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.
- * @param stream Stream to log messages to.
- */
- public static void dumpIterator(String label, Iterator<?> iter,
- PrintStream stream) {
- stream.println("*** BEGIN " + label + " ***");
- if (null == iter) {
- stream.println(" ... NULL ...");
- } else {
- while (iter.hasNext()) {
- stream.println(iter.next().toString());
- }
- }
- stream.println("*** END " + label + " ***");
- }
-
- /**
- * Convenience method for logging an array. Wraps the array in an iterator and delegates
- *
- * @see #dumpIterator(String,Iterator,PrintStream)
- */
- public static void dumpArray(String label, Object[] objs,
- PrintStream stream) {
- Iterator<?> iter = (null == objs) ? null : Arrays.asList(objs).iterator();
- dumpIterator(label, iter, stream);
- }
-
- /** create a new index writer config with random defaults */
- public static IndexWriterConfig newIndexWriterConfig(Version v, Analyzer a) {
- return newIndexWriterConfig(random, v, a);
- }
-
- /** create a new index writer config with random defaults using the specified random */
- public static IndexWriterConfig newIndexWriterConfig(Random r, Version v, Analyzer a) {
- IndexWriterConfig c = new IndexWriterConfig(v, a);
- if (r.nextBoolean()) {
- c.setMergePolicy(newTieredMergePolicy());
- } else if (r.nextBoolean()) {
- c.setMergePolicy(newLogMergePolicy());
- } else {
- c.setMergePolicy(new MockRandomMergePolicy(r));
- }
-
- if (r.nextBoolean()) {
- c.setMergeScheduler(new SerialMergeScheduler());
- }
- if (r.nextBoolean()) {
- if (rarely(r)) {
- // crazy value
- c.setMaxBufferedDocs(_TestUtil.nextInt(r, 2, 7));
- } else {
- // reasonable value
- c.setMaxBufferedDocs(_TestUtil.nextInt(r, 8, 1000));
- }
- }
- if (r.nextBoolean()) {
- if (rarely(r)) {
- // crazy value
- c.setTermIndexInterval(r.nextBoolean() ? _TestUtil.nextInt(r, 1, 31) : _TestUtil.nextInt(r, 129, 1000));
- } else {
- // reasonable value
- c.setTermIndexInterval(_TestUtil.nextInt(r, 32, 128));
- }
- }
- if (r.nextBoolean()) {
- c.setMaxThreadStates(_TestUtil.nextInt(r, 1, 20));
- }
-
- if (r.nextBoolean()) {
- c.setMergePolicy(new MockRandomMergePolicy(r));
- } else {
- c.setMergePolicy(newLogMergePolicy());
- }
-
- c.setReaderPooling(r.nextBoolean());
- c.setReaderTermsIndexDivisor(_TestUtil.nextInt(r, 1, 4));
- return c;
- }
-
- public static LogMergePolicy newLogMergePolicy() {
- return newLogMergePolicy(random);
- }
-
- public static TieredMergePolicy newTieredMergePolicy() {
- return newTieredMergePolicy(random);
- }
-
- public static LogMergePolicy newLogMergePolicy(Random r) {
- LogMergePolicy logmp = r.nextBoolean() ? new LogDocMergePolicy() : new LogByteSizeMergePolicy();
- logmp.setUseCompoundFile(r.nextBoolean());
- logmp.setCalibrateSizeByDeletes(r.nextBoolean());
- if (rarely(r)) {
- logmp.setMergeFactor(_TestUtil.nextInt(r, 2, 4));
- } else {
- logmp.setMergeFactor(_TestUtil.nextInt(r, 5, 50));
- }
- return logmp;
- }
-
- public static TieredMergePolicy newTieredMergePolicy(Random r) {
- TieredMergePolicy tmp = new TieredMergePolicy();
- if (rarely(r)) {
- tmp.setMaxMergeAtOnce(_TestUtil.nextInt(r, 2, 4));
- tmp.setMaxMergeAtOnceExplicit(_TestUtil.nextInt(r, 2, 4));
- } else {
- tmp.setMaxMergeAtOnce(_TestUtil.nextInt(r, 5, 50));
- tmp.setMaxMergeAtOnceExplicit(_TestUtil.nextInt(r, 5, 50));
- }
- tmp.setMaxMergedSegmentMB(0.2 + r.nextDouble() * 2.0);
- tmp.setFloorSegmentMB(0.2 + r.nextDouble() * 2.0);
- tmp.setExpungeDeletesPctAllowed(0.0 + r.nextDouble() * 30.0);
- tmp.setSegmentsPerTier(_TestUtil.nextInt(r, 2, 20));
- tmp.setUseCompoundFile(r.nextBoolean());
- tmp.setNoCFSRatio(0.1 + r.nextDouble()*0.8);
- tmp.setReclaimDeletesWeight(r.nextDouble()*4);
- return tmp;
- }
-
- public static LogMergePolicy newLogMergePolicy(boolean useCFS) {
- LogMergePolicy logmp = newLogMergePolicy();
- logmp.setUseCompoundFile(useCFS);
- return logmp;
- }
-
- public static LogMergePolicy newLogMergePolicy(boolean useCFS, int mergeFactor) {
- LogMergePolicy logmp = newLogMergePolicy();
- logmp.setUseCompoundFile(useCFS);
- logmp.setMergeFactor(mergeFactor);
- return logmp;
- }
-
- public static LogMergePolicy newLogMergePolicy(int mergeFactor) {
- LogMergePolicy logmp = newLogMergePolicy();
- logmp.setMergeFactor(mergeFactor);
- return logmp;
- }
-
- /**
- * Returns a new Directory instance. Use this when the test does not
- * care about the specific Directory implementation (most tests).
- * <p>
- * The Directory is wrapped with {@link MockDirectoryWrapper}.
- * By default this means it will be picky, such as ensuring that you
- * properly close it and all open files in your test. It will emulate
- * some features of Windows, such as not allowing open files to be
- * overwritten.
- */
- public static MockDirectoryWrapper newDirectory() throws IOException {
- return newDirectory(random);
- }
-
- /**
- * Returns a new Directory instance, using the specified random.
- * See {@link #newDirectory()} for more information.
- */
- public static MockDirectoryWrapper newDirectory(Random r) throws IOException {
- Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
- MockDirectoryWrapper dir = new MockDirectoryWrapper(r, impl);
- stores.put(dir, Thread.currentThread().getStackTrace());
- return dir;
- }
-
- /**
- * Returns a new Directory instance, with contents copied from the
- * provided directory. See {@link #newDirectory()} for more
- * information.
- */
- public static MockDirectoryWrapper newDirectory(Directory d) throws IOException {
- return newDirectory(random, d);
- }
-
- /** Returns a new FSDirectory instance over the given file, which must be a folder. */
- public static MockDirectoryWrapper newFSDirectory(File f) throws IOException {
- return newFSDirectory(f, null);
- }
-
- /** Returns a new FSDirectory instance over the given file, which must be a folder. */
- public static MockDirectoryWrapper newFSDirectory(File f, LockFactory lf) throws IOException {
- String fsdirClass = TEST_DIRECTORY;
- if (fsdirClass.equals("random")) {
- fsdirClass = FS_DIRECTORIES[random.nextInt(FS_DIRECTORIES.length)];
- }
-
- if (fsdirClass.indexOf(".") == -1) {// if not fully qualified, assume .store
- fsdirClass = "org.apache.lucene.store." + fsdirClass;
- }
-
- Class<? extends FSDirectory> clazz;
- try {
- try {
- clazz = Class.forName(fsdirClass).asSubclass(FSDirectory.class);
- } catch (ClassCastException e) {
- // TEST_DIRECTORY is not a sub-class of FSDirectory, so draw one at random
- fsdirClass = FS_DIRECTORIES[random.nextInt(FS_DIRECTORIES.length)];
-
- if (fsdirClass.indexOf(".") == -1) {// if not fully qualified, assume .store
- fsdirClass = "org.apache.lucene.store." + fsdirClass;
- }
-
- clazz = Class.forName(fsdirClass).asSubclass(FSDirectory.class);
- }
- MockDirectoryWrapper dir = new MockDirectoryWrapper(random, newFSDirectoryImpl(clazz, f));
- if (lf != null) {
- dir.setLockFactory(lf);
- }
- stores.put(dir, Thread.currentThread().getStackTrace());
- return dir;
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Returns a new Directory instance, using the specified random
- * with contents copied from the provided directory. See
- * {@link #newDirectory()} for more information.
- */
- public static MockDirectoryWrapper newDirectory(Random r, Directory d) throws IOException {
- Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
- for (String file : d.listAll()) {
- d.copy(impl, file, file);
- }
- MockDirectoryWrapper dir = new MockDirectoryWrapper(r, impl);
- stores.put(dir, Thread.currentThread().getStackTrace());
- return dir;
- }
-
- /** Returns a new field instance.
- * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
- public static Field newField(String name, String value, Index index) {
- return newField(random, name, value, index);
- }
-
- /** Returns a new field instance.
- * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
- public static Field newField(String name, String value, Store store, Index index) {
- return newField(random, name, value, store, index);
- }
-
- /**
- * Returns a new Field instance. Use this when the test does not
- * care about some specific field settings (most tests)
- * <ul>
- * <li>If the store value is set to Store.NO, sometimes the field will be randomly stored.
- * <li>More term vector data than you ask for might be indexed, for example if you choose YES
- * it might index term vectors with offsets too.
- * </ul>
- */
- public static Field newField(String name, String value, Store store, Index index, TermVector tv) {
- return newField(random, name, value, store, index, tv);
- }
-
- /** Returns a new field instance, using the specified random.
- * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
- public static Field newField(Random random, String name, String value, Index index) {
- return newField(random, name, value, Store.NO, index);
- }
-
- /** Returns a new field instance, using the specified random.
- * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
- public static Field newField(Random random, String name, String value, Store store, Index index) {
- return newField(random, name, value, store, index, TermVector.NO);
- }
-
- /** Returns a new field instance, using the specified random.
- * See {@link #newField(String, String, Field.Store, Field.Index, Field.TermVector)} for more information */
- public static Field newField(Random random, String name, String value, Store store, Index index, TermVector tv) {
- if (usually(random)) {
- // most of the time, don't modify the params
- return new Field(name, value, store, index, tv);
- }
-
- if (!index.isIndexed())
- return new Field(name, value, store, index, tv);
-
- if (!store.isStored() && random.nextBoolean())
- store = Store.YES; // randomly store it
-
- tv = randomTVSetting(random, tv);
-
- return new Field(name, value, store, index, tv);
- }
-
- static final TermVector tvSettings[] = {
- TermVector.NO, TermVector.YES, TermVector.WITH_OFFSETS,
- TermVector.WITH_POSITIONS, TermVector.WITH_POSITIONS_OFFSETS
- };
-
- private static TermVector randomTVSetting(Random random, TermVector minimum) {
- switch(minimum) {
- case NO: return tvSettings[_TestUtil.nextInt(random, 0, tvSettings.length-1)];
- case YES: return tvSettings[_TestUtil.nextInt(random, 1, tvSettings.length-1)];
- case WITH_OFFSETS: return random.nextBoolean() ? TermVector.WITH_OFFSETS
- : TermVector.WITH_POSITIONS_OFFSETS;
- case WITH_POSITIONS: return random.nextBoolean() ? TermVector.WITH_POSITIONS
- : TermVector.WITH_POSITIONS_OFFSETS;
- default: return TermVector.WITH_POSITIONS_OFFSETS;
- }
- }
-
- /** return a random Locale from the available locales on the system */
- public static Locale randomLocale(Random random) {
- Locale locales[] = Locale.getAvailableLocales();
- return locales[random.nextInt(locales.length)];
- }
-
- /** return a random TimeZone from the available timezones on the system */
- public static TimeZone randomTimeZone(Random random) {
- String tzIds[] = TimeZone.getAvailableIDs();
- return TimeZone.getTimeZone(tzIds[random.nextInt(tzIds.length)]);
- }
-
- /** return a Locale object equivalent to its programmatic name */
- public static Locale localeForName(String localeName) {
- String elements[] = localeName.split("\\_");
- switch(elements.length) {
- case 3: return new Locale(elements[0], elements[1], elements[2]);
- case 2: return new Locale(elements[0], elements[1]);
- case 1: return new Locale(elements[0]);
- default: throw new IllegalArgumentException("Invalid Locale: " + localeName);
- }
- }
-
- private static final String FS_DIRECTORIES[] = {
- "SimpleFSDirectory",
- "NIOFSDirectory",
- "MMapDirectory"
- };
-
- private static final String CORE_DIRECTORIES[] = {
- "RAMDirectory",
- FS_DIRECTORIES[0], FS_DIRECTORIES[1], FS_DIRECTORIES[2]
- };
-
- public static String randomDirectory(Random random) {
- if (rarely(random)) {
- return CORE_DIRECTORIES[random.nextInt(CORE_DIRECTORIES.length)];
- } else {
- return "RAMDirectory";
- }
- }
-
- private static Directory newFSDirectoryImpl(
- Class<? extends FSDirectory> clazz, File file)
- throws IOException {
- FSDirectory d = null;
- try {
- // Assuming every FSDirectory has a ctor(File), but not all may take a
- // LockFactory too, so setting it afterwards.
- Constructor<? extends FSDirectory> ctor = clazz.getConstructor(File.class);
- d = ctor.newInstance(file);
- } catch (Exception e) {
- d = FSDirectory.open(file);
- }
- return d;
- }
-
- /** Registers a temp file that will be deleted when tests are done. */
- public static void registerTempFile(File tmpFile) {
- tempDirs.put(tmpFile.getAbsoluteFile(), Thread.currentThread().getStackTrace());
- }
-
- static Directory newDirectoryImpl(Random random, String clazzName) {
- if (clazzName.equals("random"))
- clazzName = randomDirectory(random);
- if (clazzName.indexOf(".") == -1) // if not fully qualified, assume .store
- clazzName = "org.apache.lucene.store." + clazzName;
- try {
- final Class<? extends Directory> clazz = Class.forName(clazzName).asSubclass(Directory.class);
- // If it is a FSDirectory type, try its ctor(File)
- if (FSDirectory.class.isAssignableFrom(clazz)) {
- final File tmpFile = _TestUtil.createTempFile("test", "tmp", TEMP_DIR);
- tmpFile.delete();
- tmpFile.mkdir();
- registerTempFile(tmpFile);
- return newFSDirectoryImpl(clazz.asSubclass(FSDirectory.class), tmpFile);
- }
-
- // try empty ctor
- return clazz.newInstance();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- /** create a new searcher over the reader.
- * This searcher might randomly use threads. */
- public static IndexSearcher newSearcher(IndexReader r) throws IOException {
- return newSearcher(r, true);
- }
-
- /** create a new searcher over the reader.
- * This searcher might randomly use threads.
- * if <code>maybeWrap</code> is true, this searcher might wrap the reader
- * with one that returns null for getSequentialSubReaders.
- */
- public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) throws IOException {
- if (random.nextBoolean()) {
- if (maybeWrap && rarely()) {
- r = new SlowMultiReaderWrapper(r);
- }
- return new AssertingIndexSearcher(r);
- } else {
- int threads = 0;
- final ExecutorService ex = (random.nextBoolean()) ? null
- : Executors.newFixedThreadPool(threads = _TestUtil.nextInt(random, 1, 8),
- new NamedThreadFactory("LuceneTestCase"));
- if (ex != null && VERBOSE) {
- System.out.println("NOTE: newSearcher using ExecutorService with " + threads + " threads");
- }
- return new AssertingIndexSearcher(r, ex) {
- @Override
- public void close() throws IOException {
- super.close();
- shutdownExecutorService(ex);
- }
- };
- }
- }
-
- static void shutdownExecutorService(ExecutorService ex) {
- if (ex != null) {
- ex.shutdown();
- try {
- ex.awaitTermination(1000, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- public String getName() {
- return this.name;
- }
-
- /** Gets a resource from the classpath as {@link File}. This method should only be used,
- * if a real file is needed. To get a stream, code should prefer
- * {@link Class#getResourceAsStream} using {@code this.getClass()}.
- */
-
- protected File getDataFile(String name) throws IOException {
- try {
- return new File(this.getClass().getResource(name).toURI());
- } catch (Exception e) {
- throw new IOException("Cannot find resource: " + name);
- }
- }
-
- // We get here from InterceptTestCaseEvents on the 'failed' event....
- public static void reportPartialFailureInfo() {
- System.err.println("NOTE: reproduce with (hopefully): ant test -Dtestcase=" + testClassesRun.get(testClassesRun.size()-1)
- + " -Dtests.seed=" + new ThreeLongs(staticSeed, 0L, LuceneTestCaseRunner.runnerSeed)
- + reproduceWithExtraParams());
- }
-
- // We get here from InterceptTestCaseEvents on the 'failed' event....
- public void reportAdditionalFailureInfo() {
- System.err.println("NOTE: reproduce with: ant test -Dtestcase=" + getClass().getSimpleName()
- + " -Dtestmethod=" + getName() + " -Dtests.seed=" + new ThreeLongs(staticSeed, seed, LuceneTestCaseRunner.runnerSeed)
- + reproduceWithExtraParams());
- }
-
- // extra params that were overridden needed to reproduce the command
- private static String reproduceWithExtraParams() {
- StringBuilder sb = new StringBuilder();
- if (!TEST_LOCALE.equals("random")) sb.append(" -Dtests.locale=").append(TEST_LOCALE);
- if (!TEST_TIMEZONE.equals("random")) sb.append(" -Dtests.timezone=").append(TEST_TIMEZONE);
- if (!TEST_DIRECTORY.equals("random")) sb.append(" -Dtests.directory=").append(TEST_DIRECTORY);
- if (RANDOM_MULTIPLIER > 1) sb.append(" -Dtests.multiplier=").append(RANDOM_MULTIPLIER);
- if (TEST_NIGHTLY) sb.append(" -Dtests.nightly=true");
- return sb.toString();
- }
-
- // recorded seed: for beforeClass
- private static long staticSeed;
- // seed for individual test methods, changed in @before
- private long seed;
-
- static final Random seedRand = new Random();
- protected static final SmartRandom random = new SmartRandom(0);
-
- private String name = "<unknown>";
-
- /**
- * Annotation for tests that should only be run during nightly builds.
- */
- @Documented
- @Inherited
- @Retention(RetentionPolicy.RUNTIME)
- public @interface Nightly {}
-
- @Ignore("just a hack")
- public final void alwaysIgnoredTestMethod() {}
-}