package org.threadly.concurrent; import static org.junit.Assert.*; import static org.threadly.TestConstants.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; import org.threadly.concurrent.AbstractPriorityScheduler.OneTimeTaskWrapper; import org.threadly.test.concurrent.TestRunnable; import org.threadly.test.concurrent.TestUtils; import org.threadly.util.Clock; @SuppressWarnings("javadoc") public abstract class AbstractPrioritySchedulerTest extends SchedulerServiceInterfaceTest { @Override protected SchedulerServiceFactory getSchedulerServiceFactory() { return getAbstractPrioritySchedulerFactory(); } protected abstract AbstractPrioritySchedulerFactory getAbstractPrioritySchedulerFactory(); @Test public void getDefaultPriorityTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); TaskPriority priority = TaskPriority.High; try { AbstractPriorityScheduler scheduler = factory.makeAbstractPriorityScheduler(1, priority, 1000); assertEquals(priority, scheduler.getDefaultPriority()); priority = TaskPriority.Low; scheduler = factory.makeAbstractPriorityScheduler(1, priority, 1000); assertEquals(priority, scheduler.getDefaultPriority()); priority = TaskPriority.Starvable; scheduler = factory.makeAbstractPriorityScheduler(1, priority, 1000); assertEquals(priority, scheduler.getDefaultPriority()); } finally { factory.shutdown(); } } @Test public void constructorNullPriorityTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler executor = factory.makeAbstractPriorityScheduler(1, null, 1); assertTrue(executor.getDefaultPriority() == AbstractPriorityScheduler.DEFAULT_PRIORITY); } finally { factory.shutdown(); } } @Test public void getAndSetLowPriorityWaitTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); long lowPriorityWait = 1000; AbstractPriorityScheduler scheduler = factory.makeAbstractPriorityScheduler(1, TaskPriority.High, lowPriorityWait); try { assertEquals(lowPriorityWait, scheduler.getMaxWaitForLowPriority()); lowPriorityWait = Long.MAX_VALUE; scheduler.setMaxWaitForLowPriority(lowPriorityWait); assertEquals(lowPriorityWait, scheduler.getMaxWaitForLowPriority()); } finally { factory.shutdown(); } } @Test public void setLowPriorityWaitFail() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); long lowPriorityWait = 1000; AbstractPriorityScheduler scheduler = factory.makeAbstractPriorityScheduler(1, TaskPriority.High, lowPriorityWait); try { try { scheduler.setMaxWaitForLowPriority(-1); fail("Exception should have thrown"); } catch (IllegalArgumentException e) { // expected } assertEquals(lowPriorityWait, scheduler.getMaxWaitForLowPriority()); } finally { factory.shutdown(); } } @Test public void getQueuedTaskCountTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler result = factory.makeAbstractPriorityScheduler(1); // add directly to avoid starting the consumer result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(2, result.getQueuedTaskCount()); result.getQueueManager().getQueueSet(TaskPriority.Low) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.Low) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(4, result.getQueuedTaskCount()); assertEquals(4, result.getQueuedTaskCount(null)); } finally { factory.shutdown(); } } @Test public void getQueuedTaskCountStarvablePriorityTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler result = factory.makeAbstractPriorityScheduler(1); // add directly to avoid starting the consumer result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(0, result.getQueuedTaskCount(TaskPriority.Starvable)); result.getQueueManager().getQueueSet(TaskPriority.Starvable) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.Starvable) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(2, result.getQueuedTaskCount(TaskPriority.Starvable)); } finally { factory.shutdown(); } } @Test public void getQueuedTaskCountLowPriorityTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler result = factory.makeAbstractPriorityScheduler(1); // add directly to avoid starting the consumer result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(0, result.getQueuedTaskCount(TaskPriority.Low)); result.getQueueManager().getQueueSet(TaskPriority.Low) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.Low) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(2, result.getQueuedTaskCount(TaskPriority.Low)); } finally { factory.shutdown(); } } @Test public void getQueuedTaskCountHighPriorityTest() { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler result = factory.makeAbstractPriorityScheduler(1); // add directly to avoid starting the consumer result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.High) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(2, result.getQueuedTaskCount(TaskPriority.High)); result.getQueueManager().getQueueSet(TaskPriority.Low) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); result.getQueueManager().getQueueSet(TaskPriority.Low) .executeQueue.add(new OneTimeTaskWrapper(DoNothingRunnable.instance(), null, Clock.lastKnownForwardProgressingMillis())); assertEquals(2, result.getQueuedTaskCount(TaskPriority.High)); } finally { factory.shutdown(); } } @Test @Override public void executeTest() { AbstractPrioritySchedulerFactory priorityFactory = getAbstractPrioritySchedulerFactory(); try { super.executeTest(); PrioritySchedulerService scheduler = priorityFactory.makeAbstractPriorityScheduler(2); TestRunnable tr1 = new TestRunnable(); TestRunnable tr2 = new TestRunnable(); TestRunnable tr3 = new TestRunnable(); scheduler.execute(tr1, TaskPriority.High); scheduler.execute(tr2, TaskPriority.Low); scheduler.execute(tr3, TaskPriority.Starvable); scheduler.execute(tr1, TaskPriority.High); scheduler.execute(tr2, TaskPriority.Low); scheduler.execute(tr3, TaskPriority.Starvable); tr1.blockTillFinished(1000 * 10, 2); // throws exception if fails tr2.blockTillFinished(1000 * 10, 2); // throws exception if fails tr3.blockTillFinished(1000 * 1, 2); // throws exception if fails } finally { priorityFactory.shutdown(); } } @Override @Test public void submitRunnableTest() throws InterruptedException, ExecutionException { AbstractPrioritySchedulerFactory priorityFactory = getAbstractPrioritySchedulerFactory(); try { super.submitRunnableTest(); PrioritySchedulerService scheduler = priorityFactory.makeAbstractPriorityScheduler(2); TestRunnable tr1 = new TestRunnable(); TestRunnable tr2 = new TestRunnable(); TestRunnable tr3 = new TestRunnable(); scheduler.submit(tr1, TaskPriority.High); scheduler.submit(tr2, TaskPriority.Low); scheduler.submit(tr3, TaskPriority.Starvable); scheduler.submit(tr1, TaskPriority.High); scheduler.submit(tr2, TaskPriority.Low); scheduler.submit(tr3, TaskPriority.Starvable); tr1.blockTillFinished(1000 * 10, 2); // throws exception if fails tr2.blockTillFinished(1000 * 10, 2); // throws exception if fails tr3.blockTillFinished(1000 * 10, 2); // throws exception if fails } finally { priorityFactory.shutdown(); } } @Test @Override public void submitRunnableWithResultTest() throws InterruptedException, ExecutionException { AbstractPrioritySchedulerFactory priorityFactory = getAbstractPrioritySchedulerFactory(); try { super.submitRunnableWithResultTest(); PrioritySchedulerService scheduler = priorityFactory.makeAbstractPriorityScheduler(2); TestRunnable tr1 = new TestRunnable(); TestRunnable tr2 = new TestRunnable(); TestRunnable tr3 = new TestRunnable(); scheduler.submit(tr1, tr1, TaskPriority.High); scheduler.submit(tr2, tr2, TaskPriority.Low); scheduler.submit(tr3, tr3, TaskPriority.Starvable); scheduler.submit(tr1, tr1, TaskPriority.High); scheduler.submit(tr2, tr2, TaskPriority.Low); scheduler.submit(tr3, tr3, TaskPriority.Starvable); tr1.blockTillFinished(1000 * 10, 2); // throws exception if fails tr2.blockTillFinished(1000 * 10, 2); // throws exception if fails tr3.blockTillFinished(1000 * 10, 2); // throws exception if fails } finally { priorityFactory.shutdown(); } } @Test @Override public void submitCallableTest() throws InterruptedException, ExecutionException { AbstractPrioritySchedulerFactory priorityFactory = getAbstractPrioritySchedulerFactory(); try { super.submitCallableTest(); PrioritySchedulerService scheduler = priorityFactory.makeAbstractPriorityScheduler(2); TestCallable tc1 = new TestCallable(0); TestCallable tc2 = new TestCallable(0); TestCallable tc3 = new TestCallable(0); scheduler.submit(tc1, TaskPriority.High); scheduler.submit(tc2, TaskPriority.Low); scheduler.submit(tc3, TaskPriority.Starvable); tc1.blockTillTrue(); // throws exception if fails tc2.blockTillTrue(); // throws exception if fails tc3.blockTillTrue(); // throws exception if fails } finally { priorityFactory.shutdown(); } } @Test public void lowPriorityFlowControlTest() { AbstractPrioritySchedulerFactory priorityFactory = getAbstractPrioritySchedulerFactory(); final AtomicBoolean testRunning = new AtomicBoolean(true); try { final AbstractPriorityScheduler scheduler = priorityFactory.makeAbstractPriorityScheduler(1, TaskPriority.High, DELAY_TIME); new Runnable() { @Override public void run() { if (testRunning.get()) { while (scheduler.getQueueManager().getQueueSet(TaskPriority.High).executeQueue.size() < 5) { scheduler.execute(this, TaskPriority.High); } } } }.run(); TestRunnable lowPriorityRunnable = new TestRunnable(); scheduler.execute(lowPriorityRunnable, TaskPriority.Low); assertTrue(lowPriorityRunnable.getDelayTillFirstRun() >= DELAY_TIME); } finally { testRunning.set(false); priorityFactory.shutdown(); } } @Test public void removeHighPriorityRecurringRunnableTest() { removeRecurringRunnableTest(TaskPriority.High); } @Test public void removeLowPriorityRecurringRunnableTest() { removeRecurringRunnableTest(TaskPriority.Low); } @Test public void removeStarvablePriorityRecurringRunnableTest() { removeRecurringRunnableTest(TaskPriority.Starvable); } private void removeRecurringRunnableTest(TaskPriority priority) { int runFrequency = 1; AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler scheduler = factory.makeAbstractPriorityScheduler(1); TestRunnable removedTask = new TestRunnable(); TestRunnable keptTask = new TestRunnable(); scheduler.scheduleWithFixedDelay(removedTask, 0, runFrequency, priority); scheduler.scheduleWithFixedDelay(keptTask, 0, runFrequency, priority); removedTask.blockTillStarted(); assertFalse(scheduler.remove(new TestRunnable())); assertTrue(scheduler.remove(removedTask)); // verify removed is no longer running, and the kept task continues to run int keptRunCount = keptTask.getRunCount(); int runCount = removedTask.getRunCount(); TestUtils.sleep(runFrequency * 10); // may be +1 if the task was running while the remove was called assertTrue(removedTask.getRunCount() == runCount || removedTask.getRunCount() == runCount + 1); assertTrue(keptTask.getRunCount() >= keptRunCount); } finally { factory.shutdown(); } } @Test public void removeHighPriorityRunnableTest() { removeRunnableTest(TaskPriority.High); } @Test public void removeLowPriorityRunnableTest() { removeRunnableTest(TaskPriority.Low); } @Test public void removeStarvablePriorityRunnableTest() { removeRunnableTest(TaskPriority.Starvable); } private void removeRunnableTest(TaskPriority priority) { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler scheduler = factory.makeAbstractPriorityScheduler(1); TestRunnable removedTask = new TestRunnable(); scheduler.submitScheduled(removedTask, 10 * 1000, priority); assertFalse(scheduler.remove(new TestRunnable())); assertTrue(scheduler.remove(removedTask)); } finally { factory.shutdown(); } } @Test public void removeHighPriorityCallableTest() { removeCallableTest(TaskPriority.High); } @Test public void removeLowPriorityCallableTest() { removeCallableTest(TaskPriority.Low); } @Test public void removeStarvablePriorityCallableTest() { removeCallableTest(TaskPriority.Starvable); } private void removeCallableTest(TaskPriority priority) { AbstractPrioritySchedulerFactory factory = getAbstractPrioritySchedulerFactory(); try { AbstractPriorityScheduler scheduler = factory.makeAbstractPriorityScheduler(1); TestCallable task = new TestCallable(); scheduler.submitScheduled(task, 1000 * 10, priority); assertFalse(scheduler.remove(new TestCallable())); assertTrue(scheduler.remove(task)); } finally { factory.shutdown(); } } public interface AbstractPrioritySchedulerFactory extends SchedulerServiceFactory { public AbstractPriorityScheduler makeAbstractPriorityScheduler(int poolSize, TaskPriority defaultPriority, long maxWaitForLowPriority); public AbstractPriorityScheduler makeAbstractPriorityScheduler(int poolSize); } }