Revert "repl: refactor tests to not rely on timing"

This reverts commit de848ac1e0483327a2ce8716c3f8567eaeacb660.

The commit broke multiline repl.

PR-URL: https://github.com/nodejs/node/pull/18715
Refs: https://github.com/nodejs/node/pull/17828
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Ruben Bridgewater 2018-02-11 19:17:03 +01:00
parent 60c9ad7979
commit 1fc373bdf6
No known key found for this signature in database
GPG Key ID: F07496B3EB3C1762
18 changed files with 274 additions and 392 deletions

View File

@ -8,7 +8,7 @@ const os = require('os');
const util = require('util'); const util = require('util');
const debug = util.debuglog('repl'); const debug = util.debuglog('repl');
module.exports = Object.create(REPL); module.exports = Object.create(REPL);
module.exports.createInternalRepl = createInternalRepl; module.exports.createInternalRepl = createRepl;
// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary. // XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
// The debounce is to guard against code pasted into the REPL. // The debounce is to guard against code pasted into the REPL.
@ -19,7 +19,7 @@ function _writeToOutput(repl, message) {
repl._refreshLine(); repl._refreshLine();
} }
function createInternalRepl(env, opts, cb) { function createRepl(env, opts, cb) {
if (typeof opts === 'function') { if (typeof opts === 'function') {
cb = opts; cb = opts;
opts = null; opts = null;

View File

@ -109,11 +109,6 @@ writer.options = Object.assign({},
exports._builtinLibs = internalModule.builtinLibs; exports._builtinLibs = internalModule.builtinLibs;
const sep = '\u0000\u0000\u0000';
const regExMatcher = new RegExp(`^${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
`${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
`${sep}(.*)$`);
function REPLServer(prompt, function REPLServer(prompt,
stream, stream,
eval_, eval_,
@ -154,7 +149,6 @@ function REPLServer(prompt,
} }
var self = this; var self = this;
replMap.set(self, self);
self._domain = dom || domain.create(); self._domain = dom || domain.create();
self.useGlobal = !!useGlobal; self.useGlobal = !!useGlobal;
@ -171,6 +165,41 @@ function REPLServer(prompt,
self.rli = this; self.rli = this;
const savedRegExMatches = ['', '', '', '', '', '', '', '', '', '']; const savedRegExMatches = ['', '', '', '', '', '', '', '', '', ''];
const sep = '\u0000\u0000\u0000';
const regExMatcher = new RegExp(`^${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
`${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
`${sep}(.*)$`);
eval_ = eval_ || defaultEval;
// Pause taking in new input, and store the keys in a buffer.
const pausedBuffer = [];
let paused = false;
function pause() {
paused = true;
}
function unpause() {
if (!paused) return;
paused = false;
let entry;
while (entry = pausedBuffer.shift()) {
const [type, payload] = entry;
switch (type) {
case 'key': {
const [d, key] = payload;
self._ttyWrite(d, key);
break;
}
case 'close':
self.emit('exit');
break;
}
if (paused) {
break;
}
}
}
function defaultEval(code, context, file, cb) { function defaultEval(code, context, file, cb) {
var err, result, script, wrappedErr; var err, result, script, wrappedErr;
var wrappedCmd = false; var wrappedCmd = false;
@ -302,6 +331,7 @@ function REPLServer(prompt,
if (awaitPromise && !err) { if (awaitPromise && !err) {
let sigintListener; let sigintListener;
pause();
let promise = result; let promise = result;
if (self.breakEvalOnSigint) { if (self.breakEvalOnSigint) {
const interrupt = new Promise((resolve, reject) => { const interrupt = new Promise((resolve, reject) => {
@ -320,6 +350,7 @@ function REPLServer(prompt,
prioritizedSigintQueue.delete(sigintListener); prioritizedSigintQueue.delete(sigintListener);
finishExecution(undefined, result); finishExecution(undefined, result);
unpause();
}, (err) => { }, (err) => {
// Remove prioritized SIGINT listener if it was not called. // Remove prioritized SIGINT listener if it was not called.
prioritizedSigintQueue.delete(sigintListener); prioritizedSigintQueue.delete(sigintListener);
@ -329,6 +360,7 @@ function REPLServer(prompt,
Object.defineProperty(err, 'stack', { value: '' }); Object.defineProperty(err, 'stack', { value: '' });
} }
unpause();
if (err && process.domain) { if (err && process.domain) {
debug('not recoverable, send to domain'); debug('not recoverable, send to domain');
process.domain.emit('error', err); process.domain.emit('error', err);
@ -345,36 +377,6 @@ function REPLServer(prompt,
} }
} }
eval_ = eval_ || defaultEval;
// Pause taking in new input, and store the keys in a buffer.
const pausedBuffer = [];
let paused = false;
function pause() {
paused = true;
}
function unpause() {
if (!paused) return;
paused = false;
let entry;
while (entry = pausedBuffer.shift()) {
const [type, payload] = entry;
switch (type) {
case 'key': {
const [d, key] = payload;
self._ttyWrite(d, key);
break;
}
case 'close':
self.emit('exit');
break;
}
if (paused) {
break;
}
}
}
self.eval = self._domain.bind(eval_); self.eval = self._domain.bind(eval_);
self._domain.on('error', function debugDomainError(e) { self._domain.on('error', function debugDomainError(e) {
@ -403,7 +405,6 @@ function REPLServer(prompt,
top.clearBufferedCommand(); top.clearBufferedCommand();
top.lines.level = []; top.lines.level = [];
top.displayPrompt(); top.displayPrompt();
unpause();
}); });
if (!input && !output) { if (!input && !output) {
@ -592,7 +593,6 @@ function REPLServer(prompt,
const evalCmd = self[kBufferedCommandSymbol] + cmd + '\n'; const evalCmd = self[kBufferedCommandSymbol] + cmd + '\n';
debug('eval %j', evalCmd); debug('eval %j', evalCmd);
pause();
self.eval(evalCmd, self.context, 'repl', finish); self.eval(evalCmd, self.context, 'repl', finish);
function finish(e, ret) { function finish(e, ret) {
@ -605,7 +605,6 @@ function REPLServer(prompt,
'(Press Control-D to exit.)\n'); '(Press Control-D to exit.)\n');
self.clearBufferedCommand(); self.clearBufferedCommand();
self.displayPrompt(); self.displayPrompt();
unpause();
return; return;
} }
@ -643,7 +642,6 @@ function REPLServer(prompt,
// Display prompt again // Display prompt again
self.displayPrompt(); self.displayPrompt();
unpause();
} }
}); });
@ -726,6 +724,7 @@ exports.start = function(prompt,
ignoreUndefined, ignoreUndefined,
replMode); replMode);
if (!exports.repl) exports.repl = repl; if (!exports.repl) exports.repl = repl;
replMap.set(repl, repl);
return repl; return repl;
}; };

View File

@ -29,22 +29,7 @@ const repl = require('repl');
common.globalCheck = false; common.globalCheck = false;
const putIn = new common.ArrayStream(); const putIn = new common.ArrayStream();
repl.start('', putIn, null, true);
const replserver = repl.start('', putIn, null, true);
const callbacks = [];
const $eval = replserver.eval;
replserver.eval = function(code, context, file, cb) {
const expected = callbacks.shift();
return $eval.call(this, code, context, file, (...args) => {
try {
expected(cb, ...args);
} catch (e) {
console.error(e);
process.exit(1);
}
});
};
test1(); test1();
@ -63,11 +48,6 @@ function test1() {
} }
}; };
assert(!gotWrite); assert(!gotWrite);
callbacks.push(common.mustCall((cb, err, result) => {
assert.ifError(err);
assert.strictEqual(result, require('fs'));
cb(err, result);
}));
putIn.run(['fs']); putIn.run(['fs']);
assert(gotWrite); assert(gotWrite);
} }
@ -86,11 +66,6 @@ function test2() {
const val = {}; const val = {};
global.url = val; global.url = val;
assert(!gotWrite); assert(!gotWrite);
callbacks.push(common.mustCall((cb, err, result) => {
assert.ifError(err);
assert.strictEqual(result, val);
cb(err, result);
}));
putIn.run(['url']); putIn.run(['url']);
assert(gotWrite); assert(gotWrite);
} }

View File

@ -27,57 +27,40 @@ function testContext(repl) {
repl.close(); repl.close();
} }
const replserver = repl.start({ input: stream, output: stream }); testContextSideEffects(repl.start({ input: stream, output: stream }));
const callbacks = [];
const $eval = replserver.eval;
replserver.eval = function(code, context, file, cb) {
const expected = callbacks.shift();
return $eval.call(this, code, context, file, (...args) => {
try {
expected(cb, ...args);
} catch (e) {
console.error(e);
process.exit(1);
}
});
};
testContextSideEffects(replserver);
function testContextSideEffects(server) { function testContextSideEffects(server) {
assert.ok(!server.underscoreAssigned); assert.ok(!server.underscoreAssigned);
assert.strictEqual(server.lines.length, 0); assert.strictEqual(server.lines.length, 0);
// an assignment to '_' in the repl server // an assignment to '_' in the repl server
callbacks.push(common.mustCall((cb, ...args) => {
assert.ok(server.underscoreAssigned);
assert.strictEqual(server.last, 500);
cb(...args);
assert.strictEqual(server.lines.length, 1);
assert.strictEqual(server.lines[0], '_ = 500;');
// use the server to create a new context
const context = server.createContext();
// ensure that creating a new context does not
// have side effects on the server
assert.ok(server.underscoreAssigned);
assert.strictEqual(server.lines.length, 1);
assert.strictEqual(server.lines[0], '_ = 500;');
assert.strictEqual(server.last, 500);
// reset the server context
server.resetContext();
assert.ok(!server.underscoreAssigned);
assert.strictEqual(server.lines.length, 0);
// ensure that assigning to '_' in the new context
// does not change the value in our server.
assert.ok(!server.underscoreAssigned);
vm.runInContext('_ = 1000;\n', context);
assert.ok(!server.underscoreAssigned);
assert.strictEqual(server.lines.length, 0);
server.close();
}));
server.write('_ = 500;\n'); server.write('_ = 500;\n');
assert.ok(server.underscoreAssigned);
assert.strictEqual(server.lines.length, 1);
assert.strictEqual(server.lines[0], '_ = 500;');
assert.strictEqual(server.last, 500);
// use the server to create a new context
const context = server.createContext();
// ensure that creating a new context does not
// have side effects on the server
assert.ok(server.underscoreAssigned);
assert.strictEqual(server.lines.length, 1);
assert.strictEqual(server.lines[0], '_ = 500;');
assert.strictEqual(server.last, 500);
// reset the server context
server.resetContext();
assert.ok(!server.underscoreAssigned);
assert.strictEqual(server.lines.length, 0);
// ensure that assigning to '_' in the new context
// does not change the value in our server.
assert.ok(!server.underscoreAssigned);
vm.runInContext('_ = 1000;\n', context);
assert.ok(!server.underscoreAssigned);
assert.strictEqual(server.lines.length, 0);
server.close();
} }

View File

@ -21,7 +21,10 @@
'use strict'; 'use strict';
const common = require('../common'); const common = require('../common');
const assert = require('assert');
const repl = require('repl'); const repl = require('repl');
let terminalExit = 0;
let regularExit = 0;
// Create a dummy stream that does nothing // Create a dummy stream that does nothing
const stream = new common.ArrayStream(); const stream = new common.ArrayStream();
@ -38,10 +41,11 @@ function testTerminalMode() {
stream.emit('data', '\u0004'); stream.emit('data', '\u0004');
}); });
r1.on('exit', common.mustCall(function() { r1.on('exit', function() {
// should be fired from the simulated ^D keypress // should be fired from the simulated ^D keypress
terminalExit++;
testRegularMode(); testRegularMode();
})); });
} }
function testRegularMode() { function testRegularMode() {
@ -55,11 +59,17 @@ function testRegularMode() {
stream.emit('end'); stream.emit('end');
}); });
r2.on('exit', common.mustCall(function() { r2.on('exit', function() {
// should be fired from the simulated 'end' event // should be fired from the simulated 'end' event
})); regularExit++;
});
} }
process.on('exit', function() {
assert.strictEqual(terminalExit, 1);
assert.strictEqual(regularExit, 1);
});
// start // start
testTerminalMode(); testTerminalMode();

