@Test
 public void testFlushUpdatesAccessStats() throws CacheAccessException {
   final TestTimeSource timeSource = new TestTimeSource();
   final Expiry<Object, Object> expiry =
       Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS));
   final AbstractOffHeapStore<String, String> store = createAndInitStore(timeSource, expiry);
   try {
     final String key = "foo";
     final String value = "bar";
     store.put(key, value);
     final Store.ValueHolder<String> firstValueHolder = store.getAndFault(key);
     store.put(key, value);
     final Store.ValueHolder<String> secondValueHolder = store.getAndFault(key);
     timeSource.advanceTime(10);
     ((AbstractValueHolder) firstValueHolder)
         .accessed(timeSource.getTimeMillis(), expiry.getExpiryForAccess(key, value));
     timeSource.advanceTime(10);
     ((AbstractValueHolder) secondValueHolder)
         .accessed(timeSource.getTimeMillis(), expiry.getExpiryForAccess(key, value));
     assertThat(store.flush(key, new DelegatingValueHolder<String>(firstValueHolder)), is(false));
     assertThat(store.flush(key, new DelegatingValueHolder<String>(secondValueHolder)), is(true));
     timeSource.advanceTime(10); // this should NOT affect
     assertThat(
         store.getAndFault(key).lastAccessTime(TimeUnit.MILLISECONDS),
         is(secondValueHolder.creationTime(TimeUnit.MILLISECONDS) + 20));
   } finally {
     destroyStore(store);
   }
 }
 @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 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 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 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 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));
  }
 @Override
 public long getId() {
   return valueHolder.getId();
 }
 @Override
 public long hits() {
   return valueHolder.hits();
 }
 @Override
 public float hitRate(final long now, final TimeUnit unit) {
   return valueHolder.hitRate(now, unit);
 }
 @Override
 public long lastAccessTime(final TimeUnit unit) {
   return valueHolder.lastAccessTime(unit);
 }
 @Override
 public boolean isExpired(final long expirationTime, final TimeUnit unit) {
   return valueHolder.isExpired(expirationTime, unit);
 }
 @Override
 public long expirationTime(final TimeUnit unit) {
   return valueHolder.expirationTime(unit);
 }
 @Override
 public long creationTime(final TimeUnit unit) {
   return valueHolder.creationTime(unit);
 }
 @Override
 public T value() {
   return valueHolder.value();
 }
 @Override
 public void onExpiration(final K key, final Store.ValueHolder<V> valueHolder) {
   eventNotificationService.onEvent(CacheEvents.expiry(key, valueHolder.value(), this.source));
 }