/*
* Copyright Terracotta, Inc.
*
* 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 org.ehcache.integration.statistics;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.stream.StreamSupport;
import org.assertj.core.data.MapEntry;
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.core.spi.service.StatisticsService;
import org.ehcache.impl.config.persistence.DefaultPersistenceConfiguration;
import org.ehcache.impl.internal.statistics.DefaultStatisticsService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Check that calculations are accurate according to specification. Each cache method have a different impact on the statistics
* so each method should be tested
*/
public class CacheCalculationTest extends AbstractCacheCalculationTest {
private CacheManager cacheManager;
private Cache<Integer, String> cache;
public CacheCalculationTest(ResourcePoolsBuilder poolBuilder) {
super(poolBuilder);
}
@Before
public void before() throws Exception {
CacheConfiguration<Integer, String> cacheConfiguration =
CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, String.class, resources).build();
StatisticsService statisticsService = new DefaultStatisticsService();
cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("cache", cacheConfiguration)
.using(new DefaultPersistenceConfiguration(diskPath.newFolder()))
.using(statisticsService)
.build(true);
cache = cacheManager.getCache("cache", Integer.class, String.class);
cacheStatistics = statisticsService.getCacheStatistics("cache");
}
@After
public void after() {
if(cacheManager != null) {
cacheManager.close();
}
}
@Test
public void clear() {
cache.put(1, "a");
cache.put(2, "b");
changesOf(0, 0, 2, 0, 0);
cache.clear();
changesOf(0, 0, 0, 0, 0);
}
@Test
public void containsKey() {
expect(cache.containsKey(1)).isFalse();
changesOf(0, 0, 0, 0, 0);
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
expect(cache.containsKey(1)).isTrue();
changesOf(0, 0, 0, 0, 0);
}
@Test
public void get() {
expect(cache.get(1)).isNull();
changesOf(0, 1, 0, 0, 0);
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
expect(cache.get(1)).isEqualTo("a");
changesOf(1, 0, 0, 0, 0);
}
@Test
public void getAll() {
expect(cache.getAll(asSet(1))).containsExactly(MapEntry.entry(1, null));
changesOf(0, 1, 0, 0, 0);
cache.put(1, "a");
cache.put(2, "b");
changesOf(0, 0, 2, 0, 0);
expect(cache.getAll(asSet(1, 2, 3))).containsKeys(1, 2);
changesOf(2, 1, 0, 0, 0);
}
@Test
public void iterator() {
cache.put(1, "a");
cache.put(2, "b");
changesOf(0, 0, 2, 0, 0);
Iterator<Cache.Entry<Integer, String>> iterator = cache.iterator();
changesOf(1, 0, 0, 0, 0); // FIXME Why one?!?
iterator.next().getKey();
changesOf(2, 0, 0, 0, 0); // FIXME Why two?!?
expect(iterator.hasNext()).isTrue();
changesOf(0, 0, 0, 0, 0);
iterator.next().getKey();
changesOf(1, 0, 0, 0, 0);
expect(iterator.hasNext()).isFalse();
changesOf(0, 0, 0, 0, 0);
iterator.remove();
changesOf(1, 0, 0, 1, 0); // FIXME remove does hit
}
@Test
public void foreach() {
cache.put(1, "a");
cache.put(2, "b");
cache.put(3, "c");
changesOf(0, 0, 3, 0, 0);
cache.forEach(e -> {});
changesOf(6, 0, 0, 0, 0); // FIXME counted twice but works for JCache
}
@Test
public void spliterator() {
cache.put(1, "a");
cache.put(2, "b");
cache.put(3, "c");
changesOf(0, 0, 3, 0, 0);
StreamSupport.stream(cache.spliterator(), false).forEach(e -> {});
changesOf(6, 0, 0, 0, 0); // FIXME counted twice but works for JCache
}
@Test
public void put() {
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
cache.put(1, "b");
changesOf(0, 0, 1, 0, 1);
}
@Test
public void putAll() {
Map<Integer, String> vals = new HashMap<Integer, String>();
vals.put(1, "a");
vals.put(2, "b");
cache.putAll(vals);
changesOf(0, 0, 2, 0, 0);
vals.put(3, "c");
cache.putAll(vals);
changesOf(0, 0, 3, 0, 2);
}
@Test
public void putIfAbsent() {
expect(cache.putIfAbsent(1, "a")).isNull();
changesOf(0, 1, 1, 0, 0);
expect(cache.putIfAbsent(1, "b")).isEqualTo("a");
changesOf(1, 0, 0, 0, 0);
}
@Test
public void remove() {
cache.remove(1);
changesOf(0, 0, 0, 0, 0);
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
cache.remove(1);
changesOf(0, 0, 0, 1, 0);
}
@Test
public void removeKV() {
expect(cache.remove(1, "a")).isFalse();
changesOf(0, 1, 0, 0, 0);
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
expect(cache.remove(1, "xxx")).isFalse();
changesOf(1, 0, 0, 0, 0);
expect(cache.remove(1, "a")).isTrue();
changesOf(1, 0, 0, 1, 0);
}
@Test
public void removeAllKeys() {
cache.put(1, "a");
cache.put(2, "b");
changesOf(0, 0, 2, 0, 0);
cache.removeAll(asSet(1, 2, 3));
changesOf(0, 0, 0, 2, 0);
}
@Test
public void replaceKV() {
expect(cache.replace(1, "a")).isNull();
changesOf(0, 1, 0, 0, 0);
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
expect(cache.replace(1, "b")).isEqualTo("a");
changesOf(1, 0, 1, 0, 1);
}
@Test
public void replaceKON() {
expect(cache.replace(1, "a", "b")).isFalse();
changesOf(0, 1, 0, 0, 0);
cache.put(1, "a");
changesOf(0, 0, 1, 0, 0);
expect(cache.replace(1, "xxx", "b")).isFalse();
changesOf(1, 0, 0, 0, 0);
expect(cache.replace(1, "a", "b")).isTrue();
changesOf(1, 0, 1, 0, 1);
}
@Test
public void testClearingStats() {
// We do it twice because the second time we already have compensating counters, so the result might fail
innerClear();
innerClear();
}
private void innerClear() {
cache.get(1); // one miss
cache.getAll(asSet(1, 2, 3)); // 3 misses
cache.put(1, "a"); // one put
cache.put(1, "b"); // one put and update
cache.putAll(Collections.singletonMap(2, "b")); // 1 put
cache.get(1); // one hit
cache.remove(1); // one remove
cache.removeAll(asSet(2)); // one remove
changesOf(1, 4, 3, 2, 1);
cacheStatistics.clear();
changesOf(-1, -4, -3, -2, -1);
}
@Test
public void testEviction() {
String payload = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
// Wait until we reach the maximum that we can fit in
int i = 0;
long evictions;
do {
cache.put(i++, payload);
evictions = cacheStatistics.getCacheEvictions();
}
while(evictions == 0 && i < 100_000);
assertThat(evictions).isGreaterThan(0);
}
}