View File

@ -3,27 +3,21 @@ const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const repl = require('repl'); const repl = require('repl');
const exitTests = []; {
process.on('exit', () => { const stream = new common.ArrayStream();
for (const test of exitTests) test(); const options = {
}); eval: common.mustCall((cmd, context) => {
const CONTEXT = { animal: 'Sterrance' }; assert.strictEqual(cmd, '.scope\n');
const stream = new common.ArrayStream(); assert.deepStrictEqual(context, { animal: 'Sterrance' });
const options = { }),
eval: common.mustCall((cmd, context) => { input: stream,
// need to escape the domain output: stream,
exitTests.push(common.mustCall(() => { terminal: true
assert.strictEqual(cmd, '.scope'); };
assert.ok(context === CONTEXT);
}));
}),
input: stream,
output: stream,
terminal: true
};
const r = repl.start(options); const r = repl.start(options);
r.context = CONTEXT; r.context = { animal: 'Sterrance' };
stream.emit('data', '\t'); stream.emit('data', '\t');
stream.emit('.exit\n'); stream.emit('.exit\n');
}

View File

@ -3,31 +3,31 @@ const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const repl = require('repl'); const repl = require('repl');
const exitTests = []; {
process.on('exit', () => { let evalCalledWithExpectedArgs = false;
for (const test of exitTests) test();
});
const options = {
eval: common.mustCall((cmd, context) => {
// Assertions here will not cause the test to exit with an error code
// so set a boolean that is checked later instead.
exitTests.push(common.mustCall(() => {
assert.strictEqual(cmd, 'function f() {}\n');
assert.strictEqual(context.foo, 'bar');
}));
})
};
const r = repl.start(options); const options = {
r.context = { foo: 'bar' }; eval: common.mustCall((cmd, context) => {
// Assertions here will not cause the test to exit with an error code
// so set a boolean that is checked later instead.
evalCalledWithExpectedArgs = (cmd === 'function f() {}\n' &&
context.foo === 'bar');
})
};
try { const r = repl.start(options);
// Default preprocessor transforms r.context = { foo: 'bar' };
// function f() {} to
// var f = function f() {} try {
// Test to ensure that original input is preserved. // Default preprocessor transforms
// Reference: https://github.com/nodejs/node/issues/9743 // function f() {} to
r.write('function f() {}\n'); // var f = function f() {}
} finally { // Test to ensure that original input is preserved.
r.write('.exit\n'); // Reference: https://github.com/nodejs/node/issues/9743
r.write('function f() {}\n');
} finally {
r.write('.exit\n');
}
assert(evalCalledWithExpectedArgs);
} }

