package org.threadly.concurrent.statistics;
import static org.junit.Assert.*;
import static org.threadly.TestConstants.*;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeoutException;
import org.threadly.BlockingTestRunnable;
import org.threadly.concurrent.DoNothingRunnable;
import org.threadly.concurrent.TaskPriority;
import org.threadly.test.concurrent.AsyncVerifier;
import org.threadly.test.concurrent.TestCondition;
import org.threadly.test.concurrent.TestRunnable;
import org.threadly.test.concurrent.TestUtils;
import org.threadly.util.Pair;
@SuppressWarnings("javadoc")
public class ThreadedStatisticPrioritySchedulerTests {
public static void blockTillSchedulerIdle(final StatisticPriorityScheduler scheduler,
final int expectedSampleSize) {
new TestCondition() { // block till all are finished
@Override
public boolean get() {
return scheduler.getActiveTaskCount() == 0 &&
scheduler.getExecutionDurationSamples().size() >= expectedSampleSize;
}
}.blockTillTrue();
}
public static void resetCollectedStatsTest(StatisticPriorityScheduler scheduler) {
TestRunnable lastRunnable = null;
boolean flip = false;
for (int i = 0; i < TEST_QTY; i++) {
lastRunnable = new TestRunnable();
if (flip) {
scheduler.execute(lastRunnable, TaskPriority.High);
flip = false;
} else {
scheduler.execute(lastRunnable, TaskPriority.Low);
flip = true;
}
}
lastRunnable.blockTillFinished();
// block till all are finished
blockTillSchedulerIdle(scheduler, TEST_QTY);
// reset stats
scheduler.resetCollectedStats();
assertEquals(-1, scheduler.getAverageExecutionDuration(), 0);
for (TaskPriority p : TaskPriority.values()) {
assertEquals(-1, scheduler.getAverageExecutionDelay(p), 0);
}
}
public static void getTotalExecutionCountTest(StatisticPriorityScheduler scheduler) {
final int starvablePriorityCount = 1;
final int lowPriorityCount = TEST_QTY;
final int highPriorityCount = TEST_QTY * 2;
TestRunnable lastStarvablePriorityRunnable = null;
for (int i = 0; i < starvablePriorityCount; i++) {
lastStarvablePriorityRunnable = new TestRunnable();
scheduler.execute(lastStarvablePriorityRunnable, TaskPriority.Starvable);
}
TestRunnable lastLowPriorityRunnable = null;
for (int i = 0; i < lowPriorityCount; i++) {
lastLowPriorityRunnable = new TestRunnable();
scheduler.execute(lastLowPriorityRunnable, TaskPriority.Low);
}
TestRunnable lastHighPriorityRunnable = null;
for (int i = 0; i < highPriorityCount; i++) {
lastHighPriorityRunnable = new TestRunnable();
scheduler.execute(lastHighPriorityRunnable, TaskPriority.High);
}
lastLowPriorityRunnable.blockTillFinished();
lastHighPriorityRunnable.blockTillFinished();
// should not be very long after waiting above
blockTillSchedulerIdle(scheduler,
starvablePriorityCount + lowPriorityCount + highPriorityCount);
assertEquals(starvablePriorityCount, scheduler.getTotalExecutionCount(TaskPriority.Starvable));
assertEquals(lowPriorityCount, scheduler.getTotalExecutionCount(TaskPriority.Low));
assertEquals(highPriorityCount, scheduler.getTotalExecutionCount(TaskPriority.High));
assertEquals(starvablePriorityCount + lowPriorityCount + highPriorityCount,
scheduler.getTotalExecutionCount(null));
assertEquals(starvablePriorityCount + lowPriorityCount + highPriorityCount,
scheduler.getTotalExecutionCount());
}
public static void getAverageExecutionDelayNoInputTest(StatisticPriorityScheduler scheduler) {
assertEquals(-1, scheduler.getAverageExecutionDelay(), 0);
}
public static void getPriorityAverageExecutionDelayNoInputTest(StatisticPriorityScheduler scheduler,
TaskPriority testPriority) {
assertEquals(-1, scheduler.getAverageExecutionDelay(testPriority), 0);
BlockingTestRunnable br = new BlockingTestRunnable();
try {
switch (testPriority) { // submit with opposite priority
case High:
scheduler.execute(br, TaskPriority.Low);
break;
case Low:
scheduler.execute(br, TaskPriority.High);
break;
default:
throw new UnsupportedOperationException("Priority not implenented: " + testPriority);
}
br.unblock();
// wait for task to finish now
br.blockTillFinished();
blockTillSchedulerIdle(scheduler, 1);
assertEquals(-1, scheduler.getAverageExecutionDelay(testPriority), 0);
} finally {
br.unblock();
}
}
public static void getPriorityAverageExecutionDelayTest(StatisticPriorityScheduler scheduler,
TaskPriority priority) {
int lowPriorityCount = TEST_QTY;
int highPriorityCount = TEST_QTY * 2;
BlockingTestRunnable lastRunnable = null;
for (int i = 0; i < lowPriorityCount; i++) {
if (lastRunnable != null) {
TestUtils.blockTillClockAdvances();
lastRunnable.unblock();
}
lastRunnable = new BlockingTestRunnable();
scheduler.execute(lastRunnable, TaskPriority.Low);
}
for (int i = 0; i < highPriorityCount; i++) {
TestUtils.blockTillClockAdvances();
lastRunnable.unblock();
lastRunnable = new BlockingTestRunnable();
scheduler.execute(lastRunnable, TaskPriority.High);
}
TestUtils.blockTillClockAdvances();
lastRunnable.unblock();
lastRunnable.blockTillFinished();
blockTillSchedulerIdle(scheduler, lowPriorityCount + highPriorityCount);
List<Long> samples = scheduler.getExecutionDelaySamples(priority);
List<Long> allSamples = scheduler.getExecutionDelaySamples();
if (priority != null) {
assertTrue(allSamples.size() > samples.size());
}
assertTrue(allSamples.containsAll(samples));
double total = 0;
Iterator<Long> it = samples.iterator();
while (it.hasNext()) {
total += it.next();
}
double expectedAvg = total / samples.size();
assertEquals(expectedAvg, scheduler.getAverageExecutionDelay(priority), 0);
}
public static void getExecutionDelayPercentilesTest(StatisticPriorityScheduler scheduler) {
BlockingTestRunnable btr = new BlockingTestRunnable();
try {
assertEquals(0, scheduler.getExecutionDelayPercentiles(90).get(90.), 0);
scheduler.execute(btr);
scheduler.execute(new ClockUpdateRunnable());
TestUtils.blockTillClockAdvances();
btr.unblock();
assertEquals(2, scheduler.getExecutionDelayPercentiles(90).get(90.), 10);
} finally {
btr.unblock();
}
}
public static void getPriorityExecutionDelayPercentilesTest(StatisticPriorityScheduler scheduler,
TaskPriority priority) {
BlockingTestRunnable btr = new BlockingTestRunnable();
try {
assertEquals(0, scheduler.getExecutionDelayPercentiles(90).get(90.), 0);
scheduler.execute(btr);
scheduler.execute(new ClockUpdateRunnable(), priority);
TestUtils.blockTillClockAdvances();
btr.unblock();
assertEquals(2, scheduler.getExecutionDelayPercentiles(90).get(90.), 10);
} finally {
btr.unblock();
}
}
public static void getAverageExecutionDurationNoInputTest(StatisticPriorityScheduler scheduler) {
assertEquals(-1, scheduler.getAverageExecutionDuration(), 0);
}
public static void getAverageExecutionDurationSimpleTest(StatisticPriorityScheduler scheduler) {
assertEquals(-1, scheduler.getAverageExecutionDuration(), 0);
scheduler.execute(new ClockUpdateRunnable());
blockTillSchedulerIdle(scheduler, 1);
assertEquals(1, scheduler.getAverageExecutionDuration(), 1);
scheduler.execute(new ClockUpdateRunnable(DELAY_TIME));
blockTillSchedulerIdle(scheduler, 2);
assertTrue(scheduler.getAverageExecutionDuration() >= DELAY_TIME / 2);
}
public static void getAverageExecutionDurationTest(StatisticPriorityScheduler scheduler) {
int lowPriorityCount = TEST_QTY;
int highPriorityCount = TEST_QTY * 2;
for (int i = 0; i < lowPriorityCount; i++) {
scheduler.execute(DoNothingRunnable.instance(), TaskPriority.Low);
}
TestRunnable lastRunnable = null;
for (int i = 0; i < highPriorityCount; i++) {
lastRunnable = new TestRunnable();
scheduler.execute(lastRunnable, TaskPriority.High);
}
lastRunnable.blockTillFinished();
int expectedCount = lowPriorityCount + highPriorityCount;
// block till all are finished
blockTillSchedulerIdle(scheduler, expectedCount);
List<Long> runTimes = scheduler.getExecutionDurationSamples();
assertEquals(expectedCount, runTimes.size());
double totalRunTime = 0;
Iterator<Long> it = runTimes.iterator();
while (it.hasNext()) {
totalRunTime += it.next();
}
double avgRunTime = totalRunTime / runTimes.size();
assertEquals(avgRunTime, scheduler.getAverageExecutionDuration(), 0);
}
public static void getWithPriorityAverageExecutionDurationNoInputTest(StatisticPriorityScheduler scheduler) {
TestRunnable tr = new TestRunnable();
scheduler.execute(tr, TaskPriority.Low);
tr.blockTillFinished();
blockTillSchedulerIdle(scheduler, 1);
assertEquals(-1, scheduler.getAverageExecutionDuration(TaskPriority.High), 0);
}
public static void getWithPriorityAverageExecutionDurationTest(StatisticPriorityScheduler scheduler) {
int lowPriorityCount = TEST_QTY;
int highPriorityCount = TEST_QTY * 2;
for (int i = 0; i < lowPriorityCount; i++) {
scheduler.execute(DoNothingRunnable.instance(), TaskPriority.Low);
}
TestRunnable lastRunnable = null;
for (int i = 0; i < highPriorityCount; i++) {
lastRunnable = new TestRunnable();
scheduler.execute(lastRunnable, TaskPriority.High);
}
lastRunnable.blockTillFinished();
// block till all are finished
blockTillSchedulerIdle(scheduler, lowPriorityCount + highPriorityCount);
List<Long> runTimes = scheduler.getExecutionDurationSamples(TaskPriority.High);
assertEquals(highPriorityCount, runTimes.size());
double totalRunTime = 0;
Iterator<Long> it = runTimes.iterator();
while (it.hasNext()) {
totalRunTime += it.next();
}
double avgRunTime = totalRunTime / runTimes.size();
assertEquals(avgRunTime, scheduler.getAverageExecutionDuration(TaskPriority.High), 0);
}
public static void getExecutionDurationPercentilesTest(StatisticPriorityScheduler scheduler) {
assertEquals(0, scheduler.getExecutionDurationPercentiles(50).get(50.), 0);
scheduler.execute(new ClockUpdateRunnable());
blockTillSchedulerIdle(scheduler, 1);
assertEquals(1, scheduler.getExecutionDurationPercentiles(50).get(50.), 1);
scheduler.execute(new ClockUpdateRunnable());
scheduler.execute(new ClockUpdateRunnable());
scheduler.execute(new ClockUpdateRunnable());
scheduler.execute(new ClockUpdateRunnable(DELAY_TIME));
blockTillSchedulerIdle(scheduler, 5);
assertEquals(1, scheduler.getExecutionDurationPercentiles(75).get(75.), 1);
assertTrue(scheduler.getExecutionDurationPercentiles(90).get(90.) >= DELAY_TIME-ALLOWED_VARIANCE);
}
public static void getWithPriorityExecutionDurationPercentilesTest(StatisticPriorityScheduler scheduler) {
assertEquals(0, scheduler.getExecutionDurationPercentiles(TaskPriority.High, 50).get(50.), 0);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.Low);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.Low);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.Low);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.Low);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.Low);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.High);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.High);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.High);
scheduler.execute(new ClockUpdateRunnable(), TaskPriority.High);
scheduler.execute(new ClockUpdateRunnable(DELAY_TIME), TaskPriority.High);
blockTillSchedulerIdle(scheduler, 10);
assertEquals(1, scheduler.getExecutionDurationPercentiles(TaskPriority.High, 75).get(75.), 1);
assertTrue(scheduler.getExecutionDurationPercentiles(TaskPriority.High, 90).get(90.) >= (DELAY_TIME-ALLOWED_VARIANCE));
assertEquals(1, scheduler.getExecutionDurationPercentiles(TaskPriority.Low, 90).get(90.), 1);
}
public static void getLongRunningTasksTest(StatisticPriorityScheduler scheduler) {
final int checkTime = DELAY_TIME;
BlockingTestRunnable br = new BlockingTestRunnable();
scheduler.execute(br);
br.blockTillStarted();
TestUtils.sleep(checkTime + 1);
assertEquals(1, scheduler.getLongRunningTasksQty(checkTime));
List<Pair<Runnable, StackTraceElement[]>> longRunning = scheduler.getLongRunningTasks(checkTime);
br.unblock();
assertEquals(1, longRunning.size());
assertTrue(longRunning.get(0).getLeft() == br);
// wait for task to finish now
blockTillSchedulerIdle(scheduler, 1);
assertEquals(0, scheduler.getLongRunningTasksQty(0));
longRunning = scheduler.getLongRunningTasks(0);
assertTrue(longRunning.isEmpty());
}
public static void getLongRunningTasksWrappedFutureTest(final StatisticPriorityScheduler scheduler) throws InterruptedException, TimeoutException {
final AsyncVerifier av = new AsyncVerifier();
scheduler.submit(new ClockUpdateRunnable() {
@Override
public void handleRunStart() {
// even submitted (and thus wrapped in a future), we should get our direct reference
av.assertTrue(scheduler.getLongRunningTasks(-1).get(0).getLeft() == this);
av.signalComplete();
}
});
av.waitForTest();
}
public static void getLongRunningTasksQtyTest(final StatisticPriorityScheduler scheduler) {
assertEquals(0, scheduler.getLongRunningTasksQty(-1));
scheduler.execute(new ClockUpdateRunnable() {
@Override
public void handleRunStart() {
assertEquals(1, scheduler.getLongRunningTasksQty(-1));
assertEquals(0, scheduler.getLongRunningTasksQty(10));
}
});
}
}