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.
+    }    
+  }
+}