src/test.js
import {increasing, decreasing} from '@aureooms/js-compare';
import {shuffle} from '@aureooms/js-random';
import {exhaust, list, map, product, chain} from '@aureooms/js-itertools';
import {star} from '@aureooms/js-functools';
/**
* Tests the following methods:
*
* - tree.insert( value )
* - tree.find( value )
* - tree.remove( value )
* - tree[@@iterator]()
*/
const macro = (t, {empty}, compare, n, length) => {
let i;
const tree = empty(compare);
const a = [];
i = n;
while (i--) {
const x = Math.random();
tree.insert(x);
a.push(x);
if (length) t.is(tree.length, a.length);
}
a.sort(compare);
const half = Math.floor(n / 2);
// CHECK CONTENT
const b = [...tree];
t.deepEqual(b, a, 'check content');
// PREPARE FOR CHECKING PURPOSES
i = n;
while (i--) {
a[i] = [true, a[i]];
}
// CHECK FIND SORTED
i = n;
while (i--) {
b[i] = tree.find(a[i][1]);
if (length) t.is(tree.length, n);
}
t.deepEqual(b, a, 'check find sorted');
// CHECK FIND SHUFFLED
shuffle(a, 0, n);
i = n;
while (i--) {
b[i] = tree.find(a[i][1]);
if (length) t.is(tree.length, n);
}
t.deepEqual(b, a, 'check find shuffled');
const remove = (l, r, p, q, txt) => {
// REMOVE
i = r;
while (i-- > l) {
tree.remove(a[i][1]);
a[i][0] = false;
}
// CHECK CONTENT AFTER REMOVE
const e = [];
for (i = p; i < q; ++i) {
e.push(a[i][1]);
}
e.sort(compare);
const d = [...tree];
t.deepEqual(d, e, 'check content ' + txt);
// CHECK FIND AFTER REMOVE
const c = [];
i = n;
while (i--) {
b[i] = tree.find(a[i][1])[0];
c[i] = a[i][0];
}
t.deepEqual(b, c, 'check find ' + txt);
// TRY REMOVING TWICE
i = r;
while (i-- > l) {
tree.remove(a[i][1]);
}
};
remove(half, n, 0, half, 'after remove half');
// ADD NEW ELEMENTS
i = n;
while (i-- > half) {
const x = Math.random();
tree.insert(x);
a[i] = [true, x];
}
// CHECK CONTENT NEW ELEMENTS
const e = [];
for (i = 0; i < n; ++i) {
e.push(a[i][1]);
}
e.sort(compare);
const d = [...tree];
t.deepEqual(d, e, 'check content new elements');
// CHECK FIND NEW ELEMENTS
i = n;
while (i--) {
b[i] = tree.find(a[i][1]);
}
t.deepEqual(b, a, 'check find new elements');
remove(0, half, half, n, 'after remove first half');
remove(half, n, 0, 0, 'after remove second half');
t.is(tree.length, 0, 'tree is empty');
};
macro.title = (title, {name}, compare, n, length) =>
`search tree (${name}, ${compare.name}, ${n}, length: ${length})`;
const DEFAULT_COMPARE_FUNCTIONS = [increasing, decreasing];
const DEFAULT_LENGTH_VALUES = [
[1],
[16],
[17],
[31],
[32],
[33],
[127],
[128],
[129]
];
const DEFAULT_OPTIONS = {
compare: DEFAULT_COMPARE_FUNCTIONS,
lengths: DEFAULT_LENGTH_VALUES,
length: false
};
const wrap = (l) => map((x) => [x], l[Symbol.iterator] === undefined ? [l] : l);
export default function test(_test, implementations, options) {
options = Object.assign({}, DEFAULT_OPTIONS, options);
exhaust(
map((args) => {
star((tree, compare, size, length) => {
_test(macro, tree, compare, size, length);
}, list(chain(args)));
}, product([wrap(implementations), wrap(options.compare), wrap(options.lengths), wrap(options.length)], 1))
);
}