<|start_filename|>test/index.js<|end_filename|>
const test = require('tape');
const flru = require('../dist/flru');

const isObject = x => Object.prototype.toString.call(x, '[object Object]');

test('exports', t => {
	let keys = ['has', 'clear', 'get', 'set'];
	t.is(typeof flru, 'function', 'exports a function');

	let foo = new flru();
	t.true(isObject(foo), 'works with `new` keyword');
	keys.forEach(k => {
		t.is(typeof foo[k], 'function', `~> has "${k}" method`);
	});

	let bar = flru();
	t.true(isObject(bar), 'works without `new` keyword');
	keys.forEach(k => {
		t.is(typeof bar[k], 'function', `~> has "${k}" method`);
	});

	t.end();
});

test('has()', t => {
	let key = 'a';
	let foo = flru();

	foo.set(key, 1);
	t.is(foo.has(key), true, '~> returns `true` when known');
	t.is(foo.has('foobar'), false, '~> returns `false` when unknown');

	t.end();
});

test('clear()', t => {
	let foo = flru(3);
	let keys = ['a', 'b', 'c'];

	keys.forEach(foo.set);

	keys.forEach(k => {
		t.true(foo.has(k), `(pre) key "${k}" exists`);
	});

	t.is(foo.clear(), undefined, '~> clear() returns nothing');

	keys.forEach(k => {
		t.false(foo.has(k), `(post) key "${k}" unknown`);
	});

	t.end();
});

test('ordering', t => {
	let foo = flru(3);
	const keys = (arr, bool) => t.is(arr.every(foo.has), bool, `[${arr.toString()}] ~> ${bool ? '' : 'un'}known`);

	// A=[a,b,c]  &  S=[]
	['a', 'b', 'c'].forEach(foo.set);
	keys(['a', 'b', 'c'], true);

	t.pass(`>> set('d')`); // A=[d]  &  S=[a,b,c]
	t.is(foo.set('d', 4), undefined, '~> returns nothing');
	keys(['a', 'b', 'c', 'd'], true);

	foo.set('e', 5);
	t.pass(`>> set('e')`);
	// A=[d,e]  &  S=[a,b,c]
	keys(['a', 'b', 'c', 'd', 'e'], true); // 2n - 1

	foo.set('f', 6);
	t.pass(`>> set('f')`);
	// A=[d,e,f]  &  S=[a,b,c]
	keys(['d', 'e', 'f'], true);
	keys(['a', 'b', 'c'], true); //=> stale

	foo.set('g', 7);
	t.pass(`>> set('g')`);
	// A=[g]  &  S=[d,e,f]
	keys(['d', 'e', 'f', 'g'], true);
	keys(['a', 'b', 'c'], false); // purged

	t.pass(`>> get('e')`);
	// A=[g,e]  &  S=[d,e,f]
	t.is(foo.get('e'), 5, '~> returns item value');
	keys(['d', 'e', 'f', 'g'], true); // unchanged, altho "e" exists twice

	foo.get('d');
	t.pass(`>> get('d')`);
	// A=[g,e,d]  &  S=[d,e,f]
	keys(['g', 'e', 'd', 'f'], true);

	foo.set('a', 1);
	t.pass(`>> set('a'`);
	// A=[a]  &  S=[g,e,d]
	keys(['a', 'g', 'e', 'd'], true);
	keys(['f'], false); // purged

	t.end();
});


<|start_filename|>bench/index.js<|end_filename|>
const mri = require('mri');
const { Suite } = require('benchmark');
const forEach = require('@arr/foreach');

const argv = process.argv.slice(2);
const { _, limit } = mri(argv, {
	default: { limit:1e3 }
});

const libs = {
	'lru-cache': Mod => new Mod(limit),
	'tmp-cache': Mod => new Mod(limit),
	'tiny-lru': Mod => Mod(limit),
	'flru': Mod => Mod(limit),
};

const name = _.shift() || 'flru';
const setup = libs[name];

if (setup === void 0) {
	console.error(`Unknown library: ${name}`);
	process.exit(1);
}

const double = limit * 2;
console.log(`~> Generating ${double} unique keys...`);
const random = () => Math.random().toString(36).substring(4);
const CACHE = setup( require(name === 'flru' ? '../dist/flru' : name) );
const FULL = Array.from({ length:double }, random);
const HALF = FULL.slice(limit);

console.log('\n# %s', name);

new Suite()
	.add('set', () => {
		forEach(HALF, (k, i) => CACHE.set(k, i));
	})
	.add('has', () => {
		forEach(HALF, k => CACHE.has(k));
	})
	.add('get', () => {
		forEach(HALF, k => CACHE.get(k));
	})
	.add('update', () => {
		forEach(HALF, k => CACHE.set(k, k));
	})
	.add('evict', () => {
		forEach(FULL, (k, i) => CACHE.set(k, i));
	})
	.on('cycle', e => console.log(String(e.target)))
	.run();
