From: Jeremiah Senkpiel Date: Fri, 11 Dec 2015 22:45:53 +0000 (-0500) Subject: test: parallelize test-repl-persistent-history X-Git-Tag: v4.2.5~112 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fd250b8fabce5ac11d0dd0f9a22d64c4b47f3650;p=platform%2Fupstream%2Fnodejs.git test: parallelize test-repl-persistent-history Refs: https://github.com/nodejs/node/pull/2224#issuecomment-127356413 PR-URL: https://github.com/nodejs/node/pull/4247 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- diff --git a/test/parallel/test-repl-persistent-history.js b/test/parallel/test-repl-persistent-history.js new file mode 100644 index 000000000..10e0dedf5 --- /dev/null +++ b/test/parallel/test-repl-persistent-history.js @@ -0,0 +1,274 @@ +'use strict'; + +// Flags: --expose-internals + +const common = require('../common'); +const stream = require('stream'); +const REPL = require('internal/repl'); +const assert = require('assert'); +const fs = require('fs'); +const util = require('util'); +const path = require('path'); +const os = require('os'); + +common.refreshTmpDir(); + +// Mock os.homedir() +os.homedir = function() { + return common.tmpDir; +}; + +// Create an input stream specialized for testing an array of actions +class ActionStream extends stream.Stream { + run(data) { + const _iter = data[Symbol.iterator](); + const self = this; + + function doAction() { + const next = _iter.next(); + if (next.done) { + // Close the repl. Note that it must have a clean prompt to do so. + setImmediate(function() { + self.emit('keypress', '', { ctrl: true, name: 'd' }); + }); + return; + } + const action = next.value; + + if (typeof action === 'object') { + self.emit('keypress', '', action); + } else { + self.emit('data', action + '\n'); + } + setImmediate(doAction); + } + setImmediate(doAction); + } + resume() {} + pause() {} +} +ActionStream.prototype.readable = true; + + +// Mock keys +const UP = { name: 'up' }; +const ENTER = { name: 'enter' }; +const CLEAR = { ctrl: true, name: 'u' }; +// Common message bits +const prompt = '> '; +const replDisabled = '\nPersistent history support disabled. Set the ' + + 'NODE_REPL_HISTORY environment\nvariable to a valid, ' + + 'user-writable path to enable.\n'; +const convertMsg = '\nConverting old JSON repl history to line-separated ' + + 'history.\nThe new repl history file can be found at ' + + path.join(common.tmpDir, '.node_repl_history') + '.\n'; +const homedirErr = '\nError: Could not get the home directory.\n' + + 'REPL session history will not be persisted.\n'; +const replFailedRead = '\nError: Could not open history file.\n' + + 'REPL session history will not be persisted.\n'; +// File paths +const fixtures = path.join(common.testDir, 'fixtures'); +const historyFixturePath = path.join(fixtures, '.node_repl_history'); +const historyPath = path.join(common.tmpDir, '.fixture_copy_repl_history'); +const historyPathFail = path.join(common.tmpDir, '.node_repl\u0000_history'); +const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json'); +const enoentHistoryPath = path.join(fixtures, 'enoent-repl-history-file.json'); +const defaultHistoryPath = path.join(common.tmpDir, '.node_repl_history'); + + +const tests = [{ + env: { NODE_REPL_HISTORY: '' }, + test: [UP], + expected: [prompt, replDisabled, prompt] +}, +{ + env: { NODE_REPL_HISTORY: '', + NODE_REPL_HISTORY_FILE: enoentHistoryPath }, + test: [UP], + expected: [prompt, replDisabled, prompt] +}, +{ + env: { NODE_REPL_HISTORY: '', + NODE_REPL_HISTORY_FILE: oldHistoryPath }, + test: [UP], + expected: [prompt, replDisabled, prompt] +}, +{ + env: { NODE_REPL_HISTORY: historyPath }, + test: [UP, CLEAR], + expected: [prompt, prompt + '\'you look fabulous today\'', prompt] +}, +{ + env: { NODE_REPL_HISTORY: historyPath, + NODE_REPL_HISTORY_FILE: oldHistoryPath }, + test: [UP, CLEAR], + expected: [prompt, prompt + '\'you look fabulous today\'', prompt] +}, +{ + env: { NODE_REPL_HISTORY: historyPath, + NODE_REPL_HISTORY_FILE: '' }, + test: [UP, CLEAR], + expected: [prompt, prompt + '\'you look fabulous today\'', prompt] +}, +{ + env: {}, + test: [UP], + expected: [prompt] +}, +{ + env: { NODE_REPL_HISTORY_FILE: oldHistoryPath }, + test: [UP, CLEAR, '\'42\'', ENTER], + expected: [prompt, convertMsg, prompt, prompt + '\'=^.^=\'', prompt, '\'', + '4', '2', '\'', '\'42\'\n', prompt, prompt], + after: function ensureHistoryFixture() { + // XXX(Fishrock123) Make sure nothing weird happened to our fixture + // or it's temporary copy. + // Sometimes this test used to erase the fixture and I'm not sure why. + const history = fs.readFileSync(historyFixturePath, 'utf8'); + assert.strictEqual(history, + '\'you look fabulous today\'\n\'Stay Fresh~\'\n'); + const historyCopy = fs.readFileSync(historyPath, 'utf8'); + assert.strictEqual(historyCopy, '\'you look fabulous today\'' + os.EOL + + '\'Stay Fresh~\'' + os.EOL); + } +}, +{ // Requires the above testcase + env: {}, + test: [UP, UP, ENTER], + expected: [prompt, prompt + '\'42\'', prompt + '\'=^.^=\'', '\'=^.^=\'\n', + prompt] +}, +{ + env: { NODE_REPL_HISTORY: historyPath, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP, UP, CLEAR], + expected: [prompt, prompt + '\'you look fabulous today\'', prompt] +}, +{ + env: { NODE_REPL_HISTORY_FILE: oldHistoryPath, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP, UP, UP, CLEAR], + expected: [prompt, convertMsg, prompt, prompt + '\'=^.^=\'', prompt] +}, +{ + env: { NODE_REPL_HISTORY: historyPathFail, + NODE_REPL_HISTORY_SIZE: 1 }, + test: [UP], + expected: [prompt, replFailedRead, prompt, replDisabled, prompt] +}, +{ // Make sure this is always the last test, since we change os.homedir() + before: function mockHomedirFailure() { + // Mock os.homedir() failure + os.homedir = function() { + throw new Error('os.homedir() failure'); + }; + }, + env: {}, + test: [UP], + expected: [prompt, homedirErr, prompt, replDisabled, prompt] +}]; +const numtests = tests.length; + + +var testsNotRan = tests.length; + +process.on('beforeExit', () => + assert.strictEqual(testsNotRan, 0) +); + +function cleanupTmpFile() { + try { + // Write over the file, clearing any history + fs.writeFileSync(defaultHistoryPath, ''); + } catch (err) { + if (err.code === 'ENOENT') return true; + throw err; + } + return true; +} + +// Copy our fixture to the tmp directory +fs.createReadStream(historyFixturePath) + .pipe(fs.createWriteStream(historyPath)).on('unpipe', () => runTest()); + +function runTest(assertCleaned) { + const opts = tests.shift(); + if (!opts) return; // All done + + if (assertCleaned) { + try { + assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); + } catch (e) { + if (e.code !== 'ENOENT') { + console.error(`Failed test # ${numtests - tests.length}`); + throw e; + } + } + } + + const env = opts.env; + const test = opts.test; + const expected = opts.expected; + const after = opts.after; + const before = opts.before; + + if (before) before(); + + REPL.createInternalRepl(env, { + input: new ActionStream(), + output: new stream.Writable({ + write(chunk, _, next) { + const output = chunk.toString(); + + // Ignore escapes and blank lines + if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) + return next(); + + try { + assert.strictEqual(output, expected.shift()); + } catch (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + next(); + } + }), + prompt: prompt, + useColors: false, + terminal: true + }, function(err, repl) { + if (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + + repl.once('close', () => { + if (repl._flushing) { + repl.once('flushHistory', onClose); + return; + } + + onClose(); + }); + + function onClose() { + if (after) { + var cleaned = after(); + } else { + var cleaned = cleanupTmpFile(); + } + + try { + // Ensure everything that we expected was output + assert.strictEqual(expected.length, 0); + testsNotRan--; + setImmediate(runTest, cleaned); + } catch (err) { + console.error(`Failed test # ${numtests - tests.length}`); + throw err; + } + } + + repl.inputStream.run(test); + }); +} diff --git a/test/sequential/test-repl-persistent-history.js b/test/sequential/test-repl-persistent-history.js deleted file mode 100644 index 10e0dedf5..000000000 --- a/test/sequential/test-repl-persistent-history.js +++ /dev/null @@ -1,274 +0,0 @@ -'use strict'; - -// Flags: --expose-internals - -const common = require('../common'); -const stream = require('stream'); -const REPL = require('internal/repl'); -const assert = require('assert'); -const fs = require('fs'); -const util = require('util'); -const path = require('path'); -const os = require('os'); - -common.refreshTmpDir(); - -// Mock os.homedir() -os.homedir = function() { - return common.tmpDir; -}; - -// Create an input stream specialized for testing an array of actions -class ActionStream extends stream.Stream { - run(data) { - const _iter = data[Symbol.iterator](); - const self = this; - - function doAction() { - const next = _iter.next(); - if (next.done) { - // Close the repl. Note that it must have a clean prompt to do so. - setImmediate(function() { - self.emit('keypress', '', { ctrl: true, name: 'd' }); - }); - return; - } - const action = next.value; - - if (typeof action === 'object') { - self.emit('keypress', '', action); - } else { - self.emit('data', action + '\n'); - } - setImmediate(doAction); - } - setImmediate(doAction); - } - resume() {} - pause() {} -} -ActionStream.prototype.readable = true; - - -// Mock keys -const UP = { name: 'up' }; -const ENTER = { name: 'enter' }; -const CLEAR = { ctrl: true, name: 'u' }; -// Common message bits -const prompt = '> '; -const replDisabled = '\nPersistent history support disabled. Set the ' + - 'NODE_REPL_HISTORY environment\nvariable to a valid, ' + - 'user-writable path to enable.\n'; -const convertMsg = '\nConverting old JSON repl history to line-separated ' + - 'history.\nThe new repl history file can be found at ' + - path.join(common.tmpDir, '.node_repl_history') + '.\n'; -const homedirErr = '\nError: Could not get the home directory.\n' + - 'REPL session history will not be persisted.\n'; -const replFailedRead = '\nError: Could not open history file.\n' + - 'REPL session history will not be persisted.\n'; -// File paths -const fixtures = path.join(common.testDir, 'fixtures'); -const historyFixturePath = path.join(fixtures, '.node_repl_history'); -const historyPath = path.join(common.tmpDir, '.fixture_copy_repl_history'); -const historyPathFail = path.join(common.tmpDir, '.node_repl\u0000_history'); -const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json'); -const enoentHistoryPath = path.join(fixtures, 'enoent-repl-history-file.json'); -const defaultHistoryPath = path.join(common.tmpDir, '.node_repl_history'); - - -const tests = [{ - env: { NODE_REPL_HISTORY: '' }, - test: [UP], - expected: [prompt, replDisabled, prompt] -}, -{ - env: { NODE_REPL_HISTORY: '', - NODE_REPL_HISTORY_FILE: enoentHistoryPath }, - test: [UP], - expected: [prompt, replDisabled, prompt] -}, -{ - env: { NODE_REPL_HISTORY: '', - NODE_REPL_HISTORY_FILE: oldHistoryPath }, - test: [UP], - expected: [prompt, replDisabled, prompt] -}, -{ - env: { NODE_REPL_HISTORY: historyPath }, - test: [UP, CLEAR], - expected: [prompt, prompt + '\'you look fabulous today\'', prompt] -}, -{ - env: { NODE_REPL_HISTORY: historyPath, - NODE_REPL_HISTORY_FILE: oldHistoryPath }, - test: [UP, CLEAR], - expected: [prompt, prompt + '\'you look fabulous today\'', prompt] -}, -{ - env: { NODE_REPL_HISTORY: historyPath, - NODE_REPL_HISTORY_FILE: '' }, - test: [UP, CLEAR], - expected: [prompt, prompt + '\'you look fabulous today\'', prompt] -}, -{ - env: {}, - test: [UP], - expected: [prompt] -}, -{ - env: { NODE_REPL_HISTORY_FILE: oldHistoryPath }, - test: [UP, CLEAR, '\'42\'', ENTER], - expected: [prompt, convertMsg, prompt, prompt + '\'=^.^=\'', prompt, '\'', - '4', '2', '\'', '\'42\'\n', prompt, prompt], - after: function ensureHistoryFixture() { - // XXX(Fishrock123) Make sure nothing weird happened to our fixture - // or it's temporary copy. - // Sometimes this test used to erase the fixture and I'm not sure why. - const history = fs.readFileSync(historyFixturePath, 'utf8'); - assert.strictEqual(history, - '\'you look fabulous today\'\n\'Stay Fresh~\'\n'); - const historyCopy = fs.readFileSync(historyPath, 'utf8'); - assert.strictEqual(historyCopy, '\'you look fabulous today\'' + os.EOL + - '\'Stay Fresh~\'' + os.EOL); - } -}, -{ // Requires the above testcase - env: {}, - test: [UP, UP, ENTER], - expected: [prompt, prompt + '\'42\'', prompt + '\'=^.^=\'', '\'=^.^=\'\n', - prompt] -}, -{ - env: { NODE_REPL_HISTORY: historyPath, - NODE_REPL_HISTORY_SIZE: 1 }, - test: [UP, UP, CLEAR], - expected: [prompt, prompt + '\'you look fabulous today\'', prompt] -}, -{ - env: { NODE_REPL_HISTORY_FILE: oldHistoryPath, - NODE_REPL_HISTORY_SIZE: 1 }, - test: [UP, UP, UP, CLEAR], - expected: [prompt, convertMsg, prompt, prompt + '\'=^.^=\'', prompt] -}, -{ - env: { NODE_REPL_HISTORY: historyPathFail, - NODE_REPL_HISTORY_SIZE: 1 }, - test: [UP], - expected: [prompt, replFailedRead, prompt, replDisabled, prompt] -}, -{ // Make sure this is always the last test, since we change os.homedir() - before: function mockHomedirFailure() { - // Mock os.homedir() failure - os.homedir = function() { - throw new Error('os.homedir() failure'); - }; - }, - env: {}, - test: [UP], - expected: [prompt, homedirErr, prompt, replDisabled, prompt] -}]; -const numtests = tests.length; - - -var testsNotRan = tests.length; - -process.on('beforeExit', () => - assert.strictEqual(testsNotRan, 0) -); - -function cleanupTmpFile() { - try { - // Write over the file, clearing any history - fs.writeFileSync(defaultHistoryPath, ''); - } catch (err) { - if (err.code === 'ENOENT') return true; - throw err; - } - return true; -} - -// Copy our fixture to the tmp directory -fs.createReadStream(historyFixturePath) - .pipe(fs.createWriteStream(historyPath)).on('unpipe', () => runTest()); - -function runTest(assertCleaned) { - const opts = tests.shift(); - if (!opts) return; // All done - - if (assertCleaned) { - try { - assert.strictEqual(fs.readFileSync(defaultHistoryPath, 'utf8'), ''); - } catch (e) { - if (e.code !== 'ENOENT') { - console.error(`Failed test # ${numtests - tests.length}`); - throw e; - } - } - } - - const env = opts.env; - const test = opts.test; - const expected = opts.expected; - const after = opts.after; - const before = opts.before; - - if (before) before(); - - REPL.createInternalRepl(env, { - input: new ActionStream(), - output: new stream.Writable({ - write(chunk, _, next) { - const output = chunk.toString(); - - // Ignore escapes and blank lines - if (output.charCodeAt(0) === 27 || /^[\r\n]+$/.test(output)) - return next(); - - try { - assert.strictEqual(output, expected.shift()); - } catch (err) { - console.error(`Failed test # ${numtests - tests.length}`); - throw err; - } - next(); - } - }), - prompt: prompt, - useColors: false, - terminal: true - }, function(err, repl) { - if (err) { - console.error(`Failed test # ${numtests - tests.length}`); - throw err; - } - - repl.once('close', () => { - if (repl._flushing) { - repl.once('flushHistory', onClose); - return; - } - - onClose(); - }); - - function onClose() { - if (after) { - var cleaned = after(); - } else { - var cleaned = cleanupTmpFile(); - } - - try { - // Ensure everything that we expected was output - assert.strictEqual(expected.length, 0); - testsNotRan--; - setImmediate(runTest, cleaned); - } catch (err) { - console.error(`Failed test # ${numtests - tests.length}`); - throw err; - } - } - - repl.inputStream.run(test); - }); -}