From e696e3548b542f37fe800d7564c1d908777a0935 Mon Sep 17 00:00:00 2001 From: Christopher Willis-Ford <7019101+cwillisf@users.noreply.github.com> Date: Mon, 27 Mar 2023 10:31:07 -0700 Subject: [PATCH] fix: don't set run ID until storage exists (also add tests) --- src/engine/runtime.js | 10 ++++-- test/integration/runId.js | 73 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 test/integration/runId.js diff --git a/src/engine/runtime.js b/src/engine/runtime.js index 1f29bb25d..87b44d0d3 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -2,8 +2,6 @@ const EventEmitter = require('events'); const {OrderedMap} = require('immutable'); const uuid = require('uuid'); -const {setMetadata, RequestMetadata} = require('scratch-storage/src/scratchFetch.js'); - const ArgumentType = require('../extension-support/argument-type'); const Blocks = require('./blocks'); const BlocksRuntimeCache = require('./blocks-runtime-cache'); @@ -1629,6 +1627,7 @@ class Runtime extends EventEmitter { */ attachStorage (storage) { this.storage = storage; + this.resetRunId(); } // ----------------------------------------------------------------------------- @@ -2024,8 +2023,13 @@ class Runtime extends EventEmitter { * Reset the Run ID. Call this any time the project logically starts, stops, or changes identity. */ resetRunId () { + if (!this.storage) { + // see also: attachStorage + return; + } + const newRunId = uuid.v1(); - setMetadata(RequestMetadata.RunId, newRunId); + this.storage.scratchFetch.setMetadata(this.storage.scratchFetch.RequestMetadata.RunId, newRunId); } /** diff --git a/test/integration/runId.js b/test/integration/runId.js new file mode 100644 index 000000000..cdc006b20 --- /dev/null +++ b/test/integration/runId.js @@ -0,0 +1,73 @@ +const Worker = require('tiny-worker'); +const path = require('path'); +const test = require('tap').test; + +const VirtualMachine = require('../../src/index'); +const dispatch = require('../../src/dispatch/central-dispatch'); + +const makeTestStorage = require('../fixtures/make-test-storage'); +const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer; + +// it doesn't really matter which project we use: we're testing side effects of loading any project +const uri = path.resolve(__dirname, '../fixtures/default.sb3'); +const project = readFileToBuffer(uri); + +// By default Central Dispatch works with the Worker class built into the browser. Tell it to use TinyWorker instead. +dispatch.workerClass = Worker; + +test('runId', async t => { + const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; + const isGuid = data => guidRegex.test(data); + + const storage = makeTestStorage(); + + // add to this list every time the RunId should have changed + const runIdLog = []; + const pushRunId = () => { + const runId = storage.scratchFetch.getMetadata(storage.scratchFetch.RequestMetadata.RunId); + t.ok(isGuid(runId), 'Run IDs should always be a properly-formatted GUID', {runId}); + runIdLog.push(runId); + }; + + const vm = new VirtualMachine(); + vm.attachStorage(storage); + pushRunId(); // check that the initial run ID is valid + + vm.start(); // starts the VM, not the project, so this doesn't change the run ID + + vm.clear(); + pushRunId(); // clearing the project conceptually changes the project identity does it DOES change the run ID + + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + await vm.loadProject(project); + pushRunId(); + + vm.greenFlag(); + pushRunId(); + + // Turn the playgroundData event into a Promise that we can await + const playgroundDataPromise = new Promise(resolve => { + vm.on('playgroundData', data => resolve(data)); + }); + + // Let the project run for a bit, then get playground data and stop the project + // This test doesn't need the playground data but it does need to run & stop the project + setTimeout(() => { + vm.getPlaygroundData(); + vm.stopAll(); + pushRunId(); + }, 100); + + // wait for the project to run to completion + await playgroundDataPromise; + + for (let i = 0; i < runIdLog.length - 1; ++i) { + for (let j = i + 1; j < runIdLog.length; ++j) { + t.notSame(runIdLog[i], runIdLog[j], 'Run IDs should always be unique', {runIdLog}); + } + } + + vm.quit(); + t.end(); +});