/* * 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.Status; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.StoreAccessException; import org.ehcache.StateTransitionException; import org.ehcache.core.spi.function.Function; import org.ehcache.core.spi.LifeCycled; import org.hamcrest.CoreMatchers; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.Collections; import java.util.Iterator; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @SuppressWarnings({ "unchecked", "rawtypes" }) public abstract class CacheTest { protected abstract InternalCache<Object, Object> getCache(Store<Object, Object> store); @Test public void testTransistionsState() { Store<Object, Object> store = mock(Store.class); InternalCache ehcache = getCache(store); assertThat(ehcache.getStatus(), CoreMatchers.is(Status.UNINITIALIZED)); ehcache.init(); assertThat(ehcache.getStatus(), is(Status.AVAILABLE)); ehcache.close(); assertThat(ehcache.getStatus(), is(Status.UNINITIALIZED)); } @Test public void testThrowsWhenNotAvailable() throws StoreAccessException { Store<Object, Object> store = mock(Store.class); Store.Iterator mockIterator = mock(Store.Iterator.class); when(store.iterator()).thenReturn(mockIterator); InternalCache<Object, Object> ehcache = getCache(store); try { ehcache.get("foo"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.put("foo", "bar"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.remove("foo"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.remove("foo", "bar"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.containsKey("foo"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.replace("foo", "bar"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.replace("foo", "foo", "bar"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.putIfAbsent("foo", "bar"); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.clear(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.iterator(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.getAll(Collections.singleton("foo")); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.removeAll(Collections.singleton("foo")); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { ehcache.putAll(Collections.singletonMap("foo", "bar")); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } ehcache.init(); final Iterator iterator = ehcache.iterator(); ehcache.close(); try { iterator.hasNext(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { iterator.next(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } try { iterator.remove(); fail(); } catch (IllegalStateException e) { assertThat(e.getMessage().contains(Status.UNINITIALIZED.name()), is(true)); } } @Test public void testDelegatesLifecycleCallsToStore() throws Exception { InternalCache ehcache = getCache(mock(Store.class)); final LifeCycled mock = mock(LifeCycled.class); ehcache.addHook(mock); ehcache.init(); verify(mock).init(); ehcache.close(); verify(mock).close(); } @Test public void testFailingTransitionGoesToLowestStatus() throws Exception { final LifeCycled mock = mock(LifeCycled.class); InternalCache ehcache = getCache(mock(Store.class)); doThrow(new Exception()).when(mock).init(); ehcache.addHook(mock); try { ehcache.init(); fail(); } catch (StateTransitionException e) { assertThat(ehcache.getStatus(), is(Status.UNINITIALIZED)); } reset(mock); ehcache.init(); assertThat(ehcache.getStatus(), is(Status.AVAILABLE)); ehcache.close(); } @Test public void testPutIfAbsent() throws StoreAccessException { final AtomicReference<Object> existingValue = new AtomicReference<Object>(); final Store store = mock(Store.class); final String value = "bar"; when(store.computeIfAbsent(eq("foo"), any(Function.class))).thenAnswer(new Answer<Object>() { @Override public Object answer(final InvocationOnMock invocationOnMock) throws Throwable { final Function<Object, Object> biFunction = (Function<Object, Object>)invocationOnMock.getArguments()[1]; if (existingValue.get() == null) { final Object newValue = biFunction.apply(invocationOnMock.getArguments()[0]); existingValue.compareAndSet(null, newValue); } return new Store.ValueHolder<Object>() { @Override public Object value() { return existingValue.get(); } @Override public long creationTime(final TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public long expirationTime(TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public boolean isExpired(long expirationTime, TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public long lastAccessTime(final TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public float hitRate(final long now, final TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public long hits() { throw new UnsupportedOperationException("Implement me!"); } @Override public long getId() { throw new UnsupportedOperationException("Implement me!"); } }; } }); when(store.putIfAbsent(eq("foo"), any(String.class))).then(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { final Object toReturn; if ((toReturn = existingValue.get()) == null) { existingValue.compareAndSet(null, invocation.getArguments()[1]); } return new Store.ValueHolder<Object>() { @Override public Object value() { return toReturn; } @Override public long creationTime(final TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public long expirationTime(TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public boolean isExpired(long expirationTime, TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public long lastAccessTime(final TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public float hitRate(final long now, final TimeUnit unit) { throw new UnsupportedOperationException("Implement me!"); } @Override public long hits() { throw new UnsupportedOperationException("Implement me!"); } @Override public long getId() { throw new UnsupportedOperationException("Implement me!"); } }; } }); InternalCache<Object, Object> ehcache = getCache(store); ehcache.init(); assertThat(ehcache.putIfAbsent("foo", value), nullValue()); assertThat(ehcache.putIfAbsent("foo", "foo"), CoreMatchers.<Object>is(value)); assertThat(ehcache.putIfAbsent("foo", "foobar"), CoreMatchers.<Object>is(value)); assertThat(ehcache.putIfAbsent("foo", value), CoreMatchers.<Object>is(value)); } @Test public void testInvokesHooks() { Store store = mock(Store.class); InternalCache ehcache = getCache(store); final LifeCycled hook = mock(LifeCycled.class); ehcache.addHook(hook); ehcache.init(); try { verify(hook).init(); } catch (Exception e) { fail(); } reset(hook); try { if (ehcache instanceof Ehcache) { ((Ehcache)ehcache).removeHook(hook); } else { ((EhcacheWithLoaderWriter)ehcache).removeHook(hook); } fail(); } catch (IllegalStateException e) { // expected } ehcache.close(); try { verify(hook).close(); } catch (Exception e) { fail(); } } }