Files
scratch-vm/test/unit/engine_sequencer.js

188 lines
5.2 KiB
JavaScript

const {test} = require('node:test');
const assert = require('node:assert');
const Sequencer = require('../../src/engine/sequencer');
const Runtime = require('../../src/engine/runtime');
const Thread = require('../../src/engine/thread');
const RenderedTarget = require('../../src/sprites/rendered-target');
const Sprite = require('../../src/sprites/sprite');
test('spec', () => {
assert.strictEqual(typeof Sequencer, 'function');
const r = new Runtime();
const s = new Sequencer(r);
assert.strictEqual(typeof s, 'object');
assert.ok(s instanceof Sequencer);
assert.strictEqual(typeof s.stepThreads, 'function');
assert.strictEqual(typeof s.stepThread, 'function');
assert.strictEqual(typeof s.stepToBranch, 'function');
assert.strictEqual(typeof s.stepToProcedure, 'function');
assert.strictEqual(typeof s.retireThread, 'function');
});
const randomString = function () {
const top = Math.random().toString(36);
return top.substring(7);
};
const generateBlock = function (id) {
const block = {fields: Object,
id: id,
inputs: {},
STEPS: Object,
block: 'fakeBlock',
name: 'fakeName',
next: null,
opcode: 'procedures_definition',
mutation: {proccode: 'fakeCode'},
parent: null,
shadow: false,
topLevel: true,
x: 0,
y: 0
};
return block;
};
const generateBlockInput = function (id, next, inp) {
const block = {fields: Object,
id: id,
inputs: {SUBSTACK: {block: inp, name: 'SUBSTACK'}},
STEPS: Object,
block: 'fakeBlock',
name: 'fakeName',
next: next,
opcode: 'procedures_definition',
mutation: {proccode: 'fakeCode'},
parent: null,
shadow: false,
topLevel: true,
x: 0,
y: 0
};
return block;
};
const generateThread = function (runtime) {
const s = new Sprite(null, runtime);
const rt = new RenderedTarget(s, runtime);
const th = new Thread(randomString());
let next = randomString();
let inp = randomString();
let name = th.topBlock;
const pushStack = id => {
th.pushStack(id);
th.peekStackFrame().op = {id};
};
rt.blocks.createBlock(generateBlockInput(name, next, inp));
pushStack(name);
rt.blocks.createBlock(generateBlock(inp));
for (let i = 0; i < 10; i++) {
name = next;
next = randomString();
inp = randomString();
rt.blocks.createBlock(generateBlockInput(name, next, inp));
pushStack(name);
rt.blocks.createBlock(generateBlock(inp));
}
rt.blocks.createBlock(generateBlock(next));
pushStack(next);
th.target = rt;
th.blockContainer = rt.blocks;
runtime.threads.push(th);
return th;
};
test('stepThread', () => {
const r = new Runtime();
const s = new Sequencer(r);
let th = generateThread(r);
assert.notStrictEqual(th.status, Thread.STATUS_DONE);
s.stepThread(th);
assert.strictEqual(th.status, Thread.STATUS_DONE);
th = generateThread(r);
th.status = Thread.STATUS_YIELD;
s.stepThread(th);
assert.notStrictEqual(th.status, Thread.STATUS_DONE);
th.status = Thread.STATUS_PROMISE_WAIT;
s.stepThread(th);
assert.notStrictEqual(th.status, Thread.STATUS_DONE);
});
test('stepToBranch', () => {
const r = new Runtime();
const s = new Sequencer(r);
const th = generateThread(r);
s.stepToBranch(th, 2, false);
assert.strictEqual(th.peekStack(), null);
th.popStack();
s.stepToBranch(th, 1, false);
assert.strictEqual(th.peekStack(), null);
th.popStack();
th.popStack();
s.stepToBranch(th, 1, false);
assert.notStrictEqual(th.peekStack(), null);
});
test('retireThread', () => {
const r = new Runtime();
const s = new Sequencer(r);
const th = generateThread(r);
assert.strictEqual(th.stack.length, 12);
s.retireThread(th);
assert.strictEqual(th.stack.length, 0);
assert.strictEqual(th.status, Thread.STATUS_DONE);
});
test('stepToProcedure', () => {
const r = new Runtime();
const s = new Sequencer(r);
const th = generateThread(r);
let expectedBlock = th.peekStack();
s.stepToProcedure(th, '');
assert.strictEqual(th.peekStack(), expectedBlock);
s.stepToProcedure(th, 'faceCode');
assert.strictEqual(th.peekStack(), expectedBlock);
th.target.blocks.createBlock({
id: 'internalId',
opcode: 'procedures_prototype',
mutation: {
proccode: 'othercode'
}
});
expectedBlock = th.stack[th.stack.length - 4];
th.target.blocks.getBlock(expectedBlock).inputs.custom_block = {
type: 'custom_block',
block: 'internalId'
};
s.stepToProcedure(th, 'othercode');
assert.strictEqual(th.peekStack(), expectedBlock);
});
test('stepThreads', () => {
const r = new Runtime();
r.currentStepTime = Infinity;
const s = new Sequencer(r);
assert.strictEqual(s.stepThreads().length, 0);
generateThread(r);
assert.strictEqual(r.threads.length, 1);
// Threads should be marked DONE and removed in the same step they finish.
assert.strictEqual(s.stepThreads().length, 1);
});