/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.internal; import static org.junit.Assert.*; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.junit.experimental.categories.Category; import org.apache.geode.test.junit.categories.UnitTest; @Category(UnitTest.class) public class OneTaskOnlyDecoratorJUnitTest { /** * Test to make sure we only execute the task once no matter how many times we schedule it. */ @Test public void testExecuteOnlyOnce() throws Exception { ScheduledExecutorService ex = Executors.newScheduledThreadPool(1); MyConflationListener listener = new MyConflationListener(); OneTaskOnlyExecutor decorator = new OneTaskOnlyExecutor(ex, listener); final CountDownLatch latch = new CountDownLatch(1); ex.submit(new Callable() { public Object call() throws Exception { latch.await(); return null; } }); final AtomicInteger counter = new AtomicInteger(); Runnable increment = new Runnable() { public void run() { counter.incrementAndGet(); } }; for (int i = 0; i < 50; i++) { decorator.schedule(increment, 0, TimeUnit.SECONDS); } assertEquals(0, counter.get()); latch.countDown(); ex.shutdown(); ex.awaitTermination(60, TimeUnit.SECONDS); assertEquals(1, counter.get()); assertEquals(49, listener.getDropCount()); } /** * Test to make sure we reschedule the task for execution if it has already in progress. */ @Test public void testReschedule() throws Exception { ScheduledExecutorService ex = Executors.newScheduledThreadPool(1); OneTaskOnlyExecutor decorator = new OneTaskOnlyExecutor(ex); final CountDownLatch taskRunning = new CountDownLatch(1); final CountDownLatch continueTask = new CountDownLatch(1); final AtomicInteger counter = new AtomicInteger(); Callable waitForLatch = new Callable() { public Object call() throws Exception { taskRunning.countDown(); continueTask.await(); counter.incrementAndGet(); return null; } }; Runnable increment = new Runnable() { public void run() { counter.incrementAndGet(); } }; decorator.schedule(waitForLatch, 0, TimeUnit.SECONDS); taskRunning.await(); decorator.schedule(increment, 0, TimeUnit.SECONDS); assertEquals(0, counter.get()); continueTask.countDown(); ex.shutdown(); ex.awaitTermination(60, TimeUnit.SECONDS); assertEquals(2, counter.get()); } /** * Test to make sure we reschedule the task for execution if the new requested execution is * earlier than the previous one */ @Test public void testRescheduleForEarlierTime() throws Exception { ScheduledExecutorService ex = Executors.newScheduledThreadPool(1); MyConflationListener listener = new MyConflationListener(); OneTaskOnlyExecutor decorator = new OneTaskOnlyExecutor(ex, listener); final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger counter = new AtomicInteger(); Runnable increment = new Runnable() { public void run() { counter.incrementAndGet(); } }; decorator.schedule(increment, 120, TimeUnit.SECONDS); decorator.schedule(increment, 10, TimeUnit.MILLISECONDS); long start = System.nanoTime(); ex.shutdown(); ex.awaitTermination(60, TimeUnit.SECONDS); long elapsed = System.nanoTime() - start; assertEquals(1, counter.get()); assertEquals(1, listener.getDropCount()); assertTrue(elapsed < TimeUnit.SECONDS.toNanos(120)); } private static class MyConflationListener extends OneTaskOnlyExecutor.ConflatedTaskListenerAdapter { private int dropCount; @Override public void taskDropped() { dropCount++; } public int getDropCount() { return dropCount; } } }