/* * Copyright 2015 Ben Manes. All Rights Reserved. * * 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 com.github.benmanes.caffeine.cache; import static com.github.benmanes.caffeine.cache.Async.MAXIMUM_EXPIRY; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.lang.reflect.Constructor; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.mockito.Mockito; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.Async.AsyncExpiry; import com.github.benmanes.caffeine.testing.Awaits; /** * @author ben.manes@gmail.com (Ben Manes) */ public final class AsyncTest { private static final long ONE_MINUTE = TimeUnit.MINUTES.toNanos(1); @Test public void reflectivelyConstruct() throws Exception { Constructor<?> constructor = Async.class.getDeclaredConstructor(); constructor.setAccessible(true); constructor.newInstance(); } @Test(dataProvider = "successful") public void isReady_success(CompletableFuture<?> future) { assertThat(Async.isReady(future), is(true)); } @Test(dataProvider = "unsuccessful") public void isReady_fails(CompletableFuture<?> future) { assertThat(Async.isReady(future), is(false)); } @Test(dataProvider = "successful") public void getIfReady_success(CompletableFuture<?> future) { assertThat(Async.getIfReady(future), is(1)); } @Test(dataProvider = "unsuccessful") public void getIfReady_fails(CompletableFuture<?> future) { assertThat(Async.getIfReady(future), is(nullValue())); } @Test(dataProvider = "successful") public void getWhenSuccessful_success(CompletableFuture<?> future) { assertThat(Async.getWhenSuccessful(future), is(1)); } @Test public void getWhenSuccessful_success_async() { CompletableFuture<Integer> future = new CompletableFuture<Integer>(); AtomicInteger result = new AtomicInteger(); ForkJoinPool.commonPool().execute(() -> { result.set(1); result.set(Async.getWhenSuccessful(future)); }); Awaits.await().untilAtomic(result, is(1)); future.obtrudeValue(2); Awaits.await().untilAtomic(result, is(2)); } @Test(dataProvider = "unsuccessful") public void getWhenSuccessful_fails(CompletableFuture<?> future) { if ((future != null) && !future.isDone()) { AtomicInteger result = new AtomicInteger(); ForkJoinPool.commonPool().execute(() -> { result.set(1); Object value = Async.getWhenSuccessful(future); result.set((value == null) ? 2 : 3); }); Awaits.await().untilAtomic(result, is(1)); future.obtrudeException(new IllegalStateException()); Awaits.await().untilAtomic(result, is(not(1))); assertThat(result.get(), is(2)); } assertThat(Async.getWhenSuccessful(future), is(nullValue())); } @Test public void asyncExpiry_pending() { AsyncExpiry<Integer, Integer> expiry = makeAsyncExpiry(ONE_MINUTE, ONE_MINUTE, ONE_MINUTE); CompletableFuture<Integer> future = new CompletableFuture<Integer>(); assertThat(expiry.expireAfterCreate(0, future, 1L), is(Long.MAX_VALUE)); verify(expiry.delegate, never()).expireAfterCreate(any(), any(), anyLong()); assertThat(expiry.expireAfterUpdate(0, future, 1L, 2L), is(Long.MAX_VALUE)); verify(expiry.delegate, never()).expireAfterUpdate(any(), any(), anyLong(), anyLong()); assertThat(expiry.expireAfterRead(0, future, 1L, 2L), is(Long.MAX_VALUE)); verify(expiry.delegate, never()).expireAfterRead(any(), any(), anyLong(), anyLong()); } @Test public void asyncExpiry_completed() { AsyncExpiry<Integer, Integer> expiry = makeAsyncExpiry( ONE_MINUTE, 2 * ONE_MINUTE, 3 * ONE_MINUTE); CompletableFuture<Integer> future = CompletableFuture.completedFuture(100); assertThat(expiry.expireAfterCreate(0, future, 1L), is(ONE_MINUTE)); verify(expiry.delegate).expireAfterCreate(0, 100, 1L); assertThat(expiry.expireAfterUpdate(0, future, 1L, 2L), is(2 * ONE_MINUTE)); verify(expiry.delegate).expireAfterUpdate(0, 100, 1L, 2L); assertThat(expiry.expireAfterRead(0, future, 1L, 2L), is(3 * ONE_MINUTE)); verify(expiry.delegate).expireAfterRead(0, 100, 1L, 2L); } @Test public void asyncExpiry_bounded() { AsyncExpiry<Integer, Integer> expiry = makeAsyncExpiry( Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE); CompletableFuture<Integer> future = CompletableFuture.completedFuture(100); assertThat(expiry.expireAfterCreate(0, future, 1L), is(MAXIMUM_EXPIRY)); assertThat(expiry.expireAfterUpdate(0, future, 1L, 2L), is(MAXIMUM_EXPIRY)); assertThat(expiry.expireAfterRead(0, future, 1L, 2L), is(MAXIMUM_EXPIRY)); } @DataProvider(name = "successful") public Object[][] providesSuccessful() { return new Object[][] {{ CompletableFuture.completedFuture(1) }}; } @DataProvider(name = "unsuccessful") public Object[][] providesUnsuccessful() { return new Object[][] { { null }, { new CompletableFuture<Integer>() }, { newFailedFuture(new InterruptedException()) }, { newFailedFuture(new IllegalStateException()) }, }; } private static <K, V> AsyncExpiry<K, V> makeAsyncExpiry(long create, long update, long read) { @SuppressWarnings("unchecked") Expiry<K, V> mock = Mockito.mock(Expiry.class); when(mock.expireAfterCreate(any(), any(), anyLong())).thenReturn(create); when(mock.expireAfterUpdate(any(), any(), anyLong(), anyLong())).thenReturn(update); when(mock.expireAfterRead(any(), any(), anyLong(), anyLong())).thenReturn(read); return new AsyncExpiry<>(mock); } private static CompletableFuture<Integer> newFailedFuture(Exception e) { CompletableFuture<Integer> future = new CompletableFuture<Integer>(); future.completeExceptionally(e); return future; } }