// -*- mode: js2; js-run: t -*- // // Copyright (c) 2010 Ivan Shvedunov. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials // provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. var swh = require("./swank-handler"); var readFromString = require("./lisp").readFromString; var config = require("./config"); var util = require("util"); var assert = require("assert"); var cfg = new config.FakeConfig(); var expected = []; var executive = new swh.Executive({ config: cfg, pid: 4242 }); var handler = new swh.Handler(executive); handler.on( "response", function (response) { // console.log("=> %s", response); assert.ok(expected.length > 0); assert.ok(typeof(response) == "string"); // console.log("response: %s", response); var expectedResponse = expected.shift(); if (expectedResponse instanceof RegExp) assert.ok(expectedResponse.test(response)); else assert.equal(expectedResponse, response, "got response " + response + " instead of " + expectedResponse); }); function expect () { for (var i = 0; i < arguments.length; ++i) expected.push(arguments[i]); } function verifyExpectations () { // console.log("expected: %s\n", expected.map(JSON.stringify).join("\n")); assert.equal(0, expected.length); } function request (str) { for (var i = 1; i < arguments.length; ++i) expected.push(arguments[i]); handler.receive(readFromString(str)); verifyExpectations(); } request('(:emacs-rex (swank:connection-info) "COMMON-LISP-USER" t 1)', '(:return (:ok (:encoding (:coding-system "utf-8" :external-format "UTF-8") ' + ':lisp-implementation (:name "JS" :type "JS" :version "1.5") ' + ':package (:name "NODE" :prompt "NODE") ' + ':pid 4242 :version "2010-11-13")) ' + '1)'); // currently we just ignore swank-require request('(:emacs-rex (swank:swank-require \'(swank-listener-hooks swank-indentation)) "COMMON-LISP-USER" t 2)', '(:return (:ok nil) 2)'); request('(:emacs-rex (swank:create-repl nil) "COMMON-LISP-USER" t 3)', '(:return (:ok ("NODE" "NODE")) 3)'); request('(:emacs-rex (swank:listener-eval "3 * 10\n") "NODE" :repl-thread 4)', '(:return (:ok (:values "30")) 4)'); request('(:emacs-rex (swank:listener-eval "undefined") "NODE" :repl-thread 5)', '(:return (:ok nil) 5)'); request('(:emacs-rex (swank:autodoc \'("zzzz" swank::%cursor-marker%) :print-right-margin 236)' + ' "COMMON-LISP-USER" :repl-thread 6)', '(:return (:ok :not-available) 6)'); request('(:emacs-rex (swank:listener-eval "_swank.output(\'hello world\\\\n\')") "NODE" :repl-thread 7)', '(:write-string "hello world\n")', '(:return (:ok nil) 7)'); request('(:emacs-rex (swank:listener-eval "_swank.output(1234)") "NODE" :repl-thread 8)', '(:write-string "1234")', '(:return (:ok nil) 8)'); request('(:emacs-rex (swank:listener-eval "zzz") "NODE" :repl-thread 9)', /^\(:write-string "ReferenceError: zzz is not defined(.|\n)*"\)$/, '(:return (:ok nil) 9)'); // TBD: debugger function FakeRemote (name) { this.name = name; }; util.inherits(FakeRemote, swh.Remote); FakeRemote.prototype.prompt = function prompt () { return "FAKE"; }; FakeRemote.prototype.kind = function kind () { return "test"; }; FakeRemote.prototype.id = function id () { return this.name; }; FakeRemote.prototype.evaluate = function evaluate (id, str) { this.sendResult(id, [ "R:" + this.name + ":" + str.replace(/^\s*|\s*$/g, "") ]); }; request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 10)', '(:return (:ok ((1 :direct "node.js" t))) 10)'); expect('(:write-string "Remote attached: (test) test/localhost:8080\n")'); var r1 = new FakeRemote("test/localhost:8080"); executive.attachRemote(r1); verifyExpectations(); request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 11)', '(:return (:ok ((1 :direct "node.js" t) (2 :test "test/localhost:8080" nil))) 11)'); expect('(:write-string "Remote attached: (test) test/localhost:9999\n")'); var r2 = new FakeRemote("test/localhost:9999"); executive.attachRemote(r2); verifyExpectations(); request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 12)', '(:return (:ok ((1 :direct "node.js" t) ' + '(2 :test "test/localhost:8080" nil) ' + '(3 :test "test/localhost:9999" nil))) 12)'); request('(:emacs-rex (swank:listener-eval "3 * 10\n") "NODE" :repl-thread 13)', '(:return (:ok (:values "30")) 13)'); request('(:emacs-rex (js:select-remote 2 nil) "NODE" :repl-thread 14)', '(:new-package "FAKE" "FAKE")', '(:write-string "Remote selected: (test) test/localhost:8080\n")', '(:return (:ok nil) 14)'); request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 15)', '(:return (:ok ((1 :direct "node.js" nil) ' + '(2 :test "test/localhost:8080" t) ' + '(3 :test "test/localhost:9999" nil))) 15)'); request('(:emacs-rex (swank:listener-eval "3 * 10\n") "NODE" :repl-thread 16)', '(:return (:ok (:values "R:test/localhost:8080:3 * 10")) 16)'); expect('(:write-string "Remote detached: (test) test/localhost:8080\n")', '(:new-package "NODE" "NODE")', '(:write-string "Remote selected (auto): (direct) node.js\n")'); r1.disconnect(); verifyExpectations(); request('(:emacs-rex (swank:listener-eval "3 * 10\n") "NODE" :repl-thread 17)', '(:return (:ok (:values "30")) 17)'); // TBD: add higher-level functions for testing remotes request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 18)', '(:return (:ok ((1 :direct "node.js" t) ' + '(3 :test "test/localhost:9999" nil))) 18)'); expect('(:write-string "Remote detached: (test) test/localhost:9999\n")'); r2.disconnect(); verifyExpectations(); request('(:emacs-rex (swank:listener-eval "3 * 10\n") "NODE" :repl-thread 19)', '(:return (:ok (:values "30")) 19)'); request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 20)', '(:return (:ok ((1 :direct "node.js" t))) 20)'); request('(:emacs-rex (js:select-remote 2 nil) "NODE" :repl-thread 21)', '(:write-string "WARNING: bad remote index\n")', '(:return (:ok nil) 21)'); request('(:emacs-rex (swank:listener-eval "3 * 10\n") "NODE" :repl-thread 22)', '(:return (:ok (:values "30")) 22)'); request('(:emacs-rex (js:select-remote 1 nil) "NODE" :repl-thread 23)', '(:write-string "WARNING: remote already selected: (direct) node.js\n")', '(:return (:ok nil) 23)'); assert.equal(null, cfg.getNow("stickyRemote")); // test sticky remote selection expect('(:write-string "Remote attached: (test) test/localhost:8001\n")'); var r3 = new FakeRemote("test/localhost:8001"); executive.attachRemote(r3); verifyExpectations(); request('(:emacs-rex (js:list-remotes) "NODE" :repl-thread 24)', '(:return (:ok ((1 :direct "node.js" t) (4 :test "test/localhost:8001" nil))) 24)'); request('(:emacs-rex (js:select-remote 4 t) "NODE" :repl-thread 25)', '(:new-package "FAKE" "FAKE")', '(:write-string "Remote selected (sticky): (test) test/localhost:8001\n")', '(:return (:ok nil) 25)'); assert.equal("(test) test/localhost:8001", cfg.getNow("stickyRemote")); expect('(:write-string "Remote detached: (test) test/localhost:8001\n")', '(:new-package "NODE" "NODE")', '(:write-string "Remote selected (auto): (direct) node.js\n")'); r3.disconnect(); verifyExpectations(); expect('(:write-string "Remote attached: (test) test/localhost:8001\n")', '(:new-package "FAKE" "FAKE")', '(:write-string "Remote selected (auto): (test) test/localhost:8001\n")'); var r5 = new FakeRemote("test/localhost:8001"); executive.attachRemote(r5); verifyExpectations(); assert.equal("(test) test/localhost:8001", cfg.getNow("stickyRemote")); request('(:emacs-rex (js:select-remote 1 nil) "NODE" :repl-thread 26)', '(:new-package "NODE" "NODE")', '(:write-string "Remote selected: (direct) node.js\n")', '(:return (:ok nil) 26)'); request('(:emacs-rex (js:select-remote 5 nil) "NODE" :repl-thread 27)', '(:new-package "FAKE" "FAKE")', '(:write-string "Remote selected: (test) test/localhost:8001\n")', '(:return (:ok nil) 27)'); assert.equal(null, cfg.getNow("stickyRemote")); expect('(:write-string "Remote detached: (test) test/localhost:8001\n")', '(:new-package "NODE" "NODE")', '(:write-string "Remote selected (auto): (direct) node.js\n")'); r5.disconnect(); verifyExpectations(); expect('(:write-string "Remote attached: (test) test/localhost:8001\n")'); var r6 = new FakeRemote("test/localhost:8001"); executive.attachRemote(r6); verifyExpectations(); assert.equal(null, cfg.getNow("stickyRemote")); request('(:emacs-rex (js:set-target-url "http://localhost:1234/") "NODE" :repl-thread 28)', '(:return (:ok nil) 28)'); assert.equal("http://localhost:1234/", cfg.getNow("targetUrl")); request('(:emacs-rex (js:set-target-url "zzz") "NODE" :repl-thread 29)', '(:write-string "WARNING: the URL must contain host and port\n")', '(:return (:ok nil) 29)'); assert.equal("http://localhost:1234/", cfg.getNow("targetUrl")); assert.equal(null, cfg.getNow("slimeVersion")); request('(:emacs-rex (js:set-slime-version "2010-11-28") "NODE" :repl-thread 30)', '(:return (:ok nil) 30)'); assert.equal("2010-11-28", cfg.getNow("slimeVersion")); // TBD: use ## instead of numbers in the tests above (request() should take care of it) // TBD: test output from an inactive remote // TBD: are out-of-order results for :emacs-rex ok? look at slime sources /* list/select remotes along the lines of catching errors on the client: window.onerror http://stackoverflow.com/questions/951791/javascript-global-error-handling */ // TBD: add \n to messages from remotes / executive