Skip to content

Commit 840ffb5

Browse files
committed
test: don't use process.exit to end tests
Newer versions of `tap` run more asynchronously, so sometimes using `process.nextTick(process.exit)` to end a test would prevent the test from completing correctly. Removing all instances of `process.nextTick(process.exit)` put tests into three categories: * the test still worked correctly -- no fixup needed. * the test would hang because the VM's `_steppingInterval` was keeping Node alive. These tests call a new `quit()` method which ends the stepping interval. * the `load-extensions` test needed special attention because the "Video Sensing" extension starts its own loop using `setTimeout`. I added a `_stopLoop()` method on the extension and directly call that from the test. I'm not completely happy with this solution but anything more general would likely require a change to the extension spec, so I'm leaving that as a followup task.
1 parent 605b1c2 commit 840ffb5

File tree

69 files changed

+182
-156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+182
-156
lines changed

src/engine/runtime.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ class Runtime extends EventEmitter {
608608
static get PERIPHERAL_LIST_UPDATE () {
609609
return 'PERIPHERAL_LIST_UPDATE';
610610
}
611-
611+
612612
/**
613613
* Event name for when the user picks a bluetooth device to connect to
614614
* via Companion Device Manager (CDM)
@@ -2578,6 +2578,15 @@ class Runtime extends EventEmitter {
25782578
this.emit(Runtime.RUNTIME_STARTED);
25792579
}
25802580

2581+
/**
2582+
* Quit the Runtime, clearing any handles which might keep the process alive.
2583+
* Do not use the runtime after calling this method. This method is meant for test shutdown.
2584+
*/
2585+
quit () {
2586+
clearInterval(this._steppingInterval);
2587+
this._steppingInterval = null;
2588+
}
2589+
25812590
/**
25822591
* Turn on profiling.
25832592
* @param {Profiler/FrameCallback} onFrame A callback handle passed a

src/extension-support/extension-manager.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ class ExtensionManager {
8181
this.pendingWorkers = [];
8282

8383
/**
84-
* Set of loaded extension URLs/IDs (equivalent for built-in extensions).
85-
* @type {Set.<string>}
84+
* Map of loaded extension URLs/IDs (equivalent for built-in extensions) to service name.
85+
* @type {Map.<string,string>}
8686
* @private
8787
*/
8888
this._loadedExtensions = new Map();

src/extensions/scratch3_video_sensing/index.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ class Scratch3VideoSensingBlocks {
231231
* @private
232232
*/
233233
_loop () {
234-
setTimeout(this._loop.bind(this), Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL));
234+
const loopTime = Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL);
235+
this._loopInterval = setTimeout(this._loop.bind(this), loopTime);
235236

236237
// Add frame to detector
237238
const time = Date.now();
@@ -251,6 +252,13 @@ class Scratch3VideoSensingBlocks {
251252
}
252253
}
253254

255+
/**
256+
* Stop the video sampling loop. Only used for testing.
257+
*/
258+
_stopLoop () {
259+
clearTimeout(this._loopInterval);
260+
}
261+
254262
/**
255263
* Create data for a menu in scratch-blocks format, consisting of an array
256264
* of objects with text and value properties. The text is a translated

src/virtual-machine.js

+8
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,14 @@ class VirtualMachine extends EventEmitter {
175175
this.runtime.start();
176176
}
177177

178+
/**
179+
* Quit the VM, clearing any handles which might keep the process alive.
180+
* Do not use the runtime after calling this method. This method is meant for test shutdown.
181+
*/
182+
quit () {
183+
this.runtime.quit();
184+
}
185+
178186
/**
179187
* "Green flag" handler - start all threads starting with a green flag.
180188
*/

test/integration/addSprite.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ test('default cat', t => {
2727
vm.on('playgroundData', e => {
2828
const threads = JSON.parse(e.threads);
2929
t.ok(threads.length === 0);
30+
vm.quit();
3031
t.end();
31-
process.nextTick(process.exit);
3232
});
3333

3434
vm.start();

test/integration/block_to_workspace_comment_import.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ test('importing sb2 project where block comment is converted to workspace commen
3333
const invalidComments = targetComments.filter(comment => typeof comment.blockId === 'number');
3434
t.equal(invalidComments.length, 0);
3535

36+
vm.quit();
3637
t.end();
37-
process.nextTick(process.exit);
3838
});
3939

4040
// Start VM, load project, and run

test/integration/block_to_workspace_comment_import_no_scripts.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ test('importing sb2 project where block comment is converted to workspace commen
3838
const targetBlocks = Object.values(target.blocks._blocks);
3939
t.equal(targetBlocks.length, 0);
4040

41+
vm.quit();
4142
t.end();
42-
process.nextTick(process.exit);
4343
});
4444

