@threlte/core
Version:
A 3D framework for the web, built on top of Svelte and Three.js
354 lines (353 loc) • 10.9 kB
JavaScript
import { expect, test } from 'vitest';
import { Scheduler } from './Scheduler.js';
test('can run a task', () => {
const scheduler = new Scheduler();
const stage = scheduler.createStage('stage');
stage.createTask('task', () => {
expect(true).toBe(true);
});
scheduler.run(performance.now());
});
test('can run a task with a delta', () => {
const scheduler = new Scheduler();
const stage = scheduler.createStage('stage');
let i = 0;
stage.createTask('task', (delta) => {
if (i === 1) {
expect(delta).toBeGreaterThan(0);
}
i++;
});
scheduler.run(0);
scheduler.run(16); // milliseconds
});
test('can run a task with a delta that is clamped', () => {
const scheduler = new Scheduler({
clampDeltaTo: 0.1
});
const stage = scheduler.createStage('stage');
let i = 0;
stage.createTask('task', (delta) => {
if (i === 1) {
expect(delta).toBeLessThanOrEqual(0.1);
}
i++;
});
scheduler.run(0);
scheduler.run(1000); // 1 second
});
test('can run multiple tasks', () => {
const scheduler = new Scheduler();
const stage = scheduler.createStage('stage');
let i = 0;
stage.createTask('task a', () => {
i++;
});
stage.createTask('task b', () => {
i++;
});
scheduler.run(0);
expect(i).toBe(2);
});
test('can use symbols as stage key', () => {
const scheduler = new Scheduler();
const symbol = Symbol('stage');
const stage = scheduler.createStage(symbol);
let i = 0;
stage.createTask('task a', () => {
i++;
});
stage.createTask('task b', () => {
i++;
});
scheduler.run(0);
expect(i).toBe(2);
const plan = scheduler.getSchedule();
const stages = plan.stages.map((stage) => stage.key);
expect(stages).toEqual([symbol.toString()]);
});
test('can run multiple tasks in order', () => {
const scheduler = new Scheduler();
const stage = scheduler.createStage('stage');
let i = 0;
stage.createTask('task a', () => {
expect(i).toBe(0);
i++;
});
stage.createTask('task b', () => {
expect(i).toBe(1);
i++;
}, {
after: 'task a'
});
scheduler.run(0);
});
test('can run multiple stages', () => {
const scheduler = new Scheduler();
const stageA = scheduler.createStage('stage a');
const stageB = scheduler.createStage('stage b');
let i = 0;
stageA.createTask('task a', () => {
i++;
});
stageB.createTask('task b', () => {
i++;
});
scheduler.run(0);
expect(i).toBe(2);
});
test('can run multiple stages in order', () => {
const scheduler = new Scheduler();
const stageA = scheduler.createStage('stage a');
const stageB = scheduler.createStage('stage b', {
after: 'stage a'
});
let i = 0;
stageA.createTask('task a', () => {
expect(i).toBe(0);
i++;
});
stageB.createTask('task b', () => {
expect(i).toBe(1);
i++;
});
scheduler.run(0);
});
test('can run multiple stages in order with multiple tasks', () => {
const scheduler = new Scheduler();
const stageA = scheduler.createStage('stage a');
const stageB = scheduler.createStage('stage b', {
after: 'stage a'
});
let i = 0;
stageA.createTask('task a', () => {
expect(i).toBe(0);
i++;
});
stageA.createTask('task b', () => {
expect(i).toBe(1);
i++;
});
stageB.createTask('task c', () => {
expect(i).toBe(2);
i++;
});
stageB.createTask('task d', () => {
expect(i).toBe(3);
i++;
});
scheduler.run(0);
});
test('can run stages in order that have dependencies on other stages that are not yet created', () => {
const scheduler = new Scheduler();
// first create stage B, then stage A, then stage C
const stageB = scheduler.createStage('stage b');
const stageA = scheduler.createStage('stage a', {
before: 'stage b'
});
const stageC = scheduler.createStage('stage c', {
after: 'stage b'
});
let i = 0;
stageA.createTask('task a', () => {
expect(i).toBe(0);
i++;
});
stageB.createTask('task b', () => {
expect(i).toBe(1);
i++;
});
stageC.createTask('task c', () => {
expect(i).toBe(2);
i++;
});
scheduler.run(0);
});
test('can add and remove stages', () => {
let Stages;
(function (Stages) {
Stages["stageA"] = "stage a";
Stages["stageB"] = "stage b";
Stages["stageC"] = "stage c";
})(Stages || (Stages = {}));
const scheduler = new Scheduler();
scheduler.createStage(Stages.stageA);
scheduler.createStage(Stages.stageB, {
after: Stages.stageA
});
scheduler.createStage(Stages.stageC, {
after: [Stages.stageA, Stages.stageB]
});
const expectedStagesOrder = [Stages.stageA, Stages.stageB, Stages.stageC];
const plan = scheduler.getSchedule();
const actualStagesOrder = plan.stages.map((stage) => stage.key);
expect(actualStagesOrder).toEqual(expectedStagesOrder);
scheduler.removeStage(Stages.stageB);
const newPlan = scheduler.getSchedule();
const newActualStagesOrder = newPlan.stages.map((stage) => stage.key);
const newExpectedStagesOrder = [Stages.stageA, Stages.stageC];
expect(newActualStagesOrder).toEqual(newExpectedStagesOrder);
});
test('can add and remove tasks', () => {
let Tasks;
(function (Tasks) {
Tasks["taskA"] = "task a";
Tasks["taskB"] = "task b";
Tasks["taskC"] = "task c";
})(Tasks || (Tasks = {}));
const scheduler = new Scheduler();
const stage = scheduler.createStage('stage');
// eslint-disable-next-line @typescript-eslint/no-empty-function
stage.createTask(Tasks.taskA, () => { });
// eslint-disable-next-line @typescript-eslint/no-empty-function
stage.createTask(Tasks.taskB, () => { }, {
after: Tasks.taskA
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
stage.createTask(Tasks.taskC, () => { }, {
after: [Tasks.taskA, Tasks.taskB]
});
const expectedTasksOrder = [Tasks.taskA, Tasks.taskB, Tasks.taskC];
const plan = scheduler.getSchedule();
const actualTasksOrder = plan.stages
.find((stage) => stage.key === 'stage')
?.tasks?.map((task) => task);
expect(actualTasksOrder).toEqual(expectedTasksOrder);
stage.removeTask(Tasks.taskB);
const newPlan = scheduler.getSchedule();
const newActualTasksOrder = newPlan.stages
.find((stage) => stage.key === 'stage')
?.tasks?.map((task) => task);
const newExpectedStagesOrder = [Tasks.taskA, Tasks.taskC];
expect(newActualTasksOrder).toEqual(newExpectedStagesOrder);
});
test('stress test', () => {
let i = 0;
const scheduler = new Scheduler();
const defaultStage = scheduler.createStage('default stage');
defaultStage.createTask('move object', () => {
expect(i).toBe(2);
i++;
});
defaultStage.createTask('move camera', () => {
expect(i).toBe(3);
i++;
}, {
after: 'move object'
});
defaultStage.createTask('trigger sounds', () => {
expect(i).toBe(4);
i++;
}, {
after: 'move camera'
});
const renderStage = scheduler.createStage('render stage', {
after: 'default stage'
});
renderStage.createTask('render', () => {
expect(i).toBe(6);
i++;
});
defaultStage.createTask('after other tasks', () => {
expect(i).toBe(5);
i++;
}, {
after: ['move camera', 'trigger sounds']
});
const simulationStage = scheduler.createStage('physics stage', {
before: 'default stage'
});
simulationStage.createTask('physics', () => {
expect(i).toBe(1);
i++;
});
const frameAnalyticsStartStage = scheduler.createStage('frame analytics start', {
before: 'physics stage'
});
const frameAnalyticsEndStage = scheduler.createStage('frame analytics end', {
after: 'render stage'
});
frameAnalyticsStartStage.createTask('frame-analytics-start', () => {
expect(i).toBe(0);
i++;
});
frameAnalyticsEndStage.createTask('frame-analytics-end', () => {
expect(i).toBe(7);
i++;
});
scheduler.run(0);
});
test('stress test II', () => {
const scheduler = new Scheduler();
let Stages;
(function (Stages) {
Stages["default"] = "default stage";
Stages["render"] = "render stage";
Stages["physics"] = "physics stage";
Stages["frameStart"] = "frame analytics start";
Stages["frameEnd"] = "frame analytics end";
Stages["someOther"] = "some other stage";
Stages["afterSomeOther"] = "after some other stage";
})(Stages || (Stages = {}));
scheduler.createStage(Stages.default);
scheduler.createStage(Stages.render, {
after: Stages.default
});
scheduler.createStage(Stages.physics, {
before: Stages.default
});
scheduler.createStage(Stages.frameStart, {
before: Stages.physics
});
scheduler.createStage(Stages.frameEnd, {
after: Stages.render
});
scheduler.createStage(Stages.afterSomeOther, {
after: Stages.someOther
});
scheduler.createStage(Stages.someOther, {
after: Stages.default,
before: Stages.render
});
const schedule = scheduler.getSchedule();
const stages = schedule.stages.map((stage) => stage.key);
const expectedStages = [
Stages.frameStart,
Stages.physics,
Stages.default,
Stages.someOther,
Stages.afterSomeOther,
Stages.render,
Stages.frameEnd
];
expect(stages).toEqual(expectedStages);
scheduler.run(0);
});
test('receives event notitifactions on stages and tasks', () => {
const scheduler = new Scheduler();
let receivedStageAdded = false;
let receivedStageRemoved = false;
scheduler.on('node:added', () => {
receivedStageAdded = true;
});
scheduler.on('node:removed', () => {
receivedStageRemoved = true;
});
const stage = scheduler.createStage('stage');
let receivedTaskAdded = false;
let receivedTaskRemoved = false;
stage.on('node:added', () => {
receivedTaskAdded = true;
});
stage.on('node:removed', () => {
receivedTaskRemoved = true;
});
// eslint-disable-next-line @typescript-eslint/no-empty-function
stage.createTask('task', () => { });
stage.removeTask('task');
scheduler.removeStage('stage');
expect(receivedStageAdded).toBe(true);
expect(receivedStageRemoved).toBe(true);
expect(receivedTaskAdded).toBe(true);
expect(receivedTaskRemoved).toBe(true);
});