/*
* 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();
}
}