| OLD | NEW |
| 1 // Copyright 2014 Google Inc. All Rights Reserved. | 1 // Copyright 2014 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 | 14 |
| 15 library quiver.collection.lru_map_test; | 15 library quiver.collection.lru_map_test; |
| 16 | 16 |
| 17 import 'package:quiver/collection.dart'; | 17 import 'package:quiver/collection.dart'; |
| 18 import 'package:test/test.dart'; | 18 import 'package:test/test.dart'; |
| 19 | 19 |
| 20 void main() { | 20 void main() { |
| 21 group('LruMap', () { | 21 group('LruMap', () { |
| 22 /// A map that will be initialize by individual tests. | 22 /// A map that will be initialize by individual tests. |
| 23 LruMap<String, String> lruMap; | 23 LruMap<String, String> lruMap; |
| 24 | 24 |
| 25 test('the length property reflects how many keys are in the map', () { | 25 test('the length property reflects how many keys are in the map', () { |
| 26 lruMap = new LruMap(); | 26 lruMap = new LruMap(); |
| 27 expect(lruMap, hasLength(0)); | 27 expect(lruMap, hasLength(0)); |
| 28 | 28 |
| 29 lruMap.addAll({ | 29 lruMap.addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 30 'A': 'Alpha', | |
| 31 'B': 'Beta', | |
| 32 'C': 'Charlie' | |
| 33 }); | |
| 34 expect(lruMap, hasLength(3)); | 30 expect(lruMap, hasLength(3)); |
| 35 }); | 31 }); |
| 36 | 32 |
| 37 test('accessing keys causes them to be promoted', () { | 33 test('accessing keys causes them to be promoted', () { |
| 38 lruMap = new LruMap()..addAll({ | 34 lruMap = new LruMap() |
| 39 'A': 'Alpha', | 35 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 40 'B': 'Beta', | |
| 41 'C': 'Charlie' | |
| 42 }); | |
| 43 | 36 |
| 44 expect(lruMap.keys.toList(), ['C', 'B', 'A']); | 37 expect(lruMap.keys.toList(), ['C', 'B', 'A']); |
| 45 | 38 |
| 46 lruMap['B']; | 39 lruMap['B']; |
| 47 | 40 |
| 48 // In a LRU cache, the first key is the one that will be removed if the | 41 // In a LRU cache, the first key is the one that will be removed if the |
| 49 // capacity is reached, so adding keys to the end is considered to be a | 42 // capacity is reached, so adding keys to the end is considered to be a |
| 50 // 'promotion'. | 43 // 'promotion'. |
| 51 expect(lruMap.keys.toList(), ['B', 'C', 'A']); | 44 expect(lruMap.keys.toList(), ['B', 'C', 'A']); |
| 52 }); | 45 }); |
| 53 | 46 |
| 54 test('new keys are added at the beginning', () { | 47 test('new keys are added at the beginning', () { |
| 55 lruMap = new LruMap()..addAll({ | 48 lruMap = new LruMap() |
| 56 'A': 'Alpha', | 49 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 57 'B': 'Beta', | |
| 58 'C': 'Charlie' | |
| 59 }); | |
| 60 | 50 |
| 61 lruMap['D'] = 'Delta'; | 51 lruMap['D'] = 'Delta'; |
| 62 expect(lruMap.keys.toList(), ['D', 'C', 'B', 'A']); | 52 expect(lruMap.keys.toList(), ['D', 'C', 'B', 'A']); |
| 63 }); | 53 }); |
| 64 | 54 |
| 65 test('setting values on existing keys works, and promotes the key', () { | 55 test('setting values on existing keys works, and promotes the key', () { |
| 66 lruMap = new LruMap()..addAll({ | 56 lruMap = new LruMap() |
| 67 'A': 'Alpha', | 57 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 68 'B': 'Beta', | |
| 69 'C': 'Charlie' | |
| 70 }); | |
| 71 | 58 |
| 72 lruMap['B'] = 'Bravo'; | 59 lruMap['B'] = 'Bravo'; |
| 73 expect(lruMap.keys.toList(), ['B', 'C', 'A']); | 60 expect(lruMap.keys.toList(), ['B', 'C', 'A']); |
| 74 expect(lruMap['B'], 'Bravo'); | 61 expect(lruMap['B'], 'Bravo'); |
| 75 }); | 62 }); |
| 76 | 63 |
| 77 test('the least recently used key is evicted when capacity hit', () { | 64 test('the least recently used key is evicted when capacity hit', () { |
| 78 lruMap = new LruMap(maximumSize: 3)..addAll({ | 65 lruMap = new LruMap(maximumSize: 3) |
| 79 'A': 'Alpha', | 66 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 80 'B': 'Beta', | |
| 81 'C': 'Charlie' | |
| 82 }); | |
| 83 | 67 |
| 84 lruMap['D'] = 'Delta'; | 68 lruMap['D'] = 'Delta'; |
| 85 expect(lruMap.keys.toList(), ['D', 'C', 'B']); | 69 expect(lruMap.keys.toList(), ['D', 'C', 'B']); |
| 86 }); | 70 }); |
| 87 | 71 |
| 88 test('setting maximum size evicts keys until the size is met', () { | 72 test('setting maximum size evicts keys until the size is met', () { |
| 89 lruMap = new LruMap(maximumSize: 5)..addAll({ | 73 lruMap = new LruMap(maximumSize: 5) |
| 74 ..addAll({ |
| 90 'A': 'Alpha', | 75 'A': 'Alpha', |
| 91 'B': 'Beta', | 76 'B': 'Beta', |
| 92 'C': 'Charlie', | 77 'C': 'Charlie', |
| 93 'D': 'Delta', | 78 'D': 'Delta', |
| 94 'E': 'Epsilon' | 79 'E': 'Epsilon' |
| 95 }); | 80 }); |
| 96 | 81 |
| 97 lruMap.maximumSize = 3; | 82 lruMap.maximumSize = 3; |
| 98 expect(lruMap.keys.toList(), ['E', 'D', 'C']); | 83 expect(lruMap.keys.toList(), ['E', 'D', 'C']); |
| 99 }); | 84 }); |
| 100 | 85 |
| 101 test('accessing the `keys` collection does not affect position', () { | 86 test('accessing the `keys` collection does not affect position', () { |
| 102 lruMap = new LruMap()..addAll({ | 87 lruMap = new LruMap() |
| 103 'A': 'Alpha', | 88 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 104 'B': 'Beta', | |
| 105 'C': 'Charlie' | |
| 106 }); | |
| 107 | 89 |
| 108 expect(lruMap.keys.toList(), ['C', 'B', 'A']); | 90 expect(lruMap.keys.toList(), ['C', 'B', 'A']); |
| 109 | 91 |
| 110 lruMap.keys.forEach((_){}); | 92 lruMap.keys.forEach((_) {}); |
| 111 lruMap.keys.forEach((_){}); | 93 lruMap.keys.forEach((_) {}); |
| 112 | 94 |
| 113 expect(lruMap.keys.toList(), ['C', 'B', 'A']); | 95 expect(lruMap.keys.toList(), ['C', 'B', 'A']); |
| 114 }); | 96 }); |
| 115 | 97 |
| 116 test('accessing the `values` collection does not affect position', () { | 98 test('accessing the `values` collection does not affect position', () { |
| 117 lruMap = new LruMap()..addAll({ | 99 lruMap = new LruMap() |
| 118 'A': 'Alpha', | 100 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 119 'B': 'Beta', | |
| 120 'C': 'Charlie' | |
| 121 }); | |
| 122 | 101 |
| 123 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); | 102 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); |
| 124 | 103 |
| 125 lruMap.values.forEach((_){}); | 104 lruMap.values.forEach((_) {}); |
| 126 lruMap.values.forEach((_){}); | 105 lruMap.values.forEach((_) {}); |
| 127 | 106 |
| 128 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); | 107 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); |
| 129 }); | 108 }); |
| 130 | 109 |
| 131 test('clearing removes all keys and values', () { | 110 test('clearing removes all keys and values', () { |
| 132 lruMap = new LruMap()..addAll({ | 111 lruMap = new LruMap() |
| 133 'A': 'Alpha', | 112 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 134 'B': 'Beta', | |
| 135 'C': 'Charlie' | |
| 136 }); | |
| 137 | 113 |
| 138 expect(lruMap.isNotEmpty, isTrue); | 114 expect(lruMap.isNotEmpty, isTrue); |
| 139 expect(lruMap.keys.isNotEmpty, isTrue); | 115 expect(lruMap.keys.isNotEmpty, isTrue); |
| 140 expect(lruMap.values.isNotEmpty, isTrue); | 116 expect(lruMap.values.isNotEmpty, isTrue); |
| 141 | 117 |
| 142 lruMap.clear(); | 118 lruMap.clear(); |
| 143 | 119 |
| 144 expect(lruMap.isEmpty, isTrue); | 120 expect(lruMap.isEmpty, isTrue); |
| 145 expect(lruMap.keys.isEmpty, isTrue); | 121 expect(lruMap.keys.isEmpty, isTrue); |
| 146 expect(lruMap.values.isEmpty, isTrue); | 122 expect(lruMap.values.isEmpty, isTrue); |
| 147 }); | 123 }); |
| 148 | 124 |
| 149 test('`containsKey` returns true if the key is in the map', () { | 125 test('`containsKey` returns true if the key is in the map', () { |
| 150 lruMap = new LruMap()..addAll({ | 126 lruMap = new LruMap() |
| 151 'A': 'Alpha', | 127 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 152 'B': 'Beta', | |
| 153 'C': 'Charlie' | |
| 154 }); | |
| 155 | 128 |
| 156 expect(lruMap.containsKey('A'), isTrue); | 129 expect(lruMap.containsKey('A'), isTrue); |
| 157 expect(lruMap.containsKey('D'), isFalse); | 130 expect(lruMap.containsKey('D'), isFalse); |
| 158 }); | 131 }); |
| 159 | 132 |
| 160 test('`containsValue` returns true if the value is in the map', () { | 133 test('`containsValue` returns true if the value is in the map', () { |
| 161 lruMap = new LruMap()..addAll({ | 134 lruMap = new LruMap() |
| 162 'A': 'Alpha', | 135 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 163 'B': 'Beta', | |
| 164 'C': 'Charlie' | |
| 165 }); | |
| 166 | 136 |
| 167 expect(lruMap.containsValue('Alpha'), isTrue); | 137 expect(lruMap.containsValue('Alpha'), isTrue); |
| 168 expect(lruMap.containsValue('Delta'), isFalse); | 138 expect(lruMap.containsValue('Delta'), isFalse); |
| 169 }); | 139 }); |
| 170 | 140 |
| 171 test('`forEach` returns all key-value pairs without modifying order', () { | 141 test('`forEach` returns all key-value pairs without modifying order', () { |
| 172 final keys = []; | 142 final keys = []; |
| 173 final values = []; | 143 final values = []; |
| 174 | 144 |
| 175 lruMap = new LruMap()..addAll({ | 145 lruMap = new LruMap() |
| 176 'A': 'Alpha', | 146 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 177 'B': 'Beta', | |
| 178 'C': 'Charlie' | |
| 179 }); | |
| 180 | 147 |
| 181 expect(lruMap.keys.toList(), ['C', 'B', 'A']); | 148 expect(lruMap.keys.toList(), ['C', 'B', 'A']); |
| 182 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); | 149 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); |
| 183 | 150 |
| 184 lruMap.forEach((key, value) { | 151 lruMap.forEach((key, value) { |
| 185 keys.add(key); | 152 keys.add(key); |
| 186 values.add(value); | 153 values.add(value); |
| 187 }); | 154 }); |
| 188 | 155 |
| 189 expect(keys, ['C', 'B', 'A']); | 156 expect(keys, ['C', 'B', 'A']); |
| 190 expect(values, ['Charlie', 'Beta', 'Alpha']); | 157 expect(values, ['Charlie', 'Beta', 'Alpha']); |
| 191 expect(lruMap.keys.toList(), ['C', 'B', 'A']); | 158 expect(lruMap.keys.toList(), ['C', 'B', 'A']); |
| 192 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); | 159 expect(lruMap.values.toList(), ['Charlie', 'Beta', 'Alpha']); |
| 193 }); | 160 }); |
| 194 | 161 |
| 195 group('`remove`', () { | 162 group('`remove`', () { |
| 196 setUp(() { | 163 setUp(() { |
| 197 lruMap = new LruMap()..addAll({ | 164 lruMap = new LruMap() |
| 198 'A': 'Alpha', | 165 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 199 'B': 'Beta', | |
| 200 'C': 'Charlie' | |
| 201 }); | |
| 202 }); | 166 }); |
| 203 | 167 |
| 204 test('returns the value associated with a key, if it exists', () { | 168 test('returns the value associated with a key, if it exists', () { |
| 205 expect(lruMap.remove('A'), 'Alpha'); | 169 expect(lruMap.remove('A'), 'Alpha'); |
| 206 }); | 170 }); |
| 207 | 171 |
| 208 test('returns null if the provided key does not exist', () { | 172 test('returns null if the provided key does not exist', () { |
| 209 expect(lruMap.remove('D'), isNull); | 173 expect(lruMap.remove('D'), isNull); |
| 210 }); | 174 }); |
| 211 | 175 |
| 212 test('can remove the head', () { | 176 test('can remove the head', () { |
| 213 lruMap.remove('C'); | 177 lruMap.remove('C'); |
| 214 expect(lruMap.keys.toList(), ['B', 'A']); | 178 expect(lruMap.keys.toList(), ['B', 'A']); |
| 215 }); | 179 }); |
| 216 | 180 |
| 217 test('can remove the tail', () { | 181 test('can remove the tail', () { |
| 218 lruMap.remove('A'); | 182 lruMap.remove('A'); |
| 219 expect(lruMap.keys.toList(), ['C', 'B']); | 183 expect(lruMap.keys.toList(), ['C', 'B']); |
| 220 }); | 184 }); |
| 221 | 185 |
| 222 test('can remove a middle entry', () { | 186 test('can remove a middle entry', () { |
| 223 lruMap.remove('B'); | 187 lruMap.remove('B'); |
| 224 expect(lruMap.keys.toList(), ['C', 'A']); | 188 expect(lruMap.keys.toList(), ['C', 'A']); |
| 225 }); | 189 }); |
| 226 }); | 190 }); |
| 227 | 191 |
| 228 group('`putIfAbsent`', () { | 192 group('`putIfAbsent`', () { |
| 229 setUp(() { | 193 setUp(() { |
| 230 lruMap = new LruMap()..addAll({ | 194 lruMap = new LruMap() |
| 231 'A': 'Alpha', | 195 ..addAll({'A': 'Alpha', 'B': 'Beta', 'C': 'Charlie'}); |
| 232 'B': 'Beta', | |
| 233 'C': 'Charlie' | |
| 234 }); | |
| 235 }); | 196 }); |
| 236 | 197 |
| 237 test('adds an item if it does not exist, and moves it to the MRU', () { | 198 test('adds an item if it does not exist, and moves it to the MRU', () { |
| 238 expect(lruMap.putIfAbsent('D', () => 'Delta'), 'Delta'); | 199 expect(lruMap.putIfAbsent('D', () => 'Delta'), 'Delta'); |
| 239 expect(lruMap.keys.toList(), ['D', 'C', 'B', 'A']); | 200 expect(lruMap.keys.toList(), ['D', 'C', 'B', 'A']); |
| 240 }); | 201 }); |
| 241 | 202 |
| 242 test('does not add an item if it exists, but does promote it to MRU', () { | 203 test('does not add an item if it exists, but does promote it to MRU', () { |
| 243 expect(lruMap.putIfAbsent('B', () => throw 'Oops!'), 'Beta'); | 204 expect(lruMap.putIfAbsent('B', () => throw 'Oops!'), 'Beta'); |
| 244 expect(lruMap.keys.toList(), ['B', 'C', 'A']); | 205 expect(lruMap.keys.toList(), ['B', 'C', 'A']); |
| 245 }); | 206 }); |
| 246 | 207 |
| 247 test('removes the LRU item if `maximumSize` exceeded', () { | 208 test('removes the LRU item if `maximumSize` exceeded', () { |
| 248 lruMap.maximumSize = 3; | 209 lruMap.maximumSize = 3; |
| 249 expect(lruMap.putIfAbsent('D', () => 'Delta'), 'Delta'); | 210 expect(lruMap.putIfAbsent('D', () => 'Delta'), 'Delta'); |
| 250 expect(lruMap.keys.toList(), ['D', 'C', 'B']); | 211 expect(lruMap.keys.toList(), ['D', 'C', 'B']); |
| 251 }); | 212 }); |
| 252 }); | 213 }); |
| 253 }); | 214 }); |
| 254 } | 215 } |
| OLD | NEW |