diff --git a/lib/readline.js b/lib/readline.js index bca9c25ab2348b..5f28f4c43f17d5 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -48,8 +48,6 @@ const { kClearScreenDown } = CSI; -const { now } = process.binding('timer_wrap').Timer; - const kHistorySize = 30; const kMincrlfDelay = 100; // \r\n, \n, or \r followed by something other than \n @@ -411,7 +409,7 @@ Interface.prototype._normalWrite = function(b) { } var string = this._decoder.write(b); if (this._sawReturnAt && - now() - this._sawReturnAt <= this.crlfDelay) { + Date.now() - this._sawReturnAt <= this.crlfDelay) { string = string.replace(/^\n/, ''); this._sawReturnAt = 0; } @@ -424,7 +422,7 @@ Interface.prototype._normalWrite = function(b) { this._line_buffer = null; } if (newPartContainsEnding) { - this._sawReturnAt = string.endsWith('\r') ? now() : 0; + this._sawReturnAt = string.endsWith('\r') ? Date.now() : 0; // got one or more newlines; process into "line" events var lines = string.split(lineEnding); @@ -917,14 +915,14 @@ Interface.prototype._ttyWrite = function(s, key) { switch (key.name) { case 'return': // carriage return, i.e. \r - this._sawReturnAt = now(); + this._sawReturnAt = Date.now(); this._line(); break; case 'enter': // When key interval > crlfDelay if (this._sawReturnAt === 0 || - now() - this._sawReturnAt > this.crlfDelay) { + Date.now() - this._sawReturnAt > this.crlfDelay) { this._line(); } this._sawReturnAt = 0; diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index 69c0f113ff103b..fe33e244cebd20 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -869,3 +869,80 @@ function isWarned(emitter) { assert.strictEqual(rl._prompt, '$ '); } }); + +// For the purposes of the following tests, we do not care about the exact +// value of crlfDelay, only that the behaviour conforms to what's expected. +// Setting it to Infinity allows the test to succeed even under extreme +// CPU stress. +const crlfDelay = Infinity; + +[ true, false ].forEach(function(terminal) { + // sending multiple newlines at once that does not end with a new line + // and a `end` event(last line is) + + // \r\n should emit one line event, not two + { + const fi = new FakeInput(); + const rli = new readline.Interface( + { + input: fi, + output: fi, + terminal: terminal, + crlfDelay + } + ); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + let callCount = 0; + rli.on('line', function(line) { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + fi.emit('data', expectedLines.join('\r\n')); + assert.strictEqual(callCount, expectedLines.length - 1); + rli.close(); + } + + // \r\n should emit one line event when split across multiple writes. + { + const fi = new FakeInput(); + const rli = new readline.Interface({ + input: fi, + output: fi, + terminal: terminal, + crlfDelay + }); + const expectedLines = ['foo', 'bar', 'baz', 'bat']; + let callCount = 0; + rli.on('line', function(line) { + assert.strictEqual(line, expectedLines[callCount]); + callCount++; + }); + expectedLines.forEach(function(line) { + fi.emit('data', `${line}\r`); + fi.emit('data', '\n'); + }); + assert.strictEqual(callCount, expectedLines.length); + rli.close(); + } + + // Emit one line event when the delay between \r and \n is + // over the default crlfDelay but within the setting value. + { + const fi = new FakeInput(); + const delay = 125; + const rli = new readline.Interface({ + input: fi, + output: fi, + terminal: terminal, + crlfDelay + }); + let callCount = 0; + rli.on('line', () => callCount++); + fi.emit('data', '\r'); + setTimeout(common.mustCall(() => { + fi.emit('data', '\n'); + assert.strictEqual(callCount, 1); + rli.close(); + }), delay); + } +}); diff --git a/test/sequential/test-readline-interface.js b/test/sequential/test-readline-interface.js deleted file mode 100644 index 488dd8338da761..00000000000000 --- a/test/sequential/test-readline-interface.js +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -// Flags: --expose_internals -'use strict'; -const common = require('../common'); - -// These test cases are in `sequential` rather than the analogous test file in -// `parallel` because they become unreliable under load. The unreliability under -// load was determined empirically when the test cases were in `parallel` by -// running: -// tools/test.py -j 96 --repeat 192 test/parallel/test-readline-interface.js - -const assert = require('assert'); -const readline = require('readline'); -const EventEmitter = require('events').EventEmitter; -const inherits = require('util').inherits; - -function FakeInput() { - EventEmitter.call(this); -} -inherits(FakeInput, EventEmitter); -FakeInput.prototype.resume = () => {}; -FakeInput.prototype.pause = () => {}; -FakeInput.prototype.write = () => {}; -FakeInput.prototype.end = () => {}; - -[ true, false ].forEach(function(terminal) { - // sending multiple newlines at once that does not end with a new line - // and a `end` event(last line is) - - // \r\n should emit one line event, not two - { - const fi = new FakeInput(); - const rli = new readline.Interface( - { input: fi, output: fi, terminal: terminal } - ); - const expectedLines = ['foo', 'bar', 'baz', 'bat']; - let callCount = 0; - rli.on('line', function(line) { - assert.strictEqual(line, expectedLines[callCount]); - callCount++; - }); - fi.emit('data', expectedLines.join('\r\n')); - assert.strictEqual(callCount, expectedLines.length - 1); - rli.close(); - } - - // \r\n should emit one line event when split across multiple writes. - { - const fi = new FakeInput(); - const rli = new readline.Interface( - { input: fi, output: fi, terminal: terminal } - ); - const expectedLines = ['foo', 'bar', 'baz', 'bat']; - let callCount = 0; - rli.on('line', function(line) { - assert.strictEqual(line, expectedLines[callCount]); - callCount++; - }); - expectedLines.forEach(function(line) { - fi.emit('data', `${line}\r`); - fi.emit('data', '\n'); - }); - assert.strictEqual(callCount, expectedLines.length); - rli.close(); - } - - // Emit one line event when the delay between \r and \n is - // over the default crlfDelay but within the setting value. - { - const fi = new FakeInput(); - const delay = 125; - const crlfDelay = common.platformTimeout(1000); - const rli = new readline.Interface({ - input: fi, - output: fi, - terminal: terminal, - crlfDelay - }); - let callCount = 0; - rli.on('line', () => callCount++); - fi.emit('data', '\r'); - setTimeout(common.mustCall(() => { - fi.emit('data', '\n'); - assert.strictEqual(callCount, 1); - rli.close(); - }), delay); - } -});