2017-04-20 19:17:05 -04:00
|
|
|
const test = require('tap').test;
|
|
|
|
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');
|
2016-04-26 09:49:52 -04:00
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
test('spec', t => {
|
2016-04-26 09:49:52 -04:00
|
|
|
t.type(Sequencer, 'function');
|
2017-02-16 14:30:00 -05:00
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
const r = new Runtime();
|
|
|
|
const s = new Sequencer(r);
|
2017-02-16 14:30:00 -05:00
|
|
|
|
|
|
|
t.type(s, 'object');
|
|
|
|
t.ok(s instanceof Sequencer);
|
|
|
|
|
|
|
|
t.type(s.stepThreads, 'function');
|
|
|
|
t.type(s.stepThread, 'function');
|
|
|
|
t.type(s.stepToBranch, 'function');
|
|
|
|
t.type(s.stepToProcedure, 'function');
|
|
|
|
t.type(s.retireThread, 'function');
|
|
|
|
|
|
|
|
t.end();
|
|
|
|
});
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
const randomString = function () {
|
|
|
|
const top = Math.random().toString(36);
|
2017-02-16 14:30:00 -05:00
|
|
|
return top.substring(7);
|
|
|
|
};
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
const generateBlock = function (id) {
|
|
|
|
const block = {fields: Object,
|
2017-02-16 14:30:00 -05:00
|
|
|
id: id,
|
|
|
|
inputs: {},
|
|
|
|
STEPS: Object,
|
|
|
|
block: 'fakeBlock',
|
|
|
|
name: 'fakeName',
|
|
|
|
next: null,
|
2017-11-16 14:17:08 -05:00
|
|
|
opcode: 'procedures_definition',
|
2017-02-16 14:30:00 -05:00
|
|
|
mutation: {proccode: 'fakeCode'},
|
|
|
|
parent: null,
|
|
|
|
shadow: false,
|
|
|
|
topLevel: true,
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
};
|
|
|
|
return block;
|
|
|
|
};
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
const generateBlockInput = function (id, next, inp) {
|
|
|
|
const block = {fields: Object,
|
2017-02-16 14:30:00 -05:00
|
|
|
id: id,
|
|
|
|
inputs: {SUBSTACK: {block: inp, name: 'SUBSTACK'}},
|
|
|
|
STEPS: Object,
|
|
|
|
block: 'fakeBlock',
|
|
|
|
name: 'fakeName',
|
|
|
|
next: next,
|
2017-11-16 14:17:08 -05:00
|
|
|
opcode: 'procedures_definition',
|
2017-02-16 14:30:00 -05:00
|
|
|
mutation: {proccode: 'fakeCode'},
|
|
|
|
parent: null,
|
|
|
|
shadow: false,
|
|
|
|
topLevel: true,
|
|
|
|
x: 0,
|
|
|
|
y: 0
|
|
|
|
};
|
|
|
|
return block;
|
|
|
|
};
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
const generateThread = function (runtime) {
|
2019-01-29 15:30:45 -05:00
|
|
|
const s = new Sprite(null, runtime);
|
2017-04-20 19:17:05 -04:00
|
|
|
const rt = new RenderedTarget(s, runtime);
|
|
|
|
const th = new Thread(randomString());
|
2017-02-16 14:30:00 -05:00
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
let next = randomString();
|
|
|
|
let inp = randomString();
|
|
|
|
let name = th.topBlock;
|
2017-02-16 14:30:00 -05:00
|
|
|
|
|
|
|
rt.blocks.createBlock(generateBlockInput(name, next, inp));
|
|
|
|
th.pushStack(name);
|
|
|
|
rt.blocks.createBlock(generateBlock(inp));
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
for (let i = 0; i < 10; i++) {
|
2017-02-16 14:30:00 -05:00
|
|
|
name = next;
|
|
|
|
next = randomString();
|
|
|
|
inp = randomString();
|
|
|
|
|
|
|
|
rt.blocks.createBlock(generateBlockInput(name, next, inp));
|
|
|
|
th.pushStack(name);
|
|
|
|
rt.blocks.createBlock(generateBlock(inp));
|
|
|
|
}
|
|
|
|
rt.blocks.createBlock(generateBlock(next));
|
|
|
|
th.pushStack(next);
|
|
|
|
th.target = rt;
|
2018-04-10 16:29:51 -04:00
|
|
|
th.blockContainer = rt.blocks;
|
|
|
|
|
2017-02-16 14:30:00 -05:00
|
|
|
runtime.threads.push(th);
|
|
|
|
|
|
|
|
return th;
|
|
|
|
};
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
test('stepThread', t => {
|
|
|
|
const r = new Runtime();
|
|
|
|
const s = new Sequencer(r);
|
|
|
|
let th = generateThread(r);
|
2017-02-16 14:30:00 -05:00
|
|
|
t.notEquals(th.status, Thread.STATUS_DONE);
|
|
|
|
s.stepThread(th);
|
|
|
|
t.strictEquals(th.status, Thread.STATUS_DONE);
|
|
|
|
th = generateThread(r);
|
|
|
|
th.status = Thread.STATUS_YIELD;
|
|
|
|
s.stepThread(th);
|
|
|
|
t.notEquals(th.status, Thread.STATUS_DONE);
|
|
|
|
th.status = Thread.STATUS_PROMISE_WAIT;
|
|
|
|
s.stepThread(th);
|
|
|
|
t.notEquals(th.status, Thread.STATUS_DONE);
|
|
|
|
|
|
|
|
t.end();
|
|
|
|
});
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
test('stepToBranch', t => {
|
|
|
|
const r = new Runtime();
|
|
|
|
const s = new Sequencer(r);
|
|
|
|
const th = generateThread(r);
|
2017-02-16 14:30:00 -05:00
|
|
|
s.stepToBranch(th, 2, false);
|
|
|
|
t.strictEquals(th.peekStack(), null);
|
|
|
|
th.popStack();
|
|
|
|
s.stepToBranch(th, 1, false);
|
|
|
|
t.strictEquals(th.peekStack(), null);
|
|
|
|
th.popStack();
|
|
|
|
th.popStack();
|
|
|
|
s.stepToBranch(th, 1, false);
|
|
|
|
t.notEquals(th.peekStack(), null);
|
|
|
|
|
|
|
|
t.end();
|
|
|
|
});
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
test('retireThread', t => {
|
|
|
|
const r = new Runtime();
|
|
|
|
const s = new Sequencer(r);
|
|
|
|
const th = generateThread(r);
|
2017-02-16 14:30:00 -05:00
|
|
|
t.strictEquals(th.stack.length, 12);
|
|
|
|
s.retireThread(th);
|
|
|
|
t.strictEquals(th.stack.length, 0);
|
|
|
|
t.strictEquals(th.status, Thread.STATUS_DONE);
|
|
|
|
|
|
|
|
t.end();
|
|
|
|
});
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
test('stepToProcedure', t => {
|
|
|
|
const r = new Runtime();
|
|
|
|
const s = new Sequencer(r);
|
|
|
|
const th = generateThread(r);
|
|
|
|
let expectedBlock = th.peekStack();
|
2017-02-16 14:30:00 -05:00
|
|
|
s.stepToProcedure(th, '');
|
|
|
|
t.strictEquals(th.peekStack(), expectedBlock);
|
|
|
|
s.stepToProcedure(th, 'faceCode');
|
|
|
|
t.strictEquals(th.peekStack(), expectedBlock);
|
2017-10-10 12:19:27 -04:00
|
|
|
|
|
|
|
th.target.blocks.createBlock({
|
|
|
|
id: 'internalId',
|
2017-11-16 14:17:08 -05:00
|
|
|
opcode: 'procedures_prototype',
|
2017-10-10 12:19:27 -04:00
|
|
|
mutation: {
|
|
|
|
proccode: 'othercode'
|
|
|
|
}
|
|
|
|
});
|
2017-02-16 14:30:00 -05:00
|
|
|
expectedBlock = th.stack[th.stack.length - 4];
|
2017-10-10 12:19:27 -04:00
|
|
|
th.target.blocks.getBlock(expectedBlock).inputs.custom_block = {
|
|
|
|
type: 'custom_block',
|
|
|
|
block: 'internalId'
|
|
|
|
};
|
2017-02-16 14:30:00 -05:00
|
|
|
s.stepToProcedure(th, 'othercode');
|
|
|
|
t.strictEquals(th.peekStack(), expectedBlock);
|
|
|
|
|
|
|
|
|
|
|
|
t.end();
|
|
|
|
});
|
|
|
|
|
2017-04-20 19:17:05 -04:00
|
|
|
test('stepThreads', t => {
|
|
|
|
const r = new Runtime();
|
2017-02-16 14:30:00 -05:00
|
|
|
r.currentStepTime = Infinity;
|
2017-04-20 19:17:05 -04:00
|
|
|
const s = new Sequencer(r);
|
2017-02-16 14:30:00 -05:00
|
|
|
t.strictEquals(s.stepThreads().length, 0);
|
|
|
|
generateThread(r);
|
|
|
|
t.strictEquals(r.threads.length, 1);
|
2018-10-31 17:56:12 -04:00
|
|
|
// Threads should be marked DONE and removed in the same step they finish.
|
2017-02-16 14:30:00 -05:00
|
|
|
t.strictEquals(s.stepThreads().length, 1);
|
|
|
|
|
2016-04-26 09:49:52 -04:00
|
|
|
t.end();
|
|
|
|
});
|