775ac7b58c
you must login with an BTP account in order to see the app
520 lines
11 KiB
JavaScript
520 lines
11 KiB
JavaScript
var test = require('tap').test
|
|
var LRU = require('../')
|
|
|
|
test('basic', function (t) {
|
|
var cache = new LRU({max: 10})
|
|
cache.set('key', 'value')
|
|
t.equal(cache.get('key'), 'value')
|
|
t.equal(cache.get('nada'), undefined)
|
|
t.equal(cache.length, 1)
|
|
t.equal(cache.max, 10)
|
|
t.end()
|
|
})
|
|
|
|
test('least recently set', function (t) {
|
|
var cache = new LRU(2)
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'B')
|
|
cache.set('c', 'C')
|
|
t.equal(cache.get('c'), 'C')
|
|
t.equal(cache.get('b'), 'B')
|
|
t.equal(cache.get('a'), undefined)
|
|
t.end()
|
|
})
|
|
|
|
test('lru recently gotten', function (t) {
|
|
var cache = new LRU(2)
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'B')
|
|
cache.get('a')
|
|
cache.set('c', 'C')
|
|
t.equal(cache.get('c'), 'C')
|
|
t.equal(cache.get('b'), undefined)
|
|
t.equal(cache.get('a'), 'A')
|
|
t.end()
|
|
})
|
|
|
|
test('del', function (t) {
|
|
var cache = new LRU(2)
|
|
cache.set('a', 'A')
|
|
cache.del('a')
|
|
t.equal(cache.get('a'), undefined)
|
|
t.end()
|
|
})
|
|
|
|
test('max', function (t) {
|
|
var cache = new LRU(3)
|
|
|
|
// test changing the max, verify that the LRU items get dropped.
|
|
cache.max = 100
|
|
var i
|
|
for (i = 0; i < 100; i++) cache.set(i, i)
|
|
t.equal(cache.length, 100)
|
|
for (i = 0; i < 100; i++) {
|
|
t.equal(cache.get(i), i)
|
|
}
|
|
cache.max = 3
|
|
t.equal(cache.length, 3)
|
|
for (i = 0; i < 97; i++) {
|
|
t.equal(cache.get(i), undefined)
|
|
}
|
|
for (i = 98; i < 100; i++) {
|
|
t.equal(cache.get(i), i)
|
|
}
|
|
|
|
// now remove the max restriction, and try again.
|
|
cache.max = 'hello'
|
|
for (i = 0; i < 100; i++) cache.set(i, i)
|
|
t.equal(cache.length, 100)
|
|
for (i = 0; i < 100; i++) {
|
|
t.equal(cache.get(i), i)
|
|
}
|
|
// should trigger an immediate resize
|
|
cache.max = 3
|
|
t.equal(cache.length, 3)
|
|
for (i = 0; i < 97; i++) {
|
|
t.equal(cache.get(i), undefined)
|
|
}
|
|
for (i = 98; i < 100; i++) {
|
|
t.equal(cache.get(i), i)
|
|
}
|
|
t.end()
|
|
})
|
|
|
|
test('reset', function (t) {
|
|
var cache = new LRU(10)
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'B')
|
|
cache.reset()
|
|
t.equal(cache.length, 0)
|
|
t.equal(cache.max, 10)
|
|
t.equal(cache.get('a'), undefined)
|
|
t.equal(cache.get('b'), undefined)
|
|
t.end()
|
|
})
|
|
|
|
test('basic with weighed length', function (t) {
|
|
var cache = new LRU({
|
|
max: 100,
|
|
length: function (item, key) {
|
|
t.isa(key, 'string')
|
|
return item.size
|
|
}
|
|
})
|
|
cache.set('key', {val: 'value', size: 50})
|
|
t.equal(cache.get('key').val, 'value')
|
|
t.equal(cache.get('nada'), undefined)
|
|
t.equal(cache.lengthCalculator(cache.get('key'), 'key'), 50)
|
|
t.equal(cache.length, 50)
|
|
t.equal(cache.max, 100)
|
|
t.end()
|
|
})
|
|
|
|
test('weighed length item too large', function (t) {
|
|
var cache = new LRU({
|
|
max: 10,
|
|
length: function (item) { return item.size }
|
|
})
|
|
t.equal(cache.max, 10)
|
|
|
|
// should fall out immediately
|
|
cache.set('key', {val: 'value', size: 50})
|
|
|
|
t.equal(cache.length, 0)
|
|
t.equal(cache.get('key'), undefined)
|
|
t.end()
|
|
})
|
|
|
|
test('least recently set with weighed length', function (t) {
|
|
var cache = new LRU({
|
|
max: 8,
|
|
length: function (item) { return item.length }
|
|
})
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'BB')
|
|
cache.set('c', 'CCC')
|
|
cache.set('d', 'DDDD')
|
|
t.equal(cache.get('d'), 'DDDD')
|
|
t.equal(cache.get('c'), 'CCC')
|
|
t.equal(cache.get('b'), undefined)
|
|
t.equal(cache.get('a'), undefined)
|
|
t.end()
|
|
})
|
|
|
|
test('lru recently gotten with weighed length', function (t) {
|
|
var cache = new LRU({
|
|
max: 8,
|
|
length: function (item) { return item.length }
|
|
})
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'BB')
|
|
cache.set('c', 'CCC')
|
|
cache.get('a')
|
|
cache.get('b')
|
|
cache.set('d', 'DDDD')
|
|
t.equal(cache.get('c'), undefined)
|
|
t.equal(cache.get('d'), 'DDDD')
|
|
t.equal(cache.get('b'), 'BB')
|
|
t.equal(cache.get('a'), 'A')
|
|
t.end()
|
|
})
|
|
|
|
test('lru recently updated with weighed length', function (t) {
|
|
var cache = new LRU({
|
|
max: 8,
|
|
length: function (item) { return item.length }
|
|
})
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'BB')
|
|
cache.set('c', 'CCC')
|
|
t.equal(cache.length, 6) // CCC BB A
|
|
cache.set('a', '+A')
|
|
t.equal(cache.length, 7) // +A CCC BB
|
|
cache.set('b', '++BB')
|
|
t.equal(cache.length, 6) // ++BB +A
|
|
t.equal(cache.get('c'), undefined)
|
|
|
|
cache.set('c', 'oversized')
|
|
t.equal(cache.length, 6) // ++BB +A
|
|
t.equal(cache.get('c'), undefined)
|
|
|
|
cache.set('a', 'oversized')
|
|
t.equal(cache.length, 4) // ++BB
|
|
t.equal(cache.get('a'), undefined)
|
|
t.equal(cache.get('b'), '++BB')
|
|
t.end()
|
|
})
|
|
|
|
test('set returns proper booleans', function (t) {
|
|
var cache = new LRU({
|
|
max: 5,
|
|
length: function (item) { return item.length }
|
|
})
|
|
|
|
t.equal(cache.set('a', 'A'), true)
|
|
|
|
// should return false for max exceeded
|
|
t.equal(cache.set('b', 'donuts'), false)
|
|
|
|
t.equal(cache.set('b', 'B'), true)
|
|
t.equal(cache.set('c', 'CCCC'), true)
|
|
t.end()
|
|
})
|
|
|
|
test('drop the old items', function (t) {
|
|
var cache = new LRU({
|
|
max: 5,
|
|
maxAge: 50
|
|
})
|
|
|
|
cache.set('a', 'A')
|
|
|
|
setTimeout(function () {
|
|
cache.set('b', 'b')
|
|
t.equal(cache.get('a'), 'A')
|
|
}, 25)
|
|
|
|
setTimeout(function () {
|
|
cache.set('c', 'C')
|
|
// timed out
|
|
t.notOk(cache.get('a'))
|
|
}, 60 + 25)
|
|
|
|
setTimeout(function () {
|
|
t.notOk(cache.get('b'))
|
|
t.equal(cache.get('c'), 'C')
|
|
}, 90)
|
|
|
|
setTimeout(function () {
|
|
t.notOk(cache.get('c'))
|
|
t.end()
|
|
}, 155)
|
|
})
|
|
|
|
test('manual pruning', function (t) {
|
|
var cache = new LRU({
|
|
max: 5,
|
|
maxAge: 50
|
|
})
|
|
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'b')
|
|
cache.set('c', 'C')
|
|
|
|
setTimeout(function () {
|
|
cache.prune()
|
|
|
|
t.notOk(cache.get('a'))
|
|
t.notOk(cache.get('b'))
|
|
t.notOk(cache.get('c'))
|
|
|
|
t.end()
|
|
}, 100)
|
|
})
|
|
|
|
test('individual item can have its own maxAge', function (t) {
|
|
var cache = new LRU({
|
|
max: 5,
|
|
maxAge: 50
|
|
})
|
|
|
|
cache.set('a', 'A', 20)
|
|
setTimeout(function () {
|
|
t.notOk(cache.get('a'))
|
|
t.end()
|
|
}, 25)
|
|
})
|
|
|
|
test('individual item can have its own maxAge > cache', function (t) {
|
|
var cache = new LRU({
|
|
max: 5,
|
|
maxAge: 20
|
|
})
|
|
|
|
cache.set('a', 'A', 50)
|
|
setTimeout(function () {
|
|
t.equal(cache.get('a'), 'A')
|
|
t.end()
|
|
}, 25)
|
|
})
|
|
|
|
test('disposal function', function (t) {
|
|
var disposed = false
|
|
var cache = new LRU({
|
|
max: 1,
|
|
dispose: function (k, n) {
|
|
disposed = n
|
|
}
|
|
})
|
|
|
|
cache.set(1, 1)
|
|
cache.set(2, 2)
|
|
t.equal(disposed, 1)
|
|
cache.set(2, 10)
|
|
t.equal(disposed, 2)
|
|
cache.set(3, 3)
|
|
t.equal(disposed, 10)
|
|
cache.reset()
|
|
t.equal(disposed, 3)
|
|
t.end()
|
|
})
|
|
|
|
test('disposal function on too big of item', function (t) {
|
|
var disposed = false
|
|
var cache = new LRU({
|
|
max: 1,
|
|
length: function (k) {
|
|
return k.length
|
|
},
|
|
dispose: function (k, n) {
|
|
disposed = n
|
|
}
|
|
})
|
|
var obj = [ 1, 2 ]
|
|
|
|
t.equal(disposed, false)
|
|
cache.set('obj', obj)
|
|
t.equal(disposed, obj)
|
|
t.end()
|
|
})
|
|
|
|
test('has()', function (t) {
|
|
var cache = new LRU({
|
|
max: 1,
|
|
maxAge: 10
|
|
})
|
|
|
|
cache.set('foo', 'bar')
|
|
t.equal(cache.has('foo'), true)
|
|
cache.set('blu', 'baz')
|
|
t.equal(cache.has('foo'), false)
|
|
t.equal(cache.has('blu'), true)
|
|
setTimeout(function () {
|
|
t.equal(cache.has('blu'), false)
|
|
t.end()
|
|
}, 15)
|
|
})
|
|
|
|
test('stale', function (t) {
|
|
var cache = new LRU({
|
|
maxAge: 10,
|
|
stale: true
|
|
})
|
|
|
|
t.equal(cache.allowStale, true)
|
|
|
|
cache.set('foo', 'bar')
|
|
t.equal(cache.get('foo'), 'bar')
|
|
t.equal(cache.has('foo'), true)
|
|
setTimeout(function () {
|
|
t.equal(cache.has('foo'), false)
|
|
t.equal(cache.get('foo'), 'bar')
|
|
t.equal(cache.get('foo'), undefined)
|
|
t.end()
|
|
}, 15)
|
|
})
|
|
|
|
test('lru update via set', function (t) {
|
|
var cache = LRU({ max: 2 })
|
|
|
|
cache.set('foo', 1)
|
|
cache.set('bar', 2)
|
|
cache.del('bar')
|
|
cache.set('baz', 3)
|
|
cache.set('qux', 4)
|
|
|
|
t.equal(cache.get('foo'), undefined)
|
|
t.equal(cache.get('bar'), undefined)
|
|
t.equal(cache.get('baz'), 3)
|
|
t.equal(cache.get('qux'), 4)
|
|
t.end()
|
|
})
|
|
|
|
test('least recently set w/ peek', function (t) {
|
|
var cache = new LRU(2)
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'B')
|
|
t.equal(cache.peek('a'), 'A')
|
|
cache.set('c', 'C')
|
|
t.equal(cache.get('c'), 'C')
|
|
t.equal(cache.get('b'), 'B')
|
|
t.equal(cache.get('a'), undefined)
|
|
t.end()
|
|
})
|
|
|
|
test('pop the least used item', function (t) {
|
|
var cache = new LRU(3)
|
|
var last
|
|
|
|
cache.set('a', 'A')
|
|
cache.set('b', 'B')
|
|
cache.set('c', 'C')
|
|
|
|
t.equal(cache.length, 3)
|
|
t.equal(cache.max, 3)
|
|
|
|
// Ensure we pop a, c, b
|
|
cache.get('b', 'B')
|
|
|
|
last = cache.pop()
|
|
t.equal(last.key, 'a')
|
|
t.equal(last.value, 'A')
|
|
t.equal(cache.length, 2)
|
|
t.equal(cache.max, 3)
|
|
|
|
last = cache.pop()
|
|
t.equal(last.key, 'c')
|
|
t.equal(last.value, 'C')
|
|
t.equal(cache.length, 1)
|
|
t.equal(cache.max, 3)
|
|
|
|
last = cache.pop()
|
|
t.equal(last.key, 'b')
|
|
t.equal(last.value, 'B')
|
|
t.equal(cache.length, 0)
|
|
t.equal(cache.max, 3)
|
|
|
|
last = cache.pop()
|
|
t.equal(last, null)
|
|
t.equal(cache.length, 0)
|
|
t.equal(cache.max, 3)
|
|
|
|
t.end()
|
|
})
|
|
|
|
test('get and set only accepts strings and numbers as keys', function (t) {
|
|
var cache = new LRU()
|
|
|
|
cache.set('key', 'value')
|
|
cache.set(123, 456)
|
|
|
|
t.equal(cache.get('key'), 'value')
|
|
t.equal(cache.get(123), 456)
|
|
|
|
t.end()
|
|
})
|
|
|
|
test('peek with wierd keys', function (t) {
|
|
var cache = new LRU()
|
|
|
|
cache.set('key', 'value')
|
|
cache.set(123, 456)
|
|
|
|
t.equal(cache.peek('key'), 'value')
|
|
t.equal(cache.peek(123), 456)
|
|
|
|
t.equal(cache.peek({
|
|
toString: function () { return 'key' }
|
|
}), undefined)
|
|
|
|
t.end()
|
|
})
|
|
|
|
test('invalid length calc results in basic length', function (t) {
|
|
var l = new LRU({ length: true })
|
|
t.isa(l.lengthCalculator, 'function')
|
|
l.lengthCalculator = 'not a function'
|
|
t.isa(l.lengthCalculator, 'function')
|
|
t.end()
|
|
})
|
|
|
|
test('change length calculator recalculates', function (t) {
|
|
var l = new LRU({ max: 3 })
|
|
l.set(2, 2)
|
|
l.set(1, 1)
|
|
l.lengthCalculator = function (key, val) {
|
|
return key + val
|
|
}
|
|
t.equal(l.itemCount, 1)
|
|
t.equal(l.get(2), undefined)
|
|
t.equal(l.get(1), 1)
|
|
l.set(0, 1)
|
|
t.equal(l.itemCount, 2)
|
|
l.lengthCalculator = function (key, val) {
|
|
return key
|
|
}
|
|
t.equal(l.lengthCalculator(1, 10), 1)
|
|
t.equal(l.lengthCalculator(10, 1), 10)
|
|
l.lengthCalculator = { not: 'a function' }
|
|
t.equal(l.lengthCalculator(1, 10), 1)
|
|
t.equal(l.lengthCalculator(10, 1), 1)
|
|
t.end()
|
|
})
|
|
|
|
test('delete non-existent item has no effect', function (t) {
|
|
var l = new LRU({ max: 2 })
|
|
l.set('foo', 1)
|
|
l.set('bar', 2)
|
|
l.del('baz')
|
|
t.same(l.dumpLru().toArray().map(function (hit) {
|
|
return hit.key
|
|
}), [ 'bar', 'foo' ])
|
|
t.end()
|
|
})
|
|
|
|
test('maxAge on list, cleared in forEach', function (t) {
|
|
var l = new LRU({ stale: true })
|
|
l.set('foo', 1)
|
|
|
|
// hacky. make it seem older.
|
|
l.dumpLru().head.value.now = Date.now() - 100000
|
|
|
|
// setting maxAge to invalid values does nothing.
|
|
t.equal(l.maxAge, 0)
|
|
l.maxAge = -100
|
|
t.equal(l.maxAge, 0)
|
|
l.maxAge = {}
|
|
t.equal(l.maxAge, 0)
|
|
|
|
l.maxAge = 1
|
|
|
|
var saw = false
|
|
l.forEach(function (val, key) {
|
|
saw = true
|
|
t.equal(key, 'foo')
|
|
})
|
|
t.ok(saw)
|
|
t.equal(l.length, 0)
|
|
|
|
t.end()
|
|
})
|