View File

@ -7,47 +7,32 @@ const stream = require('stream');
common.globalCheck = false; common.globalCheck = false;
const input = new stream(); const r = initRepl();
input.write = input.pause = input.resume = () => {};
input.readable = true;
const output = new stream(); r.input.emit('data', 'function a() { return 42; } (1)\n');
output.writable = true; r.input.emit('data', 'a\n');
output.accumulator = []; r.input.emit('data', '.exit');
output.write = (data) => output.accumulator.push(data); const expected = '1\n[Function: a]\n';
const got = r.output.accumulator.join('');
assert.strictEqual(got, expected);
const replserver = repl.start({ function initRepl() {
input, const input = new stream();
output, input.write = input.pause = input.resume = () => {};
useColors: false, input.readable = true;
terminal: false,
prompt: '' const output = new stream();
}); output.writable = true;
const callbacks = []; output.accumulator = [];
const $eval = replserver.eval;
replserver.eval = function(code, context, file, cb) { output.write = (data) => output.accumulator.push(data);
const expected = callbacks.shift();
return $eval.call(this, code, context, file, (...args) => { return repl.start({
try { input,
expected(...args); output,
} catch (e) { useColors: false,
console.error(e); terminal: false,
process.exit(1); prompt: ''
}
cb(...args);
}); });
}; }
callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(result, 1);
}));
replserver.input.emit('data', 'function a() { return 42; } (1)\n');
callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(typeof result, 'function');
assert.strictEqual(result.toString(), 'function a() { return 42; }');
}));
replserver.input.emit('data', 'a\n');
replserver.input.emit('data', '.exit');

