anchor.diff:          Cache.lru(loader: loader, maximumSize: 5);
   
       group('constructors', () {
  -      test('missing lru', () {
  -        expect(() => Cache.lru(), throwsArgumentError);
  -      });
         test('non positive max size', () {
           expect(() => Cache.lru(loader: immediateLoader, maximumSize: 0),
               throwsArgumentError);
  
positive.code:     });
  
      group('update expire cache', () {
        test('expire a set value after it has been updated', () {
          final cache = newUpdateExpireCache(immediateLoader);
          return cache
              .set(1, '2')
              .then((_) => offset = const Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '2', reason: 'value is set'))
              .then((_) => cache.set(1, '3'))
              .then((_) => offset = const Duration(seconds: 40))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '3', reason: 'value is updated'))
              .then((_) => offset = const Duration(seconds: 41))
              .then((_) => cache.getIfPresent(1))
              .then(
                  (value) => expect(value, isNull, reason: 'value has expired'));
        });
        test('loaded value expires', () {
          final cache = newUpdateExpireCache(immediateLoader);
          return cache
              .get(1)
              .then((value) => expect(value, '1', reason: 'value is loaded'))
              .then((_) => offset = const Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then(
                  (value) => expect(value, '1', reason: 'value is still present'))
              .then((_) => offset = const Duration(seconds: 21))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, isNull, reason: 'value has expired'))
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
        test('re-loaded value expires', () {
          var counter = 0;
          final cache = newUpdateExpireCache((key) {
            counter++;
            return immediateLoader(key);
          });
          return cache
              .get(0)
              .then((value) => expect(value, '0', reason: 'value is loaded'))
              .then((value) => expect(counter, 1, reason: 'loaded once'))
              .then((_) => offset = const Duration(seconds: 20))
              .then((_) => cache.get(0))
              .then(
                  (value) => expect(value, '0', reason: 'value is still present'))
              .then((value) => expect(counter, 1, reason: 'still loaded once'))
              .then((_) => offset = const Duration(seconds: 21))
              .then((_) => cache.get(0))
              .then((value) => expect(value, '0', reason: 'value is reloaded'))
              .then((value) => expect(counter, 2, reason: 'value loaded again'));
        });
        test('reap expired items', () {
          final cache = newUpdateExpireCache(immediateLoader);
          return cache
              .set(1, '1')
              .then((_) => offset = const Duration(seconds: 21))
              .then((_) => cache.size())
              .then(
                  (size) => expect(size, 1, reason: 'cache has not been reaped'))
              .then((_) => cache.reap())
              .then((size) => expect(size, 1, reason: 'one item should be gone'))
              .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', () {
          final cache = newAccessExpireCache(immediateLoader);
          return cache
              .set(1, '2')
              .then((_) => offset = const Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '2', reason: 'value is set'))
              .then((_) => cache.set(1, '3'))
              .then((_) => offset = const Duration(seconds: 40))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, '3', reason: 'value is updated'))
              .then((_) => offset = const Duration(seconds: 61))
              .then((_) => cache.getIfPresent(1))
              .then(
                  (value) => expect(value, isNull, reason: 'value has expired'));
        });
        test('loaded value expires', () {
          final cache = newAccessExpireCache(immediateLoader);
          return cache
              .get(1)
              .then((value) => expect(value, '1', reason: 'value is loaded'))
              .then((_) => offset = const Duration(seconds: 20))
              .then((_) => cache.getIfPresent(1))
              .then(
                  (value) => expect(value, '1', reason: 'value is still present'))
              .then((_) => offset = const Duration(seconds: 41))
              .then((_) => cache.getIfPresent(1))
              .then((value) => expect(value, isNull, reason: 'value has expired'))
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
        test('re-loaded value expires', () {
          var counter = 0;
          final cache = newAccessExpireCache((key) {
            counter++;
            return immediateLoader(key);
          });
          return cache
              .get(0)
              .then((value) => expect(value, '0', reason: 'value is loaded'))
              .then((value) => expect(counter, 1, reason: 'loaded once'))
              .then((_) => offset = const Duration(seconds: 20))
              .then((_) => cache.get(0))
              .then(
                  (value) => expect(value, '0', reason: 'value is still present'))
              .then((value) => expect(counter, 1, reason: 'still loaded once'))
              .then((_) => offset = const Duration(seconds: 41))
              .then((_) => cache.get(0))
              .then((value) => expect(value, '0', reason: 'value is reloaded'))
              .then((value) => expect(counter, 2, reason: 'value loaded again'));
        });
        test('reap expired items', () {
          final cache = newAccessExpireCache(immediateLoader);
          return cache
              .set(1, '1')
              .then((_) => offset = const Duration(seconds: 120))
              .then((_) => cache.size())
              .then(
                  (size) => expect(size, 1, reason: 'cache has not been reaped'))
              .then((_) => cache.reap())
              .then((size) => expect(size, 1, reason: 'one item should be gone'))
              .then((_) => cache.size())
              .then((size) => expect(size, 0, reason: 'cache should be empty'));
        });
      });
    });
  
negative.code:   group('empty', () {
      Cache<int, String> newCache(Loader<int, String> loader) =>
          Cache.empty(loader: loader);
      statelessCacheTests(newCache);
    });
    group('delegate', () {
      Cache<int, String> newCache(Loader<int, String> loader) =>
          DelegateCache(Cache.lru(loader: loader, maximumSize: 5));
      statelessCacheTests(newCache);
      persistentCacheTests(newCache);
    });
    group('expiry', () {
      late Duration offset;
      DateTime offsetClock() => DateTime(2000).add(offset);
      setUp(() => offset = Duration.zero);
  
  