package voldemort.store.views; import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Set; import junit.framework.TestCase; import voldemort.serialization.Serializer; import voldemort.serialization.StringSerializer; import voldemort.serialization.json.JsonTypeSerializer; import voldemort.store.NoSuchCapabilityException; import voldemort.store.Store; import voldemort.store.StoreCapabilityType; import voldemort.store.memory.InMemoryStorageEngine; import voldemort.store.serialized.SerializingStore; import voldemort.utils.ByteArray; import voldemort.versioning.VectorClock; import voldemort.versioning.Version; import voldemort.versioning.Versioned; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; /** * Test cases for views * * */ public class ViewStorageEngineTest extends TestCase { private AddStrViewTrans transform = new AddStrViewTrans("42"); private InMemoryStorageEngine<ByteArray, byte[], byte[]> targetRaw1 = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("target1"); private Store<String, String, String> target1 = SerializingStore.wrap(targetRaw1, new StringSerializer(), new StringSerializer(), new StringSerializer()); private Store<String, String, String> valView = getEngine1(transform); private Serializer<Integer> keySer = new IntegerSerializer(); private Serializer<List<Integer>> valueSer = new IntegerListSerializer(); private Serializer<List<Integer>> transSer = new IntegerListSerializer(); private InMemoryStorageEngine<ByteArray, byte[], byte[]> targetRaw2 = new InMemoryStorageEngine<ByteArray, byte[], byte[]>("target2"); private Store<Integer, List<Integer>, List<Integer>> target2 = SerializingStore.wrap(targetRaw2, keySer, valueSer, transSer); private Store<Integer, List<Integer>, List<Integer>> view = getEngine2(new RangeFilterView()); @Override public void setUp() { target1.put("hello", Versioned.value("world"), null); Integer[] values1 = { 1, 2, 3, 4, 5, 6, 7, 8 }; Integer[] values2 = { 100, 200, 300, 400, 500, 600, 700 }; target2.put(1, Versioned.value(Arrays.asList(values1)), null); target2.put(100, Versioned.value(Arrays.asList(values2)), null); } public Store<String, String, String> getEngine1(View<?, ?, ?, ?> valTrans) { Serializer<String> s = new StringSerializer(); return SerializingStore.wrap(new ViewStorageEngine("test", targetRaw1, s, s, s, s, null, valTrans), s, s, s); } public Store<Integer, List<Integer>, List<Integer>> getEngine2(View<?, ?, ?, ?> view) { return SerializingStore.wrap(new ViewStorageEngine("transTest", targetRaw2, valueSer, transSer, keySer, valueSer, null, view), keySer, valueSer, transSer); } public void testGetWithValueTransform() { assertEquals("View should add 42", "world42", valView.get("hello", "concat") .get(0) .getValue()); assertEquals("Null value should return empty list", 0, valView.get("laksjdf", "concat") .size()); } public void testGetAll() { target1.put("a", Versioned.value("a"), null); target1.put("b", Versioned.value("b"), null); Map<String, List<Versioned<String>>> found = valView.getAll(ImmutableList.of("a", "b"), ImmutableMap.of("a", "concat", "b", "concat")); assertTrue(found.containsKey("a")); assertTrue(found.containsKey("b")); assertEquals("a42", found.get("a").get(0).getValue()); assertEquals("b42", found.get("b").get(0).getValue()); } public void testPut() { valView.put("abc", Versioned.value("cde"), null); assertEquals("c", target1.get("abc", null).get(0).getValue()); } public void testGetWithTransforms() { Integer[] filter2 = { 5, 8 }; assertEquals(4, view.get(1, Arrays.asList(filter2)).get(0).getValue().size()); Integer[] filter1 = { 1, 5 }; assertEquals(5, view.get(1, Arrays.asList(filter1)).get(0).getValue().size()); } public void testPutWithTransforms() { Integer[] values1 = { 9, 90, 10, 15, 25, 106 }; Integer[] filter1 = { 1, 10 }; Versioned<List<Integer>> values = Versioned.value(Arrays.asList(values1)); VectorClock clock = (VectorClock) values.getVersion(); clock.incrementVersion(0, System.currentTimeMillis()); view.put(1, Versioned.value(values.getValue(), clock), Arrays.asList(filter1)); assertEquals(10, view.get(1, Arrays.asList(filter1)).get(0).getValue().size()); Integer[] filter2 = { 5, 10 }; assertEquals(6, view.get(1, Arrays.asList(filter2)).get(0).getValue().size()); Version updatedVersion = view.get(1, Arrays.asList(filter2)).get(0).getVersion(); Integer[] filter3 = { 1, 50 }; Integer[] values2 = { 90, 15, 25, 106 }; clock = (VectorClock) updatedVersion; VectorClock clock1 = clock.incremented(0, System.currentTimeMillis()); view.put(1, Versioned.value(Arrays.asList(values2), clock1), Arrays.asList(filter3)); assertEquals(12, view.get(1, Arrays.asList(filter3)).get(0).getValue().size()); } public void testGetCapabilityThrowsNoSuchCapabilityException() { Set<StoreCapabilityType> supportedCapabilities = EnumSet.of(StoreCapabilityType.VIEW_TARGET); for (StoreCapabilityType capabilityType : StoreCapabilityType.values()) { if (supportedCapabilities.contains(capabilityType)) { assertNotNull("unexpected null value returned for StoreCapabilityType." + capabilityType, view.getCapability(capabilityType)); } else { try { view.getCapability(StoreCapabilityType.DISABLE_STORE_VERSION); fail("Expected NoSuchCapabilityException to be thrown for StoreCapabilityType." + capabilityType); } catch (NoSuchCapabilityException e) { // expected } } } } /* A view that just adds or subtracts the given string */ private static class AddStrViewTrans implements View<String, String, String, String> { private String str; public AddStrViewTrans(String str) { this.str = str; } public String storeToView(Store<String, String, String> store, String k, String s, String t) { if(s == null) return str; else if(t != null && t.equalsIgnoreCase("concat")) return s + str; return str; } public String viewToStore(Store<String, String, String> store, String k, String v, String t) { if(v == null) return null; else return v.substring(0, Math.max(0, v.length() - str.length())); } } public static class IntegerListSerializer implements Serializer<List<Integer>> { private JsonTypeSerializer serializer; public IntegerListSerializer() { this.serializer = new JsonTypeSerializer("[\"int32\"]"); } public byte[] toBytes(List<Integer> object) { return serializer.toBytes(object); } @SuppressWarnings("unchecked") public List<Integer> toObject(byte[] bytes) { return (List<Integer>) serializer.toObject(bytes); } } private static class IntegerSerializer implements Serializer<Integer> { private JsonTypeSerializer serializer; public IntegerSerializer() { this.serializer = new JsonTypeSerializer("\"int32\""); } public byte[] toBytes(Integer object) { return serializer.toBytes(object); } public Integer toObject(byte[] bytes) { return (Integer) serializer.toObject(bytes); } } }