View File

@ -36,7 +36,5 @@ const r = repl.start({
}); });
r.write(`${command}\n`); r.write(`${command}\n`);
r.on('exit', common.mustCall(() => { assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
assert.strictEqual(accum.replace(terminalCodeRegex, ''), expected);
}));
r.close(); r.close();

View File

@ -19,49 +19,35 @@ tests.forEach(function(test) {
function testSloppyMode() { function testSloppyMode() {
const cli = initRepl(repl.REPL_MODE_SLOPPY); const cli = initRepl(repl.REPL_MODE_SLOPPY);
cli.callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(result, 3);
}));
cli.input.emit('data', 'x = 3\n'); cli.input.emit('data', 'x = 3\n');
assert.strictEqual(cli.output.accumulator.join(''), '> 3\n> ');
cli.output.accumulator.length = 0;
cli.callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(result, undefined);
}));
cli.input.emit('data', 'let y = 3\n'); cli.input.emit('data', 'let y = 3\n');
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
} }
function testStrictMode() { function testStrictMode() {
const cli = initRepl(repl.REPL_MODE_STRICT); const cli = initRepl(repl.REPL_MODE_STRICT);
cli._domain.once('error', common.mustCall((err) => {
assert.ok(err);
assert.ok(/ReferenceError: x is not defined/.test(err.message));
}));
cli.input.emit('data', 'x = 3\n'); cli.input.emit('data', 'x = 3\n');
assert.ok(/ReferenceError: x is not defined/.test(
cli.output.accumulator.join('')));
cli.output.accumulator.length = 0;
cli.callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(result, undefined);
}));
cli.input.emit('data', 'let y = 3\n'); cli.input.emit('data', 'let y = 3\n');
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
} }
function testAutoMode() { function testAutoMode() {
const cli = initRepl(repl.REPL_MODE_MAGIC); const cli = initRepl(repl.REPL_MODE_MAGIC);
cli.callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(result, 3);
}));
cli.input.emit('data', 'x = 3\n'); cli.input.emit('data', 'x = 3\n');
assert.strictEqual(cli.output.accumulator.join(''), '> 3\n> ');
cli.output.accumulator.length = 0;
cli.callbacks.push(common.mustCall((err, result) => {
assert.ifError(err);
assert.strictEqual(result, undefined);
}));
cli.input.emit('data', 'let y = 3\n'); cli.input.emit('data', 'let y = 3\n');
assert.strictEqual(cli.output.accumulator.join(''), 'undefined\n> ');
} }
function initRepl(mode) { function initRepl(mode) {
@ -76,28 +62,11 @@ function initRepl(mode) {
output.accumulator = []; output.accumulator = [];
output.writable = true; output.writable = true;
const replserver = repl.start({ return repl.start({
input: input, input: input,
output: output, output: output,
useColors: false, useColors: false,
terminal: false, terminal: false,
replMode: mode replMode: mode
}); });
const callbacks = [];
const $eval = replserver.eval;
replserver.eval = function(code, context, file, cb) {
const expected = callbacks.shift();
return $eval.call(this, code, context, file, (...args) => {
console.log('EVAL RET', args);
try {
expected(...args);
} catch (e) {
console.error(e);
process.exit(1);
}
cb(...args);
});
};
replserver.callbacks = callbacks;
return replserver;
} }

View File

@ -1,5 +1,5 @@
'use strict'; 'use strict';
const common = require('../common'); require('../common');
const repl = require('repl'); const repl = require('repl');
const assert = require('assert'); const assert = require('assert');
const Stream = require('stream'); const Stream = require('stream');
@ -18,6 +18,7 @@ const replserver = repl.start({
replserver.emit('line', 'process.nextTick(() => { throw null; })'); replserver.emit('line', 'process.nextTick(() => { throw null; })');
replserver.emit('line', '.exit'); replserver.emit('line', '.exit');
replserver.on('exit', common.mustCall(() => { setTimeout(() => {
console.log(text);
assert(text.includes('Thrown: null')); assert(text.includes('Thrown: null'));
})); }, 0);

View File

@ -1,28 +1,9 @@
'use strict'; 'use strict';
const common = require('../common'); require('../common');
const repl = require('repl'); const repl = require('repl');
const assert = require('assert'); const assert = require('assert');
const callbacks = [
common.mustCall((err, value) => {
assert.ifError(err);
assert.strictEqual(value, undefined);
})
];
const replserver = new repl.REPLServer(); const replserver = new repl.REPLServer();
const $eval = replserver.eval;
replserver.eval = function(code, context, file, cb) {
const expected = callbacks.shift();
return $eval.call(this, code, context, file, (...args) => {
try {
expected(...args);
} catch (e) {
console.error(e);
process.exit(1);
}
cb(...args);
});
};
replserver._inTemplateLiteral = true; replserver._inTemplateLiteral = true;

View File

@ -22,9 +22,7 @@ function run({ command, expected }) {
}); });
r.write(`${command}\n`); r.write(`${command}\n`);
r.on('exit', common.mustCall(() => { assert.strictEqual(accum, expected);
assert.strictEqual(accum, expected);
}));
r.close(); r.close();
} }