4545
// Start VM, load project, and run

test/integration/broadcast_special_chars_sb2.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ test('importing sb2 project with special chars in message names', t => {
6262
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
6363
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);
6464

65+
vm.quit();
6566
t.end();
66-
process.nextTick(process.exit);
6767
});
6868

6969
// Start VM, load project, and run

test/integration/broadcast_special_chars_sb3.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ test('importing sb3 project with special chars in message names', t => {
6262
t.equal(catMessageBlocks[0].fields.BROADCAST_OPTION.id, ltPerfectMessageId);
6363
t.equal(catMessageBlocks[1].fields.BROADCAST_OPTION.id, abMessageId);
6464

65+
vm.quit();
6566
t.end();
66-
process.nextTick(process.exit);
6767
});
6868

6969
// Start VM, load project, and run

test/integration/clone-cleanup.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ test('clone-cleanup', t => {
6565
// The second batch of clones has deleted themselves; everything is finished
6666
verifyCounts(0, 0);
6767

68+
vm.quit();
6869
t.end();
69-
process.nextTick(process.exit);
7070
break;
7171
}
7272
};

test/integration/cloud_variables_sb2.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ test('importing an sb2 project with cloud variables', t => {
3737
// when the message is being sent to the server rather than on the client
3838
t.equal(variable.isCloud, true);
3939

40+
vm.quit();
4041
t.end();
4142
});
4243
});
@@ -60,6 +61,7 @@ test('importing an sb2 project with cloud variables at the limit for a project',
6061
// All of the 8 stage variables should be cloud variables
6162
t.equal(stageVars.filter(v => v.isCloud).length, 10);
6263

64+
vm.quit();
6365
t.end();
6466
});
6567
});
@@ -85,6 +87,7 @@ test('importing an sb2 project with cloud variables exceeding the limit for a pr
8587
// Only 8 of the variables should have the isCloud flag set to true
8688
t.equal(stageVars.filter(v => v.isCloud).length, 10);
8789

90+
vm.quit();
8891
t.end();
8992
});
9093
});
@@ -115,6 +118,7 @@ test('importing one project after the other resets cloud variable limit', t => {
115118

116119
t.equal(vm.runtime.canAddCloudVariable(), true);
117120

121+
vm.quit();
118122
t.end();
119123
});
120124
});
@@ -145,8 +149,7 @@ test('local cloud variables get imported as regular variables', t => {
145149
t.equal(spriteVars.length, 1);
146150
t.equal(spriteVars[0].isCloud, false);
147151

152+
vm.quit();
148153
t.end();
149-
150-
process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
151154
});
152155
});

test/integration/cloud_variables_sb3.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ test('importing an sb3 project with cloud variables', t => {
3535
t.equal(Number(variable.value), 100);
3636
t.equal(variable.isCloud, true);
3737

38+
vm.quit();
3839
t.end();
3940
});
4041
});
@@ -58,6 +59,7 @@ test('importing an sb3 project with cloud variables at the limit for a project',
5859
// All of the 10 stage variables should be cloud variables
5960
t.equal(stageVars.filter(v => v.isCloud).length, 10);
6061

62+
vm.quit();
6163
t.end();
6264
});
6365
});
@@ -83,6 +85,7 @@ test('importing an sb3 project with cloud variables exceeding the limit for a pr
8385
// Only 8 of the variables should have the isCloud flag set to true
8486
t.equal(stageVars.filter(v => v.isCloud).length, 10);
8587

88+
vm.quit();
8689
t.end();
8790
});
8891
});
@@ -111,6 +114,7 @@ test('importing one project after the other resets cloud variable limit', t => {
111114

112115
t.equal(vm.runtime.canAddCloudVariable(), true);
113116

117+
vm.quit();
114118
t.end();
115119
});
116120
});
@@ -141,8 +145,7 @@ test('local cloud variables get imported as regular variables', t => {
141145
t.equal(spriteVars.length, 1);
142146
t.equal(spriteVars[0].isCloud, false);
143147

148+
vm.quit();
144149
t.end();
145-
146-
process.nextTick(process.exit); // This is needed because this is the end of the last test in this file!!!
147150
});
148151
});

test/integration/comments.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ test('importing sb2 project with comments', t => {
7070
t.equal(stopAllBlock.comment, blockComments[4].id);
7171
t.equal(stopAllBlock.opcode, 'control_stop');
7272

73+
vm.quit();
7374
t.end();
74-
process.nextTick(process.exit);
7575
});
7676

