From 2ffa8eb3334fd3b71cf63ca7b8ebc27ee898bc18 Mon Sep 17 00:00:00 2001 From: "Michael \"Z\" Goddard" Date: Wed, 31 Oct 2018 17:56:12 -0400 Subject: [PATCH] update runtime thread tests Threads are now removed after every "inner" step. Any thread that reaches its DONE conditions is immediately removed instead of requiring an extra step to remove it. As such tests that check the number of threads have been updated to consider this. --- src/engine/runtime.js | 10 +++ .../hat-threads-run-every-frame.js | 68 ++++++++++++------- .../monitor-threads-run-every-frame.js | 20 ++++-- test/integration/monitors.js | 6 +- test/unit/engine_sequencer.js | 3 +- 5 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 2e17500c..a7b10d9b 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -188,6 +188,13 @@ class Runtime extends EventEmitter { */ this._nonMonitorThreadCount = 0; + /** + * All threads that finished running and were removed from this.threads + * by behaviour in Sequencer.stepThreads. + * @type {Array} + */ + this._lastStepDoneThreads = null; + /** * Currently known number of clones, used to enforce clone limit. * @type {number} @@ -1560,6 +1567,9 @@ class Runtime extends EventEmitter { this._emitProjectRunStatus( this.threads.length + doneThreads.length - this._getMonitorThreadCount([...this.threads, ...doneThreads])); + // Store threads that completed this iteration for testing and other + // internal purposes. + this._lastStepDoneThreads = doneThreads; if (this.renderer) { // @todo: Only render when this.redrawRequested or clones rendered. if (this.profiler !== null) { diff --git a/test/integration/hat-threads-run-every-frame.js b/test/integration/hat-threads-run-every-frame.js index 8a80ef76..5d3c3527 100644 --- a/test/integration/hat-threads-run-every-frame.js +++ b/test/integration/hat-threads-run-every-frame.js @@ -45,15 +45,19 @@ test('edge activated hat thread runs once every frame', t => { t.equal(vm.runtime.threads.length, 0); vm.runtime._step(); - t.equal(vm.runtime.threads.length, 1); - checkIsHatThread(t, vm, vm.runtime.threads[0]); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); + let threads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(threads.length, 1); + checkIsHatThread(t, vm, threads[0]); + t.assert(threads[0].status === Thread.STATUS_DONE); // Check that the hat thread is added again when another step is taken vm.runtime._step(); - t.equal(vm.runtime.threads.length, 1); - checkIsHatThread(t, vm, vm.runtime.threads[0]); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); + threads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(threads.length, 1); + checkIsHatThread(t, vm, threads[0]); + t.assert(threads[0].status === Thread.STATUS_DONE); t.end(); }); }); @@ -79,15 +83,19 @@ test('edge activated hat thread not added twice', t => { t.equal(vm.runtime.threads.length, 0); vm.runtime._step(); + let doneThreads = vm.runtime._lastStepDoneThreads; t.equal(vm.runtime.threads.length, 1); + t.equal(doneThreads.length, 0); const prevThread = vm.runtime.threads[0]; checkIsHatThread(t, vm, vm.runtime.threads[0]); t.assert(vm.runtime.threads[0].status === Thread.STATUS_RUNNING); // Check that no new threads are added when another step is taken vm.runtime._step(); + doneThreads = vm.runtime._lastStepDoneThreads; // There should now be one done hat thread and one new hat thread to run t.equal(vm.runtime.threads.length, 1); + t.equal(doneThreads.length, 0); checkIsHatThread(t, vm, vm.runtime.threads[0]); t.assert(vm.runtime.threads[0] === prevThread); t.end(); @@ -115,29 +123,33 @@ test('edge activated hat thread does not interrupt stack click thread', t => { t.equal(vm.runtime.threads.length, 0); vm.runtime._step(); - t.equal(vm.runtime.threads.length, 1); - checkIsHatThread(t, vm, vm.runtime.threads[0]); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); + let doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(doneThreads.length, 1); + checkIsHatThread(t, vm, doneThreads[0]); + t.assert(doneThreads[0].status === Thread.STATUS_DONE); // Add stack click thread on this hat - vm.runtime.toggleScript(vm.runtime.threads[0].topBlock, {stackClick: true}); + vm.runtime.toggleScript(doneThreads[0].topBlock, {stackClick: true}); // Check that the hat thread is added again when another step is taken vm.runtime._step(); - t.equal(vm.runtime.threads.length, 2); + doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(doneThreads.length, 2); let hatThread; let stackClickThread; - if (vm.runtime.threads[0].stackClick) { - stackClickThread = vm.runtime.threads[0]; - hatThread = vm.runtime.threads[1]; + if (doneThreads[0].stackClick) { + stackClickThread = doneThreads[0]; + hatThread = doneThreads[1]; } else { - stackClickThread = vm.runtime.threads[1]; - hatThread = vm.runtime.threads[0]; + stackClickThread = doneThreads[1]; + hatThread = doneThreads[0]; } checkIsHatThread(t, vm, hatThread); checkIsStackClickThread(t, vm, stackClickThread); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); - t.assert(vm.runtime.threads[1].status === Thread.STATUS_DONE); + t.assert(doneThreads[0].status === Thread.STATUS_DONE); + t.assert(doneThreads[1].status === Thread.STATUS_DONE); t.end(); }); }); @@ -163,7 +175,9 @@ test('edge activated hat thread does not interrupt stack click thread', t => { t.equal(vm.runtime.threads.length, 0); vm.runtime._step(); + let doneThreads = vm.runtime._lastStepDoneThreads; t.equal(vm.runtime.threads.length, 1); + t.equal(doneThreads.length, 0); checkIsHatThread(t, vm, vm.runtime.threads[0]); t.assert(vm.runtime.threads[0].status === Thread.STATUS_RUNNING); @@ -174,20 +188,22 @@ test('edge activated hat thread does not interrupt stack click thread', t => { // Check that the hat thread is added again when another step is taken vm.runtime._step(); - t.equal(vm.runtime.threads.length, 2); + doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(doneThreads.length, 2); let hatThread; let stackClickThread; - if (vm.runtime.threads[0].stackClick) { - stackClickThread = vm.runtime.threads[0]; - hatThread = vm.runtime.threads[1]; + if (doneThreads[0].stackClick) { + stackClickThread = doneThreads[0]; + hatThread = doneThreads[1]; } else { - stackClickThread = vm.runtime.threads[1]; - hatThread = vm.runtime.threads[0]; + stackClickThread = doneThreads[1]; + hatThread = doneThreads[0]; } checkIsHatThread(t, vm, hatThread); checkIsStackClickThread(t, vm, stackClickThread); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); - t.assert(vm.runtime.threads[1].status === Thread.STATUS_DONE); + t.assert(doneThreads[0].status === Thread.STATUS_DONE); + t.assert(doneThreads[1].status === Thread.STATUS_DONE); t.end(); }); }); diff --git a/test/integration/monitor-threads-run-every-frame.js b/test/integration/monitor-threads-run-every-frame.js index cebd7314..bcfe5daa 100644 --- a/test/integration/monitor-threads-run-every-frame.js +++ b/test/integration/monitor-threads-run-every-frame.js @@ -55,13 +55,19 @@ test('monitor thread runs every frame', t => { t.equal(vm.runtime.threads.length, 0); vm.runtime._step(); - checkMonitorThreadPresent(t, vm.runtime.threads); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); + let doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(doneThreads.length, 1); + checkMonitorThreadPresent(t, doneThreads); + t.assert(doneThreads[0].status === Thread.STATUS_DONE); // Check that both are added again when another step is taken vm.runtime._step(); - checkMonitorThreadPresent(t, vm.runtime.threads); - t.assert(vm.runtime.threads[0].status === Thread.STATUS_DONE); + doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 0); + t.equal(doneThreads.length, 1); + checkMonitorThreadPresent(t, doneThreads); + t.assert(doneThreads[0].status === Thread.STATUS_DONE); t.end(); }); }); @@ -103,12 +109,18 @@ test('monitor thread not added twice', t => { t.equal(vm.runtime.threads.length, 0); vm.runtime._step(); + let doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 1); + t.equal(doneThreads.length, 0); checkMonitorThreadPresent(t, vm.runtime.threads); t.assert(vm.runtime.threads[0].status === Thread.STATUS_RUNNING); const prevThread = vm.runtime.threads[0]; // Check that both are added again when another step is taken vm.runtime._step(); + doneThreads = vm.runtime._lastStepDoneThreads; + t.equal(vm.runtime.threads.length, 1); + t.equal(doneThreads.length, 0); checkMonitorThreadPresent(t, vm.runtime.threads); t.equal(vm.runtime.threads[0], prevThread); t.end(); diff --git a/test/integration/monitors.js b/test/integration/monitors.js index 52bc5b0f..38e89b43 100644 --- a/test/integration/monitors.js +++ b/test/integration/monitors.js @@ -14,8 +14,10 @@ test('importing sb2 project with monitors', t => { // Evaluate playground data and exit vm.on('playgroundData', e => { const threads = JSON.parse(e.threads); - // All monitors should leave threads running - t.equal(threads.length, 5); + // All monitors should create threads that finish during the step and + // are revoved from runtime.threads. + t.equal(threads.length, 0); + t.equal(vm.runtime._lastStepDoneThreads.length, 5); // There should be one additional hidden monitor that is in the monitorState but // does not start a thread. t.equal(vm.runtime._monitorState.size, 6); diff --git a/test/unit/engine_sequencer.js b/test/unit/engine_sequencer.js index 55faeb69..622dc64b 100644 --- a/test/unit/engine_sequencer.js +++ b/test/unit/engine_sequencer.js @@ -181,8 +181,7 @@ test('stepThreads', t => { t.strictEquals(s.stepThreads().length, 0); generateThread(r); t.strictEquals(r.threads.length, 1); - t.strictEquals(s.stepThreads().length, 0); - r.threads[0].status = Thread.STATUS_RUNNING; + // Threads should be marked DONE and removed in the same step they finish. t.strictEquals(s.stepThreads().length, 1); t.end();