View File

@ -22,9 +22,7 @@ function run({ command, expected }) {
}); });
r.write(`${command}\n`); r.write(`${command}\n`);
r.on('exit', common.mustCall(() => { assert.strictEqual(accum, expected);
assert.strictEqual(accum, expected);
}));
r.close(); r.close();
} }

View File

@ -4,11 +4,14 @@ const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const repl = require('repl'); const repl = require('repl');
let evalCount = 0;
let recovered = false; let recovered = false;
let rendered = false; let rendered = false;
function customEval(code, context, file, cb) { function customEval(code, context, file, cb) {
return cb(!recovered ? new repl.Recoverable() : null, true); evalCount++;
return cb(evalCount === 1 ? new repl.Recoverable() : null, true);
} }
const putIn = new common.ArrayStream(); const putIn = new common.ArrayStream();
@ -23,7 +26,7 @@ putIn.write = function(msg) {
} }
}; };
repl.start('', putIn, common.mustCall(customEval, 2)); repl.start('', putIn, customEval);
// https://github.com/nodejs/node/issues/2939 // https://github.com/nodejs/node/issues/2939
// Expose recoverable errors to the consumer. // Expose recoverable errors to the consumer.
@ -33,4 +36,5 @@ putIn.emit('data', '2\n');
process.on('exit', function() { process.on('exit', function() {
assert(recovered, 'REPL never recovered'); assert(recovered, 'REPL never recovered');
assert(rendered, 'REPL never rendered the result'); assert(rendered, 'REPL never rendered the result');
assert.strictEqual(evalCount, 2);
}); });

