/* * Copyright 2014 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.testing.CacheWriterVerifier.verifyWriter; import static com.github.benmanes.caffeine.cache.testing.HasRemovalNotifications.hasRemovalNotifications; import static com.github.benmanes.caffeine.cache.testing.HasStats.hasHitCount; import static com.github.benmanes.caffeine.cache.testing.HasStats.hasLoadFailureCount; import static com.github.benmanes.caffeine.cache.testing.HasStats.hasLoadSuccessCount; import static com.github.benmanes.caffeine.cache.testing.HasStats.hasMissCount; import static com.github.benmanes.caffeine.testing.IsEmptyIterable.deeplyEmpty; import static com.github.benmanes.caffeine.testing.IsEmptyMap.emptyMap; import static com.google.common.collect.Maps.immutableEntry; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.both; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.testng.annotations.Listeners; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.testing.CacheContext; import com.github.benmanes.caffeine.cache.testing.CacheProvider; import com.github.benmanes.caffeine.cache.testing.CacheSpec; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Compute; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Implementation; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Listener; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Population; import com.github.benmanes.caffeine.cache.testing.CacheSpec.ReferenceType; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Writer; import com.github.benmanes.caffeine.cache.testing.CacheValidationListener; import com.github.benmanes.caffeine.cache.testing.CheckNoStats; import com.github.benmanes.caffeine.cache.testing.CheckNoWriter; import com.github.benmanes.caffeine.cache.testing.RejectingCacheWriter.DeleteException; import com.github.benmanes.caffeine.cache.testing.RejectingCacheWriter.WriteException; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; import com.google.common.testing.SerializableTester; /** * The test cases for the {@link Cache#asMap()} view and its serializability. These tests do not * validate eviction management or concurrency behavior. * * @author ben.manes@gmail.com (Ben Manes) */ @Listeners(CacheValidationListener.class) @Test(dataProviderClass = CacheProvider.class) public final class AsMapTest { // Statistics are recorded only for computing methods for loadSuccess and loadFailure /* ---------------- is empty / size / clear -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void isEmpty(Map<Integer, Integer> map, CacheContext context) { assertThat(map.isEmpty(), is(context.original().isEmpty())); if (map.isEmpty()) { assertThat(map, is(emptyMap())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void size(Map<Integer, Integer> map, CacheContext context) { assertThat(map.size(), is(context.original().size())); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void clear(Map<Integer, Integer> map, CacheContext context) { map.clear(); assertThat(map, is(emptyMap())); assertThat(map, hasRemovalNotifications(context, context.original().size(), RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> { verifier.deletedAll(context.original(), RemovalCause.EXPLICIT); }); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void clear_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.clear(); } finally { assertThat(map, equalTo(context.original())); } } /* ---------------- contains -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void containsKey_null(Map<Integer, Integer> map, CacheContext context) { map.containsKey(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void containsKey_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.containsKey(key), is(true)); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void containsKey_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.containsKey(context.absentKey()), is(false)); } @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void containsValue_null(Map<Integer, Integer> map, CacheContext context) { map.containsValue(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void containsValue_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.containsValue(context.original().get(key)), is(true)); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void containsValue_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.containsValue(context.absentValue()), is(false)); } /* ---------------- get -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void get_null(Map<Integer, Integer> map, CacheContext context) { map.get(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void get_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.get(context.absentKey()), is(nullValue())); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void get_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.get(key), is(context.original().get(key))); } } /* ---------------- getOrDefault -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void getOrDefault_nullKey(Map<Integer, Integer> map, CacheContext context) { map.getOrDefault(null, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void getOrDefault_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.getOrDefault(context.absentKey(), null), is(nullValue())); assertThat(map.getOrDefault(context.absentKey(), context.absentKey()), is(context.absentKey())); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void getOrDefault_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.getOrDefault(key, context.absentKey()), is(context.original().get(key))); } } /* ---------------- forEach -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void forEach_null(Map<Integer, Integer> map, CacheContext context) { map.forEach(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void forEach_scan(Map<Integer, Integer> map, CacheContext context) { Map<Integer, Integer> remaining = new HashMap<>(context.original()); map.forEach(remaining::remove); assertThat(remaining, is(emptyMap())); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void forEach_modify(Map<Integer, Integer> map, CacheContext context) { // non-deterministic traversal behavior with modifications, but shouldn't become corrupted List<Integer> modified = new ArrayList<>(); map.forEach((key, value) -> { Integer newKey = context.lastKey() + key; modified.add(newKey); // for weak keys map.put(newKey, key); }); } /* ---------------- put -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void put_nullKey(Map<Integer, Integer> map, CacheContext context) { map.put(null, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void put_nullValue(Map<Integer, Integer> map, CacheContext context) { map.put(1, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void put_nullKeyAndValue(Map<Integer, Integer> map, CacheContext context) { map.put(null, null); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void put_insert_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.put(context.absentKey(), context.absentValue()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void put_replace_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.put(context.middleKey(), context.absentValue()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void put_insert(Map<Integer, Integer> map, CacheContext context) { assertThat(map.put(context.absentKey(), context.absentValue()), is(nullValue())); assertThat(map.get(context.absentKey()), is(context.absentValue())); assertThat(map.size(), is(context.original().size() + 1)); verifyWriter(context, (verifier, writer) -> { verifier.wrote(context.absentKey(), context.absentValue()); }); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.CONSUMING }) public void put_replace_sameValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.put(key, value), is(value)); assertThat(map.get(key), is(value)); } int count = context.firstMiddleLastKeys().size(); assertThat(map.size(), is(context.original().size())); if (context.isGuava() || context.isAsync()) { assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } else { assertThat(context.consumedNotifications(), hasSize(0)); } } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.CONSUMING }) public void put_replace_differentValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.put(key, context.absentValue()), is(value)); assertThat(map.get(key), is(context.absentValue())); verifyWriter(context, (verifier, writer) -> { verifier.wrote(key, context.absentValue()); }); } int count = context.firstMiddleLastKeys().size(); assertThat(map.size(), is(context.original().size())); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } /* ---------------- putAll -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putAll_null(Map<Integer, Integer> map, CacheContext context) { map.putAll(null); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void putAll_insert_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.putAll(context.absent()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void putAll_replace_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.putAll(ImmutableMap.of(context.middleKey(), context.absentValue())); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putAll_empty(Map<Integer, Integer> map, CacheContext context) { map.putAll(new HashMap<>()); assertThat(map.size(), is(context.original().size())); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putAll_insert(Map<Integer, Integer> map, CacheContext context) { int startKey = context.original().size() + 1; Map<Integer, Integer> entries = IntStream .range(startKey, 100 + startKey).boxed() .collect(Collectors.toMap(Function.identity(), key -> -key)); map.putAll(entries); assertThat(map.size(), is(100 + context.original().size())); verifyWriter(context, (verifier, writer) -> { verifier.wroteAll(entries); }); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.CONSUMING }) public void putAll_replace(Map<Integer, Integer> map, CacheContext context) { Map<Integer, Integer> entries = new LinkedHashMap<>(context.original()); entries.replaceAll((key, value) -> key); map.putAll(entries); assertThat(map, is(equalTo(entries))); assertThat(map, hasRemovalNotifications(context, entries.size(), RemovalCause.REPLACED)); verifyWriter(context, (verifier, writer) -> { verifier.wroteAll(entries); }); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.CONSUMING }) public void putAll_mixed(Map<Integer, Integer> map, CacheContext context) { Map<Integer, Integer> entries = new HashMap<>(); Map<Integer, Integer> replaced = new HashMap<>(); context.original().forEach((key, value) -> { if ((key % 2) == 0) { value++; replaced.put(key, value); } entries.put(key, value); }); map.putAll(entries); assertThat(map, is(equalTo(entries))); Map<Integer, Integer> expect = (context.isGuava() || context.isAsync()) ? entries : replaced; assertThat(map, hasRemovalNotifications(context, expect.size(), RemovalCause.REPLACED)); verifyWriter(context, (verifier, writer) -> { verifier.wroteAll(replaced); }); } /* ---------------- putIfAbsent -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putIfAbsent_nullKey(Map<Integer, Integer> map, CacheContext context) { map.putIfAbsent(null, 2); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putIfAbsent_nullValue(Map<Integer, Integer> map, CacheContext context) { map.putIfAbsent(1, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putIfAbsent_nullKeyAndValue(Map<Integer, Integer> map, CacheContext context) { map.putIfAbsent(null, null); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void putIfAbsent_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.putIfAbsent(context.absentKey(), context.absentValue()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putIfAbsent_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.putIfAbsent(key, key), is(value)); assertThat(map.get(key), is(value)); } assertThat(map.size(), is(context.original().size())); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void putIfAbsent_insert(Map<Integer, Integer> map, CacheContext context) { assertThat(map.putIfAbsent(context.absentKey(), context.absentValue()), is(nullValue())); assertThat(map.get(context.absentKey()), is(context.absentValue())); assertThat(map.size(), is(context.original().size() + 1)); verifyWriter(context, (verifier, writer) -> { verifier.wrote(context.absentKey(), context.absentValue()); }); } /* ---------------- remove -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void remove_nullKey(Map<Integer, Integer> map, CacheContext context) { map.remove(null); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void remove_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.remove(context.middleKey()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void remove_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.remove(context.absentKey()), is(nullValue())); assertThat(map.size(), is(context.original().size())); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void remove_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { map.remove(key); verifyWriter(context, (verifier, writer) -> { verifier.deleted(key, context.original().get(key), RemovalCause.EXPLICIT); }); } assertThat(map.size(), is(context.original().size() - context.firstMiddleLastKeys().size())); int count = context.firstMiddleLastKeys().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> verifier.deletions(count, RemovalCause.EXPLICIT)); } /* ---------------- remove conditionally -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void removeConditionally_nullKey(Map<Integer, Integer> map, CacheContext context) { map.remove(null, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void removeConditionally_nullValue(Map<Integer, Integer> map, CacheContext context) { assertThat(map.remove(1, null), is(false)); // see ConcurrentHashMap } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void removeConditionally_nullKeyAndValue(Map<Integer, Integer> map, CacheContext context) { map.remove(null, null); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void removeConditionally_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.remove(context.middleKey(), context.original().get(context.middleKey())); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void removeConditionally_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.remove(context.absentKey(), context.absentValue()), is(false)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void removeConditionally_presentKey(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.remove(key, key), is(false)); } assertThat(map.size(), is(context.original().size())); } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void removeConditionally_presentKeyAndValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.remove(key, value), is(true)); verifyWriter(context, (verifier, writer) -> { verifier.deleted(key, value, RemovalCause.EXPLICIT); }); } int count = context.firstMiddleLastKeys().size(); assertThat(map.size(), is(context.original().size() - count)); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> verifier.deletions(count, RemovalCause.EXPLICIT)); } /* ---------------- replace -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replace_null(Map<Integer, Integer> map, CacheContext context) { map.replace(null, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replace_nullValue(Map<Integer, Integer> map, CacheContext context) { map.replace(1, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replace_nullKeyAndValue(Map<Integer, Integer> map, CacheContext context) { map.replace(null, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replace_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.replace(context.absentKey(), context.absentValue()), is(nullValue())); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void replace_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.replace(context.middleKey(), context.absentValue()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void replace_sameValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.replace(key, value), is(value)); assertThat(map.get(key), is(value)); } assertThat(map.size(), is(context.original().size())); if (context.isGuava() || context.isAsync()) { int count = context.firstMiddleLastKeys().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } else { assertThat(context.consumedNotifications(), hasSize(0)); } } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void replace_differentValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer oldValue = context.original().get(key); assertThat(map.replace(key, context.absentValue()), is(oldValue)); assertThat(map.get(key), is(context.absentValue())); verifyWriter(context, (verifier, writer) -> verifier.wrote(key, context.absentValue())); } int count = context.firstMiddleLastKeys().size(); assertThat(map.size(), is(context.original().size())); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); verifyWriter(context, (verifier, writer) -> verifier.writes(count)); } /* ---------------- replace conditionally -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullKey(Map<Integer, Integer> map, CacheContext context) { map.replace(null, 1, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullOldValue(Map<Integer, Integer> map, CacheContext context) { map.replace(1, null, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullNewValue(Map<Integer, Integer> map, CacheContext context) { map.replace(1, 1, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullKeyAndOldValue(Map<Integer, Integer> map, CacheContext context) { map.replace(null, null, 1); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullKeyAndNewValue(Map<Integer, Integer> map, CacheContext context) { map.replace(null, 1, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullOldAndNewValue(Map<Integer, Integer> map, CacheContext context) { map.replace(1, null, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_nullKeyAndValues(Map<Integer, Integer> map, CacheContext context) { map.replace(null, null, null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_absent(Map<Integer, Integer> map, CacheContext context) { Integer key = context.absentKey(); assertThat(map.replace(key, key, key), is(false)); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void replaceConditionally_writerFails(Map<Integer, Integer> map, CacheContext context) { try { Integer key = context.middleKey(); map.replace(key, context.original().get(key), context.absentValue()); } finally { assertThat(map, equalTo(context.original())); } } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void replaceConditionally_wrongOldValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.replace(key, key, context.absentKey()), is(false)); assertThat(map.get(key), is(value)); } assertThat(map.size(), is(context.original().size())); int count = context.firstMiddleLastKeys().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } @CheckNoStats @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void replaceConditionally_sameValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.replace(key, value, value), is(true)); assertThat(map.get(key), is(value)); } assertThat(map.size(), is(context.original().size())); if (context.isGuava() || context.isAsync()) { int count = context.firstMiddleLastKeys().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } else { assertThat(context.consumedNotifications(), hasSize(0)); } } @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void replaceConditionally_differentValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.replace(key, context.original().get(key), context.absentValue()), is(true)); assertThat(map.get(key), is(context.absentValue())); verifyWriter(context, (verifier, writer) -> verifier.wrote(key, context.absentValue())); } assertThat(map.size(), is(context.original().size())); int count = context.firstMiddleLastKeys().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); verifyWriter(context, (verifier, writer) -> verifier.writes(count)); } /* ---------------- replaceAll -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void replaceAll_null(Map<Integer, Integer> map, CacheContext context) { map.replaceAll(null); } @CheckNoWriter @CheckNoStats @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void replaceAll_nullValue(Map<Integer, Integer> map, CacheContext context) { map.replaceAll((key, value) -> null); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = WriteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void replaceAll_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.replaceAll((key, value) -> context.absentValue()); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void replaceAll_sameValue(Map<Integer, Integer> map, CacheContext context) { map.replaceAll((key, value) -> value); assertThat(map, is(equalTo(context.original()))); if (context.isGuava() || context.isAsync()) { assertThat(map, hasRemovalNotifications(context, map.size(), RemovalCause.REPLACED)); } else { assertThat(context.consumedNotifications(), hasSize(0)); } } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void replaceAll_differentValue(Map<Integer, Integer> map, CacheContext context) { map.replaceAll((key, value) -> key); map.forEach((key, value) -> { assertThat(value, is(equalTo(key))); verifyWriter(context, (verifier, writer) -> verifier.wrote(key, key)); }); assertThat(map, hasRemovalNotifications(context, map.size(), RemovalCause.REPLACED)); verifyWriter(context, (verifier, writer) -> verifier.writes(context.original().size())); } /* ---------------- computeIfAbsent -------------- */ @CheckNoStats @CheckNoWriter @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void computeIfAbsent_nullKey(Map<Integer, Integer> map, CacheContext context) { map.computeIfAbsent(null, key -> -key); } @CheckNoStats @CheckNoWriter @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void computeIfAbsent_nullMappingFunction(Map<Integer, Integer> map, CacheContext context) { map.computeIfAbsent(context.absentKey(), null); } @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void computeIfAbsent_nullValue(Map<Integer, Integer> map, CacheContext context) { assertThat(map.computeIfAbsent(context.absentKey(), key -> null), is(nullValue())); assertThat(map.size(), is(context.original().size())); assertThat(context, both(hasMissCount(1)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); } // FIXME: Requires JDK8 release with JDK-8062841 fix @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(enabled = false, dataProvider = "caches", expectedExceptions = IllegalStateException.class) public void computeIfAbsent_recursive(Map<Integer, Integer> map, CacheContext context) { Function<Integer, Integer> mappingFunction = new Function<Integer, Integer>() { @Override public Integer apply(Integer key) { return map.computeIfAbsent(key, this); } }; map.computeIfAbsent(context.absentKey(), mappingFunction); } // FIXME: Requires JDK8 release with JDK-8062841 fix @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(enabled = false, dataProvider = "caches", expectedExceptions = IllegalStateException.class) public void computeIfAbsent_pingpong(Map<Integer, Integer> map, CacheContext context) { Function<Integer, Integer> mappingFunction = new Function<Integer, Integer>() { @Override public Integer apply(Integer key) { Integer value = context.original().get(key); return map.computeIfAbsent(value, this); } }; map.computeIfAbsent(context.absentKey(), mappingFunction); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void computeIfAbsent_error(Map<Integer, Integer> map, CacheContext context) { try { map.computeIfAbsent(context.absentKey(), key -> { throw new Error(); }); } catch (Error e) {} assertThat(map, is(equalTo(context.original()))); assertThat(context, both(hasMissCount(1)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void computeIfAbsent_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.computeIfAbsent(key, k -> { throw new AssertionError(); }), is(value)); } int count = context.firstMiddleLastKeys().size(); assertThat(map.size(), is(context.original().size())); assertThat(context, both(hasMissCount(0)).and(hasHitCount(count))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(0))); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void computeIfAbsent_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.computeIfAbsent(context.absentKey(), key -> context.absentValue()), is(context.absentValue())); assertThat(context, both(hasMissCount(1)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(1)).and(hasLoadFailureCount(0))); assertThat(map.get(context.absentKey()), is(context.absentValue())); assertThat(map.size(), is(1 + context.original().size())); } /* ---------------- computeIfPresent -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void computeIfPresent_nullKey(Map<Integer, Integer> map, CacheContext context) { map.computeIfPresent(null, (key, value) -> -key); } @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void computeIfPresent_nullMappingFunction(Map<Integer, Integer> map, CacheContext context) { map.computeIfPresent(1, null); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void computeIfPresent_nullValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { map.computeIfPresent(key, (k, v) -> null); } int count = context.firstMiddleLastKeys().size(); assertThat(map.size(), is(context.original().size() - count)); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(count))); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); } @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = StackOverflowError.class) public void computeIfPresent_recursive(Map<Integer, Integer> map, CacheContext context) { // As we cannot provide immediate checking without an expensive solution, e.g. ThreadLocal, // instead we assert that a stack overflow error will occur to inform the developer (vs // a live-lock or deadlock alternative). BiFunction<Integer, Integer, Integer> mappingFunction = new BiFunction<Integer, Integer, Integer>() { boolean recursed; @Override public Integer apply(Integer key, Integer value) { if (recursed) { throw new StackOverflowError(); } recursed = true; return map.computeIfPresent(key, this); } }; map.computeIfPresent(context.firstKey(), mappingFunction); } @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = StackOverflowError.class) public void computeIfPresent_pingpong(Map<Integer, Integer> map, CacheContext context) { // As we cannot provide immediate checking without an expensive solution, e.g. ThreadLocal, // instead we assert that a stack overflow error will occur to inform the developer (vs // a live-lock or deadlock alternative). BiFunction<Integer, Integer, Integer> mappingFunction = new BiFunction<Integer, Integer, Integer>() { int recursed; @Override public Integer apply(Integer key, Integer value) { if (++recursed == 2) { throw new StackOverflowError(); } return map.computeIfPresent(context.lastKey(), this); } }; map.computeIfPresent(context.firstKey(), mappingFunction); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void computeIfPresent_error(Map<Integer, Integer> map, CacheContext context) { try { map.computeIfPresent(context.firstKey(), (key, value) -> { throw new Error(); }); } catch (Error e) {} assertThat(map, is(equalTo(context.original()))); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void computeIfPresent_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.computeIfPresent(context.absentKey(), (key, value) -> value), is(nullValue())); assertThat(map.get(context.absentKey()), is(nullValue())); assertThat(map.size(), is(context.original().size())); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void computeIfPresent_present(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.computeIfPresent(key, (k, v) -> k), is(key)); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(count)).and(hasLoadFailureCount(0))); for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.get(key), is(key)); } assertThat(map.size(), is(context.original().size())); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } /* ---------------- compute -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void compute_nullKey(Map<Integer, Integer> map, CacheContext context) { map.compute(null, (key, value) -> -key); } @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void compute_nullMappingFunction(Map<Integer, Integer> map, CacheContext context) { map.compute(1, null); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void compute_remove(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.compute(key, (k, v) -> null), is(nullValue())); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(count))); assertThat(map.size(), is(context.original().size() - count)); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); } // FIXME: Requires JDK8 release with JDK-8062841 fix @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(enabled = false, dataProvider = "caches", expectedExceptions = StackOverflowError.class) public void compute_recursive(Map<Integer, Integer> map, CacheContext context) { BiFunction<Integer, Integer, Integer> mappingFunction = new BiFunction<Integer, Integer, Integer>() { @Override public Integer apply(Integer key, Integer value) { return map.compute(key, this); } }; map.compute(context.absentKey(), mappingFunction); } // FIXME: Requires JDK8 release with JDK-8062841 fix @CheckNoStats @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(enabled = false, dataProvider = "caches", expectedExceptions = StackOverflowError.class) public void compute_pingpong(Map<Integer, Integer> map, CacheContext context) { BiFunction<Integer, Integer, Integer> mappingFunction = new BiFunction<Integer, Integer, Integer>() { @Override public Integer apply(Integer key, Integer value) { return map.computeIfPresent(context.lastKey(), this); } }; map.computeIfPresent(context.firstKey(), mappingFunction); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void compute_error(Map<Integer, Integer> map, CacheContext context) { try { map.compute(context.absentKey(), (key, value) -> { throw new Error(); }); } catch (Error e) {} assertThat(map, is(equalTo(context.original()))); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void compute_absent_nullValue(Map<Integer, Integer> map, CacheContext context) { assertThat(map.compute(context.absentKey(), (key, value) -> null), is(nullValue())); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); assertThat(map.get(context.absentKey()), is(nullValue())); assertThat(map.size(), is(context.original().size())); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void compute_absent(Map<Integer, Integer> map, CacheContext context) { assertThat(map.compute(context.absentKey(), (key, value) -> context.absentValue()), is(context.absentValue())); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(1)).and(hasLoadFailureCount(0))); assertThat(map.get(context.absentKey()), is(context.absentValue())); assertThat(map.size(), is(1 + context.original().size())); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void compute_sameValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.compute(key, (k, v) -> v), is(value)); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(count)).and(hasLoadFailureCount(0))); for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.get(key), is(value)); } assertThat(map.size(), is(context.original().size())); if (context.isGuava() || context.isAsync()) { assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } else { assertThat(context.consumedNotifications(), hasSize(0)); } } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void compute_differentValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.compute(key, (k, v) -> k), is(key)); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(count)).and(hasLoadFailureCount(0))); for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.get(key), is(key)); } assertThat(map.size(), is(context.original().size())); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } /* ---------------- merge -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void merge_nullKey(Map<Integer, Integer> map, CacheContext context) { map.merge(null, 1, (key, value) -> -key); } @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void merge_nullValue(Map<Integer, Integer> map, CacheContext context) { map.merge(1, null, (key, value) -> -key); } @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void merge_nullMappingFunction(Map<Integer, Integer> map, CacheContext context) { map.merge(1, 1, null); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void merge_remove(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.merge(key, value, (k, v) -> null), is(nullValue())); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(count))); assertThat(map.size(), is(context.original().size() - count)); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); } @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches") public void merge_recursive(Map<Integer, Integer> map, CacheContext context) { BiFunction<Integer, Integer, Integer> mappingFunction = new BiFunction<Integer, Integer, Integer>() { @Override public Integer apply(Integer key, Integer value) { return map.merge(key, -key, this); } }; Integer firstValue = context.original().get(context.firstKey()); Integer value = map.merge(context.absentKey(), firstValue, mappingFunction); assertThat(value, is(firstValue)); } @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = StackOverflowError.class) public void merge_pingpong(Map<Integer, Integer> map, CacheContext context) { // As we cannot provide immediate checking without an expensive solution, e.g. ThreadLocal, // instead we assert that a stack overflow error will occur to inform the developer (vs // a live-lock or deadlock alternative). BiFunction<Integer, Integer, Integer> mappingFunction = new BiFunction<Integer, Integer, Integer>() { int recursed; @Override public Integer apply(Integer key, Integer value) { if (++recursed == 2) { throw new StackOverflowError(); } return map.merge(context.lastKey(), context.original().get(context.lastKey()), this); } }; map.merge(context.firstKey(), context.original().get(context.firstKey()), mappingFunction); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Guava, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void merge_error(Map<Integer, Integer> map, CacheContext context) { try { map.merge(context.firstKey(), context.original().get(context.firstKey()), (key, value) -> { throw new Error(); }); } catch (Error e) {} assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(0)).and(hasLoadFailureCount(1))); assertThat(map, is(equalTo(context.original()))); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void merge_absent(Map<Integer, Integer> map, CacheContext context) { Integer result = map.merge(context.absentKey(), context.absentValue(), (key, value) -> value); assertThat(result, is(context.absentValue())); assertThat(map.get(context.absentKey()), is(context.absentValue())); assertThat(map.size(), is(1 + context.original().size())); } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void merge_sameValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.merge(key, -key, (k, v) -> k), is(value)); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(count)).and(hasLoadFailureCount(0))); for (Integer key : context.firstMiddleLastKeys()) { Integer value = context.original().get(key); assertThat(map.get(key), is(value)); } assertThat(map.size(), is(context.original().size())); if (context.isGuava() || context.isAsync()) { assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } else { assertThat(context.consumedNotifications(), hasSize(0)); } } @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void merge_differentValue(Map<Integer, Integer> map, CacheContext context) { for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.merge(key, key, (k, v) -> k + v), is(0)); } int count = context.firstMiddleLastKeys().size(); assertThat(context, both(hasMissCount(0)).and(hasHitCount(0))); assertThat(context, both(hasLoadSuccessCount(count)).and(hasLoadFailureCount(0))); for (Integer key : context.firstMiddleLastKeys()) { assertThat(map.get(key), is(0)); } assertThat(map.size(), is(context.original().size())); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.REPLACED)); } /* ---------------- equals / hashCode -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void equals_null(Map<Integer, Integer> map, CacheContext context) { assertThat(map.equals(null), is(false)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @SuppressWarnings("SelfEquals") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void equals_self(Map<Integer, Integer> map, CacheContext context) { assertThat(map.equals(map), is(true)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void equals(Map<Integer, Integer> map, CacheContext context) { assertThat(map.equals(context.original()), is(true)); assertThat(context.original().equals(map), is(true)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void hashCode(Map<Integer, Integer> map, CacheContext context) { assertThat(map.hashCode(), is(equalTo(context.original().hashCode()))); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void hashCode_self(Map<Integer, Integer> map, CacheContext context) { assertThat(map.hashCode(), is(equalTo(map.hashCode()))); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void equalsAndHashCodeFail_empty(Map<Integer, Integer> map, CacheContext context) { Map<Integer, Integer> other = ImmutableMap.of(1, -1, 2, -2, 3, -3); assertThat(map.equals(other), is(false)); assertThat(other.equals(map), is(false)); assertThat(map.hashCode(), is(not(equalTo(other.hashCode())))); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void equalsAndHashCodeFail_present(Map<Integer, Integer> map, CacheContext context) { Map<Integer, Integer> other = ImmutableMap.of(1, -1, 2, -2, 3, -3); assertThat(map.equals(other), is(false)); assertThat(other.equals(map), is(false)); assertThat(map.hashCode(), is(not(equalTo(other.hashCode())))); Map<Integer, Integer> empty = ImmutableMap.of(); assertThat(map.equals(empty), is(false)); assertThat(empty.equals(map), is(false)); assertThat(map.hashCode(), is(not(equalTo(empty.hashCode())))); } /* ---------------- toString -------------- */ @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void toString(Map<Integer, Integer> map, CacheContext context) { String toString = map.toString(); if (!context.original().toString().equals(toString)) { map.forEach((key, value) -> { assertThat(toString, containsString(key + "=" + value)); }); } } /* ---------------- Key Set -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void keySetToArray_null(Map<Integer, Integer> map, CacheContext context) { map.keySet().toArray(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void keySetToArray(Map<Integer, Integer> map, CacheContext context) { int length = context.original().size(); Integer[] ints = map.keySet().toArray(new Integer[length]); assertThat(ints.length, is(length)); assertThat(Arrays.asList(ints).containsAll(context.original().keySet()), is(true)); Object[] array = map.keySet().toArray(); assertThat(array.length, is(length)); assertThat(Arrays.asList(array).containsAll(context.original().keySet()), is(true)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void keySet_whenEmpty(Map<Integer, Integer> map, CacheContext context) { assertThat(map.keySet(), is(deeplyEmpty())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = UnsupportedOperationException.class) public void keySet_addNotSupported(Map<Integer, Integer> map, CacheContext context) { map.keySet().add(1); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void keySet_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.keySet().clear(); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void keySet_clear(Map<Integer, Integer> map, CacheContext context) { map.keySet().clear(); assertThat(map, is(emptyMap())); int count = context.original().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> { verifier.deletedAll(context.original(), RemovalCause.EXPLICIT); }); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void keySet(Map<Integer, Integer> map, CacheContext context) { Set<Integer> keys = map.keySet(); assertThat(keys.contains(new Object()), is(false)); assertThat(keys.remove(new Object()), is(false)); assertThat(keys, hasSize(context.original().size())); for (Integer key : keys) { assertThat(keys.contains(key), is(true)); assertThat(keys.remove(key), is(true)); assertThat(keys.remove(key), is(false)); assertThat(keys.contains(key), is(false)); } assertThat(map, is(emptyMap())); verifyWriter(context, (verifier, writer) -> verifier.deletedAll(context.original(), RemovalCause.EXPLICIT)); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void keySet_iterator(Map<Integer, Integer> map, CacheContext context) { int iterations = 0; for (Iterator<Integer> i = map.keySet().iterator(); i.hasNext();) { assertThat(map.containsKey(i.next()), is(true)); iterations++; i.remove(); } assertThat(map, hasRemovalNotifications(context, iterations, RemovalCause.EXPLICIT)); assertThat(iterations, is(context.original().size())); assertThat(map, is(emptyMap())); verifyWriter(context, (verifier, writer) -> verifier.deletedAll(context.original(), RemovalCause.EXPLICIT)); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = IllegalStateException.class) public void keyIterator_noElement(Map<Integer, Integer> map, CacheContext context) { map.keySet().iterator().remove(); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NoSuchElementException.class) public void keyIterator_noMoreElements(Map<Integer, Integer> map, CacheContext context) { map.keySet().iterator().next(); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void keyIterator_writerFails(Map<Integer, Integer> map, CacheContext context) { try { Iterator<Integer> i = map.keySet().iterator(); i.next(); i.remove(); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void keySpliterator_forEachRemaining_null( Map<Integer, Integer> map, CacheContext context) { map.keySet().spliterator().forEachRemaining(null); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void keySpliterator_forEachRemaining(Map<Integer, Integer> map, CacheContext context) { int[] count = new int[1]; map.keySet().spliterator().forEachRemaining(key -> count[0]++); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void keySpliterator_tryAdvance_null( Map<Integer, Integer> map, CacheContext context) { map.keySet().spliterator().tryAdvance(null); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void keySpliterator_tryAdvance(Map<Integer, Integer> map, CacheContext context) { Spliterator<Integer> spliterator = map.keySet().spliterator(); int[] count = new int[1]; boolean advanced; do { advanced = spliterator.tryAdvance(key -> count[0]++); } while (advanced); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void keySpliterator_trySplit(Map<Integer, Integer> map, CacheContext context) { Spliterator<Integer> spliterator = map.keySet().spliterator(); Spliterator<Integer> other = MoreObjects.firstNonNull( spliterator.trySplit(), Spliterators.emptySpliterator()); int[] count = new int[1]; spliterator.forEachRemaining(key -> count[0]++); other.forEachRemaining(key -> count[0]++); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void keySpliterator_estimateSize(Map<Integer, Integer> map, CacheContext context) { Spliterator<Integer> spliterator = map.keySet().spliterator(); assertThat((int) spliterator.estimateSize(), is(map.size())); } /* ---------------- Values -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void valuesToArray_null(Map<Integer, Integer> map, CacheContext context) { map.values().toArray(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void valuesToArray(Map<Integer, Integer> map, CacheContext context) { int length = context.original().size(); Integer[] ints = map.values().toArray(new Integer[length]); assertThat(ints.length, is(length)); assertThat(Arrays.asList(ints).containsAll(context.original().values()), is(true)); Object[] array = map.values().toArray(); assertThat(array.length, is(length)); assertThat(Arrays.asList(array).containsAll(context.original().values()), is(true)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void values_empty(Map<Integer, Integer> map, CacheContext context) { assertThat(map.values(), is(deeplyEmpty())); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = UnsupportedOperationException.class) public void values_addNotSupported(Map<Integer, Integer> map, CacheContext context) { map.values().add(1); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void values_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.values().clear(); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void values_clear(Map<Integer, Integer> map, CacheContext context) { map.values().clear(); assertThat(map, is(emptyMap())); int count = context.original().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> { verifier.deletedAll(context.original(), RemovalCause.EXPLICIT); }); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void values_removeIf_null(Map<Integer, Integer> map, CacheContext context) { map.values().removeIf(null); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void values_removeIf(Map<Integer, Integer> map, CacheContext context) { Predicate<Integer> isEven = value -> (value % 2) == 0; boolean hasEven = map.values().stream().anyMatch(isEven); boolean removedIfEven = map.values().removeIf(isEven); assertThat(map.values().stream().anyMatch(isEven), is(false)); assertThat(removedIfEven, is(hasEven)); if (removedIfEven) { assertThat(map.size(), is(lessThan(context.original().size()))); } } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void values(Map<Integer, Integer> map, CacheContext context) { Collection<Integer> values = map.values(); assertThat(values.contains(new Object()), is(false)); assertThat(values.remove(new Object()), is(false)); assertThat(values, hasSize(context.original().size())); for (Integer value : values) { assertThat(values.contains(value), is(true)); assertThat(values.remove(value), is(true)); assertThat(values.remove(value), is(false)); assertThat(values.contains(value), is(false)); } assertThat(map, is(emptyMap())); int count = context.original().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> verifier.deletedAll(context.original(), RemovalCause.EXPLICIT)); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void valueIterator(Map<Integer, Integer> map, CacheContext context) { int iterations = 0; for (Iterator<Integer> i = map.values().iterator(); i.hasNext();) { assertThat(map.containsValue(i.next()), is(true)); iterations++; i.remove(); } assertThat(map, hasRemovalNotifications(context, iterations, RemovalCause.EXPLICIT)); assertThat(iterations, is(context.original().size())); assertThat(map, is(emptyMap())); verifyWriter(context, (verifier, writer) -> verifier.deletedAll(context.original(), RemovalCause.EXPLICIT)); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = IllegalStateException.class) public void valueIterator_noElement(Map<Integer, Integer> map, CacheContext context) { map.values().iterator().remove(); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NoSuchElementException.class) public void valueIterator_noMoreElements(Map<Integer, Integer> map, CacheContext context) { map.values().iterator().next(); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void valueIterator_writerFails(Map<Integer, Integer> map, CacheContext context) { try { Iterator<Integer> i = map.values().iterator(); i.next(); i.remove(); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void valueSpliterator_forEachRemaining_null( Map<Integer, Integer> map, CacheContext context) { map.values().spliterator().forEachRemaining(null); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void valueSpliterator_forEachRemaining(Map<Integer, Integer> map, CacheContext context) { int[] count = new int[1]; map.values().spliterator().forEachRemaining(value -> count[0]++); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void valueSpliterator_tryAdvance_null( Map<Integer, Integer> map, CacheContext context) { map.values().spliterator().tryAdvance(null); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void valueSpliterator_tryAdvance(Map<Integer, Integer> map, CacheContext context) { Spliterator<Integer> spliterator = map.values().spliterator(); int[] count = new int[1]; boolean advanced; do { advanced = spliterator.tryAdvance(value -> count[0]++); } while (advanced); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void valueSpliterator_trySplit(Map<Integer, Integer> map, CacheContext context) { Spliterator<Integer> spliterator = map.values().spliterator(); Spliterator<Integer> other = MoreObjects.firstNonNull( spliterator.trySplit(), Spliterators.emptySpliterator()); int[] count = new int[1]; spliterator.forEachRemaining(value -> count[0]++); other.forEachRemaining(value -> count[0]++); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void valueSpliterator_estimateSize(Map<Integer, Integer> map, CacheContext context) { Spliterator<Integer> spliterator = map.values().spliterator(); assertThat((int) spliterator.estimateSize(), is(map.size())); } /* ---------------- Entry Set -------------- */ @CheckNoWriter @CheckNoStats @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void entrySetToArray_null(Map<Integer, Integer> map, CacheContext context) { map.entrySet().toArray(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void entriesToArray(Map<Integer, Integer> map, CacheContext context) { int length = context.original().size(); Object[] ints = map.entrySet().toArray(new Object[length]); assertThat(ints.length, is(length)); assertThat(Arrays.asList(ints).containsAll(context.original().entrySet()), is(true)); Object[] array = map.entrySet().toArray(); assertThat(array.length, is(length)); assertThat(Arrays.asList(array).containsAll(context.original().entrySet()), is(true)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) public void entrySet_empty(Map<Integer, Integer> map, CacheContext context) { assertThat(map.entrySet(), is(deeplyEmpty())); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = Listener.DEFAULT) @Test(dataProvider = "caches", expectedExceptions = UnsupportedOperationException.class) public void entrySet_addIsNotSupported(Map<Integer, Integer> map, CacheContext context) { try { map.entrySet().add(immutableEntry(1, 2)); } finally { assertThat(map.size(), is(0)); } } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void entrySet_writerFails(Map<Integer, Integer> map, CacheContext context) { try { map.entrySet().clear(); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void entrySet_clear(Map<Integer, Integer> map, CacheContext context) { map.entrySet().clear(); assertThat(map, is(emptyMap())); int count = context.original().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> { verifier.deletedAll(context.original(), RemovalCause.EXPLICIT); }); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void entrySet_removeIf_null(Map<Integer, Integer> map, CacheContext context) { map.entrySet().removeIf(null); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void entrySet_removeIf(Map<Integer, Integer> map, CacheContext context) { Predicate<Entry<Integer, Integer>> isEven = entry -> (entry.getValue() % 2) == 0; boolean hasEven = map.entrySet().stream().anyMatch(isEven); boolean removedIfEven = map.entrySet().removeIf(isEven); assertThat(map.entrySet().stream().anyMatch(isEven), is(false)); assertThat(removedIfEven, is(hasEven)); if (removedIfEven) { assertThat(map.size(), is(lessThan(context.original().size()))); } } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void entrySet(Map<Integer, Integer> map, CacheContext context) { Set<Entry<Integer, Integer>> entries = map.entrySet(); assertThat(entries.contains(new Object()), is(false)); assertThat(entries.remove(new Object()), is(false)); assertThat(entries, hasSize(context.original().size())); entries.forEach(entry -> { assertThat(entries.contains(entry), is(true)); assertThat(entries.remove(entry), is(true)); assertThat(entries.remove(entry), is(false)); assertThat(entries.contains(entry), is(false)); }); assertThat(map, is(emptyMap())); int count = context.original().size(); assertThat(map, hasRemovalNotifications(context, count, RemovalCause.EXPLICIT)); verifyWriter(context, (verifier, writer) -> verifier.deletedAll(context.original(), RemovalCause.EXPLICIT)); } @CacheSpec @CheckNoStats @Test(dataProvider = "caches") public void entryIterator(Map<Integer, Integer> map, CacheContext context) { int iterations = 0; for (Iterator<Entry<Integer, Integer>> i = map.entrySet().iterator(); i.hasNext();) { Entry<Integer, Integer> entry = i.next(); assertThat(map, hasEntry(entry.getKey(), entry.getValue())); iterations++; i.remove(); } assertThat(map, hasRemovalNotifications(context, iterations, RemovalCause.EXPLICIT)); assertThat(iterations, is(context.original().size())); assertThat(map, is(emptyMap())); verifyWriter(context, (verifier, writer) -> verifier.deletedAll(context.original(), RemovalCause.EXPLICIT)); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = IllegalStateException.class) public void entryIterator_noElement(Map<Integer, Integer> map, CacheContext context) { map.entrySet().iterator().remove(); } @CheckNoWriter @CheckNoStats @CacheSpec(population = Population.EMPTY, removalListener = { Listener.DEFAULT, Listener.REJECTING }) @Test(dataProvider = "caches", expectedExceptions = NoSuchElementException.class) public void entryIterator_noMoreElements(Map<Integer, Integer> map, CacheContext context) { map.entrySet().iterator().next(); } @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = DeleteException.class) @CacheSpec(implementation = Implementation.Caffeine, keys = ReferenceType.STRONG, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }, compute = Compute.SYNC, writer = Writer.EXCEPTIONAL, removalListener = Listener.REJECTING) public void entryIterator_writerFails(Map<Integer, Integer> map, CacheContext context) { try { Iterator<Entry<Integer, Integer>> i = map.entrySet().iterator(); i.next(); i.remove(); } finally { assertThat(map, equalTo(context.original())); } } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void entrySpliterator_forEachRemaining_null( Map<Integer, Integer> map, CacheContext context) { map.entrySet().spliterator().forEachRemaining(null); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void entrySpliterator_forEachRemaining( Map<Integer, Integer> map, CacheContext context) { int[] count = new int[1]; map.entrySet().spliterator().forEachRemaining(entry -> { if (context.isCaffeine()) { assertThat(entry, is(instanceOf(WriteThroughEntry.class))); } count[0]++; }); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) public void entrySpliterator_tryAdvance_null( Map<Integer, Integer> map, CacheContext context) { map.entrySet().spliterator().tryAdvance(null); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void entrySpliterator_tryAdvance(Map<Integer, Integer> map, CacheContext context) { Spliterator<Entry<Integer, Integer>> spliterator = map.entrySet().spliterator(); int[] count = new int[1]; boolean advanced; do { advanced = spliterator.tryAdvance(entry -> { if (context.isCaffeine()) { assertThat(entry, is(instanceOf(WriteThroughEntry.class))); } count[0]++; }); } while (advanced); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void entrySpliterator_trySplit(Map<Integer, Integer> map, CacheContext context) { Spliterator<Entry<Integer, Integer>> spliterator = map.entrySet().spliterator(); Spliterator<Entry<Integer, Integer>> other = MoreObjects.firstNonNull( spliterator.trySplit(), Spliterators.emptySpliterator()); int[] count = new int[1]; spliterator.forEachRemaining(entry -> count[0]++); other.forEachRemaining(entry -> count[0]++); assertThat(count[0], is(map.size())); } @CacheSpec @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") public void entrySpliterator_estimateSize(Map<Integer, Integer> map, CacheContext context) { Spliterator<Entry<Integer, Integer>> spliterator = map.entrySet().spliterator(); assertThat((int) spliterator.estimateSize(), is(map.size())); } /* ---------------- WriteThroughEntry -------------- */ @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void writeThroughEntry(Map<Integer, Integer> map, CacheContext context) { Entry<Integer, Integer> entry = map.entrySet().iterator().next(); entry.setValue(3); assertThat(map.get(entry.getKey()), is(3)); assertThat(map.size(), is(context.original().size())); assertThat(map, hasRemovalNotifications(context, 1, RemovalCause.REPLACED)); verifyWriter(context, (verifier, writer) -> verifier.wrote(entry.getKey(), 3)); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches", expectedExceptions = NullPointerException.class) @CacheSpec(implementation = Implementation.Caffeine, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void writeThroughEntry_null(Map<Integer, Integer> map, CacheContext context) { map.entrySet().iterator().next().setValue(null); } @CheckNoWriter @CheckNoStats @Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, population = { Population.SINGLETON, Population.PARTIAL, Population.FULL }) public void writeThroughEntry_serialize(Map<Integer, Integer> map, CacheContext context) { Entry<Integer, Integer> entry = map.entrySet().iterator().next(); Object copy = SerializableTester.reserialize(entry); assertThat(entry, is(equalTo(copy))); } }