Skip to content

Commit

Permalink
Change SlowStorageEngineTest to avoid JUnit assertions in worker
Browse files Browse the repository at this point in the history
threads. Also removed JUnit4 style annotations (@test) since this
is actually a Junit3 style test (because it extends other Junit3
style tests that extend TestCase).
  • Loading branch information
jayjwylie committed Oct 9, 2012
1 parent 8301415 commit 4afb8c4
Showing 1 changed file with 52 additions and 63 deletions.
115 changes: 52 additions & 63 deletions test/unit/voldemort/store/memory/SlowStorageEngineTest.java
Expand Up @@ -24,7 +24,6 @@
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;


import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.junit.Test;


import voldemort.TestUtils; import voldemort.TestUtils;
import voldemort.common.VoldemortOpCode; import voldemort.common.VoldemortOpCode;
Expand Down Expand Up @@ -84,61 +83,44 @@ public List<ByteArray> getKeys(int numKeys) {
return keys; return keys;
} }


// Two modes for the runnable: give it a time to check (expectedTimeMs) or private String getOpName(Byte opCode) {
// give it a concurrent queue upon which to add its run time. switch(opCode) {
case VoldemortOpCode.GET_OP_CODE:
return "Get";
case VoldemortOpCode.GET_VERSION_OP_CODE:
return "GetVersion";
case VoldemortOpCode.GET_ALL_OP_CODE:
return "GetAll";
case VoldemortOpCode.DELETE_OP_CODE:
return "Delete";
case VoldemortOpCode.PUT_OP_CODE:
return "Put";
default:
logger.error("getOpName invoked with bad operation code: " + opCode);
}
return null;
}

public class OpInvoker implements Runnable { public class OpInvoker implements Runnable {


private final CountDownLatch signal; private final CountDownLatch signal;
private final byte opCode; private final byte opCode;


private long expectedTimeMs;
private ConcurrentLinkedQueue<Long> runTimes; private ConcurrentLinkedQueue<Long> runTimes;


private final ByteArray key; private final ByteArray key;
private final byte[] value; private final byte[] value;


protected OpInvoker(CountDownLatch signal, byte opCode) { OpInvoker(CountDownLatch signal, byte opCode, ConcurrentLinkedQueue<Long> runTimes) {
this.signal = signal; this.signal = signal;
this.opCode = opCode; this.opCode = opCode;

this.runTimes = runTimes;
this.expectedTimeMs = -1;
this.runTimes = null;

this.key = new ByteArray(ByteUtils.getBytes("key", "UTF-8")); this.key = new ByteArray(ByteUtils.getBytes("key", "UTF-8"));
this.value = ByteUtils.getBytes("value", "UTF-8"); this.value = ByteUtils.getBytes("value", "UTF-8");
logger.debug("OpInvoker created for operation " + getOpName() + "(Thread: " logger.debug("OpInvoker created for operation " + getOpName(this.opCode) + "(Thread: "
+ Thread.currentThread().getName() + ")"); + Thread.currentThread().getName() + ")");
} }


// expectedTimeMs <= 0 means not checking the runtime
OpInvoker(CountDownLatch signal, byte opCode, long expectedTimeMs) {
this(signal, opCode);
this.expectedTimeMs = expectedTimeMs;
}

OpInvoker(CountDownLatch signal, byte opCode, ConcurrentLinkedQueue<Long> runTimes) {
this(signal, opCode);
this.runTimes = runTimes;
}

private String getOpName() {
switch(this.opCode) {
case VoldemortOpCode.GET_OP_CODE:
return "Get";
case VoldemortOpCode.GET_VERSION_OP_CODE:
return "GetVersion";
case VoldemortOpCode.GET_ALL_OP_CODE:
return "GetAll";
case VoldemortOpCode.DELETE_OP_CODE:
return "Delete";
case VoldemortOpCode.PUT_OP_CODE:
return "Put";
default:
logger.error("getOpName invoked with bad operation code: " + opCode);
}
return null;
}

private void doGet() { private void doGet() {
store.get(key, null); store.get(key, null);
} }
Expand Down Expand Up @@ -189,16 +171,8 @@ public void run() {
} }
long runTimeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTimeNs); long runTimeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTimeNs);