View File

@ -1,20 +1,18 @@
'use strict'; 'use strict';
const common = require('../common'); require('../common');
// This test ensures that the repl does not // This test ensures that the repl does not
// crash or emit error when throwing `null|undefined` // crash or emit error when throwing `null|undefined`
// ie `throw null` or `throw undefined` // ie `throw null` or `throw undefined`
const assert = require('assert');
const repl = require('repl'); const repl = require('repl');
const replserver = repl.start(); const r = repl.start();
const $eval = replserver.eval;
replserver.eval = function(code, context, file, cb) {
return $eval.call(this, code, context, file,
common.mustNotCall(
'repl crashes/throw error on `throw null|undefined`'));
};
replserver.write('throw null\n');
replserver.write('throw undefined\n');
replserver.write('.exit\n'); assert.doesNotThrow(() => {
r.write('throw null\n');
r.write('throw undefined\n');
}, TypeError, 'repl crashes/throw error on `throw null|undefined`');
r.write('.exit\n');

View File

@ -28,22 +28,20 @@ function testSloppyMode() {
_; // remains 30 from user input _; // remains 30 from user input
`); `);
r.on('exit', () => { assertOutput(r.output, [
assertOutput(r.output, [ 'undefined',
'undefined', 'undefined',
'undefined', 'undefined',
'undefined', '10',
'10', '10',
'10', 'Expression assignment to _ now disabled.',
'Expression assignment to _ now disabled.', '20',
'20', '20',
'20', '30',
'30', '30',
'30', '40',
'40', '30'
'30' ]);
]);
});
} }
function testStrictMode() { function testStrictMode() {
@ -63,22 +61,20 @@ function testStrictMode() {
_; // remains 30 from user input _; // remains 30 from user input
`); `);
r.on('exit', () => { assertOutput(r.output, [
assertOutput(r.output, [ 'undefined',
'undefined', 'undefined',
'undefined', 'undefined',
'undefined', 'undefined',
'undefined', '20',
'20', '30',
'30', '30',
'30', 'undefined',
'undefined', '30',
'30', 'undefined',
'undefined', 'undefined',
'undefined', '30'
'30' ]);
]);
});
} }
function testMagicMode() { function testMagicMode() {
@ -98,22 +94,20 @@ function testMagicMode() {
_; // remains 30 from user input _; // remains 30 from user input
`); `);
r.on('exit', () => { assertOutput(r.output, [
assertOutput(r.output, [ 'undefined',
'undefined', '10',
'10', '10',
'10', 'undefined',
'undefined', '20',
'20', '30',
'30', '30',
'30', 'undefined',
'undefined', '30',
'30', 'undefined',
'undefined', '50',
'50', '30'
'30' ]);
]);
});
} }
function testResetContext() { function testResetContext() {
@ -127,17 +121,15 @@ function testResetContext() {
_; // expect 20 _; // expect 20
`); `);
r.on('exit', () => { assertOutput(r.output, [
assertOutput(r.output, [ 'Expression assignment to _ now disabled.',
'Expression assignment to _ now disabled.', '10',
'10', '10',
'10', 'Clearing context...',
'Clearing context...', '10',
'10', '20',
'20', '20'
'20' ]);
]);
});
} }
function testResetContextGlobal() { function testResetContextGlobal() {
@ -149,14 +141,12 @@ function testResetContextGlobal() {
_; // remains 10 _; // remains 10
`); `);
r.on('exit', () => { assertOutput(r.output, [
assertOutput(r.output, [ 'Expression assignment to _ now disabled.',
'Expression assignment to _ now disabled.', '10',
'10', '10',
'10', '10',
'10', ]);
]);
});
// delete globals leaked by REPL when `useGlobal` is `true` // delete globals leaked by REPL when `useGlobal` is `true`
delete global.module; delete global.module;

View File

@ -7,6 +7,13 @@ const stream = require('stream');
const repl = require('internal/repl'); const repl = require('internal/repl');
const assert = require('assert'); const assert = require('assert');
// Array of [useGlobal, expectedResult] pairs
const globalTestCases = [
[false, 'undefined'],
[true, '\'tacos\''],
[undefined, 'undefined']
];
const globalTest = (useGlobal, cb, output) => (err, repl) => { const globalTest = (useGlobal, cb, output) => (err, repl) => {
if (err) if (err)
return cb(err); return cb(err);
@ -19,12 +26,26 @@ const globalTest = (useGlobal, cb, output) => (err, repl) => {
global.lunch = 'tacos'; global.lunch = 'tacos';
repl.write('global.lunch;\n'); repl.write('global.lunch;\n');
repl.close(); repl.close();
repl.on('exit', common.mustCall(() => { delete global.lunch;
delete global.lunch; cb(null, str.trim());
cb(null, str.trim());
}));
}; };
// Test how the global object behaves in each state for useGlobal
for (const [option, expected] of globalTestCases) {
runRepl(option, globalTest, common.mustCall((err, output) => {
assert.ifError(err);
assert.strictEqual(output, expected);
}));
}
// Test how shadowing the process object via `let`
// behaves in each useGlobal state. Note: we can't
// actually test the state when useGlobal is true,
// because the exception that's generated is caught
// (see below), but errors are printed, and the test
// suite is aware of it, causing a failure to be flagged.
//
const processTestCases = [false, undefined];
const processTest = (useGlobal, cb, output) => (err, repl) => { const processTest = (useGlobal, cb, output) => (err, repl) => {
if (err) if (err)
return cb(err); return cb(err);
@ -36,37 +57,15 @@ const processTest = (useGlobal, cb, output) => (err, repl) => {
repl.write('let process;\n'); repl.write('let process;\n');
repl.write('21 * 2;\n'); repl.write('21 * 2;\n');
repl.close(); repl.close();
repl.on('exit', common.mustCall((err) => { cb(null, str.trim());
assert.ifError(err);
cb(null, str.trim());
}));
}; };
// Array of [useGlobal, expectedResult, fn] pairs for (const option of processTestCases) {
const testCases = [ runRepl(option, processTest, common.mustCall((err, output) => {
// Test how the global object behaves in each state for useGlobal assert.ifError(err);
[false, 'undefined', globalTest], assert.strictEqual(output, 'undefined\n42');
[true, '\'tacos\'', globalTest], }));
[undefined, 'undefined', globalTest], }
// Test how shadowing the process object via `let`
// behaves in each useGlobal state. Note: we can't
// actually test the state when useGlobal is true,
// because the exception that's generated is caught
// (see below), but errors are printed, and the test
// suite is aware of it, causing a failure to be flagged.
[false, 'undefined\n42', processTest]
];
const next = common.mustCall(() => {
if (testCases.length) {
const [option, expected, runner] = testCases.shift();
runRepl(option, runner, common.mustCall((err, output) => {
assert.strictEqual(output, expected);
next();
}));
}
}, testCases.length + 1);
next();
function runRepl(useGlobal, testFunc, cb) { function runRepl(useGlobal, testFunc, cb) {
const inputStream = new stream.PassThrough(); const inputStream = new stream.PassThrough();