Skip to content

Commit 5016c14

Browse files
committed
Add a little tolerance due to async scheduling
When shutting down the executor, other async tasks may be rejected that are benign to the test. For example the failed future is removed, a no-op removal notification is published (and ignored), and a cleanup cycle is triggered (and falls back to the calling thread if rejected). In these cases the executor can fail without harming the test.
1 parent 023674e commit 5016c14

File tree

5 files changed

+31
-16
lines changed

5 files changed

+31
-16
lines changed

caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import com.github.benmanes.caffeine.cache.testing.CacheProvider;
5050
import com.github.benmanes.caffeine.cache.testing.CacheSpec;
5151
import com.github.benmanes.caffeine.cache.testing.CacheSpec.CacheExecutor;
52+
import com.github.benmanes.caffeine.cache.testing.CacheSpec.ExecutorFailure;
5253
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Listener;
5354
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Loader;
5455
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Population;
@@ -161,7 +162,8 @@ public void getFunc_absent_null(AsyncLoadingCache<Integer, Integer> cache,
161162

162163
@CheckNoWriter
163164
@Test(dataProvider = "caches")
164-
@CacheSpec(loader = Loader.NULL, executor = CacheExecutor.SINGLE)
165+
@CacheSpec(loader = Loader.NULL,
166+
executor = CacheExecutor.SINGLE, executorFailure = ExecutorFailure.IGNORED)
165167
public void getFunc_absent_null_async(AsyncLoadingCache<Integer, Integer> cache,
166168
CacheContext context) {
167169
Integer key = context.absentKey();
@@ -175,9 +177,10 @@ public void getFunc_absent_null_async(AsyncLoadingCache<Integer, Integer> cache,
175177

176178
ready.set(true);
177179
Awaits.await().untilTrue(done);
180+
Awaits.await().until(() -> cache.getIfPresent(key) == null);
178181
MoreExecutors.shutdownAndAwaitTermination(context.executor(), 1, TimeUnit.MINUTES);
179182

180-
assertThat(context, both(hasMissCount(1)).and(hasHitCount(0)));
183+
assertThat(context, both(hasMissCount(2)).and(hasHitCount(0)));
181184
assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1)));
182185

183186
assertThat(valueFuture.isDone(), is(true));
@@ -201,7 +204,7 @@ public void getFunc_absent_failure(AsyncLoadingCache<Integer, Integer> cache,
201204

202205
@CheckNoWriter
203206
@Test(dataProvider = "caches")
204-
@CacheSpec(executor = CacheExecutor.SINGLE)
207+
@CacheSpec(executor = CacheExecutor.SINGLE, executorFailure = ExecutorFailure.IGNORED)
205208
public void getFunc_absent_failure_async(AsyncLoadingCache<Integer, Integer> cache,
206209
CacheContext context) {
207210
AtomicBoolean ready = new AtomicBoolean();
@@ -214,9 +217,10 @@ public void getFunc_absent_failure_async(AsyncLoadingCache<Integer, Integer> cac
214217

215218
ready.set(true);
216219
Awaits.await().untilTrue(done);
220+
Awaits.await().until(() -> cache.getIfPresent(context.absentKey()) == null);
217221
MoreExecutors.shutdownAndAwaitTermination(context.executor(), 1, TimeUnit.MINUTES);
218222

219-
assertThat(context, both(hasMissCount(1)).and(hasHitCount(0)));
223+
assertThat(context, both(hasMissCount(2)).and(hasHitCount(0)));
220224
assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1)));
221225

222226
assertThat(valueFuture.isCompletedExceptionally(), is(true));
@@ -432,7 +436,8 @@ public void get_absent_failure(AsyncLoadingCache<Integer, Integer> cache, CacheC
432436

433437
@CheckNoWriter
434438
@Test(dataProvider = "caches")
435-
@CacheSpec(executor = CacheExecutor.SINGLE, loader = Loader.EXCEPTIONAL)
439+
@CacheSpec(loader = Loader.EXCEPTIONAL,
440+
executor = CacheExecutor.SINGLE, executorFailure = ExecutorFailure.IGNORED)
436441
public void get_absent_failure_async(AsyncLoadingCache<Integer, Integer> cache,
437442
CacheContext context) throws InterruptedException {
438443
AtomicBoolean done = new AtomicBoolean();
@@ -441,9 +446,10 @@ public void get_absent_failure_async(AsyncLoadingCache<Integer, Integer> cache,
441446
valueFuture.whenComplete((r, e) -> done.set(true));
442447

443448
Awaits.await().untilTrue(done);
449+
Awaits.await().until(() -> cache.getIfPresent(context.absentKey()) == null);
444450
MoreExecutors.shutdownAndAwaitTermination(context.executor(), 1, TimeUnit.MINUTES);
445451

446-
assertThat(context, both(hasMissCount(1)).and(hasHitCount(0)));
452+
assertThat(context, both(hasMissCount(2)).and(hasHitCount(0)));
447453
assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1)));
448454