if(this.expectedTimeMs > 0) { runTimes.add(runTimeMs);
String details = "(runTimeMs: " + runTimeMs + ", Thread: " logger.debug("OpInvoker finished operation " + getOpName(this.opCode) + "(Thread: "
+ Thread.currentThread().getName() + ")";
assertFalse("OpInvoker operation time is bad " + details,
!isRunTimeBad(runTimeMs, this.expectedTimeMs));
}

if(this.runTimes != null)
runTimes.add(runTimeMs);
logger.debug("OpInvoker finished operation " + getOpName() + "(Thread: "
+ Thread.currentThread().getName() + ")"); + Thread.currentThread().getName() + ")");
signal.countDown(); signal.countDown();
} }
Expand All @@ -207,39 +181,54 @@ public void run() {
// true if runtime is not within a "reasonable" range. Reasonable // true if runtime is not within a "reasonable" range. Reasonable
// defined by a 10% fudge factor. // defined by a 10% fudge factor.
private boolean isRunTimeBad(long runTimeMs, long expectedTimeMs) { private boolean isRunTimeBad(long runTimeMs, long expectedTimeMs) {
if((runTimeMs < expectedTimeMs || runTimeMs > (expectedTimeMs * 1.1))) { if((runTimeMs < (expectedTimeMs * 0.9) || runTimeMs > (expectedTimeMs * 1.1))) {
return false; return true;
} }
return true; return false;
} }


/** /**
* Test the time of each op type individually. * Test the time of each op type individually.
*/ */
@Test
public void testEachOpTypeIndividually() { public void testEachOpTypeIndividually() {
for(int i = 0; i < 5; ++i) { // Magic constant 60 ms is based on operation times defined above.
long timeoutMs = 60; long expectedMs = 60;
if(i == 0)
timeoutMs = 0; // Magic constants 50 and 5 below allow us to make sure a tight timing

// test passes 90% of the time.
for(byte op: opList) { int numOps = 50;
int numOpsWithBadTimesOK = 5;
for(byte op: opList) {
int badTimesCounter = 0;
for(int i = 0; i < numOps; ++i) {
CountDownLatch waitForOp = new CountDownLatch(1); CountDownLatch waitForOp = new CountDownLatch(1);
new Thread(new OpInvoker(waitForOp, op, timeoutMs)).start(); ConcurrentLinkedQueue<Long> runTimes = new ConcurrentLinkedQueue<Long>();
new Thread(new OpInvoker(waitForOp, op, runTimes)).start();
try { try {
waitForOp.await(); waitForOp.await();
} catch(InterruptedException e) { } catch(InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }

long runTimeMs = runTimes.poll();
assertTrue(runTimes.isEmpty());

if(isRunTimeBad(runTimeMs, expectedMs)) {
System.err.println("Bad run time (some are expected): " + getOpName(op)
+ ", runTimeMs: " + runTimeMs + ", expectedMs: "
+ expectedMs + ")");
badTimesCounter++;
}

} }
assertFalse("Too many bad times for operation " + getOpName(op),
badTimesCounter > numOpsWithBadTimesOK);
} }

} }


/** /**
* Test repeated operations. * Test repeated operations.
*/ */
@Test
public void testEachOpTypeRepeated() { public void testEachOpTypeRepeated() {
// Magic number '2': Run once to warm up, run again and test asserts on // Magic number '2': Run once to warm up, run again and test asserts on
// second pass // second pass
Expand Down Expand Up @@ -287,7 +276,7 @@ public void testEachOpTypeRepeated() {
} }
String details = "(maxTimeMs: " + maxTimeMs + ", " + expectedTimeMs + ")"; String details = "(maxTimeMs: " + maxTimeMs + ", " + expectedTimeMs + ")";
assertFalse("OpInvoker operation time is bad " + details, assertFalse("OpInvoker operation time is bad " + details,
!isRunTimeBad(maxTimeMs, expectedTimeMs)); isRunTimeBad(maxTimeMs, expectedTimeMs));
} }
} }
} }
Expand Down

0 comments on commit 4afb8c4

Please sign in to comment.