anchor.diff:  
       statelessCacheTests(newCache);
     });
  +  group('delegate', () {
  +    Cache<int, String> newCache(Loader<int, String> loader) {
  +      return new DelegateCache(new Cache.lru(loader: loader, maximumSize: 5));
  +    }
  +
  +    statelessCacheTests(newCache);
  +    persistentCacheTests(newCache);
  +  });
     group('expiry', () {
       Duration offset;
       DateTime offsetClock() => new DateTime(2000).add(offset);
  
positive.code: void persistentCacheTests(Cache<int, String> newCache(Loader<int, String> loader)) {
    test('set and get', () {
      var cache = newCache(failingLoader);
      return cache.set(1, 'foo').then((value) => expect(value, 'foo', reason: 'return value')).then((
          _) => cache.get(1)).then((value) => expect(value, 'foo', reason: 'get value'));
    });
    test('set and size', () {
      var cache = newCache(failingLoader);
      return cache.set(1, 'foo').then((_) => cache.size()).then((value) =>
          expect(value, 1, reason: 'there should be one item'));
    });
    test('set, invalidate, get', () {
      var cache = newCache(failingLoader);
      return cache.set(1, 'foo').then((_) => cache.invalidate(1)).then((_) => cache.getIfPresent(1))
          .then((value) => expect(value, isNull, reason: 'key was invalidated'));
    });
    test('set, invalidate all, get', () {
      var cache = newCache(failingLoader);
      return cache.set(1, 'foo').then((_) => cache.invalidateAll()).then((_) => cache.getIfPresent(1))
          .then((value) => expect(value, isNull, reason: 'cache was invalidated'));
    });
    test('get with immediate value is persistent', () {
      var cache = newCache(immediateLoader);
      return cache.get(1).then((value) => expect(value, '1')).then((_) => cache.getIfPresent(1))
          .then((value) => expect(value, '1', reason: 'loaded value is persistent'));
    });
    test('get with future value is persistent', () {
      var cache = newCache(futureLoader);
      return cache.get(1).then((value) => expect(value, '1')).then((_) => cache.getIfPresent(1))
          .then((value) => expect(value, '1', reason: 'loaded value is persistent'));
    });
    test('get with delayed value is persistent', () {
      var cache = newCache(delayedLoader);
      return cache.get(1).then((value) => expect(value, '1')).then((_) => cache.getIfPresent(1))
          .then((value) => expect(value, '1', reason: 'loaded value is persistent'));
    });
    test('get with invalidated key is not persistent', () {
      var cache = newCache(delayedLoader);
      var loaded1 = cache.get(1);
      var loaded2 = cache.get(2);
      return cache.invalidate(2).then((_) => Future.wait([loaded1, loaded2])).then((_) =>
          cache.getIfPresent(1)).then((value) =>
          expect(value, '1', reason: 'value 1 should still be present')).then((_) =>
          cache.getIfPresent(2)).then((value) =>
          expect(value, isNull, reason: 'value 2 should still be absent'));
    });
    test('get with invalidated cache is not persistent', () {
      var cache = newCache(delayedLoader);
      var loaded = cache.get(1);
      return cache.invalidateAll().then((_) => loaded).then((_) => cache.getIfPresent(1)).then((
          value) => expect(value, isNull, reason: 'cache was invalidated'));
    });
  }
  
  
negative.code:     group('update expire cache', () {
        test('expire a set value after it has been updated', () {
          var cache = newUpdateExpireCache(immediateLoader);
          return cache
              .set(1, '2')
              .then((_) => offset = new Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '2', reason: 'value is set'))
              .then((_) => cache.set(1, '3'))
              .then((_) => offset = new Duration(seconds: 40))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '3', reason: 'value is updated'))
              .then((_) => offset = new Duration(seconds: 41))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, isNull, reason: 'value has expried'));
        });
        test('loaded value expires', () {
          var cache = newUpdateExpireCache(immediateLoader);
          return cache
              .get(1)
              .then((value) => expect(value, '1', reason: 'value is loaded'))
              .then((_) => offset = new Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '1', reason: 'value is still present'))
              .then((_) => offset = new Duration(seconds: 21))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, isNull, reason: 'value has expried'))
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
        test('reap expired items', () {
          var cache = newUpdateExpireCache(immediateLoader);
          return cache
              .set(1, '1')
              .then((_) => offset = new Duration(seconds: 21))
              .then((_) => cache.size())
              .then((size) => expect(size, 1, reason: 'cache has not been reaped'))
              .then((_) => cache.reap())
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
      });
      group('access expire cache', () {
        test('expire a set value after it has been updated', () {
          var cache = newAccessExpireCache(immediateLoader);
          return cache
              .set(1, '2')
              .then((_) => offset = new Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '2', reason: 'value is set'))
              .then((_) => cache.set(1, '3'))
              .then((_) => offset = new Duration(seconds: 40))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '3', reason: 'value is updated'))
              .then((_) => offset = new Duration(seconds: 61))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, isNull, reason: 'value has expried'));
        });
        test('loaded value expires', () {
          var cache = newAccessExpireCache(immediateLoader);
          return cache
              .get(1)
              .then((value) => expect(value, '1', reason: 'value is loaded'))
              .then((_) => offset = new Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '1', reason: 'value is still present'))
              .then((_) => offset = new Duration(seconds: 41))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, isNull, reason: 'value has expried'))
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
        test('reap expired items', () {
          var cache = newAccessExpireCache(immediateLoader);
          return cache
              .set(1, '1')
              .then((_) => offset = new Duration(seconds: 120))
              .then((_) => cache.size())
              .then((size) => expect(size, 1, reason: 'cache has not been reaped'))
              .then((_) => cache.reap())
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
      });
    });
  