You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2016/08/09 21:52:40 UTC

[2/2] lucene-solr:branch_6x: SOLR-9367: Improved TestInjection's randomization logic to use LuceneTestCase.random()

SOLR-9367: Improved TestInjection's randomization logic to use LuceneTestCase.random()

(cherry picked from commit 84a8c098fca2018ca8c6abde13ae0d56f081dae1)


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/8ad1a830
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/8ad1a830
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/8ad1a830

Branch: refs/heads/branch_6x
Commit: 8ad1a830fbe08e37eac50cf875edc9b889363524
Parents: ae80f3e
Author: Chris Hostetter <ho...@apache.org>
Authored: Tue Aug 9 14:46:51 2016 -0700
Committer: Chris Hostetter <ho...@apache.org>
Committed: Tue Aug 9 14:48:37 2016 -0700

----------------------------------------------------------------------
 solr/CHANGES.txt                                |  2 +
 .../org/apache/solr/util/TestInjection.java     | 95 +++++++++++++++-----
 .../org/apache/solr/util/TestTestInjection.java |  4 +
 3 files changed, 81 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8ad1a830/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 32db560..6e9868c 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -213,6 +213,8 @@ Other Changes
 
 * SOLR-9385: Add QParser.getParser(String,SolrQueryRequest) variant. (Christine Poerschke)
 
+* SOLR-9367: Improved TestInjection's randomization logic to use LuceneTestCase.random() (hossman)
+
 ==================  6.1.0 ==================
 
 Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8ad1a830/solr/core/src/java/org/apache/solr/util/TestInjection.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/TestInjection.java b/solr/core/src/java/org/apache/solr/util/TestInjection.java
index cc3f85d..03de74d 100644
--- a/solr/core/src/java/org/apache/solr/util/TestInjection.java
+++ b/solr/core/src/java/org/apache/solr/util/TestInjection.java
@@ -17,6 +17,7 @@
 package org.apache.solr.util;
 
 import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Random;
@@ -39,6 +40,11 @@ import org.slf4j.LoggerFactory;
  * Allows random faults to be injected in running code during test runs.
  * 
  * Set static strings to "true" or "false" or "true:60" for true 60% of the time.
