Tone.js/Tone/core/util/IntervalTimeline.test.ts
2019-05-23 14:00:49 -04:00

466 lines
11 KiB
TypeScript

import { expect } from "chai";
import { BasicTests } from "test/helper/Basic";
import { IntervalTimeline, IntervalTimelineEvent } from "./IntervalTimeline";
describe("IntervalTimeline", () => {
BasicTests(IntervalTimeline);
context("inserting/deleting events", () => {
it("accepts events into the timeline", () => {
const sched = new IntervalTimeline();
sched.add({
duration: 0.2,
state: "A",
time: 0,
});
sched.add({
duration: 0.4,
state: "B",
time: 1,
});
sched.add({
duration: 12,
state: "C",
time: 2,
});
sched.dispose();
});
it("computes the lenght of the timeline correctly after adding events", () => {
const sched = new IntervalTimeline();
sched.add({
duration: 0.2,
state: "A",
time: 0,
});
sched.add({
duration: 0.4,
state: "B",
time: 1,
});
sched.add({
duration: 12,
state: "C",
time: 2,
});
expect(sched.length).to.equal(3);
sched.dispose();
});
it("can remove events from the timeline", () => {
const sched = new IntervalTimeline();
const ev0 = {
duration: 0.2,
time: 0,
};
const ev1 = {
duration: 0.2,
time: 0.2,
};
const ev2 = {
duration: 0.2,
time: 0.1,
};
sched.add(ev0);
sched.add(ev1);
sched.add(ev2);
expect(sched.length).to.equal(3);
sched.remove(ev0);
sched.remove(ev1);
expect(sched.length).to.equal(1);
sched.remove(ev2);
expect(sched.length).to.equal(0);
sched.dispose();
});
it("removing on a null set does nothing", () => {
const sched = new IntervalTimeline();
expect(sched.length).to.equal(0);
// @ts-ignore
sched.remove({});
expect(sched.length).to.equal(0);
sched.dispose();
});
it("can add and remove and add again events from the timeline", () => {
const sched = new IntervalTimeline();
const ev0 = {
duration: 0.2,
time: 0,
};
const ev1 = {
duration: 0.2,
time: 0.2,
};
const ev2 = {
duration: 0.2,
time: 0.1,
};
sched.add(ev0);
sched.add(ev1);
sched.add(ev2);
expect(sched.length).to.equal(3);
sched.remove(ev0);
sched.remove(ev1);
expect(sched.length).to.equal(1);
sched.add(ev0);
sched.add(ev1);
expect(sched.length).to.equal(3);
sched.dispose();
});
it("throws an error if events do not have both time and duration attributes", () => {
const sched = new IntervalTimeline();
expect(() => {
// @ts-ignore
sched.add({
time: 0,
});
}).to.throw(Error);
expect(() => {
// @ts-ignore
sched.add({
duration: 0,
});
}).to.throw(Error);
sched.dispose();
});
});
context("getting events", () => {
it("returns null when no events are in the timeline", () => {
const sched = new IntervalTimeline();
expect(sched.get(3)).to.equal(null);
sched.dispose();
});
it("returns the event which overlaps the given time", () => {
const sched = new IntervalTimeline();
sched.add({
duration: Infinity,
state: "A",
time: 0,
});
sched.add({
duration: 0.4,
state: "B",
time: 1,
});
sched.add({
duration: 12,
state: "C",
time: 2,
});
// @ts-ignore
expect(sched.get(0.2).state).to.equal("A");
sched.dispose();
});
it("returns events exclusive of the end time", () => {
const sched = new IntervalTimeline();
sched.add({
duration: 1,
state: "A",
time: 0,
});
// @ts-ignore
expect(sched.get(0.99).state).to.equal("A");
expect(sched.get(1)).to.equal(null);
sched.dispose();
});
it("factors in start position and duration when checking for overlaps", () => {
const sched = new IntervalTimeline();
sched.add({
duration: 0.4,
time: 0,
});
expect(sched.get(0.5)).to.equal(null);
expect(sched.get(-1)).to.equal(null);
expect(sched.get(0)).to.not.equal(null);
expect(sched.get(0.39)).to.not.equal(null);
sched.dispose();
});
it("returns the event whose start is closest to the given time", () => {
const sched = new IntervalTimeline();
sched.add({
duration: Infinity,
state: "A",
time: 0,
});
sched.add({
duration: 0.4,
state: "B",
time: 0.2,
});
sched.add({
duration: 12,
state: "C",
time: 2,
});
// @ts-ignore
expect(sched.get(0.2).state).to.equal("B");
sched.dispose();
});
it("returns the events correctly after some events are removed", () => {
const sched = new IntervalTimeline();
const ev0 = {
duration: 0.2,
state: "A",
time: 0.1,
};
const ev1 = {
duration: 0.3,
state: "B",
time: 0.2,
};
const ev2 = {
duration: Infinity,
state: "C",
time: 0,
};
sched.add(ev0);
sched.add(ev1);
sched.add(ev2);
sched.remove(ev0);
sched.remove(ev1);
// @ts-ignore
expect(sched.get(0.2)).to.not.equal(null);
// @ts-ignore
expect(sched.get(0.2).state).to.equal("C");
sched.dispose();
});
it("can handle many items", () => {
const sched = new IntervalTimeline();
const len = 5000;
const events: IntervalTimelineEvent[] = [];
let duration = 1;
let time = 0;
for (let i = 0; i < len; i++) {
const event = {
duration,
time,
};
time = (time + 3.1) % 109;
duration = (duration + 5.7) % 19;
sched.add(event);
events.push(event);
}
// tslint:disable-next-line: prefer-for-of
for (let j = 0; j < events.length; j++) {
const event = events[j];
const eventVal = sched.get(event.time);
if (eventVal) {
expect(eventVal.time).to.equal(event.time);
}
}
for (let k = 0; k < events.length; k++) {
sched.remove(events[k]);
expect(sched.length).to.equal(events.length - k - 1);
}
sched.dispose();
});
});
context("cancelling", () => {
it("can cancel items after the given time", () => {
const sched = new IntervalTimeline();
for (let i = 5; i < 100; i++) {
sched.add({
duration: 10,
time: i,
});
}
sched.cancel(10);
expect(sched.length).to.equal(5);
sched.cancel(0);
expect(sched.length).to.equal(0);
sched.dispose();
});
it("can cancel items at the given time", () => {
const sched = new IntervalTimeline();
sched.add({
duration: 10,
time: 0,
});
sched.cancel(1);
expect(sched.length).to.equal(1);
sched.cancel(0);
expect(sched.length).to.equal(0);
sched.dispose();
});
});
context("Iterators", () => {
it("iterates over all items and returns and item", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0, duration: 5 });
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEach((event) => {
expect(event).to.be.an("object");
count++;
});
expect(count).to.equal(5);
sched.dispose();
});
it("iterate over null set", () => {
const sched = new IntervalTimeline();
let count = 0;
sched.forEach(() => {
count++;
});
expect(count).to.equal(0);
sched.dispose();
});
it("iterates over all items overlapping the given time", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0, duration: 5 });
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEachAtTime(0.3, (event) => {
expect(event).to.be.an("object");
expect(event.time).to.be.at.most(0.3);
count++;
});
expect(count).to.equal(4);
sched.dispose();
});
it("handles time ranges before the available objects", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEachAtTime(0, () => {
count++;
});
expect(count).to.equal(0);
sched.dispose();
});
it("handles time ranges after the available objects", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEachAtTime(5.5, () => {
count++;
});
expect(count).to.equal(0);
sched.dispose();
});
it("iterates over all items after the given time", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEachFrom(0.2, (event) => {
expect(event).to.be.an("object");
expect(event.time).to.be.gte(0.2);
count++;
});
expect(count).to.equal(3);
count = 0;
sched.forEachFrom(0.35, (event) => {
expect(event.time).to.be.gte(0.35);
count++;
});
expect(count).to.equal(1);
sched.dispose();
});
it("handles time ranges after the available objects", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEachFrom(0.5, () => {
count++;
});
expect(count).to.equal(0);
sched.dispose();
});
it("iterates over all items", () => {
const sched = new IntervalTimeline();
sched.add({ time: 0.1, duration: 5 });
sched.add({ time: 0.2, duration: 5 });
sched.add({ time: 0.3, duration: 5 });
sched.add({ time: 0.4, duration: 5 });
let count = 0;
sched.forEach(() => {
count++;
});
expect(count).to.equal(4);
sched.dispose();
});
it("can remove items during forEach iterations", () => {
const sched = new IntervalTimeline();
for (let i = 0; i < 1000; i++) {
sched.add({ time: i, duration: 0.01 });
}
sched.forEach((event) => {
sched.cancel(event.time);
});
expect(sched.length).to.equal(0);
sched.dispose();
});
it("can remove items during forEachAtTime iterations", () => {
const sched = new IntervalTimeline();
for (let i = 0; i < 1000; i++) {
sched.add({ time: i, duration: Infinity });
}
sched.forEachAtTime(1000, (event) => {
sched.cancel(event.time);
});
expect(sched.length).to.equal(0);
sched.dispose();
});
it("can remove items during forEachFrom iterations", () => {
const sched = new IntervalTimeline();
for (let i = 0; i < 1000; i++) {
sched.add({ time: i, duration: Infinity });
}
sched.forEachFrom(0, (event) => {
sched.remove(event);
});
expect(sched.length).to.equal(0);
sched.dispose();
});
});
});