You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dw...@apache.org on 2012/04/15 16:42:01 UTC
svn commit: r1326351 [14/22] - in /lucene/dev/trunk: ./ dev-tools/eclipse/
lucene/
lucene/contrib/highlighter/src/test/org/apache/lucene/search/highlight/
lucene/contrib/highlighter/src/test/org/apache/lucene/search/highlight/custom/
lucene/contrib/hig...
Modified: lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java?rev=1326351&r1=1326350&r2=1326351&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java (original)
+++ lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java Sun Apr 15 14:41:44 2012
@@ -63,12 +63,12 @@ import org.apache.lucene.index.LogByteSi
import org.apache.lucene.index.LogDocMergePolicy;
import org.apache.lucene.index.LogMergePolicy;
import org.apache.lucene.index.MockRandomMergePolicy;
+import org.apache.lucene.index.ParallelAtomicReader;
+import org.apache.lucene.index.ParallelCompositeReader;
import org.apache.lucene.index.RandomCodec;
import org.apache.lucene.index.RandomDocumentsWriterPerThreadPool;
import org.apache.lucene.index.SegmentReader;
import org.apache.lucene.index.SerialMergeScheduler;
-import org.apache.lucene.index.ParallelAtomicReader;
-import org.apache.lucene.index.ParallelCompositeReader;
import org.apache.lucene.index.SlowCompositeReaderWrapper;
import org.apache.lucene.index.ThreadAffinityDocumentsWriterThreadPool;
import org.apache.lucene.index.TieredMergePolicy;
@@ -98,15 +98,22 @@ import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.internal.AssumptionViolatedException;
-import org.junit.rules.*;
-import org.junit.runner.*;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runner.Runner;
import org.junit.runner.notification.RunListener;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
+import com.carrotsearch.randomizedtesting.JUnit4MethodProvider;
+import com.carrotsearch.randomizedtesting.RandomizedContext;
+import com.carrotsearch.randomizedtesting.RandomizedRunner;
+import com.carrotsearch.randomizedtesting.annotations.*;
+
/**
* Base class for all Lucene unit tests, Junit3 or Junit4 variant.
* <p>
@@ -133,15 +140,23 @@ import org.junit.runners.model.Statement
* if you annotate your derived class correctly with the annotations above
* @see #assertSaneFieldCaches(String)
*/
-
-@RunWith(LuceneTestCaseRunner.class)
+@TestMethodProviders({
+ LuceneJUnit3MethodProvider.class,
+ JUnit4MethodProvider.class
+})
+@Validators({
+ RequireAssertions.class,
+ NoStaticHooksShadowing.class
+})
+@RunWith(RandomizedRunner.class)
+@ThreadLeaks(failTestIfLeaking = false)
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");
-
+
public static final boolean INFOSTREAM = Boolean.parseBoolean(System.getProperty("tests.infostream", Boolean.toString(VERBOSE)));
/** Use this constant when creating Analyzers and any other version-dependent stuff.
@@ -149,23 +164,16 @@ public abstract class LuceneTestCase ext
*/
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);
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[]>());
@@ -186,14 +194,8 @@ public abstract class LuceneTestCase ext
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"));
- /** whether or not @weekly tests should run */
- public static final boolean TEST_WEEKLY = Boolean.parseBoolean(System.getProperty("tests.weekly", "false"));
- /** whether or not @slow tests should run */
- public static final boolean TEST_SLOW = Boolean.parseBoolean(System.getProperty("tests.slow", "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" */
@@ -255,11 +257,28 @@ public abstract class LuceneTestCase ext
private 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;
+ /**
+ * Access to the current {@link RandomizedContext}'s Random instance. It is safe to use
+ * this method from multiple threads, etc., but it should be called while within a runner's
+ * scope (so no static initializers). The returned {@link Random} instance will be
+ * <b>different</b> when this method is called inside a {@link BeforeClass} hook (static
+ * suite scope) and within {@link Before}/ {@link After} hooks or test methods.
+ *
+ * <p>The returned instance must not be shared with other threads or cross a single scope's
+ * boundary. For example, a {@link Random} acquired within a test method shouldn't be reused
+ * for another test case.
+ *
+ * <p>There is an overhead connected with getting the {@link Random} for a particular context
+ * and thread. It is better to cache the {@link Random} locally if tight loops with multiple
+ * invocations are present or create a derivative local {@link Random} for millions of calls
+ * like this:
+ * <pre>
+ * Random random = random();
+ * // tight loop with many invocations.
+ * </pre>
+ */
+ public static Random random() {
+ return RandomizedContext.current().getRandom();
}
@Deprecated
@@ -312,10 +331,9 @@ public abstract class LuceneTestCase ext
.around(new SubclassSetupTeardownRule());
@BeforeClass
- public static void beforeClassLuceneTestCaseJ4() {
+ static void beforeClassLuceneTestCaseJ4() {
testClassesRun.add(getTestClass().getSimpleName());
- initRandom();
tempDirs.clear();
stores = Collections.synchronizedMap(new IdentityHashMap<MockDirectoryWrapper,StackTraceElement[]>());
@@ -365,22 +383,29 @@ public abstract class LuceneTestCase ext
System.out.println("Loaded postingsFormat: '" + postingsFormat + "': " + PostingsFormat.forName(postingsFormat).getClass().getName());
}
}
-
+
savedInfoStream = InfoStream.getDefault();
+ final boolean v = random().nextBoolean();
if (INFOSTREAM) {
- // consume random for consistency
- random.nextBoolean();
InfoStream.setDefault(new PrintStreamInfoStream(System.out));
} else {
- if (random.nextBoolean()) {
+ if (v) {
InfoStream.setDefault(new NullInfoStream());
}
}
+ Class<?> targetClass = RandomizedContext.current().getTargetClass();
+ LuceneTestCase.useNoMemoryExpensiveCodec =
+ targetClass.isAnnotationPresent(UseNoMemoryExpensiveCodec.class);
+ if (useNoMemoryExpensiveCodec) {
+ System.err.println("NOTE: Using no memory expensive codecs (Memory, SimpleText) for " +
+ targetClass.getSimpleName() + ".");
+ }
+
PREFLEX_IMPERSONATION_IS_ACTIVE = false;
savedCodec = Codec.getDefault();
final Codec codec;
- int randomVal = random.nextInt(10);
+ int randomVal = random().nextInt(10);
if ("Lucene3x".equals(TEST_CODEC) || ("random".equals(TEST_CODEC) && randomVal < 2)) { // preflex-only setup
codec = Codec.forName("Lucene3x");
@@ -393,7 +418,7 @@ public abstract class LuceneTestCase ext
} else if (!"random".equals(TEST_CODEC)) {
codec = Codec.forName(TEST_CODEC);
} else if ("random".equals(TEST_POSTINGSFORMAT)) {
- codec = new RandomCodec(random, useNoMemoryExpensiveCodec);
+ codec = new RandomCodec(random(), useNoMemoryExpensiveCodec);
} else {
codec = new Lucene40Codec() {
private final PostingsFormat format = PostingsFormat.forName(TEST_POSTINGSFORMAT);
@@ -428,20 +453,20 @@ public abstract class LuceneTestCase ext
}
// END hack
- locale = TEST_LOCALE.equals("random") ? randomLocale(random) : localeForName(TEST_LOCALE);
+ locale = TEST_LOCALE.equals("random") ? randomLocale(random()) : localeForName(TEST_LOCALE);
Locale.setDefault(locale);
// TimeZone.getDefault will set user.timezone to the default timezone of the user's locale.
// So store the original property value and restore it at end.
restoreProperties.put("user.timezone", System.getProperty("user.timezone"));
savedTimeZone = TimeZone.getDefault();
- timeZone = TEST_TIMEZONE.equals("random") ? randomTimeZone(random) : TimeZone.getTimeZone(TEST_TIMEZONE);
+ timeZone = TEST_TIMEZONE.equals("random") ? randomTimeZone(random()) : TimeZone.getTimeZone(TEST_TIMEZONE);
TimeZone.setDefault(timeZone);
- similarity = random.nextBoolean() ? new DefaultSimilarity() : new RandomSimilarityProvider(random);
+ similarity = random().nextBoolean() ? new DefaultSimilarity() : new RandomSimilarityProvider(random());
testsFailed = false;
}
@AfterClass
- public static void afterClassLuceneTestCaseJ4() {
+ static void afterClassLuceneTestCaseJ4() {
for (Map.Entry<String,String> e : restoreProperties.entrySet()) {
if (e.getValue() == null) {
System.clearProperty(e.getKey());
@@ -452,8 +477,8 @@ public abstract class LuceneTestCase ext
restoreProperties.clear();
Throwable problem = null;
-
- if (! "false".equals(TEST_CLEAN_THREADS)) {
+
+ if (!"false".equals(TEST_CLEAN_THREADS)) {
int rogueThreads = threadCleanup("test class");
if (rogueThreads > 0) {
// TODO: fail here once the leaks are fixed.
@@ -502,16 +527,12 @@ public abstract class LuceneTestCase ext
if (VERBOSE || testsFailed || problem != null) {
printDebuggingInformation(codecDescription);
}
-
- // 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(String codecDescription) {
System.err.println("NOTE: test params are: codec=" + codecDescription +
@@ -580,6 +601,7 @@ public abstract class LuceneTestCase ext
* Control the outcome of each test's output status (failure, assumption-failure). This
* would ideally be handled by attaching a {@link RunListener} to a {@link Runner} (because
* then we would be notified about static block failures).
+ * TODO: make this a test listener.
*/
private class TestResultInterceptorRule implements TestRule {
@Override
@@ -589,9 +611,8 @@ public abstract class LuceneTestCase ext
public void evaluate() throws Throwable {
try {
base.evaluate();
- } catch (AssumptionViolatedException e) {
- assumptionIgnored(e, description);
- throw e;
+ } catch (AssumptionViolatedException t) {
+ throw t;
} catch (Throwable t) {
failed(t, description);
throw t;
@@ -600,17 +621,6 @@ public abstract class LuceneTestCase ext
};
}
- private void assumptionIgnored(AssumptionViolatedException e, Description description) {
- System.err.print("NOTE: Assume failed in '" + description.getDisplayName() + "' (ignored):");
- if (VERBOSE) {
- System.err.println();
- e.printStackTrace(System.err);
- } else {
- System.err.print(" ");
- System.err.println(e.getMessage());
- }
- }
-
private void failed(Throwable e, Description description) {
testsFailed = true;
reportAdditionalFailureInfo();
@@ -677,33 +687,27 @@ public abstract class LuceneTestCase ext
} catch (Throwable t) {
errors.add(t);
}
-
+
MultipleFailureException.assertEmpty(errors);
}
};
}
}
-
+
/**
* Setup before the tests.
*/
private final void setUpInternal() throws Exception {
- seed = "random".equals(TEST_SEED) ? seedRand.nextLong() : ThreeLongs.fromString(TEST_SEED).l2;
- random.setSeed(seed);
-
Thread.currentThread().setName("LTC-main#seed=" +
- new ThreeLongs(staticSeed, seed, LuceneTestCaseRunner.runnerSeed));
+ RandomizedContext.current().getRunnerSeedAsString());
savedBoolMaxClauseCount = BooleanQuery.getMaxClauseCount();
if (useNoMemoryExpensiveCodec) {
String defFormat = _TestUtil.getPostingsFormat("thisCodeMakesAbsolutelyNoSenseCanWeDeleteIt");
- // Stupid: assumeFalse in setUp() does not print any information, because
- // TestWatchman does not watch test during setUp() - getName() is also not defined...
- // => print info directly and use assume without message:
if ("SimpleText".equals(defFormat) || "Memory".equals(defFormat)) {
- System.err.println("NOTE: A test method in " + getClass().getSimpleName() + " was ignored, as it uses too much memory with " + defFormat + ".");
- Assume.assumeTrue(false);
+ assumeTrue("NOTE: A test method in " + getClass().getSimpleName()
+ + " was ignored, as it uses too much memory with " + defFormat + ".", false);
}
}
}
@@ -954,7 +958,7 @@ public abstract class LuceneTestCase ext
}
public static int atLeast(int i) {
- return atLeast(random, i);
+ return atLeast(random(), i);
}
/**
@@ -971,7 +975,7 @@ public abstract class LuceneTestCase ext
}
public static boolean rarely() {
- return rarely(random);
+ return rarely(random());
}
public static boolean usually(Random random) {
@@ -979,7 +983,7 @@ public abstract class LuceneTestCase ext
}
public static boolean usually() {
- return usually(random);
+ return usually(random());
}
public static void assumeTrue(String msg, boolean b) {
@@ -1031,7 +1035,7 @@ public abstract class LuceneTestCase ext
/** create a new index writer config with random defaults */
public static IndexWriterConfig newIndexWriterConfig(Version v, Analyzer a) {
- return newIndexWriterConfig(random, v, a);
+ return newIndexWriterConfig(random(), v, a);
}
/** create a new index writer config with random defaults using the specified random */
@@ -1085,11 +1089,11 @@ public abstract class LuceneTestCase ext
}
public static LogMergePolicy newLogMergePolicy() {
- return newLogMergePolicy(random);
+ return newLogMergePolicy(random());
}
public static TieredMergePolicy newTieredMergePolicy() {
- return newTieredMergePolicy(random);
+ return newTieredMergePolicy(random());
}
public static LogMergePolicy newLogMergePolicy(Random r) {
@@ -1161,7 +1165,7 @@ public abstract class LuceneTestCase ext
* overwritten.
*/
public static MockDirectoryWrapper newDirectory() throws IOException {
- return newDirectory(random);
+ return newDirectory(random());
}
/**
@@ -1185,7 +1189,7 @@ public abstract class LuceneTestCase ext
* information.
*/
public static MockDirectoryWrapper newDirectory(Directory d) throws IOException {
- return newDirectory(random, d);
+ return newDirectory(random(), d);
}
/** Returns a new FSDirectory instance over the given file, which must be a folder. */
@@ -1197,7 +1201,7 @@ public abstract class LuceneTestCase ext
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)];
+ fsdirClass = FS_DIRECTORIES[random().nextInt(FS_DIRECTORIES.length)];
}
Class<? extends FSDirectory> clazz;
@@ -1206,12 +1210,13 @@ public abstract class LuceneTestCase ext
clazz = CommandLineUtil.loadFSDirectoryClass(fsdirClass);
} 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)];
+ fsdirClass = FS_DIRECTORIES[random().nextInt(FS_DIRECTORIES.length)];
clazz = CommandLineUtil.loadFSDirectoryClass(fsdirClass);
}
Directory fsdir = newFSDirectoryImpl(clazz, f);
- MockDirectoryWrapper dir = new MockDirectoryWrapper(random, maybeNRTWrap(random, fsdir));
+ MockDirectoryWrapper dir = new MockDirectoryWrapper(
+ random(), maybeNRTWrap(random(), fsdir));
if (lf != null) {
dir.setLockFactory(lf);
}
@@ -1248,7 +1253,7 @@ public abstract class LuceneTestCase ext
}
public static Field newField(String name, String value, FieldType type) {
- return newField(random, name, value, type);
+ return newField(random(), name, value, type);
}
public static Field newField(Random random, String name, String value, FieldType type) {
@@ -1375,6 +1380,7 @@ public abstract class LuceneTestCase ext
/** Sometimes wrap the IndexReader as slow, parallel or filter reader (or combinations of that) */
public static IndexReader maybeWrapReader(IndexReader r) throws IOException {
+ Random random = random();
if (rarely()) {
// TODO: remove this, and fix those tests to wrap before putting slow around:
final boolean wasOriginallyAtomic = r instanceof AtomicReader;
@@ -1439,6 +1445,7 @@ public abstract class LuceneTestCase ext
* with one that returns null for getSequentialSubReaders.
*/
public static IndexSearcher newSearcher(IndexReader r, boolean maybeWrap) throws IOException {
+ Random random = random();
if (usually()) {
if (maybeWrap) {
r = maybeWrapReader(r);
@@ -1477,7 +1484,7 @@ public abstract class LuceneTestCase ext
return ret;
}
}
-
+
static void shutdownExecutorService(ExecutorService ex) {
if (ex != null) {
ex.shutdown();
@@ -1508,21 +1515,22 @@ public abstract class LuceneTestCase ext
// 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());
+ System.err.println("NOTE: reproduce with (hopefully): ant test " +
+ "-Dtests.class=*." + getTestClass().getSimpleName() +
+ " -Dtests.seed=" + RandomizedContext.current().getRunnerSeedAsString() +
+ reproduceWithExtraParams());
}
-
+
// We get here from InterceptTestCaseEvents on the 'failed' event....
public void reportAdditionalFailureInfo() {
StringBuilder b = new StringBuilder();
- b.append("NOTE: reproduce with: ant test -Dtestcase=")
- .append(getClass().getSimpleName());
+ b.append("NOTE: reproduce with: ant test ")
+ .append("-Dtests.class=*.").append(getTestClass().getSimpleName());
if (getName() != null) {
- b.append(" -Dtestmethod=").append(getName());
+ b.append(" -Dtests.method=").append(getName());
}
b.append(" -Dtests.seed=")
- .append(new ThreeLongs(staticSeed, seed, LuceneTestCaseRunner.runnerSeed))
+ .append(RandomizedContext.current().getRunnerSeedAsString())
.append(reproduceWithExtraParams());
System.err.println(b.toString());
}
@@ -1578,14 +1586,6 @@ public abstract class LuceneTestCase ext
// initialized by the TestRunner
static boolean useNoMemoryExpensiveCodec;
-
- // 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>";
@@ -1595,6 +1595,7 @@ public abstract class LuceneTestCase ext
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
+ @TestGroup(enabled = false, sysProperty = "tests.nightly")
public @interface Nightly {}
/**
@@ -1603,14 +1604,28 @@ public abstract class LuceneTestCase ext
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
+ @TestGroup(enabled = false, sysProperty = "tests.weekly")
public @interface Weekly{}
/**
+ * Annotation for tests which exhibit a known issue and are temporarily disabled.
+ */
+ @Documented
+ @Inherited
+ @Retention(RetentionPolicy.RUNTIME)
+ @TestGroup(enabled = false, sysProperty = "tests.awaitsfix")
+ public @interface AwaitsFix {
+ /** Point to JIRA entry. */
+ public String bugUrl();
+ }
+
+ /**
* Annotation for tests that are slow and should be run only when specifically asked to run
*/
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
+ @TestGroup(enabled = false, sysProperty = "tests.slow")
public @interface Slow{}
/**
@@ -1622,9 +1637,6 @@ public abstract class LuceneTestCase ext
@Target(ElementType.TYPE)
public @interface UseNoMemoryExpensiveCodec {}
- @Ignore("just a hack")
- public final void alwaysIgnoredTestMethod() {}
-
protected static boolean defaultCodecSupportsDocValues() {
return !Codec.getDefault().getName().equals("Lucene3x");
}
Added: lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/NoStaticHooksShadowing.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/NoStaticHooksShadowing.java?rev=1326351&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/NoStaticHooksShadowing.java (added)
+++ lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/NoStaticHooksShadowing.java Sun Apr 15 14:41:44 2012
@@ -0,0 +1,58 @@
+package org.apache.lucene.util;
+
+import static com.carrotsearch.randomizedtesting.MethodCollector.allDeclaredMethods;
+import static com.carrotsearch.randomizedtesting.MethodCollector.annotatedWith;
+import static com.carrotsearch.randomizedtesting.MethodCollector.flatten;
+import static com.carrotsearch.randomizedtesting.MethodCollector.removeShadowed;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import com.carrotsearch.randomizedtesting.ClassValidator;
+
+public class NoStaticHooksShadowing implements ClassValidator {
+ @Override
+ public void validate(Class<?> clazz) throws Throwable {
+ List<List<Method>> all = allDeclaredMethods(clazz);
+
+ checkNoShadows(clazz, all, BeforeClass.class);
+ checkNoShadows(clazz, all, AfterClass.class);
+ }
+
+ private void checkNoShadows(Class<?> clazz, List<List<Method>> all, Class<? extends Annotation> ann) {
+ List<List<Method>> methodHierarchy = annotatedWith(all, ann);
+ List<List<Method>> noShadows = removeShadowed(methodHierarchy);
+ if (!noShadows.equals(methodHierarchy)) {
+ Set<Method> shadowed = new HashSet<Method>(flatten(methodHierarchy));
+ shadowed.removeAll(flatten(noShadows));
+
+ StringBuilder b = new StringBuilder();
+ for (Method m : shadowed) {
+ String sig = signature(m);
+ for (Method other : flatten(methodHierarchy)) {
+ if (other != m && sig.equals(signature(other))) {
+ b.append("Method: " + m.toString()
+ + "#" + sig + " possibly shadowed by " +
+ other.toString() + "#" + signature(other) + "\n");
+ }
+ }
+ }
+
+ throw new RuntimeException("There are shadowed methods annotated with "
+ + ann.getName() + ". These methods would not be executed by JUnit and need to manually chain themselves which can lead to" +
+ " maintenance problems.\n" + b.toString().trim());
+ }
+ }
+
+ private String signature(Method m) {
+ return m.getName() + Arrays.toString(m.getParameterTypes());
+ }
+}
+
Added: lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RandomNoSetSeed.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RandomNoSetSeed.java?rev=1326351&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RandomNoSetSeed.java (added)
+++ lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RandomNoSetSeed.java Sun Apr 15 14:41:44 2012
@@ -0,0 +1,123 @@
+package org.apache.lucene.util;
+
+import java.util.Random;
+
+/**
+ * A random with a delegate, preventing calls to {@link Random#setSeed(long)} and
+ * permitting end-of-lifecycle markers.
+ */
+@SuppressWarnings("serial")
+final class RandomNoSetSeed extends Random {
+ private final Random delegate;
+
+ /**
+ * If <code>false</code>, the object is dead. Any calls to any method will result
+ * in an exception.
+ */
+ private volatile boolean alive = true;
+
+ void setDead() {
+ alive = false;
+ }
+
+ public RandomNoSetSeed(Random delegate) {
+ super(0);
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected int next(int bits) {
+ throw new RuntimeException("Shouldn't be reachable.");
+ }
+
+ @Override
+ public boolean nextBoolean() {
+ checkAlive();
+ return delegate.nextBoolean();
+ }
+
+ @Override
+ public void nextBytes(byte[] bytes) {
+ checkAlive();
+ delegate.nextBytes(bytes);
+ }
+
+ @Override
+ public double nextDouble() {
+ checkAlive();
+ return delegate.nextDouble();
+ }
+
+ @Override
+ public float nextFloat() {
+ checkAlive();
+ return delegate.nextFloat();
+ }
+
+ @Override
+ public double nextGaussian() {
+ checkAlive();
+ return delegate.nextGaussian();
+ }
+
+ @Override
+ public int nextInt() {
+ checkAlive();
+ return delegate.nextInt();
+ }
+
+ @Override
+ public int nextInt(int n) {
+ checkAlive();
+ return delegate.nextInt(n);
+ }
+
+ @Override
+ public long nextLong() {
+ checkAlive();
+ return delegate.nextLong();
+ }
+
+ @Override
+ public void setSeed(long seed) {
+ // This is an interesting case of observing uninitialized object from an instance method
+ // (this method is called from the superclass constructor). We allow it.
+ if (seed == 0 && delegate == null) {
+ return;
+ }
+
+ throw new RuntimeException(
+ RandomNoSetSeed.class.getSimpleName() +
+ " prevents changing the seed of its random generators to assure repeatability" +
+ " of tests. If you need a mutable instance of Random, create a new instance," +
+ " preferably with the initial seed aquired from this Random instance.");
+ }
+
+ @Override
+ public String toString() {
+ checkAlive();
+ return delegate.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ checkAlive();
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ checkAlive();
+ return delegate.hashCode();
+ }
+
+ /**
+ * Check the liveness status.
+ */
+ private void checkAlive() {
+ if (!alive) {
+ throw new RuntimeException("This Random is dead. Do not store references to " +
+ "Random instances, acquire an instance when you need one.");
+ }
+ }
+}
Added: lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RequireAssertions.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RequireAssertions.java?rev=1326351&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RequireAssertions.java (added)
+++ lucene/dev/trunk/lucene/test-framework/src/java/org/apache/lucene/util/RequireAssertions.java Sun Apr 15 14:41:44 2012
@@ -0,0 +1,18 @@
+package org.apache.lucene.util;
+
+import com.carrotsearch.randomizedtesting.ClassValidator;
+
+/**
+ * Require assertions for Lucene/Solr packages.
+ */
+public class RequireAssertions implements ClassValidator {
+ @Override
+ public void validate(Class<?> clazz) throws Throwable {
+ try {
+ assert false;
+ throw new RuntimeException("Enable assertions globally (-ea) or for Solr/Lucene subpackages only.");
+ } catch (AssertionError e) {
+ // Ok, enabled.
+ }
+ }
+}