+ * 
+ * All methods are No-Ops unless <code>LuceneTestCase</code> is loadable via the ClassLoader used 
+ * to load this class.  <code>LuceneTestCase.random()</code> is used as the source of all entropy.
+ * 
+ * @lucene.internal
  */
 public class TestInjection {
   
@@ -53,16 +59,42 @@ public class TestInjection {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   
   private static final Pattern ENABLED_PERCENT = Pattern.compile("(true|false)(?:\\:(\\d+))?$", Pattern.CASE_INSENSITIVE);
-  private static final Random RANDOM;
+
+  private static final String LUCENE_TEST_CASE_FQN = "org.apache.lucene.util.LuceneTestCase";
+
+  /** 
+   * If null, then we are not being run as part of a test, and all TestInjection events should be No-Ops.
+   * If non-null, then this class should be used for accessing random entropy
+   * @see #random
+   */
+  private static final Class LUCENE_TEST_CASE;
   
   static {
-    // We try to make things reproducible in the context of our tests by initializing the random instance
-    // based on the current seed
-    String seed = System.getProperty("tests.seed");
-    if (seed == null) {
-      RANDOM = new Random();
+    Class nonFinalTemp = null;
+    try {
+      ClassLoader classLoader = MethodHandles.lookup().lookupClass().getClassLoader();
+      nonFinalTemp = classLoader.loadClass(LUCENE_TEST_CASE_FQN);
+    } catch (ClassNotFoundException e) {
+      log.debug("TestInjection methods will all be No-Ops since LuceneTestCase not found");
+    }
+    LUCENE_TEST_CASE = nonFinalTemp;
+  }
+
+  /**
+   * Returns a random to be used by the current thread if available, otherwise
+   * returns null.
+   * @see #LUCENE_TEST_CASE
+   */
+  static Random random() { // non-private for testing
+    if (null == LUCENE_TEST_CASE) {
+      return null;
     } else {
-      RANDOM = new Random(seed.hashCode());
+      try {
+        Method randomMethod = LUCENE_TEST_CASE.getMethod("random");
+        return (Random) randomMethod.invoke(null);
+      } catch (Exception e) {
+        throw new IllegalStateException("Unable to use reflection to invoke LuceneTestCase.random()", e);
+      }
     }
   }
   
@@ -100,11 +132,14 @@ public class TestInjection {
   
   public static boolean injectRandomDelayInCoreCreation() {
     if (randomDelayInCoreCreation != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(randomDelayInCoreCreation);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
-        int delay = RANDOM.nextInt(randomDelayMaxInCoreCreationInSec);
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
+        int delay = rand.nextInt(randomDelayMaxInCoreCreationInSec);
         log.info("Inject random core creation delay of {}s", delay);
         try {
           Thread.sleep(delay * 1000);
@@ -118,11 +153,14 @@ public class TestInjection {
   
   public static boolean injectNonGracefullClose(CoreContainer cc) {
     if (cc.isShutDown() && nonGracefullClose != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(nonGracefullClose);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
-        if (RANDOM.nextBoolean()) {
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
+        if (rand.nextBoolean()) {
           throw new TestShutdownFailError("Test exception for non graceful close");
         } else {
           
@@ -135,7 +173,9 @@ public class TestInjection {
               // we should only need to do it once
               
               try {
-                Thread.sleep(RANDOM.nextInt(1000));
+                // call random() again to get the correct one for this thread
+                Random taskRand = random();
+                Thread.sleep(taskRand.nextInt(1000));
               } catch (InterruptedException e) {
               
               }
@@ -147,7 +187,7 @@ public class TestInjection {
           };
           Timer timer = new Timer();
           timers.add(timer);
-          timer.schedule(task, RANDOM.nextInt(500));
+          timer.schedule(task, rand.nextInt(500));
         }
       }
     }
@@ -156,10 +196,13 @@ public class TestInjection {
 
   public static boolean injectFailReplicaRequests() {
     if (failReplicaRequests != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(failReplicaRequests);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
         throw new SolrException(ErrorCode.SERVER_ERROR, "Random test update fail");
       }
     }
@@ -169,10 +212,13 @@ public class TestInjection {
   
   public static boolean injectFailUpdateRequests() {
     if (failUpdateRequests != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(failUpdateRequests);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
         throw new SolrException(ErrorCode.SERVER_ERROR, "Random test update fail");
       }
     }
@@ -182,10 +228,13 @@ public class TestInjection {
   
   public static boolean injectNonExistentCoreExceptionAfterUnload(String cname) {
     if (nonExistentCoreExceptionAfterUnload != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(nonExistentCoreExceptionAfterUnload);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
         throw new NonExistentCoreException("Core not found to unload: " + cname);
       }
     }
@@ -195,11 +244,14 @@ public class TestInjection {
   
   public static boolean injectUpdateLogReplayRandomPause() {
     if (updateLogReplayRandomPause != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(updateLogReplayRandomPause);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
-        long rndTime = RANDOM.nextInt(1000);
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
+        long rndTime = rand.nextInt(1000);
         log.info("inject random log replay delay of {}ms", rndTime);
         try {
           Thread.sleep(rndTime);
@@ -214,11 +266,14 @@ public class TestInjection {
   
   public static boolean injectUpdateRandomPause() {
     if (updateRandomPause != null) {
+      Random rand = random();
+      if (null == rand) return true;
+      
       Pair<Boolean,Integer> pair = parseValue(updateRandomPause);
       boolean enabled = pair.first();
       int chanceIn100 = pair.second();
-      if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
-        long rndTime = RANDOM.nextInt(1000);
+      if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
+        long rndTime = rand.nextInt(1000);
         log.info("inject random update delay of {}ms", rndTime);
         try {
           Thread.sleep(rndTime);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8ad1a830/solr/core/src/test/org/apache/solr/util/TestTestInjection.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/util/TestTestInjection.java b/solr/core/src/test/org/apache/solr/util/TestTestInjection.java
index 418b4a4..c4269cc 100644
--- a/solr/core/src/test/org/apache/solr/util/TestTestInjection.java
+++ b/solr/core/src/test/org/apache/solr/util/TestTestInjection.java
@@ -98,4 +98,8 @@ public class TestTestInjection extends LuceneTestCase {
       assertFalse(e.getMessage().toLowerCase(Locale.ENGLISH).contains("bad syntax"));
     }
   }
+
+  public void testUsingConsistentRandomization() {
+    assertSame(random(), TestInjection.random());
+  }
 }