blob: 803597bae1e9d00b92801db9e2fb038fccb8db0f [file] [log] [blame]
// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
library quiver.collection.lru_map_test;
import 'package:quiver/collection.dart';
import 'package:test/test.dart';
void main() {
group('LruMap', () {
/// A map that will be initialize by individual tests.
LruMap<String, String> lruMap;
test('the length property reflects how many keys are in the map', () {
lruMap = new LruMap();
expect(lruMap, hasLength(0));
lruMap.addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap, hasLength(3));
});
test('accessing keys causes them to be promoted', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.keys.toList(), ['C', 'B', 'A']);
lruMap['B'];
// In a LRU cache, the first key is the one that will be removed if the
// capacity is reached, so adding keys to the end is considered to be a
// 'promotion'.
expect(lruMap.keys.toList(), ['B', 'C', 'A']);
});
test('new keys are added at the beginning', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
lruMap['D'] = 'Delta';
expect(lruMap.keys.toList(), ['D', 'C', 'B', 'A']);
});
test('setting values on existing keys works, and promotes the key', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
lruMap['B'] = 'Bravo';
expect(lruMap.keys.toList(), ['B', 'C', 'A']);
expect(lruMap['B'], 'Bravo');
});
test('the least recently used key is evicted when capacity hit', () {
lruMap = new LruMap(maximumSize: 3)
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
lruMap['D'] = 'Delta';
expect(lruMap.keys.toList(), ['D', 'C', 'B']);
});
test('setting maximum size evicts keys until the size is met', () {
lruMap = new LruMap(maximumSize: 5)
..addAll({
'A': 'Alpha',
'B': 'Beta',
'C': 'Charlie',
'D': 'Delta',
'E': 'Epsilon'
});
lruMap.maximumSize = 3;
expect(lruMap.keys.toList(), ['E', 'D', 'C']);
});
test('accessing the `keys` collection does not affect position', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.keys.toList(), ['C', 'B', 'A']);
lruMap.keys.forEach((_) {});
lruMap.keys.forEach((_) {});
expect(lruMap.keys.toList(), ['C', 'B', 'A']);
});
test('accessing the `values` collection does not affect position', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']);
lruMap.values.forEach((_) {});
lruMap.values.forEach((_) {});
expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']);
});
test('clearing removes all keys and values', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.isNotEmpty, isTrue);
expect(lruMap.keys.isNotEmpty, isTrue);
expect(lruMap.values.isNotEmpty, isTrue);
lruMap.clear();
expect(lruMap.isEmpty, isTrue);
expect(lruMap.keys.isEmpty, isTrue);
expect(lruMap.values.isEmpty, isTrue);
});
test('`containsKey` returns true if the key is in the map', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.containsKey('A'), isTrue);
expect(lruMap.containsKey('D'), isFalse);
});
test('`containsValue` returns true if the value is in the map', () {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.containsValue('Alpha'), isTrue);
expect(lruMap.containsValue('Delta'), isFalse);
});
test('`forEach` returns all key-value pairs without modifying order', () {
final keys = [];
final values = [];
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
expect(lruMap.keys.toList(), ['C', 'B', 'A']);
expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']);
lruMap.forEach((key, value) {
keys.add(key);
values.add(value);
});
expect(keys, ['C', 'B', 'A']);
expect(values, ['Charlie', 'Beta', 'Alpha']);
expect(lruMap.keys.toList(), ['C', 'B', 'A']);
expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']);
});
group('`remove`', () {
setUp(() {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
});
test('returns the value associated with a key, if it exists', () {
expect(lruMap.remove('A'), 'Alpha');
});
test('returns null if the provided key does not exist', () {
expect(lruMap.remove('D'), isNull);
});
test('can remove the head', () {
lruMap.remove('C');
expect(lruMap.keys.toList(), ['B', 'A']);
});
test('can remove the tail', () {
lruMap.remove('A');
expect(lruMap.keys.toList(), ['C', 'B']);
});
test('can remove a middle entry', () {
lruMap.remove('B');
expect(lruMap.keys.toList(), ['C', 'A']);
});
});
group('`putIfAbsent`', () {
setUp(() {
lruMap = new LruMap()
..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'});
});
test('adds an item if it does not exist, and moves it to the MRU', () {
expect(lruMap.putIfAbsent('D', () => 'Delta'), 'Delta');
expect(lruMap.keys.toList(), ['D', 'C', 'B', 'A']);
});
test('does not add an item if it exists, but does promote it to MRU', () {
expect(lruMap.putIfAbsent('B', () => throw 'Oops!'), 'Beta');
expect(lruMap.keys.toList(), ['B', 'C', 'A']);
});
test('removes the LRU item if `maximumSize` exceeded', () {
lruMap.maximumSize = 3;
expect(lruMap.putIfAbsent('D', () => 'Delta'), 'Delta');
expect(lruMap.keys.toList(), ['D', 'C', 'B']);
});
});
});
}