/* * Copyright 2015 Terracotta, Inc., a Software AG company. * * Licensed 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.terracotta.offheapstore.util; import org.terracotta.offheapstore.util.Retryer; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import static java.util.concurrent.Executors.defaultThreadFactory; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.hamcrest.number.OrderingComparison.greaterThan; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; /** * * @author cdennis */ public class RetryerTest { @Test(expected = IllegalArgumentException.class) public void testIaeOnNullTimeUnit() { new Retryer(1, 2, null, defaultThreadFactory()); } @Test(expected = IllegalArgumentException.class) public void testIaeOnNullThreadFactory() { new Retryer(1, 2, MILLISECONDS, null); } @Test(expected = IllegalArgumentException.class) public void testIaeOnIllegalMinDelay() { new Retryer(0, 2, MILLISECONDS, defaultThreadFactory()); } @Test(expected = IllegalArgumentException.class) public void testIaeOnIllegalMaxDelay() { new Retryer(2, 1, MILLISECONDS, defaultThreadFactory()); } @Test public void testSuccessfullTaskIsNotRetried() { Retryer retryer = new Retryer(1, 1, TimeUnit.MILLISECONDS, defaultThreadFactory()); try { Runnable task = mock(Runnable.class); retryer.completeAsynchronously(task); verify(task, timeout(100).times(1)).run(); } finally { retryer.shutdownNow(); } } @Test public void testUnsuccessfullTaskIsRetried() { Retryer retryer = new Retryer(1, 1, TimeUnit.MILLISECONDS, defaultThreadFactory()); try { Runnable task = mock(Runnable.class); doThrow(new IllegalStateException()).doNothing().when(task).run(); retryer.completeAsynchronously(task); verify(task, timeout(100).times(2)).run(); } finally { retryer.shutdownNow(); } } @Test public void testRetriesBackOffCorrectly() { Retryer retryer = new Retryer(100, 1000, TimeUnit.MILLISECONDS, defaultThreadFactory()); try { final Map<Integer, Long> executionTimes = new ConcurrentHashMap<Integer, Long>(); Runnable task = mock(Runnable.class); doAnswer(new Answer() { private volatile int executions; @Override public Object answer(InvocationOnMock invocation) throws Throwable { executionTimes.put(executions, System.currentTimeMillis()); switch (executions++) { case 0: case 1: throw new IllegalStateException(); default: return null; } } }).when(task).run(); retryer.completeAsynchronously(task); verify(task, timeout(500).times(3)).run(); long deltaOne = executionTimes.get(1) - executionTimes.get(0); long deltaTwo = executionTimes.get(2) - executionTimes.get(1); assertThat(deltaTwo, greaterThan(deltaOne)); } finally { retryer.shutdownNow(); } } @Test public void testShutdownRetryerRefuses() { Retryer retryer = new Retryer(1, 1, TimeUnit.MILLISECONDS, defaultThreadFactory()); retryer.shutdownNow(); try { retryer.completeAsynchronously(new Runnable() { @Override public void run() { } }); fail("Expected RejectedExecutionException"); } catch (RejectedExecutionException e) { //expected } } }