/*
* 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.core;
import org.ehcache.Cache;
import org.ehcache.Status;
import org.ehcache.core.spi.store.StoreAccessException;
import org.ehcache.CacheIterationException;
import org.ehcache.core.spi.store.Store;
import org.ehcache.core.spi.store.Store.RemoveStatus;
import org.ehcache.spi.loaderwriter.CacheLoaderWriter;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Matchers.any;
/**
* Provides testing of basic ITERATOR operations on an {@code Ehcache}.
* <p>
* The {@link Ehcache#iterator()} tests require the use of a {@link Store Store}
* implementation which returns an {@link java.util.Iterator} from the
* {@link Store#iterator() Store.iterator} method that does <b>not</b>
* throw a {@link java.util.ConcurrentModificationException ConcurrentModificationException}.
*
* @author Clifford W. Johnson
*/
public class EhcacheBasicIteratorTest extends EhcacheBasicCrudBase {
/**
* Tests {@link Ehcache#iterator()} on an empty cache.
*/
@Test
public void testIteratorEmptyStoreGet() throws Exception {
this.store = new FakeStore(Collections.<String,String>emptyMap());
final InternalCache<String, String> ehcache = this.getEhcache();
assertThat(ehcache.iterator(), is(notNullValue()));
}
/**
* Tests {@link java.util.Iterator#hasNext()} from {@link Ehcache#iterator()} on an empty cache.
*/
@Test
public void testIteratorEmptyStoreHasNext() throws Exception {
this.store = new FakeStore(Collections.<String,String>emptyMap());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
assertThat(iterator.hasNext(), is(false));
}
/**
* Tests {@link java.util.Iterator#next()} from {@link Ehcache#iterator()} on an empty cache.
*/
@Test
public void testIteratorEmptyStoreNext() throws Exception {
this.store = new FakeStore(Collections.<String,String>emptyMap());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
try {
iterator.next();
fail();
} catch (NoSuchElementException e) {
// expected
}
}
/**
* Tests {@link java.util.Iterator#remove()} from {@link Ehcache#iterator()} on an empty cache.
*/
@Test
public void testIteratorEmptyStoreRemoveBeforeNext() throws Exception {
this.store = new FakeStore(Collections.<String,String>emptyMap());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
try {
iterator.remove();
fail();
} catch (IllegalStateException e) {
// expected
}
}
/**
* Tests {@link Ehcache#iterator()} on a non-empty cache.
*/
@Test
public void testIteratorNonEmptyStoreGet() throws Exception {
this.store = new FakeStore(this.getTestStoreEntries());
final InternalCache<String, String> ehcache = this.getEhcache();
assertThat(ehcache.iterator(), is(notNullValue()));
}
/**
* Tests {@link java.util.Iterator#hasNext()} from {@link Ehcache#iterator()} on a non-empty cache.
*/
@Test
public void testIteratorNonEmptyStoreHasNext() throws Exception {
this.store = new FakeStore(this.getTestStoreEntries());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
assertThat(iterator.hasNext(), is(true));
}
/**
* Tests {@link java.util.Iterator#next()} from {@link Ehcache#iterator()} on a non-empty cache.
*/
@Test
public void testIteratorNonEmptyStoreNext() throws Exception {
this.store = new FakeStore(this.getTestStoreEntries());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
assertThat(iterator.next(), is(notNullValue()));
}
/**
* Tests fetching all entries via an {@link Ehcache#iterator()} on a non-empty cache.
*/
@Test
public void testIteratorNonEmptyAll() throws Exception {
final Map<String, String> testStoreEntries = this.getTestStoreEntries();
this.store = new FakeStore(testStoreEntries);
final InternalCache<String, String> ehcache = this.getEhcache();
for (Cache.Entry<String, String> cacheEntry : ehcache) {
final String cacheEntryKey = cacheEntry.getKey();
assertThat(testStoreEntries, hasEntry(equalTo(cacheEntryKey), equalTo(cacheEntry.getValue())));
testStoreEntries.remove(cacheEntryKey);
}
assertThat("Iterator did not return all values", testStoreEntries.isEmpty(), is(true));
}
/**
* Tests {@link java.util.Iterator#hasNext()} <b>after</b> exhausting the {@code Iterator} returned
* from {@link Ehcache#iterator()} on a non-empty cache.
*/
@Test
public void testIteratorNonEmptyHasNextAfterLast() throws Exception {
this.store = new FakeStore(this.getTestStoreEntries());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
while (iterator.hasNext()) {
iterator.next();
}
assertThat(iterator.hasNext(), is(false));
}
/**
* Tests {@link java.util.Iterator#next()} <b>after</b> exhausting the {@code Iterator} returned
* from {@link Ehcache#iterator()} on a non-empty cache.
*/
@Test
public void testIteratorNonEmptyNextAfterLast() throws Exception {
this.store = new FakeStore(this.getTestStoreEntries());
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
while (iterator.hasNext()) {
iterator.next();
}
try {
iterator.next();
fail();
} catch (NoSuchElementException e) {
// expected
}
}
/**
* Tests the {@link java.util.Iterator} returned when the {@link Store Store}
* throws a {@link StoreAccessException StoreAccessException} from
* {@code Store.iterator}.
*/
@Test
public void testIteratorStoreAccessException() throws Exception {
@SuppressWarnings("unchecked")
Store.ValueHolder<String> valueHolder = mock(Store.ValueHolder.class);
doReturn("bar").when(valueHolder).value();
@SuppressWarnings("unchecked")
Cache.Entry<String, Store.ValueHolder<String>> storeEntry = mock(Cache.Entry.class);
doReturn(valueHolder).when(storeEntry).getValue();
doReturn("foo").when(storeEntry).getKey();
@SuppressWarnings("unchecked")
Store.Iterator<Cache.Entry<String, Store.ValueHolder<String>>> storeIterator = mock(Store.Iterator.class);
doReturn(true).when(storeIterator).hasNext();
doReturn(storeEntry).when(storeIterator).next();
doReturn(storeIterator).when(this.store).iterator();
doReturn(valueHolder).when(this.store).get(eq("foo"));
final InternalCache<String, String> ehcache = this.getEhcache();
final Iterator<Cache.Entry<String, String>> iterator = ehcache.iterator();
assertThat(iterator, is(notNullValue()));
assertThat(iterator.hasNext(), is(true));
doThrow(new StoreAccessException("")).when(storeIterator).next();
Cache.Entry<String, String> entry = iterator.next();
assertThat(entry.getKey(), is("foo"));
assertThat(entry.getValue(), is("bar"));
doThrow(new StoreAccessException("")).when(storeIterator).next();
doReturn(RemoveStatus.REMOVED).when(this.store).remove(anyString(), anyString());
try {
iterator.next();
fail();
} catch (CacheIterationException e) {
// Expected
}
assertThat(iterator.hasNext(), is(false));
try {
iterator.next();
fail();
} catch (NoSuchElementException e) {
// Expected
}
try {
iterator.remove();
} catch (Exception e) {
fail();
}
try {
iterator.remove();
fail();
} catch (IllegalStateException e) {
// Expected
}
}
protected Map<String, String> getTestStoreEntries() {
final Map<String, String> storeEntries = new HashMap<String, String>();
storeEntries.put("key1", "value1");
storeEntries.put("keyA", "valueA");
storeEntries.put("key2", "value2");
storeEntries.put("keyB", "valueB");
return storeEntries;
}
/**
* Gets an initialized {@link InternalCache Ehcache} instance using a
* mock {@link CacheLoaderWriter} instance which throws for any method called.
*
* @return a new {@code Ehcache} instance
*/
protected InternalCache<String, String> getEhcache() throws Exception {
final Ehcache<String, String> ehcache = new Ehcache<String, String>(CACHE_CONFIGURATION, this.store, cacheEventDispatcher, LoggerFactory.getLogger(Ehcache.class + "-" + "EhcacheBasicIteratorTest"));
ehcache.init();
assertThat("cache not initialized", ehcache.getStatus(), Matchers.is(Status.AVAILABLE));
this.spiedResilienceStrategy = this.setResilienceStrategySpy(ehcache);
return ehcache;
}
@SuppressWarnings("unchecked")
static Iterable<? extends String> getAnyStringIterable() {
return any(Iterable.class);
}
@SuppressWarnings("unchecked")
static Iterable<? extends Map.Entry<? extends String, ? extends String>> getAnyMapEntryIterable() {
return any(Iterable.class);
}
}