Initial commit of 001code-html Scratch frontend project.
Includes scratch-gui, scratch-vm, scratch-blocks, scratch-render, scratch-l10n, and deployment config. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
192
scratch-vm/test/unit/util_task-queue.js
Normal file
192
scratch-vm/test/unit/util_task-queue.js
Normal file
@@ -0,0 +1,192 @@
|
||||
const test = require('tap').test;
|
||||
|
||||
const TaskQueue = require('../../src/util/task-queue');
|
||||
|
||||
const MockTimer = require('../fixtures/mock-timer');
|
||||
const testCompare = require('../fixtures/test-compare');
|
||||
|
||||
// Max tokens = 1000
|
||||
// Refill 1000 tokens per second (1 per millisecond)
|
||||
// Token bucket starts empty
|
||||
// Max total cost of queued tasks = 10000 tokens = 10 seconds
|
||||
const makeTestQueue = () => {
|
||||
const bukkit = new TaskQueue(1000, 1000, {
|
||||
startingTokens: 0,
|
||||
maxTotalCost: 10000
|
||||
});
|
||||
|
||||
const mockTimer = new MockTimer();
|
||||
bukkit._timer = mockTimer;
|
||||
mockTimer.start();
|
||||
|
||||
return bukkit;
|
||||
};
|
||||
|
||||
test('spec', t => {
|
||||
t.type(TaskQueue, 'function');
|
||||
const bukkit = makeTestQueue();
|
||||
|
||||
t.type(bukkit, 'object');
|
||||
|
||||
t.type(bukkit.length, 'number');
|
||||
t.type(bukkit.do, 'function');
|
||||
t.type(bukkit.cancel, 'function');
|
||||
t.type(bukkit.cancelAll, 'function');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('constructor', t => {
|
||||
t.ok(new TaskQueue(1, 1));
|
||||
t.ok(new TaskQueue(1, 1, {}));
|
||||
t.ok(new TaskQueue(1, 1, {startingTokens: 0}));
|
||||
t.ok(new TaskQueue(1, 1, {maxTotalCost: 999}));
|
||||
t.ok(new TaskQueue(1, 1, {startingTokens: 0, maxTotalCost: 999}));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('run tasks', async t => {
|
||||
const bukkit = makeTestQueue();
|
||||
|
||||
const taskResults = [];
|
||||
|
||||
const promises = [
|
||||
bukkit.do(() => {
|
||||
taskResults.push('a');
|
||||
testCompare(t, bukkit._timer.timeElapsed(), '>=', 50, 'Costly task must wait');
|
||||
}, 50),
|
||||
bukkit.do(() => {
|
||||
taskResults.push('b');
|
||||
testCompare(t, bukkit._timer.timeElapsed(), '>=', 60, 'Tasks must run in serial');
|
||||
}, 10),
|
||||
bukkit.do(() => {
|
||||
taskResults.push('c');
|
||||
testCompare(t, bukkit._timer.timeElapsed(), '<=', 70, 'Cheap task should run soon');
|
||||
}, 1)
|
||||
];
|
||||
|
||||
// advance 10 simulated milliseconds per JS tick
|
||||
while (bukkit.length > 0) {
|
||||
await bukkit._timer.advanceMockTimeAsync(10);
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
t.deepEqual(taskResults, ['a', 'b', 'c'], 'All tasks must run in correct order');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('cancel', async t => {
|
||||
const bukkit = makeTestQueue();
|
||||
|
||||
const taskResults = [];
|
||||
const goodCancelMessage = 'Task was canceled correctly';
|
||||
const afterCancelMessage = 'Task was run correctly';
|
||||
const cancelTaskPromise = bukkit.do(
|
||||
() => {
|
||||
taskResults.push('nope');
|
||||
}, 999);
|
||||
const cancelCheckPromise = cancelTaskPromise.then(
|
||||
() => {
|
||||
t.fail('Task should have been canceled');
|
||||
},
|
||||
() => {
|
||||
taskResults.push(goodCancelMessage);
|
||||
}
|
||||
);
|
||||
const keepTaskPromise = bukkit.do(
|
||||
() => {
|
||||
taskResults.push(afterCancelMessage);
|
||||
testCompare(t, bukkit._timer.timeElapsed(), '<', 10, 'Canceled task must not delay other tasks');
|
||||
}, 5);
|
||||
|
||||
// give the bucket a chance to make a mistake
|
||||
await bukkit._timer.advanceMockTimeAsync(1);
|
||||
|
||||
t.equal(bukkit.length, 2);
|
||||
const taskWasCanceled = bukkit.cancel(cancelTaskPromise);
|
||||
t.ok(taskWasCanceled);
|
||||
t.equal(bukkit.length, 1);
|
||||
|
||||
while (bukkit.length > 0) {
|
||||
await bukkit._timer.advanceMockTimeAsync(1);
|
||||
}
|
||||
|
||||
return Promise.all([cancelCheckPromise, keepTaskPromise]).then(() => {
|
||||
t.deepEqual(taskResults, [goodCancelMessage, afterCancelMessage]);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('cancelAll', async t => {
|
||||
const bukkit = makeTestQueue();
|
||||
|
||||
const taskResults = [];
|
||||
const goodCancelMessage1 = 'Task1 was canceled correctly';
|
||||
const goodCancelMessage2 = 'Task2 was canceled correctly';
|
||||
|
||||
const promises = [
|
||||
bukkit.do(() => taskResults.push('nope'), 999).then(
|
||||
() => {
|
||||
t.fail('Task1 should have been canceled');
|
||||
},
|
||||
() => {
|
||||
taskResults.push(goodCancelMessage1);
|
||||
}
|
||||
),
|
||||
bukkit.do(() => taskResults.push('nah'), 999).then(
|
||||
() => {
|
||||
t.fail('Task2 should have been canceled');
|
||||
},
|
||||
() => {
|
||||
taskResults.push(goodCancelMessage2);
|
||||
}
|
||||
)
|
||||
];
|
||||
|
||||
// advance time, but not enough that any task should run
|
||||
await bukkit._timer.advanceMockTimeAsync(100);
|
||||
|
||||
bukkit.cancelAll();
|
||||
|
||||
// advance enough that both tasks would run if they hadn't been canceled
|
||||
await bukkit._timer.advanceMockTimeAsync(10000);
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
t.deepEqual(taskResults, [goodCancelMessage1, goodCancelMessage2], 'Tasks should cancel in order');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('max total cost', async t => {
|
||||
const bukkit = makeTestQueue();
|
||||
|
||||
let numTasks = 0;
|
||||
|
||||
const task = () => ++numTasks;
|
||||
|
||||
// Fill the queue
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
bukkit.do(task, 1000);
|
||||
}
|
||||
|
||||
// This one should be rejected because the queue is full
|
||||
bukkit
|
||||
.do(task, 1000)
|
||||
.then(
|
||||
() => {
|
||||
t.fail('Full queue did not reject task');
|
||||
},
|
||||
() => {
|
||||
t.pass();
|
||||
}
|
||||
);
|
||||
|
||||
while (bukkit.length > 0) {
|
||||
await bukkit._timer.advanceMockTimeAsync(1000);
|
||||
}
|
||||
|
||||
// this should be 10 if the last task is rejected or 11 if it runs
|
||||
t.equal(numTasks, 10);
|
||||
t.end();
|
||||
});
|
||||
Reference in New Issue
Block a user