7777
// Start VM, load project, and run

test/integration/comments_sb3.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ test('load an sb3 project with comments', t => {
7070
t.equal(stopAllBlock.comment, blockComments[4].id);
7171
t.equal(stopAllBlock.opcode, 'control_stop');
7272

73+
vm.quit();
7374
t.end();
74-
process.nextTick(process.exit);
7575
});
7676

7777
// Start VM, load project, and run

test/integration/complex.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ test('complex', t => {
1919
vm.on('playgroundData', e => {
2020
const threads = JSON.parse(e.threads);
2121
t.ok(threads.length === 0);
22+
vm.quit();
2223
t.end();
23-
process.nextTick(process.exit);
2424
});
2525

2626
// Manipulate each target

test/integration/control.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ test('control', t => {
1515
vm.on('playgroundData', e => {
1616
const threads = JSON.parse(e.threads);
1717
t.ok(threads.length > 0);
18+
vm.quit();
1819
t.end();
19-
process.nextTick(process.exit);
2020
});
2121

2222
// Start VM, load project, and run

test/integration/data.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ test('data', t => {
1414
// Evaluate playground data and exit
1515
vm.on('playgroundData', () => {
1616
// @todo Additional tests
17+
vm.quit();
1718
t.end();
18-
process.nextTick(process.exit);
1919
});
2020

2121
// Start VM, load project, and run

test/integration/delete-and-restore-sprite.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ test('default cat', t => {
2727
vm.on('playgroundData', e => {
2828
const threads = JSON.parse(e.threads);
2929
t.ok(threads.length === 0);
30+
vm.quit();
3031
t.end();
31-
process.nextTick(process.exit);
3232
});
3333

3434
vm.start();

test/integration/event.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ test('event', t => {
1515
vm.on('playgroundData', e => {
1616
const threads = JSON.parse(e.threads);
1717
t.ok(threads.length > 0);
18+
vm.quit();
1819
t.end();
19-
process.nextTick(process.exit);
2020
});
2121

2222
// Start VM, load project, and run

test/integration/execute.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ fs.readdirSync(executeDir)
6666
log.suggest.deny('vm', 'error');
6767
t.tearDown(() => log.suggest.clear());
6868

69+
const vm = new VirtualMachine();
70+
6971
// Map string messages to tap reporting methods. This will be used
7072
// with events from scratch's runtime emitted on block instructions.
7173
let didPlan;
@@ -86,6 +88,7 @@ fs.readdirSync(executeDir)
8688
},
8789
end () {
8890
didEnd = true;
91+
vm.quit();
8992
t.end();
9093
}
9194
};
@@ -100,7 +103,6 @@ fs.readdirSync(executeDir)
100103
return reporters.comment(text);
101104
};
102105

103-
const vm = new VirtualMachine();
104106
vm.attachStorage(makeTestStorage());
105107

106108
// Start the VM and initialize some vm properties.
@@ -138,6 +140,7 @@ fs.readdirSync(executeDir)
138140
// it can be resolved.
139141
if (!didEnd) {
140142
t.fail('did not say "end"');
143+
vm.quit();
141144
t.end();
142145
}
143146
});

test/integration/hat-execution-order.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ test('complex', t => {
2020
const results = vm.runtime.targets[0].variables[resultKey].value;
2121
t.deepEqual(results, ['3', '2', '1', 'stage']);
2222

23+
vm.quit();
2324
t.end();
24-
process.nextTick(process.exit);
2525
});
2626

2727
// Start VM, load project, and run

test/integration/import-sb.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ test('default', t => {
1515
vm.on('playgroundData', e => {
1616
const threads = JSON.parse(e.threads);
1717
t.ok(threads.length === 0);
18+
vm.quit();
1819
t.end();
19-
process.nextTick(process.exit);
2020
});
2121

2222
// Start VM, load project, and run

test/integration/import-sb2-from-object.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ test('default', t => {
1515
vm.on('playgroundData', e => {
1616
const threads = JSON.parse(e.threads);
1717
t.ok(threads.length === 0);
18+
vm.quit();
1819
t.end();
19-
process.nextTick(process.exit);
2020
});
2121

2222
// Start VM, load project, and run

test/integration/list-monitor-rename.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ test('importing sb3 project with incorrect list monitor name', t => {
3131
t.equal(monitorBlock.fields.LIST.value, renamedListName);
3232
}
3333

34+
vm.quit();
3435
t.end();
35-
process.nextTick(process.exit);
3636
});
3737

3838
// Start VM, load project, and run

0 commit comments

Comments
 (0)