mirror of https://github.com/apache/lucene.git
SOLR-9367: Improved TestInjection's randomization logic to use LuceneTestCase.random()
This commit is contained in:
parent
dbe6f6a108
commit
84a8c098fc
|
@ -247,6 +247,8 @@ Other Changes
|
||||||
|
|
||||||
* SOLR-9385: Add QParser.getParser(String,SolrQueryRequest) variant. (Christine Poerschke)
|
* 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 ==================
|
================== 6.1.0 ==================
|
||||||
|
|
||||||
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.
|
Consult the LUCENE_CHANGES.txt file for additional, low level, changes in this release.
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.apache.solr.util;
|
package org.apache.solr.util;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
@ -39,6 +40,11 @@ import org.slf4j.LoggerFactory;
|
||||||
* Allows random faults to be injected in running code during test runs.
|
* 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.
|
* 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 {
|
public class TestInjection {
|
||||||
|
|
||||||
|
@ -53,16 +59,42 @@ public class TestInjection {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
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 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 {
|
static {
|
||||||
// We try to make things reproducible in the context of our tests by initializing the random instance
|
Class nonFinalTemp = null;
|
||||||
// based on the current seed
|
try {
|
||||||
String seed = System.getProperty("tests.seed");
|
ClassLoader classLoader = MethodHandles.lookup().lookupClass().getClassLoader();
|
||||||
if (seed == null) {
|
nonFinalTemp = classLoader.loadClass(LUCENE_TEST_CASE_FQN);
|
||||||
RANDOM = new Random();
|
} 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 {
|
} 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() {
|
public static boolean injectRandomDelayInCoreCreation() {
|
||||||
if (randomDelayInCoreCreation != null) {
|
if (randomDelayInCoreCreation != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(randomDelayInCoreCreation);
|
Pair<Boolean,Integer> pair = parseValue(randomDelayInCoreCreation);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
int chanceIn100 = pair.second();
|
||||||
if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
|
if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
|
||||||
int delay = RANDOM.nextInt(randomDelayMaxInCoreCreationInSec);
|
int delay = rand.nextInt(randomDelayMaxInCoreCreationInSec);
|
||||||
log.info("Inject random core creation delay of {}s", delay);
|
log.info("Inject random core creation delay of {}s", delay);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(delay * 1000);
|
Thread.sleep(delay * 1000);
|
||||||
|
@ -118,11 +153,14 @@ public class TestInjection {
|
||||||
|
|
||||||
public static boolean injectNonGracefullClose(CoreContainer cc) {
|
public static boolean injectNonGracefullClose(CoreContainer cc) {
|
||||||
if (cc.isShutDown() && nonGracefullClose != null) {
|
if (cc.isShutDown() && nonGracefullClose != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(nonGracefullClose);
|
Pair<Boolean,Integer> pair = parseValue(nonGracefullClose);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
int chanceIn100 = pair.second();
|
||||||
if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
|
if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
|
||||||
if (RANDOM.nextBoolean()) {
|
if (rand.nextBoolean()) {
|
||||||
throw new TestShutdownFailError("Test exception for non graceful close");
|
throw new TestShutdownFailError("Test exception for non graceful close");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -135,7 +173,9 @@ public class TestInjection {
|
||||||
// we should only need to do it once
|
// we should only need to do it once
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (InterruptedException e) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -147,7 +187,7 @@ public class TestInjection {
|
||||||
};
|
};
|
||||||
Timer timer = new Timer();
|
Timer timer = new Timer();
|
||||||
timers.add(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() {
|
public static boolean injectFailReplicaRequests() {
|
||||||
if (failReplicaRequests != null) {
|
if (failReplicaRequests != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(failReplicaRequests);
|
Pair<Boolean,Integer> pair = parseValue(failReplicaRequests);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
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");
|
throw new SolrException(ErrorCode.SERVER_ERROR, "Random test update fail");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,10 +212,13 @@ public class TestInjection {
|
||||||
|
|
||||||
public static boolean injectFailUpdateRequests() {
|
public static boolean injectFailUpdateRequests() {
|
||||||
if (failUpdateRequests != null) {
|
if (failUpdateRequests != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(failUpdateRequests);
|
Pair<Boolean,Integer> pair = parseValue(failUpdateRequests);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
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");
|
throw new SolrException(ErrorCode.SERVER_ERROR, "Random test update fail");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,10 +228,13 @@ public class TestInjection {
|
||||||
|
|
||||||
public static boolean injectNonExistentCoreExceptionAfterUnload(String cname) {
|
public static boolean injectNonExistentCoreExceptionAfterUnload(String cname) {
|
||||||
if (nonExistentCoreExceptionAfterUnload != null) {
|
if (nonExistentCoreExceptionAfterUnload != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(nonExistentCoreExceptionAfterUnload);
|
Pair<Boolean,Integer> pair = parseValue(nonExistentCoreExceptionAfterUnload);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
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);
|
throw new NonExistentCoreException("Core not found to unload: " + cname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,11 +244,14 @@ public class TestInjection {
|
||||||
|
|
||||||
public static boolean injectUpdateLogReplayRandomPause() {
|
public static boolean injectUpdateLogReplayRandomPause() {
|
||||||
if (updateLogReplayRandomPause != null) {
|
if (updateLogReplayRandomPause != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(updateLogReplayRandomPause);
|
Pair<Boolean,Integer> pair = parseValue(updateLogReplayRandomPause);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
int chanceIn100 = pair.second();
|
||||||
if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
|
if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
|
||||||
long rndTime = RANDOM.nextInt(1000);
|
long rndTime = rand.nextInt(1000);
|
||||||
log.info("inject random log replay delay of {}ms", rndTime);
|
log.info("inject random log replay delay of {}ms", rndTime);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(rndTime);
|
Thread.sleep(rndTime);
|
||||||
|
@ -214,11 +266,14 @@ public class TestInjection {
|
||||||
|
|
||||||
public static boolean injectUpdateRandomPause() {
|
public static boolean injectUpdateRandomPause() {
|
||||||
if (updateRandomPause != null) {
|
if (updateRandomPause != null) {
|
||||||
|
Random rand = random();
|
||||||
|
if (null == rand) return true;
|
||||||
|
|
||||||
Pair<Boolean,Integer> pair = parseValue(updateRandomPause);
|
Pair<Boolean,Integer> pair = parseValue(updateRandomPause);
|
||||||
boolean enabled = pair.first();
|
boolean enabled = pair.first();
|
||||||
int chanceIn100 = pair.second();
|
int chanceIn100 = pair.second();
|
||||||
if (enabled && RANDOM.nextInt(100) >= (100 - chanceIn100)) {
|
if (enabled && rand.nextInt(100) >= (100 - chanceIn100)) {
|
||||||
long rndTime = RANDOM.nextInt(1000);
|
long rndTime = rand.nextInt(1000);
|
||||||
log.info("inject random update delay of {}ms", rndTime);
|
log.info("inject random update delay of {}ms", rndTime);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(rndTime);
|
Thread.sleep(rndTime);
|
||||||
|
|
|
@ -98,4 +98,8 @@ public class TestTestInjection extends LuceneTestCase {
|
||||||
assertFalse(e.getMessage().toLowerCase(Locale.ENGLISH).contains("bad syntax"));
|
assertFalse(e.getMessage().toLowerCase(Locale.ENGLISH).contains("bad syntax"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testUsingConsistentRandomization() {
|
||||||
|
assertSame(random(), TestInjection.random());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue