diff --git a/test/fixtures/cloud_variables_exceeded_limit.sb2 b/test/fixtures/cloud_variables_exceeded_limit.sb2 new file mode 100644 index 000000000..cf1cc0850 Binary files /dev/null and b/test/fixtures/cloud_variables_exceeded_limit.sb2 differ diff --git a/test/fixtures/cloud_variables_exceeded_limit.sb3 b/test/fixtures/cloud_variables_exceeded_limit.sb3 new file mode 100644 index 000000000..ce1b78ac7 Binary files /dev/null and b/test/fixtures/cloud_variables_exceeded_limit.sb3 differ diff --git a/test/fixtures/cloud_variables_limit.sb2 b/test/fixtures/cloud_variables_limit.sb2 new file mode 100644 index 000000000..e33de969d Binary files /dev/null and b/test/fixtures/cloud_variables_limit.sb2 differ diff --git a/test/fixtures/cloud_variables_limit.sb3 b/test/fixtures/cloud_variables_limit.sb3 new file mode 100644 index 000000000..4907e50f0 Binary files /dev/null and b/test/fixtures/cloud_variables_limit.sb3 differ diff --git a/test/fixtures/cloud_variables_local.sb2 b/test/fixtures/cloud_variables_local.sb2 new file mode 100644 index 000000000..61e4556e3 Binary files /dev/null and b/test/fixtures/cloud_variables_local.sb2 differ diff --git a/test/fixtures/cloud_variables_local.sb3 b/test/fixtures/cloud_variables_local.sb3 new file mode 100644 index 000000000..35df5de92 Binary files /dev/null and b/test/fixtures/cloud_variables_local.sb3 differ diff --git a/test/fixtures/cloud_variables_simple.sb2 b/test/fixtures/cloud_variables_simple.sb2 new file mode 100644 index 000000000..ddbf782d7 Binary files /dev/null and b/test/fixtures/cloud_variables_simple.sb2 differ diff --git a/test/fixtures/cloud_variables_simple.sb3 b/test/fixtures/cloud_variables_simple.sb3 new file mode 100644 index 000000000..a25bd6252 Binary files /dev/null and b/test/fixtures/cloud_variables_simple.sb3 differ diff --git a/test/integration/cloud_variables_sb2.js b/test/integration/cloud_variables_sb2.js new file mode 100644 index 000000000..e42cbe499 --- /dev/null +++ b/test/integration/cloud_variables_sb2.js @@ -0,0 +1,152 @@ +const path = require('path'); +const test = require('tap').test; +const makeTestStorage = require('../fixtures/make-test-storage'); +const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer; +const VirtualMachine = require('../../src/index'); + +const cloudVarSimpleUri = path.resolve(__dirname, '../fixtures/cloud_variables_simple.sb2'); +const cloudVarLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_limit.sb2'); +const cloudVarExceededLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_exceeded_limit.sb2'); +const cloudVarLocalUri = path.resolve(__dirname, '../fixtures/cloud_variables_local.sb2'); + +const cloudVarSimple = readFileToBuffer(cloudVarSimpleUri); +const cloudVarLimit = readFileToBuffer(cloudVarLimitUri); +const cloudVarExceededLimit = readFileToBuffer(cloudVarExceededLimitUri); +const cloudVarLocal = readFileToBuffer(cloudVarLocalUri); + +test('importing an sb2 project with cloud variables', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarSimple).then(() => { + t.equal(vm.runtime.hasCloudData, true); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + t.equal(stageVars.length, 1); + + const variable = stageVars[0]; + t.equal(variable.name, '☁ firstCloud'); + t.equal(Number(variable.value), 100); // Though scratch 2 requires + // cloud variables to be numbers, this is something that happens + // when the message is being sent to the server rather than on the client + t.equal(variable.isCloud, true); + + t.end(); + }); +}); + +test('importing an sb2 project with cloud variables at the limit for a project', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarLimit).then(() => { + t.equal(vm.runtime.hasCloudData, true); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + + t.equal(stageVars.length, 8); + // All of the 8 stage variables should be cloud variables + t.equal(stageVars.filter(v => v.isCloud).length, 8); + + t.end(); + }); +}); + +test('importing an sb2 project with cloud variables exceeding the limit for a project', t => { + // This tests a hacked project where additional cloud variables exceeding + // the project limit have been added. + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarExceededLimit).then(() => { + t.equal(vm.runtime.hasCloudData, true); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + + t.equal(stageVars.length, 15); + // Only 8 of the variables should have the isCloud flag set to true + t.equal(stageVars.filter(v => v.isCloud).length, 8); + + t.end(); + }); +}); + +test('importing one project after the other resets cloud variable limit', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarExceededLimit).then(() => { + t.equal(vm.runtime.canAddNewCloudVariable(), false); + + vm.loadProject(cloudVarSimple).then(() => { + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + t.equal(stageVars.length, 1); + + const variable = stageVars[0]; + t.equal(variable.name, '☁ firstCloud'); + t.equal(Number(variable.value), 100); // Though scratch 2 requires + // cloud variables to be numbers, this is something that happens + // when the message is being sent to the server rather than on the client + t.equal(variable.isCloud, true); + + t.equal(vm.runtime.canAddNewCloudVariable(), true); + + t.end(); + }); + }); +}); + +test('local cloud variables get imported as regular variables', t => { + // This tests a hacked project where additional cloud variables exceeding + // the project limit have been added. + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarLocal).then(() => { + t.equal(vm.runtime.hasCloudData, false); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + + t.equal(stageVars.length, 0); + + const sprite = vm.runtime.targets[1]; + const spriteVars = Object.values(sprite.variables); + + t.equal(spriteVars.length, 1); + t.equal(spriteVars[0].isCloud, false); + + t.end(); + + process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!! + }); +}); diff --git a/test/integration/cloud_variables_sb3.js b/test/integration/cloud_variables_sb3.js new file mode 100644 index 000000000..385af3aaa --- /dev/null +++ b/test/integration/cloud_variables_sb3.js @@ -0,0 +1,148 @@ +const path = require('path'); +const test = require('tap').test; +const makeTestStorage = require('../fixtures/make-test-storage'); +const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer; +const VirtualMachine = require('../../src/index'); + +const cloudVarSimpleUri = path.resolve(__dirname, '../fixtures/cloud_variables_simple.sb3'); +const cloudVarLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_limit.sb3'); +const cloudVarExceededLimitUri = path.resolve(__dirname, '../fixtures/cloud_variables_exceeded_limit.sb3'); +const cloudVarLocalUri = path.resolve(__dirname, '../fixtures/cloud_variables_local.sb3'); + +const cloudVarSimple = readFileToBuffer(cloudVarSimpleUri); +const cloudVarLimit = readFileToBuffer(cloudVarLimitUri); +const cloudVarExceededLimit = readFileToBuffer(cloudVarExceededLimitUri); +const cloudVarLocal = readFileToBuffer(cloudVarLocalUri); + +test('importing an sb3 project with cloud variables', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarSimple).then(() => { + t.equal(vm.runtime.hasCloudData, true); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + t.equal(stageVars.length, 1); + + const variable = stageVars[0]; + t.equal(variable.name, '☁ firstCloud'); + t.equal(Number(variable.value), 100); + t.equal(variable.isCloud, true); + + t.end(); + }); +}); + +test('importing an sb3 project with cloud variables at the limit for a project', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarLimit).then(() => { + t.equal(vm.runtime.hasCloudData, true); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + + t.equal(stageVars.length, 8); + // All of the 8 stage variables should be cloud variables + t.equal(stageVars.filter(v => v.isCloud).length, 8); + + t.end(); + }); +}); + +test('importing an sb3 project with cloud variables exceeding the limit for a project', t => { + // This tests a hacked project where additional cloud variables exceeding + // the project limit have been added. + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarExceededLimit).then(() => { + t.equal(vm.runtime.hasCloudData, true); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + + t.equal(stageVars.length, 15); + // Only 8 of the variables should have the isCloud flag set to true + t.equal(stageVars.filter(v => v.isCloud).length, 8); + + t.end(); + }); +}); + +test('importing one project after the other resets cloud variable limit', t => { + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarExceededLimit).then(() => { + t.equal(vm.runtime.canAddNewCloudVariable(), false); + + vm.loadProject(cloudVarSimple).then(() => { + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + t.equal(stageVars.length, 1); + + const variable = stageVars[0]; + t.equal(variable.name, '☁ firstCloud'); + t.equal(Number(variable.value), 100); + t.equal(variable.isCloud, true); + + t.equal(vm.runtime.canAddNewCloudVariable(), true); + + t.end(); + }); + }); +}); + +test('local cloud variables get imported as regular variables', t => { + // This tests a hacked project where additional cloud variables exceeding + // the project limit have been added. + const vm = new VirtualMachine(); + vm.attachStorage(makeTestStorage()); + + // Start VM, load project, and run + vm.start(); + vm.clear(); + vm.setCompatibilityMode(false); + vm.setTurboMode(false); + vm.loadProject(cloudVarLocal).then(() => { + t.equal(vm.runtime.hasCloudData, false); + + const stage = vm.runtime.targets[0]; + const stageVars = Object.values(stage.variables); + + t.equal(stageVars.length, 0); + + const sprite = vm.runtime.targets[1]; + const spriteVars = Object.values(sprite.variables); + + t.equal(spriteVars.length, 1); + t.equal(spriteVars[0].isCloud, false); + + t.end(); + + process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!! + }); +}); diff --git a/test/unit/engine_runtime.js b/test/unit/engine_runtime.js index a82900191..c5788c240 100644 --- a/test/unit/engine_runtime.js +++ b/test/unit/engine_runtime.js @@ -127,3 +127,38 @@ test('Project loaded emits runtime event', t => { t.end(); }); }); + +test('Cloud variable limit allows only 8 cloud variables', t => { + // This is a test of just the cloud variable limit mechanism + // The functions being tested below need to be used when + // creating and deleting cloud variables in the runtime. + + const rt = new Runtime(); + + for (let i = 0; i < 8; i++) { + t.equal(rt.canAddNewCloudVariable(), true); + } + + // We should be at the cloud variable limit now + t.equal(rt.canAddNewCloudVariable(), false); + + // Removing a cloud variable should allow the addition of exactly one more + // when we are at the cloud variable limit + rt.removeExistingCloudVariable(); + + t.equal(rt.canAddNewCloudVariable(), true); + t.equal(rt.canAddNewCloudVariable(), false); + + // Disposing of the runtime should reset the cloud variable limitations + rt.dispose(); + + for (let i = 0; i < 8; i++) { + t.equal(rt.canAddNewCloudVariable(), true); + } + + // We should be at the cloud variable limit now + t.equal(rt.canAddNewCloudVariable(), false); + + t.end(); + +});