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 org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.ConcurrentMergeScheduler;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.LogDocMergePolicy;
import org.apache.lucene.index.LogMergePolicy;
import org.apache.lucene.index.SerialMergeScheduler;
import org.apache.lucene.index.codecs.Codec;
import org.apache.lucene.index.codecs.CodecProvider;
import org.apache.lucene.index.codecs.mockintblock.MockFixedIntBlockCodec;
import org.apache.lucene.index.codecs.mockintblock.MockVariableIntBlockCodec;
import org.apache.lucene.index.codecs.mocksep.MockSepCodec;
import org.apache.lucene.index.codecs.preflex.PreFlexCodec;
import org.apache.lucene.index.codecs.preflexrw.PreFlexRWCodec;
import org.apache.lucene.index.codecs.pulsing.PulsingCodec;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.FieldCache;
import org.apache.lucene.search.FieldCache.CacheEntry;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.MMapDirectory;
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.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestWatchman;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runner.manipulation.Filter;
import org.junit.runner.manipulation.NoTestsRemainException;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.TimeZone;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* Base class for all Lucene unit tests, Junit4 variant.
* Replaces LuceneTestCase.
* <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>
*
* @After - replaces setup
* @Before - replaces teardown
* @Test - 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)
*/
// If we really need functionality in runBare override from LuceneTestCase,
// we can introduce RunBareWrapper and override runChild, and add the
// @RunWith annotation as below. runChild will be called for
// every test. But the functionality we used to
// get from that override is provided by InterceptTestCaseEvents
//@RunWith(RunBareWrapper.class)
@RunWith(LuceneTestCaseJ4.LuceneTestCaseRunner.class)
public abstract class LuceneTestCaseJ4 {
/**
* 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_40;
/**
* 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);
}
// by default we randomly pick a different codec for
// each test case (non-J4 tests) and each test class (J4
// tests)
/** Gets the codec to run tests with. */
static final String TEST_CODEC = System.getProperty("tests.codec", "random");
/** Gets the locale to run tests with */
static final String TEST_LOCALE = System.getProperty("tests.locale", "random");
/** Gets the timezone to run tests with */
static final String TEST_TIMEZONE = System.getProperty("tests.timezone", "random");
/** Gets the directory to run tests with */
static final String TEST_DIRECTORY = System.getProperty("tests.directory", "random");
/** Get the number of times to run tests */
static final int TEST_ITER = Integer.parseInt(System.getProperty("tests.iter", "1"));
private static final Pattern codecWithParam = Pattern.compile("(.*)\\(\\s*(\\d+)\\s*\\)");
/**
* 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;
private volatile Thread.UncaughtExceptionHandler savedUncaughtExceptionHandler = null;
/** Used to track if setUp and tearDown are called correctly from subclasses */
private boolean setup;
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>());
// checks if class correctly annotated
private static final Object PLACEHOLDER = new Object();
private static final Map<Class<? extends LuceneTestCaseJ4>,Object> checkedClasses =
Collections.synchronizedMap(new WeakHashMap<Class<? extends LuceneTestCaseJ4>,Object>());
// saves default codec: we do this statically as many build indexes in @beforeClass
private static String savedDefaultCodec;
private static Codec codec;
private static Locale locale;
private static Locale savedLocale;
private static TimeZone timeZone;
private static TimeZone savedTimeZone;
private static Map<MockDirectoryWrapper,StackTraceElement[]> stores;
private static final String[] TEST_CODECS = new String[] {"MockSep", "MockFixedIntBlock", "MockVariableIntBlock"};
private static void swapCodec(Codec c) {
final CodecProvider cp = CodecProvider.getDefault();
Codec prior = null;
try {
prior = cp.lookup(c.name);
} catch (IllegalArgumentException iae) {
}
if (prior != null) {
cp.unregister(prior);
}
cp.register(c);
}
// returns current default codec
static Codec installTestCodecs() {
final CodecProvider cp = CodecProvider.getDefault();
savedDefaultCodec = CodecProvider.getDefaultCodec();
String codec = TEST_CODEC;
final boolean codecHasParam;
int codecParam = 0;
if (codec.equals("random")) {
codec = pickRandomCodec(seedRnd);
codecHasParam = false;
} else {
Matcher m = codecWithParam.matcher(codec);
if (m.matches()) {
// codec has a fixed param
codecHasParam = true;
codec = m.group(1);
codecParam = Integer.parseInt(m.group(2));
} else {
codecHasParam = false;
}
}
CodecProvider.setDefaultCodec(codec);
if (codec.equals("PreFlex")) {
// If we're running w/ PreFlex codec we must swap in the
// test-only PreFlexRW codec (since core PreFlex can
// only read segments):
swapCodec(new PreFlexRWCodec());
}
swapCodec(new MockSepCodec());
swapCodec(new PulsingCodec(codecHasParam && "Pulsing".equals(codec) ? codecParam : _TestUtil.nextInt(seedRnd, 1, 20)));
swapCodec(new MockFixedIntBlockCodec(codecHasParam && "MockFixedIntBlock".equals(codec) ? codecParam : _TestUtil.nextInt(seedRnd, 1, 2000)));
// baseBlockSize cannot be over 127:
swapCodec(new MockVariableIntBlockCodec(codecHasParam && "MockVariableIntBlock".equals(codec) ? codecParam : _TestUtil.nextInt(seedRnd, 1, 127)));
return cp.lookup(codec);
}
// returns current PreFlex codec
static void removeTestCodecs(Codec codec) {
final CodecProvider cp = CodecProvider.getDefault();
if (codec.name.equals("PreFlex")) {
final Codec preFlex = cp.lookup("PreFlex");
if (preFlex != null) {
cp.unregister(preFlex);
}
cp.register(new PreFlexCodec());
}
cp.unregister(cp.lookup("MockSep"));
cp.unregister(cp.lookup("MockFixedIntBlock"));
cp.unregister(cp.lookup("MockVariableIntBlock"));
swapCodec(new PulsingCodec(1));
CodecProvider.setDefaultCodec(savedDefaultCodec);
}
// randomly picks from core and test codecs
static String pickRandomCodec(Random rnd) {
int idx = rnd.nextInt(CodecProvider.CORE_CODECS.length +
TEST_CODECS.length);
if (idx < CodecProvider.CORE_CODECS.length) {
return CodecProvider.CORE_CODECS[idx];
} else {
return TEST_CODECS[idx - CodecProvider.CORE_CODECS.length];
}
}
@BeforeClass
public static void beforeClassLuceneTestCaseJ4() {
stores = Collections.synchronizedMap(new IdentityHashMap<MockDirectoryWrapper,StackTraceElement[]>());
codec = installTestCodecs();
savedLocale = Locale.getDefault();
locale = TEST_LOCALE.equals("random") ? randomLocale(seedRnd) : localeForName(TEST_LOCALE);
Locale.setDefault(locale);
savedTimeZone = TimeZone.getDefault();
timeZone = TEST_TIMEZONE.equals("random") ? randomTimeZone(seedRnd) : TimeZone.getTimeZone(TEST_TIMEZONE);
TimeZone.setDefault(timeZone);
}
@AfterClass
public static void afterClassLuceneTestCaseJ4() {
removeTestCodecs(codec);
Locale.setDefault(savedLocale);
TimeZone.setDefault(savedTimeZone);
System.clearProperty("solr.solr.home");
System.clearProperty("solr.data.dir");
// now look for unclosed resources
for (MockDirectoryWrapper d : stores.keySet()) {
if (d.isOpen()) {
StackTraceElement elements[] = stores.get(d);
StackTraceElement element = (elements.length > 1) ? elements[1] : null;
fail("directory of test was not closed, opened from: " + element);
}
}
stores = null;
}
// 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) {
reportAdditionalFailureInfo();
super.failed(e, method);
}
@Override
public void starting(FrameworkMethod method) {
// set current method name for logging
LuceneTestCaseJ4.this.name = method.getName();
// check if the current test's class annotated all test* methods with @Test
final Class<? extends LuceneTestCaseJ4> clazz = LuceneTestCaseJ4.this.getClass();
if (!checkedClasses.containsKey(clazz)) {
checkedClasses.put(clazz, PLACEHOLDER);
for (Method m : clazz.getMethods()) {
if (m.getName().startsWith("test") && m.getAnnotation(Test.class) == null) {
fail("In class '" + clazz.getName() + "' the method '" + m.getName() + "' is not annotated with @Test.");
}
}
}
super.starting(method);
}
};
@Before
public void setUp() throws Exception {
Assert.assertFalse("ensure your tearDown() calls super.tearDown()!!!", setup);
setup = true;
savedUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread t, Throwable e) {
uncaughtExceptions.add(new UncaughtExceptionEntry(t, e));
if (savedUncaughtExceptionHandler != null)
savedUncaughtExceptionHandler.uncaughtException(t, e);
}
});
ConcurrentMergeScheduler.setTestMode();
savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
seed = null;
}
/**
* 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();
}
@After
public void tearDown() throws Exception {
Assert.assertTrue("ensure your setUp() calls super.setUp()!!!", setup);
setup = false;
BooleanQuery.setMaxClauseCount(savedBoolMaxClauseCount);
try {
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!");
}
// 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());
if (ConcurrentMergeScheduler.anyUnhandledExceptions()) {
// Clear the failure so that we don't just keep
// failing subsequent test cases
ConcurrentMergeScheduler.clearUnhandledExceptions();
fail("ConcurrentMergeScheduler hit unhandled exceptions");
}
} finally {
purgeFieldCache(FieldCache.DEFAULT);
}
Thread.setDefaultUncaughtExceptionHandler(savedUncaughtExceptionHandler);
}
/**
* 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 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);
}
}
}
/**
* Convinience 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 + " ***");
}
/**
* Convinience 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);
}
/**
* Returns a {@link Random} instance for generating random numbers during the test.
* The random seed is logged during test execution and printed to System.out on any failure
* for reproducing the test using {@link #newRandom(long)} with the recorded seed
* .
*/
public Random newRandom() {
if (seed != null) {
throw new IllegalStateException("please call LuceneTestCaseJ4.newRandom only once per test");
}
this.seed = Long.valueOf(seedRnd.nextLong());
if (VERBOSE) {
System.out.println("NOTE: random seed of testcase '" + getName() + "' is: " + this.seed);
}
return new Random(seed);
}
/**
* Returns a {@link Random} instance for generating random numbers during the test.
* If an error occurs in the test that is not reproducible, you can use this method to
* initialize the number generator with the seed that was printed out during the failing test.
*/
public Random newRandom(long seed) {
if (this.seed != null) {
throw new IllegalStateException("please call LuceneTestCaseJ4.newRandom only once per test");
}
System.out.println("WARNING: random seed of testcase '" + getName() + "' is fixed to: " + seed);
this.seed = Long.valueOf(seed);
return new Random(seed);
}
private static final Map<Class<? extends LuceneTestCaseJ4>,Long> staticSeeds =
Collections.synchronizedMap(new WeakHashMap<Class<? extends LuceneTestCaseJ4>,Long>());
/**
* Returns a {@link Random} instance for generating random numbers from a beforeclass
* annotated method.
* The random seed is logged during test execution and printed to System.out on any failure
* for reproducing the test using {@link #newStaticRandom(Class, long)} with the recorded seed
* .
*/
public static Random newStaticRandom(Class<? extends LuceneTestCaseJ4> clazz) {
Long seed = seedRnd.nextLong();
staticSeeds.put(clazz, seed);
return new Random(seed);
}
/**
* Returns a {@link Random} instance for generating random numbers from a beforeclass
* annotated method.
* If an error occurs in the test that is not reproducible, you can use this method to
* initialize the number generator with the seed that was printed out during the failing test.
*/
public static Random newStaticRandom(Class<? extends LuceneTestCaseJ4> clazz, long seed) {
staticSeeds.put(clazz, Long.valueOf(seed));
System.out.println("WARNING: random static seed of testclass '" + clazz + "' is fixed to: " + seed);
return new Random(seed);
}
/** create a new index writer config with random defaults */
public static IndexWriterConfig newIndexWriterConfig(Random r, Version v, Analyzer a) {
IndexWriterConfig c = new IndexWriterConfig(v, a);
if (r.nextBoolean()) {
c.setMergePolicy(new LogDocMergePolicy());
}
if (r.nextBoolean()) {
c.setMergeScheduler(new SerialMergeScheduler());
}
if (r.nextBoolean()) {
c.setMaxBufferedDocs(_TestUtil.nextInt(r, 2, 1000));
}
if (r.nextBoolean()) {
c.setTermIndexInterval(_TestUtil.nextInt(r, 1, 1000));
}
if (r.nextBoolean()) {
c.setMaxThreadStates(_TestUtil.nextInt(r, 1, 20));
}
if (c.getMergePolicy() instanceof LogMergePolicy) {
LogMergePolicy logmp = (LogMergePolicy) c.getMergePolicy();
logmp.setUseCompoundDocStore(r.nextBoolean());
logmp.setUseCompoundFile(r.nextBoolean());
logmp.setCalibrateSizeByDeletes(r.nextBoolean());
logmp.setMergeFactor(_TestUtil.nextInt(r, 2, 20));
}
c.setReaderPooling(r.nextBoolean());
c.setReaderTermsIndexDivisor(_TestUtil.nextInt(r, 1, 4));
return c;
}
/**
* Returns a new Dictionary 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(Random r) throws IOException {
StackTraceElement[] stack = new Exception().getStackTrace();
Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
MockDirectoryWrapper dir = new MockDirectoryWrapper(impl);
stores.put(dir, stack);
return dir;
}
/**
* Returns a new Dictionary instance, with contents copied from the
* provided directory. See {@link #newDirectory(Random)} for more
* information.
*/
public static MockDirectoryWrapper newDirectory(Random r, Directory d) throws IOException {
StackTraceElement[] stack = new Exception().getStackTrace();
Directory impl = newDirectoryImpl(r, TEST_DIRECTORY);
for (String file : d.listAll()) {
d.copy(impl, file, file);
}
MockDirectoryWrapper dir = new MockDirectoryWrapper(impl);
stores.put(dir, stack);
return dir;
}
/** 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 String CORE_DIRECTORIES[] = {
"RAMDirectory",
"SimpleFSDirectory",
"NIOFSDirectory",
"MMapDirectory"
};
public static String randomDirectory(Random random) {
if (random.nextInt(10) == 0) {
return CORE_DIRECTORIES[random.nextInt(CORE_DIRECTORIES.length)];
} else {
return "RAMDirectory";
}
}
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);
try {
// try empty ctor
return clazz.newInstance();
} catch (Exception e) {
final File tmpFile = File.createTempFile("test", "tmp", TEMP_DIR);
tmpFile.delete();
tmpFile.mkdir();
try {
Constructor<? extends Directory> ctor = clazz.getConstructor(File.class);
Directory d = ctor.newInstance(tmpFile);
// try not to enable this hack unless we must.
if (d instanceof MMapDirectory && Constants.WINDOWS && MMapDirectory.UNMAP_SUPPORTED)
((MMapDirectory)d).setUseUnmap(true);
return d;
} catch (Exception e2) {
// try .open(File)
Method method = clazz.getMethod("open", new Class[] { File.class });
return (Directory) method.invoke(null, tmpFile);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
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 void reportAdditionalFailureInfo() {
Long staticSeed = staticSeeds.get(getClass());
if (staticSeed != null) {
System.out.println("NOTE: random static seed of testclass '" + getName() + "' was: " + staticSeed);
}
System.out.println("NOTE: random codec of testcase '" + getName() + "' was: " + codec);
if (TEST_LOCALE.equals("random"))
System.out.println("NOTE: random locale of testcase '" + getName() + "' was: " + locale);
if (TEST_TIMEZONE.equals("random")) // careful to not deliver NPE here in case they forgot super.setUp
System.out.println("NOTE: random timezone of testcase '" + getName() + "' was: " + (timeZone == null ? "(null)" : timeZone.getID()));
if (seed != null) {
System.out.println("NOTE: random seed of testcase '" + getName() + "' was: " + seed);
}
}
// recorded seed
protected Long seed = null;
// static members
private static final Random seedRnd = new Random();
private String name = "<unknown>";
/** optionally filters the tests to be run by TEST_METHOD */
public static class LuceneTestCaseRunner extends BlockJUnit4ClassRunner {
@Override
protected void runChild(FrameworkMethod arg0, RunNotifier arg1) {
for (int i = 0; i < TEST_ITER; i++)
super.runChild(arg0, arg1);
}
public LuceneTestCaseRunner(Class<?> clazz) throws InitializationError {
super(clazz);
Filter f = new Filter() {
@Override
public String describe() { return "filters according to TEST_METHOD"; }
@Override
public boolean shouldRun(Description d) {
return TEST_METHOD == null || d.getMethodName().equals(TEST_METHOD);
}
};
try {
f.apply(this);
} catch (NoTestsRemainException e) {
throw new RuntimeException(e);
}
}
}
}