/* * Copyright 2015 Ben Manes. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.github.benmanes.caffeine.cache; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Mockito.verify; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.github.benmanes.caffeine.cache.Policy.Eviction; import com.github.benmanes.caffeine.cache.Policy.Expiration; import com.github.benmanes.caffeine.cache.stats.StatsCounter; import com.google.common.testing.FakeTicker; import com.google.common.util.concurrent.MoreExecutors; /** * A test for the builder methods. * * @author ben.manes@gmail.com (Ben Manes) */ public final class CaffeineTest { @Mock StatsCounter statsCounter; @Mock Expiry<Object, Object> expiry; @Mock CacheLoader<Object, Object> loader; @Mock CacheWriter<Object, Object> writer; @BeforeClass public void beforeClass() { MockitoAnnotations.initMocks(this); } @Test public void unconfigured() { assertThat(Caffeine.newBuilder().build(), is(not(nullValue()))); assertThat(Caffeine.newBuilder().build(loader), is(not(nullValue()))); assertThat(Caffeine.newBuilder().buildAsync(loader), is(not(nullValue()))); assertThat(Caffeine.newBuilder().toString(), is(Caffeine.newBuilder().toString())); } @Test public void configured() { Caffeine<Object, Object> configured = Caffeine.newBuilder() .initialCapacity(1).weakKeys().softValues() .expireAfterAccess(1, TimeUnit.SECONDS).expireAfterWrite(1, TimeUnit.SECONDS) .removalListener((k, v, c) -> {}).recordStats(); assertThat(configured.build(), is(not(nullValue()))); assertThat(configured.build(loader), is(not(nullValue()))); assertThat(Caffeine.newBuilder().buildAsync(loader), is(not(nullValue()))); assertThat(configured.refreshAfterWrite(1, TimeUnit.SECONDS).toString(), is(not(Caffeine.newBuilder().toString()))); assertThat(Caffeine.newBuilder().maximumSize(1).toString(), is(not(Caffeine.newBuilder().maximumWeight(1).toString()))); } @Test(expectedExceptions = NullPointerException.class) public void fromSpec_null() { Caffeine.from((CaffeineSpec) null); } @Test public void fromSpec_lenientParsing() { Caffeine.from(CaffeineSpec.parse("maximumSize=100")).weigher((k, v) -> 0).build(); } @Test public void fromSpec() { assertThat(Caffeine.from(CaffeineSpec.parse("")), is(not(nullValue()))); } @Test(expectedExceptions = NullPointerException.class) public void fromString_null() { Caffeine.from((String) null); } @Test public void fromString_lenientParsing() { Caffeine.from("maximumSize=100").weigher((k, v) -> 0).build(); } @Test public void fromString() { assertThat(Caffeine.from(""), is(not(nullValue()))); } /* ---------------- loading -------------- */ @Test(expectedExceptions = NullPointerException.class) public void loading_nullLoader() { Caffeine.newBuilder().build(null); } /* ---------------- async -------------- */ @Test public void async_nullLoader() { try { Caffeine.newBuilder().buildAsync((CacheLoader<Object, Object>) null); Assert.fail(); } catch (NullPointerException expected) {} try { Caffeine.newBuilder().buildAsync((AsyncCacheLoader<Object, Object>) null); Assert.fail(); } catch (NullPointerException expected) {} } @Test public void async_asyncLoader() { Caffeine.newBuilder().buildAsync(loader::asyncLoad); } @Test(expectedExceptions = IllegalStateException.class) public void async_weakValues() { Caffeine.newBuilder().weakValues().buildAsync(loader); } @Test(expectedExceptions = IllegalStateException.class) public void async_softValues() { Caffeine.newBuilder().softValues().buildAsync(loader); } @Test(expectedExceptions = IllegalStateException.class) public void async_writer() { Caffeine.newBuilder().writer(writer).buildAsync(loader); } /* ---------------- initialCapacity -------------- */ @Test(expectedExceptions = IllegalArgumentException.class) public void initialCapacity_negative() { Caffeine.newBuilder().initialCapacity(-1); } @Test(expectedExceptions = IllegalStateException.class) public void initialCapacity_twice() { Caffeine.newBuilder().initialCapacity(1).initialCapacity(1); } @Test public void initialCapacity_small() { // can't check, so just assert that it builds Caffeine<?, ?> builder = Caffeine.newBuilder().initialCapacity(0); assertThat(builder.initialCapacity, is(0)); builder.build(); } @Test public void initialCapacity_large() { // don't build! just check that it configures Caffeine<?, ?> builder = Caffeine.newBuilder().initialCapacity(Integer.MAX_VALUE); assertThat(builder.initialCapacity, is(Integer.MAX_VALUE)); } /* ---------------- maximumSize -------------- */ @Test(expectedExceptions = IllegalArgumentException.class) public void maximumSize_negative() { Caffeine.newBuilder().maximumSize(-1); } @Test(expectedExceptions = IllegalStateException.class) public void maximumSize_twice() { Caffeine.newBuilder().maximumSize(1).maximumSize(1); } @Test(expectedExceptions = IllegalStateException.class) public void maximumSize_maximumWeight() { Caffeine.newBuilder().maximumWeight(1).maximumSize(1); } @Test(expectedExceptions = IllegalStateException.class) public void maximumSize_weigher() { Caffeine.newBuilder().weigher(Weigher.singletonWeigher()).maximumSize(1); } @Test public void maximumSize_small() { Caffeine<?, ?> builder = Caffeine.newBuilder().maximumSize(0); assertThat(builder.maximumSize, is(0L)); Cache<?, ?> cache = builder.build(); assertThat(cache.policy().eviction().get().getMaximum(), is(0L)); } @Test public void maximumSize_large() { Caffeine<?, ?> builder = Caffeine.newBuilder().maximumSize(Integer.MAX_VALUE); assertThat(builder.maximumSize, is((long) Integer.MAX_VALUE)); Cache<?, ?> cache = builder.build(); assertThat(cache.policy().eviction().get().getMaximum(), is((long) Integer.MAX_VALUE)); } /* ---------------- maximumWeight -------------- */ @Test(expectedExceptions = IllegalArgumentException.class) public void maximumWeight_negative() { Caffeine.newBuilder().maximumWeight(-1); } @Test(expectedExceptions = IllegalStateException.class) public void maximumWeight_twice() { Caffeine.newBuilder().maximumWeight(1).maximumWeight(1); } @Test(expectedExceptions = IllegalStateException.class) public void maximumWeight_noWeigher() { Caffeine.newBuilder().maximumWeight(1).build(); } @Test(expectedExceptions = IllegalStateException.class) public void maximumWeight_maximumSize() { Caffeine.newBuilder().maximumSize(1).maximumWeight(1); } @Test public void maximumWeight_small() { Caffeine<?, ?> builder = Caffeine.newBuilder() .maximumWeight(0).weigher(Weigher.singletonWeigher()); assertThat(builder.weigher, is(Weigher.singletonWeigher())); assertThat(builder.maximumWeight, is(0L)); Eviction<?, ?> eviction = builder.build().policy().eviction().get(); assertThat(eviction.getMaximum(), is(0L)); assertThat(eviction.isWeighted(), is(true)); } @Test public void maximumWeight_large() { Caffeine<?, ?> builder = Caffeine.newBuilder() .maximumWeight(Integer.MAX_VALUE).weigher(Weigher.singletonWeigher()); assertThat(builder.maximumWeight, is((long) Integer.MAX_VALUE)); assertThat(builder.weigher, is(Weigher.singletonWeigher())); Eviction<?, ?> eviction = builder.build().policy().eviction().get(); assertThat(eviction.getMaximum(), is((long) Integer.MAX_VALUE)); assertThat(eviction.isWeighted(), is(true)); } /* ---------------- weigher -------------- */ @Test(expectedExceptions = NullPointerException.class) public void weigher_null() { Caffeine.newBuilder().weigher(null); } @Test(expectedExceptions = IllegalStateException.class) public void weigher_twice() { Caffeine.newBuilder().weigher(Weigher.singletonWeigher()).weigher(Weigher.singletonWeigher()); } @Test(expectedExceptions = IllegalStateException.class) public void weigher_maximumSize() { Caffeine.newBuilder().maximumSize(1).weigher(Weigher.singletonWeigher()); } @Test(expectedExceptions = IllegalStateException.class) public void weigher_noMaximumWeight() { Caffeine.newBuilder().weigher(Weigher.singletonWeigher()).build(); } @Test public void weigher() { Weigher<Object, Object> weigher = (k, v) -> 0; Caffeine<?, ?> builder = Caffeine.newBuilder().maximumWeight(0).weigher(weigher); assertThat(builder.weigher, is(sameInstance(weigher))); builder.build(); } /* ---------------- expireAfterAccess -------------- */ @Test(expectedExceptions = IllegalArgumentException.class) public void expireAfterAccess_negative() { Caffeine.newBuilder().expireAfterAccess(-1, TimeUnit.MILLISECONDS); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfterAccess_expiry() { Caffeine.newBuilder().expireAfter(expiry).expireAfterAccess(1, TimeUnit.MILLISECONDS); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfterAccess_twice() { Caffeine.newBuilder().expireAfterAccess(1, TimeUnit.MILLISECONDS) .expireAfterAccess(1, TimeUnit.MILLISECONDS); } @Test public void expireAfterAccess_small() { Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfterAccess(0, TimeUnit.MILLISECONDS); assertThat(builder.expireAfterAccessNanos, is(0L)); Expiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get(); assertThat(expiration.getExpiresAfter(TimeUnit.MILLISECONDS), is(0L)); } @Test public void expireAfterAccess_large() { Caffeine<?, ?> builder = Caffeine.newBuilder() .expireAfterAccess(Integer.MAX_VALUE, TimeUnit.NANOSECONDS); assertThat(builder.expireAfterAccessNanos, is((long) Integer.MAX_VALUE)); Expiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get(); assertThat(expiration.getExpiresAfter(TimeUnit.NANOSECONDS), is((long) Integer.MAX_VALUE)); } /* ---------------- expireAfterWrite -------------- */ @Test(expectedExceptions = IllegalArgumentException.class) public void expireAfterWrite_negative() { Caffeine.newBuilder().expireAfterWrite(-1, TimeUnit.MILLISECONDS); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfterWrite_expiry() { Caffeine.newBuilder().expireAfter(expiry).expireAfterWrite(1, TimeUnit.MILLISECONDS); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfterWrite_twice() { Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MILLISECONDS) .expireAfterWrite(1, TimeUnit.MILLISECONDS); } @Test public void expireAfterWrite_small() { Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfterWrite(0, TimeUnit.MILLISECONDS); assertThat(builder.expireAfterWriteNanos, is(0L)); Expiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get(); assertThat(expiration.getExpiresAfter(TimeUnit.MILLISECONDS), is(0L)); } @Test public void expireAfterWrite_large() { Caffeine<?, ?> builder = Caffeine.newBuilder() .expireAfterWrite(Integer.MAX_VALUE, TimeUnit.NANOSECONDS); assertThat(builder.expireAfterWriteNanos, is((long) Integer.MAX_VALUE)); Expiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get(); assertThat(expiration.getExpiresAfter(TimeUnit.NANOSECONDS), is((long) Integer.MAX_VALUE)); } /* ---------------- expiry -------------- */ @Test(expectedExceptions = NullPointerException.class) public void expireAfter_null() { Caffeine.newBuilder().expireAfter(null); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfter_twice() { Caffeine.newBuilder().expireAfter(expiry).expireAfter(expiry); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfter_access() { Caffeine.newBuilder().expireAfterAccess(1, TimeUnit.MILLISECONDS).expireAfter(expiry); } @Test(expectedExceptions = IllegalStateException.class) public void expireAfter_write() { Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MILLISECONDS).expireAfter(expiry); } @Test public void expireAfter() { Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfter(expiry); assertThat(builder.expiry, is(expiry)); builder.build(); } /* ---------------- refreshAfterWrite -------------- */ @Test(expectedExceptions = IllegalStateException.class) public void refreshAfterWrite_twice() { Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MILLISECONDS) .refreshAfterWrite(1, TimeUnit.MILLISECONDS); } @Test(expectedExceptions = IllegalStateException.class) public void refreshAfterWrite_noCacheLoader() { Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MILLISECONDS).build(); } @Test(expectedExceptions = IllegalArgumentException.class) public void refreshAfterWrite_zero() { Caffeine.newBuilder().refreshAfterWrite(0, TimeUnit.MILLISECONDS); } @Test public void refreshAfterWrite() { Caffeine<Object, Object> builder = Caffeine.newBuilder() .refreshAfterWrite(1, TimeUnit.MILLISECONDS); assertThat(builder.getRefreshAfterWriteNanos(), is(TimeUnit.MILLISECONDS.toNanos(1))); builder.build(k -> k); } /* ---------------- weakKeys -------------- */ @Test(expectedExceptions = IllegalStateException.class) public void weakKeys_twice() { Caffeine.newBuilder().weakKeys().weakKeys(); } @Test(expectedExceptions = IllegalStateException.class) public void weakKeys_writer() { Caffeine.newBuilder().writer(writer).weakKeys(); } @Test public void weakKeys() { Caffeine.newBuilder().weakKeys().build(); } /* ---------------- weakValues -------------- */ @Test(expectedExceptions = IllegalStateException.class) public void weakValues_twice() { Caffeine.newBuilder().weakValues().weakValues(); } @Test public void weakValues() { Caffeine.newBuilder().weakValues().build(); } /* ---------------- softValues -------------- */ @Test(expectedExceptions = IllegalStateException.class) public void softValues_twice() { Caffeine.newBuilder().softValues().softValues(); } @Test public void softValues() { Caffeine.newBuilder().softValues().build(); } /* ---------------- executor -------------- */ @Test(expectedExceptions = NullPointerException.class) public void executor_null() { Caffeine.newBuilder().executor(null); } @Test(expectedExceptions = IllegalStateException.class) public void executor_twice() { Caffeine.newBuilder().executor(MoreExecutors.directExecutor()) .executor(MoreExecutors.directExecutor()); } @Test public void executor() { Caffeine<?, ?> builder = Caffeine.newBuilder().executor(MoreExecutors.directExecutor()); assertThat(builder.getExecutor(), is(MoreExecutors.directExecutor())); builder.build(); } /* ---------------- ticker -------------- */ @Test(expectedExceptions = NullPointerException.class) public void ticker_null() { Caffeine.newBuilder().ticker(null); } @Test(expectedExceptions = IllegalStateException.class) public void ticker_twice() { Caffeine.newBuilder().ticker(Ticker.systemTicker()).ticker(Ticker.systemTicker()); } @Test public void ticker() { Ticker ticker = new FakeTicker()::read; Caffeine<?, ?> builder = Caffeine.newBuilder().ticker(ticker); assertThat(builder.ticker, is(ticker)); builder.build(); } /* ---------------- stats -------------- */ @Test(expectedExceptions = NullPointerException.class) public void recordStats_null() { Caffeine.newBuilder().recordStats(null); } @Test public void recordStats_twice() { Supplier<StatsCounter> supplier = () -> statsCounter; Runnable[] tasks = { () -> Caffeine.newBuilder().recordStats().recordStats(), () -> Caffeine.newBuilder().recordStats(supplier).recordStats(), () -> Caffeine.newBuilder().recordStats().recordStats(supplier), () -> Caffeine.newBuilder().recordStats(supplier).recordStats(supplier), }; for (Runnable task : tasks) { try { task.run(); Assert.fail(); } catch (IllegalStateException expected) {} } } @Test public void recordStats() { Caffeine<?, ?> builder = Caffeine.newBuilder().recordStats(); assertThat(builder.statsCounterSupplier, is(Caffeine.ENABLED_STATS_COUNTER_SUPPLIER)); builder.build(); } @Test public void recordStats_custom() { Supplier<StatsCounter> supplier = () -> statsCounter; Caffeine<?, ?> builder = Caffeine.newBuilder().recordStats(supplier); builder.statsCounterSupplier.get().recordEviction(1); verify(statsCounter).recordEviction(1); builder.build(); } /* ---------------- removalListener -------------- */ @Test(expectedExceptions = NullPointerException.class) public void removalListener_null() { Caffeine.newBuilder().removalListener(null); } @Test(expectedExceptions = IllegalStateException.class) public void removalListener_twice() { Caffeine.newBuilder().removalListener((k, v, c) -> {}).removalListener((k, v, c) -> {}); } @Test public void removalListener() { RemovalListener<Object, Object> removalListener = (k, v, c) -> {}; Caffeine<?, ?> builder = Caffeine.newBuilder().removalListener(removalListener); assertThat(builder.getRemovalListener(false), is(removalListener)); builder.build(); } /* ---------------- cacheWriter -------------- */ @Test(expectedExceptions = NullPointerException.class) public void writer_null() { Caffeine.newBuilder().writer(null); } @Test(expectedExceptions = IllegalStateException.class) public void writer_twice() { Caffeine.newBuilder().writer(writer).writer(writer); } @Test(expectedExceptions = IllegalStateException.class) public void writer_weakKeys() { Caffeine.newBuilder().writer(writer).weakKeys(); } @Test public void writer() { Caffeine<?, ?> builder = Caffeine.newBuilder().writer(writer); assertThat(builder.getCacheWriter(), is(writer)); builder.build(); } }