VulpesOS/gaia/apps/calendar/test/unit/interval_tree_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

603 lines
13 KiB
JavaScript
Executable file

define(function(require) {
'use strict';
var Factory = require('test/support/factory');
var IntervalTree = require('interval_tree');
var Timespan = require('common/timespan');
var binsearch = require('common/binsearch');
var compare = require('common/compare');
suite('interval_tree', function() {
var subject;
var items;
var list;
var expectedRange;
var span;
var Node;
function factory(id, start, end) {
return {
_id: id,
_startDateMS: start,
_endDateMS: end
};
}
suiteSetup(function() {
Node = IntervalTree.Node;
});
setup(function() {
IntervalTree.overlapTime = 0;
// we use this range as a baseline
// through the less complicated tests
expectedRange = {
start: 1200,
end: 1300
};
span = new Timespan(expectedRange.start, expectedRange.end);
// setup the basic list of items
items = {};
items.before = factory(1, 100, 800);
items.overlapBefore = factory(4, 1050, 1400);
items.middle = factory(3, 1250, 1280);
items.after = factory(2, 1500, 2000);
// should be in order of start times
list = [
items.before,
items.overlapBefore,
items.middle,
items.after
];
subject = new IntervalTree(list);
});
test('integration', function() {
var span = new Timespan(
100,
800
);
var result = subject.query(span);
assert.deepEqual(result, [items.before]);
subject.remove(items.before);
result = subject.query(span);
assert.deepEqual(result, []);
var added = factory(30, 400, 1200);
subject.add(added);
subject.add(factory(31, 1100, 1200));
assert.isFalse(subject.synced);
span.end = 1100;
assert.deepEqual(
subject.query(span),
[
added,
items.overlapBefore
]
);
});
test('initialization', function() {
assert.deepEqual(subject.items, list);
assert.ok(!subject.synced);
assert.ok(!subject.rootNode);
assert.deepEqual(subject.byId, {});
});
test('init without list', function() {
var subject = new IntervalTree();
assert.deepEqual(subject.items, []);
});
test('#build', function() {
subject.build();
var node = subject.rootNode;
assert.ok(subject.synced);
assert.ok(node);
subject.build();
assert.equal(
node, subject.rootNode,
'should not rebuild tree when in sync'
);
});
suite('#add', function() {
setup(function() {
// start from clean tree
subject.items = [];
subject.synced = true;
subject.add(items.after);
});
test('re-add item with same _id', function() {
var id = items.after._id;
var obj = { _id: id };
subject.add(obj);
assert.deepEqual(
subject.items,
[items.after]
);
});
test('first add', function() {
assert.deepEqual(
subject.items,
[items.after]
);
assert.isFalse(subject.synced);
assert.equal(
subject.byId[items.after._id],
items.after,
'should add to byId cache'
);
});
test('multiple adds', function() {
subject.add(items.overlapBefore);
subject.add(items.before);
assert.isFalse(subject.synced);
assert.deepEqual(
subject.items,
[
items.before,
items.overlapBefore,
items.after
]
);
});
test('yahoo recurring allday', function() {
var allday = factory(555, 1000, 1000);
subject.add(allday);
assert.deepEqual(subject.items, [allday, items.after]);
});
});
suite('#removeFutureIntervals', function() {
var list;
setup(function() {
subject.items = [];
list = {};
});
function add(name, start, end) {
setup(function() {
list[name] = factory(name, start, end);
subject.add(list[name]);
});
}
add('before', 10, 100);
add('on', 50, 100);
add('just after', 51, 100);
add('after', 60, 100);
test('when items need to be removed', function() {
assert.isFalse(subject.synced, 'not synced');
subject.synced = true;
subject.removeFutureIntervals(50);
var expectedIds = [
'before',
'on'
];
assert.ok(!subject.byId['just after']);
assert.ok(!subject.byId.after);
var ids = subject.items.map(function(item) {
return item._id;
});
assert.deepEqual(ids, expectedIds);
assert.isFalse(subject.synced, 'should be synced');
});
test('when last item needs to be removed', function() {
subject.removeFutureIntervals(59);
var expectedIds = [
'before',
'on',
'just after'
];
var ids = subject.items.map(function(item) {
return item._id;
});
assert.deepEqual(ids, expectedIds);
});
});
suite('#removePastIntervals', function() {
var list;
setup(function() {
subject.items = [];
list = {};
});
function add(name, start, end) {
setup(function() {
list[name] = factory(name, start, end);
subject.add(list[name]);
});
}
add('overlap all', 1, 1000);
add('ends before', 2, 50);
add('ends on 1', 3, 150);
add('ends on 2', 4, 150);
add('ends on 3', 5, 150);
add('ends just after', 6, 151);
add('ends after', 7, 200);
test('when items need to be removed', function() {
assert.isFalse(subject.synced, 'not synced');
subject.synced = true;
subject.removePastIntervals(150);
var expectedIds = [
'overlap all',
'ends just after',
'ends after'
];
assert.ok(!subject.byId['ends on 1']);
assert.ok(!subject.byId['ends on 2']);
assert.ok(!subject.byId['ends on 3']);
var ids = subject.items.map(function(item) {
assert.ok(subject.byId[item._id], 'should have: ' + item._id);
return item._id;
});
assert.deepEqual(ids, expectedIds);
assert.isFalse(subject.synced, 'should be synced');
});
});
suite('#remove', function() {
test('when removing nonexisting item', function() {
subject.synced = true;
var result = subject.remove({ start: 100 });
assert.isFalse(result, 'should return false when item is not removed');
assert.isTrue(subject.synced, 'should not remark sync to false');
});
test('item that does not share start', function() {
subject.synced = true;
var result = subject.remove(items.middle);
assert.isTrue(result, 'remove should return true');
assert.isFalse(subject.synced);
assert.deepEqual(
subject.items,
[
items.before,
items.overlapBefore,
items.after
]
);
});
suite('items that share start time', function() {
var middle;
var addedBefore;
var addedAfter;
setup(function() {
middle = items.before;
subject.items = [];
subject.synced = false;
addedBefore = factory(10, middle[subject.START], middle[subject.END]);
addedAfter = factory(12, middle[subject.START], middle[subject.END]);
// its going to shuffle
// each item is going to displace
// the next pushing it further in
// the array.
subject.add(addedBefore);
subject.add(middle);
subject.add(addedAfter);
assert.deepEqual(
subject.items,
[
addedAfter,
middle,
addedBefore
]
);
});
test('remove last added item', function() {
subject.remove(addedAfter);
assert.deepEqual(
subject.items,
[
middle,
addedBefore
]
);
assert.ok(!subject.byId[addedAfter._id]);
});
test('remove first added item', function() {
subject.remove(addedBefore);
assert.deepEqual(
subject.items,
[
addedAfter,
middle
]
);
});
test('remove middle item', function() {
subject.remove(middle);
assert.deepEqual(
subject.items,
[
addedAfter,
addedBefore
]
);
});
});
});
suite('Node', function() {
setup(function() {
subject.build();
subject = subject.rootNode;
});
suite('node alignment', function() {
test('base alignment', function() {
var left = subject.left;
var right = subject.right;
assert.isTrue(
(left.median < subject.median),
'lower values should be on the left'
);
assert.isTrue(
(right.median > subject.median),
'higher values should be on the right'
);
});
});
test('#traverse', function() {
var results = [];
subject.traverse(span, function(item, node) {
results.push(item);
assert.instanceOf(node, IntervalTree.Node);
});
assert.deepEqual(
results,
[items.overlapBefore, items.middle]
);
});
suite('#query', function() {
var sublist;
var id;
setup(function() {
id = 0;
sublist = [];
});
function compareDateMS(aObj, bObj) {
var a = aObj._startDateMS;
var b = bObj._startDateMS;
return compare(a, b);
}
function orderedAdd(item, arr) {
var idx = binsearch.insert(
arr,
item,
compareDateMS
);
arr.splice(idx, 0, item);
}
function add(start, end) {
var item = factory(id++, start, end);
orderedAdd(item, sublist);
return item;
}
test('large dataset with gaps', function() {
var expected = [];
var i = 0;
// create some out of range in lower bounds
for (i = 0; i < 1021; i++) {
add(i, i + 200);
}
// create two really huge spans
for (i = 0; i < 75; i++) {
orderedAdd(add(i, 5500), expected);
}
for (i = 0; i < 1099; i++) {
orderedAdd(add(5201 + i, 6000 + i), expected);
}
orderedAdd(add(7000, 7070), expected);
orderedAdd(add(8000, 8100), expected);
orderedAdd(add(9000, 9001), expected);
// middle values
orderedAdd(add(10200, 10500), expected);
orderedAdd(add(10300, 10500), expected);
orderedAdd(add(10400, 10500), expected);
orderedAdd(add(10000, 10800), expected);
// yahoo recurring all day events have same start/end dates
orderedAdd(add(10000, 10000), expected);
// create some out of range in upper bounds
for (i = 0; i < 1021; i++) {
add(i + 2500, i + 2505);
}
subject = new IntervalTree(sublist);
var result = subject.query(new Timespan(
5201,
11750
));
// this deep equality assertion is more expensive
// then the entire process of building
// the tree and finding the results...
assert.deepEqual(
result,
expected
);
});
test('basic start range query', function() {
var range = new Timespan(
80,
900
);
assert.deepEqual(
subject.query(range),
[items.before]
);
});
test('basic end range query', function() {
var range = new Timespan(
1600,
1800
);
assert.deepEqual(
subject.query(range),
[items.after]
);
});
test('basic middle query', function() {
var range = new Timespan(
expectedRange.start,
expectedRange.end
);
var results = subject.query(range);
assert.deepEqual(
results,
[
items.overlapBefore,
items.middle
]
);
});
});
});
suite('#createIndex / #index', function() {
var one;
var two;
var three;
setup(function() {
one = Factory('busytime', { eventId: 'xxx' });
two = Factory('busytime', { eventId: 'xxx' });
three = Factory('busytime', { eventId: 'xxx' });
subject.createIndex('eventId');
subject.add(one);
subject.add(two);
subject.add(three);
});
test('add', function() {
assert.deepEqual(
subject.index('eventId', one.eventId),
[one, two, three]
);
});
test('remove middle', function() {
subject.remove(two);
assert.deepEqual(
subject.index('eventId', one.eventId),
[one, three]
);
});
test('remove start', function() {
subject.remove(one);
assert.deepEqual(
subject.index('eventId', one.eventId),
[two, three]
);
});
test('remove end', function() {
subject.remove(three);
assert.deepEqual(
subject.index('eventId', one.eventId),
[one, two]
);
});
test('remove all', function() {
subject.remove(one);
subject.remove(two);
subject.remove(three);
assert.ok(!subject.index('eventId', one.eventId));
});
});
});
});