Skip to content

Commit

Permalink
readline: use a "string_decoder" to parse "keypress" events
Browse files Browse the repository at this point in the history
While updating the readline test cases to test both "terimal: false" and
"terminal: true" mode, it turned out that the test case testing utf8 chars
being sent over multiple write() calls was failing. The solution is to use
a string_decoder instance when parsing the "keypress" events.
  • Loading branch information
TooTallNate committed Nov 7, 2012
1 parent e95e095 commit 3c91a7a
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 91 deletions.
8 changes: 5 additions & 3 deletions lib/readline.js
Original file line number Diff line number Diff line change
Expand Up @@ -801,12 +801,14 @@ exports.Interface = Interface;
*/

function emitKeypressEvents(stream) {
if (stream._emitKeypress) return;
stream._emitKeypress = true;
if (stream._keypressDecoder) return;
var StringDecoder = require('string_decoder').StringDecoder; // lazy load
stream._keypressDecoder = new StringDecoder('utf8');

function onData(b) {
if (stream.listeners('keypress').length > 0) {
emitKey(stream, b);
var r = stream._keypressDecoder.write(b);
if (r) emitKey(stream, r);
} else {
// Nobody's watching anyway
stream.removeListener('data', onData);
Expand Down
180 changes: 92 additions & 88 deletions test/simple/test-readline-interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,100 +32,104 @@ function FakeInput() {
inherits(FakeInput, EventEmitter);
FakeInput.prototype.resume = function() {};
FakeInput.prototype.pause = function() {};
FakeInput.prototype.write = function() {};
FakeInput.prototype.end = function() {};

var fi;
var rli;
var called;
[ true, false ].forEach(function(terminal) {
var fi;
var rli;
var called;

// sending a full line
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'asdf');
});
fi.emit('data', 'asdf\n');
assert.ok(called);
// sending a full line
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'asdf');
});
fi.emit('data', 'asdf\n');
assert.ok(called);

// sending a blank line
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, '');
});
fi.emit('data', '\n');
assert.ok(called);
// sending a blank line
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, '');
});
fi.emit('data', '\n');
assert.ok(called);

// sending a single character with no newline
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
});
fi.emit('data', 'a');
assert.ok(!called);
rli.close();
// sending a single character with no newline
fi = new FakeInput();
rli = new readline.Interface(fi, {});

This comment has been minimized.

Copy link
@tjwebb

tjwebb Mar 20, 2016

@TooTallNate is there some reason why this instantiation was not updated in the same manner as the others?

called = false;
rli.on('line', function(line) {
called = true;
});
fi.emit('data', 'a');
assert.ok(!called);
rli.close();

// sending a single character with no newline and then a newline
fi = new FakeInput();
rli = new readline.Interface(fi, {});
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'a');
});
fi.emit('data', 'a');
assert.ok(!called);
fi.emit('data', '\n');
assert.ok(called);
rli.close();
// sending a single character with no newline and then a newline
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
called = false;
rli.on('line', function(line) {
called = true;
assert.equal(line, 'a');
});
fi.emit('data', 'a');
assert.ok(!called);
fi.emit('data', '\n');
assert.ok(called);
rli.close();

// sending multiple newlines at once
fi = new FakeInput();
rli = new readline.Interface(fi, {});
var expectedLines = ['foo\n', 'bar\n', 'baz\n'];
var callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\n') + '\n');
assert.equal(callCount, expectedLines.length);
rli.close();
// sending multiple newlines at once
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
var expectedLines = ['foo', 'bar', 'baz'];
var callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\n') + '\n');
assert.equal(callCount, expectedLines.length);
rli.close();

// sending multiple newlines at once that does not end with a new line
fi = new FakeInput();
rli = new readline.Interface(fi, {});
var expectedLines = ['foo\n', 'bar\n', 'baz\n', 'bat'];
var callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\n'));
assert.equal(callCount, expectedLines.length - 1);
rli.close();
// sending multiple newlines at once that does not end with a new line
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
expectedLines = ['foo', 'bar', 'baz', 'bat'];
callCount = 0;
rli.on('line', function(line) {
assert.equal(line, expectedLines[callCount]);
callCount++;
});
fi.emit('data', expectedLines.join('\n'));
assert.equal(callCount, expectedLines.length - 1);
rli.close();

// sending a multi-byte utf8 char over multiple writes
var buf = Buffer('☮', 'utf8');
fi = new FakeInput();
rli = new readline.Interface(fi, {});
callCount = 0;
rli.on('line', function(line) {
callCount++;
assert.equal(line, buf.toString('utf8'));
});
[].forEach.call(buf, function(i) {
fi.emit('data', Buffer([i]));
});
assert.equal(callCount, 0);
fi.emit('data', '\n');
assert.equal(callCount, 1);
rli.close();
// sending a multi-byte utf8 char over multiple writes
var buf = Buffer('☮', 'utf8');
fi = new FakeInput();
rli = new readline.Interface({ input: fi, output: fi, terminal: terminal });
callCount = 0;
rli.on('line', function(line) {
callCount++;
assert.equal(line, buf.toString('utf8'));
});
[].forEach.call(buf, function(i) {
fi.emit('data', Buffer([i]));
});
assert.equal(callCount, 0);
fi.emit('data', '\n');
assert.equal(callCount, 1);
rli.close();

assert.deepEqual(fi.listeners('end'), []);
assert.deepEqual(fi.listeners('data'), []);
assert.deepEqual(fi.listeners('end'), []);
assert.deepEqual(fi.listeners(terminal ? 'keypress' : 'data'), []);
});

0 comments on commit 3c91a7a

Please sign in to comment.