diff --git a/index.js b/index.js index a632b6a..18ade3c 100644 --- a/index.js +++ b/index.js @@ -235,4 +235,4 @@ PythonShell.prototype.end = function (callback) { return this; }; -module.exports = PythonShell; +module.exports = PythonShell; \ No newline at end of file diff --git a/test/python/conversation.py b/test/python/conversation.py new file mode 100644 index 0000000..9b03a05 --- /dev/null +++ b/test/python/conversation.py @@ -0,0 +1,71 @@ +import sys, json, time + +ended = False + +def is_json(myjson): + try: + json_object = json.loads(myjson) + except ValueError, e: + return False + return True + +def makeError(reason): + ended = True + return { + 'action': 'error', + 'reason': reason + } + +def handleKnockKnock(obj): + response = { + 'action': 'knockknockjoke' + }; + + message = obj['message']; + if (message == 'Knock, knock.'): + response['message'] = "Who's there?" + return response; + + if (message == 'Orange.'): + response['message'] = "Orange who?" + return response; + + if (message == "Orange you glad I didn't say, 'banana'?"): + response['message'] = "Ha ha." + ended = True + return response; + + return makeError('Unrecognised knock-knock phase.') + +def handleAction(obj): + if 'action' not in obj: + return makeError("Unsupported input; expected 'action' key.") + + action = obj['action'] + if action == 'knockknockjoke': + return handleKnockKnock(obj) + + return makeError("Unrecognised action: {0}".format(action)) + +def handleLine(line): + if not is_json(line): + return makeError('Malformed input could not be parsed as JSON: {0}'.format(line)) + + parsed = json.loads(line) + + if type (parsed) != type({}): + return makeError('Malformed input: expected JSON object; received JSON primitive instead: {0}'.format(parsed)) + + return handleAction(parsed) + +# simple JSON echo script +while not ended: + line = sys.stdin.readline() + if not line: + break +# for line in sys.stdin: + + response = handleLine(line) + + print json.dumps(response) + sys.stdout.flush() \ No newline at end of file diff --git a/test/test-python-shell.js b/test/test-python-shell.js index 4898617..19479f6 100644 --- a/test/test-python-shell.js +++ b/test/test-python-shell.js @@ -1,5 +1,7 @@ var should = require('should'); var PythonShell = require('..'); +var util = require('util'); +var os = require("os"); describe('PythonShell', function () { @@ -63,32 +65,129 @@ describe('PythonShell', function () { }); describe('.send(message)', function () { - it('should send string messages when mode is "text"', function (done) { - var pyshell = new PythonShell('echo_text.py', { - mode: 'text' - }); - var output = ''; - pyshell.stdout.on('data', function (data) { - output += ''+data; + context('text mode', function() { + it('should send string messages', function (done) { + var pyshell = new PythonShell('echo_text.py', { + mode: 'text' + }); + var output = ''; + pyshell.stdout.on('data', function (data) { + output += ''+data; + }); + pyshell.send('hello').send('world').end(function (err) { + if (err) return done(err); + output.should.be.exactly('hello\nworld\n'); + done(); + }); + }); + }) + context('JSON mode', function() { + it('should send JSON messages', function (done) { + var pyshell = new PythonShell('echo_json.py', { + mode: 'json' + }); + var output = ''; + pyshell.stdout.on('data', function (data) { + output += ''+data; + }); + pyshell.send({ a: 'b' }).send(null).send([1, 2, 3]).end(function (err) { + if (err) return done(err); + output.should.be.exactly('{"a": "b"}\nnull\n[1, 2, 3]\n'); + done(); + }); }); - pyshell.send('hello').send('world').end(function (err) { - if (err) return done(err); - output.should.be.exactly('hello\nworld\n'); - done(); + it('holds a conversation', function (done) { + var pyshell = new PythonShell('conversation.py', { + mode: 'json' + }); + var receivedMessages = []; + + function makeKnockKnockMessage(message) { + return { + action: 'knockknockjoke', + message: message + }; + } + + var outgoingMessages = [ + "Knock, knock.", + "Orange.", + "Orange you glad I didn't say, 'banana'?" + ]; + + var incomingMessages = [ + "Who's there?", + "Orange who?", + "Ha ha." + ]; + + var makeKnockKnockReply = makeKnockKnockMessage; + + function handleReply(reply) { + switch(reply.message) { + case incomingMessages[0]: + pyshell.send(makeKnockKnockMessage(outgoingMessages[1])); + break; + case incomingMessages[1]: + pyshell.send(makeKnockKnockMessage(outgoingMessages[2])); + break; + case incomingMessages[2]: + endAndAssert(); + break; + } + } + + pyshell.on('message', function (message) { + receivedMessages.push(message); + + switch(message.action) { + case 'knockknockjoke': + handleReply(message); + break; + default: + done(util.format("Unexpected action: '%s'", data.action)) + } + }); + + pyshell.send(makeKnockKnockMessage(outgoingMessages[0])); + + function endAndAssert() { + pyshell.end(function (err) { + if (err) { + return done(err); + } + + should(receivedMessages[0]) + .eql(makeKnockKnockReply(incomingMessages[0]), + "Correct knock-knock reply received."); + + should(receivedMessages[1]) + .eql(makeKnockKnockReply(incomingMessages[1]), + "Correct knock-knock reply received."); + + should(receivedMessages[2]) + .eql(makeKnockKnockReply(incomingMessages[2]), + "Correct knock-knock reply received."); + + done(); + }); + } }); }); - it('should send JSON messages when mode is "json"', function (done) { - var pyshell = new PythonShell('echo_json.py', { - mode: 'json' - }); - var output = ''; - pyshell.stdout.on('data', function (data) { - output += ''+data; - }); - pyshell.send({ a: 'b' }).send(null).send([1, 2, 3]).end(function (err) { - if (err) return done(err); - output.should.be.exactly('{"a": "b"}\nnull\n[1, 2, 3]\n'); - done(); + context('binary mode', function() { + it('should write as-is', function (done) { + var pyshell = new PythonShell('echo_binary.py', { + mode: 'binary' + }); + var output = ''; + pyshell.stdout.on('data', function (data) { + output += ''+data; + }); + pyshell.send(new Buffer('i am not a string')).end(function (err) { + if (err) return done(err); + output.should.be.exactly('i am not a string'); + done(); + }); }); }); it('should use a custom formatter', function (done) { @@ -107,20 +206,6 @@ describe('PythonShell', function () { done(); }); }); - it('should write as-is when mode is "binary"', function (done) { - var pyshell = new PythonShell('echo_binary.py', { - mode: 'binary' - }); - var output = ''; - pyshell.stdout.on('data', function (data) { - output += ''+data; - }); - pyshell.send(new Buffer('i am not a string')).end(function (err) { - if (err) return done(err); - output.should.be.exactly('i am not a string'); - done(); - }); - }); }); describe('.receive(data)', function () {