449455
assertThat(valueFuture.isCompletedExceptionally(), is(true));

caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.github.benmanes.caffeine.cache.testing.CacheSpec.CacheExecutor;
4646
import com.github.benmanes.caffeine.cache.testing.CacheSpec.CacheWeigher;
4747
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Compute;
48+
import com.github.benmanes.caffeine.cache.testing.CacheSpec.ExecutorFailure;
4849
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Expire;
4950
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Implementation;
5051
import com.github.benmanes.caffeine.cache.testing.CacheSpec.InitialCapacity;
@@ -94,8 +95,9 @@ public void putWeighted_noOverflow() {
9495

9596
@Test(dataProvider = "caches")
9697
@CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine,
97-
population = Population.FULL, maximumSize = MaximumSize.FULL, executorMayFail = true,
98-
executor = CacheExecutor.REJECTING, removalListener = Listener.CONSUMING)
98+
population = Population.FULL, maximumSize = MaximumSize.FULL,
99+
executorFailure = ExecutorFailure.EXPECTED, executor = CacheExecutor.REJECTING,
100+
removalListener = Listener.CONSUMING)
99101
public void evict_rejected(Cache<Integer, Integer> cache, CacheContext context) {
100102
cache.put(context.absentKey(), context.absentValue());
101103
}

caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpirationTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.github.benmanes.caffeine.cache.testing.CacheSpec;
4040
import com.github.benmanes.caffeine.cache.testing.CacheSpec.CacheWeigher;
4141
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Compute;
42+
import com.github.benmanes.caffeine.cache.testing.CacheSpec.ExecutorFailure;
4243
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Expire;
4344
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Implementation;
4445
import com.github.benmanes.caffeine.cache.testing.CacheSpec.Listener;
@@ -87,9 +88,9 @@ public void expire_zero(Cache<Integer, Integer> cache, CacheContext context) {
8788
@Test(dataProvider = "caches", expectedExceptions = DeleteException.class)
8889
@CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG,
8990
population = Population.FULL, writer = Writer.EXCEPTIONAL, requiresExpiration = true,
90-
expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE}, executorMayFail = true,
91-
expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE},
92-
compute = Compute.SYNC, removalListener = Listener.REJECTING)
91+
expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE},
92+
expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE}, compute = Compute.SYNC,
93+
executorFailure = ExecutorFailure.EXPECTED, removalListener = Listener.REJECTING)
9394
public void getIfPresent_writerFails(Cache<Integer, Integer> cache, CacheContext context) {
9495
try {
9596
context.ticker().advance(1, TimeUnit.HOURS);
@@ -834,10 +835,11 @@ public void computeIfPresent(Map<Integer, Integer> map, CacheContext context) {
834835

835836
@Test(dataProvider = "caches", expectedExceptions = DeleteException.class)
836837
@CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG,
837-
population = Population.FULL, requiresExpiration = true, executorMayFail = true,
838+
population = Population.FULL, requiresExpiration = true,
838839
expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE},
839840
expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE},
840-
compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING)
841+
compute = Compute.SYNC, writer = Writer.EXCEPTIONAL,
842+
executorFailure = ExecutorFailure.EXPECTED, removalListener = Listener.REJECTING)
841843
public void computeIfPresent_writerFails(Map<Integer, Integer> map, CacheContext context) {
842844
try {
843845
Integer key = context.firstKey();

caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheSpec.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,11 @@ CacheExecutor[] executor() default {
481481
};
482482

483483
/** If the executor is allowed to have failures. */
484-
boolean executorMayFail() default false;
484+
ExecutorFailure executorFailure() default ExecutorFailure.DISALLOWED;
485+
486+
enum ExecutorFailure {
487+
EXPECTED, DISALLOWED, IGNORED
488+
}
485489

486490
/** The executors that the cache can be configured with. */
487491
enum CacheExecutor implements Supplier<TrackingExecutor> {

caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheValidationListener.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
4141
import com.github.benmanes.caffeine.cache.Cache;
42+
import com.github.benmanes.caffeine.cache.testing.CacheSpec.ExecutorFailure;
4243

4344
/**
4445
* A listener that validates the internal structure after a successful test execution.
@@ -88,9 +89,9 @@ private static void checkExecutor(ITestResult testResult, CacheContext context)
8889

8990
assertThat("CacheContext required", context, is(not(nullValue())));
9091
TrackingExecutor executor = context.executor();
91-
if (cacheSpec.executorMayFail()) {
92+
if (cacheSpec.executorFailure() == ExecutorFailure.EXPECTED) {
9293
assertThat(executor.failureCount(), is(greaterThan(0)));
93-
} else {
94+
} else if (cacheSpec.executorFailure() == ExecutorFailure.DISALLOWED) {
9495
assertThat(executor.failureCount(), is(0));
9596
}
9697
}

0 commit comments

Comments
 (0)