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:
290
scratch-vm/test/unit/engine_thread.js
Normal file
290
scratch-vm/test/unit/engine_thread.js
Normal file
@@ -0,0 +1,290 @@
|
||||
const test = require('tap').test;
|
||||
const Thread = require('../../src/engine/thread');
|
||||
const RenderedTarget = require('../../src/sprites/rendered-target');
|
||||
const Sprite = require('../../src/sprites/sprite');
|
||||
const Runtime = require('../../src/engine/runtime');
|
||||
|
||||
test('spec', t => {
|
||||
t.type(Thread, 'function');
|
||||
|
||||
const th = new Thread('arbitraryString');
|
||||
t.type(th, 'object');
|
||||
t.ok(th instanceof Thread);
|
||||
t.type(th.pushStack, 'function');
|
||||
t.type(th.reuseStackForNextBlock, 'function');
|
||||
t.type(th.popStack, 'function');
|
||||
t.type(th.stopThisScript, 'function');
|
||||
t.type(th.peekStack, 'function');
|
||||
t.type(th.peekStackFrame, 'function');
|
||||
t.type(th.peekParentStackFrame, 'function');
|
||||
t.type(th.pushReportedValue, 'function');
|
||||
t.type(th.initParams, 'function');
|
||||
t.type(th.pushParam, 'function');
|
||||
t.type(th.peekStack, 'function');
|
||||
t.type(th.getParam, 'function');
|
||||
t.type(th.atStackTop, 'function');
|
||||
t.type(th.goToNextBlock, 'function');
|
||||
t.type(th.isRecursiveCall, 'function');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('pushStack', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('popStack', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
t.strictEquals(th.popStack(), 'arbitraryString');
|
||||
t.strictEquals(th.popStack(), undefined);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('atStackTop', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
th.pushStack('secondString');
|
||||
t.strictEquals(th.atStackTop(), false);
|
||||
th.popStack();
|
||||
t.strictEquals(th.atStackTop(), true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('reuseStackForNextBlock', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
th.reuseStackForNextBlock('secondString');
|
||||
t.strictEquals(th.popStack(), 'secondString');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('peekStackFrame', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
t.strictEquals(th.peekStackFrame().warpMode, false);
|
||||
th.popStack();
|
||||
t.strictEquals(th.peekStackFrame(), null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('peekParentStackFrame', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
th.peekStackFrame().warpMode = true;
|
||||
t.strictEquals(th.peekParentStackFrame(), null);
|
||||
th.pushStack('secondString');
|
||||
t.strictEquals(th.peekParentStackFrame().warpMode, true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('pushReportedValue', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
th.pushStack('secondString');
|
||||
th.pushReportedValue('value');
|
||||
t.strictEquals(th.justReported, 'value');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('peekStack', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
t.strictEquals(th.peekStack(), 'arbitraryString');
|
||||
th.popStack();
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('PushGetParam', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
th.pushStack('arbitraryString');
|
||||
th.initParams();
|
||||
th.pushParam('testParam', 'testValue');
|
||||
t.strictEquals(th.peekStackFrame().params.testParam, 'testValue');
|
||||
t.strictEquals(th.getParam('testParam'), 'testValue');
|
||||
// Params outside of define stack always evaluate to null
|
||||
t.strictEquals(th.getParam('nonExistentParam'), null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('goToNextBlock', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
const r = new Runtime();
|
||||
const s = new Sprite(null, r);
|
||||
const rt = new RenderedTarget(s, r);
|
||||
const block1 = {fields: Object,
|
||||
id: 'arbitraryString',
|
||||
inputs: Object,
|
||||
STEPS: Object,
|
||||
block: 'fakeBlock',
|
||||
name: 'STEPS',
|
||||
next: 'secondString',
|
||||
opcode: 'motion_movesteps',
|
||||
parent: null,
|
||||
shadow: false,
|
||||
topLevel: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
const block2 = {fields: Object,
|
||||
id: 'secondString',
|
||||
inputs: Object,
|
||||
STEPS: Object,
|
||||
block: 'fakeBlock',
|
||||
name: 'STEPS',
|
||||
next: null,
|
||||
opcode: 'procedures_call',
|
||||
mutation: {proccode: 'fakeCode'},
|
||||
parent: null,
|
||||
shadow: false,
|
||||
topLevel: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
rt.blocks.createBlock(block1);
|
||||
rt.blocks.createBlock(block2);
|
||||
rt.blocks.createBlock(block2);
|
||||
th.target = rt;
|
||||
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
th.pushStack('secondString');
|
||||
t.strictEquals(th.peekStack(), 'secondString');
|
||||
th.goToNextBlock();
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
th.pushStack('secondString');
|
||||
th.pushStack('arbitraryString');
|
||||
t.strictEquals(th.peekStack(), 'arbitraryString');
|
||||
th.goToNextBlock();
|
||||
t.strictEquals(th.peekStack(), 'secondString');
|
||||
th.goToNextBlock();
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('stopThisScript', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
const r = new Runtime();
|
||||
const s = new Sprite(null, r);
|
||||
const rt = new RenderedTarget(s, r);
|
||||
const block1 = {fields: Object,
|
||||
id: 'arbitraryString',
|
||||
inputs: Object,
|
||||
STEPS: Object,
|
||||
block: 'fakeBlock',
|
||||
name: 'STEPS',
|
||||
next: null,
|
||||
opcode: 'motion_movesteps',
|
||||
parent: null,
|
||||
shadow: false,
|
||||
topLevel: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
const block2 = {fields: Object,
|
||||
id: 'secondString',
|
||||
inputs: Object,
|
||||
STEPS: Object,
|
||||
block: 'fakeBlock',
|
||||
name: 'STEPS',
|
||||
next: null,
|
||||
opcode: 'procedures_call',
|
||||
mutation: {proccode: 'fakeCode'},
|
||||
parent: null,
|
||||
shadow: false,
|
||||
topLevel: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
rt.blocks.createBlock(block1);
|
||||
rt.blocks.createBlock(block2);
|
||||
th.target = rt;
|
||||
|
||||
th.stopThisScript();
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
th.pushStack('arbitraryString');
|
||||
t.strictEquals(th.peekStack(), 'arbitraryString');
|
||||
th.stopThisScript();
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
th.pushStack('arbitraryString');
|
||||
th.pushStack('secondString');
|
||||
th.stopThisScript();
|
||||
t.strictEquals(th.peekStack(), null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('isRecursiveCall', t => {
|
||||
const th = new Thread('arbitraryString');
|
||||
const r = new Runtime();
|
||||
const s = new Sprite(null, r);
|
||||
const rt = new RenderedTarget(s, r);
|
||||
const block1 = {fields: Object,
|
||||
id: 'arbitraryString',
|
||||
inputs: Object,
|
||||
STEPS: Object,
|
||||
block: 'fakeBlock',
|
||||
name: 'STEPS',
|
||||
next: null,
|
||||
opcode: 'motion_movesteps',
|
||||
parent: null,
|
||||
shadow: false,
|
||||
topLevel: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
const block2 = {fields: Object,
|
||||
id: 'secondString',
|
||||
inputs: Object,
|
||||
STEPS: Object,
|
||||
block: 'fakeBlock',
|
||||
name: 'STEPS',
|
||||
next: null,
|
||||
opcode: 'procedures_call',
|
||||
mutation: {proccode: 'fakeCode'},
|
||||
parent: null,
|
||||
shadow: false,
|
||||
topLevel: true,
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
rt.blocks.createBlock(block1);
|
||||
rt.blocks.createBlock(block2);
|
||||
th.target = rt;
|
||||
|
||||
const pushStack = id => {
|
||||
th.pushStack(id);
|
||||
th.peekStackFrame().op = {id};
|
||||
};
|
||||
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), false);
|
||||
pushStack('secondString');
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), false);
|
||||
pushStack('arbitraryString');
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), true);
|
||||
pushStack('arbitraryString');
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), true);
|
||||
th.popStack();
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), true);
|
||||
th.popStack();
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), false);
|
||||
th.popStack();
|
||||
t.strictEquals(th.isRecursiveCall('fakeCode'), false);
|
||||
|
||||
t.end();
|
||||
});
|
||||
Reference in New Issue
Block a user