// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:typed_mock/typed_mock.dart';

main() {
  defineReflectiveSuite(() {
    defineReflectiveTests(MemoryCachingByteStoreTest);
    defineReflectiveTests(NullByteStoreTest);
  });
}

List<int> _b(int length) {
  return new List<int>(length);
}

@reflectiveTest
class MemoryCachingByteStoreTest {
  test_get_notFound_evict() {
    var store = new _TestByteStore();
    var cachingStore = new MemoryCachingByteStore(store, 100);

    // Request '1'.  Nothing found.
    cachingStore.get('1');

    // Add enough data to the store to force an eviction.
    cachingStore.put('2', _b(40));
    cachingStore.put('3', _b(40));
    cachingStore.put('4', _b(40));
  }

  test_get_notFound_retry() {
    var mockStore = new _TestByteStore();
    var baseStore = new MemoryCachingByteStore(mockStore, 1000);
    var cachingStore = new MemoryCachingByteStore(baseStore, 100);

    // Request '1'.  Nothing found.
    expect(cachingStore.get('1'), isNull);

    // Add data to the base store, bypassing the caching store.
    baseStore.put('1', _b(40));

    // Request '1' again.  The previous `null` result should not have been
    // cached.
    expect(cachingStore.get('1'), isNotNull);
  }

  test_get_put_evict() {
    var store = new _TestByteStore();
    var cachingStore = new MemoryCachingByteStore(store, 100);

    // Keys: [1, 2].
    cachingStore.put('1', _b(40));
    cachingStore.put('2', _b(50));

    // Request '1', so now it is the most recently used.
    // Keys: [2, 1].
    cachingStore.get('1');

    // 40 + 50 + 30 > 100
    // So, '2' is evicted.
    cachingStore.put('3', _b(30));
    expect(cachingStore.get('1'), hasLength(40));
    expect(cachingStore.get('2'), isNull);
    expect(cachingStore.get('3'), hasLength(30));
  }

  test_put_evict_first() {
    var store = new _TestByteStore();
    var cachingStore = new MemoryCachingByteStore(store, 100);

    // 40 + 50 < 100
    cachingStore.put('1', _b(40));
    cachingStore.put('2', _b(50));
    expect(cachingStore.get('1'), hasLength(40));
    expect(cachingStore.get('2'), hasLength(50));

    // 40 + 50 + 30 > 100
    // So, '1' is evicted.
    cachingStore.put('3', _b(30));
    expect(cachingStore.get('1'), isNull);
    expect(cachingStore.get('2'), hasLength(50));
    expect(cachingStore.get('3'), hasLength(30));
  }

  test_put_evict_firstAndSecond() {
    var store = new _TestByteStore();
    var cachingStore = new MemoryCachingByteStore(store, 100);

    // 10 + 80 < 100
    cachingStore.put('1', _b(10));
    cachingStore.put('2', _b(80));
    expect(cachingStore.get('1'), hasLength(10));
    expect(cachingStore.get('2'), hasLength(80));

    // 10 + 80 + 30 > 100
    // So, '1' and '2' are evicted.
    cachingStore.put('3', _b(30));
    expect(cachingStore.get('1'), isNull);
    expect(cachingStore.get('2'), isNull);
    expect(cachingStore.get('3'), hasLength(30));
  }
}

@reflectiveTest
class NullByteStoreTest {
  test_get() {
    var store = new NullByteStore();

    expect(store.get('1'), isNull);

    store.put('1', _b(10));
    expect(store.get('1'), isNull);
  }
}

class _TestByteStore extends TypedMock implements ByteStore {}
