/* * 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.clustered.server.store; import org.ehcache.clustered.common.internal.store.Chain; import org.ehcache.clustered.common.internal.store.Element; import org.ehcache.clustered.common.internal.store.ServerStore; import org.hamcrest.MatcherAssert; import org.hamcrest.core.Is; import org.junit.Test; import java.nio.ByteBuffer; import java.util.Iterator; import static org.junit.Assert.assertThat; import static org.hamcrest.Matchers.is; /** * Verify Server Store */ public abstract class ServerStoreTest { public abstract ServerStore newStore(); public abstract ChainBuilder newChainBuilder(); public abstract ElementBuilder newElementBuilder(); private final ChainBuilder chainBuilder = newChainBuilder(); private final ElementBuilder elementBuilder = newElementBuilder(); private static void populateStore(ServerStore store) throws Exception { for(int i = 1 ; i <= 16; i++) { store.append(i, createPayload(i)); } } private static long readPayLoad(ByteBuffer byteBuffer) { return byteBuffer.getLong(); } protected static ByteBuffer createPayload(long key) { ByteBuffer byteBuffer = ByteBuffer.allocate(8).putLong(key); byteBuffer.flip(); return byteBuffer; } private static void assertChainAndReverseChainOnlyHave(Chain chain, long... payLoads) { Iterator<Element> elements = chain.iterator(); for (long payLoad : payLoads) { assertThat(readPayLoad(elements.next().getPayload()), is(Long.valueOf(payLoad))); } assertThat(elements.hasNext(), is(false)); Iterator<Element> reverseElements = chain.reverseIterator(); for (int i = payLoads.length -1; i >= 0; i--) { assertThat(readPayLoad(reverseElements.next().getPayload()), is(Long.valueOf(payLoads[i]))); } assertThat(reverseElements.hasNext(), is(false)); } @Test public void testGetNoMappingExists() throws Exception { ServerStore store = newStore(); Chain chain = store.get(1); assertThat(chain.isEmpty(), is(true)); assertThat(chain.iterator().hasNext(), is(false)); } @Test public void testGetMappingExists() throws Exception { ServerStore store = newStore(); populateStore(store); Chain chain = store.get(1); assertThat(chain.isEmpty(), is(false)); assertChainAndReverseChainOnlyHave(chain, 1); } @Test public void testAppendNoMappingExists() throws Exception { ServerStore store = newStore(); store.append(1, createPayload(1)); Chain chain = store.get(1); assertThat(chain.isEmpty(), is(false)); assertChainAndReverseChainOnlyHave(chain, 1); } @Test public void testAppendMappingExists() throws Exception { ServerStore store = newStore(); populateStore(store); store.append(2, createPayload(22)); Chain chain = store.get(2); assertThat(chain.isEmpty(), is(false)); assertChainAndReverseChainOnlyHave(chain, 2, 22); } @Test public void testGetAndAppendNoMappingExists() throws Exception { ServerStore store = newStore(); Chain chain = store.getAndAppend(1, createPayload(1)); assertThat(chain.isEmpty(), is(true)); chain = store.get(1); assertChainAndReverseChainOnlyHave(chain, 1); } @Test public void testGetAndAppendMappingExists() throws Exception { ServerStore store = newStore(); populateStore(store); Chain chain = store.getAndAppend(1, createPayload(22)); for (Element element : chain) { assertThat(readPayLoad(element.getPayload()), is(Long.valueOf(1))); } chain = store.get(1); assertChainAndReverseChainOnlyHave(chain, 1, 22); } @Test public void testReplaceAtHeadSucceedsMappingExistsHeadMatchesStrictly() throws Exception { ServerStore store = newStore(); populateStore(store); Chain existingMapping = store.get(1); store.replaceAtHead(1, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(11)))); Chain chain = store.get(1); assertChainAndReverseChainOnlyHave(chain, 11); store.append(2, createPayload(22)); store.append(2, createPayload(222)); existingMapping = store.get(2); store.replaceAtHead(2, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(2222)))); chain = store.get(2); assertChainAndReverseChainOnlyHave(chain, 2222); } @Test public void testReplaceAtHeadSucceedsMappingExistsHeadMatches() throws Exception { ServerStore store = newStore(); populateStore(store); Chain existingMapping = store.get(1); store.append(1, createPayload(11)); store.replaceAtHead(1, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(111)))); Chain chain = store.get(1); assertChainAndReverseChainOnlyHave(chain, 111, 11); store.append(2, createPayload(22)); existingMapping = store.get(2); store.append(2, createPayload(222)); store.replaceAtHead(2, existingMapping, chainBuilder.build(elementBuilder.build(createPayload(2222)))); chain = store.get(2); assertChainAndReverseChainOnlyHave(chain, 2222, 222); } @Test public void testReplaceAtHeadIgnoredMappingExistsHeadMisMatch() throws Exception { ServerStore store = newStore(); populateStore(store); store.append(1, createPayload(11)); store.append(1, createPayload(111)); Chain mappingReadFirst = store.get(1); store.replaceAtHead(1, mappingReadFirst, chainBuilder.build(elementBuilder.build(createPayload(111)))); Chain current = store.get(1); assertChainAndReverseChainOnlyHave(current, 111); store.append(1, createPayload(1111)); store.replaceAtHead(1, mappingReadFirst, chainBuilder.build(elementBuilder.build(createPayload(11111)))); Chain toVerify = store.get(1); assertChainAndReverseChainOnlyHave(toVerify, 111, 1111); } @Test public void test_append_doesNotConsumeBuffer() throws Exception { ServerStore store = newStore(); ByteBuffer payload = createPayload(1L); store.append(1L, payload); MatcherAssert.assertThat(payload.remaining(), Is.is(8)); } @Test public void test_getAndAppend_doesNotConsumeBuffer() throws Exception { ServerStore store = newStore(); ByteBuffer payload = createPayload(1L); store.getAndAppend(1L, payload); MatcherAssert.assertThat(payload.remaining(), Is.is(8)); } @Test public void test_replaceAtHead_doesNotConsumeBuffer() throws Exception { ServerStore store = newStore(); ByteBuffer payload = createPayload(1L); Chain expected = newChainBuilder().build(newElementBuilder().build(payload), newElementBuilder().build(payload)); Chain update = newChainBuilder().build(newElementBuilder().build(payload)); store.replaceAtHead(1L, expected, update); MatcherAssert.assertThat(payload.remaining(), Is.is(8)); } }