@Test public void testBulkComputeWithReplaceEqual() throws CacheAccessException { final Map<Long, Store.ValueHolder<Value>> results = store.bulkCompute( singleton(KEY), new Function< Iterable<? extends Map.Entry<? extends Long, ? extends Value>>, Iterable<? extends Map.Entry<? extends Long, ? extends Value>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends Value>> apply( Iterable<? extends Map.Entry<? extends Long, ? extends Value>> entries) { return singletonMap(KEY, VALUE).entrySet(); } }, REPLACE_EQUAL); store.bulkCompute( singleton(KEY), new Function< Iterable<? extends Map.Entry<? extends Long, ? extends Value>>, Iterable<? extends Map.Entry<? extends Long, ? extends Value>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends Value>> apply( Iterable<? extends Map.Entry<? extends Long, ? extends Value>> entries) { compareReadValues(results.get(KEY).value(), entries.iterator().next().getValue()); return entries; } }, REPLACE_EQUAL); compareValues(VALUE, results.get(KEY).value()); }
@Test public void testBulkComputeIfAbsent() throws CacheAccessException { Map<Long, Store.ValueHolder<Value>> results = store.bulkComputeIfAbsent( singleton(KEY), new Function< Iterable<? extends Long>, Iterable<? extends Map.Entry<? extends Long, ? extends Value>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends Value>> apply( Iterable<? extends Long> longs) { return singletonMap(KEY, VALUE).entrySet(); } }); Map<Long, Store.ValueHolder<Value>> secondResults = store.bulkComputeIfAbsent( singleton(KEY), new Function< Iterable<? extends Long>, Iterable<? extends Map.Entry<? extends Long, ? extends Value>>>() { @Override public Iterable<? extends Map.Entry<? extends Long, ? extends Value>> apply( Iterable<? extends Long> longs) { fail("There should have been a mapping!"); return null; } }); compareValues(VALUE, results.get(KEY).value()); compareReadValues(results.get(KEY).value(), secondResults.get(KEY).value()); }
@Test public void testComputeWithReplaceEqual() throws CacheAccessException { final Store.ValueHolder<Value> firstValue = store.compute( KEY, new BiFunction<Long, Value, Value>() { @Override public Value apply(Long aLong, Value value) { return VALUE; } }, REPLACE_EQUAL); store.compute( KEY, new BiFunction<Long, Value, Value>() { @Override public Value apply(Long aLong, Value value) { compareReadValues(value, firstValue.value()); return value; } }, REPLACE_EQUAL); compareValues(VALUE, firstValue.value()); }
@Test public void testComputeIfPresentWithReplaceEqual() throws CacheAccessException { store.put(KEY, VALUE); final Store.ValueHolder<Value> computeValue = store.computeIfPresent( KEY, new BiFunction<Long, Value, Value>() { @Override public Value apply(Long aLong, Value value) { return value; } }, REPLACE_EQUAL); store.computeIfPresent( KEY, new BiFunction<Long, Value, Value>() { @Override public Value apply(Long aLong, Value value) { compareReadValues(computeValue.value(), value); return value; } }, REPLACE_EQUAL); compareValues(VALUE, computeValue.value()); }
@Test public void testIterator() throws CacheAccessException { store.put(KEY, VALUE); Store.Iterator<Cache.Entry<Long, Store.ValueHolder<Value>>> iterator = store.iterator(); assertThat(iterator.hasNext(), is(true)); while (iterator.hasNext()) { Cache.Entry<Long, Store.ValueHolder<Value>> entry = iterator.next(); compareValues(entry.getValue().value(), VALUE); } }
@Test public void testPutAndGet() throws CacheAccessException { store.put(KEY, VALUE); Store.ValueHolder<Value> firstStoreValue = store.get(KEY); Store.ValueHolder<Value> secondStoreValue = store.get(KEY); compareValues(VALUE, firstStoreValue.value()); compareValues(VALUE, secondStoreValue.value()); compareReadValues(firstStoreValue.value(), secondStoreValue.value()); }
@Test public void testPutNotSerializableKey() throws Exception { OnHeapStore<Serializable, Serializable> store = newStore(); try { store.put( new ArrayList<Object>() { { add(new Object()); } }, "value"); fail(); } catch (CacheAccessException cae) { assertThat(cae.getCause(), instanceOf(SerializerException.class)); } }
@Test public void testKeyUniqueObject() throws Exception { OnHeapStore<Serializable, Serializable> store = newStore(); List<String> key = new ArrayList<String>(); key.add("key"); String value = "value"; store.put((Serializable) key, value); // mutate the key -- should not affect cache key.clear(); Serializable storeKey = store.iterator().next().getKey(); if (storeKey == key || !storeKey.equals(Collections.singletonList("key"))) { throw new AssertionError(); } }
@Test public void testValueUniqueObject() throws Exception { OnHeapStore<Serializable, Serializable> store = newStore(); String key = "key"; List<String> value = new ArrayList<String>(); value.add("value"); store.put(key, (Serializable) value); // mutate the value -- should not affect cache value.clear(); ValueHolder<Serializable> valueHolder = store.get(key); if (valueHolder.value() == value || !valueHolder.value().equals(Collections.singletonList("value"))) { throw new AssertionError(); } }
@Test public void testBulkComputeStoreRemovesValueWhenFunctionReturnsNullMappings() throws Exception { Store.Configuration<Number, CharSequence> configuration = mockStoreConfig(); OnHeapStore<Number, CharSequence> store = new OnHeapStore<Number, CharSequence>(configuration, SystemTimeSource.INSTANCE, false); store.put(1, "one"); store.put(2, "two"); store.put(3, "three"); Map<Number, Store.ValueHolder<CharSequence>> result = store.bulkCompute( Arrays.asList(2, 1, 5), new Function< Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>>, Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>>>() { @Override public Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>> apply( Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>> entries) { Map<Number, CharSequence> newValues = new HashMap<Number, CharSequence>(); for (Map.Entry<? extends Number, ? extends CharSequence> entry : entries) { newValues.put(entry.getKey(), null); } return newValues.entrySet(); } }); assertThat(result.size(), is(3)); assertThat(store.get(1), is(nullValue())); assertThat(store.get(2), is(nullValue())); assertThat(store.get(3).value(), Matchers.<CharSequence>equalTo("three")); assertThat(store.get(5), is(nullValue())); }
@Test public void testKeyCopierCalledOnGetOrComputeIfAbsent() throws Exception { LongCopier keyCopier = new LongCopier(); OnHeapStore<Long, Long> store = newStore( SystemTimeSource.INSTANCE, Expirations.noExpiration(), Eviction.none(), keyCopier, new SerializingCopier<Long>( new CompactJavaSerializer<Long>(ClassLoader.getSystemClassLoader())), 100); ValueHolder<Long> computed = store.getOrComputeIfAbsent( 1L, new Function<Long, ValueHolder<Long>>() { @Override public ValueHolder<Long> apply(final Long key) { return new AbstractValueHolder<Long>(-1, -1) { @Override public Long value() { return key * 1000L; } @Override protected TimeUnit nativeTimeUnit() { return TimeUnit.MILLISECONDS; } }; } }); assertThat(computed.value(), is(1000L)); assertThat(keyCopier.copyForWriteCount, is(1)); assertThat(keyCopier.copyForReadCount, is(0)); }
@Test public void testComputeIfAbsent() throws CacheAccessException { Store.ValueHolder<Value> computedValue = store.computeIfAbsent( KEY, new Function<Long, Value>() { @Override public Value apply(Long aLong) { return VALUE; } }); Store.ValueHolder<Value> secondComputedValue = store.computeIfAbsent( KEY, new Function<Long, Value>() { @Override public Value apply(Long aLong) { fail("There should have been a mapping"); return null; } }); compareValues(VALUE, computedValue.value()); compareReadValues(computedValue.value(), secondComputedValue.value()); }
@Test public void testBulkComputeIfAbsentFunctionDoesNotGetPresentKeys() throws Exception { Store.Configuration<Number, CharSequence> configuration = mockStoreConfig(); OnHeapStore<Number, CharSequence> store = new OnHeapStore<Number, CharSequence>(configuration, SystemTimeSource.INSTANCE, false); store.put(1, "one"); store.put(2, "two"); store.put(3, "three"); Map<Number, Store.ValueHolder<CharSequence>> result = store.bulkComputeIfAbsent( Arrays.asList(1, 2, 3, 4, 5, 6), new Function< Iterable<? extends Number>, Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>>>() { @Override public Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>> apply( Iterable<? extends Number> keys) { Map<Number, CharSequence> result = new HashMap<Number, CharSequence>(); for (Number key : keys) { if (key.equals(1)) { fail(); } else if (key.equals(2)) { fail(); } else if (key.equals(3)) { fail(); } else { result.put(key, null); } } return result.entrySet(); } }); assertThat(result.size(), is(6)); assertThat(result.get(1).value(), Matchers.<CharSequence>equalTo("one")); assertThat(result.get(2).value(), Matchers.<CharSequence>equalTo("two")); assertThat(result.get(3).value(), Matchers.<CharSequence>equalTo("three")); assertThat(result.get(4), is(nullValue())); assertThat(result.get(5), is(nullValue())); assertThat(result.get(6), is(nullValue())); assertThat(store.get(1).value(), Matchers.<CharSequence>equalTo("one")); assertThat(store.get(2).value(), Matchers.<CharSequence>equalTo("two")); assertThat(store.get(3).value(), Matchers.<CharSequence>equalTo("three")); assertThat(store.get(4), is(nullValue())); assertThat(store.get(5), is(nullValue())); assertThat(store.get(6), is(nullValue())); }
@Test public void testBulkComputeHappyPath() throws Exception { Store.Configuration<Number, CharSequence> configuration = mockStoreConfig(); OnHeapStore<Number, CharSequence> store = new OnHeapStore<Number, CharSequence>(configuration, SystemTimeSource.INSTANCE, false); store.put(1, "one"); Map<Number, Store.ValueHolder<CharSequence>> result = store.bulkCompute( Arrays.asList(1, 2), new Function< Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>>, Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>>>() { @Override public Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>> apply( Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>> entries) { Map<Number, CharSequence> newValues = new HashMap<Number, CharSequence>(); for (Map.Entry<? extends Number, ? extends CharSequence> entry : entries) { if (entry.getKey().intValue() == 1) { newValues.put(entry.getKey(), "un"); } else if (entry.getKey().intValue() == 2) { newValues.put(entry.getKey(), "deux"); } } return newValues.entrySet(); } }); assertThat(result.size(), is(2)); assertThat(result.get(1).value(), Matchers.<CharSequence>equalTo("un")); assertThat(result.get(2).value(), Matchers.<CharSequence>equalTo("deux")); assertThat(store.get(1).value(), Matchers.<CharSequence>equalTo("un")); assertThat(store.get(2).value(), Matchers.<CharSequence>equalTo("deux")); }
@Test public void testBulkComputeIfAbsentDoNothingOnNullValues() throws Exception { Store.Configuration<Number, CharSequence> configuration = mockStoreConfig(); OnHeapStore<Number, CharSequence> store = new OnHeapStore<Number, CharSequence>(configuration, SystemTimeSource.INSTANCE, false); store.put(1, "one"); store.put(2, "two"); store.put(3, "three"); Map<Number, Store.ValueHolder<CharSequence>> result = store.bulkComputeIfAbsent( Arrays.asList(2, 1, 5), new Function< Iterable<? extends Number>, Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>>>() { @Override public Iterable<? extends Map.Entry<? extends Number, ? extends CharSequence>> apply( Iterable<? extends Number> numbers) { Map<Number, CharSequence> result = new HashMap<Number, CharSequence>(); for (Number key : numbers) { // 5 is a missing key, so it's the only key that is going passed to the function if (key.equals(5)) { result.put(key, null); } } Set<Number> numbersSet = new HashSet<Number>(); for (Number number : numbers) { numbersSet.add(number); } assertThat(numbersSet.size(), is(1)); assertThat(numbersSet.iterator().next(), Matchers.<Number>equalTo(5)); return result.entrySet(); } }); assertThat(result.size(), is(3)); assertThat(result.get(2).value(), Matchers.<CharSequence>equalTo("two")); assertThat(result.get(1).value(), Matchers.<CharSequence>equalTo("one")); assertThat(result.get(5), is(nullValue())); assertThat(store.get(1).value(), Matchers.<CharSequence>equalTo("one")); assertThat(store.get(2).value(), Matchers.<CharSequence>equalTo("two")); assertThat(store.get(3).value(), Matchers.<CharSequence>equalTo("three")); assertThat(store.get(5), is(nullValue())); }
@Test public void testBulkComputeFunctionGetsValuesOfEntries() throws Exception { @SuppressWarnings("rawtypes") Store.Configuration config = mock(Store.Configuration.class); when(config.getExpiry()).thenReturn(Expirations.noExpiration()); when(config.getKeyType()).thenReturn(Number.class); when(config.getValueType()).thenReturn(Number.class); Store.Configuration<Number, Number> configuration = config; OnHeapStore<Number, Number> store = new OnHeapStore<Number, Number>(configuration, SystemTimeSource.INSTANCE, false); store.put(1, 2); store.put(2, 3); store.put(3, 4); Map<Number, Store.ValueHolder<Number>> result = store.bulkCompute( Arrays.asList(1, 2, 3, 4, 5, 6), new Function< Iterable<? extends Map.Entry<? extends Number, ? extends Number>>, Iterable<? extends Map.Entry<? extends Number, ? extends Number>>>() { @Override public Iterable<? extends Map.Entry<? extends Number, ? extends Number>> apply( Iterable<? extends Map.Entry<? extends Number, ? extends Number>> entries) { Map<Number, Number> newValues = new HashMap<Number, Number>(); for (Map.Entry<? extends Number, ? extends Number> entry : entries) { final Number currentValue = entry.getValue(); if (currentValue == null) { if (entry.getKey().equals(4)) { newValues.put(entry.getKey(), null); } else { newValues.put(entry.getKey(), 0); } } else { newValues.put(entry.getKey(), currentValue.intValue() * 2); } } return newValues.entrySet(); } }); ConcurrentMap<Number, Number> check = new ConcurrentHashMap<Number, Number>(); check.put(1, 4); check.put(2, 6); check.put(3, 8); check.put(4, 0); check.put(5, 0); check.put(6, 0); assertThat(result.get(1).value(), Matchers.<Number>is(check.get(1))); assertThat(result.get(2).value(), Matchers.<Number>is(check.get(2))); assertThat(result.get(3).value(), Matchers.<Number>is(check.get(3))); assertThat(result.get(4), nullValue()); assertThat(result.get(5).value(), Matchers.<Number>is(check.get(5))); assertThat(result.get(6).value(), Matchers.<Number>is(check.get(6))); for (Number key : check.keySet()) { final Store.ValueHolder<Number> holder = store.get(key); if (holder != null) { check.remove(key, holder.value()); } } assertThat(check.size(), is(1)); assertThat(check.containsKey(4), is(true)); }