diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2022-09-25 16:15:16 -0400 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2022-09-25 16:15:16 -0400 |
commit | 650c20f1ca4e07591a727e1cfcc74b3363d15985 (patch) | |
tree | 85d11f6437cde22f410c25e0e5f71a3131ebd07d /test/lisp/erc | |
parent | 8869332684c2302b5ba1ead4568bbc7ba1c0183e (diff) | |
parent | 4b85ae6a24380fb67a3315eaec9233f17a872473 (diff) | |
download | emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.gz emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.tar.bz2 emacs-650c20f1ca4e07591a727e1cfcc74b3363d15985.zip |
Merge 'master' into noverlay
Diffstat (limited to 'test/lisp/erc')
128 files changed, 13788 insertions, 5 deletions
diff --git a/test/lisp/erc/erc-dcc-tests.el b/test/lisp/erc/erc-dcc-tests.el new file mode 100644 index 00000000000..a1dfbab9dc5 --- /dev/null +++ b/test/lisp/erc/erc-dcc-tests.el @@ -0,0 +1,167 @@ +;;; erc-dcc-tests.el --- Tests for erc-dcc -*- lexical-binding:t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: +(require 'ert) +(require 'erc-dcc) + +(ert-deftest erc-dcc-ctcp-query-send-regexp () + (let ((s "DCC SEND \"file name\" 2130706433 9899 1405135128")) + (should (string-match erc-dcc-ctcp-query-send-regexp s)) + (should-not (match-string 2 s)) + (should (string= "file name" (match-string 1 s))) + (should (string= "SEND" (match-string 6 s)))) + (let ((s "DCC SEND \"file \\\" name\" 2130706433 9899 1405135128")) + (should (string-match erc-dcc-ctcp-query-send-regexp s)) + (should-not (match-string 2 s)) + (should (string= "SEND" (match-string 6 s))) + (should (string= "file \" name" + (erc-dcc-unquote-filename (match-string 1 s))))) + (let ((s "DCC SEND filename 2130706433 9899 1405135128")) + (should (string-match erc-dcc-ctcp-query-send-regexp s)) + (should (string= "filename" (match-string 2 s))) + (should (string= "2130706433" (match-string 3 s))) + (should (string= "9899" (match-string 4 s))) + (should (string= "1405135128" (match-string 5 s)))) + (let ((s "DCC TSEND filename 2130706433 9899 1405135128")) + (should (string-match erc-dcc-ctcp-query-send-regexp s)) + (should (string= "TSEND" (match-string 6 s))))) + +;; This also indirectly tests base functionality for +;; `erc-dcc-do-LIST-command' + +(defun erc-dcc-tests--dcc-handle-ctcp-send (turbo) + (let (erc-send-completed-hook + erc-insert-modify-hook + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (with-current-buffer (get-buffer-create "fake-server") + (erc-mode) + (setq erc-server-process + (start-process "fake" (current-buffer) "sleep" "10") + erc-input-marker (make-marker) + erc-insert-marker (make-marker) + erc-server-current-nick "dummy") + (set-process-query-on-exit-flag erc-server-process nil) + (should-not erc-dcc-list) + (erc-ctcp-query-DCC erc-server-process + "tester" + "~tester" + "fake.irc" + "dummy" + (concat "DCC " (if turbo "TSEND" "SEND") + " foo 2130706433 9899 1405135128")) + (should-not (cdr erc-dcc-list)) + (should (equal (plist-put (car erc-dcc-list) :parent 'fake) + `(:nick "tester!~tester@fake.irc" + :type GET + :peer nil + :parent fake + :ip "127.0.0.1" + :port "9899" + :file "foo" + :size 1405135128 + :turbo ,(and turbo t) + :secure nil))) + (goto-char (point-min)) + (should (search-forward "file foo offered by tester" nil t)) + (erc-dcc-do-LIST-command erc-server-process) + (should (search-forward-regexp (concat + "GET +no +1405135128 +foo" + (and turbo " +(T)") "$") + nil t)) + (when noninteractive + (kill-buffer)))) + ;; `erc-dcc-list' is global; must leave it empty + (should erc-dcc-list) + (setq erc-dcc-list nil)) + +(ert-deftest erc-dcc-handle-ctcp-send--base () + (erc-dcc-tests--dcc-handle-ctcp-send nil)) + +(ert-deftest erc-dcc-handle-ctcp-send--turbo () + (erc-dcc-tests--dcc-handle-ctcp-send t)) + +(ert-deftest erc-dcc-do-GET-command () + (with-temp-buffer + (let* ((proc (start-process "fake" (current-buffer) "sleep" "10")) + (elt (list :nick "tester!~tester@fake.irc" + :type 'GET + :peer nil + :parent proc + :ip "127.0.0.1" + :port "9899" + :file "foo.bin" + :size 1405135128)) + (erc-dcc-list (list elt)) + ;; + erc-accidental-paste-threshold-seconds + erc-insert-modify-hook erc-send-completed-hook + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook + calls) + (erc-mode) + (setq erc-server-process proc + erc-input-marker (make-marker) + erc-insert-marker (make-marker) + erc-server-current-nick "dummy") + (set-process-query-on-exit-flag proc nil) + (cl-letf (((symbol-function 'read-file-name) + (lambda (&rest _) "foo.bin")) + ((symbol-function 'erc-dcc-get-file) + (lambda (&rest r) (push r calls)))) + (goto-char (point-max)) + (set-marker erc-insert-marker (point-max)) + (erc-display-prompt) + + (ert-info ("No turbo") + (should-not (plist-member elt :turbo)) + (goto-char erc-input-marker) + (insert "/dcc GET tester foo.bin") + (erc-send-current-line) + (should-not (plist-member (car erc-dcc-list) :turbo)) + (should (equal (pop calls) (list elt "foo.bin" proc)))) + + (ert-info ("Arg turbo in pos 2") + (should-not (plist-member elt :turbo)) + (goto-char erc-input-marker) + (insert "/dcc GET -t tester foo.bin") + (erc-send-current-line) + (should (eq t (plist-get (car erc-dcc-list) :turbo))) + (should (equal (pop calls) (list elt "foo.bin" proc)))) + + (ert-info ("Arg turbo in pos 4") + (setq elt (plist-put elt :turbo nil) + erc-dcc-list (list elt)) + (goto-char erc-input-marker) + (insert "/dcc GET tester -t foo.bin") + (erc-send-current-line) + (should (eq t (plist-get (car erc-dcc-list) :turbo))) + (should (equal (pop calls) (list elt "foo.bin" proc)))) + + (ert-info ("Arg turbo in pos 6") + (setq elt (plist-put elt :turbo nil) + erc-dcc-list (list elt)) + (goto-char erc-input-marker) + (insert "/dcc GET tester foo.bin -t") + (erc-send-current-line) + (should (eq t (plist-get (car erc-dcc-list) :turbo))) + (should (equal (pop calls) (list elt "foo.bin" proc)))))))) + +;;; erc-dcc-tests.el ends here diff --git a/test/lisp/erc/erc-join-tests.el b/test/lisp/erc/erc-join-tests.el new file mode 100644 index 00000000000..8210defbfbd --- /dev/null +++ b/test/lisp/erc/erc-join-tests.el @@ -0,0 +1,361 @@ +;;; erc-join-tests.el --- Tests for erc-join. -*- lexical-binding:t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Code: + +(require 'ert-x) +(require 'erc-join) +(require 'erc-networks) + +(ert-deftest erc-autojoin-channels--connect () + (should (eq erc-autojoin-timing 'connect)) + (should (= erc-autojoin-delay 30)) + (should-not erc--autojoin-timer) + + (let (calls + common + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (cl-letf (((symbol-function 'erc-server-send) + (lambda (line) (push line calls)))) + + (setq common + (lambda () + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-network 'FooNet + erc-session-server "irc.gnu.chat" + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-announced-name "foo.gnu.chat") + (set-process-query-on-exit-flag erc-server-process nil) + (erc-autojoin-channels erc-server-announced-name + "tester") + (should-not erc--autojoin-timer)))) + + (ert-info ("Join immediately on connect; server") + (let ((erc-autojoin-channels-alist '(("\\.gnu\\.chat\\'" "#chan")))) + (funcall common)) + (should (equal (pop calls) "JOIN #chan"))) + + (ert-info ("Join immediately on connect; network") + (let ((erc-autojoin-channels-alist '((FooNet "#chan")))) + (funcall common)) + (should (equal (pop calls) "JOIN #chan"))) + + (ert-info ("Do nothing; server") + (let ((erc-autojoin-channels-alist '(("bar\\.gnu\\.chat" "#chan")))) + (funcall common)) + (should-not calls)) + + (ert-info ("Do nothing; network") + (let ((erc-autojoin-channels-alist '((BarNet "#chan")))) + (funcall common)) + (should-not calls))))) + +(ert-deftest erc-autojoin-channels--delay () + (should (eq erc-autojoin-timing 'connect)) + (should (= erc-autojoin-delay 30)) + (should-not erc--autojoin-timer) + + (let (calls + common + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook + (erc-autojoin-timing 'ident) + (erc-autojoin-delay 0.05)) + + (cl-letf (((symbol-function 'erc-server-send) + (lambda (line) (push line calls))) + ((symbol-function 'erc-autojoin-after-ident) + (lambda (&rest _r) (error "I ran but shouldn't have")))) + + (setq common + (lambda () + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-network 'FooNet + erc-session-server "irc.gnu.chat" + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-announced-name "foo.gnu.chat") + (set-process-query-on-exit-flag erc-server-process nil) + (should-not erc--autojoin-timer) + (erc-autojoin-channels erc-server-announced-name "tester") + (should erc--autojoin-timer) + (should-not calls) + (sleep-for 0.1)))) + + (ert-info ("Deferred on connect; server") + (let ((erc-autojoin-channels-alist '(("\\.gnu\\.chat\\'" "#chan")))) + (funcall common)) + (should (equal (pop calls) "JOIN #chan"))) + + (ert-info ("Deferred on connect; network") + (let ((erc-autojoin-channels-alist '((FooNet "#chan")))) + (funcall common)) + (should (equal (pop calls) "JOIN #chan"))) + + (ert-info ("Do nothing; server") + (let ((erc-autojoin-channels-alist '(("bar\\.gnu\\.chat" "#chan")))) + (funcall common)) + (should-not calls))))) + +(ert-deftest erc-autojoin-channels--ident () + (should (eq erc-autojoin-timing 'connect)) + (should (= erc-autojoin-delay 30)) + (should-not erc--autojoin-timer) + + (let (calls + common + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook + (erc-autojoin-timing 'ident)) + + (cl-letf (((symbol-function 'erc-server-send) + (lambda (line) (push line calls)))) + + (setq common + (lambda () + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-network 'FooNet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-announced-name "foo.gnu.chat") + (set-process-query-on-exit-flag erc-server-process nil) + (erc-autojoin-after-ident 'FooNet "tester") + (should-not erc--autojoin-timer)))) + + (ert-info ("Join on NickServ hook; server") + (let ((erc-autojoin-channels-alist '(("\\.gnu\\.chat\\'" "#chan")))) + (funcall common)) + (should (equal (pop calls) "JOIN #chan"))) + + (ert-info ("Join on NickServ hook; network") + (let ((erc-autojoin-channels-alist '((FooNet "#chan")))) + (funcall common)) + (should (equal (pop calls) "JOIN #chan")))))) + +(defun erc-join-tests--autojoin-add--common (setup &optional fwd) + (let (calls + erc-autojoin-channels-alist + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (cl-letf (((symbol-function 'erc-handle-parsed-server-response) + (lambda (_p m) (push m calls)))) + + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-server-current-nick "tester" + erc--isupport-params (make-hash-table) + erc-server-announced-name "foo.gnu.chat") + (puthash 'CHANTYPES '("&#") erc--isupport-params) + (funcall setup) + (set-process-query-on-exit-flag erc-server-process nil) + (should-not calls) + + (ert-info ("Add #chan") + (erc-parse-server-response erc-server-process + (concat ":tester!~i@c.u JOIN #chan" + (and fwd " * :Tes Ter"))) + (should calls) + (erc-autojoin-add erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist '((FooNet "#chan"))))) + + (ert-info ("More recently joined chans are prepended") + (erc-parse-server-response + erc-server-process ; with account username + (concat ":tester!~i@c.u JOIN #spam" (and fwd " tester :Tes Ter"))) + (should calls) + (erc-autojoin-add erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '((FooNet "#spam" "#chan"))))) + + (ert-info ("Duplicates skipped") + (erc-parse-server-response erc-server-process + (concat ":tester!~i@c.u JOIN #chan" + (and fwd " * :Tes Ter"))) + (should calls) + (erc-autojoin-add erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '((FooNet "#spam" "#chan"))))) + + (ert-info ("Server used for local channel") + (erc-parse-server-response erc-server-process + (concat ":tester!~i@c.u JOIN &local" + (and fwd " * :Tes Ter"))) + (should calls) + (erc-autojoin-add erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '(("foo\\.gnu\\.chat" "&local") + (FooNet "#spam" "#chan"))))))))) + +(ert-deftest erc-autojoin-add--network () + (erc-join-tests--autojoin-add--common + (lambda () (setq erc-network 'FooNet + erc-networks--id (erc-networks--id-create nil))))) + +(ert-deftest erc-autojoin-add--network-extended-syntax () + (erc-join-tests--autojoin-add--common + (lambda () (setq erc-network 'FooNet + erc-networks--id (erc-networks--id-create nil))) + 'forward-compatible)) + +(ert-deftest erc-autojoin-add--network-id () + (erc-join-tests--autojoin-add--common + (lambda () (setq erc-network 'invalid + erc-networks--id (erc-networks--id-create 'FooNet))))) + +(ert-deftest erc-autojoin-add--server () + (let (calls + erc-autojoin-channels-alist + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (cl-letf (((symbol-function 'erc-handle-parsed-server-response) + (lambda (_p m) (push m calls)))) + + (ert-info ("Network unavailable, announced name used") + (setq erc-autojoin-channels-alist nil) + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-server-current-nick "tester" + erc-server-announced-name "foo.gnu.chat" + erc-networks--id (make-erc-networks--id)) ; assume too early + (set-process-query-on-exit-flag erc-server-process nil) + (should-not calls) + (erc-parse-server-response erc-server-process + ":tester!~u@q6ddatxcq6txy.irc JOIN #chan") + (should calls) + (erc-autojoin-add erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '(("gnu.chat" "#chan"))))))))) + +(defun erc-join-tests--autojoin-remove--common (setup) + (let (calls + erc-autojoin-channels-alist + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (cl-letf (((symbol-function 'erc-handle-parsed-server-response) + (lambda (_p m) (push m calls)))) + + (setq erc-autojoin-channels-alist ; mutated, so can't quote whole thing + (list '(FooNet "#spam" "##chan") + '(BarNet "#bar" "##bar") + '("foo\\.gnu\\.chat" "&local"))) + + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-server-current-nick "tester" + erc--isupport-params (make-hash-table) + erc-server-announced-name "foo.gnu.chat") + (puthash 'CHANTYPES '("&#") erc--isupport-params) + (funcall setup) + (set-process-query-on-exit-flag erc-server-process nil) + (should-not calls) + + (ert-info ("Remove #chan") + (erc-parse-server-response erc-server-process + ":tester!~i@c.u PART ##chan") + (should calls) + (erc-autojoin-remove erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '((FooNet "#spam") + (BarNet "#bar" "##bar") + ("foo\\.gnu\\.chat" "&local"))))) + + (ert-info ("Wrong network, nothing done") + (erc-parse-server-response erc-server-process + ":tester!~i@c.u PART #bar") + (should calls) + (erc-autojoin-remove erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '((FooNet "#spam") + (BarNet "#bar" "##bar") + ("foo\\.gnu\\.chat" "&local"))))) + + (ert-info ("Local channel keyed by server found") + (erc-parse-server-response erc-server-process + ":tester!~i@c.u PART &local") + (should calls) + (erc-autojoin-remove erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '((FooNet "#spam") (BarNet "#bar" "##bar"))))))))) + +(ert-deftest erc-autojoin-remove--network () + (erc-join-tests--autojoin-remove--common + (lambda () (setq erc-network 'FooNet + erc-networks--id (erc-networks--id-create nil))))) + +(ert-deftest erc-autojoin-remove--network-id () + (erc-join-tests--autojoin-remove--common + (lambda () (setq erc-network 'fake-a-roo + erc-networks--id (erc-networks--id-create 'FooNet))))) + +(ert-deftest erc-autojoin-remove--server () + (let (calls + erc-autojoin-channels-alist + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (cl-letf (((symbol-function 'erc-handle-parsed-server-response) + (lambda (_p m) (push m calls)))) + + (setq erc-autojoin-channels-alist (list '("gnu.chat" "#spam" "##chan") + '("fsf.chat" "#bar" "##bar"))) + + (ert-with-test-buffer (:name "foonet") + (erc-mode) + (setq erc-server-process + (start-process "true" (current-buffer) "true") + erc-server-current-nick "tester" + erc-server-announced-name "foo.gnu.chat" + ;; Assume special case w/o known network + erc-networks--id (make-erc-networks--id)) + (set-process-query-on-exit-flag erc-server-process nil) + (should-not calls) + + (ert-info ("Announced name matched, #chan removed") + (erc-parse-server-response erc-server-process + ":tester!~i@c.u PART ##chan") + (should calls) + (erc-autojoin-remove erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '(("gnu.chat" "#spam") + ("fsf.chat" "#bar" "##bar"))))) + + (ert-info ("Wrong announced name, nothing done") + (erc-parse-server-response erc-server-process + ":tester!~i@c.u PART #bar") + (should calls) + (erc-autojoin-remove erc-server-process (pop calls)) + (should (equal erc-autojoin-channels-alist + '(("gnu.chat" "#spam") + ("fsf.chat" "#bar" "##bar"))))))))) + +;;; erc-join-tests.el ends here diff --git a/test/lisp/erc/erc-match-tests.el b/test/lisp/erc/erc-match-tests.el new file mode 100644 index 00000000000..cd7598703b5 --- /dev/null +++ b/test/lisp/erc/erc-match-tests.el @@ -0,0 +1,193 @@ +;;; erc-match-tests.el --- Tests for erc-match. -*- lexical-binding:t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: +;;; Code: + +(require 'ert-x) +(require 'erc-match) + + +(ert-deftest erc-add-entry-to-list () + (let ((erc-pals '("z")) + (erc-match-quote-when-adding 'ask)) + + (ert-info ("Default (ask)") + (ert-simulate-keys "\t\ry\r" + (erc-add-entry-to-list 'erc-pals "?" '((".")) nil) + (should (equal (pop erc-pals) "\\."))) + + (ert-info ("Inverted") + (ert-simulate-keys "\t\ry\r" + (erc-add-entry-to-list 'erc-pals "?" '((".")) nil) + (should (equal (pop erc-pals) "\\.")))) + + (ert-info ("Skipped") + (ert-simulate-keys "\t\r" + (erc-add-entry-to-list 'erc-pals "?" '(("x")) nil) + (should (equal (pop erc-pals) "x"))))) + + (ert-info ("Verbatim") + (setq erc-match-quote-when-adding nil) + (ert-simulate-keys "\t\r" + (erc-add-entry-to-list 'erc-pals "?" '((".")) nil) + (should (equal (pop erc-pals) "."))) + + (ert-info ("Inverted") + (ert-simulate-keys "\t\r" + (erc-add-entry-to-list 'erc-pals "?" '((".")) t) + (should (equal (pop erc-pals) "\\."))))) + + (ert-info ("Quoted") + (setq erc-match-quote-when-adding t) + (ert-simulate-keys "\t\r" + (erc-add-entry-to-list 'erc-pals "?" '((".")) nil) + (should (equal (pop erc-pals) "\\."))) + + (ert-info ("Inverted") + (ert-simulate-keys "\t\r" + (erc-add-entry-to-list 'erc-pals "?" '((".")) t) + (should (equal (pop erc-pals) "."))))) + + (should (equal erc-pals '("z"))))) + +(ert-deftest erc-pals () + (with-temp-buffer + (setq erc-server-process (start-process "true" (current-buffer) "true") + erc-server-users (make-hash-table :test #'equal)) + (set-process-query-on-exit-flag erc-server-process nil) + (erc-add-server-user "FOO[m]" (make-erc-server-user :nickname "foo[m]")) + (erc-add-server-user "tester" (make-erc-server-user :nickname "tester")) + + (let ((erc-match-quote-when-adding t) + erc-pals calls rvs) + (cl-letf (((symbol-function 'completing-read) + (lambda (&rest r) (push r calls) (pop rvs)))) + + (ert-info ("`erc-add-pal'") + (push "foo[m]" rvs) + (ert-simulate-command '(erc-add-pal)) + (should (equal (cadr (pop calls)) '(("tester") ("foo[m]")))) + (should (equal erc-pals '("foo\\[m]")))) + + (ert-info ("`erc-match-pal-p'") + (should (erc-match-pal-p "FOO[m]!~u@example.net" nil))) + + (ert-info ("`erc-delete-pal'") + (push "foo\\[m]" rvs) + (ert-simulate-command '(erc-delete-pal)) + (should (equal (cadr (pop calls)) '(("foo\\[m]")))) + (should-not erc-pals)) + + (ert-info ("`erc-add-pal' verbatim") + (push "foo[m]" rvs) + (ert-simulate-command '(erc-add-pal (4))) + (should (equal (cadr (pop calls)) '(("tester") ("foo[m]")))) + (should (equal erc-pals '("foo[m]")))))))) + +(ert-deftest erc-fools () + (with-temp-buffer + (setq erc-server-process (start-process "true" (current-buffer) "true") + erc-server-users (make-hash-table :test #'equal)) + (set-process-query-on-exit-flag erc-server-process nil) + (erc-add-server-user "FOO[m]" (make-erc-server-user :nickname "foo[m]")) + (erc-add-server-user "tester" (make-erc-server-user :nickname "tester")) + + (let ((erc-match-quote-when-adding t) + erc-fools calls rvs) + (cl-letf (((symbol-function 'completing-read) + (lambda (&rest r) (push r calls) (pop rvs)))) + + (ert-info ("`erc-add-fool'") + (push "foo[m]" rvs) + (ert-simulate-command '(erc-add-fool)) + (should (equal (cadr (pop calls)) '(("tester") ("foo[m]")))) + (should (equal erc-fools '("foo\\[m]")))) + + (ert-info ("`erc-match-fool-p'") + (should (erc-match-fool-p "FOO[m]!~u@example.net" "")) + (should (erc-match-fool-p "tester!~u@example.net" "FOO[m]: die"))) + + (ert-info ("`erc-delete-fool'") + (push "foo\\[m]" rvs) + (ert-simulate-command '(erc-delete-fool)) + (should (equal (cadr (pop calls)) '(("foo\\[m]")))) + (should-not erc-fools)) + + (ert-info ("`erc-add-fool' verbatim") + (push "foo[m]" rvs) + (ert-simulate-command '(erc-add-fool (4))) + (should (equal (cadr (pop calls)) '(("tester") ("foo[m]")))) + (should (equal erc-fools '("foo[m]")))))))) + +(ert-deftest erc-keywords () + (let ((erc-match-quote-when-adding t) + erc-keywords calls rvs) + (cl-letf (((symbol-function 'completing-read) + (lambda (&rest r) (push r calls) (pop rvs)))) + + (ert-info ("`erc-add-keyword'") + (push "[cit. needed]" rvs) + (ert-simulate-command '(erc-add-keyword)) + (should (equal (cadr (pop calls)) nil)) + (should (equal erc-keywords '("\\[cit\\. needed]")))) + + (ert-info ("`erc-match-keyword-p'") + (should (erc-match-keyword-p nil "is pretty [cit. needed]"))) + + (ert-info ("`erc-delete-keyword'") + (push "\\[cit\\. needed]" rvs) + (ert-simulate-command '(erc-delete-keyword)) + (should (equal (cadr (pop calls)) '(("\\[cit\\. needed]")))) + (should-not erc-keywords)) + + (ert-info ("`erc-add-keyword' verbatim") + (push "[...]" rvs) + (ert-simulate-command '(erc-add-keyword (4))) + (should (equal (cadr (pop calls)) nil)) + (should (equal erc-keywords '("[...]"))))))) + +(ert-deftest erc-dangerous-hosts () + (let ((erc-match-quote-when-adding t) + erc-dangerous-hosts calls rvs) + (cl-letf (((symbol-function 'completing-read) + (lambda (&rest r) (push r calls) (pop rvs)))) + + (ert-info ("`erc-add-dangerous-host'") + (push "example.net" rvs) + (ert-simulate-command '(erc-add-dangerous-host)) + (should (equal (cadr (pop calls)) nil)) + (should (equal erc-dangerous-hosts '("example\\.net")))) + + (ert-info ("`erc-match-dangerous-host-p'") + (should (erc-match-dangerous-host-p "FOO[m]!~u@example.net" nil))) + + (ert-info ("`erc-delete-dangerous-host'") + (push "example\\.net" rvs) + (ert-simulate-command '(erc-delete-dangerous-host)) + (should (equal (cadr (pop calls)) '(("example\\.net")))) + (should-not erc-dangerous-hosts)) + + (ert-info ("`erc-add-dangerous-host' verbatim") + (push "example.net" rvs) + (ert-simulate-command '(erc-add-dangerous-host (4))) + (should (equal (cadr (pop calls)) nil)) + (should (equal erc-dangerous-hosts '("example.net"))))))) + +;;; erc-match-tests.el ends here diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el new file mode 100644 index 00000000000..66a334b7091 --- /dev/null +++ b/test/lisp/erc/erc-networks-tests.el @@ -0,0 +1,1707 @@ +;;; erc-networks-tests.el --- Tests for erc-networks. -*- lexical-binding:t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Code: + +(require 'ert-x) ; cl-lib +(require 'erc-networks) + +(defun erc-networks-tests--create-dead-proc (&optional buf) + (let ((p (start-process "true" (or buf (current-buffer)) "true"))) + (while (process-live-p p) (sit-for 0.1)) + p)) + +(defun erc-networks-tests--create-live-proc (&optional buf) + (let ((proc (start-process "sleep" (or buf (current-buffer)) "sleep" "1"))) + (set-process-query-on-exit-flag proc nil) + proc)) + +;; When we drop 27, call `get-buffer-create with INHIBIT-BUFFER-HOOKS. +(defun erc-networks-tests--clean-bufs () + (let (erc-kill-channel-hook + erc-kill-server-hook + erc-kill-buffer-hook) + (dolist (buf (erc-buffer-list)) + (kill-buffer buf)))) + +(defun erc-networks-tests--bufnames (prefix) + (let* ((case-fold-search) + (pred (lambda (b) (string-prefix-p prefix (buffer-name b)))) + (prefixed (seq-filter pred (buffer-list)))) + (sort (mapcar #'buffer-name prefixed) #'string<))) + +(ert-deftest erc-networks--id () + (cl-letf (((symbol-function 'float-time) + (lambda (&optional _) 0.0))) + + ;; Fixed + (should (equal (erc-networks--id-fixed-create 'foo) + (make-erc-networks--id-fixed :ts (float-time) + :symbol 'foo))) + + ;; Eliding + (let* ((erc-network 'FooNet) + (erc-server-current-nick "Joe") + (identity (erc-networks--id-create nil))) + + (should (equal identity #s(erc-networks--id-qualifying + 0.0 FooNet [FooNet "joe"] 1))) + (should (equal (erc-networks--id-qualifying-grow-id identity) + 'FooNet/joe)) + (should (equal identity #s(erc-networks--id-qualifying + 0.0 FooNet/joe [FooNet "joe"] 2))) + (should-not (erc-networks--id-qualifying-grow-id identity)) + (should (equal identity #s(erc-networks--id-qualifying + 0.0 FooNet/joe [FooNet "joe"] 2)))) + + ;; Compat + (with-current-buffer (get-buffer-create "fake.chat") + (with-suppressed-warnings ((obsolete erc-rename-buffers)) + (let (erc-rename-buffers) + (should (equal (erc-networks--id-create nil) + (make-erc-networks--id-fixed :ts (float-time) + :symbol 'fake.chat))))) + (kill-buffer)))) + +(ert-deftest erc-networks--id-create () + (cl-letf (((symbol-function 'float-time) + (lambda (&optional _) 0.0))) + + (should (equal (erc-networks--id-create 'foo) + (make-erc-networks--id-fixed :ts (float-time) + :symbol 'foo))) + (should (equal (erc-networks--id-create "foo") + (make-erc-networks--id-fixed :ts (float-time) + :symbol 'foo))) + (should (equal (erc-networks--id-create [h i]) + (make-erc-networks--id-fixed :ts (float-time) + :symbol (quote \[h\ \i\])))) + + (with-current-buffer (get-buffer-create "foo") + (let ((expected (make-erc-networks--id-fixed :ts (float-time) + :symbol 'foo))) + (with-suppressed-warnings ((obsolete erc-rename-buffers)) + (let (erc-rename-buffers) + (should (equal (erc-networks--id-create nil) expected)))) + (with-suppressed-warnings ((obsolete erc-reuse-buffers)) + (let (erc-reuse-buffers) + (should (equal (erc-networks--id-create nil) expected)) + (should (equal (erc-networks--id-create 'bar) expected))))) + (kill-buffer)))) + +(ert-deftest erc-networks--id-qualifying-prefix-length () + (should-not (erc-networks--id-qualifying-prefix-length + (make-erc-networks--id-qualifying) + (make-erc-networks--id-qualifying))) + + (should-not (erc-networks--id-qualifying-prefix-length + (make-erc-networks--id-qualifying :parts [1 2]) + (make-erc-networks--id-qualifying :parts [2 3]))) + + (should (= 1 (erc-networks--id-qualifying-prefix-length + (make-erc-networks--id-qualifying :parts [1]) + (make-erc-networks--id-qualifying :parts [1 2])))) + + (should (= 1 (erc-networks--id-qualifying-prefix-length + (make-erc-networks--id-qualifying :parts [1 2]) + (make-erc-networks--id-qualifying :parts [1 3])))) + + (should (= 2 (erc-networks--id-qualifying-prefix-length + (make-erc-networks--id-qualifying :parts [1 2]) + (make-erc-networks--id-qualifying :parts [1 2])))) + + (should (= 1 (erc-networks--id-qualifying-prefix-length + (make-erc-networks--id-qualifying :parts ["1"]) + (make-erc-networks--id-qualifying :parts ["1"]))))) + +(ert-deftest erc-networks--id-sort-buffers () + (let (oldest middle newest) + + (with-temp-buffer + (setq erc-networks--id (erc-networks--id-fixed-create 'oldest) + oldest (current-buffer)) + + (with-temp-buffer + (setq erc-networks--id (erc-networks--id-fixed-create 'middle) + middle (current-buffer)) + + (with-temp-buffer + (setq erc-networks--id (erc-networks--id-fixed-create 'newest) + newest (current-buffer)) + + (should (equal (erc-networks--id-sort-buffers + (list oldest newest middle)) + (list newest middle oldest)))))))) + +(ert-deftest erc-networks-rename-surviving-target-buffer--channel () + (should (memq #'erc-networks-rename-surviving-target-buffer + erc-kill-channel-hook)) + + (let ((chan-foonet-buffer (get-buffer-create "#chan@foonet"))) + + (with-current-buffer chan-foonet-buffer + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "bob"] :len 1) + erc--target (erc--target-from-string "#chan"))) + + (with-current-buffer (get-buffer-create "#chan@barnet") + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [barnet "bob"] :len 1) + erc--target (erc--target-from-string "#chan"))) + + (kill-buffer "#chan@barnet") + (should (equal (erc-networks-tests--bufnames "#chan") '("#chan"))) + (should (eq chan-foonet-buffer (get-buffer "#chan")))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks-rename-surviving-target-buffer--query () + (should (memq #'erc-networks-rename-surviving-target-buffer + erc-kill-buffer-hook)) + + (let ((bob-foonet (get-buffer-create "bob@foonet"))) + + (with-current-buffer bob-foonet + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "bob"] :len 1) + erc--target (erc--target-from-string "bob"))) + + (with-current-buffer (get-buffer-create "bob@barnet") + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [barnet "bob"] :len 1) + erc--target (erc--target-from-string "bob"))) + + (kill-buffer "bob@barnet") + (should (equal (erc-networks-tests--bufnames "bob") '("bob"))) + (should (eq bob-foonet (get-buffer "bob")))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks-rename-surviving-target-buffer--multi () + + (ert-info ("Multiple leftover channels untouched") + (with-current-buffer (get-buffer-create "#chan@foonet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan"))) + (with-current-buffer (get-buffer-create "#chan@barnet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan"))) + (with-current-buffer (get-buffer-create "#chan@baznet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan"))) + (kill-buffer "#chan@baznet") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@barnet" "#chan@foonet"))) + (erc-networks-tests--clean-bufs)) + + (ert-info ("Multiple leftover queries untouched") + (with-current-buffer (get-buffer-create "bob@foonet") + (erc-mode) + (setq erc--target (erc--target-from-string "bob"))) + (with-current-buffer (get-buffer-create "bob@barnet") + (erc-mode) + (setq erc--target (erc--target-from-string "bob"))) + (with-current-buffer (get-buffer-create "bob@baznet") + (erc-mode) + (setq erc--target (erc--target-from-string "bob"))) + (kill-buffer "bob@baznet") + (should (equal (erc-networks-tests--bufnames "bob") + '("bob@barnet" "bob@foonet"))) + (erc-networks-tests--clean-bufs))) + +;; As of May 2022, this "shrink" stuff runs whenever an ERC buffer is +;; killed because `erc-networks-shrink-ids-and-buffer-names' is a +;; default member of all three erc-kill-* functions. + +;; Note: this overlaps a fair bit with the "hook" variants, i.e., +;; `erc-networks--shrink-ids-and-buffer-names--hook-outstanding-*' If +;; this ever fails, just delete this and fix those. But please copy +;; over and adapt the comments first. + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--perform-outstanding () + ;; While some buffer #a@barnet/dummy is being killed, its display ID + ;; is not collapsed because collisions still exist. + ;; + ;; Note that we don't have to set `erc-server-connected' because + ;; this function is intentionally connectivity agnostic. + (with-current-buffer (get-buffer-create "foonet/tester") + (erc-mode) + (setq erc-server-current-nick "tester" ; Always set (`erc-open') + ;; Set when transport connected + erc-server-process (erc-networks-tests--create-live-proc) + ;; Both set just before IRC (logically) connected (post MOTD) + erc-network 'foonet + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/tester + :parts [foonet "tester"] + :len 2))) ; is/was a plain foonet collision + + ;; Presumably, some server buffer named foonet/dummy was just + ;; killed, hence the length 2 display ID. + + ;; A target buffer for chan #a exists for foonet/tester. The + ;; precise form of its name should not affect shrinking. + (with-current-buffer (get-buffer-create + (elt ["#a" "#a@foonet" "#a@foonet/tester"] (random 3))) + (erc-mode) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet/tester")) + erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet/tester")) + erc--target (erc--target-from-string "#a"))) + + ;; Another network context exists (so we have buffers to iterate + ;; over), and it's also part of a collision group. + (with-current-buffer (get-buffer-create "barnet/tester") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'barnet/tester + :parts [barnet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "barnet/dummy") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'barnet/dummy + :parts [barnet "dummy"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + ;; The buffer being killed is not part of the foonet collision + ;; group, which contains one display ID eligible for shrinkage. + (with-current-buffer (get-buffer-create + (elt ["#a@barnet" "#a@barnet/tester"] (random 2))) + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "barnet/tester")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "barnet/tester")) + erc--target (erc--target-from-string "#a"))) + + (with-temp-buffer ; doesn't matter what the current buffer is + (setq erc-networks--id (make-erc-networks--id-qualifying)) ; mock + (erc-networks--shrink-ids-and-buffer-names)) + + (should (equal (mapcar #'buffer-name (erc-buffer-list)) + '("foonet" ; shrunk + "#a@foonet" ; shrunk + "barnet/tester" + "barnet/dummy" + "#a@barnet/tester"))) + + (erc-networks-tests--clean-bufs)) + +;; This likewise overlaps with the "hook" variants below. If this +;; should ever fail, just delete it and optionally fix those. + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--perform-collapse () + ;; This is similar to the "outstanding" variant above, but both + ;; groups are eligible for renaming, which is abnormal but possible + ;; when recovering from some mishap. + (with-current-buffer (get-buffer-create "foonet/tester") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/tester + :parts [foonet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer + (get-buffer-create (elt ["#a" "#a@foonet/tester"] (random 2))) + (erc-mode) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet/tester")) + erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet/tester")) + erc--target (erc--target-from-string "#a"))) + + (with-current-buffer (get-buffer-create "barnet/tester") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'barnet/tester + :parts [barnet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer + (get-buffer-create (elt ["#b" "#b@foonet/tester"] (random 2))) + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "barnet/tester")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "barnet/tester")) + erc--target (erc--target-from-string "#b"))) + + (with-temp-buffer + (setq erc-networks--id (make-erc-networks--id-qualifying)) + (erc-networks--shrink-ids-and-buffer-names)) + + (should (equal (mapcar #'buffer-name (erc-buffer-list)) + '("foonet" "#a" "barnet" "#b"))) + + (erc-networks-tests--clean-bufs)) + +(defun erc-networks--shrink-ids-and-buffer-names--hook-outstanding-common () + + (with-current-buffer (get-buffer-create "foonet/tester") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/tester + :parts [foonet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "#a@foonet/tester") + (erc-mode) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet/tester")) + erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet/tester")) + erc--target (erc--target-from-string "#a"))) + + (with-current-buffer (get-buffer-create "barnet/tester") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'barnet/tester + :parts [barnet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "barnet/dummy") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'barnet/dummy + :parts [barnet "dummy"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "#a@barnet/tester") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "barnet/tester")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "barnet/tester")) + erc--target (erc--target-from-string "#a")))) + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--hook-outstanding-srv () + (erc-networks--shrink-ids-and-buffer-names--hook-outstanding-common) + (with-current-buffer (get-buffer-create "foonet/dummy") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/dummy + :parts [foonet "dummy"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc)) + (kill-buffer)) + + (should (equal (mapcar #'buffer-name (erc-buffer-list)) + '("foonet" + "#a@foonet" + "barnet/tester" + "barnet/dummy" + "#a@barnet/tester"))) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--hook-outstanding-tgt () + (erc-networks--shrink-ids-and-buffer-names--hook-outstanding-common) + (with-current-buffer (get-buffer-create "#a@foonet/dummy") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/dummy + :parts [foonet "dummy"] + :len 2) + erc--target (erc--target-from-string "#a") + erc-server-process (with-temp-buffer + (erc-networks-tests--create-dead-proc)))) + + (with-current-buffer "#a@foonet/dummy" (kill-buffer)) + + ;; Identical to *-server variant above + (should (equal (mapcar #'buffer-name (erc-buffer-list)) + '("foonet" + "#a@foonet" + "barnet/tester" + "barnet/dummy" + "#a@barnet/tester"))) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks-rename-surviving-target-buffer--shrink () + (erc-networks--shrink-ids-and-buffer-names--hook-outstanding-common) + + ;; This buffer isn't "#a@foonet" (yet) because the shrink-ids hook + ;; hasn't run. However, when it's the rename hook runs, its network + ;; id *is* "foonet", not "foonet/tester". + (with-current-buffer "#a@foonet/tester" (kill-buffer)) + + (should (equal (mapcar #'buffer-name (erc-buffer-list)) + '("foonet" + "barnet/tester" + "barnet/dummy" + "#a"))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--server () + + (with-current-buffer (get-buffer-create "foonet/tester") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/tester + :parts [foonet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "foonet/dummy") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/dummy + :parts [foonet "dummy"] + :len 2) + erc-server-process (erc-networks-tests--create-dead-proc)) + (kill-buffer)) + + (should (equal (mapcar #'buffer-name (erc-buffer-list)) '("foonet"))) + + (erc-networks-tests--clean-bufs)) + +(defun erc-networks--shrink-ids-and-buffer-names--hook-collapse (check) + + (with-current-buffer (get-buffer-create "foonet/tester") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/tester + :parts [foonet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "#a@foonet/tester") + (erc-mode) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet/tester")) + erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet/tester")) + erc--target (erc--target-from-string "#a"))) + + (with-current-buffer (get-buffer-create "barnet/tester") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'barnet/tester + :parts [barnet "tester"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "#b@foonet/tester") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "barnet/tester")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "barnet/tester")) + erc--target (erc--target-from-string "#b"))) + + (funcall check) + + (should (equal (mapcar #'buffer-name (erc-buffer-list)) + '("foonet" "#a" "barnet" "#b"))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--hook-collapse-server () + (erc-networks--shrink-ids-and-buffer-names--hook-collapse + (lambda () + (with-current-buffer (get-buffer-create "foonet/dummy") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/dummy + :parts [foonet "dummy"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc)) + (kill-buffer))))) + +(ert-deftest erc-networks--shrink-ids-and-buffer-names--hook-collapse-target () + (erc-networks--shrink-ids-and-buffer-names--hook-collapse + (lambda () + (with-current-buffer (get-buffer-create "#a@foonet/dummy") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "dummy" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/dummy + :parts [foonet "dummy"] + :len 2) + ;; `erc-kill-buffer-function' uses legacy target detection + ;; but falls back on buffer name, so no need for: + ;; + ;; erc-default-recipients '("#a") + ;; + erc--target (erc--target-from-string "#a") + erc-server-process (with-temp-buffer + (erc-networks-tests--create-dead-proc))) + (kill-buffer))))) + +;; FIXME this test is old and may describe impossible states: +;; leftover identities being qual-equal but not eq (implies +;; `erc-networks--reclaim-orphaned-target-buffers' is somehow broken). +;; +;; Otherwise, the point of this test is to show that server process +;; identity does not impact the hunt for duplicates. + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--duplicates (start) + + (with-current-buffer (get-buffer-create "foonet") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-process (funcall start))) + + (with-current-buffer (get-buffer-create "#chan") ; prior session + (erc-mode) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet")) + erc--target (erc--target-from-string "#chan") + erc-networks--id (erc-networks--id-create nil))) + + (ert-info ("Conflicts not recognized as ERC buffers and not renamed") + (get-buffer-create "#chan@foonet") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan" "#chan@foonet")))) + + ;; These are dupes (not "collisions") + + (with-current-buffer "#chan@foonet" ; same proc + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-network 'foonet + erc-server-current-nick "tester" + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet")) + erc-networks--id (erc-networks--id-create nil))) + + (with-current-buffer (get-buffer-create "#chan@foonet<dead>") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-server-process (erc-networks-tests--create-dead-proc) + erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil))) + + (with-current-buffer (get-buffer-create "#chan@foonet<live>") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-server-process (erc-networks-tests--create-live-proc) + erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil))) + + (let ((created (list (get-buffer "#chan@foonet<live>") + (get-buffer "#chan@foonet<dead>") + (get-buffer "#chan@foonet")))) + + (with-current-buffer "foonet" + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan"))) + + (ert-info ("All buffers considered dupes renamed") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan" "#chan<2>" "#chan<3>" "#chan<4>")))) + + (ert-info ("All buffers renamed from newest to oldest") + (should (equal created (list (get-buffer "#chan<2>") + (get-buffer "#chan<3>") + (get-buffer "#chan<4>")))))) + + (erc-networks-tests--clean-bufs)) + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--dupes-given (go) + + ;; The connection's network is discovered before target buffers are + ;; created. This shows that the network doesn't matter when only + ;; "given" IDs are present. + (with-current-buffer (get-buffer-create "oofnet") + (erc-mode) + (setq erc-networks--id (erc-networks--id-create 'oofnet) + erc-network 'foonet + erc-server-current-nick "tester" + erc-server-process (funcall go))) + + (with-current-buffer (get-buffer-create "#chan") ; prior session + (erc-mode) + (setq erc-networks--id (erc-networks--id-create 'oofnet) + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "oofnet")) + erc--target (erc--target-from-string "#chan"))) + + (with-current-buffer (get-buffer-create "#chan@oofnet") ;dupe/not collision + (erc-mode) + (setq erc-networks--id (erc-networks--id-create 'oofnet) + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "oofnet")) + erc--target (erc--target-from-string "#chan"))) + + (with-current-buffer "oofnet" + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan"))) + + (ert-info ("All buffers matching target and network renamed") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan" "#chan<2>")))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--reconcile-buffer-names--duplicates () + (ert-info ("Process live, no error") + (erc-tests--prep-erc-networks--reconcile-buffer-names--duplicates + #'erc-networks-tests--create-live-proc)) + + (ert-info ("Process live, no error, given ID") + (erc-tests--prep-erc-networks--reconcile-buffer-names--dupes-given + #'erc-networks-tests--create-live-proc)) + + (ert-info ("Process dead") + (erc-tests--prep-erc-networks--reconcile-buffer-names--duplicates + #'erc-networks-tests--create-dead-proc)) + + (ert-info ("Process dead, given ID") + (erc-tests--prep-erc-networks--reconcile-buffer-names--dupes-given + #'erc-networks-tests--create-dead-proc))) + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf (check) + (let ((foonet-proc (with-temp-buffer + (erc-networks-tests--create-dead-proc)))) + (with-current-buffer (get-buffer-create "barnet") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-process (erc-networks-tests--create-dead-proc))) + + ;; Different proc and not "qual-equal" (different elts) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc--target (erc--target-from-string "#chan") + erc-server-process foonet-proc)) + (funcall check) + (erc-networks-tests--clean-bufs))) + +(ert-deftest erc-networks--reconcile-buffer-names--no-server-buf () + (ert-info ("Existing #chan buffer respected") + (erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf + (lambda () + (with-current-buffer "barnet" + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan@barnet"))) + (ert-info ("Existing #chan buffer found and renamed") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@foonet"))))))) + + (ert-info ("Existing #chan buffer") + (erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf + (lambda () + (with-current-buffer (get-buffer-create "foonet") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-process (erc-networks-tests--create-dead-proc)) + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan"))) + (ert-info ("Nothing renamed") + (should (equal (erc-networks-tests--bufnames "#chan") '("#chan"))))))) + + (ert-info ("Existing #chan@foonet and #chan@barnet buffers") + (erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf + (lambda () + (with-current-buffer "#chan" + (rename-buffer "#chan@foonet")) + (should-not (get-buffer "#chan@barnet")) + (with-current-buffer (get-buffer-create "#chan@barnet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "barnet")) + erc-networks--id (erc-networks--id-create nil))) + (with-current-buffer (get-buffer-create "foonet") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-live-proc) + erc-networks--id (erc-networks--id-create nil)) + (set-process-query-on-exit-flag erc-server-process nil) + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan@foonet"))) + (ert-info ("Nothing renamed") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@barnet" "#chan@foonet")))))))) + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf-given + (check) + (let ((oofnet-proc (with-temp-buffer + (erc-networks-tests--create-dead-proc)))) + + (with-current-buffer (get-buffer-create "rabnet") + (erc-mode) + ;; Again, given name preempts network lookup (unrealistic but + ;; highlights priorities) + (setq erc-networks--id (erc-networks--id-create 'rabnet) + erc-network 'barnet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-dead-proc))) + + ;; Identity is not "qual-equal" to above + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-networks--id (erc-networks--id-create 'oofnet) + erc-network 'foonet + erc--target (erc--target-from-string "#chan") + erc-server-process oofnet-proc)) + (funcall check) + (erc-networks-tests--clean-bufs))) + +(ert-deftest erc-networks--reconcile-buffer-names--no-server-buf-given () + + (ert-info ("Existing #chan buffer respected") + (erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf-given + (lambda () + (with-current-buffer "rabnet" + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan@rabnet"))) + + (ert-info ("Existing #chan buffer found and renamed") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@oofnet"))))))) + + (ert-info ("Existing #chan@oofnet and #chan@rabnet buffers") + (erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf-given + (lambda () + ;; #chan has already been uniquified (but not grown) + (with-current-buffer "#chan" (rename-buffer "#chan@oofnet")) + (should-not (get-buffer "#chan@rabnet")) + + (with-current-buffer (get-buffer-create "#chan@rabnet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "rabnet")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "rabnet")))) + + (with-current-buffer (get-buffer-create "oofnet") + (erc-mode) + (setq erc-network 'oofnet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-live-proc) + erc-networks--id (erc-networks--id-create 'oofnet)) ; given + (set-process-query-on-exit-flag erc-server-process nil) + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan@oofnet"))) + + (ert-info ("Nothing renamed") + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@oofnet" "#chan@rabnet")))))))) + +;; This shows a corner case where a user explicitly assigns a "given" +;; ID via `erc-tls' but later connects again without one. It would +;; actually probably be better if the given identity were to win and +;; the derived one got an <n>-suffix. +;; +;; If we just compared net identities, the two would match, but they +;; don't here because one has a given name and the other a +;; discovered/assembled one; so they are *not* qual-equal. +(ert-deftest erc-networks--reconcile-buffer-names--no-srv-buf-given-mismatch () + ;; Existing #chan buffer *not* respected + (erc-tests--prep-erc-networks--reconcile-buffer-names--no-srv-buf-given + (lambda () + (with-current-buffer (get-buffer-create "oofnet") + (erc-mode) + (setq erc-network 'oofnet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-dead-proc) + erc-networks--id (erc-networks--id-create nil)) ; derived + (should (string= (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id) + "#chan@oofnet"))) + + (ert-info ("Collision renamed but not grown (because it's a given)") + ;; Original chan uniquified and moved out of the way + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@oofnet<2>"))))))) + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net (check) + + (with-current-buffer (get-buffer-create "foonet") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-dead-proc) + erc-networks--id (erc-networks--id-create nil))) ; derived + + (with-current-buffer (get-buffer-create "barnet") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-dead-proc) + erc-networks--id (erc-networks--id-create nil))) ; derived + + (with-current-buffer + (get-buffer-create (elt ["#chan" "#chan@foonet"] (random 2))) + (erc-mode) + (setq erc--target (erc--target-from-string "#chan")) + (cl-multiple-value-setq (erc-server-process erc-networks--id) + (with-current-buffer "foonet" + (list erc-server-process erc-networks--id)))) + + (with-current-buffer (get-buffer-create "#chan@barnet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan")) + (cl-multiple-value-setq (erc-server-process erc-networks--id) + (with-current-buffer "barnet" + (list erc-server-process erc-networks--id)))) + + (funcall check) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--reconcile-buffer-names--multi-net () + (ert-info ("Same network rename") + (erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net + (lambda () + (with-current-buffer "foonet" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@foonet")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@barnet" "#chan@foonet")))))) + + (ert-info ("Same network keep name") + (erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net + (lambda () + (with-current-buffer "barnet" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@barnet")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@barnet" "#chan@foonet"))))))) + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net-given + (check) + + (with-current-buffer (get-buffer-create "oofnet") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create 'oofnet) ; one given + erc-server-process (erc-networks-tests--create-dead-proc))) + + (with-current-buffer (get-buffer-create "rabnet") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create 'rabnet) ; another given + erc-server-process (erc-networks-tests--create-dead-proc))) + + (with-current-buffer (get-buffer-create (elt ["chan" "#chan@oofnet"] + (random 2))) + (erc-mode) + (setq erc--target (erc--target-from-string "#chan")) + (cl-multiple-value-setq (erc-server-process erc-networks--id) + (with-current-buffer "oofnet" + (list erc-server-process erc-networks--id)))) + + (with-current-buffer (get-buffer-create "#chan@barnet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan")) + (cl-multiple-value-setq (erc-server-process erc-networks--id) + (with-current-buffer "rabnet" + (list erc-server-process erc-networks--id)))) + + (funcall check) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--reconcile-buffer-names--multi-net-given () + (ert-info ("Same network rename") + (erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net-given + (lambda () + (with-current-buffer "oofnet" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@oofnet")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@oofnet" "#chan@rabnet")))))) + + (ert-info ("Same network keep name") + (erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net-given + (lambda () + (with-current-buffer "rabnet" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@rabnet")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@oofnet" "#chan@rabnet"))))))) + +(defun erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net-mixed + (check) + + (with-current-buffer (get-buffer-create "foonet") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) ; one derived + erc-server-process (erc-networks-tests--create-dead-proc))) + + (with-current-buffer (get-buffer-create "my-conn") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create 'my-conn) ; one given + erc-server-process (erc-networks-tests--create-dead-proc))) + + (with-current-buffer (get-buffer-create (elt ["#chan" "#chan@foonet"] + (random 2))) + (erc-mode) + (setq erc--target (erc--target-from-string "#chan")) + (cl-multiple-value-setq (erc-server-process erc-networks--id) + (with-current-buffer "foonet" + (list erc-server-process erc-networks--id)))) + + (with-current-buffer (get-buffer-create "#chan@my-conn") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan")) + (cl-multiple-value-setq (erc-server-process erc-networks--id) + (with-current-buffer "my-conn" + (list erc-server-process erc-networks--id)))) + + (funcall check) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--reconcile-buffer-names--multi-net-existing () + + (ert-info ("Buf name derived from network") + (erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net-mixed + (lambda () + (with-current-buffer "foonet" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@foonet")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@foonet" "#chan@my-conn")))))) + + (ert-info ("Buf name given") + (erc-tests--prep-erc-networks--reconcile-buffer-names--multi-net-mixed + (lambda () + (with-current-buffer "my-conn" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@my-conn")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@foonet" "#chan@my-conn"))))))) + +(ert-deftest erc-networks--reconcile-buffer-names--multi-net-suffixed () + ;; Two networks, same channel. One network has two connections. + ;; When the same channel is joined on the latter under a different + ;; nick, all buffer names involving that network are suffixed with + ;; the network identity. + + (with-current-buffer (get-buffer-create "foonet/bob") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "bob" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/bob + :parts [foonet "bob"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create + (elt ["#chan@foonet" "#chan@foonet/bob"] (random 2))) + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "foonet/bob")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet/bob")))) + + (with-current-buffer (get-buffer-create "barnet") + (erc-mode) + (setq erc-network 'barnet + erc-server-current-nick (elt ["alice" "bob"] (random 2)) + erc-networks--id (erc-networks--id-create 'barnet) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer (get-buffer-create "#chan@barnet") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "barnet")) + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "barnet")))) + + (with-current-buffer (get-buffer-create "foonet/alice") + (erc-mode) + (setq erc-network 'foonet + erc-server-current-nick "alice" + erc-networks--id (make-erc-networks--id-qualifying + :symbol 'foonet/alice + :parts [foonet "alice"] + :len 2) + erc-server-process (erc-networks-tests--create-live-proc))) + + (with-current-buffer "foonet/alice" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "#chan") erc-networks--id))) + (should (string= result "#chan@foonet/alice")))) + + (should (equal (erc-networks-tests--bufnames "#chan") + '("#chan@barnet" "#chan@foonet/bob"))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--reconcile-buffer-names--local () + (with-current-buffer (get-buffer-create "DALnet") + (erc-mode) + (setq erc-network 'DALnet + erc-server-announced-name "elysium.ga.us.dal.net" + erc-server-process (erc-networks-tests--create-dead-proc) + erc--isupport-params (make-hash-table) + erc-networks--id (erc-networks--id-create nil)) + (puthash 'CHANTYPES '("&#") erc--isupport-params)) + + (ert-info ("Local chan buffer from older, disconnected identity") + (with-current-buffer (get-buffer-create "&chan") + (erc-mode) + ;; Cheat here because localp is determined on identity init + (setq erc--target (with-current-buffer "DALnet" + (erc--target-from-string "&chan")) + erc-network 'DALnet + erc-server-announced-name "twisted.ma.us.dal.net" + erc-server-process (erc-networks-tests--create-dead-proc) + erc-networks--id (erc-networks--id-create nil)))) + + (ert-info ("Local channels renamed using network server names") + (with-current-buffer "DALnet" + (let ((result (erc-networks--reconcile-buffer-names + (erc--target-from-string "&chan") erc-networks--id))) + (should (string= result "&chan@elysium.ga.us.dal.net"))))) + + (should (get-buffer "&chan@twisted.ma.us.dal.net")) + (should-not (get-buffer "&chan")) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--set-name () + (with-current-buffer (get-buffer-create "localhost:6667") + (let (erc-server-announced-name + (erc--isupport-params (make-hash-table)) + erc-network + calls) + (erc-mode) + + (cl-letf (((symbol-function 'erc-display-line) + (lambda (&rest r) (push r calls)))) + + (ert-info ("Signals when `erc-server-announced-name' unset") + (should-error (erc-networks--set-name nil (make-erc-response))) + (should-not calls)) + + (ert-info ("Signals when table empty and NETWORK param unset") + (setq erc-server-announced-name "irc.fake.gnu.org") + (let ((err (should-error (erc-networks--set-name + nil (make-erc-response))))) + (should (string-match-p "failed" (cadr err))) + (should (eq (car err) 'error))) + (should (string-match-p (rx "*** Failed") (car (pop calls))))))) + + (erc-networks-tests--clean-bufs))) + +(ert-deftest erc-networks--ensure-announced () + (with-current-buffer (get-buffer-create "localhost:6667") + (should (local-variable-if-set-p 'erc-server-announced-name)) + (let (erc-insert-modify-hook + (erc-server-process (erc-networks-tests--create-live-proc)) + (parsed (make-erc-response + :unparsed ":irc.barnet.org 422 tester :MOTD File is missing" + :sender "irc.barnet.org" + :command "422" + :command-args '("tester" "MOTD File is missing") + :contents "MOTD File is missing"))) + + (erc-mode) ; boilerplate displayable start (needs `erc-server-process') + (insert "\n\n") + (setq erc-input-marker (make-marker) erc-insert-marker (make-marker)) + (set-marker erc-insert-marker (point-max)) + (erc-display-prompt) ; boilerplate displayable end + + (erc-networks--ensure-announced erc-server-process parsed) + (goto-char (point-min)) + (search-forward "Failed") + (should (string= erc-server-announced-name "irc.barnet.org"))) + (when noninteractive (kill-buffer)))) + +(ert-deftest erc-networks--rename-server-buffer--no-existing--orphan () + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc--target (erc--target-from-string "#chan") + erc-networks--id (erc-networks--id-create nil))) + + (with-current-buffer (get-buffer-create "irc.foonet.org") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-live-proc) + erc-networks--id (erc-networks--id-create nil)) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) "FooNet"))) + + (ert-info ("Channel buffer reassociated") + (erc-server-process-alive "#chan") + (with-current-buffer "#chan" + (should erc-server-connected) + (erc-with-server-buffer + (should (string= (buffer-name) "FooNet"))))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--rename-server-buffer--existing--reuse () + (let* ((old-buf (get-buffer-create "FooNet")) + (old-proc (erc-networks-tests--create-dead-proc old-buf))) + + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc + erc-networks--id (erc-networks--id-create nil))) + + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-process old-proc + erc-networks--id (erc-networks--id-create nil) + erc--target (erc--target-from-string "#chan"))) + + (ert-info ("New buffer steals name, content") + (with-current-buffer (get-buffer-create "irc.foonet.org") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-live-proc) + erc-networks--id (erc-networks--id-create nil)) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) "FooNet")) + (goto-char (point-min)) + (should (search-forward "Old buf")))) + + (ert-info ("Channel buffer reassociated") + (erc-server-process-alive "#chan") + (with-current-buffer "#chan" + (should erc-server-connected) + (should-not (eq erc-server-process old-proc)) + (erc-with-server-buffer + (should (string= (buffer-name) "FooNet"))))) + + (ert-info ("Original buffer killed off") + (should-not (buffer-live-p old-buf)))) + + (erc-networks-tests--clean-bufs)) + +;; This is for compatibility with pre-28.1 behavior. Basically, we're +;; trying to match the behavior bug for bug. All buffers were always +;; suffixed and never reassociated. 28.1 introduced a regression that +;; reversed the latter, but we've reverted that. + +(ert-deftest erc-networks--rename-server-buffer--existing--noreuse () + (with-suppressed-warnings ((obsolete erc-reuse-buffers)) + (should erc-reuse-buffers) ; default + (let* ((old-buf (get-buffer-create "irc.foonet.org:6697/irc.foonet.org")) + (old-proc (erc-networks-tests--create-dead-proc old-buf)) + erc-reuse-buffers) + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc + erc-networks--id (erc-networks--id-create nil))) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-process old-proc + erc-networks--id (buffer-local-value 'erc-networks--id old-buf) + erc--target (erc--target-from-string "#chan")) + (rename-buffer (erc-networks--construct-target-buffer-name erc--target))) + + (ert-info ("Server buffer uniquely renamed") + (with-current-buffer + (get-buffer-create "irc.foonet.org:6697/irc.foonet.org<2>") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-process (erc-networks-tests--create-live-proc) + erc-networks--id (erc-networks--id-create nil)) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) + "irc.foonet.org:6697/irc.foonet.org<2>")) + (goto-char (point-min)) + (should-not (search-forward "Old buf" nil t)))) + + (ert-info ("Channel buffer not reassociated") + (should-not + (erc-server-process-alive + (should (get-buffer "#chan/irc.foonet.org")))) + (with-current-buffer (get-buffer "#chan/irc.foonet.org") + (should-not erc-server-connected) + (should (eq erc-server-process old-proc)) + (erc-with-server-buffer + (should (string= (buffer-name) + "irc.foonet.org:6697/irc.foonet.org"))))) + + (ert-info ("Old buffer still around") + (should (buffer-live-p old-buf))))) + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--rename-server-buffer--reconnecting () + (let* ((old-buf (get-buffer-create "FooNet")) + (old-proc (erc-networks-tests--create-dead-proc old-buf))) + + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc + erc-networks--id (erc-networks--id-create nil))) + + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-process old-proc + erc--target (erc--target-from-string "#chan") + erc-networks--id (erc-networks--id-create nil))) + + (ert-info ("No new buffer") + (with-current-buffer old-buf + (setq erc-server-process (erc-networks-tests--create-live-proc)) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) "FooNet")) + (goto-char (point-min)) + (should (search-forward "Old buf")))) + + (ert-info ("Channel buffer updated with live proc") + (erc-server-process-alive "#chan") + (with-current-buffer "#chan" + (should erc-server-connected) + (should-not (eq erc-server-process old-proc)) + (erc-with-server-buffer + (should (string= (buffer-name) "FooNet")))))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--rename-server-buffer--id () + (let* ((old-buf (get-buffer-create "MySession")) + (old-proc (erc-networks-tests--create-dead-proc old-buf))) + + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-networks--id (erc-networks--id-create 'MySession) + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc)) + + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'FooNet + erc-networks--id (erc-networks--id-create 'MySession) + erc-server-process old-proc + erc--target (erc--target-from-string "#chan"))) + + (ert-info ("No new buffer") + (with-current-buffer old-buf + (setq erc-server-process (erc-networks-tests--create-live-proc)) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) "MySession")) + (goto-char (point-min)) + (should (search-forward "Old buf")))) + + (ert-info ("Channel buffer updated with live proc") + (erc-server-process-alive "#chan") + (with-current-buffer "#chan" + (should erc-server-connected) + (should-not (eq erc-server-process old-proc)) + (erc-with-server-buffer + (should (string= (buffer-name) "MySession")))))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--rename-server-buffer--existing--live () + (let* (erc-kill-server-hook + erc-insert-modify-hook + (old-buf (get-buffer-create "FooNet")) + (old-proc (erc-networks-tests--create-live-proc old-buf))) ; live + + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc + erc-networks--id (erc-networks--id-create nil)) + (should (erc-server-process-alive))) + + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-process old-proc + erc-networks--id (erc-networks--id-create nil) + erc-server-connected t + erc--target (erc--target-from-string "#chan"))) + + (ert-info ("New buffer rejected, abandoned, not killed") + (with-current-buffer (get-buffer-create "irc.foonet.org") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process (erc-networks-tests--create-live-proc) + erc-networks--id (erc-networks--id-create nil)) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (eq erc-active-buffer old-buf)) + (should-not (erc-server-process-alive)) + (should (string= (buffer-name) "irc.foonet.org")) + (goto-char (point-min)) + (search-forward "still connected"))) + + (ert-info ("Channel buffer updated with live proc") + (should (erc-server-process-alive "#chan")) + (with-current-buffer "#chan" + (should erc-server-connected) + (should (erc-server-buffer-live-p)) + (should (eq erc-server-process old-proc)) + (should (buffer-live-p (process-buffer erc-server-process))) + (with-current-buffer (process-buffer erc-server-process) + (should (eq (current-buffer) (get-buffer "FooNet"))) + (should (eq (current-buffer) old-buf)))))) + + (should (get-buffer "FooNet")) + (should (get-buffer "irc.foonet.org")) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--rename-server-buffer--local-match () + (let* ((old-buf (get-buffer-create "FooNet")) + (old-proc (erc-networks-tests--create-dead-proc old-buf))) + + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-announced-name "us-east.foonet.org" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc + erc--isupport-params (make-hash-table) + erc-networks--id (erc-networks--id-create nil)) + (puthash 'CHANTYPES '("&#") erc--isupport-params)) + + (with-current-buffer (get-buffer-create "&chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-process old-proc + erc-server-announced-name "us-east.foonet.org" + erc--target (erc--target-from-string "&chan") + erc-networks--id (erc-networks--id-create nil))) + + (ert-info ("New server buffer steals name, content") + (with-current-buffer (get-buffer-create "irc.foonet.org") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-announced-name "us-east.foonet.org" + erc-server-process (erc-networks-tests--create-live-proc) + erc--isupport-params (make-hash-table) + erc-networks--id (erc-networks--id-create nil)) + (puthash 'CHANTYPES '("&#") erc--isupport-params) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) "FooNet")) + (goto-char (point-min)) + (should (search-forward "Old buf")))) + + (ert-info ("Channel buffer reassociated when &local server matches") + (should (erc-server-process-alive "&chan")) + (with-current-buffer "&chan" + (should erc-server-connected) + (should-not (eq erc-server-process old-proc)) + (erc-with-server-buffer + (should (string= (buffer-name) "FooNet"))))) + + (ert-info ("Original buffer killed off") + (should-not (buffer-live-p old-buf))) + + (erc-networks-tests--clean-bufs))) + +(ert-deftest erc-networks--rename-server-buffer--local-nomatch () + (let* ((old-buf (get-buffer-create "FooNet")) + (old-proc (erc-networks-tests--create-dead-proc old-buf))) + + (with-current-buffer old-buf + (erc-mode) + (insert "*** Old buf") + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-announced-name "us-west.foonet.org" + erc-insert-marker (set-marker (make-marker) (point-max)) + erc-server-process old-proc + erc--isupport-params (make-hash-table) + erc-networks--id (erc-networks--id-create nil)) + (puthash 'CHANTYPES '("&#") erc--isupport-params)) + + (with-current-buffer (get-buffer-create "&chan") + (erc-mode) + (setq erc-network 'FooNet + erc-server-process old-proc + erc-server-announced-name "us-west.foonet.org" ; west + erc--target (erc--target-from-string "&chan") + erc-networks--id (erc-networks--id-create nil))) + + (ert-info ("New server buffer steals name, content") + (with-current-buffer (get-buffer-create "irc.foonet.org") + (erc-mode) + (setq erc-network 'FooNet + erc-server-current-nick "tester" + erc-server-announced-name "us-east.foonet.org" ; east + erc-server-process (erc-networks-tests--create-live-proc) + erc--isupport-params (make-hash-table) + erc-networks--id (erc-networks--id-create nil)) + + (puthash 'CHANTYPES '("&#") erc--isupport-params) + (should-not (erc-networks--rename-server-buffer erc-server-process)) + (should (string= (buffer-name) "FooNet")) + (goto-char (point-min)) + (should (search-forward "Old buf")))) + + (ert-info ("Channel buffer now orphaned even though network matches") + (should-not (erc-server-process-alive "&chan")) + (with-current-buffer "&chan" + (should-not erc-server-connected) + (should (eq erc-server-process old-proc)) + (erc-with-server-buffer + (should (string= (buffer-name) "FooNet"))))) + + (ert-info ("Original buffer killed off") + (should-not (buffer-live-p old-buf))) + + (erc-networks-tests--clean-bufs))) + +(ert-deftest erc-networks--update-server-identity--double-existing () + (with-temp-buffer + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "bob"] :len 1)) + + (with-current-buffer (get-buffer-create "#chan@foonet/bob") + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "bob"] :len 2))) + (with-current-buffer (get-buffer-create "foonet/alice") + (erc-mode) + (setq erc-networks--id + (make-erc-networks--id-qualifying :parts [foonet "alice"] :len 2))) + + (ert-info ("Adopt equivalent identity") + (should (eq (erc-networks--update-server-identity) + (buffer-local-value 'erc-networks--id + (get-buffer "#chan@foonet/bob"))))) + + (ert-info ("Ignore non-matches") + (should-not (erc-networks--update-server-identity)) + (should (eq erc-networks--id + (buffer-local-value 'erc-networks--id + (get-buffer "#chan@foonet/bob")))))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--update-server-identity--double-new () + (with-temp-buffer + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "bob"] :len 1)) + + (with-current-buffer (get-buffer-create "foonet/alice") + (erc-mode) + (setq erc-networks--id + (make-erc-networks--id-qualifying :parts [foonet "alice"] :len 2))) + (with-current-buffer (get-buffer-create "#chan@foonet/alice") + (erc-mode) + (setq erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet/alice")))) + + (ert-info ("Evolve identity to prevent ambiguity") + (should-not (erc-networks--update-server-identity)) + (should (= (erc-networks--id-qualifying-len erc-networks--id) 2)) + (should (eq (erc-networks--id-symbol erc-networks--id) 'foonet/bob)))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--update-server-identity--double-bounded () + (with-temp-buffer + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "bob"] :len 1)) + + (with-current-buffer (get-buffer-create "foonet/alice/home") + (erc-mode) + (setq erc-networks--id (make-erc-networks--id-qualifying + :parts [foonet "alice" home] :len 3))) + (with-current-buffer (get-buffer-create "#chan@foonet/alice/home") + (erc-mode) + (setq erc-networks--id + (buffer-local-value 'erc-networks--id + (get-buffer "foonet/alice/home")))) + + (ert-info ("Evolve identity to prevent ambiguity") + (should-not (erc-networks--update-server-identity)) + (should (= (erc-networks--id-qualifying-len erc-networks--id) 2)) + (should (eq (erc-networks--id-symbol erc-networks--id) 'foonet/bob)))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--update-server-identity--double-even () + (with-temp-buffer + (erc-mode) + (setq erc-networks--id + (make-erc-networks--id-qualifying :parts [foonet "bob"] :len 1)) + + (with-current-buffer (get-buffer-create "foonet") + (erc-mode) + (setq erc-networks--id + (make-erc-networks--id-qualifying :parts [foonet "alice"] :len 1))) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc--target (erc--target-from-string "#chan") + erc-networks--id (buffer-local-value 'erc-networks--id + (get-buffer "foonet")))) + + (ert-info ("Evolve identity to prevent ambiguity") + (should-not (erc-networks--update-server-identity)) + (should (= (erc-networks--id-qualifying-len erc-networks--id) 2)) + (should (eq (erc-networks--id-symbol erc-networks--id) 'foonet/bob))) + + (ert-info ("Collision renamed") + (with-current-buffer "foonet/alice" + (should (eq (erc-networks--id-symbol erc-networks--id) 'foonet/alice))) + + (with-current-buffer "#chan@foonet/alice" + (should (eq (erc-networks--id-symbol erc-networks--id) + 'foonet/alice))))) + + (erc-networks-tests--clean-bufs)) + +(ert-deftest erc-networks--update-server-identity--triple-new () + (with-temp-buffer + (erc-mode) + (setq erc-networks--id + (make-erc-networks--id-qualifying :parts [foonet "bob" home] :len 1)) + + (with-current-buffer (get-buffer-create "foonet/bob/office") + (erc-mode) + (setq erc-networks--id + (make-erc-networks--id-qualifying :parts [foonet "bob" office] + :len 3))) + (with-current-buffer (get-buffer-create "#chan@foonet/bob/office") + (erc-mode) + (setq erc-networks--id + (buffer-local-value 'erc-networks--id + (get-buffer "foonet/bob/office")))) + + (ert-info ("Extend our identity's canonical ID so that it's unique") + (should-not (erc-networks--update-server-identity)) + (should (= (erc-networks--id-qualifying-len erc-networks--id) 3)))) + + (erc-networks-tests--clean-bufs)) + +;;; erc-networks-tests.el ends here diff --git a/test/lisp/erc/erc-scenarios-auth-source.el b/test/lisp/erc/erc-scenarios-auth-source.el new file mode 100644 index 00000000000..3d399a18154 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-auth-source.el @@ -0,0 +1,178 @@ +;;; erc-scenarios-auth-source.el --- auth-source scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;; Commentary: +;; +;; For practical reasons (mainly lack of imagination), this file +;; contains tests for both server-password and NickServ contexts. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join) + (require 'erc-services)) + +(defun erc-scenarios-common--auth-source (id dialog &rest rest) + (push "machine GNU.chat port %d user \"#chan\" password spam" rest) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/auth-source") + (dumb-server (erc-d-run "localhost" t dialog)) + (port (process-contact dumb-server :service)) + (ents `(,@(mapcar (lambda (fmt) (format fmt port)) rest) + "machine MyHost port irc password 123")) + (netrc-file (make-temp-file "auth-source-test" nil nil + (string-join ents "\n"))) + (auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-scenarios-common-extra-teardown (lambda () + (delete-file netrc-file)))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester" + :id id) + (should (string= (buffer-name) (if id + (symbol-name id) + (format "127.0.0.1:%d" port)))) + (erc-d-t-wait-for 5 (eq erc-network 'FooNet)))))) + +(ert-deftest erc-scenarios-base-auth-source-server--dialed () + :tags '(:expensive-test) + (erc-scenarios-common--auth-source + nil 'foonet + "machine GNU.chat port %d user tester password fake" + "machine FooNet port %d user tester password fake" + "machine 127.0.0.1 port %d user tester password changeme" + "machine 127.0.0.1 port %d user imposter password fake")) + +(ert-deftest erc-scenarios-base-auth-source-server--netid () + :tags '(:expensive-test) + (erc-scenarios-common--auth-source + 'MySession 'foonet + "machine MySession port %d user tester password changeme" + "machine 127.0.0.1 port %d user tester password fake" + "machine FooNet port %d user tester password fake")) + +(ert-deftest erc-scenarios-base-auth-source-server--netid-custom () + :tags '(:expensive-test) + (let ((erc-auth-source-server-function + (lambda (&rest _) (erc-auth-source-search :host "MyHost")))) + (erc-scenarios-common--auth-source + 'MySession 'foonet + "machine 127.0.0.1 port %d user tester password fake" + "machine MyHost port %d user tester password changeme" + "machine MySession port %d user tester password fake"))) + +(ert-deftest erc-scenarios-base-auth-source-server--nopass () + :tags '(:expensive-test) + (let (erc-auth-source-server-function) + (erc-scenarios-common--auth-source nil 'nopass))) + +(ert-deftest erc-scenarios-base-auth-source-server--nopass-netid () + :tags '(:expensive-test) + (let (erc-auth-source-server-function) + (erc-scenarios-common--auth-source 'MySession 'nopass))) + +;; Identify via auth source with no initial password + +(defun erc-scenarios-common--services-auth-source (&rest rest) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "services/auth-source") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'libera)) + (port (process-contact dumb-server :service)) + (ents `(,@(mapcar (lambda (fmt) (format fmt port)) rest) + "machine MyHost port irc password 123")) + (netrc-file (make-temp-file "auth-source-test" nil nil + (string-join ents "\n"))) + (auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-modules (cons 'services erc-modules)) + (erc-use-auth-source-for-nickserv-password t) ; do consult for NickServ + (expect (erc-d-t-make-expecter)) + (erc-scenarios-common-extra-teardown (lambda () + (delete-file netrc-file)))) + + (cl-letf (((symbol-function 'read-passwd) + (lambda (&rest _) (error "Unexpected read-passwd call")))) + (ert-info ("Connect without password") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-wait-for 8 (eq erc-network 'Libera.Chat)) + (funcall expect 3 "This nickname is registered.") + (funcall expect 3 "You are now identified") + (funcall expect 3 "Last login from") + (erc-cmd-QUIT "")))) + + (erc-services-mode -1) + + (should-not (memq 'services erc-modules)))) + +;; These tests are about authenticating to nick services + +(ert-deftest erc-scenarios-services-auth-source--network () + :tags '(:expensive-test) + ;; Skip consulting auth-source for the server password (PASS). + (let (erc-auth-source-server-function) + (erc-scenarios-common--services-auth-source + "machine 127.0.0.1 port %d user tester password spam" + "machine zirconium.libera.chat port %d user tester password fake" + "machine Libera.Chat port %d user tester password changeme"))) + +(ert-deftest erc-scenarios-services-auth-source--network-connect-lookup () + :tags '(:expensive-test) + ;; Do consult auth-source for the server password (and find nothing) + (erc-scenarios-common--services-auth-source + "machine zirconium.libera.chat port %d user tester password fake" + "machine Libera.Chat port %d user tester password changeme")) + +(ert-deftest erc-scenarios-services-auth-source--announced () + :tags '(:expensive-test) + (let (erc-auth-source-server-function) + (erc-scenarios-common--services-auth-source + "machine 127.0.0.1 port %d user tester password spam" + "machine zirconium.libera.chat port %d user tester password changeme"))) + +(ert-deftest erc-scenarios-services-auth-source--dialed () + :tags '(:expensive-test) + ;; Support legacy host -> domain name + ;; (likely most common in real configs) + (let (erc-auth-source-server-function) + (erc-scenarios-common--services-auth-source + "machine 127.0.0.1 port %d user tester password changeme"))) + +(ert-deftest erc-scenarios-services-auth-source--custom () + :tags '(:expensive-test) + (let (erc-auth-source-server-function + (erc-auth-source-services-function + (lambda (&rest _) (erc-auth-source-search :host "MyAccount")))) + (erc-scenarios-common--services-auth-source + "machine zirconium.libera.chat port %d user tester password spam" + "machine MyAccount port %d user tester password changeme" + "machine 127.0.0.1 port %d user tester password fake"))) + +;;; erc-scenarios-auth-source.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-association-nick.el b/test/lisp/erc/erc-scenarios-base-association-nick.el new file mode 100644 index 00000000000..3e848be4df2 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-association-nick.el @@ -0,0 +1,163 @@ +;;; erc-scenarios-base-association-nick.el --- base assoc scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +;; You register a new nick, disconnect, and log back in, but your nick +;; is not granted, so ERC obtains a backtick'd version. You open a +;; query buffer for NickServ, and ERC names it using the net-ID (which +;; includes the backtick'd nick) as a suffix. The original +;; (disconnected) NickServ buffer gets renamed with *its* net-ID as +;; well. You then identify to NickServ, and the dead session is no +;; longer considered distinct. + +(ert-deftest erc-scenarios-base-association-nick-bumped () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bumped") + (dumb-server (erc-d-run "localhost" t 'renicked 'again)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.5) + (erc-server-flood-margin 30)) + + (ert-info ("Connect to foonet with nick tester") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (erc-scenarios-common-assert-initial-buf-name nil port) + (erc-d-t-wait-for 5 (eq erc-network 'foonet)))) + + (ert-info ("Create an account for tester and quit") + (with-current-buffer "foonet" + (funcall expect 3 "debug mode") + + (erc-cmd-QUERY "NickServ") + (with-current-buffer "NickServ" + (erc-scenarios-common-say "REGISTER changeme") + (funcall expect 5 "Account created") + (funcall expect 1 "You're now logged in as tester")) + + (with-current-buffer "foonet" + (erc-cmd-QUIT "") + (erc-d-t-wait-for 4 (not (erc-server-process-alive))) + (funcall expect 5 "ERC finished")))) + + (with-current-buffer "foonet" + (erc-cmd-RECONNECT)) + + (erc-d-t-wait-for 10 "Nick request rejection prevents reassociation (good)" + (get-buffer "foonet/tester`")) + + (ert-info ("Ask NickServ to change nick") + (with-current-buffer "foonet/tester`" + (funcall expect 3 "already in use") + (funcall expect 3 "debug mode") + (erc-cmd-QUERY "NickServ")) + + (erc-d-t-wait-for 1 "Dead NickServ query buffer renamed, now qualified" + (get-buffer "NickServ@foonet/tester")) + + (with-current-buffer "NickServ@foonet/tester`" ; new one + (erc-scenarios-common-say "IDENTIFY tester changeme") + (funcall expect 5 "You're now logged in as tester") + (ert-info ("Original buffer found, reused") + (erc-d-t-wait-for 2 (equal (buffer-name) "NickServ"))))) + + (ert-info ("Ours is the only NickServ buffer that remains") + (should-not (cdr (erc-scenarios-common-buflist "NickServ")))) + + (ert-info ("Visible network ID truncated to one component") + (should (not (get-buffer "foonet/tester`"))) + (should (not (get-buffer "foonet/tester"))) + (should (get-buffer "foonet"))))) + +;; A less common variant is when your bouncer switches to an alternate +;; nick while you're disconnected, and upon reconnecting, you get +;; a new nick. + +(ert-deftest erc-scenarios-base-association-nick-bumped-mandated-renick () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bumped") + (dumb-server (erc-d-run "localhost" t 'foisted 'refoisted)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.5) + (erc-server-flood-margin 30)) + + (ert-info ("Connect to foonet with nick tester") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (erc-scenarios-common-assert-initial-buf-name nil port) + (erc-d-t-wait-for 5 (eq erc-network 'foonet)))) + + (ert-info ("Greet bob and quit") + (with-current-buffer "foonet" + (funcall expect 3 "debug mode") + + (erc-cmd-QUERY "bob") + (with-current-buffer "bob" + (erc-scenarios-common-say "hi") + (funcall expect 5 "hola") + (funcall expect 1 "how r u?")) + + (with-current-buffer "foonet" + (erc-cmd-QUIT "") + (erc-d-t-wait-for 4 (not (erc-server-process-alive))) + (funcall expect 5 "ERC finished")))) + + ;; Since we use reconnect, a new buffer won't be created + ;; TODO add variant with clean `erc' invocation + (with-current-buffer "foonet" + (erc-cmd-RECONNECT)) + + (ert-info ("Server-initiated renick") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet/dummy")) + (should-not (get-buffer "foonet/tester")) + (funcall expect 15 "debug mode")) + + (erc-d-t-wait-for 1 "Old query renamed, now qualified" + (get-buffer "bob@foonet/tester")) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "bob@foonet/dummy")) + (erc-cmd-NICK "tester") + (ert-info ("Buffers combined") + (erc-d-t-wait-for 2 (equal (buffer-name) "bob"))))) + + (with-current-buffer "foonet" + (funcall expect 5 "You're now logged in as tester")) + + (ert-info ("Ours is the only bob buffer that remains") + (should-not (cdr (erc-scenarios-common-buflist "bob")))) + + (ert-info ("Visible network ID truncated to one component") + (should (not (get-buffer "foonet/dummy"))) + (should (get-buffer "foonet"))))) + +;;; erc-scenarios-base-association-nick.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-association-samenet.el b/test/lisp/erc/erc-scenarios-base-association-samenet.el new file mode 100644 index 00000000000..b7c7079df34 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-association-samenet.el @@ -0,0 +1,144 @@ +;;; erc-scenarios-base-association-samenet.el --- assoc samenet scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(declare-function erc-network-name "erc-networks") +(declare-function erc-network "erc-networks") +(defvar erc-autojoin-channels-alist) +(defvar erc-network) + +;; One network, two simultaneous connections, no IDs. +;; Reassociates on reconnect with and without server buffer. + +(defun erc-scenarios-common--base-association-samenet (after) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/samenet") + (dumb-server (erc-d-run "localhost" t 'tester 'chester 'tester2)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.5) + (erc-server-flood-margin 30)) + + (ert-info ("Connect to foonet with nick tester") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (erc-scenarios-common-assert-initial-buf-name nil port) + (erc-d-t-wait-for 5 (eq erc-network 'foonet)))) + + (ert-info ("Connect to foonet with nick chester") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "chester" + :password "changeme" + :full-name "chester") + (erc-scenarios-common-assert-initial-buf-name nil port))) + + (erc-d-t-wait-for 3 "Dialed Buflist is Empty" + (not (erc-scenarios-common-buflist "127.0.0.1"))) + + (with-current-buffer "foonet/tester" + (funcall expect 3 "debug mode") + (erc-cmd-JOIN "#chan")) + + (erc-d-t-wait-for 10 (get-buffer "#chan@foonet/tester")) + (with-current-buffer "foonet/chester" (funcall expect 3 "debug mode")) + (erc-d-t-wait-for 10 (get-buffer "#chan@foonet/chester")) + + (ert-info ("Nick tester sees other nick chester in channel") + (with-current-buffer "#chan@foonet/tester" + (funcall expect 5 "chester") + (funcall expect 5 "find the forester") + (erc-cmd-QUIT ""))) + + (ert-info ("Nick chester sees other nick tester in same channel") + (with-current-buffer "#chan@foonet/chester" + (funcall expect 5 "tester") + (funcall expect 5 "find the forester"))) + + (funcall after expect))) + +(ert-deftest erc-scenarios-base-association-samenet--reconnect-one () + :tags '(:expensive-test) + (erc-scenarios-common--base-association-samenet + (lambda (expect) + + (ert-info ("Connection tester reconnects") + (with-current-buffer "foonet/tester" + (erc-d-t-wait-for 10 (not (erc-server-process-alive))) + (funcall expect 10 "*** ERC finished") + (erc-cmd-RECONNECT) + (funcall expect 5 "debug mode"))) + + (ert-info ("Reassociated to same channel") + (with-current-buffer "#chan@foonet/tester" + (funcall expect 5 "chester") + (funcall expect 5 "welcome again") + (erc-cmd-QUIT ""))) + + (with-current-buffer "#chan@foonet/chester" + (funcall expect 5 "tester") + (funcall expect 5 "welcome again") + (funcall expect 5 "welcome again") + (erc-cmd-QUIT ""))))) + +(ert-deftest erc-scenarios-base-association-samenet--new-buffer () + :tags '(:expensive-test) + (erc-scenarios-common--base-association-samenet + (lambda (expect) + + (ert-info ("Tester kills buffer and connects from scratch") + + (let (port) + (with-current-buffer "foonet/tester" + (erc-d-t-wait-for 10 (not (erc-server-process-alive))) + (funcall expect 10 "*** ERC finished") + (setq port erc-session-port) + (kill-buffer)) + + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + + (erc-d-t-wait-for 5 (eq erc-network 'foonet))))) + + (with-current-buffer "foonet/tester" (funcall expect 3 "debug mode")) + + (ert-info ("Reassociated to same channel") + (with-current-buffer "#chan@foonet/tester" + (funcall expect 5 "chester") + (funcall expect 5 "welcome again") + (erc-cmd-QUIT ""))) + + (with-current-buffer "#chan@foonet/chester" + (funcall expect 5 "tester") + (funcall expect 5 "welcome again") + (funcall expect 5 "welcome again") + (erc-cmd-QUIT ""))))) + +;;; erc-scenarios-base-association-samenet.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-association.el b/test/lisp/erc/erc-scenarios-base-association.el new file mode 100644 index 00000000000..83e5101e3ad --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-association.el @@ -0,0 +1,192 @@ +;;; erc-scenarios-base-association.el --- base assoc scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(declare-function erc-network-name "erc-networks") +(declare-function erc-network "erc-networks") +(defvar erc-autojoin-channels-alist) +(defvar erc-network) + +;; Two networks, same channel name, no confusion (no bouncer). Some +;; of this draws from bug#47522 "foil-in-server-buf". It shows that +;; disambiguation-related changes added for bug#48598 are not specific +;; to bouncers. + +(defun erc-scenarios-common--base-association-multi-net (second-join) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/multi-net") + (erc-server-flood-penalty 0.1) + (dumb-server-foonet-buffer (get-buffer-create "*server-foonet*")) + (dumb-server-barnet-buffer (get-buffer-create "*server-barnet*")) + (dumb-server-foonet (erc-d-run "localhost" t "server-foonet" 'foonet)) + (dumb-server-barnet (erc-d-run "localhost" t "server-barnet" 'barnet)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet, join #chan") + (with-current-buffer + (erc :server "127.0.0.1" + :port (process-contact dumb-server-foonet :service) + :nick "tester" + :password "changeme" + :full-name "tester") + (funcall expect 3 "debug mode") + (erc-cmd-JOIN "#chan"))) + + (erc-d-t-wait-for 2 (get-buffer "#chan")) + + (ert-info ("Connect to barnet, join #chan") + (with-current-buffer + (erc :server "127.0.0.1" + :port (process-contact dumb-server-barnet :service) + :nick "tester" + :password "changeme" + :full-name "tester") + (funcall expect 5 "debug mode"))) + + (funcall second-join) + + (erc-d-t-wait-for 3 (get-buffer "#chan@barnet")) + + (erc-d-t-wait-for 2 "Buf #chan now #chan@foonet" + (and (get-buffer "#chan@foonet") (not (get-buffer "#chan")))) + + (ert-info ("All #chan@foonet output consumed") + (with-current-buffer "#chan@foonet" + (funcall expect 3 "bob") + (funcall expect 3 "was created on") + (funcall expect 3 "prosperous"))) + + (ert-info ("All #chan@barnet output consumed") + (with-current-buffer "#chan@barnet" + (funcall expect 3 "mike") + (funcall expect 3 "was created on") + (funcall expect 20 "ingenuous"))))) + +(ert-deftest erc-scenarios-base-association-multi-net--baseline () + :tags '(:expensive-test) + (erc-scenarios-common--base-association-multi-net + (lambda () (with-current-buffer "barnet" (erc-cmd-JOIN "#chan"))))) + +;; The /join command only targets the current buffer's process. This +;; recasts scenario bug#48598 "ambiguous-join" (which was based on +;; bug#47522) to show that issuing superfluous /join commands +;; (apparently fairly common) is benign. + +(ert-deftest erc-scenarios-base-association-multi-net--ambiguous-join () + :tags '(:expensive-test) + (erc-scenarios-common--base-association-multi-net + (lambda () + (ert-info ("Nonsensical JOIN attempts silently dropped.") + (with-current-buffer "foonet" (erc-cmd-JOIN "#chan")) + (sit-for 0.1) + (with-current-buffer "#chan" (erc-cmd-JOIN "#chan")) + (sit-for 0.1) + (erc-d-t-wait-for 2 (get-buffer "#chan")) + (erc-d-t-wait-for 1 "Only one #chan buffer exists" + (should (equal (erc-scenarios-common-buflist "#chan") + (list (get-buffer "#chan"))))) + (with-current-buffer "*server-barnet*" + (erc-d-t-absent-for 0.1 "JOIN")) + (with-current-buffer "barnet" (erc-cmd-JOIN "#chan")))))) + +;; Playback for same channel on two networks routed correctly. +;; Originally from Bug#48598: 28.0.50; buffer-naming collisions +;; involving bouncers in ERC. + +(ert-deftest erc-scenarios-base-association-bouncer-history () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bouncer-history") + (erc-d-t-cleanup-sleep-secs 1) + (dumb-server (erc-d-run "localhost" t 'foonet 'barnet)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.5) + (expect (erc-d-t-make-expecter)) + erc-autojoin-channels-alist + erc-server-buffer-foo erc-server-process-foo + erc-server-buffer-bar erc-server-process-bar) + + (ert-info ("Connect to foonet") + (with-current-buffer + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester")) + (setq erc-server-process-foo erc-server-process) + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (funcall expect 5 "foonet"))) + + (erc-d-t-wait-for 5 (get-buffer "#chan")) + + (ert-info ("Connect to barnet") + (with-current-buffer + (setq erc-server-buffer-bar (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester")) + (setq erc-server-process-bar erc-server-process) + (erc-d-t-wait-for 5 "Temporary name assigned" + (string= (buffer-name) (format "127.0.0.1:%d" port))) + (funcall expect 5 "barnet"))) + + (ert-info ("Server buffers are unique") + (should-not (eq erc-server-buffer-foo erc-server-buffer-bar))) + + (ert-info ("Networks correctly determined and adopted as buffer names") + (with-current-buffer erc-server-buffer-foo + (erc-d-t-wait-for 3 "network name foonet becomes buffer name" + (and (eq (erc-network) 'foonet) (string= (buffer-name) "foonet")))) + (with-current-buffer erc-server-buffer-bar + (erc-d-t-wait-for 3 "network name barnet becomes buffer name" + (and (eq (erc-network) 'barnet) (string= (buffer-name) "barnet"))))) + + (erc-d-t-wait-for 5 (get-buffer "#chan@barnet")) + + (ert-info ("Two channel buffers created, original #chan renamed") + (should (= 4 (length (erc-buffer-list)))) + (should (equal (list (get-buffer "#chan@barnet") + (get-buffer "#chan@foonet")) + (erc-scenarios-common-buflist "#chan")))) + + (ert-info ("#chan@foonet is exclusive, no cross-contamination") + (with-current-buffer "#chan@foonet" + (erc-d-t-search-for 1 "<bob>") + (erc-d-t-absent-for 0.1 "<joe>") + (should (eq erc-server-process erc-server-process-foo)))) + + (ert-info ("#chan@barnet is exclusive, no cross-contamination") + (with-current-buffer "#chan@barnet" + (erc-d-t-search-for 1 "<joe>") + (erc-d-t-absent-for 0.1 "<bob>") + (should (eq erc-server-process erc-server-process-bar)))) + + (ert-info ("All output sent") + (with-current-buffer "#chan@foonet" + (erc-d-t-search-for 10 "please your lordship")) + (with-current-buffer "#chan@barnet" + (erc-d-t-search-for 10 "I'll bid adieu"))))) + +;;; erc-scenarios-base-association.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el b/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el new file mode 100644 index 00000000000..474739d01be --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-compat-rename-bouncer.el @@ -0,0 +1,171 @@ +;;; erc-scenarios-compat-rename-bouncer.el --- compat-rename scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +;; Ensure deprecated option still respected when old default value +;; explicitly set ("respected" in the sense of having names reflect +;; dialed TCP endpoints with possible uniquifiers but without any of +;; the old issues, pre-bug#48598). + +(defun erc-scenarios-common--base-compat-no-rename-bouncer (dialogs auto more) + (erc-scenarios-common-with-cleanup + ;; These actually *are* (assigned-)network-id related because + ;; our kludge assigns one after the fact. + ((erc-scenarios-common-dialog "base/netid/bouncer") + (erc-d-t-cleanup-sleep-secs 1) + (erc-server-flood-penalty 0.1) + (dumb-server (apply #'erc-d-run "localhost" t dialogs)) + (port (process-contact dumb-server :service)) + (chan-buf-foo (format "#chan@127.0.0.1:%d" port)) + (chan-buf-bar (format "#chan@127.0.0.1:%d<2>" port)) + (expect (erc-d-t-make-expecter)) + (erc-server-auto-reconnect auto) + erc-server-buffer-foo erc-server-process-foo + erc-server-buffer-bar erc-server-process-bar) + + (ert-info ("Connect to foonet") + (with-current-buffer + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester" + :id nil)) + (setq erc-server-process-foo erc-server-process) + (erc-d-t-wait-for 3 (eq (erc-network) 'foonet)) + (erc-d-t-wait-for 3 "Final buffer name determined" + (string= (buffer-name) (format "127.0.0.1:%d" port))) + (funcall expect 5 "foonet"))) + + (ert-info ("Join #chan@foonet") + (with-current-buffer erc-server-buffer-foo (erc-cmd-JOIN "#chan")) + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 5 "<alice>"))) + + (ert-info ("Connect to barnet") + (with-current-buffer + (setq erc-server-buffer-bar (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester" + :id nil)) + (setq erc-server-process-bar erc-server-process) + (erc-d-t-wait-for 3 (eq (erc-network) 'barnet)) + (erc-d-t-wait-for 3 "Final buffer name determined" + (string= (buffer-name) (format "127.0.0.1:%d<2>" port))) + (funcall expect 5 "barnet"))) + + (ert-info ("Server buffers are unique, no names based on IPs") + (should-not (eq erc-server-buffer-foo erc-server-buffer-bar)) + (should (equal (erc-scenarios-common-buflist "127.0.0.1") + (list (get-buffer (format "127.0.0.1:%d<2>" port)) + (get-buffer (format "127.0.0.1:%d" port)))))) + + (ert-info ("Join #chan@barnet") + (with-current-buffer erc-server-buffer-bar (erc-cmd-JOIN "#chan"))) + + (erc-d-t-wait-for 5 "Exactly 2 #chan-prefixed buffers exist" + (equal (list (get-buffer chan-buf-bar) + (get-buffer chan-buf-foo)) + (erc-scenarios-common-buflist "#chan"))) + + (ert-info ("#chan@127.0.0.1:$port is exclusive to foonet") + (with-current-buffer chan-buf-foo + (erc-d-t-search-for 1 "<bob>") + (erc-d-t-absent-for 0.1 "<joe>") + (should (eq erc-server-process erc-server-process-foo)) + (erc-d-t-search-for 10 "ape is dead") + (erc-d-t-wait-for 5 (not (erc-server-process-alive))))) + + (ert-info ("#chan@127.0.0.1:$port<2> is exclusive to barnet") + (with-current-buffer chan-buf-bar + (erc-d-t-search-for 1 "<joe>") + (erc-d-t-absent-for 0.1 "<bob>") + (should (eq erc-server-process erc-server-process-bar)) + (erc-d-t-search-for 10 "keeps you from dishonour") + (erc-d-t-wait-for 5 (not (erc-server-process-alive))))) + + (when more (funcall more)))) + +(ert-deftest erc-scenarios-base-compat-no-rename-bouncer--basic () + :tags '(:expensive-test) + (with-suppressed-warnings ((obsolete erc-rename-buffers)) + (let (erc-rename-buffers) + (erc-scenarios-common--base-compat-no-rename-bouncer + '(foonet barnet) nil nil)))) + +(ert-deftest erc-scenarios-base-compat-no-rename-bouncer--reconnect () + :tags '(:expensive-test) + (let ((erc-d-tmpl-vars '((token . (group (| "barnet" "foonet"))))) + (erc-d-match-handlers + (list :pass #'erc-scenarios-common--clash-rename-pass-handler)) + (dialogs '(foonet-drop barnet-drop stub-again stub-again + foonet-again barnet-again)) + (after + (lambda () + (pcase-let* ((`(,barnet ,foonet) + (erc-scenarios-common-buflist "127.0.0.1")) + (port (process-contact (with-current-buffer foonet + erc-server-process) + :service))) + + (ert-info ("Sanity check: barnet retains uniquifying suffix") + (should (string-suffix-p "<2>" (buffer-name barnet)))) + + ;; Simulate disconnection and `erc-server-auto-reconnect' + (ert-info ("Reconnect to foonet and barnet back-to-back") + (with-current-buffer foonet + (erc-d-t-wait-for 5 (erc-server-process-alive))) + (with-current-buffer barnet + (erc-d-t-wait-for 5 (erc-server-process-alive)))) + + (ert-info ("#chan@127.0.0.1:<port> is exclusive to foonet") + (with-current-buffer (format "#chan@127.0.0.1:%d" port) + (erc-d-t-search-for 1 "<alice>") + (erc-d-t-absent-for 0.1 "<joe>") + (erc-d-t-search-for 10 "please your lordship"))) + + (ert-info ("#chan@barnet is exclusive to barnet") + (with-current-buffer (format "#chan@127.0.0.1:%d<2>" port) + (erc-d-t-search-for 1 "<joe>") + (erc-d-t-absent-for 0.1 "<bob>") + (erc-d-t-search-for 1 "much in private"))) + + ;; Ordering deterministic here even though not so for reconnect + (should (equal (list barnet foonet) + (erc-scenarios-common-buflist "127.0.0.1"))) + (should (equal (list + (get-buffer (format "#chan@127.0.0.1:%d<2>" port)) + (get-buffer (format "#chan@127.0.0.1:%d" port))) + (erc-scenarios-common-buflist "#chan"))))))) + + (with-suppressed-warnings ((obsolete erc-rename-buffers)) + (let (erc-rename-buffers) + (erc-scenarios-common--base-compat-no-rename-bouncer dialogs + 'auto after))))) + +;;; erc-scenarios-compat-rename-bouncer.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-misc-regressions.el b/test/lisp/erc/erc-scenarios-base-misc-regressions.el new file mode 100644 index 00000000000..8f5700df14b --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-misc-regressions.el @@ -0,0 +1,126 @@ +;;; erc-scenarios-base-misc-regressions.el --- misc regressions scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +(defun erc-scenarios--rebuffed-gapless-pass-handler (dialog exchange) + (when (eq (erc-d-dialog-name dialog) 'pass-stub) + (let* ((match (erc-d-exchange-match exchange 1)) + (sym (if (string= match "foonet") 'foonet 'barnet))) + (should (member match (list "foonet" "barnet"))) + (erc-d-load-replacement-dialog dialog sym 1)))) + +(ert-deftest erc-scenarios-base-gapless-connect () + "Back-to-back entry-point invocations happen successfully. +Originally from scenario rebuffed/gapless as explained in Bug#48598: +28.0.50; buffer-naming collisions involving bouncers in ERC." + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/gapless-connect") + (erc-server-flood-penalty 0.1) + (erc-server-flood-penalty erc-server-flood-penalty) + (erc-d-tmpl-vars '((token . (group (| "barnet" "foonet"))))) + (erc-d-match-handlers + (list :pass #'erc-scenarios--rebuffed-gapless-pass-handler)) + (dumb-server (erc-d-run "localhost" t + 'pass-stub 'pass-stub 'barnet 'foonet)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + erc-autojoin-channels-alist + erc-server-buffer-foo + erc-server-buffer-bar) + + (ert-info ("Connect twice to same endpoint without pausing") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester") + erc-server-buffer-bar (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester"))) + + (ert-info ("Returned server buffers are unique") + (should-not (eq erc-server-buffer-foo erc-server-buffer-bar))) + + (ert-info ("Both connections still alive") + (should (get-process (format "erc-127.0.0.1-%d" port))) + (should (get-process (format "erc-127.0.0.1-%d<1>" port)))) + + (with-current-buffer erc-server-buffer-bar + (funcall expect 2 "marked as being away")) + + (with-current-buffer (erc-d-t-wait-for 20 (get-buffer "#bar")) + (funcall expect 10 "was created on") + (funcall expect 2 "his second fit")) + + (with-current-buffer (erc-d-t-wait-for 20 (get-buffer "#foo")) + (funcall expect 10 "was created on") + (funcall expect 2 "no use of him")))) + +;; This defends against a regression in `erc-server-PRIVMSG' caused by +;; the removal of `erc-auto-query'. When an active channel buffer is +;; killed off and PRIVMSGs arrive targeting it, the buffer should be +;; recreated. See elsewhere for NOTICE logic, which is more complex. + +(ert-deftest erc-scenarios-base-channel-buffer-revival () + :tags '(:expensive-test) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/channel-buffer-revival") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (ert-info ("Server buffer is unique and temp name is absent") + (erc-d-t-wait-for 1 (get-buffer "FooNet")) + (should-not (erc-scenarios-common-buflist "127.0.0.1")) + (with-current-buffer erc-server-buffer-foo + (erc-cmd-JOIN "#chan"))) + + (ert-info ("Channel buffer #chan alive and well") + (with-current-buffer (erc-d-t-wait-for 8 (get-buffer "#chan")) + (erc-d-t-search-for 10 "Our queen and all her elves") + (kill-buffer))) + + (should-not (get-buffer "#chan")) + + (ert-info ("Channel buffer #chan revived") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (erc-d-t-search-for 10 "and be prosperous"))))) + +;;; erc-scenarios-base-misc-regressions.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-netid-bouncer-id.el b/test/lisp/erc/erc-scenarios-base-netid-bouncer-id.el new file mode 100644 index 00000000000..6c6568cad68 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-netid-bouncer-id.el @@ -0,0 +1,34 @@ +;;; erc-scenarios-base-netid-bouncer-id.el --- net-id bouncer ID scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-netid-bouncer--id-foo () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer '(:foo-id t) 'foonet 'barnet)) + +(ert-deftest erc-scenarios-base-netid-bouncer--id-bar () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer '(:bar-id t) 'foonet 'barnet)) + +;;; erc-scenarios-base-netid-bouncer-id.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-base.el b/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-base.el new file mode 100644 index 00000000000..f48e1ef3940 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-base.el @@ -0,0 +1,30 @@ +;;; erc-scenarios-base-netid-bouncer-recon-base.el --- net-id base scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-netid-bouncer--recon-base () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer--reconnect nil nil)) + +;;; erc-scenarios-base-netid-bouncer-recon-base.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-both.el b/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-both.el new file mode 100644 index 00000000000..2f58c3269e3 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-both.el @@ -0,0 +1,32 @@ +;;; erc-scenarios-base-netid-bouncer-recon-both.el --- net-id both scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-scenarios-common) + +(ert-deftest erc-scenarios-base-netid-bouncer--recon-both () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer--reconnect 'foo-id 'bar-id)) + +;;; erc-scenarios-base-netid-bouncer-recon-both.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-id.el b/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-id.el new file mode 100644 index 00000000000..72510809ab4 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-netid-bouncer-recon-id.el @@ -0,0 +1,35 @@ +;;; erc-scenarios-base-netid-bouncer-recon-id.el --- recon ID scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-netid-bouncer--reconnect-id-foo () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer--reconnect 'foo-id nil)) + +(ert-deftest erc-scenarios-base-netid-bouncer--reconnect-id-bar () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer--reconnect nil 'bar-id)) + + +;;; erc-scenarios-base-netid-bouncer-recon-id.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-netid-bouncer.el b/test/lisp/erc/erc-scenarios-base-netid-bouncer.el new file mode 100644 index 00000000000..d171e1f9f91 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-netid-bouncer.el @@ -0,0 +1,35 @@ +;;; erc-scenarios-base-netid-bouncer.el --- net-id bouncer scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-netid-bouncer--base () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer () 'foonet 'barnet)) + +(ert-deftest erc-scenarios-base-netid-bouncer--both () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-bouncer '(:foo-id t :bar-id t) + 'foonet 'barnet)) + +;;; erc-scenarios-base-netid-bouncer.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-netid-samenet.el b/test/lisp/erc/erc-scenarios-base-netid-samenet.el new file mode 100644 index 00000000000..248144d6f9b --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-netid-samenet.el @@ -0,0 +1,147 @@ +;;; erc-scenarios-base-network-id-samenet.el --- netid-id samenet scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +(cl-defun erc-scenarios-common--base-network-id-same-network + ((&key nick id server chan + &aux (nick-a nick) (id-a id) (serv-buf-a server) (chan-buf-a chan)) + (&key nick id server chan + &aux (nick-b nick) (id-b id) (serv-buf-b server) (chan-buf-b chan))) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/netid/samenet") + (dumb-server (erc-d-run "localhost" t 'tester 'chester)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.1) + (erc-server-flood-margin 30) + erc-serv-buf-a erc-serv-buf-b) + + (ert-info ("Connect to foonet with nick tester") + (with-current-buffer + (setq erc-serv-buf-a (erc :server "127.0.0.1" + :port port + :nick nick-a + :password "changeme" + :full-name nick-a + :id id-a)) + (erc-scenarios-common-assert-initial-buf-name id-a port) + (erc-d-t-wait-for 5 (eq erc-network 'foonet)))) + + (ert-info ("Connect to foonet with nick chester") + (with-current-buffer + (setq erc-serv-buf-b (erc :server "127.0.0.1" + :port port + :nick nick-b + :password "changeme" + :full-name nick-b + :id id-b)) + (erc-scenarios-common-assert-initial-buf-name id-b port))) + + (erc-d-t-wait-for 3 (not (erc-scenarios-common-buflist "127.0.0.1"))) + + (with-current-buffer erc-serv-buf-a + (should (string= (buffer-name) serv-buf-a)) + (funcall expect 8 "debug mode") + (erc-cmd-JOIN "#chan")) + + (with-current-buffer erc-serv-buf-b + (should (string= (buffer-name) serv-buf-b)) + (funcall expect 8 "debug mode") + (erc-cmd-JOIN "#chan")) + + (erc-d-t-wait-for 10 (get-buffer chan-buf-a)) + (erc-d-t-wait-for 10 (get-buffer chan-buf-b)) + + (ert-info ("Greets other nick in same channel") + (with-current-buffer chan-buf-a + (funcall expect 5 "chester") + (funcall expect 5 "find the forester") + (erc-cmd-MSG "#chan chester: hi"))) + + (ert-info ("Sees other nick in same channel") + (with-current-buffer chan-buf-b + (funcall expect 5 "tester") + (funcall expect 10 "<tester> chester: hi") + (funcall expect 5 "This was lofty") + (erc-cmd-MSG "#chan hi tester"))) + + (with-current-buffer chan-buf-a + (funcall expect 5 "To employ you towards") + (erc-cmd-QUIT "")) + + (with-current-buffer chan-buf-b + (funcall expect 5 "To employ you towards") + (erc-cmd-QUIT "")))) + +(ert-deftest erc-scenarios-base-network-id-same-network--two-ids () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-same-network + (list :nick "tester" + :id 'tester/foonet + :server "tester/foonet" + :chan "#chan@tester/foonet") + (list :nick "chester" + :id 'chester/foonet + :server "chester/foonet" + :chan "#chan@chester/foonet"))) + +(ert-deftest erc-scenarios-base-network-id-same-network--one-id-tester () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-same-network + (list :nick "tester" + :id 'tester/foonet + :server "tester/foonet" + :chan "#chan@tester/foonet") + (list :nick "chester" + :id nil + :server "foonet" + :chan "#chan@foonet"))) + +(ert-deftest erc-scenarios-base-network-id-same-network--one-id-chester () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-same-network + (list :nick "tester" + :id nil + :server "foonet" + :chan "#chan@foonet") + (list :nick "chester" + :id 'chester/foonet + :server "chester/foonet" + :chan "#chan@chester/foonet"))) + +(ert-deftest erc-scenarios-base-network-id-same-network--no-ids () + :tags '(:expensive-test) + (erc-scenarios-common--base-network-id-same-network + (list :nick "tester" + :id nil + :server "foonet/tester" + :chan "#chan@foonet/tester") ; <- note net before nick + (list :nick "chester" + :id nil + :server "foonet/chester" + :chan "#chan@foonet/chester"))) + +;;; erc-scenarios-base-network-id-samenet.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el b/test/lisp/erc/erc-scenarios-base-reconnect.el new file mode 100644 index 00000000000..49298dc5942 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-reconnect.el @@ -0,0 +1,227 @@ +;;; erc-scenarios-base-reconnect.el --- Base-reconnect scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +;; This ensures we only reconnect `erc-server-reconnect-attempts' +;; (rather than infinitely many) times, which can easily happen when +;; tweaking code related to process sentinels in erc-backend.el. + +(ert-deftest erc-scenarios-base-reconnect-timer () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (dumb-server (erc-d-run "localhost" t 'timer 'timer 'timer-last)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-auto-reconnect t) + erc-autojoin-channels-alist + erc-server-buffer) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (ert-info ("Server tries to connect thrice (including initial attempt)") + (with-current-buffer erc-server-buffer + (dotimes (n 3) + (ert-info ((format "Attempt %d" n)) + (funcall expect 3 "Opening connection") + (funcall expect 2 "Password incorrect") + (funcall expect 2 "Connection failed!") + (funcall expect 2 "Re-establishing connection"))) + (ert-info ("Prev attempt was final") + (erc-d-t-absent-for 1 "Opening connection" (point))))) + + (ert-info ("Server buffer is unique and temp name is absent") + (should (equal (list (get-buffer (format "127.0.0.1:%d" port))) + (erc-scenarios-common-buflist "127.0.0.1")))))) + +(defun erc-scenarios-common--base-reconnect-options (test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (dumb-server (erc-d-run "localhost" t 'options 'options-again)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.1) + (erc-server-auto-reconnect t) + erc-autojoin-channels-alist + erc-server-buffer) + + (should (memq 'autojoin erc-modules)) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (funcall expect 10 "debug mode"))) + + (ert-info ("Wait for some output in channels") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 10 "welcome"))) + + (ert-info ("Server buffer shows connection failed") + (with-current-buffer erc-server-buffer + (funcall expect 10 "Connection failed! Re-establishing"))) + + (should (equal erc-autojoin-channels-alist '((FooNet "#chan")))) + + (funcall test) + + ;; A manual /JOIN command tells ERC we're done auto-reconnecting + (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam")) + + (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'" + (not (eq (window-buffer) (get-buffer "#spam")))) + + (ert-info ("Wait for auto reconnect") + (with-current-buffer erc-server-buffer + (funcall expect 10 "still in debug mode"))) + + (ert-info ("Wait for activity to recommence in channels") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 10 "forest of Arden")) + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam")) + (funcall expect 10 "her elves come here anon"))))) + +(ert-deftest erc-scenarios-base-reconnect-options--buffer () + :tags '(:expensive-test) + (should (eq erc-join-buffer 'bury)) + (should-not erc-reconnect-display) + + ;; FooNet (the server buffer) is not switched to because it's + ;; already current (but not shown) when `erc-open' is called. See + ;; related conditional guard towards the end of that function. + + (let ((erc-reconnect-display 'buffer)) + (erc-scenarios-common--base-reconnect-options + (lambda () + (pop-to-buffer-same-window "*Messages*") + + (erc-d-t-ensure-for 1 "Server buffer not shown" + (not (eq (window-buffer) (get-buffer "FooNet")))) + + (erc-d-t-wait-for 5 "Channel #chan shown when autojoined" + (eq (window-buffer) (get-buffer "#chan"))))))) + +(ert-deftest erc-scenarios-base-reconnect-options--default () + :tags '(:expensive-test) + (should (eq erc-join-buffer 'bury)) + (should-not erc-reconnect-display) + + (erc-scenarios-common--base-reconnect-options + + (lambda () + (pop-to-buffer-same-window "*Messages*") + + (erc-d-t-ensure-for 1 "Server buffer not shown" + (not (eq (window-buffer) (get-buffer "FooNet")))) + + (erc-d-t-ensure-for 3 "Channel #chan not shown" + (not (eq (window-buffer) (get-buffer "#chan")))) + + (eq (window-buffer) (messages-buffer))))) + +;; Upon reconnecting, playback for channel and target buffers is +;; routed correctly. Autojoin is irrelevant here, but for the +;; skeptical, see `erc-scenarios-common--join-network-id', which +;; overlaps with this and includes spurious JOINs ignored by the +;; server. + +(ert-deftest erc-scenarios-base-association-reconnect-playback () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/reconplay") + (erc-server-flood-penalty 0.1) + (erc-server-flood-margin 30) + (dumb-server (erc-d-run "localhost" t 'foonet 'again)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (ert-info ("Setup") + + (ert-info ("Server buffer is unique and temp name is absent") + (erc-d-t-wait-for 3 (get-buffer "foonet")) + (should-not (erc-scenarios-common-buflist "127.0.0.1"))) + + (ert-info ("Channel buffer #chan playback received") + (with-current-buffer (erc-d-t-wait-for 8 (get-buffer "#chan")) + (funcall expect 10 "But purgatory"))) + + (ert-info ("Ask for help from services or bouncer bot") + (with-current-buffer erc-server-buffer-foo + (erc-cmd-MSG "*status help"))) + + (ert-info ("Help received") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "*status")) + (funcall expect 10 "Rehash"))) + + (ert-info ("#chan convo done") + (with-current-buffer "#chan" + (funcall expect 10 "most egregious indignity")))) + + ;; KLUDGE (see note above test) + (should erc-autojoin-channels-alist) + (setq erc-autojoin-channels-alist nil) + + (with-current-buffer erc-server-buffer-foo + (erc-cmd-QUIT "") + (erc-d-t-wait-for 4 (not (erc-server-process-alive))) + (erc-cmd-RECONNECT)) + + (ert-info ("Channel buffer found and associated") + (with-current-buffer "#chan" + (funcall expect 10 "Wilt thou rest damned"))) + + (ert-info ("Help buffer found and associated") + (with-current-buffer "*status" + (erc-scenarios-common-say "help") + (funcall expect 10 "Restart ZNC"))) + + (ert-info ("#chan convo done") + (with-current-buffer "#chan" + (funcall expect 10 "here comes the lady"))))) + +;;; erc-scenarios-base-reconnect.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-renick.el b/test/lisp/erc/erc-scenarios-base-renick.el new file mode 100644 index 00000000000..bf27f61b3fc --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-renick.el @@ -0,0 +1,305 @@ +;;; erc-scenarios-base-renick.el --- Re-nicking scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +;; The server changes your nick just after registration. + +(ert-deftest erc-scenarios-base-renick-self-auto () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/renick/self") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'auto)) + (port (process-contact dumb-server :service)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "foonet")) + (erc-d-t-search-for 10 "Your new nickname is dummy")) + + (ert-info ("Joined by bouncer to #foo, own nick present") + (with-current-buffer (erc-d-t-wait-for 1 (get-buffer "#foo")) + (erc-d-t-search-for 10 "dummy") + (erc-d-t-search-for 10 "On Thursday"))))) + +;; You change your nickname manually in a server buffer; a message is +;; printed in channel buffers. + +(ert-deftest erc-scenarios-base-renick-self-manual () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/renick/self") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'manual)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (erc-d-t-wait-for 3 (get-buffer "foonet")) + + (ert-info ("Joined by bouncer to #foo, own nick present") + (with-current-buffer (erc-d-t-wait-for 1 (get-buffer "#foo")) + (funcall expect 5 "tester") + (funcall expect 5 "On Thursday") + (erc-with-server-buffer (erc-cmd-NICK "dummy")) + (funcall expect 5 "Your new nickname is dummy") + (funcall expect 5 "<bob> dummy: Hi") + ;; Regression in which changing a nick would trigger #foo@foonet + (erc-d-t-ensure-for 0.4 (equal (buffer-name) "#foo")))))) + +;; You connect to the same network with two different nicks. You +;; manually change the first nick at some point, and buffer names are +;; updated correctly. + +(ert-deftest erc-scenarios-base-renick-self-qualified () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/renick/self") + (dumb-server (erc-d-run "localhost" t 'qual-tester 'qual-chester)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.1) + (erc-server-flood-margin 30) + erc-serv-buf-a erc-serv-buf-b) + + (ert-info ("Connect to foonet with nick tester") + (with-current-buffer + (setq erc-serv-buf-a (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (erc-d-t-wait-for 5 (eq erc-network 'foonet)))) + + (ert-info ("Connect to foonet with nick chester") + (with-current-buffer + (setq erc-serv-buf-b (erc :server "127.0.0.1" + :port port + :nick "chester" + :password "changeme" + :full-name "chester")))) + + (erc-d-t-wait-for 3 "Dialed Buflist is Empty" + (not (erc-scenarios-common-buflist "127.0.0.1"))) + + (with-current-buffer "foonet/tester" + (funcall expect 3 "debug mode") + (erc-cmd-JOIN "#chan")) + + (with-current-buffer "foonet/chester" + (funcall expect 3 "debug mode") + (erc-cmd-JOIN "#chan")) + + (erc-d-t-wait-for 10 (get-buffer "#chan@foonet/tester")) + (erc-d-t-wait-for 10 (get-buffer "#chan@foonet/chester")) + + (ert-info ("Greets other nick in same channel") + (with-current-buffer "#chan@foonet/tester" + (funcall expect 5 "<bob> chester, welcome!") + (erc-cmd-NICK "dummy") + (funcall expect 5 "Your new nickname is dummy") + (funcall expect 5 "find the forester") + (erc-d-t-wait-for 5 (string= (buffer-name) "#chan@foonet/dummy")))) + + (ert-info ("Renick propagated throughout all buffers of process") + (should-not (get-buffer "#chan@foonet/tester")) + (should-not (get-buffer "foonet/tester")) + (should (get-buffer "foonet/dummy"))))) + +;; When a channel user changes their nick, any query buffers for them +;; are updated. + +(ert-deftest erc-scenarios-base-renick-queries-solo () + :tags '(:expensive-test) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/renick/queries") + (erc-server-flood-penalty 0.1) + (erc-server-flood-margin 20) + (dumb-server (erc-d-run "localhost" t 'solo)) + (port (process-contact dumb-server :service)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (erc-d-t-wait-for 1 (get-buffer "foonet")) + + (ert-info ("Joined by bouncer to #foo, pal persent") + (with-current-buffer (erc-d-t-wait-for 1 (get-buffer "#foo")) + (erc-d-t-search-for 1 "On Thursday") + (erc-scenarios-common-say "hi"))) + + (erc-d-t-wait-for 10 "Query buffer appears with message from pal" + (get-buffer "Lal")) + + (ert-info ("Chat with pal, who changes name") + (with-current-buffer "Lal" + (erc-d-t-search-for 3 "hello") + (erc-scenarios-common-say "hi") + (erc-d-t-search-for 10 "is now known as Linguo") + (should-not (search-forward "is now known as Linguo" nil t)))) + + (erc-d-t-wait-for 1 (get-buffer "Linguo")) + (should-not (get-buffer "Lal")) + + (with-current-buffer "Linguo" (erc-scenarios-common-say "howdy Linguo")) + + (with-current-buffer "#foo" + (erc-d-t-search-for 10 "is now known as Linguo") + (should-not (search-forward "is now known as Linguo" nil t)) + (erc-cmd-PART "")) + + (with-current-buffer "Linguo" + (erc-d-t-search-for 10 "get along")))) + +;; You share a channel and a query buffer with a user on two different +;; networks (through a proxy). The user changes their nick on both +;; networks at the same time. Query buffers are updated accordingly. + +(ert-deftest erc-scenarios-base-renick-queries-bouncer () + :tags '(:expensive-test) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/renick/queries") + (erc-server-flood-penalty 0.1) + (erc-server-flood-margin 30) + (dumb-server (erc-d-run "localhost" t 'bouncer-foonet 'bouncer-barnet)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + erc-accidental-paste-threshold-seconds + erc-autojoin-channels-alist + erc-server-buffer-foo + erc-server-buffer-bar) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (erc-d-t-wait-for 5 (get-buffer "foonet")) + + (ert-info ("Connect to barnet") + (setq erc-server-buffer-bar (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-bar + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (erc-d-t-wait-for 5 (get-buffer "barnet")) + (should-not (erc-scenarios-common-buflist "127.0.0.1")) + + (ert-info ("Joined by bouncer to #chan@foonet, pal persent") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan@foonet")) + (funcall expect 1 "rando") + (funcall expect 1 "simply misused"))) + + (ert-info ("Joined by bouncer to #chan@barnet, pal persent") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan@barnet")) + (funcall expect 1 "rando") + (funcall expect 2 "come, sir, I am"))) + + (ert-info ("Query buffer exists for rando@foonet") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "rando@foonet")) + (funcall expect 1 "guess not") + (erc-scenarios-common-say "I here"))) + + (ert-info ("Query buffer exists for rando@barnet") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "rando@barnet")) + (funcall expect 2 "rentacop") + (erc-scenarios-common-say "Linda said you were gonna kill me."))) + + (ert-info ("Sync convo for rando@foonet") + (with-current-buffer "rando@foonet" + (funcall expect 1 "u are dumb") + (erc-scenarios-common-say "not so"))) + + (ert-info ("Sync convo for rando@barnet") + (with-current-buffer "rando@barnet" + (funcall expect 3 "I never saw her before") + (erc-scenarios-common-say "You aren't with Wage?"))) + + (erc-d-t-wait-for 3 (get-buffer "frenemy@foonet")) + (erc-d-t-wait-for 3 (get-buffer "frenemy@barnet")) + (should-not (get-buffer "rando@foonet")) + (should-not (get-buffer "rando@barnet")) + + (with-current-buffer "frenemy@foonet" + (funcall expect 1 "now known as") + (funcall expect 1 "doubly so")) + + (with-current-buffer "frenemy@barnet" + (funcall expect 1 "now known as") + (funcall expect 1 "reality picture")) + + (when noninteractive + (with-current-buffer "frenemy@barnet" (kill-buffer)) + (erc-d-t-wait-for 2 (get-buffer "frenemy")) + (should-not (get-buffer "frenemy@foonet"))) + + (with-current-buffer "#chan@foonet" + (funcall expect 10 "is now known as frenemy") + (should-not (search-forward "now known as frenemy" nil t)) ; regression + (funcall expect 10 "words are razors")) + + (with-current-buffer "#chan@barnet" + (funcall expect 10 "is now known as frenemy") + (should-not (search-forward "now known as frenemy" nil t)) + (erc-d-t-search-for 25 "I have lost")))) + +;;; erc-scenarios-base-renick.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el new file mode 100644 index 00000000000..8e7e939d046 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el @@ -0,0 +1,233 @@ +;;; erc-scenarios-base-reuse-buffers.el --- base-reuse-buffers scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +(defun erc-scenarios-common--base-reuse-buffers-server-buffers (&optional more) + "Show that `erc-reuse-buffers' doesn't affect server buffers. +Overlaps some with `clash-of-chans/uniquify'. Adapted from +rebuffed/reuseless, described in Bug#48598: 28.0.50; buffer-naming +collisions involving bouncers in ERC. Run EXTRA." + (erc-scenarios-common-with-cleanup + ((dumb-server (erc-d-run "localhost" t 'foonet 'barnet)) + (port (process-contact dumb-server :service)) + erc-autojoin-channels-alist) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester") + (should (string= (buffer-name) + (format "127.0.0.1:%d/127.0.0.1" port))) + (erc-d-t-search-for 12 "marked as being away"))) + + (ert-info ("Connect to barnet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester") + (should (string= (buffer-name) + (format "127.0.0.1:%d/127.0.0.1<2>" port))) + (erc-d-t-search-for 45 "marked as being away"))) + + (erc-d-t-wait-for 2 (get-buffer (format "127.0.0.1:%d/127.0.0.1" port))) + (erc-d-t-wait-for 2 (get-buffer (format "127.0.0.1:%d/127.0.0.1<2>" port))) + + (ert-info ("Server buffers are unique, no IP-based names") + (should (cdr (erc-scenarios-common-buflist "127.0.0.1")))) + (when more (funcall more port)))) + +;; XXX maybe remove: already covered many times over by other scenarios +(ert-deftest erc-scenarios-base-reuse-buffers-server-buffers--enabled () + :tags '(:expensive-test) + (with-suppressed-warnings ((obsolete erc-reuse-buffers)) + (should erc-reuse-buffers)) + (let ((erc-scenarios-common-dialog "base/reuse-buffers/server")) + (erc-scenarios-common-with-cleanup + ((dumb-server (erc-d-run "localhost" t 'foonet 'barnet)) + (port (process-contact dumb-server :service)) + erc-autojoin-channels-alist) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-search-for 12 "marked as being away"))) + + (ert-info ("Connect to barnet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-search-for 45 "marked as being away"))) + + (erc-d-t-wait-for 2 (get-buffer "foonet")) + (erc-d-t-wait-for 2 (get-buffer "barnet")) + + (ert-info ("Server buffers are unique, no IP-based names") + (should-not (eq (get-buffer "foonet") (get-buffer "barnet"))) + (should-not (erc-scenarios-common-buflist "127.0.0.1")))))) + +;; FIXME no sense in running this twice (JOIN variant includes this) +(ert-deftest erc-scenarios-base-reuse-buffers-server-buffers--disabled () + :tags '(:expensive-test) + (with-suppressed-warnings ((obsolete erc-reuse-buffers)) + (should erc-reuse-buffers) + (let ((erc-scenarios-common-dialog "base/reuse-buffers/server") + erc-reuse-buffers) + (erc-scenarios-common--base-reuse-buffers-server-buffers nil)))) + +;; This also asserts that `erc-cmd-JOIN' is no longer susceptible to a +;; regression introduced in 28.1 (ERC 5.4) that caused phantom target +;; buffers of the form target/server to be created via +;; `switch-to-buffer' ("phantom" because they would go unused"). This +;; would happen (in place of a JOIN being sent out) when a previously +;; used (parted) target buffer existed and `erc-reuse-buffers' was +;; nil. +;; +;; Note: All the `erc-get-channel-user' calls have to do with the fact +;; that `erc-default-target' relies on the ambiguously defined +;; `erc-default-recipients' (meaning it's overloaded in the sense of +;; being used both for retrieving a target name and checking if a +;; channel has been PARTed). While not ideal, `erc-get-channel-user' +;; can (also) be used to detect the latter. + +(defun erc-scenarios-common--base-reuse-buffers-channel-buffers (port) + "The option `erc-reuse-buffers' is still respected when nil. +Adapted from scenario clash-of-chans/uniquify described in Bug#48598: +28.0.50; buffer-naming collisions involving bouncers in ERC." + (let* ((expect (erc-d-t-make-expecter)) + (server-buffer-foo + (get-buffer (format "127.0.0.1:%d/127.0.0.1" port))) + (server-buffer-bar + (get-buffer (format "127.0.0.1:%d/127.0.0.1<2>" port))) + (server-process-foo + (buffer-local-value 'erc-server-process server-buffer-foo)) + (server-process-bar + (buffer-local-value 'erc-server-process server-buffer-bar))) + + (ert-info ("Unique #chan buffers exist") + (erc-d-t-wait-for 3 (get-buffer "#chan/127.0.0.1<2>")) + (erc-d-t-wait-for 3 (get-buffer "#chan/127.0.0.1"))) + + (ert-info ("#chan@foonet is exclusive and not contaminated") + (with-current-buffer "#chan/127.0.0.1" + (funcall expect 1 "<bob>") + (erc-d-t-absent-for 0.1 "<joe>") + (funcall expect 1 "strength to climb") + (should (eq erc-server-process server-process-foo)))) + + (ert-info ("#chan@barnet is exclusive and not contaminated") + (with-current-buffer "#chan/127.0.0.1<2>" + (funcall expect 1 "<joe>") + (erc-d-t-absent-for 0.1 "<bob>") + (funcall expect 1 "the loudest noise") + (should (eq erc-server-process server-process-bar)))) + + (ert-info ("Part #chan@foonet") + (with-current-buffer "#chan/127.0.0.1" + (erc-d-t-search-for 1 "shake my sword") + (erc-cmd-PART "#chan") + (funcall expect 3 "You have left channel #chan") + (erc-cmd-JOIN "#chan"))) + + (ert-info ("Part #chan@barnet") + (with-current-buffer "#chan/127.0.0.1<2>" + (funcall expect 10 "Arm it in rags") + (should (erc-get-channel-user (erc-current-nick))) + (erc-cmd-PART "#chan") + (funcall expect 3 "You have left channel #chan") + (should-not (erc-get-channel-user (erc-current-nick))) + (erc-cmd-JOIN "#chan"))) + + (erc-d-t-wait-for 3 "New unique target buffer for #chan@foonet created" + (get-buffer "#chan/127.0.0.1<3>")) + + (ert-info ("Activity continues in new, <n>-suffixed #chan@foonet buffer") + (with-current-buffer "#chan/127.0.0.1" + (should-not (erc-get-channel-user (erc-current-nick)))) + (with-current-buffer "#chan/127.0.0.1<3>" + (should (erc-get-channel-user (erc-current-nick))) + (funcall expect 2 "You have joined channel #chan") + (funcall expect 2 "#chan was created on") + (funcall expect 2 "<alice>") + (should (eq erc-server-process server-process-foo)) + (erc-d-t-absent-for 0.2 "<joe>"))) + + (sit-for 3) + (erc-d-t-wait-for 5 "New unique target buffer for #chan@barnet created" + (get-buffer "#chan/127.0.0.1<4>")) + + (ert-info ("Activity continues in new, <n>-suffixed #chan@barnet buffer") + (with-current-buffer "#chan/127.0.0.1<2>" + (should-not (erc-get-channel-user (erc-current-nick)))) + (with-current-buffer "#chan/127.0.0.1<4>" + (funcall expect 2 "You have joined channel #chan") + (funcall expect 1 "Users on #chan: @mike joe tester") + (funcall expect 2 "<mike>") + (should (eq erc-server-process server-process-bar)) + (erc-d-t-absent-for 0.2 "<bob>"))) + + (ert-info ("Two new chans created for a total of four") + (let* ((bufs (erc-scenarios-common-buflist "#chan")) + (names (sort (mapcar #'buffer-name bufs) #'string<))) + (should + (equal names (mapcar (lambda (f) (concat "#chan/127.0.0.1" f)) + '("" "<2>" "<3>" "<4>")))))) + + (ert-info ("All output sent") + (with-current-buffer "#chan/127.0.0.1<3>" + (funcall expect 10 "most lively")) + (with-current-buffer "#chan/127.0.0.1<4>" + (funcall expect 10 "soul black"))) + + ;; TODO ensure the exact <N>'s aren't reassigned during killing as + ;; they are when the option is on. + (ert-info ("Buffers are exempt from shortening") + (kill-buffer "#chan/127.0.0.1<4>") + (kill-buffer "#chan/127.0.0.1<3>") + (kill-buffer "#chan/127.0.0.1<2>") + (should-not (get-buffer "#chan")) + (should (get-buffer "#chan/127.0.0.1"))))) + +(ert-deftest erc-scenarios-base-reuse-buffers-channel-buffers--disabled () + :tags '(:expensive-test) + (with-suppressed-warnings ((obsolete erc-reuse-buffers)) + (should erc-reuse-buffers) + (let ((erc-scenarios-common-dialog "base/reuse-buffers/channel") + (erc-server-flood-penalty 0.1) + erc-reuse-buffers) + (erc-scenarios-common--base-reuse-buffers-server-buffers + #'erc-scenarios-common--base-reuse-buffers-channel-buffers)))) + +;;; erc-scenarios-base-reuse-buffers.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-unstable.el b/test/lisp/erc/erc-scenarios-base-unstable.el new file mode 100644 index 00000000000..2313a15842c --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-unstable.el @@ -0,0 +1,134 @@ +;;; erc-scenarios-base-unstable.el --- base unstable scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +;; Not unstable, but stashed here for now + +(ert-deftest erc-scenarios-aux-unix-socket () + :tags '(:expensive-test) + (skip-unless (featurep 'make-network-process '(:family local))) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/renick/self") + (erc-server-flood-penalty 0.1) + (sock (expand-file-name "erc-d.sock" temporary-file-directory)) + (erc-scenarios-common-extra-teardown (lambda () (delete-file sock))) + (erc-server-connect-function + (lambda (n b _ p &rest r) + (apply #'make-network-process + `(:name ,n :buffer ,b :service ,p :family local ,@r)))) + (dumb-server (erc-d-run nil sock 'auto)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "fake" + :port sock + :nick "tester" + :password "foonet:changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "fake:%s" sock))))) + + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "foonet")) + (erc-d-t-search-for 10 "Your new nickname is dummy")) + + (ert-info ("Joined by bouncer to #foo, own nick present") + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "#foo")) + (erc-d-t-search-for 10 "dummy") + (erc-d-t-search-for 10 "On Thursday"))))) + +;; See `erc-networks--rename-server-buffer'. A perceived loss in +;; network connectivity turns out to be a false alarm, but the bouncer +;; has already accepted the second connection + +(defun erc-scenarios--base-aborted-reconnect () + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (erc-d-t-cleanup-sleep-secs 1) + (dumb-server (erc-d-run "localhost" t 'aborted 'aborted-dupe)) + (port (process-contact dumb-server :service)) + erc-autojoin-channels-alist + erc-server-buffer-foo) + + (ert-info ("Connect to foonet") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (ert-info ("Server buffer is unique and temp name is absent") + (erc-d-t-wait-for 10 (get-buffer "FooNet")) + (should-not (erc-scenarios-common-buflist "127.0.0.1")) + (with-current-buffer erc-server-buffer-foo + (erc-cmd-JOIN "#chan"))) + + (ert-info ("Channel buffer #chan alive and well") + (with-current-buffer (erc-d-t-wait-for 4 (get-buffer "#chan")) + (erc-d-t-search-for 10 "welcome"))) + + (ert-info ("Connect to foonet again") + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester")) + (let ((inhibit-message noninteractive)) + (with-current-buffer erc-server-buffer-foo + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-wait-for 5 (not (erc-server-process-alive))) + (erc-d-t-search-for 10 "FooNet still connected")))) + + (ert-info ("Server buffer is unique and temp name is absent") + (should (equal (list (get-buffer "FooNet")) + (erc-scenarios-common-buflist "FooNet"))) + (should (equal (list (get-buffer (format "127.0.0.1:%d" port))) + (erc-scenarios-common-buflist "127.0.0.1")))) + + (ert-info ("Channel buffer #chan still going") + (with-current-buffer "#chan" + (erc-d-t-search-for 10 "and be prosperous"))))) + +(ert-deftest erc-scenarios-base-aborted-reconnect () + :tags '(:unstable) + (let ((tries 3) + (timeout 1) + failed) + (while (condition-case _err + (progn + (erc-scenarios--base-aborted-reconnect) + nil) + (ert-test-failed + (message "Test %S failed; %s attempt(s) remaining." + (ert-test-name (ert-running-test)) + tries) + (sleep-for (cl-incf timeout)) + (not (setq failed (zerop (cl-decf tries))))))) + (should-not failed))) + +;;; erc-scenarios-base-unstable.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-upstream-recon-soju.el b/test/lisp/erc/erc-scenarios-base-upstream-recon-soju.el new file mode 100644 index 00000000000..5a5b363f31d --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-upstream-recon-soju.el @@ -0,0 +1,43 @@ +;;; erc-scenarios-upstream-recon-soju.el --- Upstream soju -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;; Commentary: +;; +;; These concern the loss and recovery of a proxy's IRC-side connection. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-upstream-recon--soju () + :tags '(:expensive-test) + (erc-scenarios-common--upstream-reconnect + (lambda () + (with-current-buffer "foonet" + (erc-d-t-search-for 1 "disconnected from foonet") + (erc-d-t-search-for 1 "connected from foonet")) + (with-current-buffer "barnet" + (erc-d-t-search-for 1 "disconnected from barnet") + (erc-d-t-search-for 1 "connected from barnet"))) + 'soju-foonet + 'soju-barnet)) + +;;; erc-scenarios-upstream-recon-soju.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-upstream-recon-znc.el b/test/lisp/erc/erc-scenarios-base-upstream-recon-znc.el new file mode 100644 index 00000000000..6e9a2172459 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-upstream-recon-znc.el @@ -0,0 +1,43 @@ +;;; erc-scenarios-upstream-recon-znc.el --- Upstream znc -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;; Commentary: +;; +;; These concern the loss and recovery of a proxy's IRC-side connection. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-upstream-recon--znc () + :tags '(:expensive-test) + (erc-scenarios-common--upstream-reconnect + (lambda () + (with-current-buffer "*status@foonet" + (erc-d-t-search-for 1 "Disconnected from IRC") + (erc-d-t-search-for 1 "Connected!")) + (with-current-buffer "*status@barnet" + (erc-d-t-search-for 1 "Disconnected from IRC") + (erc-d-t-search-for 1 "Connected!"))) + 'znc-foonet + 'znc-barnet)) + +;;; erc-scenarios-upstream-recon-znc.el ends here diff --git a/test/lisp/erc/erc-scenarios-internal.el b/test/lisp/erc/erc-scenarios-internal.el new file mode 100644 index 00000000000..e4e1edb97e3 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-internal.el @@ -0,0 +1,27 @@ +;;; erc-scenarios-internal.el --- Proxy file for erc-d tests -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (expand-file-name "erc-d" (ert-resource-directory)) + load-path))) + (load "erc-d-tests" nil 'silent))) + +;;; erc-scenarios-internal.el ends here diff --git a/test/lisp/erc/erc-scenarios-join-auth-source.el b/test/lisp/erc/erc-scenarios-join-auth-source.el new file mode 100644 index 00000000000..94336db07c5 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-join-auth-source.el @@ -0,0 +1,67 @@ +;;; erc-scenarios-join-auth-source.el --- join-auth-source scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; TODO add another test with autojoin and channel keys + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-join-auth-source--network () + :tags '(:expensive-test) + (should erc-auth-source-join-function) + (erc-scenarios-common-with-cleanup + ((entries + '("machine 127.0.0.1 port %d login \"#foo\" password spam" + "machine irc.foonet.org port %d login tester password fake" + "machine irc.foonet.org login \"#spam\" password secret" + "machine foonet port %d login dummy password fake" + "machine 127.0.0.1 port %d login dummy password changeme")) + (erc-scenarios-common-dialog "join/auth-source") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service)) + (ents (mapcar (lambda (fmt) (format fmt port)) entries)) + (netrc-file (make-temp-file "auth-source-test" nil nil + (string-join ents "\n"))) + (auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (expect (erc-d-t-make-expecter)) + (erc-scenarios-common-extra-teardown (lambda () + (delete-file netrc-file)))) + + (ert-info ("Connect without password") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "dummy" + :full-name "dummy") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-wait-for 8 (eq erc-network 'foonet)) + (funcall expect 10 "user modes") + (erc-scenarios-common-say "/JOIN #spam"))) + + (ert-info ("Join #spam") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam")) + (funcall expect 10 "#spam was created on"))))) + +;;; erc-scenarios-join-auth-source.el ends here diff --git a/test/lisp/erc/erc-scenarios-join-netid-newcmd-id.el b/test/lisp/erc/erc-scenarios-join-netid-newcmd-id.el new file mode 100644 index 00000000000..e2e437321d9 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-join-netid-newcmd-id.el @@ -0,0 +1,50 @@ +;;; erc-scenarios-join-netid-newcmd-id.el --- join netid newcmd scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-join-netid--newcmd-id () + :tags '(:expensive-test) + (let ((connect (lambda () + (erc :server "127.0.0.1" + :port (with-current-buffer "oofnet" + (process-contact erc-server-process :service)) + :nick "tester" + :password "foonet:changeme" + :full-name "tester" + :id 'oofnet)))) + (erc-scenarios-common--join-network-id connect 'oofnet nil))) + +(ert-deftest erc-scenarios-join-netid--newcmd-ids () + :tags '(:expensive-test) + (let ((connect (lambda () + (erc :server "127.0.0.1" + :port (with-current-buffer "oofnet" + (process-contact erc-server-process :service)) + :nick "tester" + :password "foonet:changeme" + :full-name "tester" + :id 'oofnet)))) + (erc-scenarios-common--join-network-id connect 'oofnet 'rabnet))) + +;;; erc-scenarios-join-netid-newcmd-id.el ends here diff --git a/test/lisp/erc/erc-scenarios-join-netid-newcmd.el b/test/lisp/erc/erc-scenarios-join-netid-newcmd.el new file mode 100644 index 00000000000..1a541a46b3f --- /dev/null +++ b/test/lisp/erc/erc-scenarios-join-netid-newcmd.el @@ -0,0 +1,37 @@ +;;; erc-scenarios-join-netid-newcmd.el --- join netid newcmd scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-join-netid--newcmd () + :tags '(:expensive-test) + (let ((connect (lambda () + (erc :server "127.0.0.1" + :port (with-current-buffer "foonet" + (process-contact erc-server-process :service)) + :nick "tester" + :password "foonet:changeme" + :full-name "tester")))) + (erc-scenarios-common--join-network-id connect nil nil))) + +;;; erc-scenarios-join-netid-newcmd.el ends here diff --git a/test/lisp/erc/erc-scenarios-join-netid-recon-id.el b/test/lisp/erc/erc-scenarios-join-netid-recon-id.el new file mode 100644 index 00000000000..92bdd643de8 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-join-netid-recon-id.el @@ -0,0 +1,46 @@ +;;; erc-scenarios-join-netid-recon-id.el --- join-netid-recon scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-join-netid--recon-id () + :tags '(:expensive-test) + (let ((connect (lambda () + (with-current-buffer "oofnet" + (erc-cmd-RECONNECT) + (should (eq (current-buffer) + (process-buffer erc-server-process))) + (current-buffer))))) + (erc-scenarios-common--join-network-id connect 'oofnet nil))) + +(ert-deftest erc-scenarios-join-netid--recon-ids () + :tags '(:expensive-test) + (let ((connect (lambda () + (with-current-buffer "oofnet" + (erc-cmd-RECONNECT) + (should (eq (current-buffer) + (process-buffer erc-server-process))) + (current-buffer))))) + (erc-scenarios-common--join-network-id connect 'oofnet 'rabnet))) + +;;; erc-scenarios-join-netid-recon-id.el ends here diff --git a/test/lisp/erc/erc-scenarios-join-netid-recon.el b/test/lisp/erc/erc-scenarios-join-netid-recon.el new file mode 100644 index 00000000000..cbdba07e256 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-join-netid-recon.el @@ -0,0 +1,36 @@ +;;; erc-scenarios-join-netid-recon.el --- join-netid-recon scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-join-netid--recon () + :tags '(:expensive-test) + (let ((connect (lambda () + (with-current-buffer "foonet" + (erc-cmd-RECONNECT) + (should (eq (current-buffer) + (process-buffer erc-server-process))) + (current-buffer))))) + (erc-scenarios-common--join-network-id connect nil nil))) + +;;; erc-scenarios-join-netid-recon.el ends here diff --git a/test/lisp/erc/erc-scenarios-misc.el b/test/lisp/erc/erc-scenarios-misc.el new file mode 100644 index 00000000000..ded620ccc1d --- /dev/null +++ b/test/lisp/erc/erc-scenarios-misc.el @@ -0,0 +1,180 @@ +;;; erc-scenarios-misc.el --- Misc scenarios for ERC -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +(ert-deftest erc-scenarios-base-flood () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/flood") + (dumb-server (erc-d-run "localhost" t 'soju)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.5) ; this ratio MUST match + (erc-server-flood-margin 1.5) ; the default of 3:10 + (expect (erc-d-t-make-expecter)) + erc-autojoin-channels-alist) + + (ert-info ("Connect to bouncer") + (with-current-buffer + (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (funcall expect 5 "Soju"))) + + (ert-info ("#chan@foonet exists") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan/foonet")) + (erc-d-t-search-for 2 "<bob/foonet>") + (erc-d-t-absent-for 0.1 "<joe") + (funcall expect 3 "was created on"))) + + (ert-info ("#chan@barnet exists") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan/barnet")) + (erc-d-t-search-for 2 "<joe/barnet>") + (erc-d-t-absent-for 0.1 "<bob") + (funcall expect 3 "was created on") + (funcall expect 5 "To get good guard"))) + + (ert-info ("Message not held in queue limbo") + (with-current-buffer "#chan/foonet" + ;; Without 'no-penalty param in `erc-server-send', should fail + ;; after ~10 secs with: + ;; + ;; (erc-d-timeout "Timed out awaiting request: (:name ~privmsg + ;; :pattern \\`PRIVMSG #chan/foonet :alice: hi :timeout 2 + ;; :dialog soju)") + ;; + ;; Try reversing commit and spying on queue interactively + (erc-cmd-MSG "#chan/foonet alice: hi") + (funcall expect 5 "tester: Good, very good"))) + + (ert-info ("All output sent") + (with-current-buffer "#chan/foonet" + (funcall expect 8 "Some man or other")) + (with-current-buffer "#chan/barnet" + (funcall expect 10 "That's he that was Othello"))))) + +;; Corner case demoing fallback behavior for an absent 004 RPL but a +;; present 422 or 375. If this is unlikely enough, remove or guard +;; with `ert-skip' plus some condition so it only runs when explicitly +;; named via ERT specifier + +(ert-deftest erc-scenarios-networks-announced-missing () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "networks/announced-missing") + (expect (erc-d-t-make-expecter)) + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service))) + + (ert-info ("Connect without password") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (let ((err (should-error (sleep-for 1)))) + (should (string-match-p "Failed to determine" (cadr err)))) + (funcall expect 1 "Failed to determine") + (funcall expect 1 "Failed to determine") + (should-not erc-network) + (should (string= erc-server-announced-name "irc.foonet.org")))))) + +;; Targets that are host/server masks like $*, $$*, and #* are routed +;; to the server buffer: https://github.com/ircdocs/wooooms/issues/5 + +(ert-deftest erc-scenarios-base-mask-target-routing () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/mask-target-routing") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (erc-d-t-wait-for 10 (get-buffer "foonet")) + + (ert-info ("Channel buffer #foo playback received") + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "#foo")) + (funcall expect 10 "Excellent workman"))) + + (ert-info ("Global notices routed to server buffer") + (with-current-buffer "foonet" + (funcall expect 10 "going down soon") + (funcall expect 10 "this is a warning") + (funcall expect 10 "second warning") + (funcall expect 10 "final warning"))) + + (should-not (get-buffer "$*")))) + +(ert-deftest erc-scenarios-dcc-chat-accept () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "dcc/chat") + (dcc-server (erc-d-run "127.0.0.1" t "erc-dcc-server" 'accept-dcc + :ending "\n")) + (dcc-port (process-contact dcc-server :service)) + (dumb-server (erc-d-run "localhost" t 'accept :tmpl-vars + `((port . ,(number-to-string dcc-port))))) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (ert-info ("Offer received") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet")) + (funcall expect 10 "DCC: chat offered by dummy") + (erc-cmd-DCC "CHAT" "dummy"))) + + ;; Regression + (erc-d-t-ensure-for 1 (not (get-buffer "tester"))) + + ;; Becomes current buffer by default (because `erc-join-buffer') + (erc-d-t-wait-for 10 (get-buffer "DCC-CHAT-dummy")) + + (with-current-buffer "foonet" + (funcall expect 10 "*** DCC: accepting chat from dummy")) + + (ert-info ("Chat with dummy") + (with-current-buffer "DCC-CHAT-dummy" + (erc-scenarios-common-say "Hi") + (funcall expect 10 "Hola"))))) + +;;; erc-scenarios-misc.el ends here diff --git a/test/lisp/erc/erc-scenarios-services-misc.el b/test/lisp/erc/erc-scenarios-services-misc.el new file mode 100644 index 00000000000..cb1aa6ff324 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-services-misc.el @@ -0,0 +1,86 @@ +;;; erc-scenarios-services-misc.el --- Services-misc scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join) + (require 'erc-services)) + +(ert-deftest erc-scenarios-services-password () + :tags '(:expensive-test) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "services/password") + (erc-server-flood-penalty 0.1) + (erc-modules (cons 'services erc-modules)) + (erc-nickserv-passwords '((Libera.Chat (("joe" . "bar") + ("tester" . "changeme"))))) + (expect (erc-d-t-make-expecter)) + (dumb-server (erc-d-run "localhost" t 'libera)) + (port (process-contact dumb-server :service))) + + (ert-info ("Connect without password") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (erc-d-t-wait-for 5 (eq erc-network 'Libera.Chat)) + (funcall expect 5 "This nickname is registered.") + (funcall expect 2 "You are now identified") + (funcall expect 1 "Last login from") + (erc-cmd-QUIT ""))) + + (erc-services-mode -1) + + (should-not (memq 'services erc-modules)))) + +(ert-deftest erc-scenarios-services-prompt () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "services/password") + (erc-server-flood-penalty 0.1) + (inhibit-interaction nil) + (erc-modules (cons 'services erc-modules)) + (expect (erc-d-t-make-expecter)) + (dumb-server (erc-d-run "localhost" t 'libera)) + (port (process-contact dumb-server :service))) + + (ert-info ("Connect without password") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (ert-simulate-keys "changeme\r" + (erc-d-t-wait-for 10 (eq erc-network 'Libera.Chat)) + (funcall expect 3 "This nickname is registered.") + (funcall expect 3 "You are now identified") + (funcall expect 3 "Last login from")) + (erc-cmd-QUIT ""))) + + (erc-services-mode -1) + + (should-not (memq 'services erc-modules)))) + +;;; erc-scenarios-services-misc.el ends here diff --git a/test/lisp/erc/erc-services-tests.el b/test/lisp/erc/erc-services-tests.el new file mode 100644 index 00000000000..8e2b8d29273 --- /dev/null +++ b/test/lisp/erc/erc-services-tests.el @@ -0,0 +1,574 @@ +;;; erc-services-tests.el --- Tests for erc-services. -*- lexical-binding:t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published +;; by the Free Software Foundation, either version 3 of the License, +;; or (at your option) any later version. +;; +;; GNU Emacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; TODO: move the auth-source tests somewhere else. They've been +;; stashed here for pragmatic reasons. + +;;; Code: + +(require 'ert-x) +(require 'erc-services) +(require 'erc-compat) +(require 'secrets) + +;;;; Core auth-source + +(ert-deftest erc--auth-source-determine-params-merge () + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'fake) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create 'GNU.chat))) + + (should (equal (erc--auth-source-determine-params-merge) + '(:host ("GNU.chat" "my.gnu.org" "irc.gnu.org") + :port ("6697" "irc") + :require (:secret)))) + + (should (equal (erc--auth-source-determine-params-merge :host "fake") + '(:host ("fake" "GNU.chat" "my.gnu.org" "irc.gnu.org") + :port ("6697" "irc") + :require (:secret)))) + + (should (equal (erc--auth-source-determine-params-merge + :host '("fake") :require :host) + '(:host ("fake" "GNU.chat" "my.gnu.org" "irc.gnu.org") + :require (:host :secret) + :port ("6697" "irc")))) + + (should (equal (erc--auth-source-determine-params-merge + :host '("fake" "GNU.chat") :port "1234" :x "x") + '(:host ("fake" "GNU.chat" "my.gnu.org" "irc.gnu.org") + :port ("1234" "6697" "irc") + :x ("x") + :require (:secret)))))) + +;; Some of the following may be related to bug#23438. + +(defun erc-services-tests--auth-source-standard (search) + + (ert-info ("Session wins") + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'fake) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create 'GNU.chat))) + (should (string= (funcall search :user "#chan") "foo")))) + + (ert-info ("Network wins") + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string= (funcall search :user "#chan") "foo")))) + + (ert-info ("Announced wins") + (let ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + erc-network + (erc-networks--id (erc-networks--id-create nil))) + (should (string= (funcall search :user "#chan") "baz"))))) + +(defun erc-services-tests--auth-source-announced (search) + (let* ((erc--isupport-params (make-hash-table)) + (erc-server-parameters '(("CHANTYPES" . "&#"))) + (erc--target (erc--target-from-string "&chan"))) + + (ert-info ("Announced prioritized") + + (ert-info ("Announced wins") + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string= (funcall search :user "#chan") "baz")))) + + (ert-info ("Peer next") + (let* ((erc-server-announced-name "irc.gnu.org") + (erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string= (funcall search :user "#chan") "bar")))) + + (ert-info ("Network used as fallback") + (let* ((erc-session-port 6697) + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil))) + (should (string= (funcall search :user "#chan") "foo"))))))) + +(defun erc-services-tests--auth-source-overrides (search) + (let* ((erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-network 'GNU.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil)) + (erc-session-port 6667)) + + (ert-info ("Specificity and overrides") + + (ert-info ("More specific port") + (let ((erc-session-port 6697)) + (should (string= (funcall search :user "#chan") "spam")))) + + (ert-info ("More specific user (network loses)") + (should (string= (funcall search :user '("#fsf")) "42"))) + + (ert-info ("Actual override") + (should (string= (funcall search :port "6667") "sesame"))) + + (ert-info ("Overrides don't interfere with post-processing") + (should (string= (funcall search :host "MyHost") "123")))))) + +;; auth-source netrc backend + +(defvar erc-services-tests--auth-source-entries + '("machine irc.gnu.org port irc user \"#chan\" password bar" + "machine my.gnu.org port irc user \"#chan\" password baz" + "machine GNU.chat port irc user \"#chan\" password foo")) + +;; FIXME explain what this is for +(defun erc-services-tests--auth-source-shuffle (&rest extra) + (string-join `(,@(sort (append erc-services-tests--auth-source-entries extra) + (lambda (&rest _) (zerop (random 2)))) + "") + "\n")) + +(ert-deftest erc--auth-source-search--netrc-standard () + (ert-with-temp-file netrc-file + :prefix "erc--auth-source-search--standard" + :text (erc-services-tests--auth-source-shuffle) + + (let ((auth-sources (list netrc-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-standard #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--netrc-announced () + (ert-with-temp-file netrc-file + :prefix "erc--auth-source-search--announced" + :text (erc-services-tests--auth-source-shuffle) + + (let ((auth-sources (list netrc-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-announced #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--netrc-overrides () + (ert-with-temp-file netrc-file + :prefix "erc--auth-source-search--overrides" + :text (erc-services-tests--auth-source-shuffle + "machine GNU.chat port 6697 user \"#chan\" password spam" + "machine my.gnu.org port irc user \"#fsf\" password 42" + "machine irc.gnu.org port 6667 password sesame" + "machine MyHost port irc password 456" + "machine MyHost port 6667 password 123") + + (let ((auth-sources (list netrc-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-overrides #'erc-auth-source-search)))) + +;; auth-source plstore backend + +(defun erc-services-test--call-with-plstore (&rest args) + (advice-add 'epg-decrypt-string :override + (lambda (&rest r) (prin1-to-string (cadr r))) + '((name . erc--auth-source-plstore))) + (advice-add 'epg-find-configuration :override + (lambda (&rest _) "" '((program . "/bin/true"))) + '((name . erc--auth-source-plstore))) + (unwind-protect + (apply #'erc-auth-source-search args) + (advice-remove 'epg-decrypt-string 'erc--auth-source-plstore) + (advice-remove 'epg-find-configuration 'erc--auth-source-plstore))) + +(defvar erc-services-tests--auth-source-plstore-standard-entries + '(("ba950d38118a76d71f9f0591bb373d6cb366a512" + :secret-secret t + :host "irc.gnu.org" + :user "#chan" + :port "irc") + ("7f17ca445d11158065e911a6d0f4cbf52ca250e3" + :secret-secret t + :host "my.gnu.org" + :user "#chan" + :port "irc") + ("fcd3c8bd6daf4509de0ad6ee98e744ce0fca9377" + :secret-secret t + :host "GNU.chat" + :user "#chan" + :port "irc"))) + +(defvar erc-services-tests--auth-source-plstore-standard-secrets + '(("ba950d38118a76d71f9f0591bb373d6cb366a512" :secret "bar") + ("7f17ca445d11158065e911a6d0f4cbf52ca250e3" :secret "baz") + ("fcd3c8bd6daf4509de0ad6ee98e744ce0fca9377" :secret "foo"))) + +(ert-deftest erc--auth-source-search--plstore-standard () + (ert-with-temp-file plstore-file + :suffix ".plist" + :text (concat ";;; public entries -*- mode: plstore -*- \n" + (prin1-to-string + erc-services-tests--auth-source-plstore-standard-entries) + "\n;;; secret entries\n" + (prin1-to-string + erc-services-tests--auth-source-plstore-standard-secrets) + "\n") + + (let ((auth-sources (list plstore-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-standard + #'erc-services-test--call-with-plstore)))) + +(ert-deftest erc--auth-source-search--plstore-announced () + (ert-with-temp-file plstore-file + :suffix ".plist" + :text (concat ";;; public entries -*- mode: plstore -*- \n" + (prin1-to-string + erc-services-tests--auth-source-plstore-standard-entries) + "\n;;; secret entries\n" + (prin1-to-string + erc-services-tests--auth-source-plstore-standard-secrets) + "\n") + + (let ((auth-sources (list plstore-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-announced + #'erc-services-test--call-with-plstore)))) + +(ert-deftest erc--auth-source-search--plstore-overrides () + (ert-with-temp-file plstore-file + :suffix ".plist" + :text (concat + ";;; public entries -*- mode: plstore -*- \n" + (prin1-to-string + `(,@erc-services-tests--auth-source-plstore-standard-entries + ("1b3fab249a8dff77a4d8fe7eb4b0171b25cc711a" + :secret-secret t :host "GNU.chat" :user "#chan" :port "6697") + ("6cbcdc39476b8cfcca6f3e9a7876f41ec3f708cc" + :secret-secret t :host "my.gnu.org" :user "#fsf" :port "irc") + ("a33e2b3bd2d6f33995a4b88710a594a100c5e41d" + :secret-secret t :host "irc.gnu.org" :port "6667") + ("ab2fd349b2b7d6a9215bb35a92d054261b0b1537" + :secret-secret t :host "MyHost" :port "irc") + ("61a6bd552059494f479ff720e8de33e22574650a" + :secret-secret t :host "MyHost" :port "6667"))) + "\n;;; secret entries\n" + (prin1-to-string + `(,@erc-services-tests--auth-source-plstore-standard-secrets + ("1b3fab249a8dff77a4d8fe7eb4b0171b25cc711a" :secret "spam") + ("6cbcdc39476b8cfcca6f3e9a7876f41ec3f708cc" :secret "42") + ("a33e2b3bd2d6f33995a4b88710a594a100c5e41d" :secret "sesame") + ("ab2fd349b2b7d6a9215bb35a92d054261b0b1537" :secret "456") + ("61a6bd552059494f479ff720e8de33e22574650a" :secret "123"))) + "\n") + + (let ((auth-sources (list plstore-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-overrides + #'erc-services-test--call-with-plstore)))) + +;; auth-source JSON backend + +(defvar erc-services-tests--auth-source-json-standard-entries + [(:host "irc.gnu.org" :port "irc" :user "#chan" :secret "bar") + (:host "my.gnu.org" :port "irc" :user "#chan" :secret "baz") + (:host "GNU.chat" :port "irc" :user "#chan" :secret "foo")]) + +(ert-deftest erc--auth-source-search--json-standard () + (ert-with-temp-file json-store + :suffix ".json" + :text (let ((json-object-type 'plist)) + (json-encode + erc-services-tests--auth-source-json-standard-entries)) + (let ((auth-sources (list json-store)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-standard #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--json-announced () + (ert-with-temp-file plstore-file + :suffix ".json" + :text (let ((json-object-type 'plist)) + (json-encode + erc-services-tests--auth-source-json-standard-entries)) + + (let ((auth-sources (list plstore-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-announced #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--json-overrides () + (ert-with-temp-file json-file + :suffix ".json" + :text (let ((json-object-type 'plist)) + (json-encode + (vconcat + erc-services-tests--auth-source-json-standard-entries + [(:secret "spam" :host "GNU.chat" :user "#chan" :port "6697") + (:secret "42" :host "my.gnu.org" :user "#fsf" :port "irc") + (:secret "sesame" :host "irc.gnu.org" :port "6667") + (:secret "456" :host "MyHost" :port "irc") + (:secret "123" :host "MyHost" :port "6667")]))) + + (let ((auth-sources (list json-file)) + (auth-source-do-cache nil)) + (erc-services-tests--auth-source-overrides #'erc-auth-source-search)))) + +;; auth-source-secrets backend + +(defvar erc-services-tests--auth-source-secrets-standard-entries + '(("#chan@irc.gnu.org:irc" ; label + (:host . "irc.gnu.org") + (:user . "#chan") + (:port . "irc") + (:xdg:schema . "org.freedesktop.Secret.Generic")) + ("#chan@my.gnu.org:irc" + (:host . "my.gnu.org") + (:user . "#chan") + (:port . "irc") + (:xdg:schema . "org.freedesktop.Secret.Generic")) + ("#chan@GNU.chat:irc" + (:host . "GNU.chat") + (:user . "#chan") + (:port . "irc") + (:xdg:schema . "org.freedesktop.Secret.Generic")))) + +(defvar erc-services-tests--auth-source-secrets-standard-secrets + '(("#chan@irc.gnu.org:irc" . "bar") + ("#chan@my.gnu.org:irc" . "baz") + ("#chan@GNU.chat:irc" . "foo"))) + +(ert-deftest erc--auth-source-search--secrets-standard () + (skip-unless (bound-and-true-p secrets-enabled)) + (let ((auth-sources '("secrets:Test")) + (auth-source-do-cache nil) + (entries erc-services-tests--auth-source-secrets-standard-entries) + (secrets erc-services-tests--auth-source-secrets-standard-secrets)) + + (cl-letf (((symbol-function 'secrets-search-items) + (lambda (col &rest r) + (should (equal col "Test")) + (should (plist-get r :user)) + (map-keys entries))) + ((symbol-function 'secrets-get-secret) + (lambda (col label) + (should (equal col "Test")) + (assoc-default label secrets))) + ((symbol-function 'secrets-get-attributes) + (lambda (col label) + (should (equal col "Test")) + (assoc-default label entries)))) + + (erc-services-tests--auth-source-standard #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--secrets-announced () + (skip-unless (bound-and-true-p secrets-enabled)) + (let ((auth-sources '("secrets:Test")) + (auth-source-do-cache nil) + (entries erc-services-tests--auth-source-secrets-standard-entries) + (secrets erc-services-tests--auth-source-secrets-standard-secrets)) + + (cl-letf (((symbol-function 'secrets-search-items) + (lambda (col &rest r) + (should (equal col "Test")) + (should (plist-get r :user)) + (map-keys entries))) + ((symbol-function 'secrets-get-secret) + (lambda (col label) + (should (equal col "Test")) + (assoc-default label secrets))) + ((symbol-function 'secrets-get-attributes) + (lambda (col label) + (should (equal col "Test")) + (assoc-default label entries)))) + + (erc-services-tests--auth-source-announced #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--secrets-overrides () + (skip-unless (bound-and-true-p secrets-enabled)) + (let ((auth-sources '("secrets:Test")) + (auth-source-do-cache nil) + (entries `(,@erc-services-tests--auth-source-secrets-standard-entries + ("#chan@GNU.chat:6697" + (:host . "GNU.chat") (:user . "#chan") (:port . "6697") + (:xdg:schema . "org.freedesktop.Secret.Generic")) + ("#fsf@my.gnu.org:irc" + (:host . "my.gnu.org") (:user . "#fsf") (:port . "irc") + (:xdg:schema . "org.freedesktop.Secret.Generic")) + ("irc.gnu.org:6667" + (:host . "irc.gnu.org") (:port . "6667") + (:xdg:schema . "org.freedesktop.Secret.Generic")) + ("MyHost:irc" + (:host . "MyHost") (:port . "irc") + (:xdg:schema . "org.freedesktop.Secret.Generic")) + ("MyHost:6667" + (:host . "MyHost") (:port . "6667") + (:xdg:schema . "org.freedesktop.Secret.Generic")))) + (secrets `(,@erc-services-tests--auth-source-secrets-standard-secrets + ("#chan@GNU.chat:6697" . "spam") + ("#fsf@my.gnu.org:irc" . "42" ) + ("irc.gnu.org:6667" . "sesame") + ("MyHost:irc" . "456") + ("MyHost:6667" . "123")))) + + (cl-letf (((symbol-function 'secrets-search-items) + (lambda (col &rest _) + (should (equal col "Test")) + (map-keys entries))) + ((symbol-function 'secrets-get-secret) + (lambda (col label) + (should (equal col "Test")) + (assoc-default label secrets))) + ((symbol-function 'secrets-get-attributes) + (lambda (col label) + (should (equal col "Test")) + (assoc-default label entries)))) + + (erc-services-tests--auth-source-overrides #'erc-auth-source-search)))) + +;; auth-source-pass backend + +(require 'auth-source-pass) + +;; `auth-source-pass--find-match-unambiguous' returns something like: +;; +;; (list :host "irc.gnu.org" +;; :port "6697" +;; :user "rms" +;; :secret +;; #[0 "\301\302\300\"\207" +;; [((secret . "freedom")) auth-source-pass--get-attr secret] 3]) +;; +;; This function gives ^ (faked here to avoid gpg and file IO). See +;; `auth-source-pass--with-store' in ../auth-source-pass-tests.el +(defun erc-services-tests--asp-parse-entry (store entry) + (when-let ((found (cl-find entry store :key #'car :test #'string=))) + (list (assoc 'secret (cdr found))))) + +(defvar erc-join-tests--auth-source-pass-entries + '(("irc.gnu.org:irc/#chan" + ("port" . "irc") ("user" . "#chan") (secret . "bar")) + ("my.gnu.org:irc/#chan" + ("port" . "irc") ("user" . "#chan") (secret . "baz")) + ("GNU.chat:irc/#chan" + ("port" . "irc") ("user" . "#chan") (secret . "foo")))) + +(ert-deftest erc--auth-source-search--pass-standard () + (ert-skip "Pass backend not yet supported") + (let ((store erc-join-tests--auth-source-pass-entries) + (auth-sources '(password-store)) + (auth-source-do-cache nil)) + + (cl-letf (((symbol-function 'auth-source-pass-parse-entry) + (apply-partially #'erc-services-tests--asp-parse-entry store)) + ((symbol-function 'auth-source-pass-entries) + (lambda () (mapcar #'car store)))) + + (erc-services-tests--auth-source-standard #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--pass-announced () + (ert-skip "Pass backend not yet supported") + (let ((store erc-join-tests--auth-source-pass-entries) + (auth-sources '(password-store)) + (auth-source-do-cache nil)) + + (cl-letf (((symbol-function 'auth-source-pass-parse-entry) + (apply-partially #'erc-services-tests--asp-parse-entry store)) + ((symbol-function 'auth-source-pass-entries) + (lambda () (mapcar #'car store)))) + + (erc-services-tests--auth-source-announced #'erc-auth-source-search)))) + +(ert-deftest erc--auth-source-search--pass-overrides () + (ert-skip "Pass backend not yet supported") + (let ((store + `(,@erc-join-tests--auth-source-pass-entries + ("GNU.chat:6697/#chan" + ("port" . "6697") ("user" . "#chan") (secret . "spam")) + ("my.gnu.org:irc/#fsf" + ("port" . "irc") ("user" . "#fsf") (secret . "42")) + ("irc.gnu.org:6667" + ("port" . "6667") (secret . "sesame")) + ("MyHost:irc" + ("port" . "irc") (secret . "456")) + ("MyHost:6667" + ("port" . "6667") (secret . "123")))) + (auth-sources '(password-store)) + (auth-source-do-cache nil)) + + (cl-letf (((symbol-function 'auth-source-pass-parse-entry) + (apply-partially #'erc-services-tests--asp-parse-entry store)) + ((symbol-function 'auth-source-pass-entries) + (lambda () (mapcar #'car store)))) + + (erc-services-tests--auth-source-overrides #'erc-auth-source-search)))) + +;;;; The services module + +(ert-deftest erc-nickserv-get-password () + (should erc-prompt-for-nickserv-password) + (ert-with-temp-file netrc-file + :prefix "erc-nickserv-get-password" + :text (mapconcat 'identity + '("machine GNU/chat port 6697 user bob password spam" + "machine FSF.chat port 6697 user bob password sesame" + "machine MyHost port irc password 123") + "\n") + + (let* ((auth-sources (list netrc-file)) + (auth-source-do-cache nil) + (erc-nickserv-passwords '((FSF.chat (("alice" . "foo") + ("joe" . "bar"))))) + (erc-use-auth-source-for-nickserv-password t) + (erc-session-server "irc.gnu.org") + (erc-server-announced-name "my.gnu.org") + (erc-network 'FSF.chat) + (erc-server-current-nick "tester") + (erc-networks--id (erc-networks--id-create nil)) + (erc-session-port 6697)) + + (ert-info ("Lookup custom option") + (should (string= (erc-nickserv-get-password "alice") "foo"))) + + (ert-info ("Auth source") + (ert-info ("Network") + (should (string= (erc-nickserv-get-password "bob") "sesame"))) + + (ert-info ("Network ID") + (let ((erc-networks--id (erc-networks--id-create 'GNU/chat))) + (should (string= (erc-nickserv-get-password "bob") "spam"))))) + + (ert-info ("Read input") + (should (string= + (ert-simulate-keys "baz\r" (erc-nickserv-get-password "mike")) + "baz"))) + + (ert-info ("Failed") + (should-not (ert-simulate-keys "\r" + (erc-nickserv-get-password "fake"))))))) + + +;;; erc-services-tests.el ends here diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el new file mode 100644 index 00000000000..b2ed29e80ec --- /dev/null +++ b/test/lisp/erc/erc-tests.el @@ -0,0 +1,978 @@ +;;; erc-tests.el --- Tests for erc. -*- lexical-binding:t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. + +;; Author: Lars Ingebrigtsen <larsi@gnus.org> + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +;;; Code: + +(require 'ert-x) +(require 'erc) +(require 'erc-ring) +(require 'erc-networks) + +(ert-deftest erc--read-time-period () + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) ""))) + (should (equal (erc--read-time-period "foo: ") nil))) + + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) " "))) + (should (equal (erc--read-time-period "foo: ") nil))) + + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) " 432 "))) + (should (equal (erc--read-time-period "foo: ") 432))) + + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "432"))) + (should (equal (erc--read-time-period "foo: ") 432))) + + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1h"))) + (should (equal (erc--read-time-period "foo: ") 3600))) + + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1h10s"))) + (should (equal (erc--read-time-period "foo: ") 3610))) + + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1d"))) + (should (equal (erc--read-time-period "foo: ") 86400)))) + +(ert-deftest erc--meta--backend-dependencies () + (with-temp-buffer + (insert-file-contents-literally + (concat (file-name-sans-extension (symbol-file 'erc)) ".el")) + (let ((beg (search-forward ";; Defined in erc-backend")) + (end (search-forward "\n\n")) + vars) + (save-excursion + (save-restriction + (narrow-to-region beg end) + (goto-char (point-min)) + (with-syntax-table lisp-data-mode-syntax-table + (condition-case _ + (while (push (cadr (read (current-buffer))) vars)) + (end-of-file))))) + (should (= (point) end)) + (dolist (var vars) + (setq var (concat "\\_<" (symbol-name var) "\\_>")) + (ert-info (var) + (should (save-excursion (search-forward-regexp var nil t)))))))) + +(ert-deftest erc-with-all-buffers-of-server () + (let (proc-exnet + proc-onet + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (with-current-buffer (get-buffer-create "OtherNet") + (erc-mode) + (setq proc-onet (start-process "sleep" (current-buffer) "sleep" "1") + erc-server-process proc-onet + erc-network 'OtherNet) + (set-process-query-on-exit-flag erc-server-process nil)) + + (with-current-buffer (get-buffer-create "ExampleNet") + (erc-mode) + (setq proc-exnet (start-process "sleep" (current-buffer) "sleep" "1") + erc-server-process proc-exnet + erc-network 'ExampleNet) + (set-process-query-on-exit-flag erc-server-process nil)) + + (with-current-buffer (get-buffer-create "#foo") + (erc-mode) + (setq erc-server-process proc-exnet) + (setq erc-default-recipients '("#foo"))) + + (with-current-buffer (get-buffer-create "#spam") + (erc-mode) + (setq erc-server-process proc-onet) + (setq erc-default-recipients '("#spam"))) + + (with-current-buffer (get-buffer-create "#bar") + (erc-mode) + (setq erc-server-process proc-onet) + (setq erc-default-recipients '("#bar"))) + + (with-current-buffer (get-buffer-create "#baz") + (erc-mode) + (setq erc-server-process proc-exnet) + (setq erc-default-recipients '("#baz"))) + + (should (eq (get-buffer-process "ExampleNet") proc-exnet)) + (erc-with-all-buffers-of-server (get-buffer-process "ExampleNet") + nil + (kill-buffer)) + + (should-not (get-buffer "ExampleNet")) + (should-not (get-buffer "#foo")) + (should-not (get-buffer "#baz")) + (should (get-buffer "OtherNet")) + (should (get-buffer "#bar")) + (should (get-buffer "#spam")) + + (let* ((test (lambda () (not (string= (buffer-name) "#spam")))) + (calls 0) + (get-test (lambda () (cl-incf calls) test))) + + (erc-with-all-buffers-of-server proc-onet + (funcall get-test) + (kill-buffer)) + + (should (= calls 1))) + + (should-not (get-buffer "OtherNet")) + (should-not (get-buffer "#bar")) + (should (get-buffer "#spam")) + (kill-buffer "#spam"))) + +(defun erc-tests--send-prep () + ;; Caller should probably shadow `erc-insert-modify-hook' or + ;; populate user tables for erc-button. + (erc-mode) + (insert "\n\n") + (setq erc-input-marker (make-marker) + erc-insert-marker (make-marker)) + (set-marker erc-insert-marker (point-max)) + (erc-display-prompt) + (should (= (point) erc-input-marker))) + +(defun erc-tests--set-fake-server-process (&rest args) + (setq erc-server-process + (apply #'start-process (car args) (current-buffer) args)) + (set-process-query-on-exit-flag erc-server-process nil)) + +(ert-deftest erc-hide-prompt () + (let (erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (with-current-buffer (get-buffer-create "ServNet") + (erc-tests--send-prep) + (goto-char erc-insert-marker) + (should (looking-at-p (regexp-quote erc-prompt))) + (erc-tests--set-fake-server-process "sleep" "1") + (set-process-sentinel erc-server-process #'ignore) + (setq erc-network 'ServNet) + (set-process-query-on-exit-flag erc-server-process nil)) + + (with-current-buffer (get-buffer-create "#chan") + (erc-tests--send-prep) + (goto-char erc-insert-marker) + (should (looking-at-p (regexp-quote erc-prompt))) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "ServNet")) + erc-default-recipients '("#chan"))) + + (with-current-buffer (get-buffer-create "bob") + (erc-tests--send-prep) + (goto-char erc-insert-marker) + (should (looking-at-p (regexp-quote erc-prompt))) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "ServNet")) + erc-default-recipients '("bob"))) + + (ert-info ("Value: t (default)") + (should (eq erc-hide-prompt t)) + (with-current-buffer "ServNet" + (should (= (point) erc-insert-marker)) + (erc--hide-prompt erc-server-process) + (should (string= ">" (get-text-property (point) 'display)))) + + (with-current-buffer "#chan" + (goto-char erc-insert-marker) + (should (string= ">" (get-text-property (point) 'display))) + (should (memq #'erc--unhide-prompt-on-self-insert pre-command-hook)) + (goto-char erc-input-marker) + (ert-simulate-command '(self-insert-command 1 ?/)) + (goto-char erc-insert-marker) + (should-not (get-text-property (point) 'display)) + (should-not (memq #'erc--unhide-prompt-on-self-insert + pre-command-hook))) + + (with-current-buffer "bob" + (goto-char erc-insert-marker) + (should (string= ">" (get-text-property (point) 'display))) + (should (memq #'erc--unhide-prompt-on-self-insert pre-command-hook)) + (goto-char erc-input-marker) + (ert-simulate-command '(self-insert-command 1 ?/)) + (goto-char erc-insert-marker) + (should-not (get-text-property (point) 'display)) + (should-not (memq #'erc--unhide-prompt-on-self-insert + pre-command-hook))) + + (with-current-buffer "ServNet" + (should (get-text-property erc-insert-marker 'display)) + (should (memq #'erc--unhide-prompt-on-self-insert pre-command-hook)) + (erc--unhide-prompt) + (should-not (memq #'erc--unhide-prompt-on-self-insert + pre-command-hook)) + (should-not (get-text-property erc-insert-marker 'display)))) + + (ert-info ("Value: server") + (setq erc-hide-prompt '(server)) + (with-current-buffer "ServNet" + (erc--hide-prompt erc-server-process) + (should (string= ">" (get-text-property erc-insert-marker 'display)))) + + (with-current-buffer "#chan" + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "bob" + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "ServNet" + (erc--unhide-prompt) + (should-not (get-text-property erc-insert-marker 'display)))) + + (ert-info ("Value: channel") + (setq erc-hide-prompt '(channel)) + (with-current-buffer "ServNet" + (erc--hide-prompt erc-server-process) + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "bob" + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "#chan" + (should (string= ">" (get-text-property erc-insert-marker 'display))) + (erc--unhide-prompt) + (should-not (get-text-property erc-insert-marker 'display)))) + + (ert-info ("Value: query") + (setq erc-hide-prompt '(query)) + (with-current-buffer "ServNet" + (erc--hide-prompt erc-server-process) + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "bob" + (should (string= ">" (get-text-property erc-insert-marker 'display))) + (erc--unhide-prompt) + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "#chan" + (should-not (get-text-property erc-insert-marker 'display)))) + + (ert-info ("Value: nil") + (setq erc-hide-prompt nil) + (with-current-buffer "ServNet" + (erc--hide-prompt erc-server-process) + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "bob" + (should-not (get-text-property erc-insert-marker 'display))) + + (with-current-buffer "#chan" + (should-not (get-text-property erc-insert-marker 'display)) + (erc--unhide-prompt) ; won't blow up when prompt already showing + (should-not (get-text-property erc-insert-marker 'display)))) + + (when noninteractive + (kill-buffer "#chan") + (kill-buffer "bob") + (kill-buffer "ServNet")))) + +(ert-deftest erc--switch-to-buffer () + (defvar erc-modified-channels-alist) ; lisp/erc/erc-track.el + + (let ((proc (start-process "aNet" (current-buffer) "true")) + (erc-modified-channels-alist `(("fake") (,(messages-buffer)))) + (inhibit-message noninteractive) + (completion-fail-discreetly t) ; otherwise ^G^G printed to .log file + ;; + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (with-current-buffer (get-buffer-create "server") + (erc-mode) + (set-process-buffer (setq erc-server-process proc) (current-buffer)) + (set-process-query-on-exit-flag erc-server-process nil) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-server-process proc)) + (with-current-buffer (get-buffer-create "#foo") + (erc-mode) + (setq erc-server-process proc)) + + (ert-info ("Channel #chan selectable from server buffer") + (ert-simulate-keys (list ?# ?c ?h ?a ?n ?\C-m) + (should (string= "#chan" (erc--switch-to-buffer)))))) + + (ert-info ("Channel #foo selectable from non-ERC buffer") + (ert-simulate-keys (list ?# ?f ?o ?o ?\C-m) + (should (string= "#foo" (erc--switch-to-buffer))))) + + (ert-info ("Default selectable") + (ert-simulate-keys (list ?\C-m) + (should (string= "*Messages*" (erc--switch-to-buffer))))) + + (ert-info ("Extant but non-ERC buffer not selectable") + (get-buffer-create "#fake") ; not ours + (ert-simulate-keys (kbd "#fake C-m C-a C-k C-m") + ;; Initial query fails ~~~~~~^; clearing input accepts default + (should (string= "*Messages*" (erc--switch-to-buffer))))) + + (with-current-buffer (get-buffer-create "other") + (erc-mode) + (setq erc-server-process (start-process "bNet" (current-buffer) "true")) + (set-process-query-on-exit-flag erc-server-process nil)) + + (ert-info ("Foreign ERC buffer not selectable") + (ert-simulate-keys (kbd "other C-m C-a C-k C-m") + (with-current-buffer "server" + (should (string= "*Messages*" (erc--switch-to-buffer)))))) + + (ert-info ("Any ERC-buffer selectable from non-ERC buffer") + (should-not (eq major-mode 'erc-mode)) + (ert-simulate-keys (list ?o ?t ?h ?e ?r ?\C-m) + (should (string= "other" (erc--switch-to-buffer))))) + + (dolist (b '("server" "other" "#chan" "#foo" "#fake")) + (kill-buffer b)))) + +(ert-deftest erc-lurker-maybe-trim () + (let (erc-lurker-trim-nicks + (erc-lurker-ignore-chars "_`")) + + (should (string= "nick`" (erc-lurker-maybe-trim "nick`"))) + + (setq erc-lurker-trim-nicks t) + (should (string= "nick" (erc-lurker-maybe-trim "nick`"))) + (should (string= "ni`_ck" (erc-lurker-maybe-trim "ni`_ck__``"))) + + (setq erc-lurker-ignore-chars "_-`") ; set of chars, not character alts + (should (string= "nick" (erc-lurker-maybe-trim "nick-_`"))))) + +(ert-deftest erc--parse-isupport-value () + (should (equal (erc--parse-isupport-value "a,b") '("a" "b"))) + (should (equal (erc--parse-isupport-value "a,b,c") '("a" "b" "c"))) + + (should (equal (erc--parse-isupport-value "abc") '("abc"))) + (should (equal (erc--parse-isupport-value "\\x20foo") '(" foo"))) + (should (equal (erc--parse-isupport-value "foo\\x20") '("foo "))) + (should (equal (erc--parse-isupport-value "a\\x20b\\x20c") '("a b c"))) + (should (equal (erc--parse-isupport-value "a\\x20b\\x20c\\x20") '("a b c "))) + (should (equal (erc--parse-isupport-value "\\x20a\\x20b\\x20c") '(" a b c"))) + (should (equal (erc--parse-isupport-value "a\\x20\\x20c") '("a c"))) + (should (equal (erc--parse-isupport-value "\\x20\\x20\\x20") '(" "))) + (should (equal (erc--parse-isupport-value "\\x5Co/") '("\\o/"))) + (should (equal (erc--parse-isupport-value "\\x7F,\\x19") '("\\x7F" "\\x19"))) + (should (equal (erc--parse-isupport-value "a\\x2Cb,c") '("a,b" "c")))) + +(ert-deftest erc--get-isupport-entry () + (let ((erc--isupport-params (make-hash-table)) + (erc-server-parameters '(("FOO" . "1") ("BAR") ("BAZ" . "A,B,C"))) + (items (lambda () + (cl-loop for k being the hash-keys of erc--isupport-params + using (hash-values v) collect (cons k v))))) + + (should-not (erc--get-isupport-entry 'FAKE)) + (should-not (erc--get-isupport-entry 'FAKE 'single)) + (should (zerop (hash-table-count erc--isupport-params))) + + (should (equal (erc--get-isupport-entry 'BAR) '(BAR))) + (should-not (erc--get-isupport-entry 'BAR 'single)) + (should (= 1 (hash-table-count erc--isupport-params))) + + (should (equal (erc--get-isupport-entry 'BAZ) '(BAZ "A" "B" "C"))) + (should (equal (erc--get-isupport-entry 'BAZ 'single) "A")) + (should (= 2 (hash-table-count erc--isupport-params))) + + (should (equal (erc--get-isupport-entry 'FOO 'single) "1")) + (should (equal (erc--get-isupport-entry 'FOO) '(FOO "1"))) + + (should (equal (funcall items) + '((BAR . --empty--) (BAZ "A" "B" "C") (FOO "1")))))) + +(ert-deftest erc-server-005 () + (let* ((hooked 0) + (verify #'ignore) + (hook (lambda (_ _) (funcall verify) (cl-incf hooked))) + (erc-server-005-functions (list #'erc-server-005 hook #'ignore)) + erc-server-parameters + erc--isupport-params + erc-timer-hook + calls + args + parsed) + + (cl-letf (((symbol-function 'erc-display-message) + (lambda (_ _ _ line) (push line calls)))) + + (ert-info ("Baseline") + (setq args '("tester" "BOT=B" "EXCEPTS" "PREFIX=(ov)@+" "are supp...") + parsed (make-erc-response :command-args args :command "005")) + + (setq verify + (lambda () + (should (equal erc-server-parameters + '(("PREFIX" . "(ov)@+") ("EXCEPTS") + ("BOT" . "B")))) + (should (zerop (hash-table-count erc--isupport-params))) + (should (equal "(ov)@+" (erc--get-isupport-entry 'PREFIX t))) + (should (equal '(EXCEPTS) (erc--get-isupport-entry 'EXCEPTS))) + (should (equal "B" (erc--get-isupport-entry 'BOT t))) + (should (string= (pop calls) + "BOT=B EXCEPTS PREFIX=(ov)@+ are supp...")) + (should (equal args (erc-response.command-args parsed))))) + + (erc-call-hooks nil parsed)) + + (ert-info ("Negated, updated") + (setq args '("tester" "-EXCEPTS" "-FAKE" "PREFIX=(ohv)@%+" "are su...") + parsed (make-erc-response :command-args args :command "005")) + + (setq verify + (lambda () + (should (equal erc-server-parameters + '(("PREFIX" . "(ohv)@%+") ("BOT" . "B")))) + (should (string= (pop calls) + "-EXCEPTS -FAKE PREFIX=(ohv)@%+ are su...")) + (should (equal "(ohv)@%+" (erc--get-isupport-entry 'PREFIX t))) + (should (equal "B" (erc--get-isupport-entry 'BOT t))) + (should-not (erc--get-isupport-entry 'EXCEPTS)) + (should (equal args (erc-response.command-args parsed))))) + + (erc-call-hooks nil parsed)) + (should (= hooked 2))))) + +(ert-deftest erc-downcase () + (let ((erc--isupport-params (make-hash-table))) + + (puthash 'PREFIX '("(ov)@+") erc--isupport-params) + (puthash 'BOT '("B") erc--isupport-params) + + (ert-info ("ascii") + (puthash 'CASEMAPPING '("ascii") erc--isupport-params) + (should (equal (erc-downcase "Bob[m]`") "bob[m]`")) + (should (equal (erc-downcase "Tilde~") "tilde~" )) + (should (equal (erc-downcase "\\O/") "\\o/" ))) + + (ert-info ("rfc1459") + (puthash 'CASEMAPPING '("rfc1459") erc--isupport-params) + (should (equal (erc-downcase "Bob[m]`") "bob{m}`" )) + (should (equal (erc-downcase "Tilde~") "tilde^" )) + (should (equal (erc-downcase "\\O/") "|o/" ))) + + (ert-info ("rfc1459-strict") + (puthash 'CASEMAPPING '("rfc1459-strict") erc--isupport-params) + (should (equal (erc-downcase "Bob[m]`") "bob{m}`")) + (should (equal (erc-downcase "Tilde~") "tilde~" )) + (should (equal (erc-downcase "\\O/") "|o/" ))))) + +(ert-deftest erc--valid-local-channel-p () + (ert-info ("Local channels not supported") + (let ((erc--isupport-params (make-hash-table))) + (puthash 'CHANTYPES '("#") erc--isupport-params) + (should-not (erc--valid-local-channel-p "#chan")) + (should-not (erc--valid-local-channel-p "&local")))) + (ert-info ("Local channels supported") + (let ((erc--isupport-params (make-hash-table))) + (puthash 'CHANTYPES '("&#") erc--isupport-params) + (should-not (erc--valid-local-channel-p "#chan")) + (should (erc--valid-local-channel-p "&local"))))) + +(ert-deftest erc--target-from-string () + (should (equal (erc--target-from-string "#chan") + #s(erc--target-channel "#chan" \#chan))) + + (should (equal (erc--target-from-string "Bob") + #s(erc--target "Bob" bob))) + + (let ((erc--isupport-params (make-hash-table))) + (puthash 'CHANTYPES '("&#") erc--isupport-params) + (should (equal (erc--target-from-string "&Bitlbee") + #s(erc--target-channel-local "&Bitlbee" &bitlbee))))) + +(ert-deftest erc-ring-previous-command-base-case () + (ert-info ("Create ring when nonexistent and do nothing") + (let (erc-input-ring + erc-input-ring-index) + (erc-previous-command) + (should (ring-p erc-input-ring)) + (should (zerop (ring-length erc-input-ring))) + (should-not erc-input-ring-index))) + (should-not erc-input-ring)) + +(ert-deftest erc-ring-previous-command () + (with-current-buffer (get-buffer-create "*#fake*") + (erc-mode) + (erc-tests--send-prep) + (setq-local erc-last-input-time 0) + (should-not (local-variable-if-set-p 'erc-send-completed-hook)) + (set (make-local-variable 'erc-send-completed-hook) nil) ; skip t (globals) + ;; Just in case erc-ring-mode is already on + (setq-local erc-pre-send-functions nil) + (add-hook 'erc-pre-send-functions #'erc-add-to-input-ring) + ;; + (cl-letf (((symbol-function 'erc-process-input-line) + (lambda (&rest _) + (insert-before-markers + (erc-display-message-highlight 'notice "echo: one\n")))) + ((symbol-function 'erc-command-no-process-p) + (lambda (&rest _) t))) + (ert-info ("Create ring, populate, recall") + (insert "/one") + (erc-send-current-line) + (should (ring-p erc-input-ring)) + (should (zerop (ring-member erc-input-ring "/one"))) ; equal + (should (save-excursion (forward-line -1) + (looking-at-p "[*]+ echo: one"))) + (should-not erc-input-ring-index) + (erc-bol) + (should (looking-at "$")) + (erc-previous-command) + (erc-bol) + (should (looking-at "/one")) + (should (zerop erc-input-ring-index))) + (ert-info ("Back to one") + (should (= (ring-length erc-input-ring) (1+ erc-input-ring-index))) + (erc-previous-command) + (should-not erc-input-ring-index) + (erc-bol) + (should (looking-at "$")) + (should (equal (ring-ref erc-input-ring 0) "/one"))) + (ert-info ("Swap input after prompt with previous (#bug46339)") + (insert "abc") + (erc-previous-command) + (should (= 1 erc-input-ring-index)) + (erc-bol) + (should (looking-at "/one")) + (should (equal (ring-ref erc-input-ring 0) "abc")) + (should (equal (ring-ref erc-input-ring 1) "/one")) + (erc-next-command) + (erc-bol) + (should (looking-at "abc"))))) + (when noninteractive + (kill-buffer "*#fake*"))) + +(ert-deftest erc-log-irc-protocol () + (should-not erc-debug-irc-protocol) + (with-temp-buffer + (setq erc-server-process (start-process "fake" (current-buffer) "true") + erc-server-current-nick "tester" + erc-session-server "myproxy.localhost" + erc-session-port 6667) + (let ((inhibit-message noninteractive)) + (erc-toggle-debug-irc-protocol) + (erc-log-irc-protocol "PASS changeme\r\n" 'outgoing) + (setq erc-server-announced-name "irc.gnu.org") + (erc-log-irc-protocol ":irc.gnu.org 001 tester :Welcome") + (erc-log-irc-protocol ":irc.gnu.org 002 tester :Your host is irc.gnu.org") + (setq erc-network 'FooNet) + (setq erc-networks--id (erc-networks--id-create nil)) + (erc-log-irc-protocol ":irc.gnu.org 422 tester :MOTD missing") + (setq erc-networks--id (erc-networks--id-create 'BarNet)) + (erc-log-irc-protocol ":irc.gnu.org 221 tester +i") + (set-process-query-on-exit-flag erc-server-process nil))) + (with-current-buffer "*erc-protocol*" + (goto-char (point-min)) + (search-forward "Version") + (search-forward "\r\n\r\n") + (search-forward "myproxy.localhost:6667 >> PASS" (pos-eol)) + (forward-line) + (search-forward "irc.gnu.org << :irc.gnu.org 001" (pos-eol)) + (forward-line) + (search-forward "irc.gnu.org << :irc.gnu.org 002" (pos-eol)) + (forward-line) + (search-forward "FooNet << :irc.gnu.org 422" (pos-eol)) + (forward-line) + (search-forward "BarNet << :irc.gnu.org 221" (pos-eol))) + (when noninteractive + (kill-buffer "*erc-protocol*") + (should-not erc-debug-irc-protocol))) + +(ert-deftest erc--input-line-delim-regexp () + (let ((p erc--input-line-delim-regexp)) + ;; none + (should (equal '("a" "b") (split-string "a\r\nb" p))) + (should (equal '("a" "b") (split-string "a\nb" p))) + (should (equal '("a" "b") (split-string "a\rb" p))) + + ;; one + (should (equal '("") (split-string "" p))) + (should (equal '("a" "" "b") (split-string "a\r\rb" p))) + (should (equal '("a" "" "b") (split-string "a\n\rb" p))) + (should (equal '("a" "" "b") (split-string "a\n\nb" p))) + (should (equal '("a" "" "b") (split-string "a\r\r\nb" p))) + (should (equal '("a" "" "b") (split-string "a\n\r\nb" p))) + (should (equal '("a" "") (split-string "a\n" p))) + (should (equal '("a" "") (split-string "a\r" p))) + (should (equal '("a" "") (split-string "a\r\n" p))) + (should (equal '("" "b") (split-string "\nb" p))) + (should (equal '("" "b") (split-string "\rb" p))) + (should (equal '("" "b") (split-string "\r\nb" p))) + + ;; two + (should (equal '("" "") (split-string "\r" p))) + (should (equal '("" "") (split-string "\n" p))) + (should (equal '("" "") (split-string "\r\n" p))) + + ;; three + (should (equal '("" "" "") (split-string "\r\r" p))) + (should (equal '("" "" "") (split-string "\n\n" p))) + (should (equal '("" "" "") (split-string "\n\r" p))))) + +(ert-deftest erc--blank-in-multiline-input-p () + (let ((check (lambda (s) + (erc--blank-in-multiline-input-p + (split-string s erc--input-line-delim-regexp))))) + + (ert-info ("With `erc-send-whitespace-lines'") + (let ((erc-send-whitespace-lines t)) + (should (funcall check "")) + (should-not (funcall check "\na")) + (should-not (funcall check "/msg a\n")) ; real /cmd + (should-not (funcall check "a\n\nb")) ; "" allowed + (should-not (funcall check "/msg a\n\nb")) ; non-/cmd + (should-not (funcall check " ")) + (should-not (funcall check "\t")) + (should-not (funcall check "a\nb")) + (should-not (funcall check "a\n ")) + (should-not (funcall check "a\n \t")) + (should-not (funcall check "a\n \f")) + (should-not (funcall check "a\n \nb")) + (should-not (funcall check "a\n \t\nb")) + (should-not (funcall check "a\n \f\nb")))) + + (should (funcall check "")) + (should (funcall check " ")) + (should (funcall check "\t")) + (should (funcall check "a\n\nb")) + (should (funcall check "a\n\nb")) + (should (funcall check "a\n ")) + (should (funcall check "a\n \t")) + (should (funcall check "a\n \f")) + (should (funcall check "a\n \nb")) + (should (funcall check "a\n \t\nb")) + + (should-not (funcall check "a\rb")) + (should-not (funcall check "a\nb")) + (should-not (funcall check "a\r\nb")))) + +(defun erc-tests--with-process-input-spy (test) + (with-current-buffer (get-buffer-create "FakeNet") + (let* ((erc-pre-send-functions + (remove #'erc-add-to-input-ring erc-pre-send-functions)) ; for now + (inhibit-message noninteractive) + (erc-server-current-nick "tester") + (erc-last-input-time 0) + erc-accidental-paste-threshold-seconds + erc-send-modify-hook + ;; + calls) + (cl-letf (((symbol-function 'erc-process-input-line) + (lambda (&rest r) (push r calls))) + ((symbol-function 'erc-server-buffer) + (lambda () (current-buffer)))) + (erc-tests--send-prep) + (funcall test (lambda () (pop calls))))) + (when noninteractive (kill-buffer)))) + +(ert-deftest erc--check-prompt-input-functions () + (erc-tests--with-process-input-spy + (lambda (next) + + (ert-info ("Errors when point not in prompt area") ; actually just dings + (insert "/msg #chan hi") + (forward-line -1) + (let ((e (should-error (erc-send-current-line)))) + (should (equal "Point is not in the input area" (cadr e)))) + (goto-char (point-max)) + (ert-info ("Input remains untouched") + (should (save-excursion (erc-bol) (looking-at "/msg #chan hi"))))) + + (ert-info ("Errors when no process running") + (let ((e (should-error (erc-send-current-line)))) + (should (equal "ERC: No process running" (cadr e)))) + (ert-info ("Input remains untouched") + (should (save-excursion (erc-bol) (looking-at "/msg #chan hi"))))) + + (ert-info ("Errors when line contains empty newline") + (erc-bol) + (delete-region (point) (point-max)) + (insert "one\n") + (let ((e (should-error (erc-send-current-line)))) + (should (equal "Blank line - ignoring..." (cadr e)))) + (goto-char (point-max)) + (ert-info ("Input remains untouched") + (should (save-excursion (goto-char erc-input-marker) + (looking-at "one\n"))))) + + (should (= 0 erc-last-input-time)) + (should-not (funcall next))))) + +;; These also indirectly tests `erc-send-input' + +(ert-deftest erc-send-current-line () + (erc-tests--with-process-input-spy + (lambda (next) + (erc-tests--set-fake-server-process "sleep" "1") + (should (= 0 erc-last-input-time)) + + (ert-info ("Simple command") + (insert "/msg #chan hi") + (erc-send-current-line) + (ert-info ("Prompt restored") + (forward-line 0) + (should (looking-at-p erc-prompt))) + (ert-info ("Input cleared") + (erc-bol) + (should (eq (point) (point-max)))) + ;; Commands are forced (no flood protection) + (should (equal (funcall next) '("/msg #chan hi\n" t nil)))) + + (ert-info ("Simple non-command") + (insert "hi") + (erc-send-current-line) + (should (eq (point) (point-max))) + (should (save-excursion (forward-line -1) + (search-forward "<tester> hi"))) + ;; Non-ommands are forced only when `erc-flood-protect' is nil + (should (equal (funcall next) '("hi\n" nil t)))) + + (should (consp erc-last-input-time))))) + +(ert-deftest erc-send-whitespace-lines () + (erc-tests--with-process-input-spy + (lambda (next) + (erc-tests--set-fake-server-process "sleep" "1") + (setq-local erc-send-whitespace-lines t) + + (ert-info ("Multiline hunk with blank line correctly split") + (insert "one\n\ntwo") + (erc-send-current-line) + (ert-info ("Prompt restored") + (forward-line 0) + (should (looking-at-p erc-prompt))) + (ert-info ("Input cleared") + (erc-bol) + (should (eq (point) (point-max)))) + (should (equal (funcall next) '("two\n" nil t))) + (should (equal (funcall next) '("\n" nil t))) + (should (equal (funcall next) '("one\n" nil t)))) + + (ert-info ("Multiline hunk with trailing newline filtered") + (insert "hi\n") + (erc-send-current-line) + (ert-info ("Input cleared") + (erc-bol) + (should (eq (point) (point-max)))) + (should (equal (funcall next) '("hi\n" nil t))) + (should-not (funcall next))) + + (ert-info ("Multiline hunk with trailing carriage filtered") + (insert "hi\r") + (erc-send-current-line) + (ert-info ("Input cleared") + (erc-bol) + (should (eq (point) (point-max)))) + (should (equal (funcall next) '("hi\n" nil t))) + (should-not (funcall next))) + + (ert-info ("Multiline command with trailing blank filtered") + (pcase-dolist (`(,p . ,q) + '(("/a b\r" "/a b\n") ("/a b\n" "/a b\n") + ("/a b\n\n" "/a b\n") ("/a b\r\n" "/a b\n") + ("a b\nc\n\n" "c\n" "a b\n") + ("/a b\nc\n\n" "c\n" "/a b\n") + ("/a b\n\nc\n\n" "c\n" "\n" "/a b\n"))) + (insert p) + (erc-send-current-line) + (erc-bol) + (should (eq (point) (point-max))) + (while q + (should (equal (funcall next) (list (pop q) nil t)))) + (should-not (funcall next)))) + + (ert-info ("Multiline hunk with trailing whitespace not filtered") + (insert "there\n ") + (erc-send-current-line) + (should (equal (funcall next) '(" \n" nil t))) + (should (equal (funcall next) '("there\n" nil t))) + (should-not (funcall next)))))) + +(ert-deftest erc--check-prompt-input-for-excess-lines () + (ert-info ("Without `erc-inhibit-multiline-input'") + (should-not erc-inhibit-multiline-input) + (should-not (erc--check-prompt-input-for-excess-lines "" '("a" "b")))) + + (ert-info ("With `erc-inhibit-multiline-input' as t (2)") + (let ((erc-inhibit-multiline-input t)) + (should-not (erc--check-prompt-input-for-excess-lines "" '("a"))) + (should-not (erc--check-prompt-input-for-excess-lines "" '("a" ""))) + (should (erc--check-prompt-input-for-excess-lines "" '("a" "b"))))) + + (ert-info ("With `erc-inhibit-multiline-input' as 3") + (let ((erc-inhibit-multiline-input 3)) + (should-not (erc--check-prompt-input-for-excess-lines "" '("a" "b"))) + (should-not (erc--check-prompt-input-for-excess-lines "" '("a" "b" ""))) + (should (erc--check-prompt-input-for-excess-lines "" '("a" "b" "c"))))) + + (ert-info ("With `erc-ask-about-multiline-input'") + (let ((erc-inhibit-multiline-input t) + (erc-ask-about-multiline-input t)) + (ert-simulate-keys '(?n ?\r ?y ?\r) + (should (erc--check-prompt-input-for-excess-lines "" '("a" "b"))) + (should-not (erc--check-prompt-input-for-excess-lines "" '("a" "b"))))) + (should-not erc-ask-about-multiline-input))) + +;; The point of this test is to ensure output is handled identically +;; regardless of whether a command handler is summoned. + +(ert-deftest erc-process-input-line () + (let (erc-server-last-sent-time + erc-server-flood-queue + (orig-erc-cmd-MSG (symbol-function 'erc-cmd-MSG)) + (erc-default-recipients '("#chan")) + calls) + (with-temp-buffer + (cl-letf (((symbol-function 'erc-cmd-MSG) + (lambda (line) + (push line calls) + (funcall orig-erc-cmd-MSG line))) + ((symbol-function 'erc-server-buffer) + (lambda () (current-buffer))) + ((symbol-function 'erc-server-process-alive) + (lambda () t)) + ((symbol-function 'erc-server-send-queue) + #'ignore)) + + (ert-info ("Dispatch to user command handler") + + (ert-info ("Baseline") + (erc-process-input-line "/msg #chan hi\n") + (should (equal (pop calls) " #chan hi")) + (should (equal (pop erc-server-flood-queue) + '("PRIVMSG #chan :hi\r\n" . utf-8)))) + + (ert-info ("Quote preserves line intact") + (erc-process-input-line "/QUOTE FAKE foo bar\n") + (should (equal (pop erc-server-flood-queue) + '("FAKE foo bar\r\n" . utf-8)))) + + (ert-info ("Unknown command respected") + (erc-process-input-line "/FAKE foo bar\n") + (should (equal (pop erc-server-flood-queue) + '("FAKE foo bar\r\n" . utf-8)))) + + (ert-info ("Spaces preserved") + (erc-process-input-line "/msg #chan hi you\n") + (should (equal (pop calls) " #chan hi you")) + (should (equal (pop erc-server-flood-queue) + '("PRIVMSG #chan :hi you\r\n" . utf-8)))) + + (ert-info ("Empty line honored") + (erc-process-input-line "/msg #chan\n") + (should (equal (pop calls) " #chan")) + (should (equal (pop erc-server-flood-queue) + '("PRIVMSG #chan :\r\n" . utf-8))))) + + (ert-info ("Implicit cmd via `erc-send-input-line-function'") + + (ert-info ("Baseline") + (erc-process-input-line "hi\n") + (should (equal (pop erc-server-flood-queue) + '("PRIVMSG #chan :hi\r\n" . utf-8)))) + + (ert-info ("Spaces preserved") + (erc-process-input-line "hi you\n") + (should (equal (pop erc-server-flood-queue) + '("PRIVMSG #chan :hi you\r\n" . utf-8)))) + + (ert-info ("Empty line transmitted with injected-space kludge") + (erc-process-input-line "\n") + (should (equal (pop erc-server-flood-queue) + '("PRIVMSG #chan : \r\n" . utf-8)))) + + (should-not calls)))))) + +;; Note: if adding an erc-backend-tests.el, please relocate this there. + +(ert-deftest erc-message () + (should-not erc-server-last-peers) + (let (server-proc + calls + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (cl-letf (((symbol-function 'erc-display-message) + (lambda (_ _ _ line) (push line calls))) + ((symbol-function 'erc-server-send) + (lambda (line _) (push line calls))) + ((symbol-function 'erc-server-buffer) + (lambda () (process-buffer server-proc)))) + (with-current-buffer (get-buffer-create "ExampleNet") + (erc-mode) + (setq erc-server-current-nick "tester" + server-proc (start-process "sleep" (current-buffer) "sleep" "1") + erc-server-process server-proc + erc-server-last-peers (cons nil nil) + erc-server-users (make-hash-table :test 'equal) + erc-network 'ExampleNet) + (set-process-query-on-exit-flag erc-server-process nil)) + + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "ExampleNet")) + erc-default-recipients '("#chan") + erc-channel-users (make-hash-table :test 'equal) + erc-network 'ExampleNet) + (erc-update-current-channel-member "alice" "alice") + (erc-update-current-channel-member "tester" "tester")) + + (with-current-buffer "ExampleNet" + (erc-server-PRIVMSG erc-server-process + (make-erc-response + :sender "alice!~u@fsf.org" + :command "PRIVMSG" + :command-args '("#chan" "hi") + :unparsed ":alice!~u@fsf.org PRIVMSG #chan :hi")) + (should (equal erc-server-last-peers '("alice"))) + (should (string-match "<alice>" (pop calls)))) + + (with-current-buffer "#chan" + (ert-info ("Shortcuts usable in target buffers") + (should-not (local-variable-p 'erc-server-last-peers)) + (should-not erc-server-last-peers) + (erc-message "PRIVMSG" ". hi") + (should-not erc-server-last-peers) + (should (eq 'no-target (pop calls))) + (erc-message "PRIVMSG" ", hi") + (should-not erc-server-last-peers) + (should (string-match "alice :hi" (pop calls))))) + + (with-current-buffer "ExampleNet" + (ert-info ("Shortcuts local in server bufs") + (should (equal erc-server-last-peers '("alice" . "alice"))) + (erc-message "PRIVMSG" ", hi") + (should (equal erc-server-last-peers '("alice" . "alice"))) + (should (string-match "PRIVMSG alice :hi" (pop calls))) + (setcdr erc-server-last-peers "bob") + (erc-message "PRIVMSG" ". hi") + (should (equal erc-server-last-peers '("alice" . "bob"))) + (should (string-match "PRIVMSG bob :hi" (pop calls))))) + + (with-current-buffer "#chan" + (ert-info ("Non-shortcuts are local to server buffer") + (should-not (local-variable-p 'erc-server-last-peers)) + (should-not erc-server-last-peers) + (erc-message "PRIVMSG" "#chan hola") + (should-not erc-server-last-peers) + (should-not (default-value 'erc-server-last-peers)) + (should (equal (buffer-local-value 'erc-server-last-peers + (get-buffer "ExampleNet")) + '("alice" . "#chan"))) + (should (string-match "hola" (pop calls)))))) + + (should-not erc-server-last-peers) + (should-not calls) + (kill-buffer "ExampleNet") + (kill-buffer "#chan"))) + +;;; erc-tests.el ends here diff --git a/test/lisp/erc/erc-track-tests.el b/test/lisp/erc/erc-track-tests.el index 6e36ed4071b..475a436bb1d 100644 --- a/test/lisp/erc/erc-track-tests.el +++ b/test/lisp/erc/erc-track-tests.el @@ -1,6 +1,6 @@ -;;; erc-track-tests.el --- Tests for erc-track. +;;; erc-track-tests.el --- Tests for erc-track. -*- lexical-binding:t -*- -;; Copyright (C) 2016-2017 Free Software Foundation, Inc. +;; Copyright (C) 2016-2022 Free Software Foundation, Inc. ;; Author: Mario Lang <mlang@delysid.org> ;; Author: Vivek Dasmohapatra <vivek@etla.org> @@ -24,7 +24,6 @@ (require 'ert) (require 'erc-track) -(require 'font-core) (ert-deftest erc-track--shorten-aggressive-nil () "Test non-aggressive erc track buffer name shortening." @@ -107,8 +106,8 @@ (ert-deftest erc-track--erc-faces-in () "`erc-faces-in' should pick up both 'face and 'font-lock-face properties." - (let ((str0 "is bold") - (str1 "is bold")) + (let ((str0 (copy-sequence "is bold")) + (str1 (copy-sequence "is bold"))) ;; Turn on Font Lock mode: this initialize `char-property-alias-alist' ;; to '((face font-lock-face)). Note that `font-lock-mode' don't ;; turn on the mode if the test is run on batch mode or if the @@ -120,3 +119,5 @@ '(bold erc-current-nick-face) str1) (should (erc-faces-in str0)) (should (erc-faces-in str1)) )) + +;;; erc-track-tests.el ends here diff --git a/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld b/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld new file mode 100644 index 00000000000..35a9a570b6d --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld @@ -0,0 +1,44 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :barnet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Wed, 28 Apr 2021 06:59:59 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@xrir8fpe4d7ak.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :joe @mike tester") + (0 ":irc.barnet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:25] mike: Belike, for joy the emperor hath a son.") + (0 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:27] joe: Protest their first of manhood.") + (0 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:29] mike: As frozen water to a starved snake.") + (0 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:34] joe: My mirth it much displeas'd, but pleas'd my woe.") + (0 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:38] mike: Why, Marcus, no man should be mad but I.") + (0 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:44] joe: Faith, I have heard too much, for your words and performances are no kin together.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":irc.barnet.org NOTICE tester :[07:00:01] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((mode 6 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1619593200") + (0.25 ":joe!~u@svpn88yjcdj42.irc PRIVMSG #chan :mike: But, in defense, by mercy, 'tis most just.") + (0.25 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :joe: The Marshal of France, Monsieur la Far.") + (0.25 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :mike: And bide the penance of each three years' day.") + (0.25 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :joe: Madam, within; but never man so chang'd.") + (0.25 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :mike: As much in private, and I'll bid adieu.")) diff --git a/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld b/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld new file mode 100644 index 00000000000..58df79e19fa --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld @@ -0,0 +1,48 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Wed, 28 Apr 2021 07:00:00 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@nvfhxvqm92rm6.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice @bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:02] alice: Here come the lovers, full of joy and mirth.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:07] bob: According to the fool's bolt, sir, and such dulcet diseases.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:10] alice: And hang himself. I pray you, do my greeting.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:18] bob: And you sat smiling at his cruel prey.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:21] alice: Or never after look me in the face.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:25] bob: If that may be, than all is well. Come, sit down, every mother's son, and rehearse your parts. Pyramus, you begin: when you have spoken your speech, enter into that brake; and so every one according to his cue.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:30] alice: Where I espied the panther fast asleep.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:32] bob: Alas! he is too young: yet he looks successfully.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:37] alice: Here, at your lordship's service.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:42] bob: By my troth, and in good earnest, and so God mend me, and by all pretty oaths that are not dangerous, if you break one jot of your promise or come one minute behind your hour, I will think you the most pathetical break-promise, and the most hollow lover, and the most unworthy of her you call Rosalind, that may be chosen out of the gross band of the unfaithful. Therefore, beware my censure, and keep your promise.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":irc.foonet.org NOTICE tester :[07:00:32] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 6 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1619593200") + (0.9 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: Grows, lives, and dies, in single blessedness.") + (0.25 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :alice: For these two hours, Rosalind, I will leave thee.") + (0.25 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: By this hand, it will not kill a fly. But come, now I will be your Rosalind in a more coming-on disposition; and ask me what you will, I will grant it.") + (0.25 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :alice: That I must love a loathed enemy.") + (0.25 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: As't please your lordship: I'll leave you.")) diff --git a/test/lisp/erc/resources/base/assoc/bumped/again.eld b/test/lisp/erc/resources/base/assoc/bumped/again.eld new file mode 100644 index 00000000000..ab3c7b06214 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/bumped/again.eld @@ -0,0 +1,30 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.0 ":irc.foonet.org 433 * tester :Nickname is reserved by a different account") + (0.0 ":irc.foonet.org FAIL NICK NICKNAME_RESERVED tester :Nickname is reserved by a different account")) + +((nick 3 "NICK tester`") + (0.1 ":irc.foonet.org 001 tester` :Welcome to the foonet IRC Network tester`") + (0.0 ":irc.foonet.org 002 tester` :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") + (0.0 ":irc.foonet.org 003 tester` :This server was created Fri, 24 Sep 2021 01:38:36 UTC") + (0.0 ":irc.foonet.org 004 tester` irc.foonet.org oragono-2.6.1-937b9b02368748e5 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.foonet.org 005 tester` AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.1 ":irc.foonet.org 005 tester` MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.1 ":irc.foonet.org 005 tester` draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 tester` :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 tester` 0 :IRC Operators online") + (0.0 ":irc.foonet.org 253 tester` 0 :unregistered connections") + (0.0 ":irc.foonet.org 254 tester` 1 :channels formed") + (0.0 ":irc.foonet.org 255 tester` :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 tester` 3 3 :Current local users 3, max 3") + (0.2 ":irc.foonet.org 266 tester` 3 3 :Current global users 3, max 3") + (0.0 ":irc.foonet.org 422 tester` :MOTD File is missing")) + +((mode-user 3.2 "MODE tester` +i") + (0.0 ":irc.foonet.org 221 tester` +i") + (0.0 ":irc.foonet.org NOTICE tester` :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((privmsg 42.6 "PRIVMSG NickServ :IDENTIFY tester changeme") + (0.01 ":tester`!~u@rpaau95je67ci.irc NICK tester") + (0.0 ":NickServ!NickServ@localhost NOTICE tester :You're now logged in as tester")) diff --git a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld new file mode 100644 index 00000000000..5c36e58d9d3 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld @@ -0,0 +1,30 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") + (0.0 ":irc.foonet.org 003 tester :This server was created Fri, 24 Sep 2021 01:38:36 UTC") + (0.0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.1-937b9b02368748e5 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.01 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.0 ":irc.foonet.org 254 tester 1 :channels formed") + (0.0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.0 ":irc.foonet.org 221 tester +i") + (0.0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((privmsg 17.21 "PRIVMSG bob :hi") + (0.02 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :hola") + (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG tester :how r u?")) + +((quit 18.19 "QUIT :" quit) + (0.01 ":tester!~u@rpaau95je67ci.irc QUIT :Quit: " quit)) +((drop 1 DROP)) diff --git a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld new file mode 100644 index 00000000000..33e4168ac46 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld @@ -0,0 +1,31 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.1 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy") + (0.0 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") + (0.0 ":irc.foonet.org 003 dummy :This server was created Fri, 24 Sep 2021 01:38:36 UTC") + (0.0 ":irc.foonet.org 004 dummy irc.foonet.org oragono-2.6.1-937b9b02368748e5 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.foonet.org 005 dummy AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.1 ":irc.foonet.org 005 dummy MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.1 ":irc.foonet.org 005 dummy draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 dummy :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 dummy 0 :IRC Operators online") + (0.0 ":irc.foonet.org 253 dummy 0 :unregistered connections") + (0.0 ":irc.foonet.org 254 dummy 1 :channels formed") + (0.0 ":irc.foonet.org 255 dummy :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 dummy 3 3 :Current local users 3, max 3") + (0.2 ":irc.foonet.org 266 dummy 3 3 :Current global users 3, max 3") + ;; Could arrive anytime around this point + (0.0 ":tester!~u@rpaau95je67ci.irc NICK :dummy") + (0.0 ":irc.foonet.org 422 dummy :MOTD File is missing") + ;; Playback + (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG dummy :back?") + ) + +((mode-user 1.2 "MODE dummy +i") + (0.0 ":irc.foonet.org 221 dummy +i") + (0.0 ":irc.foonet.org NOTICE dummy :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((renick 42.6 "NICK tester") + (0.01 ":dummy!~u@rpaau95je67ci.irc NICK tester") + (0.0 ":NickServ!NickServ@localhost NOTICE dummy :You're now logged in as tester")) diff --git a/test/lisp/erc/resources/base/assoc/bumped/renicked.eld b/test/lisp/erc/resources/base/assoc/bumped/renicked.eld new file mode 100644 index 00000000000..4e96fd73045 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/bumped/renicked.eld @@ -0,0 +1,30 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.1-937b9b02368748e5") + (0.0 ":irc.foonet.org 003 tester :This server was created Fri, 24 Sep 2021 01:38:36 UTC") + (0.0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.1-937b9b02368748e5 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.01 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.0 ":irc.foonet.org 254 tester 1 :channels formed") + (0.0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 12 "MODE tester +i") + (0.0 ":irc.foonet.org 221 tester +i") + (0.0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((privmsg 17.21 "PRIVMSG NickServ :REGISTER changeme") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Account created") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :You're now logged in as tester")) + +((quit 18.19 "QUIT :" quit) + (0.01 ":tester!~u@rpaau95je67ci.irc QUIT :Quit: " quit)) +((drop 1 DROP)) diff --git a/test/lisp/erc/resources/base/assoc/multi-net/barnet.eld b/test/lisp/erc/resources/base/assoc/multi-net/barnet.eld new file mode 100644 index 00000000000..c62a22a11c7 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/multi-net/barnet.eld @@ -0,0 +1,42 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Tue, 04 May 2021 05:06:19 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 253 tester 0 :unregistered connections") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 8 "MODE tester +i") + (0 ":irc.barnet.org 221 tester +i") + (0 ":irc.barnet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 2 "JOIN #chan") + (0 ":tester!~u@jnu48g2wrycbw.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :@mike joe tester") + (0 ":irc.barnet.org 366 tester #chan :End of NAMES list")) + +((mode 2 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620104779") + (0.1 ":mike!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":joe!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":mike!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :joe: Whipp'd first, sir, and hang'd after.") + (0.1 ":joe!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :mike: We have yet many among us can gripe as hard as Cassibelan; I do not say I am one, but I have a hand. Why tribute ? why should we pay tribute ? If C sar can hide the sun from us with a blanket, or put the moon in his pocket, we will pay him tribute for light; else, sir, no more tribute, pray you now.") + (0.1 ":mike!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :joe: Double and treble admonition, and still forfeit in the same kind ? This would make mercy swear, and play the tyrant.") + (0.1 ":joe!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :mike: And secretly to greet the empress' friends.") + (0.1 ":mike!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :joe: You have not been inquired after: I have sat here all day.") + (0.1 ":joe!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :mike: That same Berowne I'll torture ere I go.") + (0.1 ":mike!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :joe: For mine own part,no offence to the general, nor any man of quality,I hope to be saved.") + (0.1 ":joe!~u@kd7gmjbnbkn8c.irc PRIVMSG #chan :mike: Mehercle! if their sons be ingenuous, they shall want no instruction; if their daughters be capable, I will put it to them. But, vir sapit qui pauca loquitur. A soul feminine saluteth us.")) diff --git a/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld b/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld new file mode 100644 index 00000000000..f30b7deca11 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 8 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 2 "JOIN #chan") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 2 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Signior Iachimo will not from it. Pray, let us follow 'em.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Our queen and all her elves come here anon.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: The ground is bloody; search about the churchyard.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: You have discharged this honestly: keep it to yourself. Many likelihoods informed me of this before, which hung so tottering in the balance that I could neither believe nor misdoubt. Pray you, leave me: stall this in your bosom; and I thank you for your honest care. I will speak with you further anon.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Give me that mattock, and the wrenching iron.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Stand you! You have land enough of your own; but he added to your having, gave you some ground.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: And will you, being a man of your breeding, be married under a bush, like a beggar ? Get you to church, and have a good priest that can tell you what marriage is: this fellow will but join you together as they join wainscot; then one of you will prove a shrunk panel, and like green timber, warp, warp.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Live, and be prosperous; and farewell, good fellow.")) diff --git a/test/lisp/erc/resources/base/assoc/reconplay/again.eld b/test/lisp/erc/resources/base/assoc/reconplay/again.eld new file mode 100644 index 00000000000..4210c07e41a --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/reconplay/again.eld @@ -0,0 +1,42 @@ +;; -*- mode: lisp-data; -*- +((pass 4.0 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0.0 ":irc.foonet.org 003 tester :This server was created Wed, 16 Jun 2021 04:15:00 UTC") + (0.0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.foonet.org 254 tester 1 :channels formed") + (0.0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10 "MODE tester +i") + ;; No mode answer + (0.0 ":tester!~u@mw6kegwt77kwe.irc JOIN #chan") + (0.0 ":irc.foonet.org 353 tester = #chan :alice @bob tester") + (0.0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0.0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0.0 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:37:52] bob: Thou pout'st upon thy fortune and thy love.") + (0.0 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:37:56] alice: With these mortals on the ground.") + (0.0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1623816901") + (0.1 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :alice: My name, my good lord, is Parolles.") + (0.1 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :bob: Wilt thou rest damned ? God help thee, shallow man! God make incision in thee! thou art raw.")) + +((privmsg 3.0 "PRIVMSG *status :help") + (0.0 ":*status!znc@znc.in PRIVMSG tester :In the following list all occurrences of <#chan> support wildcards (* and ?) except ListNicks") + (0.0 ":*status!znc@znc.in PRIVMSG tester :\2Version\17: Print which version of ZNC this is") + (0.0 ":*status!znc@znc.in PRIVMSG tester :\2Shutdown [message]\17: Shut down ZNC completely") + (0.0 ":*status!znc@znc.in PRIVMSG tester :\2Restart [message]\17: Restart ZNC") + (0.1 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :alice: In that word's death; no words can that woe sound.") + (0.1 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :bob: Look, sir, here comes the lady towards my cell.")) diff --git a/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld b/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld new file mode 100644 index 00000000000..f916fea2374 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld @@ -0,0 +1,52 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0.0 ":irc.foonet.org 003 tester :This server was created Wed, 16 Jun 2021 04:15:00 UTC") + (0.0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.foonet.org 254 tester 1 :channels formed") + (0.0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 5 "MODE tester +i") + ;; No mode answer + (0.0 ":irc.znc.in 306 tester :You have been marked as being away") + (0.0 ":tester!~u@mw6kegwt77kwe.irc JOIN #chan") + (0.0 ":irc.foonet.org 353 tester = #chan :alice @bob tester") + (0.0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0.0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0.0 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:35:50] bob: To Laced mon did my land extend.") + (0.0 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:35:55] alice: This is but a custom in your tongue; you bear a graver purpose, I hope.") + (0.0 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:37:16] bob: To imitate them; faults that are rich are fair.") + (0.0 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:37:18] alice: Our Romeo hath not been in bed to-night.") + (0.0 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:37:21] bob: But, in defense, by mercy, 'tis most just.") + (0.0 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :[10:37:25] alice: Younger than she are happy mothers made.") + (0.0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0.0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 3 "MODE #chan") + (1.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1623816901") + (0.1 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :bob: At thy good heart's oppression.") + (0.1 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :alice: But purgatory, torture, hell itself.")) + +((privmsg 3 "PRIVMSG *status :help") + (0.0 ":*status!znc@znc.in PRIVMSG tester :In the following list all occurrences of <#chan> support wildcards (* and ?) except ListNicks") + (0.0 ":*status!znc@znc.in PRIVMSG tester :\2AddPort <[+]port> <ipv4|ipv6|all> <web|irc|all> [bindhost [uriprefix]]\17: Add another port for ZNC to listen on") + (0.0 ":*status!znc@znc.in PRIVMSG tester :\2DelPort <port> <ipv4|ipv6|all> [bindhost]\17: Remove a port from ZNC") + (0.0 ":*status!znc@znc.in PRIVMSG tester :\2Rehash\17: Reload global settings, modules, and listeners from znc.conf") + (0.1 ":alice!~u@mw6kegwt77kwe.irc PRIVMSG #chan :bob: And at my suit, sweet, pardon what is past.") + (0.1 ":bob!~u@mw6kegwt77kwe.irc PRIVMSG #chan :alice: My lord, you give me most egregious indignity.")) + +((quit 2 "QUIT :\2ERC\2")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/assoc/samenet/chester.eld b/test/lisp/erc/resources/base/assoc/samenet/chester.eld new file mode 100644 index 00000000000..0132de677cb --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/samenet/chester.eld @@ -0,0 +1,40 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :changeme")) +((nick 1 "NICK chester")) +((user 1 "USER user 0 * :chester") + (0 ":irc.foonet.org 001 chester :Welcome to the foonet IRC Network chester") + (0 ":irc.foonet.org 002 chester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 chester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 chester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 chester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 chester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 chester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 chester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 chester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 chester 1 :unregistered connections") + (0 ":irc.foonet.org 254 chester 1 :channels formed") + (0 ":irc.foonet.org 255 chester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 chester 3 4 :Current local users 3, max 4") + (0 ":irc.foonet.org 266 chester 3 4 :Current global users 3, max 4") + (0 ":irc.foonet.org 422 chester :MOTD File is missing")) + +((mode-user 12 "MODE chester +i") + (0 ":irc.foonet.org 221 chester +i") + (0 ":chester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 chester = #chan :tester chester @alice bob") + (0 ":irc.foonet.org 366 chester #chan :End of NAMES list") + (0 ":irc.foonet.org NOTICE chester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 chester #chan +nt") + (0.0 ":irc.foonet.org 329 chester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Dispatch, I say, and find the forester.") + (0.0 ":tester!~u@yuvqisyu7m7qs.irc QUIT :Quit: " quit) + (0.5 ":tester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome again!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome again!")) + +((quit 20 "QUIT :" quit) + (0.0 ":chester!~u@yuvqisyu7m7qs.irc QUIT :Quit: " quit)) diff --git a/test/lisp/erc/resources/base/assoc/samenet/tester.eld b/test/lisp/erc/resources/base/assoc/samenet/tester.eld new file mode 100644 index 00000000000..995fab00f7d --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/samenet/tester.eld @@ -0,0 +1,42 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 12 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 15 "JOIN #chan") + (0 ":tester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :tester @alice bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome!") + (0.0 ":chester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Dispatch, I say, and find the forester.")) + +((quit 4 "QUIT ")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/assoc/samenet/tester2.eld b/test/lisp/erc/resources/base/assoc/samenet/tester2.eld new file mode 100644 index 00000000000..33a05fe2611 --- /dev/null +++ b/test/lisp/erc/resources/base/assoc/samenet/tester2.eld @@ -0,0 +1,39 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 4.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":tester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :tester @alice bob chester") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((~useless-join 10 "JOIN #chan")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome again!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome again!")) + +((quit 4 "QUIT :" quit) + (0 ":tester!~u@yuvqisyu7m7qs.irc QUIT :Quit: " quit)) + +((linger 5 LINGER)) diff --git a/test/lisp/erc/resources/base/auth-source/foonet.eld b/test/lisp/erc/resources/base/auth-source/foonet.eld new file mode 100644 index 00000000000..1fe772c7e23 --- /dev/null +++ b/test/lisp/erc/resources/base/auth-source/foonet.eld @@ -0,0 +1,23 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) diff --git a/test/lisp/erc/resources/base/auth-source/nopass.eld b/test/lisp/erc/resources/base/auth-source/nopass.eld new file mode 100644 index 00000000000..3fdb4ecf7bc --- /dev/null +++ b/test/lisp/erc/resources/base/auth-source/nopass.eld @@ -0,0 +1,22 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) diff --git a/test/lisp/erc/resources/base/channel-buffer-revival/foonet.eld b/test/lisp/erc/resources/base/channel-buffer-revival/foonet.eld new file mode 100644 index 00000000000..b09692327c7 --- /dev/null +++ b/test/lisp/erc/resources/base/channel-buffer-revival/foonet.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 12 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 6 "JOIN #chan") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 8 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Signior Iachimo will not from it. Pray, let us follow 'em.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Our queen and all her elves come here anon.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: The ground is bloody; search about the churchyard.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: You have discharged this honestly: keep it to yourself. Many likelihoods informed me of this before, which hung so tottering in the balance that I could neither believe nor misdoubt. Pray you, leave me: stall this in your bosom; and I thank you for your honest care. I will speak with you further anon.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Give me that mattock, and the wrenching iron.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Stand you! You have land enough of your own; but he added to your having, gave you some ground.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: And will you, being a man of your breeding, be married under a bush, like a beggar ? Get you to church, and have a good priest that can tell you what marriage is: this fellow will but join you together as they join wainscot; then one of you will prove a shrunk panel, and like green timber, warp, warp.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Live, and be prosperous; and farewell, good fellow.")) diff --git a/test/lisp/erc/resources/base/flood/soju.eld b/test/lisp/erc/resources/base/flood/soju.eld new file mode 100644 index 00000000000..05266ca9411 --- /dev/null +++ b/test/lisp/erc/resources/base/flood/soju.eld @@ -0,0 +1,87 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.13 ":soju.im 001 tester :Welcome to soju, tester") + (0.0 ":soju.im 002 tester :Your host is soju.im") + (0.0 ":soju.im 004 tester soju.im soju aiwroO OovaimnqpsrtklbeI") + (0.0 ":soju.im 005 tester CHATHISTORY=1000 CASEMAPPING=ascii NETWORK=Soju :are supported") + (0.0 ":soju.im 422 tester :No MOTD")) + +((mode 1 "MODE tester +i") + (0.0 ":tester!tester@10.0.2.100 JOIN #chan/foonet") + (0.25 ":soju.im 331 tester #chan/foonet :No topic is set") + (0.0 ":soju.im 353 tester = #chan/foonet :@bob/foonet alice/foonet tester") + (0.01 ":soju.im 366 tester #chan/foonet :End of /NAMES list") + (0.0 ":tester!tester@10.0.2.100 JOIN #chan/barnet") + (0.04 ":soju.im 331 tester #chan/barnet :No topic is set") + (0.0 ":soju.im 353 tester = #chan/barnet :tester @mike/barnet joe/barnet") + (0.01 ":soju.im 366 tester #chan/barnet :End of /NAMES list") + (0.01 ":bob/foonet PRIVMSG #chan/foonet :alice: Then this breaking of his has been but a try for his friends.") + (0.16 ":alice/foonet PRIVMSG #chan/foonet :bob: By my troth, I take my young lord to be a very melancholy man.") + (0.91 ":bob/foonet PRIVMSG #chan/foonet :alice: No, truly, for the truest poetry is the most feigning; and lovers are given to poetry, and what they swear in poetry may be said as lovers they do feign.") + (0.01 ":alice/foonet PRIVMSG #chan/foonet :bob: Sir, his wife some two months since fled from his house: her pretence is a pilgrimage to Saint Jaques le Grand; which holy undertaking with most austere sanctimony she accomplished; and, there residing, the tenderness of her nature became as a prey to her grief; in fine, made a groan of her last breath, and now she sings in heaven.") + (0.0 ":mike/barnet PRIVMSG #chan/barnet :joe: Who ? not the duke ? yes, your beggar of fifty, and his use was to put a ducat in her clack-dish; the duke had crotchets in him. He would be drunk too; that let me inform you.") + (0.01 ":joe/barnet PRIVMSG #chan/barnet :mike: Prove it before these varlets here, thou honourable man, prove it.") + (0.0 ":mike/barnet PRIVMSG #chan/barnet :joe: That my report is just and full of truth.") + (0.0 ":joe/barnet PRIVMSG #chan/barnet :mike: It is impossible they bear it out.") + ;; Expected, since we blindly send +i + (0.0 ":soju.im 501 tester :Cannot change user mode in multi-upstream mode")) + +((~mode-foonet 5 "MODE #chan/foonet") + (0.0 ":soju.im 324 tester #chan/foonet +nt") + (0.16 ":soju.im 329 tester #chan/foonet 1647158643") + ;; Start frantic pinging + (0.0 "PING :soju-msgid-1")) + +((~mode-barnet 5 "MODE #chan/barnet") + (0.0 ":soju.im 324 tester #chan/barnet +nt") + (0.0 ":soju.im 329 tester #chan/barnet 1647158643")) + +((pong-1 5 "PONG :soju-msgid-1") + (0.0 ":bob/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :alice: The king's coming; I know by his trumpets. Sirrah, inquire further after me; I had talk of you last night: though you are a fool and a knave, you shall eat: go to, follow.") + (0.0 ":mike/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :joe: Up: so. How is 't ? Feel you your legs ? You stand.") + (0.0 ":alice/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :bob: Consider then we come but in despite.") + (0.1 "PING :soju-msgid-2")) + +((pong-2 2 "PONG :soju-msgid-2") + (0.1 ":joe/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :mike: All hail, Macbeth! that shalt be king hereafter.") + (0.1 "PING :soju-msgid-3")) + +((pong-3 2 "PONG :soju-msgid-3") + (0.1 ":bob/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :alice: And that at my bidding you could so stand up.") + (0.1 "PING :soju-msgid-4")) + +((pong-4 2 "PONG :soju-msgid-4") + (0.03 ":mike/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :joe: Now he tells how she plucked him to my chamber. O! I see that nose of yours, but not the dog I shall throw it to.") + (0.1 "PING :soju-msgid-5")) + +((pong-5 2 "PONG :soju-msgid-5") + (0.1 ":alice/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :bob: For policy sits above conscience.") + (0.1 "PING :soju-msgid-6")) + +((pong-6 2 "PONG :soju-msgid-6") + (0.0 ":joe/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :mike: Take heed o' the foul fiend. Obey thy parents; keep thy word justly; swear not; commit not with man's sworn spouse; set not thy sweet heart on proud array. Tom's a-cold.") + (0.1 "PING :soju-msgid-7")) + +((pong-7 2 "PONG :soju-msgid-7") + (0.08 ":mike/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :joe: To suffer with him. Good love, call him back.") + (0.1 "PING :soju-msgid-8")) + +((pong-9 2 "PONG :soju-msgid-8") + (0.1 ":bob/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :alice: Be not obdurate, open thy deaf ears.") + (0.0 "PING :soju-msgid-9")) + +((pong-10 2 "PONG :soju-msgid-9") + (0.04 ":joe/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :mike: To get good guard and go along with me.") + (0.1 "PING :soju-msgid-10")) + +((~privmsg 2 "PRIVMSG #chan/foonet :alice: hi") + (0.1 ":alice/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :tester: Good, very good; it is so then: good, very good. Let it be concealed awhile.")) + +((pong-11 2 "PONG :soju-msgid-10") + (0.1 ":alice/foonet!~u@g56t7uz8xjj4e.irc PRIVMSG #chan/foonet :bob: Some man or other must present Wall; and let him have some plaster, or some loam, or some rough-cast about him, to signify wall; and let him hold his fingers thus, and through that cranny shall Pyramus and Thisby whisper.") + (0.0 "PING :soju-msgid-11")) + +((pong-12 5 "PONG :soju-msgid-11") + (0.1 ":mike/barnet!~u@qsidzk5cytcai.irc PRIVMSG #chan/barnet :joe: That's he that was Othello; here I am.")) diff --git a/test/lisp/erc/resources/base/gapless-connect/barnet.eld b/test/lisp/erc/resources/base/gapless-connect/barnet.eld new file mode 100644 index 00000000000..4e658802ef6 --- /dev/null +++ b/test/lisp/erc/resources/base/gapless-connect/barnet.eld @@ -0,0 +1,40 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :barnet:changeme")) +((nick 10 "NICK tester")) +((user 0.2 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.5.1-4860c5cad0179db1") + (0 ":irc.barnet.org 003 tester :This server was created Fri, 19 Mar 2021 10:23:19 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.5.1-4860c5cad0179db1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m INVEX KICKLEN=390 MAXLIST=beI:60 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 1 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 0 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 1 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 1 1 :Current local users 1, max 1") + (0 ":irc.barnet.org 266 tester 1 1 :Current global users 1, max 1") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@8cgjyczyrjgby.irc JOIN #bar") + (0 ":irc.barnet.org 353 tester = #bar :@mike joe tester") + (0 ":irc.barnet.org 366 tester #bar :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #bar :Buffer Playback...") + (0 ":joe!~u@8cgjyczyrjgby.irc PRIVMSG #bar :[10:23:28] tester, welcome!") + (0 ":mike!~u@8cgjyczyrjgby.irc PRIVMSG #bar :[10:23:28] tester, welcome!") + (0 ":joe!~u@8cgjyczyrjgby.irc PRIVMSG #bar :[10:24:49] mike: Bid me farewell, and let me hear thee going.") + (0 ":mike!~u@8cgjyczyrjgby.irc PRIVMSG #bar :[10:24:54] joe: By heaven, thy love is black as ebony.") + (0 ":***!znc@znc.in PRIVMSG #bar :Playback Complete.") + (0 ":irc.barnet.org NOTICE tester :[10:23:22] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((mode 20 "MODE #bar") + (0 ":irc.barnet.org 324 tester #bar +nt") + (0 ":irc.barnet.org 329 tester #bar 1616149403") + (0.1 ":mike!~u@8cgjyczyrjgby.irc PRIVMSG #bar :joe: To ask of whence you are: report it.") + (0.1 ":joe!~u@8cgjyczyrjgby.irc PRIVMSG #bar :mike: Friar, thou knowest not the duke so well as I do: he's a better woodman than thou takest him for.") + (0.1 ":mike!~u@8cgjyczyrjgby.irc PRIVMSG #bar :joe: Like the sequel, I. Signior Costard, adieu.") + (0.1 ":joe!~u@8cgjyczyrjgby.irc PRIVMSG #bar :mike: This is his second fit; he had one yesterday.")) diff --git a/test/lisp/erc/resources/base/gapless-connect/foonet.eld b/test/lisp/erc/resources/base/gapless-connect/foonet.eld new file mode 100644 index 00000000000..4ac4a3e5968 --- /dev/null +++ b/test/lisp/erc/resources/base/gapless-connect/foonet.eld @@ -0,0 +1,41 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Sun, 25 Apr 2021 11:28:28 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@xrir8fpe4d7ak.irc JOIN #foo") + (0 ":irc.foonet.org 353 tester = #foo :joe @mike tester") + (0 ":irc.foonet.org 366 tester #foo :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #foo :Buffer Playback...") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :[07:02:41] bob: To-morrow is the joyful day, Audrey; to-morrow will we be married.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :[07:02:44] alice: Why dost thou call them knaves ? thou know'st them not.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :[07:03:05] bob: Now, by the faith of my love, I will: tell me where it is.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :[07:03:09] alice: Give me the letter; I will look on it.") + (0 ":***!znc@znc.in PRIVMSG #foo :Playback Complete.") + (0 ":irc.foonet.org NOTICE tester :[11:29:00] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 8 "MODE #foo") + (0 ":irc.foonet.org 324 tester #foo +nt") + (0 ":irc.foonet.org 329 tester #foo 1619593200") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: By this hand, it will not kill a fly. But come, now I will be your Rosalind in a more coming-on disposition; and ask me what you will, I will grant it.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: That I must love a loathed enemy.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: His discretion, I am sure, cannot carry his valour, for the goose carries not the fox. It is well: leave it to his discretion, and let us listen to the moon.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: As living here and you no use of him.")) diff --git a/test/lisp/erc/resources/base/gapless-connect/pass-stub.eld b/test/lisp/erc/resources/base/gapless-connect/pass-stub.eld new file mode 100644 index 00000000000..0c8dfd19d00 --- /dev/null +++ b/test/lisp/erc/resources/base/gapless-connect/pass-stub.eld @@ -0,0 +1,4 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :" token ":changeme")) + +((fake 1 "FAKE no op")) diff --git a/test/lisp/erc/resources/base/mask-target-routing/foonet.eld b/test/lisp/erc/resources/base/mask-target-routing/foonet.eld new file mode 100644 index 00000000000..796d5566b65 --- /dev/null +++ b/test/lisp/erc/resources/base/mask-target-routing/foonet.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Mon, 31 May 2021 09:56:24 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 2 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@gq7yjr7gsu7nn.irc JOIN #foo") + (0 ":irc.foonet.org 353 tester = #foo :alice @bob rando tester") + (0 ":irc.foonet.org 366 tester #foo :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #foo :Buffer Playback...") + (0 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:02] bob: All that he is hath reference to your highness.") + (0 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:06] alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0 ":***!znc@znc.in PRIVMSG #foo :Playback Complete.") + (0 ":irc.foonet.org NOTICE tester :[09:56:57] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 5 "MODE #foo") + (0 ":irc.foonet.org 324 tester #foo +nt") + (0 ":irc.foonet.org 329 tester #foo 1622454985") + ;; Invalid msg + (0.1 ":rando!~u@em2i467d4ejul.irc PRIVMSG :") + (0.1 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :bob: Farewell, pretty lady: you must hold the credit of your father.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc NOTICE $* :[Global notice] going down soon.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #foo :bob: Well, this is the forest of Arden.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc NOTICE $$* :[Global notice] this is a warning.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #foo :bob: Be married under a bush, like a beggar ? Get you to church, and have a good priest that can tell you what marriage is: this fellow will but join you together as they join wainscot; then one of you will prove a shrunk panel, and like green timber, warp, warp.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG $* :[Global msg] second warning.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #foo :bob: And will you, being a man of your breeding.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc NOTICE #* :[Global notice] final warning.")) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld new file mode 100644 index 00000000000..e2fe1430283 --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet-again.eld @@ -0,0 +1,50 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :barnet:changeme")) +((nick 3 "NICK tester")) +((user 3 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Wed, 12 May 2021 07:41:08 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer ^ + + (0 ":tester!~u@xrir8fpe4d7ak.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :joe @mike tester") + (0 ":irc.barnet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:25] mike: Belike, for joy the emperor hath a son.") + (0 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:27] joe: Protest their first of manhood.") + (0 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:29] mike: As frozen water to a starved snake.") + (0 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:34] joe: My mirth it much displeas'd, but pleas'd my woe.") + (0 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:38] mike: Why, Marcus, no man should be mad but I.") + (0 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :[07:04:44] joe: Faith, I have heard too much, for your words and performances are no kin together.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":irc.barnet.org NOTICE tester :[07:00:01] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((~join 3 "JOIN #chan")) + +((mode 5 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620805269") + (0.1 ":joe!~u@svpn88yjcdj42.irc PRIVMSG #chan :mike: But, in defense, by mercy, 'tis most just.") + (0.1 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :joe: The Marshal of France, Monsieur la Far.") + (0.1 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :mike: And bide the penance of each three years' day.") + (0.1 ":mike!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :joe: Madam, within; but never man so chang'd.") + (0.1 ":joe!~u@xrir8fpe4d7ak.irc PRIVMSG #chan :mike: As much in private, and I'll bid adieu.")) + +((linger 10 LINGER)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld new file mode 100644 index 00000000000..2c3d297b9cf --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld @@ -0,0 +1,41 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :barnet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Wed, 12 May 2021 07:41:08 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((join 1 "JOIN #chan") + (0 ":tester!~u@awyxgybtkx7uq.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :@joe mike tester") + (0 ":irc.barnet.org 366 tester #chan :End of NAMES list") + (0.1 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :tester, welcome!") + (0 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :tester, welcome!")) + +((mode 1 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620805269") + (0.1 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: But you have outfaced them all.") + (0.1 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: Why, will shall break it; will, and nothing else.") + (0.1 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: Yes, a dozen; and as many to the vantage, as would store the world they played for.") + (0.05 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: As he regards his aged father's life.") + (0.05 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonour in doing it.")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld new file mode 100644 index 00000000000..abfcc6ed481 --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld @@ -0,0 +1,41 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :barnet:changeme")) +((nick 3 "NICK tester")) +((user 3 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Wed, 12 May 2021 07:41:08 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((join 1 "JOIN #chan") + (0 ":tester!~u@awyxgybtkx7uq.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :@joe mike tester") + (0 ":irc.barnet.org 366 tester #chan :End of NAMES list") + (0.1 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :tester, welcome!") + (0 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :tester, welcome!")) + +((mode 3 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620805269") + (0.1 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: But you have outfaced them all.") + (0.1 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: Why, will shall break it; will, and nothing else.") + (0.1 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: Yes, a dozen; and as many to the vantage, as would store the world they played for.") + (0.05 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: As he regards his aged father's life.") + (0.05 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonour in doing it.")) + +((linger 1 LINGER)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld new file mode 100644 index 00000000000..bf8712305a4 --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet-again.eld @@ -0,0 +1,50 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :foonet:changeme")) +((nick 3 "NICK tester")) +((user 3 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Wed, 12 May 2021 07:41:09 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":tester!~u@nvfhxvqm92rm6.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice @bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:02] alice: Here come the lovers, full of joy and mirth.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:07] bob: According to the fool's bolt, sir, and such dulcet diseases.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:10] alice: And hang himself. I pray you, do my greeting.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:18] bob: And you sat smiling at his cruel prey.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:21] alice: Or never after look me in the face.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:25] bob: If that may be, than all is well. Come, sit down, every mother's son, and rehearse your parts. Pyramus, you begin: when you have spoken your speech, enter into that brake; and so every one according to his cue.") + (0 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:30] alice: Where I espied the panther fast asleep.") + (0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :[07:04:32] bob: Alas! he is too young: yet he looks successfully.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + + (0 ":irc.foonet.org NOTICE tester :[07:00:32] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((~join 3 "JOIN #chan")) + +((mode 8 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620805271") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: Grows, lives, and dies, in single blessedness.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :alice: For these two hours, Rosalind, I will leave thee.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: By this hand, it will not kill a fly. But come, now I will be your Rosalind in a more coming-on disposition; and ask me what you will, I will grant it.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #chan :alice: That I must love a loathed enemy.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #chan :bob: As't please your lordship: I'll leave you.")) + +((linger 10 LINGER)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet-drop.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet-drop.eld new file mode 100644 index 00000000000..b99621cc311 --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet-drop.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Wed, 12 May 2021 07:41:09 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((join 1 "JOIN #chan") + (0 ":tester!~u@ertp7idh9jtgi.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :@alice bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :tester, welcome!") + (0 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :tester, welcome!")) + +((mode 1 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620805271") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: He cannot be heard of. Out of doubt he is transported.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: More evident than this; for this was stol'n.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Sell when you can; you are not for all markets.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: There's the fool hangs on your back already.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Why, if you have a stomach to't, monsieur, if you think your mystery in stratagem can bring this instrument of honor again into its native quarter, be magnanimous in the enterprise and go on; I will grace the attempt for a worthy exploit: if you speed well in it, the duke shall both speak of it, and extend to you what further becomes his greatness, even to the utmost syllable of your worthiness.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: For he hath still been tried a holy man.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: To have the touches dearest priz'd.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: And must advise the emperor for his good.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Orlando, my liege; the youngest son of Sir Rowland de Boys.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: The ape is dead, and I must conjure him.")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld new file mode 100644 index 00000000000..b0964fb9537 --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :foonet:changeme")) +((nick 3 "NICK tester")) +((user 3 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Wed, 12 May 2021 07:41:09 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 4.2 "MODE tester +i") + ;; No mode answer ^ + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((join 1 "JOIN #chan") + (0 ":tester!~u@ertp7idh9jtgi.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :@alice bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :tester, welcome!") + (0 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :tester, welcome!")) + +((mode 3 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620805271") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: He cannot be heard of. Out of doubt he is transported.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: More evident than this; for this was stol'n.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Sell when you can; you are not for all markets.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: There's the fool hangs on your back already.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Why, if you have a stomach to't, monsieur, if you think your mystery in stratagem can bring this instrument of honor again into its native quarter, be magnanimous in the enterprise and go on; I will grace the attempt for a worthy exploit: if you speed well in it, the duke shall both speak of it, and extend to you what further becomes his greatness, even to the utmost syllable of your worthiness.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: For he hath still been tried a holy man.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: To have the touches dearest priz'd.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: And must advise the emperor for his good.") + (0.1 ":alice!~u@ertp7idh9jtgi.irc PRIVMSG #chan :bob: Orlando, my liege; the youngest son of Sir Rowland de Boys.") + (0.1 ":bob!~u@ertp7idh9jtgi.irc PRIVMSG #chan :alice: The ape is dead, and I must conjure him.")) + +((linger 1 LINGER)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/stub-again.eld b/test/lisp/erc/resources/base/netid/bouncer/stub-again.eld new file mode 100644 index 00000000000..c666ee4fa0f --- /dev/null +++ b/test/lisp/erc/resources/base/netid/bouncer/stub-again.eld @@ -0,0 +1,4 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :" token ":changeme")) + +((fake 1 "FAKE no op")) diff --git a/test/lisp/erc/resources/base/netid/samenet/chester.eld b/test/lisp/erc/resources/base/netid/samenet/chester.eld new file mode 100644 index 00000000000..7b4bfee9c9a --- /dev/null +++ b/test/lisp/erc/resources/base/netid/samenet/chester.eld @@ -0,0 +1,48 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :changeme")) +((nick 1 "NICK chester")) +((user 1 "USER user 0 * :chester") + (0 ":irc.foonet.org 001 chester :Welcome to the foonet IRC Network chester") + (0 ":irc.foonet.org 002 chester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 chester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 chester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 chester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 chester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 chester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 chester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 chester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 chester 1 :unregistered connections") + (0 ":irc.foonet.org 254 chester 1 :channels formed") + (0 ":irc.foonet.org 255 chester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 chester 3 4 :Current local users 3, max 4") + (0 ":irc.foonet.org 266 chester 3 4 :Current global users 3, max 4") + (0 ":irc.foonet.org 422 chester :MOTD File is missing")) + +((mode-user 10.2 "MODE chester +i") + (0 ":irc.foonet.org 221 chester +i") + (0 ":irc.foonet.org NOTICE chester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 14 "JOIN #chan") + (0 ":chester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 chester = #chan :tester chester @alice bob") + (0 ":irc.foonet.org 366 chester #chan :End of NAMES list")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 chester #chan +nt") + (0.0 ":irc.foonet.org 329 chester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: That ever eye with sight made heart lament.") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: The bitter past, more welcome is the sweet.") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Dispatch, I say, and find the forester.") + (0.1 ":tester!~u@yuvqisyu7m7qs.irc PRIVMSG #chan :chester: hi") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: This was lofty! Now name the rest of the players. This is Ercles' vein, a tyrant's vein; a lover is more condoling.")) + +((privmsg 4 "PRIVMSG #chan :hi tester") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: As the ox hath his bow, sir, the horse his curb, and the falcon her bells, so man hath his desires; and as pigeons bill, so wedlock would be nibbling.") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: Most friendship is feigning, most loving mere folly.") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: To employ you towards this Roman. Come, our queen.")) + +((quit 5 "QUIT :" quit) + (0.0 ":tester!~u@yuvqisyu7m7qs.irc QUIT :Quit: " quit) + (0.0 ":chester!~u@yuvqisyu7m7qs.irc QUIT :Quit: " quit)) diff --git a/test/lisp/erc/resources/base/netid/samenet/tester.eld b/test/lisp/erc/resources/base/netid/samenet/tester.eld new file mode 100644 index 00000000000..f41b041db4b --- /dev/null +++ b/test/lisp/erc/resources/base/netid/samenet/tester.eld @@ -0,0 +1,52 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 15 "JOIN #chan") + (0 ":tester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :tester @alice bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Marry, that, I think, be young Petruchio.") + (0.4 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: You speak of him when he was less furnished than now he is with that which makes him both without and within.") + (0.2 ":chester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: That ever eye with sight made heart lament.") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: The bitter past, more welcome is the sweet.") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Dispatch, I say, and find the forester.")) + +((privmsg 3 "PRIVMSG #chan :chester: hi") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: This was lofty! Now name the rest of the players. This is Ercles' vein, a tyrant's vein; a lover is more condoling.") + (0.1 ":chester!~u@yuvqisyu7m7qs.irc PRIVMSG #chan :hi tester") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: As the ox hath his bow, sir, the horse his curb, and the falcon her bells, so man hath his desires; and as pigeons bill, so wedlock would be nibbling.") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: Most friendship is feigning, most loving mere folly.") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: To employ you towards this Roman. Come, our queen.")) + +((quit 4 "QUIT :" quit) + (0 ":tester!~u@yuvqisyu7m7qs.irc QUIT :Quit: " quit)) diff --git a/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld b/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld new file mode 100644 index 00000000000..8e299ec44c0 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld @@ -0,0 +1,28 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :changeme")) +((nick 1 "NICK tester")) + +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (-0.02 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (-0.02 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (-0.02 ":irc.foonet.org 253 tester 0 :unregistered connections") + (-0.02 ":irc.foonet.org 254 tester 1 :channels formed") + (-0.02 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (-0.02 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (-0.02 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (-0.02 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((~mode-user 3.2 "MODE tester +i") + (-0.02 ":irc.foonet.org 221 tester +i") + (-0.02 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((~join 10 "JOIN #chan")) +((eof 5 EOF)) +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/reconnect/aborted.eld b/test/lisp/erc/resources/base/reconnect/aborted.eld new file mode 100644 index 00000000000..5c32070d85f --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/aborted.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 12 "JOIN #chan") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 10 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Signior Iachimo will not from it. Pray, let us follow 'em.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Our queen and all her elves come here anon.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: The ground is bloody; search about the churchyard.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: You have discharged this honestly: keep it to yourself. Many likelihoods informed me of this before, which hung so tottering in the balance that I could neither believe nor misdoubt. Pray you, leave me: stall this in your bosom; and I thank you for your honest care. I will speak with you further anon.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Give me that mattock, and the wrenching iron.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Stand you! You have land enough of your own; but he added to your having, gave you some ground.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: And will you, being a man of your breeding, be married under a bush, like a beggar ? Get you to church, and have a good priest that can tell you what marriage is: this fellow will but join you together as they join wainscot; then one of you will prove a shrunk panel, and like green timber, warp, warp.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Live, and be prosperous; and farewell, good fellow.")) diff --git a/test/lisp/erc/resources/base/reconnect/options-again.eld b/test/lisp/erc/resources/base/reconnect/options-again.eld new file mode 100644 index 00000000000..f1fcc439cc3 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/options-again.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is still in debug mode.")) + +((~join-chan 12 "JOIN #chan") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((~join-spam 12 "JOIN #spam") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #spam") + (0 ":irc.foonet.org 353 tester = #spam :alice tester @bob") + (0 ":irc.foonet.org 366 tester #spam :End of NAMES list")) + +((~mode-chan 4 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.")) + +((mode-spam 4 "MODE #spam") + (0 ":irc.foonet.org 324 tester #spam +nt") + (0 ":irc.foonet.org 329 tester #spam 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #spam :alice: Signior Iachimo will not from it. Pray, let us follow 'em.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #spam :bob: Our queen and all her elves come here anon.")) diff --git a/test/lisp/erc/resources/base/reconnect/options.eld b/test/lisp/erc/resources/base/reconnect/options.eld new file mode 100644 index 00000000000..3b305d85594 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/options.eld @@ -0,0 +1,35 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode.") + + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode-chan 4 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/reconnect/timer-last.eld b/test/lisp/erc/resources/base/reconnect/timer-last.eld new file mode 100644 index 00000000000..23849bc1bad --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/timer-last.eld @@ -0,0 +1,6 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.znc.in 464 tester :Invalid Password")) +((linger 1 LINGER)) diff --git a/test/lisp/erc/resources/base/reconnect/timer.eld b/test/lisp/erc/resources/base/reconnect/timer.eld new file mode 100644 index 00000000000..95c6af8d880 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/timer.eld @@ -0,0 +1,6 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.znc.in 464 tester :Invalid Password")) +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/renick/queries/bouncer-barnet.eld b/test/lisp/erc/resources/base/renick/queries/bouncer-barnet.eld new file mode 100644 index 00000000000..0c8cdac0379 --- /dev/null +++ b/test/lisp/erc/resources/base/renick/queries/bouncer-barnet.eld @@ -0,0 +1,54 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :barnet:changeme")) +((nick 3 "NICK tester")) +((user 3 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Tue, 01 Jun 2021 07:49:23 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@286u8jcpis84e.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :@joe mike rando tester") + (0 ":irc.barnet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":joe!~u@286u8jcpis84e.irc PRIVMSG #chan :[09:19:19] mike: Chi non te vede, non te pretia.") + (0 ":mike!~u@286u8jcpis84e.irc PRIVMSG #chan :[09:19:28] joe: The valiant heart's not whipt out of his trade.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":rando!~u@95i756tt32ym8.irc PRIVMSG tester :[09:18:20] Why'd you pull that scene at the arcade?") + (0 ":rando!~u@95i756tt32ym8.irc PRIVMSG tester :[09:18:32] I had to mess up this rentacop came after me with nunchucks.") + (0 ":irc.barnet.org NOTICE tester :[09:13:24] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((mode 5 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1622538742") + (0.1 ":joe!~u@286u8jcpis84e.irc PRIVMSG #chan :mike: By favors several which they did bestow.") + (0.1 ":mike!~u@286u8jcpis84e.irc PRIVMSG #chan :joe: You, Roderigo! come, sir, I am for you.")) + +((privmsg-a 5 "PRIVMSG rando :Linda said you were gonna kill me.") + (0.1 ":joe!~u@286u8jcpis84e.irc PRIVMSG #chan :mike: Play, music, then! Nay, you must do it soon.") + (0.1 ":rando!~u@95i756tt32ym8.irc PRIVMSG tester :Linda said? I never saw her before I came up here.") + (0.1 ":mike!~u@286u8jcpis84e.irc PRIVMSG #chan :joe: Of arts inhibited and out of warrant.")) + +((privmsg-b 3 "PRIVMSG rando :You aren't with Wage?") + (0.1 ":joe!~u@286u8jcpis84e.irc PRIVMSG #chan :mike: But most of all, agreeing with the proclamation.") + (0.1 ":rando!~u@95i756tt32ym8.irc PRIVMSG tester :I think you screwed up, Case.") + (0.1 ":mike!~u@286u8jcpis84e.irc PRIVMSG #chan :joe: Good gentleman, go your gait, and let poor volk pass. An chud ha' bin zwaggered out of my life, 'twould not ha' bin zo long as 'tis by a vortnight. Nay, come not near th' old man; keep out, che vor ye, or ise try whether your costard or my ballow be the harder. Chill be plain with you.") + ;; Nick change + (0.1 ":rando!~u@95i756tt32ym8.irc NICK frenemy") + (0.1 ":joe!~u@286u8jcpis84e.irc PRIVMSG #chan :mike: Till time beget some careful remedy.") + (0.1 ":frenemy!~u@95i756tt32ym8.irc PRIVMSG tester :I showed up and you just fit me right into your reality picture.") + (0.1 ":mike!~u@286u8jcpis84e.irc PRIVMSG #chan :joe: For I have lost him on a dangerous sea.")) diff --git a/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld b/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld new file mode 100644 index 00000000000..162e8bf9655 --- /dev/null +++ b/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld @@ -0,0 +1,52 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 01 Jun 2021 07:49:22 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 5.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@u4mvbswyw8gbg.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice @bob rando tester") + (0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":bob!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :[09:19:28] alice: Great men should drink with harness on their throats.") + (0 ":alice!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :[09:19:31] bob: Your lips will feel them the sooner: shallow again. A more sounder instance; come.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":rando!~u@bivkhq8yav938.irc PRIVMSG tester :[09:17:51] u thur?") + (0 ":rando!~u@bivkhq8yav938.irc PRIVMSG tester :[09:17:58] guess not") + (0 ":irc.foonet.org NOTICE tester :[09:12:53] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 10 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1622538742") + (0.1 ":bob!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :alice: When there is nothing living but thee, thou shalt be welcome. I had rather be a beggar's dog than Apemantus.") + (0.1 ":alice!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :bob: You have simply misused our sex in your love-prate: we must have your doublot and hose plucked over your head, and show the world what the bird hath done to her own nest.")) + +((privmsg-a 6 "PRIVMSG rando :I here") + (0.1 ":bob!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :alice: And I will make thee think thy swan a crow.") + (0.1 ":rando!~u@bivkhq8yav938.irc PRIVMSG tester :u are dumb") + (0.1 ":alice!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :bob: Lie not, to say mine eyes are murderers.")) + +((privmsg-b 3 "PRIVMSG rando :not so") + (0.1 ":bob!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :alice: Commit myself, my person, and the cause.") + ;; Nick change + (0.1 ":rando!~u@bivkhq8yav938.irc NICK frenemy") + (0.1 ":alice!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :bob: Of raging waste! It cannot hold; it will not.") + (0.1 ":frenemy!~u@bivkhq8yav938.irc PRIVMSG tester :doubly so") + (0.1 ":bob!~u@u4mvbswyw8gbg.irc PRIVMSG #chan :alice: These words are razors to my wounded heart.")) diff --git a/test/lisp/erc/resources/base/renick/queries/solo.eld b/test/lisp/erc/resources/base/renick/queries/solo.eld new file mode 100644 index 00000000000..12fa7d264e9 --- /dev/null +++ b/test/lisp/erc/resources/base/renick/queries/solo.eld @@ -0,0 +1,55 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Mon, 31 May 2021 09:56:24 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 2 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 8 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@gq7yjr7gsu7nn.irc JOIN #foo") + (0 ":irc.foonet.org 353 tester = #foo :alice @bob Lal tester") + (0 ":irc.foonet.org 366 tester #foo :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #foo :Buffer Playback...") + (0 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:02] bob: All that he is hath reference to your highness.") + (0 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:06] alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0 ":***!znc@znc.in PRIVMSG #foo :Playback Complete.") + (0 ":irc.foonet.org NOTICE tester :[09:56:57] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 1 "MODE #foo") + (0 ":irc.foonet.org 324 tester #foo +nt") + (0 ":irc.foonet.org 329 tester #foo 1622454985") + (0.1 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :bob: Farewell, pretty lady: you must hold the credit of your father.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :alice: On Thursday, sir ? the time is very short.")) + +((privmsg-a 10 "PRIVMSG #foo :hi") + (0.2 ":Lal!~u@b82mytupn2t5k.irc PRIVMSG tester :hello") + (0.2 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :alice: And brought to yoke, the enemies of Rome.") + (0.2 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :bob: Thou art thy father's daughter; there's enough.")) + +((privmsg-b 10 "PRIVMSG Lal :hi") + (0.2 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :alice: Here are the beetle brows shall blush for me.") + (0.2 ":Lal!~u@b82mytupn2t5k.irc NICK Linguo") + (0.2 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :bob: He hath abandoned his physicians, madam; under whose practices he hath persecuted time with hope, and finds no other advantage in the process but only the losing of hope by time.")) + +((privmsg-c 10 "PRIVMSG Linguo :howdy Linguo") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :alice: And brought to yoke, the enemies of Rome.") + (0.2 ":Linguo!~u@b82mytupn2t5k.irc PART #foo")) + +((part 10 "PART #foo :\2ERC\2") + (0 ":tester!~u@gq7yjr7gsu7nn.irc PART #foo :\2ERC\2") + (0.1 ":Linguo!~u@b82mytupn2t5k.irc PRIVMSG tester :get along little doggie")) diff --git a/test/lisp/erc/resources/base/renick/self/auto.eld b/test/lisp/erc/resources/base/renick/self/auto.eld new file mode 100644 index 00000000000..851db7f1cf7 --- /dev/null +++ b/test/lisp/erc/resources/base/renick/self/auto.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the FooNet Internet Relay Chat Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org[188.240.145.101/6697], running version solanum-1.0-dev") + (0 ":irc.foonet.org 003 tester :This server was created Sat May 22 2021 at 19:04:17 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org solanum-1.0-dev DGQRSZaghilopsuwz CFILMPQSbcefgijklmnopqrstuvz bkloveqjfI") + (0 ":irc.foonet.org 005 tester WHOX FNC KNOCK SAFELIST ELIST=CTU CALLERID=g MONITOR=100 ETRACE CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz :are supported by this server") + (0 ":irc.foonet.org 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=foonet STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server") + (0 ":irc.foonet.org 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz CLIENTVER=3.0 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 33 users and 14113 invisible on 17 servers") + (0 ":irc.foonet.org 252 tester 34 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 12815 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 726 clients and 1 servers") + (0 ":irc.foonet.org 265 tester 726 739 :Current local users 726, max 739") + (0 ":irc.foonet.org 266 tester 14146 14541 :Current global users 14146, max 14541") + (0 ":irc.foonet.org 250 tester :Highest connection count: 740 (739 clients) (3790 connections received)") + (0 ":tester!~u@gq7yjr7gsu7nn.irc NICK :dummy") + (0 ":irc.foonet.org 375 dummy :- irc.foonet.org Message of the Day - ") + (0 ":irc.foonet.org 372 dummy :- This server provided by NORDUnet/SUNET") + (0 ":irc.foonet.org 372 dummy :- Welcome to foonet, the IRC network for free & open-source software") + (0 ":irc.foonet.org 372 dummy :- and peer directed projects.") + (0 ":irc.foonet.org 372 dummy :- ") + (0 ":irc.foonet.org 372 dummy :- Please visit us in #libera for questions and support.") + (0 ":irc.foonet.org 376 dummy :End of /MOTD command.")) + +((mode-user 10.2 "MODE dummy +i") + (0 ":dummy!~u@gq7yjr7gsu7nn.irc MODE dummy :+RZi") + (0 ":irc.znc.in 306 dummy :You have been marked as being away") + (0 ":dummy!~u@gq7yjr7gsu7nn.irc JOIN #foo") + + (0 ":irc.foonet.org 353 dummy = #foo :alice @bob Lal dummy") + (0 ":irc.foonet.org 366 dummy #foo :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #foo :Buffer Playback...") + (0 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:02] bob: All that he is hath reference to your highness.") + (0 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:06] alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0 ":***!znc@znc.in PRIVMSG #foo :Playback Complete.") + (0 ":irc.foonet.org NOTICE dummy :[09:56:57] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 dummy :You are no longer marked as being away")) + +((mode 10 "MODE #foo") + (0 ":irc.foonet.org 324 dummy #foo +nt") + (0 ":irc.foonet.org 329 dummy #foo 1622454985") + (0.1 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :bob: Farewell, pretty lady: you must hold the credit of your father.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :alice: On Thursday, sir ? the time is very short.")) diff --git a/test/lisp/erc/resources/base/renick/self/manual.eld b/test/lisp/erc/resources/base/renick/self/manual.eld new file mode 100644 index 00000000000..dd107b806d5 --- /dev/null +++ b/test/lisp/erc/resources/base/renick/self/manual.eld @@ -0,0 +1,50 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the FooNet Internet Relay Chat Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org[188.240.145.101/6697], running version solanum-1.0-dev") + (0 ":irc.foonet.org 003 tester :This server was created Sat May 22 2021 at 19:04:17 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org solanum-1.0-dev DGQRSZaghilopsuwz CFILMPQSbcefgijklmnopqrstuvz bkloveqjfI") + (0 ":irc.foonet.org 005 tester WHOX FNC KNOCK SAFELIST ELIST=CTU CALLERID=g MONITOR=100 ETRACE CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz :are supported by this server") + (0 ":irc.foonet.org 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=foonet STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server") + (0 ":irc.foonet.org 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz CLIENTVER=3.0 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 33 users and 14113 invisible on 17 servers") + (0 ":irc.foonet.org 252 tester 34 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 12815 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 726 clients and 1 servers") + (0 ":irc.foonet.org 265 tester 726 739 :Current local users 726, max 739") + (0 ":irc.foonet.org 266 tester 14146 14541 :Current global users 14146, max 14541") + (0 ":irc.foonet.org 250 tester :Highest connection count: 740 (739 clients) (3790 connections received)") + (0 ":irc.foonet.org 375 tester :- irc.foonet.org Message of the Day - ") + (0 ":irc.foonet.org 372 tester :- This server provided by NORDUnet/SUNET") + (0 ":irc.foonet.org 372 tester :- Welcome to foonet, the IRC network for free & open-source software") + (0 ":irc.foonet.org 372 tester :- and peer directed projects.") + (0 ":irc.foonet.org 372 tester :- ") + (0 ":irc.foonet.org 372 tester :- Please visit us in #libera for questions and support.") + (0 ":irc.foonet.org 376 tester :End of /MOTD command.")) + +((mode-user 1.2 "MODE tester +i") + (0 ":tester!~u@gq7yjr7gsu7nn.irc MODE tester :+RZi") + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@gq7yjr7gsu7nn.irc JOIN #foo") + + (0 ":irc.foonet.org 353 tester = #foo :alice @bob Lal tester") + (0 ":irc.foonet.org 366 tester #foo :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #foo :Buffer Playback...") + (0 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:02] bob: All that he is hath reference to your highness.") + (0 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :[10:00:06] alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0 ":***!znc@znc.in PRIVMSG #foo :Playback Complete.") + (0 ":irc.foonet.org NOTICE tester :[09:56:57] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 1 "MODE #foo") + (0 ":irc.foonet.org 324 tester #foo +nt") + (0 ":irc.foonet.org 329 tester #foo 1622454985") + (0.1 ":alice!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :bob: Farewell, pretty lady: you must hold the credit of your father.") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :alice: On Thursday, sir ? the time is very short.")) + +((nick 2 "NICK dummy") + (0 ":tester!~u@gq7yjr7gsu7nn.irc NICK :dummy") + (0.1 ":dummy!~u@gq7yjr7gsu7nn.irc MODE dummy :+RZi") + (0.1 ":bob!~u@gq7yjr7gsu7nn.irc PRIVMSG #foo :dummy: Hi.")) diff --git a/test/lisp/erc/resources/base/renick/self/qual-chester.eld b/test/lisp/erc/resources/base/renick/self/qual-chester.eld new file mode 100644 index 00000000000..75b50fe68bd --- /dev/null +++ b/test/lisp/erc/resources/base/renick/self/qual-chester.eld @@ -0,0 +1,40 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK chester")) +((user 1 "USER user 0 * :chester") + (0 ":irc.foonet.org 001 chester :Welcome to the foonet IRC Network chester") + (0 ":irc.foonet.org 002 chester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 chester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 chester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 chester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 chester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 chester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 chester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 chester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 chester 1 :unregistered connections") + (0 ":irc.foonet.org 254 chester 1 :channels formed") + (0 ":irc.foonet.org 255 chester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 chester 3 4 :Current local users 3, max 4") + (0 ":irc.foonet.org 266 chester 3 4 :Current global users 3, max 4") + (0 ":irc.foonet.org 422 chester :MOTD File is missing")) + +((mode-user 1.2 "MODE chester +i") + (0 ":irc.foonet.org 221 chester +i") + (0 ":irc.foonet.org NOTICE chester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 14 "JOIN #chan") + (0 ":chester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 chester = #chan :tester chester @alice bob") + (0 ":irc.foonet.org 366 chester #chan :End of NAMES list")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 chester #chan +nt") + (0.0 ":irc.foonet.org 329 chester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0 ":tester!~u@gq7yjr7gsu7nn.irc NICK :dummy") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: That ever eye with sight made heart lament.") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: The bitter past, more welcome is the sweet.") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Dispatch, I say, and find the forester.")) + +((linger 10 LINGER)) diff --git a/test/lisp/erc/resources/base/renick/self/qual-tester.eld b/test/lisp/erc/resources/base/renick/self/qual-tester.eld new file mode 100644 index 00000000000..25199226658 --- /dev/null +++ b/test/lisp/erc/resources/base/renick/self/qual-tester.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Sun, 13 Jun 2021 05:45:20 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 15 "JOIN #chan") + (0 ":tester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :tester @alice bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 10 "MODE #chan") + (0.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1623563121") + (0.0 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome!") + (0.0 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Marry, that, I think, be young Petruchio.") + (0.4 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: You speak of him when he was less furnished than now he is with that which makes him both without and within.") + (0.2 ":chester!~u@yuvqisyu7m7qs.irc JOIN #chan") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :chester, welcome!")) + +((nick 5 "NICK dummy") + (0 ":tester!~u@gq7yjr7gsu7nn.irc NICK :dummy") + (0.1 ":dummy!~u@gq7yjr7gsu7nn.irc MODE dummy :+RZi") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: That ever eye with sight made heart lament.") + (0.1 ":alice!~u@wyb9b355rgzi8.irc PRIVMSG #chan :bob: The bitter past, more welcome is the sweet.") + (0.1 ":bob!~u@wyb9b355rgzi8.irc PRIVMSG #chan :alice: Dispatch, I say, and find the forester.")) diff --git a/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld b/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld new file mode 100644 index 00000000000..efc2506fd6f --- /dev/null +++ b/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld @@ -0,0 +1,68 @@ +;; -*- mode: lisp-data; -*- +((pass 3 "PASS :barnet:changeme")) +((nick 3 "NICK tester")) +((user 3 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Wed, 05 May 2021 09:05:33 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@wvys46tx8tpmk.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :joe @mike tester") + (0 ":irc.barnet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:16] joe: Tush! none but minstrels like of sonneting.") + (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:19] mike: Prithee, nuncle, be contented; 'tis a naughty night to swim in. Now a little fire in a wide field were like an old lecher's heart; a small spark, all the rest on's body cold. Look! here comes a walking fire.") + (0 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:22] joe: My name is Edgar, and thy father's son.") + (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:26] mike: Good my lord, be good to me; your honor is accounted a merciful man; good my lord.") + (0 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:31] joe: Thy child shall live, and I will see it nourish'd.") + (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :[09:09:33] mike: Quick, quick; fear nothing; I'll be at thy elbow.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":irc.barnet.org NOTICE tester :[09:05:35] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) + +((mode 3 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620205534") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: That will be given to the loudest noise we make.") + (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: If it please your honor, I am the poor duke's constable, and my name is Elbow: I do lean upon justice, sir; and do bring in here before your good honor two notorious benefactors.") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Following the signs, woo'd but the sign of she.") + (0.5 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: That, sir, which I will not report after her.") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Boyet, prepare: I will away to-night.") + (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: If the man be a bachelor, sir, I can; but if he be a married man, he is his wife's head, and I can never cut off a woman's head.") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Thyself upon thy virtues, they on thee.") + (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: Arm it in rags, a pigmy's straw doth pierce it.")) + +((part 5.1 "PART #chan :" quit) + (0 ":tester!~u@wvys46tx8tpmk.irc PART #chan :" quit)) + +((join 10.1 "JOIN #chan") + (0 ":tester!~u@wvys46tx8tpmk.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :@mike joe tester") + (0 ":irc.barnet.org 366 tester #chan :End of NAMES list") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :tester, welcome!") + (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :tester, welcome!")) + +((mode 1 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620205534") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Chi non te vede, non te pretia.") + (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: Well, if ever thou dost fall from this faith, thou wilt prove a notable argument.") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Of heavenly oaths, vow'd with integrity.") + (0.1 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :mike: These herblets shall, which we upon you strew.") + (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :joe: Aaron will have his soul black like his face.")) + +((linger 0.5 LINGER)) diff --git a/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld b/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld new file mode 100644 index 00000000000..a11cfac2e73 --- /dev/null +++ b/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld @@ -0,0 +1,66 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Wed, 05 May 2021 09:05:34 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 12 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":tester!~u@247eaxkrufj44.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice @bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:19] bob: Is this; she hath bought the name of whore thus dearly.") + (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:24] alice: He sent to me, sir,Here he comes.") + (0 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:26] bob: Till I torment thee for this injury.") + (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:07:29] alice: There's an Italian come; and 'tis thought, one of Leonatus' friends.") + (0 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:09:33] bob: Ay, and the particular confirmations, point from point, to the full arming of the verity.") + (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :[09:09:35] alice: Kneel in the streets and beg for grace in vain.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0 ":irc.foonet.org NOTICE tester :[09:06:05] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) + +((mode 10 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620205534") + (0.5 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Nor I no strength to climb without thy help.") + (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: Nothing, but let him have thanks. Demand of him my condition, and what credit I have with the duke.") + (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Show me this piece. I am joyful of your sights.") + (0.2 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: Whilst I can shake my sword or hear the drum.")) + +((part 5 "PART #chan :" quit) + (0 ":tester!~u@247eaxkrufj44.irc PART #chan :" quit)) + +((join 10 "JOIN #chan") + (0 ":tester!~u@247eaxkrufj44.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :@bob alice tester") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :tester, welcome!") + (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :tester, welcome!")) + +((mode 1 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620205534") + (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Thou desirest me to stop in my tale against the hair.") + (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: And dar'st not stand, nor look me in the face.") + (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: It should not be, by the persuasion of his new feasting.") + (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: It was not given me, nor I did not buy it.") + (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: He that would vouch it in any place but here.") + (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :bob: In everything I wait upon his will.") + (0.1 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :alice: Thou counterfeit'st most lively.")) + +((linger 8 LINGER)) diff --git a/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld b/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld new file mode 100644 index 00000000000..cc7aff10076 --- /dev/null +++ b/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld @@ -0,0 +1,24 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :barnet:changeme")) +((nick 1 "NICK tester")) +((user 2 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Sun, 25 Apr 2021 11:28:28 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.barnet.org NOTICE tester :[11:29:00] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.barnet.org 305 tester :You are no longer marked as being away")) diff --git a/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld b/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld new file mode 100644 index 00000000000..3a846108466 --- /dev/null +++ b/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld @@ -0,0 +1,24 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Sun, 25 Apr 2021 11:28:28 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.znc.in 306 tester :You have been marked as being away") + (0 ":irc.foonet.org NOTICE tester :[11:29:00] This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0 ":irc.foonet.org 305 tester :You are no longer marked as being away")) diff --git a/test/lisp/erc/resources/base/upstream-reconnect/soju-barnet.eld b/test/lisp/erc/resources/base/upstream-reconnect/soju-barnet.eld new file mode 100644 index 00000000000..3711eb8f8e6 --- /dev/null +++ b/test/lisp/erc/resources/base/upstream-reconnect/soju-barnet.eld @@ -0,0 +1,64 @@ +;; -*- mode: lisp-data; -*- +((pass 6 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER tester@vanilla/barnet 0 * :tester") + (0.01 ":soju.im 001 tester :Welcome to soju, tester") + (0.01 ":soju.im 002 tester :Your host is soju.im") + (0.00 ":soju.im 004 tester soju.im soju aiwroO OovaimnqpsrtklbeI") + (0.53 ":soju.im 005 tester CHATHISTORY=1000 CASEMAPPING=ascii BOUNCER_NETID=2 AWAYLEN=390 CHANLIMIT=#:100 INVEX NETWORK=barnet NICKLEN=32 WHOX MODES BOT=B ELIST=U MAXLIST=beI:60 :are supported") + (0.01 ":soju.im 005 tester TOPICLEN=390 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 EXCEPTS EXTBAN=,m KICKLEN=390 TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 MAXTARGETS=4 MONITOR=100 CHANTYPES=# PREFIX=(qaohv)~&@%+ UTF8ONLY :are supported") + (0.22 ":soju.im 221 tester +Zi") + (0.00 ":soju.im 422 tester :Use /motd to read the message of the day")) + +((mode 5 "MODE tester +i") + (0.00 ":tester!tester@10.0.2.100 JOIN #chan") + (0.06 ":soju.im 353 tester = #chan :tester @mike joe") + (0.01 ":soju.im 366 tester #chan :End of /NAMES list") + (0.23 ":irc.barnet.org 221 tester +Zi")) + +((mode 5 "MODE #chan") + (0.00 ":soju.im 324 tester #chan +tn") + (0.01 ":soju.im 329 tester #chan 1652878846") + (0.01 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: There is five in the first show.") + (0.00 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Sir, I was an inward of his. A shy fellow was the duke; and, I believe I know the cause of his withdrawing.") + (0.00 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Proud of employment, willingly I go.") + (0.09 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Dull not device by coldness and delay.") + (0.09 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Our states are forfeit: seek not to undo us.") + (0.06 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Come, you are too severe a moraler. As the time, the place, and the condition of this country stands, I could heartily wish this had not befallen, but since it is as it is, mend it for your own good.") + (0.06 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Who hath upon him still that natural stamp.") + (0.07 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Arraign her first; 'tis Goneril. I here take my oath before this honourable assembly, she kicked the poor king her father.") + (0.06 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Lady, I will commend you to mine own heart.") + (0.08 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Look, what I will not, that I cannot do.") + (0.08 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: That he would wed me, or else die my lover.") + (0.08 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Come your way, sir. Bless you, good father friar.") + (0.08 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Under correction, sir, we know whereuntil it doth amount.") + (0.08 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: For I am nothing if not critical.") + (0.06 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Once more I'll read the ode that I have writ.") + (0.06 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: This is the foul fiend Flibbertigibbet: he begins at curfew, and walks till the first cock; he gives the web and the pin, squints the eye, and makes the harelip; mildews the white wheat, and hurts the poor creature of earth.") + (0.06 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Sir, I praise the Lord for you, and so may my parishioners; for their sons are well tutored by you, and their daughters profit very greatly under you: you are a good member of the commonwealth.") + (0.08 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: If it please your honor, I know not well what they are; but precise villains they are, that I am sure of, and void of all profanation in the world that good Christians ought to have.") + ;; Unexpected disconnect + (0.03 ":BouncerServ!BouncerServ@BouncerServ NOTICE tester :disconnected from barnet: failed to handle messages: failed to read IRC command: read tcp [::1]:54990->[::1]:6668: read: software caused connection abort") + ;; Eventual reconnect + (0.79 ":BouncerServ!BouncerServ@BouncerServ NOTICE tester :connected to barnet") + ;; No MOTD or other numerics + (0.01 ":soju.im 005 tester AWAYLEN=390 BOT=B CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m INVEX KICKLEN=390 :are supported") + (0.01 ":soju.im 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported") + (0.22 ":irc.barnet.org 221 tester +Zi") + (0.01 ":irc.barnet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + ;; Server-initialed join + (0.01 ":tester!tester@10.0.2.100 JOIN #chan")) + +((mode 5 "MODE #chan") + (0.22 ":soju.im 353 tester = #chan :@mike joe tester") + (0.00 ":soju.im 366 tester #chan :End of /NAMES list") + (0.00 ":soju.im 324 tester #chan +nt") + (0.00 ":soju.im 329 tester #chan 1652878846") + (0.00 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :tester, welcome!") + (0.00 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :tester, welcome!") + (0.06 ":soju.im 324 tester #chan +nt") + (0.00 ":soju.im 329 tester #chan 1652878846") + (0.62 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: Thou art my brother; so we'll hold thee ever.") + (0.00 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Very well! go to! I cannot go to, man; nor 'tis not very well: by this hand, I say, it is very scurvy, and begin to find myself fobbed in it.") + (0.00 ":joe!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :mike: The heir of Alen on, Katharine her name.") + (0.09 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Go to; farewell! put money enough in your purse.")) diff --git a/test/lisp/erc/resources/base/upstream-reconnect/soju-foonet.eld b/test/lisp/erc/resources/base/upstream-reconnect/soju-foonet.eld new file mode 100644 index 00000000000..63dfcb184c8 --- /dev/null +++ b/test/lisp/erc/resources/base/upstream-reconnect/soju-foonet.eld @@ -0,0 +1,72 @@ +;; -*- mode: lisp-data; -*- +((pass 5 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER tester@vanilla/foonet 0 * :tester") + (0.01 ":soju.im 001 tester :Welcome to soju, tester") + (0.02 ":soju.im 002 tester :Your host is soju.im") + (0.01 ":soju.im 004 tester soju.im soju aiwroO OovaimnqpsrtklbeI") + (0.00 ":soju.im 005 tester CHATHISTORY=1000 CASEMAPPING=ascii BOUNCER_NETID=1 CHANTYPES=# PREFIX=(qaohv)~&@%+ UTF8ONLY AWAYLEN=390 NICKLEN=32 WHOX CHANLIMIT=#:100 INVEX NETWORK=foonet MODES :are supported") + (0.00 ":soju.im 005 tester TOPICLEN=390 BOT=B ELIST=U MAXLIST=beI:60 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 EXCEPTS EXTBAN=,m KICKLEN=390 MAXTARGETS=4 MONITOR=100 :are supported") + (0.00 ":soju.im 221 tester +Zi") + (0.00 ":soju.im 422 tester :Use /motd to read the message of the day")) + +((mode 5 "MODE tester +i") + (0.2 ":irc.foonet.org 221 tester +Zi") + (0.0 ":tester!tester@10.0.2.100 JOIN #chan") + (0.0 ":soju.im 353 tester = #chan :tester @alice bob") + (0.1 ":soju.im 366 tester #chan :End of /NAMES list") + (0.0 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Princely shall be thy usage every way.") + (0.1 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: Tell me thy reason why thou wilt marry.")) + +((mode 5 "MODE #chan") + (0.00 ":soju.im 324 tester #chan +nt") + (0.01 ":soju.im 329 tester #chan 1652878847") + (0.02 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: There is no leprosy but what thou speak'st.") + (0.09 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: For I upon this bank will rest my head.") + (0.01 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: To ruffle in the commonwealth of Rome.") + (0.08 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: For I can nowhere find him like a man.") + (0.09 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Ay, sir; but she will none, she gives you thanks.") + (0.05 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: That man should be at woman's command, and yet no hurt done! Though honesty be no puritan, yet it will do no hurt; it will wear the surplice of humility over the black gown of a big heart. I am going, forsooth: the business is for Helen to come hither.") + (0.07 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Indeed, I should have asked you that before.") + (0.09 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: Faith, we met, and found the quarrel was upon the seventh cause.") + (0.05 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: And then, I hope, thou wilt be satisfied.") + (0.06 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: Well, I will forget the condition of my estate, to rejoice in yours.") + (0.05 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Ah! sirrah, this unlook'd-for sport comes well.") + (0.01 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: Mayst thou inherit too! Welcome to Paris.") + (0.04 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: That I would choose, were I to choose anew.") + (0.08 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: Good Tom Drum, lend me a handkercher: so, I thank thee. Wait on me home, I'll make sport with thee: let thy curtsies alone, they are scurvy ones.") + (0.06 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Excellent workman! Thou canst not paint a man so bad as is thyself.") + (0.07 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: That every braggart shall be found an ass.") + (0.07 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: This is but a custom in your tongue; you bear a graver purpose, I hope.") + (0.02 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: Well, we will have such a prologue, and it shall be written in eight and six.") + (0.01 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Tell me thy reason why thou wilt marry.") + (0.06 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: According to the measure of their states.") + + ;; Unexpected disconnect + (0.07 ":BouncerServ!BouncerServ@BouncerServ NOTICE tester :disconnected from foonet: failed to handle messages: failed to read IRC command: read tcp [::1]:57224->[::1]:6667: read: software caused connection abort") + ;; Eventual reconnect + (1.02 ":BouncerServ!BouncerServ@BouncerServ NOTICE tester :connected to foonet") + ;; No MOTD or other numerics + (0.01 ":soju.im 005 tester AWAYLEN=390 BOT=B CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m INVEX KICKLEN=390 :are supported") + (0.02 ":soju.im 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8ONLY WHOX :are supported") + (0.02 ":irc.foonet.org 221 tester +Zi") + (0.23 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + ;; Server-initialed join + (0.02 ":tester!tester@10.0.2.100 JOIN #chan")) + +((mode 5 "MODE #chan") + (0.03 ":soju.im 353 tester = #chan :@alice bob tester") + (0.03 ":soju.im 366 tester #chan :End of /NAMES list") + (0.00 ":soju.im 324 tester #chan +nt") + (0.00 ":soju.im 329 tester #chan 1652878847") + (0.00 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :tester, welcome!") + (0.00 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :tester, welcome!") + (0.46 ":soju.im 324 tester #chan +nt") + (0.01 ":soju.im 329 tester #chan 1652878847") + (0.00 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: Thou desirest me to stop in my tale against the hair.") + (0.07 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: But my intents are fix'd and will not leave me.") + (0.09 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: That last is true; the sweeter rest was mine.") + (0.09 ":alice!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :bob: No matter whither, so you come not here.") + (0.09 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: My lord, in heart; and let the health go round.")) + +((linger 12 LINGER)) diff --git a/test/lisp/erc/resources/base/upstream-reconnect/znc-barnet.eld b/test/lisp/erc/resources/base/upstream-reconnect/znc-barnet.eld new file mode 100644 index 00000000000..bf5c2b5a749 --- /dev/null +++ b/test/lisp/erc/resources/base/upstream-reconnect/znc-barnet.eld @@ -0,0 +1,93 @@ +;; -*- mode: lisp-data; -*- +((pass 6 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER tester@vanilla/barnet 0 * :tester") + (0.00 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0.01 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version ergo-v2.8.0") + (0.01 ":irc.barnet.org 003 tester :This server was created Thu, 19 May 2022 05:33:02 UTC") + (0.00 ":irc.barnet.org 004 tester irc.barnet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.00 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.01 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.00 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.00 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0.11 ":irc.barnet.org 254 tester 1 :channels formed") + (0.00 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0.00 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0.00 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0.00 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode 5 "MODE tester +i") + (0.0 ":tester!~u@fsr9fwzfeeybc.irc JOIN #chan") + (0.05 ":irc.barnet.org 353 tester = #chan :@joe mike tester") + (0.01 ":irc.barnet.org 366 tester #chan :End of /NAMES list.") + (0.0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0.0 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:13] mike: But send the midwife presently to me.") + (0.01 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:18] joe: Alas! poor rogue, I think, i' faith, she loves me.") + (0.01 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:20] mike: They did not bless us with one happy word.") + (0.01 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:24] joe: And hear the sentence of your moved prince.") + (0.21 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:29] mike: Swear me to this, and I will ne'er say no.") + (0.01 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:32] joe: As they had seen me with these hangman's hands.") + (0.01 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:34] mike: Boyet, prepare: I will away to-night.") + (0.01 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :[05:48:36] joe: For being a little bad: so may my husband.") + (0.04 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0.0 ":irc.barnet.org 221 tester +Zi") + (2.55 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: And whirl along with thee about the globe.")) + +((mode 5 "MODE #chan") + (0.00 ":irc.barnet.org 324 tester #chan +nt") + (0.00 ":irc.barnet.org 329 tester #chan 1652938384") + (0.06 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Unless good-counsel may the cause remove.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Thyself domestic officers thine enemy.") + (0.01 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Go after her: she's desperate; govern her.") + (0.30 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Or else to heaven she heaves them for revenge.") + (0.01 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Keep up your bright swords, for the dew will rust them.") + (0.04 ":*status!znc@znc.in PRIVMSG tester :Disconnected from IRC (Connection aborted). Reconnecting...") + (0.41 ":*status!znc@znc.in PRIVMSG tester :Disconnected from IRC. Reconnecting...") + (0.59 ":*status!znc@znc.in PRIVMSG tester :Connected!") + (0.02 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0.01 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version ergo-v2.8.0") + (0.01 ":irc.barnet.org 003 tester :This server was created Thu, 19 May 2022 05:33:02 UTC") + (0.01 ":irc.barnet.org 004 tester irc.barnet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.01 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.01 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.22 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.00 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.01 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.barnet.org 253 tester 0 :unregistered connections") + (0.00 ":irc.barnet.org 254 tester 1 :channels formed") + (0.00 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0.00 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0.17 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0.00 ":irc.barnet.org 422 tester :MOTD File is missing") + (0.01 ":irc.barnet.org 221 tester +Zi") + (0.00 ":irc.barnet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0.05 ":irc.barnet.org 352 tester * ~u fsr9fwzfeeybc.irc irc.barnet.org tester H :0 ZNC - https://znc.in") + (0.02 ":irc.barnet.org 315 tester tester!*@* :End of WHO list") + (0.08 ":tester!~u@fsr9fwzfeeybc.irc JOIN #chan")) + +((mode 5 "MODE #chan") + (0.05 ":irc.barnet.org 353 tester = #chan :mike tester @joe") + (0.01 ":irc.barnet.org 366 tester #chan :End of NAMES list") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :tester, welcome!") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :tester, welcome!") + (0.02 ":irc.barnet.org 324 tester #chan +nt") + (0.01 ":irc.barnet.org 329 tester #chan 1652938384") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: See, here he comes, and I must ply my theme.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Confine yourself but in a patient list.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: And bide the penance of each three years' day.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Bid me farewell, and let me hear thee going.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Nor shall not, if I do as I intend.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Our corn's to reap, for yet our tithe's to sow.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: And almost broke my heart with extreme laughter.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Of modern seeming do prefer against him.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Like humble-visag'd suitors, his high will.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: But yet, poor Claudio! There's no remedy.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Let him make treble satisfaction.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: He's that he is; I may not breathe my censure.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: To check their folly, passion's solemn tears.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: Villain, I have done thy mother.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Please you, therefore, draw nigh, and take your places.") + (0.00 ":mike!~u@6t6jcije78we2.irc PRIVMSG #chan :joe: You shall not be admitted to his sight.") + (0.00 ":joe!~u@6t6jcije78we2.irc PRIVMSG #chan :mike: Sir, you shall present before her the Nine Worthies. Sir Nathaniel, as concerning some entertainment of time, some show in the posterior of this day, to be rendered by our assistance, at the king's command, and this most gallant, illustrate, and learned gentleman, before the princess; I say, none so fit as to present the Nine Worthies.") + (0.00 ":mike!~u@6d9pasqcqwb2s.irc PRIVMSG #chan :joe: Go to; farewell! put money enough in your purse.")) diff --git a/test/lisp/erc/resources/base/upstream-reconnect/znc-foonet.eld b/test/lisp/erc/resources/base/upstream-reconnect/znc-foonet.eld new file mode 100644 index 00000000000..39c2950aa09 --- /dev/null +++ b/test/lisp/erc/resources/base/upstream-reconnect/znc-foonet.eld @@ -0,0 +1,86 @@ +;; -*- mode: lisp-data; -*- +((pass 6 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER tester@vanilla/foonet 0 * :tester") + (0.16 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.00 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.8.0") + (0.00 ":irc.foonet.org 003 tester :This server was created Thu, 19 May 2022 05:33:02 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.00 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.01 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.foonet.org 254 tester 1 :channels formed") + (0.00 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0.00 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0.00 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0.00 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode 6 "MODE tester +i") + (0.00 ":tester!~u@rmtvrz9zcwbdq.irc JOIN #chan") + (0.09 ":irc.foonet.org 353 tester = #chan :@alice bob tester") + (0.00 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0.00 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0.00 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:11] alice: And be aveng'd on cursed Tamora.") + (0.00 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:13] bob: The stronger part of it by her own letters, which make her story true, even to the point of her death: her death itself, which could not be her office to say is come, was faithfully confirmed by the rector of the place.") + (0.01 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:15] alice: The ape is dead, and I must conjure him.") + (0.00 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:17] bob: Not so; but I answer you right painted cloth, from whence you have studied your questions.") + (0.01 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:21] alice: The valiant Paris seeks you for his love.") + (0.00 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:26] bob: To prison with her; and away with him.") + (0.00 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:30] alice: Tell them there I have gold; look, so I have.") + (0.00 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :[05:48:35] bob: Will even weigh, and both as light as tales.") + (0.00 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0.00 ":irc.foonet.org 221 tester +Zi") + (0.08 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: By some vile forfeit of untimely death.")) + +((mode 3.51 "MODE #chan") + (0.1 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1652938384") + (0.0 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: What does this knave here ? Get you gone, sirrah: the complaints I have heard of you I do not all believe: 'tis my slowness that I do not; for I know you lack not folly to commit them, and have ability enough to make such knaveries yours.") + (0.0 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: When sects and factions were newly born.") + (0.1 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: Fall, when Love please! marry, to each, but one.") + (0.1 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: For I ne'er saw true beauty till this night.") + (0.1 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: Or say, sweet love, what thou desir'st to eat.") + (0.1 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: Yes, and will nobly him remunerate.") + (0.1 ":*status!znc@znc.in PRIVMSG tester :Disconnected from IRC (Connection aborted). Reconnecting...") + (0.4 ":*status!znc@znc.in PRIVMSG tester :Disconnected from IRC. Reconnecting...") + (0.9 ":*status!znc@znc.in PRIVMSG tester :Connected!") + (0.0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.8.0") + (0.0 ":irc.foonet.org 003 tester :This server was created Thu, 19 May 2022 05:33:02 UTC") + (0.0 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.1 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.1 ":irc.foonet.org 254 tester 1 :channels formed") + (0.0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.foonet.org 422 tester :MOTD File is missing") + (0.0 ":irc.foonet.org 221 tester +Zi") + (0.0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0.6 ":irc.foonet.org 352 tester * ~u rmtvrz9zcwbdq.irc irc.foonet.org tester H :0 ZNC - https://znc.in") + (0.0 ":irc.foonet.org 315 tester tester!*@* :End of WHO list") + (0.0 ":tester!~u@rmtvrz9zcwbdq.irc JOIN #chan")) + +((mode 6 "MODE #chan") + (0.0 ":irc.foonet.org 353 tester = #chan :@alice bob tester") + (0.0 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.0 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :tester, welcome!") + (0.0 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :tester, welcome!") + (0.0 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: Being of no power to make his wishes good.") + (0.0 ":irc.foonet.org 324 tester #chan +nt") + (0.0 ":irc.foonet.org 329 tester #chan 1652938384") + (0.0 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: In everything I wait upon his will.") + (0.0 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: Make choice of which your highness will see first.") + (0.0 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: We waste our lights in vain, like lamps by day.") + (0.0 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: No, I know that; but it is fit I should commit offence to my inferiors.") + (0.1 ":bob!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :alice: By my head, here come the Capulets.") + (0.0 ":alice!~u@rmtvrz9zcwbdq.irc PRIVMSG #chan :bob: Well, I will forget the condition of my estate, to rejoice in yours.") + (0.0 ":bob!~u@h35cf3bf7rbt4.irc PRIVMSG #chan :alice: My lord, in heart; and let the health go round.")) + +((linger 12 LINGER)) diff --git a/test/lisp/erc/resources/dcc/chat/accept-dcc.eld b/test/lisp/erc/resources/dcc/chat/accept-dcc.eld new file mode 100644 index 00000000000..23828a8115e --- /dev/null +++ b/test/lisp/erc/resources/dcc/chat/accept-dcc.eld @@ -0,0 +1,3 @@ +;; -*- mode: lisp-data; -*- +((open 10 "Hi") + (0 "Hola")) diff --git a/test/lisp/erc/resources/dcc/chat/accept.eld b/test/lisp/erc/resources/dcc/chat/accept.eld new file mode 100644 index 00000000000..a23e9580bcc --- /dev/null +++ b/test/lisp/erc/resources/dcc/chat/accept.eld @@ -0,0 +1,23 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Mon, 31 May 2021 09:56:24 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 2 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + ;; No mode answer + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0.2 ":dummy!~u@34n9brushbpj2.irc PRIVMSG tester :\C-aDCC CHAT chat 2130706433 " port "\C-a")) diff --git a/test/lisp/erc/resources/erc-d/erc-d-i.el b/test/lisp/erc/resources/erc-d/erc-d-i.el new file mode 100644 index 00000000000..db113335a82 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/erc-d-i.el @@ -0,0 +1,124 @@ +;;; erc-d-i.el --- IRC helpers for ERC test server -*- lexical-binding: t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(cl-defstruct (erc-d-i-message (:conc-name erc-d-i-message.)) + "Identical to `erc-response'. +When member `compat' is nil, it means the raw message was decoded as +UTF-8 text before parsing, which is nonstandard." + (unparsed "" :type string) + (sender "" :type string) + (command "" :type string) + (command-args nil :type (list-of string)) + (contents "" :type string) + (tags nil :type (list-of (cons symbol string))) + (compat t :type boolean)) + +(defconst erc-d-i--tag-escapes + '((";" . "\\:") (" " . "\\s") ("\\" . "\\\\") ("\r" . "\\r") ("\n" . "\\n"))) + +;; These are not mirror inverses; unescaping may drop stranded or +;; misplaced backslashes. + +(defconst erc-d-i--tag-escaped-regexp (rx (or ?\; ?\ ?\\ ?\r ?\n))) + +(defconst erc-d-i--tag-unescaped-regexp + (rx (or "\\:" "\\s" "\\\\" "\\r" "\\n" + (seq "\\" (or string-end (not (or ":" "s" "n" "r" "\\"))))))) + +(defun erc-d-i--unescape-tag-value (str) + "Undo substitution of char placeholders in raw tag value STR." + (replace-regexp-in-string erc-d-i--tag-unescaped-regexp + (lambda (s) + (or (car (rassoc s erc-d-i--tag-escapes)) + (substring s 1))) + str t t)) + +(defun erc-d-i--escape-tag-value (str) + "Swap out banned chars in tag value STR with message representation." + (replace-regexp-in-string erc-d-i--tag-escaped-regexp + (lambda (s) + (cdr (assoc s erc-d-i--tag-escapes))) + str t t)) + +(defconst erc-d-i--invalid-tag-regexp (rx (any "\0\7\r\n; "))) + +(defun erc-d-i--validate-tags (raw) + "Validate tags portion of some RAW incoming message. +RAW must not have a leading \"@\" or a trailing space. The spec says +validation shouldn't be performed on keys and that undecodeable values +or ones with illegal (unescaped) chars may be dropped. This does not +respect any of that. Its purpose is to catch bad input created by us." + (unless (> 4094 (string-bytes raw)) + ;; 417 ERR_INPUTTOOLONG Input line was too long + (error "Message tags exceed 4094 bytes: %S" raw)) + (let (tags + (tag-strings (split-string raw ";"))) + (dolist (s tag-strings (nreverse tags)) + (let* ((m (if (>= emacs-major-version 28) + (string-search "=" s) + (string-match-p "=" s))) + (key (if m (substring s 0 m) s)) + (val (when-let* (m ; check first, like (m), but shadow + (v (substring s (1+ m))) + ((not (string-equal v "")))) + (when (string-match-p erc-d-i--invalid-tag-regexp v) + (error "Bad tag: %s" s)) + (thread-first v + (decode-coding-string 'utf-8 t) + (erc-d-i--unescape-tag-value))))) + (when (string-empty-p key) + (error "Tag missing key: %S" s)) + (setf (alist-get (intern key) tags) val))))) + +(defun erc-d-i--parse-message (s &optional decode) + "Parse string S into `erc-d-i-message' object. +With DECODE, decode as UTF-8 text." + (when (string-suffix-p "\r\n" s) + (error "Unstripped message encountered")) + (when decode + (setq s (decode-coding-string s 'utf-8 t))) + (let ((mes (make-erc-d-i-message :unparsed s :compat (not decode))) + tokens) + (when-let* (((not (string-empty-p s))) + ((eq ?@ (aref s 0))) + (m (string-match " " s)) + (u (substring s 1 m))) + (setf (erc-d-i-message.tags mes) (erc-d-i--validate-tags u) + s (substring s (1+ m)))) + (if-let* ((m (string-match " :" s)) + (other-toks (split-string (substring s 0 m) " " t)) + (rest (substring s (+ 2 m)))) + (setf (erc-d-i-message.contents mes) rest + tokens (nconc other-toks (list rest))) + (setq tokens (split-string s " " t " "))) + (when (and tokens (eq ?: (aref (car tokens) 0))) + (setf (erc-d-i-message.sender mes) (substring (pop tokens) 1))) + (setf (erc-d-i-message.command mes) (or (pop tokens) "") + (erc-d-i-message.command-args mes) tokens) + mes)) + +(provide 'erc-d-i) +;;; erc-d-i.el ends here diff --git a/test/lisp/erc/resources/erc-d/erc-d-t.el b/test/lisp/erc/resources/erc-d/erc-d-t.el new file mode 100644 index 00000000000..a1a7e7e88d5 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/erc-d-t.el @@ -0,0 +1,170 @@ +;;; erc-d-t.el --- ERT helpers for ERC test server -*- lexical-binding: t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: +(eval-and-compile + (let* ((d (file-name-directory (or (macroexp-file-name) buffer-file-name))) + (load-path (cons (directory-file-name d) load-path))) + (require 'erc-d-u))) + +(require 'ert) + +(defun erc-d-t-kill-related-buffers () + "Kill all erc- or erc-d- related buffers." + (let (buflist) + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when (or erc-d-u--process-buffer + (derived-mode-p 'erc-mode)) + (push buf buflist)))) + (dolist (buf buflist) + (when (and (boundp 'erc-server-flood-timer) + (timerp erc-server-flood-timer)) + (cancel-timer erc-server-flood-timer)) + (when-let ((proc (get-buffer-process buf))) + (delete-process proc)) + (when (buffer-live-p buf) + (kill-buffer buf)))) + (while (when-let ((buf (pop erc-d-u--canned-buffers))) + (kill-buffer buf)))) + +(defun erc-d-t-silence-around (orig &rest args) + "Run ORIG function with ARGS silently. +Use this on `erc-handle-login' and `erc-server-connect'." + (let ((inhibit-message t)) + (apply orig args))) + +(defvar erc-d-t-cleanup-sleep-secs 0.1) + +(defmacro erc-d-t-with-cleanup (bindings cleanup &rest body) + "Execute BODY and run CLEANUP form regardless of outcome. +`let*'-bind BINDINGS and make them available in BODY and CLEANUP. +After CLEANUP, destroy any values in BINDINGS that remain bound to +buffers or processes. Sleep `erc-d-t-cleanup-sleep-secs' before +returning." + (declare (indent 2)) + `(let* ,bindings + (unwind-protect + (progn ,@body) + ,cleanup + (when noninteractive + (let (bufs procs) + (dolist (o (list ,@(mapcar (lambda (b) (or (car-safe b) b)) + bindings))) + (when (bufferp o) + (push o bufs)) + (when (processp o) + (push o procs))) + (dolist (proc procs) + (delete-process proc) + (when-let ((buf (process-buffer proc))) + (push buf bufs))) + (dolist (buf bufs) + (when-let ((proc (get-buffer-process buf))) + (delete-process proc)) + (when (bufferp buf) + (ignore-errors (kill-buffer buf))))) + (sleep-for erc-d-t-cleanup-sleep-secs))))) + +(defmacro erc-d-t-wait-for (max-secs msg &rest body) + "Wait for BODY to become non-nil. +Or signal error with MSG after MAX-SECS. When MAX-SECS is negative, +signal if BODY is ever non-nil before MAX-SECS elapses. On success, +return BODY's value. + +Note: this assumes BODY is waiting on a peer's output. It tends to +artificially accelerate consumption of all process output, which may not +be desirable." + (declare (indent 2)) + (unless (or (stringp msg) (memq (car-safe msg) '(format concat))) + (push msg body) + (setq msg (prin1-to-string body))) + (let ((inverted (make-symbol "inverted")) + (time-out (make-symbol "time-out")) + (result (make-symbol "result"))) + `(ert-info ((concat "Awaiting: " ,msg)) + (let ((,time-out (abs ,max-secs)) + (,inverted (< ,max-secs 0)) + (,result ',result)) + (with-timeout (,time-out (if ,inverted + (setq ,inverted nil) + (error "Failed awaiting: %s" ,msg))) + (while (not (setq ,result (progn ,@body))) + (when (and (accept-process-output nil 0.1) (not noninteractive)) + (redisplay)))) + (when ,inverted + (error "Failed awaiting: %s" ,msg)) + ,result)))) + +(defmacro erc-d-t-ensure-for (max-secs msg &rest body) + "Ensure BODY remains non-nil for MAX-SECS. +On failure, emit MSG." + (declare (indent 2)) + (unless (or (stringp msg) (memq (car-safe msg) '(format concat))) + (push msg body) + (setq msg (prin1-to-string body))) + `(erc-d-t-wait-for (- (abs ,max-secs)) ,msg (not (progn ,@body)))) + +(defun erc-d-t-search-for (timeout text &optional from on-success) + "Wait for TEXT to appear in current buffer before TIMEOUT secs. +With marker or number FROM, only consider the portion of the buffer from +that point forward. If TEXT is a cons, interpret it as an RX regular +expression. If ON-SUCCESS is a function, call it when TEXT is found." + (save-restriction + (widen) + (let* ((rxp (consp text)) + (fun (if rxp #'search-forward-regexp #'search-forward)) + (pat (if rxp (rx-to-string text) text)) + res) + (erc-d-t-wait-for timeout (format "string: %s" text) + (goto-char (or from (point-min))) + (setq res (funcall fun pat nil t)) + (if (and on-success res) + (funcall on-success) + res))))) + +(defun erc-d-t-absent-for (timeout text &optional from on-success) + "Assert TEXT doesn't appear in current buffer for TIMEOUT secs." + (erc-d-t-search-for (- (abs timeout)) text from on-success)) + +(defun erc-d-t-make-expecter () + "Return function to search for new output in buffer. +Assume new text is only inserted at or after `erc-insert-marker'. + +The returned function works like `erc-d-t-search-for', but it never +revisits previously covered territory, and the optional fourth argument, +ON-SUCCESS, is nonexistent. To reset, specify a FROM argument." + (let (positions) + (lambda (timeout text &optional reset-from) + (let* ((pos (cdr (assq (current-buffer) positions))) + (cb (lambda () + (unless pos + (push (cons (current-buffer) (setq pos (make-marker))) + positions)) + (marker-position + (set-marker pos (min (point) (1- (point-max)))))))) + (when reset-from + (set-marker pos reset-from)) + (erc-d-t-search-for timeout text pos cb))))) + +(provide 'erc-d-t) +;;; erc-d-t.el ends here diff --git a/test/lisp/erc/resources/erc-d/erc-d-tests.el b/test/lisp/erc/resources/erc-d/erc-d-tests.el new file mode 100644 index 00000000000..a4befd96b5e --- /dev/null +++ b/test/lisp/erc/resources/erc-d/erc-d-tests.el @@ -0,0 +1,1373 @@ +;;; erc-d-tests.el --- tests for erc-d -*- lexical-binding: t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (expand-file-name ".." (ert-resource-directory)) + load-path))) + (require 'erc-d) + (require 'erc-d-t))) + +(require 'erc) + +;; Temporary kludge to silence warning +(put 'erc-parse-tags 'erc-v3-warned-p t) + +(ert-deftest erc-d-u--canned-load-dialog--basic () + (should-not (get-buffer "basic.eld")) + (should-not erc-d-u--canned-buffers) + (let* ((exes (erc-d-u--canned-load-dialog 'basic)) + (reap (lambda () + (cl-loop with e = (erc-d-u--read-dialog exes) + for s = (erc-d-u--read-exchange e) + while s collect s)))) + (should (get-buffer "basic.eld")) + (should (memq (get-buffer "basic.eld") erc-d-u--canned-buffers)) + (should (equal (funcall reap) '((pass 10.0 "PASS " (? ?:) "changeme")))) + (should (equal (funcall reap) '((nick 0.2 "NICK tester")))) + (let ((r (funcall reap))) + (should (equal (car r) '(user 0.2 "USER user 0 * :tester"))) + (should (equal + (car (last r)) + '(0 ":irc.example.org 422 tester :MOTD File is missing")))) + (should (equal (car (funcall reap)) '(mode-user 5 "MODE tester +i"))) + (should (equal (funcall reap) + '((mode-chan 1.2 "MODE #chan") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")))) + ;; See `define-error' site for `iter-end-of-sequence' + (ert-info ("EOB detected") (should-not (erc-d-u--read-dialog exes)))) + (should-not (get-buffer "basic.eld")) + (should-not erc-d-u--canned-buffers)) + +(defun erc-d-tests--make-hunk-reader (hunks) + (let ((p (erc-d-u--read-dialog hunks))) + (lambda () (erc-d-u--read-exchange p)))) + +;; Fuzzies need to be able to access any non-exhausted genny. +(ert-deftest erc-d-u--canned-load-dialog--intermingled () + (should-not (get-buffer "basic.eld")) + (should-not erc-d-u--canned-buffers) + (let* ((exes (erc-d-u--canned-load-dialog 'basic)) + (pass (erc-d-tests--make-hunk-reader exes)) + (nick (erc-d-tests--make-hunk-reader exes)) + (user (erc-d-tests--make-hunk-reader exes)) + (modu (erc-d-tests--make-hunk-reader exes)) + (modc (erc-d-tests--make-hunk-reader exes))) + + (should (equal (funcall user) '(user 0.2 "USER user 0 * :tester"))) + (should (equal (funcall modu) '(mode-user 5 "MODE tester +i"))) + (should (equal (funcall modc) '(mode-chan 1.2 "MODE #chan"))) + + (cl-loop repeat 8 do (funcall user)) ; skip a few + (should (equal (funcall user) + '(0 ":irc.example.org 254 tester 1 :channels formed"))) + (should (equal (funcall modu) + '(0 ":irc.example.org 221 tester +Zi"))) + (should (equal (cl-loop for s = (funcall modc) while s collect s) ; done + '((0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")))) + + (cl-loop repeat 3 do (funcall user)) + (cl-loop repeat 3 do (funcall modu)) + + (ert-info ("Change up the order") + (should + (equal (funcall modu) + '(0 ":irc.example.org 366 alice #chan :End of NAMES list"))) + (should + (equal (funcall user) + '(0 ":irc.example.org 422 tester :MOTD File is missing")))) + + ;; Exhaust these + (should (equal (cl-loop for s = (funcall pass) while s collect s) ; done + '((pass 10.0 "PASS " (? ?:) "changeme")))) + (should (equal (cl-loop for s = (funcall nick) while s collect s) ; done + '((nick 0.2 "NICK tester")))) + + (ert-info ("End of file but no teardown because hunks outstanding") + (should-not (erc-d-u--read-dialog exes)) + (should (get-buffer "basic.eld"))) + + ;; Finish + (should-not (funcall user)) + (should-not (funcall modu))) + + (should-not (get-buffer "basic.eld")) + (should-not erc-d-u--canned-buffers)) + +;; This indirectly tests `erc-d-u--canned-read' cleanup/teardown + +(ert-deftest erc-d-u--rewrite-for-slow-mo () + (should-not (get-buffer "basic.eld")) + (should-not (get-buffer "basic.eld<2>")) + (should-not (get-buffer "basic.eld<3>")) + (should-not erc-d-u--canned-buffers) + (let ((exes (erc-d-u--canned-load-dialog 'basic)) + (exes-lower (erc-d-u--canned-load-dialog 'basic)) + (exes-custom (erc-d-u--canned-load-dialog 'basic)) + (reap (lambda (e) (cl-loop with p = (erc-d-u--read-dialog e) + for s = (erc-d-u--read-exchange p) + while s collect s)))) + (should (get-buffer "basic.eld")) + (should (get-buffer "basic.eld<2>")) + (should (get-buffer "basic.eld<3>")) + (should (equal (list (get-buffer "basic.eld<3>") + (get-buffer "basic.eld<2>") + (get-buffer "basic.eld")) + erc-d-u--canned-buffers)) + + (ert-info ("Rewrite for slowmo basic") + (setq exes (erc-d-u--rewrite-for-slow-mo 10 exes)) + (should (equal (funcall reap exes) + '((pass 20.0 "PASS " (? ?:) "changeme")))) + (should (equal (funcall reap exes) + '((nick 10.2 "NICK tester")))) + (let ((r (funcall reap exes))) + (should (equal (car r) '(user 10.2 "USER user 0 * :tester"))) + (should (equal + (car (last r)) + '(0 ":irc.example.org 422 tester :MOTD File is missing")))) + (should (equal (car (funcall reap exes)) + '(mode-user 15 "MODE tester +i"))) + (should (equal (car (funcall reap exes)) + '(mode-chan 11.2 "MODE #chan"))) + (should-not (erc-d-u--read-dialog exes))) + + (ert-info ("Rewrite for slowmo bounded") + (setq exes-lower (erc-d-u--rewrite-for-slow-mo -5 exes-lower)) + (should (equal (funcall reap exes-lower) + '((pass 10.0 "PASS " (? ?:) "changeme")))) + (should (equal (funcall reap exes-lower) + '((nick 5 "NICK tester")))) + (should (equal (car (funcall reap exes-lower)) + '(user 5 "USER user 0 * :tester"))) + (should (equal (car (funcall reap exes-lower)) + '(mode-user 5 "MODE tester +i"))) + (should (equal (car (funcall reap exes-lower)) + '(mode-chan 5 "MODE #chan"))) + (should-not (erc-d-u--read-dialog exes-lower))) + + (ert-info ("Rewrite for slowmo custom") + (setq exes-custom (erc-d-u--rewrite-for-slow-mo + (lambda (n) (* 2 n)) exes-custom)) + (should (equal (funcall reap exes-custom) + '((pass 20.0 "PASS " (? ?:) "changeme")))) + (should (equal (funcall reap exes-custom) + '((nick 0.4 "NICK tester")))) + (should (equal (car (funcall reap exes-custom)) + '(user 0.4 "USER user 0 * :tester"))) + (should (equal (car (funcall reap exes-custom)) + '(mode-user 10 "MODE tester +i"))) + (should (equal (car (funcall reap exes-custom)) + '(mode-chan 2.4 "MODE #chan"))) + (should-not (erc-d-u--read-dialog exes-custom)))) + + (should-not (get-buffer "basic.eld")) + (should-not (get-buffer "basic.eld<2>")) + (should-not (get-buffer "basic.eld<3>")) + (should-not erc-d-u--canned-buffers)) + +(ert-deftest erc-d--active-ex-p () + (let ((ring (make-ring 5))) + (ert-info ("Empty ring returns nil for not active") + (should-not (erc-d--active-ex-p ring))) + (ert-info ("One fuzzy member returns nil for not active") + (ring-insert ring (make-erc-d-exchange :tag '~foo)) + (should-not (erc-d--active-ex-p ring))) + (ert-info ("One active member returns t for active") + (ring-insert-at-beginning ring (make-erc-d-exchange :tag 'bar)) + (should (erc-d--active-ex-p ring))))) + +(defun erc-d-tests--parse-message-upstream (raw) + "Hack shim for parsing RAW line recvd from peer." + (cl-letf (((symbol-function #'erc-handle-parsed-server-response) + (lambda (_ p) p))) + (let ((erc-active-buffer nil)) + (erc-parse-server-response nil raw)))) + +(ert-deftest erc-d-i--validate-tags () + (should (erc-d-i--validate-tags + (concat "batch=4cc99692bf24a4bec4aa03da437364f5;" + "time=2021-01-04T00:32:13.839Z"))) + (should (erc-d-i--validate-tags "+foo=bar;baz=spam")) + (should (erc-d-i--validate-tags "foo=\\:ok;baz=\\s")) + (should (erc-d-i--validate-tags "foo=\303\247edilla")) + (should (erc-d-i--validate-tags "foo=\\")) + (should (erc-d-i--validate-tags "foo=bar\\baz")) + (should-error (erc-d-i--validate-tags "foo=\\\\;baz=\\\r\\\n")) + (should-error (erc-d-i--validate-tags "foo=\n")) + (should-error (erc-d-i--validate-tags "foo=\0ok")) + (should-error (erc-d-i--validate-tags "foo=bar baz")) + (should-error (erc-d-i--validate-tags "foo=bar\r")) + (should-error (erc-d-i--validate-tags "foo=bar;"))) + +(ert-deftest erc-d-i--parse-message () + (let* ((raw (concat "@time=2020-11-23T09:10:33.088Z " + ":tilde.chat BATCH +1 chathistory :#meta")) + (upstream (erc-d-tests--parse-message-upstream raw)) + (ours (erc-d-i--parse-message raw))) + + (ert-info ("Baseline upstream") + (should (equal (erc-response.unparsed upstream) raw)) + (should (equal (erc-response.sender upstream) "tilde.chat")) + (should (equal (erc-response.command upstream) "BATCH")) + (should (equal (erc-response.command-args upstream) + '("+1" "chathistory" "#meta"))) + (should (equal (erc-response.contents upstream) "#meta"))) + + (ert-info ("Ours my not compare cl-equalp but is otherwise the same") + (should (equal (erc-d-i-message.unparsed ours) raw)) + (should (equal (erc-d-i-message.sender ours) "tilde.chat")) + (should (equal (erc-d-i-message.command ours) "BATCH")) + (should (equal (erc-d-i-message.command-args ours) + '("+1" "chathistory" "#meta"))) + (should (equal (erc-d-i-message.contents ours) "#meta")) + (should (equal (erc-d-i-message.tags ours) + '((time . "2020-11-23T09:10:33.088Z"))))) + + (ert-info ("No compat decodes the whole message as utf-8") + (setq ours (erc-d-i--parse-message + "@foo=\303\247edilla TAGMSG #ch\303\240n" + 'decode)) + (should-not (erc-d-i-message.compat ours)) + (should (equal (erc-d-i-message.command-args ours) '("#chà n"))) + (should (equal (erc-d-i-message.contents ours) "")) + (should (equal (erc-d-i-message.tags ours) '((foo . "çedilla"))))))) + +(ert-deftest erc-d-i--unescape-tag-value () + (should (equal (erc-d-i--unescape-tag-value + "\\sabc\\sdef\\s\\sxyz\\s") + " abc def xyz ")) + (should (equal (erc-d-i--unescape-tag-value + "\\\\abc\\\\def\\\\\\\\xyz\\\\") + "\\abc\\def\\\\xyz\\")) + (should (equal (erc-d-i--unescape-tag-value "a\\bc") "abc")) + (should (equal (erc-d-i--unescape-tag-value + "\\\\abc\\\\def\\\\\\\\xyz\\") + "\\abc\\def\\\\xyz")) + (should (equal (erc-d-i--unescape-tag-value "a\\:b\\r\\nc\\sd") + "a;b\r\nc d"))) + +(ert-deftest erc-d-i--escape-tag-value () + (should (equal (erc-d-i--escape-tag-value " abc def xyz ") + "\\sabc\\sdef\\s\\sxyz\\s")) + (should (equal (erc-d-i--escape-tag-value "\\abc\\def\\\\xyz\\") + "\\\\abc\\\\def\\\\\\\\xyz\\\\")) + (should (equal (erc-d-i--escape-tag-value "a;b\r\nc d") + "a\\:b\\r\\nc\\sd"))) + +;; TODO add tests for msg-join, mask-match, userhost-split, +;; validate-hostname + +(ert-deftest erc-d-i--parse-message--irc-parser-tests () + (let* ((data (with-temp-buffer + (insert-file-contents + (expand-file-name "irc-parser-tests.eld" + (ert-resource-directory))) + (read (current-buffer)))) + (tests (assoc-default 'tests (assoc-default 'msg-split data))) + input atoms m ours) + (dolist (test tests) + (setq input (assoc-default 'input test) + atoms (assoc-default 'atoms test) + m (erc-d-i--parse-message input)) + (ert-info ("Parses tags correctly") + (setq ours (erc-d-i-message.tags m)) + (if-let ((tags (assoc-default 'tags atoms))) + (pcase-dolist (`(,key . ,value) ours) + (should (string= (cdr (assq key tags)) (or value "")))) + (should-not ours))) + (ert-info ("Parses verbs correctly") + (setq ours (erc-d-i-message.command m)) + (if-let ((verbs (assoc-default 'verb atoms))) + (should (string= (downcase verbs) (downcase ours))) + (should (string-empty-p ours)))) + (ert-info ("Parses sources correctly") + (setq ours (erc-d-i-message.sender m)) + (if-let ((source (assoc-default 'source atoms))) + (should (string= source ours)) + (should (string-empty-p ours)))) + (ert-info ("Parses params correctly") + (setq ours (erc-d-i-message.command-args m)) + (if-let ((params (assoc-default 'params atoms))) + (should (equal ours params)) + (should-not ours)))))) + +(defun erc-d-tests--new-ex (existing raw-hunk) + (let* ((f (lambda (_) (pop raw-hunk))) + (sd (make-erc-d-u-scan-d :f f))) + (setf (erc-d-exchange-hunk existing) (make-erc-d-u-scan-e :sd sd) + (erc-d-exchange-spec existing) (make-erc-d-spec))) + (erc-d--iter existing)) + +(ert-deftest erc-d--render-entries () + (let* ((erc-nick "foo") + (dialog (make-erc-d-dialog :vars `((:a . 1) + (c . ((a b) (: a space b))) + (d . (c alpha digit)) + (bee . 2) + (f . ,(lambda () "3")) + (i . erc-nick)))) + (exchange (make-erc-d-exchange :dialog dialog)) + (mex (apply-partially #'erc-d-tests--new-ex exchange)) + it) + + (erc-d-exchange-reload dialog exchange) + + (ert-info ("Baseline Outgoing") + (setq it (funcall mex '((0 "abc")))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "abc"))) + + (ert-info ("Incoming are regexp escaped") + (setq it (funcall mex '((i 0.0 "fsf" ".org")))) + (should (equal (cons (funcall it) (funcall it)) '(i . 0.0))) + (should (equal (funcall it) "\\`fsf\\.org"))) + + (ert-info ("Incoming can access vars via rx-let") + (setq it (funcall mex '((i 0.0 bee)))) + (should (equal (cons (funcall it) (funcall it)) '(i . 0.0))) + (should (equal (funcall it) "\\`\002"))) + + (ert-info ("Incoming rx-let params") + (setq it (funcall mex '((i 0.0 d)))) + (should (equal (cons (funcall it) (funcall it)) '(i . 0.0))) + (should (equal (funcall it) "\\`[[:alpha:]][[:space:]][[:digit:]]"))) + + (ert-info ("Incoming literal rx forms") + (setq it (funcall mex '((i 0.0 (= 3 alpha) ".org")))) + (should (equal (cons (funcall it) (funcall it)) '(i . 0.0))) + (should (equal (funcall it) "\\`[[:alpha:]]\\{3\\}\\.org"))) + + (ert-info ("Self-quoting disallowed") + (setq it (funcall mex '((0 :a "abc")))) + (should (equal (funcall it) 0)) + (should-error (funcall it))) + + (ert-info ("Global vars and short vars") + (setq it (funcall mex '((0 i f erc-nick)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "foo3foo"))) + + (ert-info ("Exits clean") + (when (listp (alist-get 'f (erc-d-dialog-vars dialog))) ; may be compiled + (should (eq 'closure (car (alist-get 'f (erc-d-dialog-vars dialog)))))) + (should-not (funcall it)) + (should (equal (erc-d-dialog-vars dialog) + `((:a . 1) + (c . ((a b) (: a space b))) + (d . (c alpha digit)) + (bee . 2) + (f . ,(alist-get 'f (erc-d-dialog-vars dialog))) + (i . erc-nick))))))) + +(ert-deftest erc-d--render-entries--matches () + (let* ((alist (list + (cons 'f (lambda (a) (funcall a :match 1))) + (cons 'g (lambda () (match-string 2 "foo bar baz"))) + (cons 'h (lambda (a) (concat (funcall a :match 0) + (funcall a :request)))) + (cons 'i (lambda (_ e) (erc-d-exchange-request e))) + (cons 'j (lambda () + (set-match-data '(0 1)) + (match-string 0 "j"))))) + (dialog (make-erc-d-dialog :vars alist)) + (exchange (make-erc-d-exchange :dialog dialog + :request "foo bar baz" + ;; 11 222 + :match-data '(4 11 4 6 8 11))) + (mex (apply-partially #'erc-d-tests--new-ex exchange)) + it) + + (erc-d-exchange-reload dialog exchange) + + (ert-info ("One arg, match") + (setq it (funcall mex '((0 f)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "ba"))) + + (ert-info ("No args") + (setq it (funcall mex '((0 g)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "baz"))) + + (ert-info ("Second arg is exchange object") + (setq it (funcall mex '((0 i)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "foo bar baz"))) + + (ert-info ("One arg, multiple calls") + (setq it (funcall mex '((0 h)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "bar bazfoo bar baz"))) + + (ert-info ("Match data restored") + (setq it (funcall mex '((0 j)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "j")) + + (setq it (funcall mex '((0 g)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "baz"))) + + (ert-info ("Bad signature") + (let ((qlist (list 'f '(lambda (p q x) (ignore))))) + (setf (erc-d-dialog-vars dialog) qlist) + (should-error (erc-d-exchange-reload dialog exchange)))))) + +(ert-deftest erc-d--render-entries--dynamic () + (let* ((alist (list + (cons 'foo "foo") + (cons 'f (lambda (a) (funcall a :get-binding 'foo))) + (cons 'h (lambda (a) (upcase (funcall a :get-var 'foo)))) + (cons 'g (lambda (a) + (funcall a :rebind 'g (funcall a :get-var 'f)) + "bar")) + (cons 'j (lambda (a) (funcall a :set "123") "abc")) + (cons 'k (lambda () "abc")))) + (dialog (make-erc-d-dialog :vars alist)) + (exchange (make-erc-d-exchange :dialog dialog)) + (mex (apply-partially #'erc-d-tests--new-ex exchange)) + it) + + (erc-d-exchange-reload dialog exchange) + + (ert-info ("Initial reference calls function") + (setq it (funcall mex '((0 j) (0 j)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "abc"))) + + (ert-info ("Subsequent reference expands to string") + (should (equal (funcall it) 0)) + (should (equal (funcall it) "123"))) + + (ert-info ("Outside manipulation: initial reference calls function") + (setq it (funcall mex '((0 k) (0 k)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "abc"))) + + (ert-info ("Outside manipulation: subsequent reference expands to string") + (erc-d-exchange-rebind dialog exchange 'k "123") + (should (equal (funcall it) 0)) + (should (equal (funcall it) "123"))) + + (ert-info ("Swap one function for another") + (setq it (funcall mex '((0 g) (0 g)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "bar")) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "foo"))) + + (ert-info ("Bindings accessible inside functions") + (setq it (funcall mex '((0 f h)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "fooFOO"))) + + (ert-info ("Rebuild alist by sending flag") + (setq it (funcall mex '((0 f) (1 f) (2 f) (i 3 f)))) + (should (equal (funcall it) 0)) + (should (equal (funcall it) "foo")) + (erc-d-exchange-rebind dialog exchange 'f "bar") + (should (equal (funcall it) 1)) + (should (equal (funcall it) "bar")) + (setq alist (setf (alist-get 'f (erc-d-dialog-vars dialog)) + (lambda nil "baz"))) + (should (eq (funcall it) 2)) + (should (equal (funcall it 'reload) "baz")) + (setq alist (setf (alist-get 'f (erc-d-dialog-vars dialog)) "spam")) + (should (eq (funcall it) 'i)) + (should (eq (funcall it 'reload) 3)) + (should (equal (funcall it) "\\`spam"))))) + +(ert-deftest erc-d-t-with-cleanup () + (should-not (get-buffer "*echo*")) + (should-not (get-buffer "*foo*")) + (should-not (get-buffer "*bar*")) + (should-not (get-buffer "*baz*")) + (erc-d-t-with-cleanup + ((echo (start-process "echo" (get-buffer-create "*echo*") "sleep" "1")) + (buffer-foo (get-buffer-create "*foo*")) + (buffer-bar (get-buffer-create "*bar*")) + (clean-up (list (intern (process-name echo)))) ; let* + buffer-baz) + (ert-info ("Clean Up") + (should (equal clean-up '(ran echo))) + (should (bufferp buffer-baz)) + (should (bufferp buffer-foo)) + (setq buffer-foo nil)) + (setq buffer-baz (get-buffer-create "*baz*")) + (push 'ran clean-up)) + (ert-info ("Buffers and procs destroyed") + (should-not (get-buffer "*echo*")) + (should-not (get-buffer "*bar*")) + (should-not (get-buffer "*baz*"))) + (ert-info ("Buffer foo spared") + (should (get-buffer "*foo*")) + (kill-buffer "*foo*"))) + +(ert-deftest erc-d-t-wait-for () + :tags '(:unstable) + (let (v) + (run-at-time 0.2 nil (lambda () (setq v t))) + (should (erc-d-t-wait-for 0.4 "result becomes non-nil" v)) + (should-error (erc-d-t-wait-for 0.4 "result stays nil" (not v))) + (setq v nil) + (should-not (erc-d-t-wait-for -0.4 "inverted stays nil" v)) + (run-at-time 0.2 nil (lambda () (setq v t))) + (setq v nil) + (should-error (erc-d-t-wait-for -0.4 "inverted becomes non-nil" v)))) + +(defvar erc-d-tests-with-server-password "changeme") + +;; Compromise between removing `autojoin' from `erc-modules' entirely +;; and allowing side effects to meddle excessively +(defvar erc-autojoin-channels-alist) + +;; This is only meant to be used by tests in this file. +(cl-defmacro erc-d-tests-with-server ((dumb-server-var erc-server-buffer-var) + dialog &rest body) + "Create server for DIALOG and run BODY. +DIALOG may also be a list of dialogs. ERC-SERVER-BUFFER-VAR and +DUMB-SERVER-VAR are bound accordingly in BODY." + (declare (indent 2)) + (when (eq '_ dumb-server-var) + (setq dumb-server-var (make-symbol "dumb-server-var"))) + (when (eq '_ erc-server-buffer-var) + (setq erc-server-buffer-var (make-symbol "erc-server-buffer-var"))) + (if (listp dialog) + (setq dialog (mapcar (lambda (f) (list 'quote f)) dialog)) + (setq dialog `((quote ,dialog)))) + `(let* (auth-source-do-cache + (,dumb-server-var (erc-d-run "localhost" t ,@dialog)) + ,erc-server-buffer-var + ;; + (erc-server-flood-penalty 0.05) + erc-autojoin-channels-alist + erc-server-auto-reconnect) + (should-not erc-d--slow-mo) + (with-current-buffer "*erc-d-server*" (erc-d-t-search-for 4 "Starting")) + ;; Allow important messages through, even in -batch mode. + (advice-add #'erc-handle-login :around #'erc-d-t-silence-around) + (advice-add #'erc-server-connect :around #'erc-d-t-silence-around) + (unless (or noninteractive erc-debug-irc-protocol) + (erc-toggle-debug-irc-protocol)) + (setq ,erc-server-buffer-var + (erc :server "localhost" + :password erc-d-tests-with-server-password + :port (process-contact ,dumb-server-var :service) + :nick "tester" + :full-name "tester")) + (unwind-protect + (progn + ,@body + (erc-d-t-wait-for 1 "dumb-server death" + (not (process-live-p ,dumb-server-var)))) + (when (process-live-p erc-server-process) + (delete-process erc-server-process)) + (advice-remove #'erc-handle-login #'erc-d-t-silence-around) + (advice-remove #'erc-server-connect #'erc-d-t-silence-around) + (when noninteractive + (kill-buffer ,erc-server-buffer-var) + (erc-d-t-kill-related-buffers))))) + +(defmacro erc-d-tests-with-failure-spy (found func-syms &rest body) + "Wrap functions with advice for inspecting errors caused by BODY. +Do this for functions whose names appear in FUNC-SYMS. When running +advice code, add errors to list FOUND. Note: the teardown finalizer is +not added by default. Also, `erc-d-linger-secs' likely has to be +nonzero for this to work." + (declare (indent 2)) + ;; Catch errors thrown by timers that `should-error'ignores + `(progn + (let ((ad (lambda (f o &rest r) + (condition-case err + (apply o r) + (error (push err ,found) + (advice-remove f 'spy)))))) + (dolist (sym ,func-syms) + (advice-add sym :around (apply-partially ad sym) '((name . spy))))) + (progn ,@body) + (dolist (sym ,func-syms) + (advice-remove sym 'spy)) + (setq ,found (nreverse ,found)))) + +(ert-deftest erc-d-run-nonstandard-messages () + :tags '(:expensive-test) + (let* ((erc-d-linger-secs 0.2) + (dumb-server (erc-d-run "localhost" t 'nonstandard)) + (dumb-server-buffer (get-buffer "*erc-d-server*")) + (expect (erc-d-t-make-expecter)) + client) + (with-current-buffer "*erc-d-server*" (erc-d-t-search-for 4 "Starting")) + (setq client (open-network-stream "erc-d-client" nil + "localhost" + (process-contact dumb-server :service) + :coding 'binary)) + (ert-info ("Server splits CRLF delimited lines") + (process-send-string client "ONE one\r\nTWO two\r\n") + (with-current-buffer dumb-server-buffer + (funcall expect 1 '(: "<- nonstandard:" (+ digit) " ONE one" eol)) + (funcall expect 1 '(regex "<- nonstandard:[[:digit:]]+ TWO two$")))) + (ert-info ("Server doesn't discard empty lines") + (process-send-string client "\r\n") + (with-current-buffer dumb-server-buffer + (funcall expect 1 '(regex "<- nonstandard:[[:digit:]]+ $")))) + (ert-info ("Server preserves spaces") + (process-send-string client " \r\n") + (with-current-buffer dumb-server-buffer + (funcall expect 1 '(regex "<- nonstandard:[[:digit:]]+ \\{2\\}$"))) + (process-send-string client " \r\n") + (with-current-buffer dumb-server-buffer + (funcall expect 1 '(regex "<- nonstandard:[[:digit:]]+ \\{3\\}$")))) + (erc-d-t-wait-for 3 "dumb-server death" + (not (process-live-p dumb-server))) + (delete-process client) + (when noninteractive + (kill-buffer dumb-server-buffer)))) + +(ert-deftest erc-d-run-basic () + :tags '(:expensive-test) + (erc-d-tests-with-server (_ _) basic + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "#chan")) + (erc-d-t-search-for 2 "hey")) + (when noninteractive + (kill-buffer "#chan")))) + +(ert-deftest erc-d-run-eof () + :tags '(:expensive-test) + (skip-unless noninteractive) + (erc-d-tests-with-server (_ erc-s-buf) eof + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "#chan")) + (erc-d-t-search-for 2 "hey")) + (with-current-buffer erc-s-buf + (process-send-eof erc-server-process)))) + +(ert-deftest erc-d-run-eof-fail () + :tags '(:expensive-test) + (let (errors) + (erc-d-tests-with-failure-spy errors '(erc-d--teardown) + (erc-d-tests-with-server (_ _) eof + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (erc-d-t-search-for 2 "hey")) + (erc-d-t-wait-for 10 errors))) + (should (string-match-p "Timed out awaiting request.*__EOF__" + (cadr (pop errors)))))) + +(ert-deftest erc-d-run-linger () + :tags '(:unstable :expensive-test) + (erc-d-tests-with-server (dumb-s _) linger + (with-current-buffer (erc-d-t-wait-for 6 (get-buffer "#chan")) + (erc-d-t-search-for 2 "hey")) + (with-current-buffer (process-buffer dumb-s) + (erc-d-t-search-for 2 "Lingering for 1.00 seconds")) + (with-current-buffer (process-buffer dumb-s) + (erc-d-t-search-for 3 "Lingered for 1.00 seconds")))) + +(ert-deftest erc-d-run-linger-fail () + :tags '(:unstable :expensive-test) + (let ((erc-server-flood-penalty 0.1) + errors) + (erc-d-tests-with-failure-spy + errors '(erc-d--teardown erc-d-command) + (erc-d-tests-with-server (_ _) linger + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (erc-d-t-search-for 2 "hey") + (erc-cmd-MSG "#chan hi")) + (erc-d-t-wait-for 10 "Bad match" errors))) + (should (string-match-p "Match failed.*hi" (cadr (pop errors)))))) + +(ert-deftest erc-d-run-linger-direct () + :tags '(:unstable :expensive-test) + (let* ((dumb-server (erc-d-run "localhost" t + 'linger-multi-a 'linger-multi-b)) + (port (process-contact dumb-server :service)) + (dumb-server-buffer (get-buffer "*erc-d-server*")) + (client-buffer-a (get-buffer-create "*erc-d-client-a*")) + (client-buffer-b (get-buffer-create "*erc-d-client-b*")) + (start (current-time)) + client-a client-b) + (with-current-buffer "*erc-d-server*" (erc-d-t-search-for 4 "Starting")) + (setq client-a (open-network-stream "erc-d-client-a" client-buffer-a + "localhost" port + :coding 'binary) + client-b (open-network-stream "erc-d-client-b" client-buffer-b + "localhost" port + :coding 'binary)) + (process-send-string client-a "PASS :a\r\n") + (sleep-for 0.01) + (process-send-string client-b "PASS :b\r\n") + (sleep-for 0.01) + (erc-d-t-wait-for 3 "dumb-server death" + (not (process-live-p dumb-server))) + (ert-info ("Ensure linger of one second") + (should (time-less-p 1 (time-subtract (current-time) start))) + (should (time-less-p (time-subtract (current-time) start) 1.5))) + (delete-process client-a) + (delete-process client-b) + (when noninteractive + (kill-buffer client-buffer-a) + (kill-buffer client-buffer-b) + (kill-buffer dumb-server-buffer)))) + +(ert-deftest erc-d-run-drop-direct () + :tags '(:unstable) + (let* ((dumb-server (erc-d-run "localhost" t 'drop-a 'drop-b)) + (port (process-contact dumb-server :service)) + (dumb-server-buffer (get-buffer "*erc-d-server*")) + (client-buffer-a (get-buffer-create "*erc-d-client-a*")) + (client-buffer-b (get-buffer-create "*erc-d-client-b*")) + (start (current-time)) + client-a client-b) + (with-current-buffer "*erc-d-server*" (erc-d-t-search-for 4 "Starting")) + (setq client-a (open-network-stream "erc-d-client-a" client-buffer-a + "localhost" port + :coding 'binary) + client-b (open-network-stream "erc-d-client-b" client-buffer-b + "localhost" port + :coding 'binary)) + (process-send-string client-a "PASS :a\r\n") + (sleep-for 0.01) + (process-send-string client-b "PASS :b\r\n") + (erc-d-t-wait-for 3 "client-a dies" (not (process-live-p client-a))) + (should (time-less-p (time-subtract (current-time) start) 0.32)) + (erc-d-t-wait-for 3 "dumb-server death" + (not (process-live-p dumb-server))) + (ert-info ("Ensure linger of one second") + (should (time-less-p 1 (time-subtract (current-time) start)))) + (delete-process client-a) + (delete-process client-b) + (when noninteractive + (kill-buffer client-buffer-a) + (kill-buffer client-buffer-b) + (kill-buffer dumb-server-buffer)))) + +(ert-deftest erc-d-run-no-match () + :tags '(:expensive-test) + (let ((erc-d-linger-secs 1) + erc-server-auto-reconnect + errors) + (erc-d-tests-with-failure-spy errors '(erc-d--teardown erc-d-command) + (erc-d-tests-with-server (_ erc-server-buffer) no-match + (with-current-buffer erc-server-buffer + (erc-d-t-search-for 2 "away") + (erc-cmd-JOIN "#foo") + (erc-d-t-wait-for 10 "Bad match" errors)))) + (should (string-match-p "Match failed.*foo.*chan" (cadr (pop errors)))) + (should-not (get-buffer "#foo")))) + +(ert-deftest erc-d-run-timeout () + :tags '(:expensive-test) + (let ((erc-d-linger-secs 1) + err errors) + (erc-d-tests-with-failure-spy errors '(erc-d--teardown) + (erc-d-tests-with-server (_ _) timeout + (erc-d-t-wait-for 10 "error caught" errors))) + (setq err (pop errors)) + (should (eq (car err) 'erc-d-timeout)) + (should (string-match-p "Timed out" (cadr err))))) + +(ert-deftest erc-d-run-unexpected () + :tags '(:expensive-test) + (let ((erc-d-linger-secs 2) + errors) + (erc-d-tests-with-failure-spy + errors '(erc-d--teardown erc-d-command) + (erc-d-tests-with-server (_ _) unexpected + (ert-info ("All specs consumed when more input arrives") + (erc-d-t-wait-for 10 "error caught" (cdr errors))))) + (should (string-match-p "unexpected.*MODE" (cadr (pop errors)))) + ;; Nonsensical normally because func would have already exited when + ;; first error was thrown + (should (string-match-p "Match failed" (cadr (pop errors)))))) + +(ert-deftest erc-d-run-unexpected-depleted () + :tags '(:expensive-test) + (let ((erc-d-linger-secs 3) + errors) + (erc-d-tests-with-failure-spy errors '(erc-d--teardown erc-d-command) + (let* ((dumb-server-buffer (get-buffer-create "*erc-d-server*")) + (dumb-server (erc-d-run "localhost" t 'depleted)) + (expect (erc-d-t-make-expecter)) + (client-buf (get-buffer-create "*erc-d-client*")) + client-proc) + (with-current-buffer dumb-server-buffer + (erc-d-t-search-for 3 "Starting")) + (setq client-proc (make-network-process + :buffer client-buf + :name "erc-d-client" + :family 'ipv4 + :noquery t + :coding 'binary + :service (process-contact dumb-server :service) + :host "localhost")) + (with-current-buffer dumb-server-buffer + (funcall expect 3 "open from")) + (process-send-string client-proc "PASS :changeme\r\n") + (sleep-for 0.01) + (process-send-string client-proc "NICK tester\r\n") + (sleep-for 0.01) + (process-send-string client-proc "USER user 0 * :tester\r\n") + (sleep-for 0.01) + (when (process-live-p client-proc) + (process-send-string client-proc "BLAH :too much\r\n") + (sleep-for 0.01)) + (with-current-buffer client-buf + (funcall expect 3 "Welcome to the Internet")) + (erc-d-t-wait-for 2 "dumb-server death" + (not (process-live-p dumb-server))) + (delete-process client-proc) + (when noninteractive + (kill-buffer client-buf) + (kill-buffer dumb-server-buffer)))) + (should (string-match-p "unexpected.*BLAH" (cadr (pop errors)))) + ;; Wouldn't happen IRL + (should (string-match-p "unexpected.*BLAH" (cadr (pop errors)))) + (should-not errors))) + +(defun erc-d-tests--dynamic-match-user (_dialog exchange) + "Shared pattern/response handler for canned dynamic DIALOG test." + (should (string= (match-string 1 (erc-d-exchange-request exchange)) + "tester"))) + +(defun erc-d-tests--run-dynamic () + "Perform common assertions for \"dynamic\" dialog." + (erc-d-tests-with-server (dumb-server erc-server-buffer) dynamic + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (erc-d-t-search-for 2 "tester: hey")) + (with-current-buffer erc-server-buffer + (let ((expect (erc-d-t-make-expecter))) + (funcall expect 2 "host is irc.fsf.org") + (funcall expect 2 "modes for tester"))) + (with-current-buffer (process-buffer dumb-server) + (erc-d-t-search-for 2 "irc.fsf.org")) + (when noninteractive + (kill-buffer "#chan")))) + +(ert-deftest erc-d-run-dynamic-default-match () + :tags '(:expensive-test) + (let* (dynamic-tally + (erc-d-tmpl-vars '((user . "user") + (ignored . ((a b) (: a space b))) + (realname . (group (+ graph))))) + (nick (lambda (a) + (push '(nick . match-user) dynamic-tally) + (funcall a :set (funcall a :match 1) 'export))) + (dom (lambda (a) + (push '(dom . match-user) dynamic-tally) + (funcall a :set erc-d-server-fqdn))) + (erc-d-match-handlers + (list :user (lambda (d e) + (erc-d-exchange-rebind d e 'nick nick) + (erc-d-exchange-rebind d e 'dom dom) + (erc-d-tests--dynamic-match-user d e)) + :mode-user (lambda (d e) + (erc-d-exchange-rebind d e 'nick "tester") + (erc-d-exchange-rebind d e 'dom dom)))) + (erc-d-server-fqdn "irc.fsf.org")) + (erc-d-tests--run-dynamic) + (should (equal '((dom . match-user) (nick . match-user) (dom . match-user)) + dynamic-tally)))) + +(ert-deftest erc-d-run-dynamic-default-match-rebind () + :tags '(:expensive-test) + (let* (tally + ;; + (erc-d-tmpl-vars '((user . "user") + (ignored . ((a b) (: a space b))) + (realname . (group (+ graph))))) + (erc-d-match-handlers + (list :user + (lambda (d e) + (erc-d-exchange-rebind + d e 'nick + (lambda (a) + (push 'bind-nick tally) + (funcall a :rebind 'nick (funcall a :match 1) 'export))) + (erc-d-exchange-rebind + d e 'dom + (lambda () + (push 'bind-dom tally) + (erc-d-exchange-rebind d e 'dom erc-d-server-fqdn))) + (erc-d-tests--dynamic-match-user d e)) + :mode-user + (lambda (d e) + (erc-d-exchange-rebind d e 'nick "tester") + (erc-d-exchange-rebind d e 'dom erc-d-server-fqdn)))) + (erc-d-server-fqdn "irc.fsf.org")) + (erc-d-tests--run-dynamic) + (should (equal '(bind-nick bind-dom) tally)))) + +(ert-deftest erc-d-run-dynamic-runtime-stub () + :tags '(:expensive-test) + (let ((erc-d-tmpl-vars '((token . (group (or "barnet" "foonet"))))) + (erc-d-match-handlers + (list :pass (lambda (d _e) + (erc-d-load-replacement-dialog d 'dynamic-foonet)))) + (erc-d-tests-with-server-password "foonet:changeme")) + (erc-d-tests-with-server (_ erc-server-buffer) + (dynamic-stub dynamic-foonet) + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "#chan")) + (erc-d-t-search-for 2 "alice:") + (erc-d-t-absent-for 0.1 "joe")) + (with-current-buffer erc-server-buffer + (let ((expect (erc-d-t-make-expecter))) + (funcall expect 2 "host is irc.foonet.org") + (funcall expect 2 "NETWORK=FooNet"))) + (when noninteractive + (kill-buffer "#chan"))))) + +(ert-deftest erc-d-run-dynamic-runtime-stub-skip () + :tags '(:expensive-test) + (let ((erc-d-tmpl-vars '((token . "barnet"))) + (erc-d-match-handlers + (list :pass (lambda (d _e) + (erc-d-load-replacement-dialog + d 'dynamic-barnet 1)))) + (erc-d-tests-with-server-password "barnet:changeme")) + (erc-d-tests-with-server (_ erc-server-buffer) + (dynamic-stub dynamic-barnet) + (with-current-buffer (erc-d-t-wait-for 3 (get-buffer "#chan")) + (erc-d-t-search-for 2 "joe:") + (erc-d-t-absent-for 0.1 "alice")) + (with-current-buffer erc-server-buffer + (let ((expect (erc-d-t-make-expecter))) + (funcall expect 2 "host is irc.barnet.org") + (funcall expect 2 "NETWORK=BarNet"))) + (when noninteractive + (kill-buffer "#chan"))))) + +;; Two servers, in-process, one client per +(ert-deftest erc-d-run-dual-direct () + :tags '(:expensive-test) + (let* ((erc-d--slow-mo -1) + (server-a (erc-d-run "localhost" t "erc-d-server-a" 'dynamic-foonet)) + (server-b (erc-d-run "localhost" t "erc-d-server-b" 'dynamic-barnet)) + (server-a-buffer (get-buffer "*erc-d-server-a*")) + (server-b-buffer (get-buffer "*erc-d-server-b*")) + (client-a-buffer (get-buffer-create "*erc-d-client-a*")) + (client-b-buffer (get-buffer-create "*erc-d-client-b*")) + client-a client-b) + (with-current-buffer server-a-buffer (erc-d-t-search-for 4 "Starting")) + (with-current-buffer server-b-buffer (erc-d-t-search-for 4 "Starting")) + (setq client-a (make-network-process + :buffer client-a-buffer + :name "erc-d-client-a" + :family 'ipv4 + :noquery t + :coding 'binary + :service (process-contact server-a :service) + :host "localhost") + client-b (make-network-process + :buffer client-b-buffer + :name "erc-d-client-b" + :family 'ipv4 + :noquery t + :coding 'binary + :service (process-contact server-b :service) + :host "localhost")) + ;; Also tests slo-mo indirectly because FAKE would fail without it + (process-send-string client-a "NICK tester\r\n") + (process-send-string client-b "FAKE noop\r\nNICK tester\r\n") + (sleep-for 0.01) + (process-send-string client-a "USER user 0 * :tester\r\n") + (process-send-string client-b "USER user 0 * :tester\r\n") + (sleep-for 0.01) + (process-send-string client-a "MODE tester +i\r\n") + (process-send-string client-b "MODE tester +i\r\n") + (sleep-for 0.01) + (process-send-string client-a "MODE #chan\r\n") + (process-send-string client-b "MODE #chan\r\n") + (sleep-for 0.01) + (erc-d-t-wait-for 2 "server-a death" (not (process-live-p server-a))) + (erc-d-t-wait-for 2 "server-b death" (not (process-live-p server-b))) + (when noninteractive + (kill-buffer client-a-buffer) + (kill-buffer client-b-buffer) + (kill-buffer server-a-buffer) + (kill-buffer server-b-buffer)))) + +;; This can be removed; only exists to get a baseline for next test +(ert-deftest erc-d-run-fuzzy-direct () + :tags '(:expensive-test) + (let* ((erc-d-tmpl-vars + `((now . ,(lambda () (format-time-string "%FT%T.%3NZ" nil t))))) + (dumb-server (erc-d-run "localhost" t 'fuzzy)) + (dumb-server-buffer (get-buffer "*erc-d-server*")) + (client-buffer (get-buffer-create "*erc-d-client*")) + client) + (with-current-buffer "*erc-d-server*" (erc-d-t-search-for 4 "Starting")) + (setq client (make-network-process + :buffer client-buffer + :name "erc-d-client" + :family 'ipv4 + :noquery t + :coding 'binary + :service (process-contact dumb-server :service) + :host "localhost")) + ;; We could also just send this as a single fatty + (process-send-string client "PASS :changeme\r\n") + (sleep-for 0.01) + (process-send-string client "NICK tester\r\n") + (sleep-for 0.01) + (process-send-string client "USER user 0 * :tester\r\n") + (sleep-for 0.01) + (process-send-string client "MODE tester +i\r\n") + (sleep-for 0.01) + (process-send-string client "JOIN #bar\r\n") + (sleep-for 0.01) + (process-send-string client "JOIN #foo\r\n") + (sleep-for 0.01) + (process-send-string client "MODE #bar\r\n") + (sleep-for 0.01) + (process-send-string client "MODE #foo\r\n") + (sleep-for 0.01) + (erc-d-t-wait-for 1 "dumb-server death" + (not (process-live-p dumb-server))) + (when noninteractive + (kill-buffer client-buffer) + (kill-buffer dumb-server-buffer)))) + +;; Without adjusting penalty, takes ~15 secs. With is comparable to direct ^. +(ert-deftest erc-d-run-fuzzy () + :tags '(:expensive-test) + (let ((erc-server-flood-penalty 1.2) ; penalty < margin/sends is basically 0 + (erc-d-linger-secs 0.1) + (erc-d-tmpl-vars + `((now . ,(lambda () (format-time-string "%FT%T.%3NZ" nil t))))) + erc-server-auto-reconnect) + (erc-d-tests-with-server (_ erc-server-buffer) fuzzy + (with-current-buffer erc-server-buffer + (erc-d-t-search-for 2 "away") + (goto-char erc-input-marker) + (erc-cmd-JOIN "#bar")) + (erc-d-t-wait-for 2 (get-buffer "#bar")) + (with-current-buffer erc-server-buffer + (erc-cmd-JOIN "#foo")) + (erc-d-t-wait-for 20 (get-buffer "#foo")) + (with-current-buffer "#bar" + (erc-d-t-search-for 1 "was created on")) + (with-current-buffer "#foo" + (erc-d-t-search-for 5 "was created on"))))) + +(ert-deftest erc-d-run-no-block () + :tags '(:expensive-test) + (let ((erc-server-flood-penalty 1) + (erc-d-linger-secs 1.2) + (expect (erc-d-t-make-expecter)) + erc-server-auto-reconnect) + (erc-d-tests-with-server (_ erc-server-buffer) no-block + (with-current-buffer erc-server-buffer + (funcall expect 2 "away") + (funcall expect 1 erc-prompt) + (with-current-buffer erc-server-buffer (erc-cmd-JOIN "#foo"))) + (with-current-buffer (erc-d-t-wait-for 2 (get-buffer "#foo")) + (funcall expect 2 "was created on")) + + (ert-info ("Join #bar") + (with-current-buffer erc-server-buffer (erc-cmd-JOIN "#bar")) + (erc-d-t-wait-for 2 (get-buffer "#bar"))) + + (with-current-buffer "#bar" (funcall expect 1 "was created on")) + + (ert-info ("Server expects next pattern but keeps sending") + (with-current-buffer "#foo" (funcall expect 2 "Rosalind, I will ")) + (with-current-buffer "#bar" (funcall expect 1 "hi 123")) + (with-current-buffer "#foo" + (should-not (search-forward "<bob> I am heard" nil t)) + (funcall expect 1.5 "<bob> I am heard")))))) + +(defun erc-d-tests--run-proxy-direct (dumb-server dumb-server-buffer port) + "Start DUMB-SERVER with DUMB-SERVER-BUFFER and PORT. +These are steps shared by in-proc and subproc variants testing a +bouncer-like setup." + (when (version< emacs-version "28") (ert-skip "TODO connection refused")) + (let ((client-buffer-foo (get-buffer-create "*erc-d-client-foo*")) + (client-buffer-bar (get-buffer-create "*erc-d-client-bar*")) + (expect (erc-d-t-make-expecter)) + client-foo + client-bar) + (setq client-foo (make-network-process + :buffer client-buffer-foo + :name "erc-d-client-foo" + :family 'ipv4 + :noquery t + :coding 'binary + :service port + :host "localhost") + client-bar (make-network-process + :buffer client-buffer-bar + :name "erc-d-client-bar" + :family 'ipv4 + :noquery t + :coding 'binary + :service port + :host "localhost")) + (with-current-buffer dumb-server-buffer + (funcall expect 3 "open from")) + (process-send-string client-foo "PASS :foo:changeme\r\n") + (process-send-string client-bar "PASS :bar:changeme\r\n") + (sleep-for 0.01) + (process-send-string client-foo "NICK tester\r\n") + (process-send-string client-bar "NICK tester\r\n") + (sleep-for 0.01) + (process-send-string client-foo "USER user 0 * :tester\r\n") + (process-send-string client-bar "USER user 0 * :tester\r\n") + (sleep-for 0.01) + (process-send-string client-foo "MODE tester +i\r\n") + (process-send-string client-bar "MODE tester +i\r\n") + (sleep-for 0.01) + (with-current-buffer client-buffer-foo + (funcall expect 3 "FooNet") + (funcall expect 3 "irc.foo.net") + (funcall expect 3 "marked as being away") + (goto-char (point-min)) + (should-not (search-forward "bar" nil t))) + (with-current-buffer client-buffer-bar + (funcall expect 3 "BarNet") + (funcall expect 3 "irc.bar.net") + (funcall expect 3 "marked as being away") + (goto-char (point-min)) + (should-not (search-forward "foo" nil t))) + (erc-d-t-wait-for 2 "dumb-server death" + (not (process-live-p dumb-server))) + (delete-process client-foo) + (delete-process client-bar) + (when noninteractive + (kill-buffer client-buffer-foo) + (kill-buffer client-buffer-bar) + (kill-buffer dumb-server-buffer)))) + +;; This test shows the simplest way to set up template variables: put +;; everything needed for the whole session in `erc-d-tmpl-vars' before +;; starting the server. + +(ert-deftest erc-d-run-proxy-direct-spec-vars () + :tags '(:expensive-test) + (let* ((dumb-server-buffer (get-buffer-create "*erc-d-server*")) + (erc-d-linger-secs 0.5) + (erc-d-tmpl-vars + `((network . (group (+ alpha))) + (fqdn . ,(lambda (a) + (let ((network (funcall a :match 1 'pass))) + (should (member network '("foo" "bar"))) + (funcall a :set (concat "irc." network ".net"))))) + (net . ,(lambda (a) + (let ((network (funcall a :match 1 'pass))) + (should (member network '("foo" "bar"))) + (concat (capitalize network) "Net")))))) + (dumb-server (erc-d-run "localhost" t 'proxy-foonet 'proxy-barnet)) + (port (process-contact dumb-server :service))) + (with-current-buffer dumb-server-buffer + (erc-d-t-search-for 3 "Starting")) + (erc-d-tests--run-proxy-direct dumb-server dumb-server-buffer port))) + +(cl-defun erc-d-tests--start-server (&key dialogs buffer linger program libs) + "Start and return a server in a subprocess using BUFFER and PORT. +DIALOGS are symbols representing the base names of dialog files in +`erc-d-u-canned-dialog-dir'. LIBS are extra files to load." + (push (locate-library "erc-d" nil (list erc-d-u--library-directory)) libs) + (cl-assert (car libs)) + (let* ((args `("erc-d-server" ,buffer + ,(concat invocation-directory invocation-name) + "-Q" "-batch" "-L" ,erc-d-u--library-directory + ,@(let (o) (while libs (push (pop libs) o) (push "-l" o)) o) + "-eval" ,(format "%S" program) "-f" "erc-d-serve" + ,@(when linger (list "--linger" (number-to-string linger))) + ,@(mapcar #'erc-d-u--expand-dialog-symbol dialogs))) + (proc (apply #'start-process args))) + (set-process-query-on-exit-flag proc nil) + (with-current-buffer buffer + (erc-d-t-search-for 5 "Starting") + (search-forward " (") + (backward-char)) + (let ((pair (read buffer))) + (cons proc (cdr pair))))) + +(ert-deftest erc-d-run-proxy-direct-subprocess () + :tags '(:expensive-test) + (let* ((buffer (get-buffer-create "*erc-d-server*")) + ;; These are quoted because they're passed as printed forms to subproc + (fqdn '(lambda (a e) + (let* ((d (erc-d-exchange-dialog e)) + (name (erc-d-dialog-name d))) + (funcall a :set (if (eq name 'proxy-foonet) + "irc.foo.net" + "irc.bar.net"))))) + (net '(lambda (a) + (funcall a :rebind 'net + (if (eq (funcall a :dialog-name) 'proxy-foonet) + "FooNet" + "BarNet")))) + (program `(setq erc-d-tmpl-vars '((fqdn . ,fqdn) + (net . ,net) + (network . (group (+ alpha)))))) + (port (erc-d-tests--start-server + :linger 0.3 + :program program + :buffer buffer + :dialogs '(proxy-foonet proxy-barnet))) + (server (pop port))) + (erc-d-tests--run-proxy-direct server buffer port))) + +(ert-deftest erc-d-run-proxy-direct-subprocess-lib () + :tags '(:expensive-test) + (let* ((buffer (get-buffer-create "*erc-d-server*")) + (lib (expand-file-name "proxy-subprocess.el" + (ert-resource-directory))) + (port (erc-d-tests--start-server :linger 0.3 + :buffer buffer + :dialogs '(proxy-foonet proxy-barnet) + :libs (list lib))) + (server (pop port))) + (erc-d-tests--run-proxy-direct server buffer port))) + +(ert-deftest erc-d-run-no-pong () + :tags '(:expensive-test) + (let* (erc-d-auto-pong + ;; + (erc-d-tmpl-vars + `((nonce . (group (: digit digit))) + (echo . ,(lambda (a) + (should (string= (funcall a :match 1) "42")) "42")))) + (dumb-server-buffer (get-buffer-create "*erc-d-server*")) + (dumb-server (erc-d-run "localhost" t 'no-pong)) + (expect (erc-d-t-make-expecter)) + (client-buf (get-buffer-create "*erc-d-client*")) + client-proc) + (with-current-buffer dumb-server-buffer + (erc-d-t-search-for 3 "Starting")) + (setq client-proc (make-network-process + :buffer client-buf + :name "erc-d-client" + :family 'ipv4 + :noquery t + :coding 'binary + :service (process-contact dumb-server :service) + :host "localhost")) + (with-current-buffer dumb-server-buffer + (funcall expect 3 "open from")) + (process-send-string client-proc "PASS :changeme\r\nNICK tester\r\n") + (sleep-for 0.01) + (process-send-string client-proc "USER user 0 * :tester\r\n") + (sleep-for 0.01) + (process-send-string client-proc "MODE tester +i\r\n") + (sleep-for 0.01) + (with-current-buffer client-buf + (funcall expect 3 "ExampleOrg") + (funcall expect 3 "irc.example.org") + (funcall expect 3 "marked as being away")) + (ert-info ("PING is not intercepted by specialized method") + (process-send-string client-proc "PING 42\r\n") + (with-current-buffer client-buf + (funcall expect 3 "PONG"))) + (erc-d-t-wait-for 2 "dumb-server death" + (not (process-live-p dumb-server))) + (delete-process client-proc) + (when noninteractive + (kill-buffer client-buf) + (kill-buffer dumb-server-buffer)))) + +;; Inspect replies as they arrive within a single exchange, i.e., ensure we +;; don't regress to prior buggy version in which inspection wasn't possible +;; until all replies had been sent by the server. +(ert-deftest erc-d-run-incremental () + :tags '(:expensive-test) + (let ((erc-server-flood-penalty 0) + (expect (erc-d-t-make-expecter)) + erc-d-linger-secs) + (erc-d-tests-with-server (_ erc-server-buffer) incremental + (with-current-buffer erc-server-buffer + (funcall expect 3 "marked as being away")) + (with-current-buffer erc-server-buffer + (erc-cmd-JOIN "#foo")) + (with-current-buffer (erc-d-t-wait-for 1 (get-buffer "#foo")) + (funcall expect 1 "Users on #foo") + (funcall expect 1 "Look for me") + (not (search-forward "Done" nil t)) + (funcall expect 10 "Done") + (erc-send-message "Hi"))))) + +(ert-deftest erc-d-unix-socket-direct () + :tags '(:expensive-test) + (skip-unless (featurep 'make-network-process '(:family local))) + (let* ((erc-d-linger-secs 0.1) + (sock (expand-file-name "erc-d.sock" temporary-file-directory)) + (dumb-server (erc-d-run nil sock 'basic)) + (dumb-server-buffer (get-buffer "*erc-d-server*")) + (client-buffer (get-buffer-create "*erc-d-client*")) + client) + (with-current-buffer "*erc-d-server*" + (erc-d-t-search-for 4 "Starting")) + (unwind-protect + (progn + (setq client (make-network-process + :buffer client-buffer + :name "erc-d-client" + :family 'local + :noquery t + :coding 'binary + :service sock)) + (process-send-string client "PASS :changeme\r\n") + (sleep-for 0.01) + (process-send-string client "NICK tester\r\n") + (sleep-for 0.01) + (process-send-string client "USER user 0 * :tester\r\n") + (sleep-for 0.1) + (process-send-string client "MODE tester +i\r\n") + (sleep-for 0.01) + (process-send-string client "MODE #chan\r\n") + (sleep-for 0.01) + (erc-d-t-wait-for 1 "dumb-server death" + (not (process-live-p dumb-server))) + (when noninteractive + (kill-buffer client-buffer) + (kill-buffer dumb-server-buffer))) + (delete-file sock)))) + +(ert-deftest erc-d-run-direct-foreign-protocol () + :tags '(:expensive-test) + (let* ((server (erc-d-run "localhost" t "erc-d-server" 'foreign + :ending "\n")) + (server-buffer (get-buffer "*erc-d-server*")) + (client-buffer (get-buffer-create "*erc-d-client*")) + client) + (with-current-buffer server-buffer (erc-d-t-search-for 4 "Starting")) + (setq client (make-network-process + :buffer client-buffer + :name "erc-d-client" + :family 'ipv4 + :noquery t + :coding 'binary + :service (process-contact server :service) + :host "localhost")) + (process-send-string client "ONE one\n") + (with-current-buffer client-buffer + (erc-d-t-search-for 5 "echo ONE one")) + (process-send-string client "TWO two\n") + (with-current-buffer client-buffer + (erc-d-t-search-for 2 "echo TWO two")) + (erc-d-t-wait-for 2 "server death" (not (process-live-p server))) + (when noninteractive + (kill-buffer client-buffer) + (kill-buffer server-buffer)))) + +;;; erc-d-tests.el ends here diff --git a/test/lisp/erc/resources/erc-d/erc-d-u.el b/test/lisp/erc/resources/erc-d/erc-d-u.el new file mode 100644 index 00000000000..ce13efef624 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/erc-d-u.el @@ -0,0 +1,213 @@ +;;; erc-d-u.el --- Helpers for ERC test server -*- lexical-binding: t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; The utilities here are kept separate from those in `erc-d' so that +;; tests running the server in a subprocess can use them without +;; having to require the main lib. If migrating outside of test/lisp, +;; there may be no reason to continue this. +;; +;; Another (perhaps misguided) goal here is to avoid having ERC itself +;; as a dependency. +;; +;; FIXME this ^ is no longer the case (ERC is not a dependency) + +;;; Code: +(require 'rx) +(require 'subr-x) +(eval-when-compile (require 'ert)) + +(defvar erc-d-u--canned-buffers nil + "List of canned dialog buffers currently open for reading.") + +(cl-defstruct (erc-d-u-scan-d) ; dialog scanner + (buf nil :type buffer) + (done nil :type boolean) + (last nil :type integer) + (hunks nil :type (list-of marker)) + (f #'erc-d-u--read-exchange-default :type function)) + +(cl-defstruct (erc-d-u-scan-e) ; exchange scanner + (sd nil :type erc-d-u-scan-d) + (pos nil :type marker)) + +(defun erc-d-u--read-dialog (info) + "Read dialog file and stash relevant state in `erc-d-u-scan-d' INFO." + (if (and (buffer-live-p (erc-d-u-scan-d-buf info)) + (with-current-buffer (erc-d-u-scan-d-buf info) + (condition-case _err + (progn + (when (erc-d-u-scan-d-last info) + (goto-char (erc-d-u-scan-d-last info)) + (forward-list)) + (setf (erc-d-u-scan-d-last info) (point)) + (down-list) + (push (set-marker (make-marker) (point)) + (erc-d-u-scan-d-hunks info))) + ((end-of-buffer scan-error) + (setf (erc-d-u-scan-d-done info) t) + nil)))) + (make-erc-d-u-scan-e :sd info :pos (car (erc-d-u-scan-d-hunks info))) + (unless (erc-d-u-scan-d-hunks info) + (kill-buffer (erc-d-u-scan-d-buf info)) + nil))) + +(defun erc-d-u--read-exchange-default (info) + "Read from marker in exchange `erc-d-u-scan-e' object INFO." + (let ((hunks (erc-d-u-scan-e-sd info)) + (pos (erc-d-u-scan-e-pos info))) + (or (and (erc-d-u-scan-d-hunks hunks) + (with-current-buffer (erc-d-u-scan-d-buf hunks) + (goto-char pos) + (condition-case _err + (read pos) + ;; Raised unless malformed + (invalid-read-syntax + nil)))) + (unless (or (cl-callf (lambda (s) (delq pos s)) ; flip + (erc-d-u-scan-d-hunks hunks)) + (not (erc-d-u-scan-d-done hunks))) + (kill-buffer (erc-d-u-scan-d-buf hunks)) + nil)))) + +(defun erc-d-u--read-exchange (info) + "Call exchange reader assigned in `erc-d-u-scan-e' object INFO." + (funcall (erc-d-u-scan-d-f (erc-d-u-scan-e-sd info)) info)) + +(defun erc-d-u--canned-read (file) + "Dispense a reader for each exchange in dialog FILE." + (let ((buf (generate-new-buffer (file-name-nondirectory file)))) + (push buf erc-d-u--canned-buffers) + (with-current-buffer buf + (setq-local parse-sexp-ignore-comments t + coding-system-for-read 'utf-8) + (add-hook 'kill-buffer-hook + (lambda () (setq erc-d-u--canned-buffers + (delq buf erc-d-u--canned-buffers))) + nil 'local) + (insert-file-contents-literally file) + (lisp-data-mode)) + (make-erc-d-u-scan-d :buf buf))) + +(defvar erc-d-u--library-directory (file-name-directory load-file-name)) +(defvar erc-d-u-canned-dialog-dir + (file-name-as-directory (expand-file-name "resources" + erc-d-u--library-directory))) + +(defun erc-d-u--normalize-canned-name (dialog) + "Return DIALOG name as a symbol without validating it." + (if (symbolp dialog) + dialog + (intern (file-name-base dialog)))) + +(defvar erc-d-u-canned-file-name-extension ".eld") + +(defun erc-d-u--expand-dialog-symbol (dialog) + "Return filename based on symbol DIALOG." + (let ((name (symbol-name dialog))) + (unless (equal (file-name-extension name) + erc-d-u-canned-file-name-extension) + (setq name (concat name erc-d-u-canned-file-name-extension))) + (expand-file-name name erc-d-u-canned-dialog-dir))) + +(defun erc-d-u--massage-canned-name (dialog) + "Return DIALOG in a form acceptable to `erc-d-run'." + (if (or (symbolp dialog) (file-exists-p dialog)) + dialog + (erc-d-u--expand-dialog-symbol (intern dialog)))) + +(defun erc-d-u--canned-load-dialog (dialog) + "Load dispensing exchanges from DIALOG. +If DIALOG is a string, consider it a filename. Otherwise find a file +in `erc-d-u-canned-dialog-dir' with a base name matching the symbol's +name. + +Return an iterator that yields exchanges, each one an iterator of spec +forms. The first is a so-called request spec and the rest are composed +of zero or more response specs." + (when (symbolp dialog) + (setq dialog (erc-d-u--expand-dialog-symbol dialog))) + (unless (file-exists-p dialog) + (error "File not found: %s" dialog)) + (erc-d-u--canned-read dialog)) + +(defun erc-d-u--read-exchange-slowly (num orig info) + (when-let ((spec (funcall orig info))) + (when (symbolp (car spec)) + (setf spec (copy-sequence spec) + (nth 1 spec) (cond ((functionp num) (funcall num (nth 1 spec))) + ((< num 0) (max (nth 1 spec) (- num))) + (t (+ (nth 1 spec) num))))) + spec)) + +(defun erc-d-u--rewrite-for-slow-mo (num read-info) + "Return READ-INFO with a modified reader. +When NUM is a positive number, delay incoming requests by NUM more +seconds. If NUM is negative, raise insufficient incoming delays to at +least -NUM seconds. If NUM is a function, set each delay to whatever it +returns when called with the existing value." + (let ((orig (erc-d-u-scan-d-f read-info))) + (setf (erc-d-u-scan-d-f read-info) + (apply-partially #'erc-d-u--read-exchange-slowly num orig)) + read-info)) + +(defun erc-d-u--get-remote-port (process) + "Return peer TCP port for client PROCESS. +When absent, just generate an id." + (let ((remote (plist-get (process-contact process t) :remote))) + (if (vectorp remote) + (aref remote (1- (length remote))) + (format "%s:%d" (process-contact process :local) + (logand 1023 (time-convert nil 'integer)))))) + +(defun erc-d-u--format-bind-address (process) + "Return string or (STRING . INT) for bind address of network PROCESS." + (let ((local (process-contact process :local))) + (if (vectorp local) ; inet + (cons (mapconcat #'number-to-string (seq-subseq local 0 -1) ".") + (aref local (1- (length local)))) + local))) + +(defun erc-d-u--unkeyword (plist) + "Return a copy of PLIST with keywords keys converted to non-keywords." + (cl-loop for (key value) on plist by #'cddr + when (keywordp key) + do (setq key (intern (substring (symbol-name key) 1))) + append (list key value))) + +(defun erc-d-u--massage-rx-args (key val) + " Massage val so it's suitable for an `rx-let' binding. +Handle cases in which VAL is ([ARGLIST] RX-FORM) rather than just +RX-FORM. KEY becomes the binding name." + (if (and (listp val) + (cdr val) + (not (cddr val)) + (consp (car val))) + (cons key val) + (list key val))) + +(defvar-local erc-d-u--process-buffer nil + "Beacon for erc-d process buffers. +The server process is usually deleted first, but we may want to examine +the buffer afterward.") + +(provide 'erc-d-u) +;;; erc-d-u.el ends here diff --git a/test/lisp/erc/resources/erc-d/erc-d.el b/test/lisp/erc/resources/erc-d/erc-d.el new file mode 100644 index 00000000000..d6082227c52 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/erc-d.el @@ -0,0 +1,1009 @@ +;;; erc-d.el --- A dumb test server for ERC -*- lexical-binding: t -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This is a netcat style server for testing ERC. The "d" in the name +;; stands for "daemon" as well as for "dialog" (as well as for "dumb" +;; because this server isn't very smart). It either spits out a +;; canned reply when an incoming request matches the expected regexp +;; or signals an error and dies. The entry point function is +;; `erc-d-run'. +;; +;; Canned scripts, or "dialogs," should be Lisp-Data files containing +;; one or more request/reply forms like this: +;; +;; | ((mode-chan 1.5 "MODE #chan") ; request: tag, expr, regex +;; | (0.1 ":irc.org 324 bob #chan +Cint") ; reply: delay, content +;; | (0.0 ":irc.org 329 bob #chan 12345")) ; reply: ... +;; +;; These are referred to as "exchanges." The first element is a list +;; whose CAR is a descriptive "tag" and whose CDR is an incoming +;; "spec" representing an inbound message from the client. The rest +;; of the exchange is composed of outgoing specs representing +;; server-to-client messages. A tag can be any symbol (ideally unique +;; in the dialog), but a leading tilde means the request should be +;; allowed to arrive out of order (within the allotted time). +;; +;; The first element in an incoming spec is a number indicating the +;; maximum number of seconds to wait for a match before raising an +;; error. The CDR is interpreted as the collective arguments of an +;; `rx' form to be matched against the raw request (stripped of its +;; CRLF line ending). A "string-start" backslash assertion, "\\`", is +;; prepended to all patterns. +;; +;; Similarly, the leading number in an *outgoing* spec indicates how +;; many seconds to wait before sending the line, which is rendered by +;; concatenating the other members after evaluating each in place. +;; CRLF line endings are appended on the way out and should be absent. +;; +;; Recall that IRC is "asynchronous," meaning some flow intervals +;; don't jibe with lockstep request-reply semantics. However, for our +;; purposes, grouping things as [input, output1, ..., outputN] makes +;; sense, even though input and output may be completely unrelated. +;; +;; Template interpolation: +;; +;; A rudimentary templating facility is provided for additional +;; flexibility. However, it's best to keep things simple (even if +;; overly verbose), so others can easily tell what's going on at a +;; glance. If necessary, consult existing tests for examples (grep +;; for the variables `erc-d-tmpl-vars' and `erc-d-match-handlers'). +;; +;; Subprocess or in-process?: +;; +;; Running in-process confers better visibility and easier setup at +;; the cost of additional cleanup and resource wrangling. With a +;; subprocess, cleanup happens by pulling the plug, but configuration +;; means loading a separate file or passing -eval "(forms...)" during +;; invocation. In some cases, a subprocess may be the only option, +;; like when trying to avoid `require'ing this file. +;; +;; Dialog objects: +;; +;; For a given exchange, the first argument passed to a request +;; handler is the `erc-d-dialog' object representing the overall +;; conversation with the connecting peer. It can be used to pass +;; information between handlers during a session. Some important +;; items are: +;; +;; * name (symbol); name of the current dialog +;; +;; * queue (ring); a backlog of unhandled raw requests, minus CRLF +;; endings. +;; +;; * timers (list of timers); when run, these send messages originally +;; deferred as per the most recently matched exchange's delay info. +;; Normally, all outgoing messages must be sent before another request +;; is considered. (See `erc-d--send-outgoing' for an escape hatch.) +;; +;; * hunks (iterator of iterators); unconsumed exchanges as read from +;; a Lisp-Data dialog file. The exchange iterators being dispensed +;; themselves yield portions of member forms as a 2- or 3-part +;; sequence: [tag] spec. (Here, "hunk" just means "list of raw, +;; unrendered exchange elements") +;; +;; * vars (alist of cons pairs); for sharing state among template +;; functions during the lifetime of an exchange. Initially populated +;; by `erc-d-tmpl-vars', these KEY/VALUE pairs are expanded in the +;; templates and optionally updated by "exchange handlers" (see +;; `erc-d-match-handlers'). When VALUE is a function, occurrences of +;; KEY in an outgoing spec are replaced with the result of calling +;; VALUE with match data set appropriately. See +;; `erc-d--render-entries' for details. +;; +;; * exchanges (ring of erc-d-exchange objects); activated hunks +;; allowed to match out of order, plus the current active exchange +;; being yielded from, if any. See `erc-d-exchange'. +;; +;; TODO +;; +;; - Remove un(der)used functionality and simplify API +;; - Maybe migrate d-u and d-i dependencies here + +;;; Code: +(eval-and-compile + (let* ((d (file-name-directory (or (macroexp-file-name) buffer-file-name))) + (load-path (cons (directory-file-name d) load-path))) + (require 'erc-d-i) + (require 'erc-d-u))) + +(require 'ring) + +(defvar erc-d-server-name "erc-d-server" + "Default name of a server process and basis for its buffer name. +Only relevant when starting a server with `erc-d-run'.") + +(defvar erc-d-server-fqdn "irc.example.org" + "Usually the same as the server's RPL_MYINFO \"announced name\". +Possibly used by overriding handlers, like the one for PING, and/or +dialog templates for the sender portion of a reply message.") + +(defvar erc-d-line-ending "\r\n" + "Protocol line delimiter for sending and receiving.") + +(defvar erc-d-linger-secs nil + "Seconds to wait before quitting for all dialogs. +For more granular control, use the provided LINGER `rx' variable (alone) +as the incoming template spec of a dialog's last exchange.") + +(defvar erc-d-tmpl-vars nil + "An alist of template bindings available to client dialogs. +Populate it when calling `erc-d-run', and the contents will be made +available to all client dialogs through the `erc-d-dialog' \"vars\" +field and (therefore) to all templates as variables when rendering. For +example, a key/value pair like (network . \"oftc\") will cause instances +of the (unquoted) symbol `network' to be replaced with \"oftc\" in the +rendered template string. + +This list provides default template bindings common to all dialogs. +Each new client-connection process makes a shallow copy on init, but the +usual precautions apply when mutating member items. Within the span of +a dialog, updates not applicable to all exchanges should die with their +exchange. See `erc-d--render-entries' for details. In the unlikely +event that an exchange-specific handler is needed, see +`erc-d-match-handlers'.") + +(defvar erc-d-match-handlers nil + "A plist of exchange-tag symbols mapped to request-handler functions. +This is meant to address edge cases for which `erc-d-tmpl-vars' comes up +short. These may include (1) needing access to the client process +itself and/or (2) adding or altering outgoing response templates before +rendering. Note that (2) requires using `erc-d-exchange-rebind' instead +of manipulating exchange bindings directly. + +The hook-like function `erc-d-on-match' calls any handler whose key is +`eq' to the tag of the currently matched exchange (passing the client +`erc-d-dialog' as the first argument and the current `erc-d-exchange' +object as the second). The handler runs just prior to sending the first +response.") + +(defvar erc-d-auto-pong t + "Handle PING requests automatically.") + +(defvar erc-d--in-process t + "Whether the server is running in the same Emacs as ERT.") + +(defvar erc-d--slow-mo nil + "Adjustment for all incoming timeouts. +This is to allow for human interaction or a slow Emacs or CI runner. +The value is the number of seconds to extend all incoming spec timeouts +by on init. If the value is a negative number, it's negated and +interpreted as a lower bound to raise all incoming timeouts to. If the +value is a function, it should take an existing timeout in seconds and +return a replacement.") + +(defconst erc-d--eof-sentinel "__EOF__") +(defconst erc-d--linger-sentinel "__LINGER__") +(defconst erc-d--drop-sentinel "__DROP__") + +(defvar erc-d--clients nil + "List containing all clients for this server session.") + +;; Some :type names may just be made up (not actual CL types) + +(cl-defstruct (erc-d-spec) ; see `erc-d--render-entries' + (head nil :type symbol) ; or number? + (entry nil :type list) + (state 0 :type integer)) + +(cl-defstruct (erc-d-exchange) + "Object representing a request/response unit from a canned dialog." + (dialog nil :type erc-d-dialog) ; owning dialog + (tag nil :type symbol) ; a.k.a. tag, the caar + (pattern nil :type string) ; regexp to match requests against + (inspec nil :type list) ; original unrendered incoming spec + (hunk nil :type erc-d-u-scan-e) ; active raw exchange hunk being yielded + (spec nil :type erc-d-spec) ; active spec, see `erc-d--render-entries' + (timeout nil :type number) ; time allotted for current request + (timer nil :type timer) ; match timer fires when timeout expires + (bindings nil :type list) ; `eval'-style env pairs (KEY . VAL) ... + (rx-bindings nil :type list) ; rx-let bindings + (deferred nil :type boolean) ; whether sender is paused + ;; Post-match + (match-data nil :type match-data) ; from the latest matched request + (request nil :type string)) ; the original request sans CRLF + +(cl-defstruct (erc-d-dialog) + "Session state for managing a client conversation." + (process nil :type process) ; client-connection process + (name nil :type symbol) ; likely the interned stem of the file + (queue nil :type ring) ; backlog of incoming lines to process + (hunks nil :type erc-d-u-scan-d) ; nil when done; info on raw exchange hunks + (timers nil :type list) ; unsent replies + (vars nil :type list) ; template bindings for rendering + (exchanges nil :type ring) ; ring of erc-d-exchange objects + (state nil :type symbol) ; handler's last recorded control state + (matched nil :type erc-d-exchange) ; currently matched exchange + (message nil :type erc-d-i-message) ; `erc-d-i-message' + (match-handlers nil :type list) ; copy of `erc-d-match-handlers' + (server-fqdn nil :type string) ; copy of `erc-d-server-fqdn' + (finalizer nil :type function) ; custom teardown, passed dialog and exchange + ;; Post-match history is a plist whose keys are exchange tags + ;; (symbols) and whose values are a cons of match-data and request + ;; values from prior matches. + (history nil :type list)) + +(defun erc-d--initialize-client (process) + "Initialize state variables used by a client PROCESS." + ;; Discard server-only/owned props + (process-put process :dialog-dialogs nil) + (let* ((server (process-get process :server)) + (reader (pop (process-get server :dialog-dialogs))) + (name (pop reader)) + ;; Copy handlers so they can self-mutate per process + (mat-h (copy-sequence (process-get process :dialog-match-handlers))) + (fqdn (copy-sequence (process-get process :dialog-server-fqdn))) + (vars (copy-sequence (process-get process :dialog-vars))) + (ending (process-get process :dialog-ending)) + (dialog (make-erc-d-dialog :name name + :process process + :queue (make-ring 5) + :exchanges (make-ring 10) + :match-handlers mat-h + :server-fqdn fqdn))) + ;; Add items expected by convenience commands like `erc-d-exchange-reload'. + (setf (alist-get 'EOF vars) `(: ,erc-d--eof-sentinel eot) + (alist-get 'LINGER vars) `(: ,erc-d--linger-sentinel eot) + (alist-get 'DROP vars) `(: ,erc-d--drop-sentinel eot) + (erc-d-dialog-vars dialog) vars + (erc-d-dialog-hunks dialog) reader) + ;; Add reverse link, register client, launch + (process-put process :dialog dialog) + (process-put process :ending ending) + (process-put process :ending-regexp (rx-to-string `(+ ,ending))) + (push process erc-d--clients) + (erc-d--command-refresh dialog nil) + (erc-d--on-request process))) + +(defun erc-d-load-replacement-dialog (dialog replacement &optional skip) + "Find REPLACEMENT among backlog and swap out current DIALOG's iterator. +With int SKIP, advance past that many exchanges." + (let* ((process (erc-d-dialog-process dialog)) + (server (process-get process :server)) + (reader (assoc-default replacement + (process-get server :dialog-dialogs) + #'eq))) + (when skip (while (not (zerop skip)) + (erc-d-u--read-dialog reader) + (cl-decf skip))) + (dolist (timer (erc-d-dialog-timers dialog)) + (cancel-timer timer)) + (dolist (exchange (ring-elements (erc-d-dialog-exchanges dialog))) + (cancel-timer (erc-d-exchange-timer exchange))) + (setf (erc-d-dialog-hunks dialog) reader) + (erc-d--command-refresh dialog nil))) + +(defvar erc-d--m-debug (getenv "ERC_D_DEBUG")) + +(defmacro erc-d--m (process format-string &rest args) + "Output ARGS using FORMAT-STRING somewhere depending on context. +PROCESS should be a client connection or a server network process." + `(let ((format-string (if erc-d--m-debug + (concat (format-time-string "%s.%N: ") + ,format-string) + ,format-string)) + (want-insert (and ,process erc-d--in-process))) + (when want-insert + (with-current-buffer (process-buffer (process-get ,process :server)) + (goto-char (point-max)) + (insert (concat (format ,format-string ,@args) "\n")))) + (when (or erc-d--m-debug (not want-insert)) + (message format-string ,@args)))) + +(defmacro erc-d--log (process string &optional outbound) + "Log STRING sent to (OUTBOUND) or received from PROCESS peer." + `(let ((id (or (process-get ,process :log-id) + (let ((port (erc-d-u--get-remote-port ,process))) + (process-put ,process :log-id port) + port))) + (name (erc-d-dialog-name (process-get ,process :dialog)))) + (if ,outbound + (erc-d--m process "-> %s:%s %s" name id ,string) + (dolist (line (split-string ,string (process-get process :ending))) + (erc-d--m process "<- %s:%s %s" name id line))))) + +(defun erc-d--log-process-event (server process msg) + (erc-d--m server "%s: %s" process (string-trim-right msg))) + +(defun erc-d--send (process string) + "Send STRING to PROCESS peer." + (erc-d--log process string 'outbound) + (process-send-string process (concat string (process-get process :ending)))) + +(define-inline erc-d--fuzzy-p (exchange) + (inline-letevals (exchange) + (inline-quote + (let ((tag (symbol-name (erc-d-exchange-tag ,exchange)))) + (eq ?~ (aref tag 0)))))) + +(define-error 'erc-d-timeout "Timed out awaiting expected request") + +(defun erc-d--finalize-dialog (dialog) + "Delete client-connection and finalize DIALOG. +Return associated server." + (let ((process (erc-d-dialog-process dialog))) + (setq erc-d--clients (delq process erc-d--clients)) + (dolist (timer (erc-d-dialog-timers dialog)) + (cancel-timer timer)) + (dolist (exchange (ring-elements (erc-d-dialog-exchanges dialog))) + (cancel-timer (erc-d-exchange-timer exchange))) + (prog1 (process-get process :server) + (delete-process process)))) + +(defun erc-d--teardown (&optional sig &rest msg) + "Clean up processes and maybe send signal SIG using MSG." + (unless erc-d--in-process + (when sig + (erc-d--m nil "%s %s" sig (apply #'format-message msg))) + (kill-emacs (if msg 1 0))) + (let (process servers) + (while (setq process (pop erc-d--clients)) + (push (erc-d--finalize-dialog (process-get process :dialog)) servers)) + (dolist (server servers) + (delete-process server))) + (dolist (timer timer-list) + (when (memq (timer--function timer) + '(erc-d--send erc-d--command-handle-all)) + (erc-d--m nil "Stray timer found: %S" (timer--function timer)) + (cancel-timer timer))) + (when sig + (dolist (buf erc-d-u--canned-buffers) + (kill-buffer buf)) + (setq erc-d-u--canned-buffers nil) + (signal sig (list (apply #'format-message msg))))) + +(defun erc-d--teardown-this-dialog-at-least (dialog) + "Run `erc-d--teardown' after destroying DIALOG if it's the last one." + (let ((server (process-get (erc-d-dialog-process dialog) :server)) + (us (erc-d-dialog-process dialog))) + (erc-d--finalize-dialog dialog) + (cl-assert (not (memq us erc-d--clients))) + (unless (or (process-get server :dialog-dialogs) + (catch 'other + (dolist (process erc-d--clients) + (when (eq (process-get process :server) server) + (throw 'other process))))) + (push us erc-d--clients) + (erc-d--teardown)))) + +(defun erc-d--expire (dialog exchange) + "Raise timeout error for EXCHANGE. +This will start the teardown for DIALOG." + (setf (erc-d-exchange-spec exchange) nil) + (if-let ((finalizer (erc-d-dialog-finalizer dialog))) + (funcall finalizer dialog exchange) + (erc-d--teardown 'erc-d-timeout "Timed out awaiting request: %s" + (list :name (erc-d-exchange-tag exchange) + :pattern (erc-d-exchange-pattern exchange) + :timeout (erc-d-exchange-timeout exchange) + :dialog (erc-d-dialog-name dialog))))) + +;; Using `run-at-time' here allows test cases to examine replies as +;; they arrive instead of forcing tests to wait until an exchange +;; completes. The `run-at-time' in `erc-d--command-meter-replies' +;; does the same. When running as a subprocess, a normal while loop +;; with a `sleep-for' works fine (including with multiple dialogs). +;; FYI, this issue was still present in older versions that called +;; this directly from `erc-d--filter'. + +(defun erc-d--on-request (process) + "Handle one request for client-connection PROCESS." + (when (process-live-p process) + (let* ((dialog (process-get process :dialog)) + (queue (erc-d-dialog-queue dialog))) + (unless (ring-empty-p queue) + (let* ((parsed (ring-remove queue)) + (cmd (intern (erc-d-i-message.command parsed)))) + (setf (erc-d-dialog-message dialog) parsed) + (erc-d-command dialog cmd))) + (run-at-time nil nil #'erc-d--on-request process)))) + +(defun erc-d--drop-p (exchange) + (memq 'DROP (erc-d-exchange-inspec exchange))) + +(defun erc-d--linger-p (exchange) + (memq 'LINGER (erc-d-exchange-inspec exchange))) + +(defun erc-d--fake-eof (dialog) + "Simulate receiving a fictitious \"EOF\" message from peer." + (setf (erc-d-dialog-message dialog) ; use downcase for internal cmds + (make-erc-d-i-message :command "eof" :unparsed erc-d--eof-sentinel)) + (run-at-time nil nil #'erc-d-command dialog 'eof)) + +(defun erc-d--process-sentinel (process event) + "Set up or tear down client-connection PROCESS depending on EVENT." + (erc-d--log-process-event process process event) + (if (eq 'open (process-status process)) + (erc-d--initialize-client process) + (let* ((dialog (process-get process :dialog)) + (exes (and dialog (erc-d-dialog-exchanges dialog)))) + (if (and exes (not (ring-empty-p exes))) + (cond ((string-prefix-p "connection broken" event) + (erc-d--fake-eof dialog)) + ;; Ignore disconnecting peer when pattern is DROP + ((and (string-prefix-p "deleted" event) + (erc-d--drop-p (ring-ref exes -1)))) + (t (erc-d--teardown))) + (erc-d--teardown))))) + +(defun erc-d--filter (process string) + "Handle input received from peer. +PROCESS represents a client peer connection and STRING is a raw request +including line delimiters." + (let ((queue (erc-d-dialog-queue (process-get process :dialog))) + (delim (process-get process :ending-regexp))) + (setq string (concat (process-get process :stashed-input) string)) + (while (and string (string-match delim string)) + (let ((line (substring string 0 (match-beginning 0)))) + (setq string (unless (= (match-end 0) (length string)) + (substring string (match-end 0)))) + (erc-d--log process line nil) + (ring-insert queue (erc-d-i--parse-message line 'decode)))) + (when string + (setf (process-get process :stashed-input) string)))) + +;; Misc process properties: +;; +;; The server property `:dialog-dialogs' is an alist of (symbol +;; . erc-d-u-scan-d) conses, each of which pairs a dialogs name with +;; info on its read progress (described above in the Commentary). +;; This list is populated by `erc-d-run' at the start of each session. +;; +;; Client-connection processes keep a reference to their server via a +;; `:server' property, which can be used to share info with other +;; clients. There is currently no built-in way to do the same with +;; clients of other servers. Clients also keep references to their +;; dialogs and raw messages via `:dialog' and `:stashed-input'. +;; +;; The logger stores a unique, human-friendly process name in the +;; client-process property `:log-id'. + +(defun erc-d--start (host service name &rest plist) + "Serve canned replies on HOST at SERVICE. +Return the new server process immediately when `erc-d--in-process' is +non-nil. Otherwise, serve forever. PLIST becomes the plist of the +server process and is used to initialize the plists of connection +processes. NAME is used for the process and the buffer." + (let* ((buf (get-buffer-create (concat "*" name "*"))) + (proc (make-network-process :server t + :buffer buf + :noquery t + :filter #'erc-d--filter + :log #'erc-d--log-process-event + :sentinel #'erc-d--process-sentinel + :name name + :family (if host 'ipv4 'local) + :coding 'binary + :service (or service t) + :host host + :plist plist))) + (process-put proc :server proc) + ;; We don't have a minor mode, so use an arbitrary variable to mark + ;; buffers owned by us instead + (with-current-buffer buf (setq erc-d-u--process-buffer t)) + (erc-d--m proc "Starting network process: %S %S" + proc (erc-d-u--format-bind-address proc)) + (if erc-d--in-process + proc + (while (process-live-p proc) + (accept-process-output nil 0.01))))) + +(defun erc-d--wrap-func-val (dialog exchange key func) + "Return a form invoking FUNC when evaluated. +Arrange for FUNC to be called with the args it expects based on +the description in `erc-d--render-entries'." + (let (args) + ;; Ignore &rest or &optional + (pcase-let ((`(,n . ,_) (func-arity func))) + (pcase n + (0) + (1 (push (apply-partially #'erc-d-exchange-multi dialog exchange key) + args)) + (2 (push exchange args) + (push (apply-partially #'erc-d-exchange-multi dialog exchange key) + args)) + (_ (error "Incompatible function: %s" func)))) + (lambda () (apply func args)))) + +(defun erc-d-exchange-reload (dialog exchange) + "Rebuild all bindings for EXCHANGE from those in DIALOG." + (cl-loop for (key . val) in (erc-d-dialog-vars dialog) + unless (keywordp key) + do (push (erc-d-u--massage-rx-args key val) + (erc-d-exchange-rx-bindings exchange)) + when (functionp val) do + (setq val (erc-d--wrap-func-val dialog exchange key val)) + do (push (cons key val) (erc-d-exchange-bindings exchange)))) + +(defun erc-d-exchange-rebind (dialog exchange key val &optional export) + "Modify a binding between renders. + +Bind symbol KEY to VAL, replacing whatever existed before, which may +have been a function. A third, optional argument, if present and +non-nil, results in the DIALOG's bindings for all EXCHANGEs adopting +this binding. VAL can either be a function of the type described in +`erc-d--render-entries' or any value acceptable as an argument to the +function `concat'. + +DIALOG and EXCHANGE are the current `erc-d-dialog' and `erc-d-exchange' +objects for the request context." + (when export + (setf (alist-get key (erc-d-dialog-vars dialog)) val)) + (if (functionp val) + (setf (alist-get key (erc-d-exchange-bindings exchange)) + (erc-d--wrap-func-val dialog exchange key val)) + (setf (alist-get key (erc-d-exchange-rx-bindings exchange)) (list val) + (alist-get key (erc-d-exchange-bindings exchange)) val)) + val) + +(defun erc-d-exchange-match (exchange match-number &optional tag) + "Return match portion of current or previous request. +MATCH-NUMBER is the match group number. TAG, if provided, means the +exchange tag (name) from some previously matched request." + (if tag + (pcase-let* ((dialog (erc-d-exchange-dialog exchange)) + (`(,m-d . ,req) (plist-get (erc-d-dialog-history dialog) + tag))) + (set-match-data m-d) + (match-string match-number req)) + (match-string match-number (erc-d-exchange-request exchange)))) + +(defun erc-d-exchange-multi (dialog exchange key cmd &rest args) + "Call CMD with ARGS. +This is a utility passed as the first argument to all template +functions. DIALOG and EXCHANGE are pre-applied. A few pseudo +commands, like `:request', are provided for convenience so that +the caller's definition doesn't have to include this file. The +rest are access and mutation utilities, such as `:set', which +assigns KEY a new value, `:get-binding', which looks up KEY in +`erc-d-exchange-bindings', and `:get-var', which looks up KEY in +`erc-d-dialog-vars'." + (pcase cmd + (:set (apply #'erc-d-exchange-rebind dialog exchange key args)) + (:reload (apply #'erc-d-exchange-reload dialog exchange args)) + (:rebind (apply #'erc-d-exchange-rebind dialog exchange args)) + (:match (apply #'erc-d-exchange-match exchange args)) + (:request (erc-d-exchange-request exchange)) + (:match-data (erc-d-exchange-match-data exchange)) + (:dialog-name (erc-d-dialog-name dialog)) + (:get-binding (cdr (assq (car args) (erc-d-exchange-bindings exchange)))) + (:get-var (alist-get (car args) (erc-d-dialog-vars dialog))))) + +(defun erc-d--render-incoming-entry (exchange spec) + (let ((rx--local-definitions (rx--extend-local-defs + (erc-d-exchange-rx-bindings exchange)))) + (rx-to-string `(: bos ,@(erc-d-spec-entry spec)) 'no-group))) + +(defun erc-d--render-outgoing-entry (exchange entry) + (let (out this) + (while (setq this (pop entry)) + (set-match-data (erc-d-exchange-match-data exchange)) + (unless (stringp this) + (cl-assert (symbolp this)) + (setq this (or (alist-get this (erc-d-exchange-bindings exchange)) + (symbol-value this))) + ;; Allow reference to overlong var name unbecoming of a template + (when this + (when (symbolp this) (setq this (symbol-value this))) + (when (functionp this) (setq this (save-match-data (funcall this)))) + (unless (stringp this) (error "Unexpected token %S" this)))) + (push this out)) + (apply #'concat (nreverse out)))) + +(defun erc-d--render-entries (exchange &optional yield-result) + "Act as an iterator producing rendered strings from EXCHANGE hunks. +When an entry's CAR is an arbitrary symbol, yield that back first, and +consider the entry an \"incoming\" entry. Then, regardless of the +entry's type (incoming or outgoing), yield back the next element, which +should be a number representing either a timeout (incoming) or a +delay (outgoing). After that, yield a rendered template (outgoing) or a +regular expression (incoming); both should be treated as immutable. + +When evaluating a template, bind the keys in the alist stored in the +dialog's `vars' field to its values, but skip any self-quoters, like +:foo. When an entry is incoming, replace occurrences of a key with its +value, which can be any valid `rx' form (see Info node `(elisp) +Extending Rx'). Do the same when an entry is outgoing, but expect a +value's form to be (anything that evaluates to) something acceptable by +`concat' or, alternatively, a function that returns a string or nil. + +Repeat the last two steps for the remaining entries, all of which are +assumed to be outgoing. That is, continue yielding a timeout/delay and +a rendered string for each entry, and yield nil when exhausted. + +Once again, for an incoming entry, the yielded string is a regexp to be +matched against the raw request. For outgoing, it's the final response, +ready to be sent out (after adding the appropriate line ending). + +To help with testing, bindings are not automatically created from +DIALOG's \"vars\" alist when this function is invoked. But this can be +forced by sending a non-nil YIELD-RESULT into the generator on the +second \"next\" invocation of a given iteration. This clobbers any +temporary bindings that don't exist in the DIALOG's `vars' alist, such +as those added via `erc-d-exchange-rebind' (unless \"exported\"). + +As noted earlier, template symbols can be bound to functions. When +called during rendering, the match data from the current (matched) +request is accessible by calling the function `match-data'. + +A function may ask for up to two required args, which are provided as +needed. When applicable, the first required arg is a `funcall'-able +helper that accepts various keyword-based commands, like :rebind, and a +variable number of args. See `erc-d-exchange-multi' for details. When +specified, the second required arg is the current `erc-d-exchange' +object, which has among its members its owning `erc-d-dialog' object. +This should suffice as a safety valve for any corner-case needs. +Non-required args are ignored." + (let ((spec (erc-d-exchange-spec exchange)) + (dialog (erc-d-exchange-dialog exchange)) + (entries (erc-d-exchange-hunk exchange))) + (unless (erc-d-spec-entry spec) + (setf (erc-d-spec-entry spec) (erc-d-u--read-exchange entries))) + (catch 'yield + (while (erc-d-spec-entry spec) + (pcase (erc-d-spec-state spec) + (0 (cl-incf (erc-d-spec-state spec)) + (throw 'yield (setf (erc-d-spec-head spec) + (pop (erc-d-spec-entry spec))))) + (1 (cl-incf (erc-d-spec-state spec)) + (when yield-result + (erc-d-exchange-reload dialog exchange)) + (unless (numberp (erc-d-spec-head spec)) + (setf (erc-d-exchange-inspec exchange) (erc-d-spec-entry spec)) + (throw 'yield + (prog1 (pop (erc-d-spec-entry spec)) + (setf (erc-d-spec-entry spec) + (erc-d--render-incoming-entry exchange spec)))))) + (2 (setf (erc-d-spec-state spec) 0) + (throw 'yield + (let ((entry (erc-d-spec-entry spec))) + (setf (erc-d-spec-entry spec) nil) + (if (stringp entry) + entry + (erc-d--render-outgoing-entry exchange entry)))))))))) + +(defun erc-d--iter (exchange) + (apply-partially #'erc-d--render-entries exchange)) + +(defun erc-d-on-match (dialog exchange) + "Handle matched exchange request. +Allow the first handler in `erc-d-match-handlers' whose key matches TAG +to manipulate replies before they're sent to the DIALOG peer." + (when-let* ((tag (erc-d-exchange-tag exchange)) + (handler (plist-get (erc-d-dialog-match-handlers dialog) tag))) + (let ((md (erc-d-exchange-match-data exchange))) + (set-match-data md) + (funcall handler dialog exchange)))) + +(defun erc-d--send-outgoing (dialog exchange) + "Send outgoing lines for EXCHANGE to DIALOG peer. +Assume the next spec is outgoing. If its delay value is zero, render +the template and send the resulting message straight away. Do the same +when DELAY is negative, only arrange for its message to be sent (abs +DELAY) seconds later, and then keep on processing. If DELAY is +positive, pause processing and yield DELAY." + (let ((specs (erc-d--iter exchange)) + (process (erc-d-dialog-process dialog)) + (deferred (erc-d-exchange-deferred exchange)) + delay) + ;; Could stash/pass thunk instead to ensure specs can't be mutated + ;; between calls (by temporarily replacing dialog member with a fugazi) + (when deferred + (erc-d--send process (funcall specs)) + (setf deferred nil (erc-d-exchange-deferred exchange) deferred)) + (while (and (not deferred) (setq delay (funcall specs))) + (cond ((zerop delay) (erc-d--send process (funcall specs))) + ((< delay 0) (push (run-at-time (- delay) nil #'erc-d--send + process (funcall specs)) + (erc-d-dialog-timers dialog))) + ((setf deferred t (erc-d-exchange-deferred exchange) deferred)))) + delay)) + +(defun erc-d--add-dialog-linger (dialog exchange) + "Add finalizer for EXCHANGE in DIALOG." + (erc-d--m (erc-d-dialog-process dialog) + "Lingering for %.2f seconds" (erc-d-exchange-timeout exchange)) + (let ((start (current-time))) + (setf (erc-d-dialog-finalizer dialog) + (lambda (&rest _) + (erc-d--m (erc-d-dialog-process dialog) + "Lingered for %.2f seconds" + (float-time (time-subtract (current-time) start))) + (erc-d--teardown-this-dialog-at-least dialog))))) + +(defun erc-d--add-dialog-drop (dialog exchange) + "Add finalizer for EXCHANGE in DIALOG." + (erc-d--m (erc-d-dialog-process dialog) + "Dropping in %.2f seconds" (erc-d-exchange-timeout exchange)) + (setf (erc-d-dialog-finalizer dialog) + (lambda (&rest _) + (erc-d--m (erc-d-dialog-process dialog) + "Dropping %S" (erc-d-dialog-name dialog)) + (erc-d--finalize-dialog dialog)))) + +(defun erc-d--create-exchange (dialog hunk) + "Initialize next exchange HUNK for DIALOG." + (let* ((spec (make-erc-d-spec)) + (exchange (make-erc-d-exchange :dialog dialog :hunk hunk :spec spec)) + (specs (erc-d--iter exchange))) + (setf (erc-d-exchange-tag exchange) (funcall specs) + (erc-d-exchange-timeout exchange) (funcall specs t) + (erc-d-exchange-pattern exchange) (funcall specs)) + (cond ((erc-d--linger-p exchange) + (erc-d--add-dialog-linger dialog exchange)) + ((erc-d--drop-p exchange) + (erc-d--add-dialog-drop dialog exchange))) + (setf (erc-d-exchange-timer exchange) + (run-at-time (erc-d-exchange-timeout exchange) + nil #'erc-d--expire dialog exchange)) + exchange)) + +(defun erc-d--command-consider-prep-fail (dialog line exes) + (list 'error "Match failed: %S %S" line + (list :exes (mapcar #'erc-d-exchange-pattern + (ring-elements exes)) + :dialog (erc-d-dialog-name dialog)))) + +(defun erc-d--command-consider-prep-success (dialog line exes matched) + (setf (erc-d-exchange-request matched) line + (erc-d-exchange-match-data matched) (match-data) + ;; Also add current to match history, indexed by exchange tag + (plist-get (erc-d-dialog-history dialog) + (erc-d-exchange-tag matched)) + (cons (match-data) line)) ; do we need to make a copy of this? + (cancel-timer (erc-d-exchange-timer matched)) + (ring-remove exes (ring-member exes matched))) + +(cl-defun erc-d--command-consider (dialog) + "Maybe return next matched exchange for DIALOG. +Upon encountering a mismatch, return an error of the form (ERROR-SYMBOL +DATA). But when only fuzzies remain in the exchange pool, return nil." + (let* ((parsed (erc-d-dialog-message dialog)) + (line (erc-d-i-message.unparsed parsed)) + (exes (erc-d-dialog-exchanges dialog)) + ;; + matched) + (let ((elts (ring-elements exes))) + (while (and (setq matched (pop elts)) + (not (string-match (erc-d-exchange-pattern matched) line))) + (if (and (not elts) (erc-d--fuzzy-p matched)) + ;; Nothing to do, so advance + (cl-return-from erc-d--command-consider nil) + (cl-assert (or (not elts) (erc-d--fuzzy-p matched)))))) + (if matched + (erc-d--command-consider-prep-success dialog line exes matched) + (erc-d--command-consider-prep-fail dialog line exes)))) + +(defun erc-d--active-ex-p (ring) + "Return non-nil when RING has a non-fuzzy exchange. +That is, return nil when RING is empty or when it only has exchanges +with leading-tilde tags." + (let ((i 0) + (len (ring-length ring)) + ex found) + (while (and (not found) (< i len)) + (unless (erc-d--fuzzy-p (setq ex (ring-ref ring i))) + (setq found ex)) + (cl-incf i)) + found)) + +(defun erc-d--finalize-done (dialog) + ;; Linger logic for individual dialogs is handled elsewhere + (if-let ((finalizer (erc-d-dialog-finalizer dialog))) + (funcall finalizer dialog) + (let ((d (process-get (erc-d-dialog-process dialog) :dialog-linger-secs))) + (push (run-at-time d nil #'erc-d--teardown) + (erc-d-dialog-timers dialog))))) + +(defun erc-d--advance-or-die (dialog) + "Govern the lifetime of DIALOG. +Replenish exchanges from reader and insert them into the pool of +expected matches, as produced. Return a symbol indicating session +status: deferring, matching, depleted, or done." + (let ((exes (erc-d-dialog-exchanges dialog)) + hunk) + (cond ((erc-d--active-ex-p exes) 'deferring) + ((setq hunk (erc-d-u--read-dialog (erc-d-dialog-hunks dialog))) + (let ((exchange (erc-d--create-exchange dialog hunk))) + (if (erc-d--fuzzy-p exchange) + (ring-insert exes exchange) + (ring-insert-at-beginning exes exchange))) + 'matching) + ((not (ring-empty-p exes)) 'depleted) + (t 'done)))) + +(defun erc-d--command-meter-replies (dialog exchange &optional cmd) + "Ignore requests until all replies have been sent. +Do this for some previously matched EXCHANGE in DIALOG based on CMD, a +symbol. As a side effect, maybe schedule the resumption of the main +loop after some delay." + (let (delay) + (if (or (not cmd) (eq 'resume cmd)) + (when (setq delay (erc-d--send-outgoing dialog exchange)) + (push (run-at-time delay nil #'erc-d--command-handle-all + dialog 'resume) + (erc-d-dialog-timers dialog)) + (erc-d-dialog-state dialog)) + (setf (erc-d-dialog-state dialog) 'sending)))) + +(defun erc-d--die-unexpected (dialog) + (erc-d--teardown 'error "Received unexpected input: %S" + (erc-d-i-message.unparsed (erc-d-dialog-message dialog)))) + +(defun erc-d--command-refresh (dialog matched) + (let ((state (erc-d--advance-or-die dialog))) + (when (eq state 'done) + (erc-d--finalize-done dialog)) + (unless matched + (when (eq state 'depleted) + (erc-d--die-unexpected dialog)) + (cl-assert (memq state '(matching depleted)) t)) + (setf (erc-d-dialog-state dialog) state))) + +(defun erc-d--command-handle-all (dialog cmd) + "Create handler to act as control agent and process DIALOG requests. +Have it ingest internal control commands (lowercase symbols) and yield +back others indicating the lifecycle stage of the current dialog." + (let ((matched (erc-d-dialog-matched dialog))) + (cond + (matched + (or (erc-d--command-meter-replies dialog matched cmd) + (setf (erc-d-dialog-matched dialog) nil) + (erc-d--command-refresh dialog t))) + ((pcase cmd ; FIXME remove command facility or make extensible + ('resume nil) + ('eof (erc-d--m (erc-d-dialog-process dialog) "Received an EOF") nil))) + (t ; matching + (setq matched nil) + (catch 'yield + (while (not matched) + (when (ring-empty-p (erc-d-dialog-exchanges dialog)) + (erc-d--die-unexpected dialog)) + (when (setq matched (erc-d--command-consider dialog)) + (if (eq (car-safe matched) 'error) + (apply #'erc-d--teardown matched) + (erc-d-on-match dialog matched) + (setf (erc-d-dialog-matched dialog) matched) + (if-let ((s (erc-d--command-meter-replies dialog matched nil))) + (throw 'yield s) + (setf (erc-d-dialog-matched dialog) nil)))) + (erc-d--command-refresh dialog matched))))))) + +;;;; Handlers for IRC commands + +(cl-defgeneric erc-d-command (dialog cmd) + "Handle new CMD from client for DIALOG. +By default, defer to this dialog's `erc-d--command-handle-all' instance, +which is stored in its `handler' field.") + +(cl-defmethod erc-d-command ((dialog erc-d-dialog) cmd) + (when (eq 'sending (erc-d--command-handle-all dialog cmd)) + (ring-insert-at-beginning (erc-d-dialog-queue dialog) + (erc-d-dialog-message dialog)))) + +;; A similar PONG handler would be useless because we know when to +;; expect them + +(cl-defmethod erc-d-command ((dialog erc-d-dialog) (_cmd (eql PING)) + &context (erc-d-auto-pong (eql t))) + "Respond to PING request from DIALOG peer when ERC-D-AUTO-PONG is t." + (let* ((parsed (erc-d-dialog-message dialog)) + (process (erc-d-dialog-process dialog)) + (nonce (car (erc-d-i-message.command-args parsed))) + (fqdn (erc-d-dialog-server-fqdn dialog))) + (erc-d--send process (format ":%s PONG %s :%s" fqdn fqdn nonce)))) + + +;;;; Entry points + +(defun erc-d-run (host service &optional server-name &rest dialogs) + "Start serving DIALOGS on HOST at SERVICE. +Pass HOST and SERVICE directly to `make-network-process'. When present, +use string SERVER-NAME for the server-process name as well as that of +its buffer (w. surrounding asterisks). When absent, do the same with +`erc-d-server-name'. When running \"in process,\" return the server +process; otherwise sleep until it dies. + +A dialog must be a symbol matching the base name of a dialog file in +`erc-d-u-canned-dialog-dir'. Global variables `erc-d-server-fqdn', +`erc-d-linger-secs', and `erc-d-tmpl-vars' determine the process's +`erc-d-dialog' fields `:server-fqdn', `:linger-secs', and `:vars', +respectively. The latter may also be populated via keyword pairs +appearing among DIALOGS." + (when (and server-name (symbolp server-name)) + (push server-name dialogs) + (setq server-name nil)) + (let (loaded kwds defaults args) + (while dialogs + (if-let* ((dlog (pop dialogs)) + ((keywordp dlog))) + (progn (push (pop dialogs) kwds) (push dlog kwds)) + (let ((reader (erc-d-u--canned-load-dialog dlog))) + (when erc-d--slow-mo + (setq reader (erc-d-u--rewrite-for-slow-mo erc-d--slow-mo reader))) + (push (cons (erc-d-u--normalize-canned-name dlog) reader) loaded)))) + (setq kwds (erc-d-u--unkeyword kwds) + defaults `((ending . ,erc-d-line-ending) + (server-fqdn . ,erc-d-server-fqdn) + (linger-secs . ,erc-d-linger-secs) + (vars . ,(or (plist-get kwds 'tmpl-vars) erc-d-tmpl-vars)) + (dialogs . ,(nreverse loaded))) + args (list :dialog-match-handlers + (erc-d-u--unkeyword (or (plist-get kwds 'match-handlers) + erc-d-match-handlers)))) + (pcase-dolist (`(,var . ,def) defaults) + (push (or (plist-get kwds var) def) args) + (push (intern (format ":dialog-%s" var)) args)) + (apply #'erc-d--start host service (or server-name erc-d-server-name) + args))) + +(defun erc-d-serve () + "Start serving canned dialogs from the command line. +Although not autoloaded, this function is meant to be summoned via the +Emacs -f flag while starting a batch session. It prints incoming and +outgoing messages to standard out. + +The main options are --host HOST and --port PORT, which default to +localhost and auto, respectively. The args are the dialogs to run. +Unlike with `erc-d-run', dialogs here *must* be files, meaning Lisp-Data +files adhering to the required format. (These consist of \"specs\" +detailing timing and template info; see commentary for specifics.) + +An optional --add-time N option can also be passed to hike up timeouts +by some number of seconds N. For example, you might run: + + $ emacs -Q -batch -L . \\ + > -l erc-d.el \\ + > -f erc-d-serve \\ + > --host 192.168.124.1 \\ + > --port 16667 \\ + > --add-time 10 \\ + > ./my-dialog.eld + +from a Makefile or manually with \\<global-map>\\[compile]. And then in +another terminal, do: + + $ nc -C 192.168.124.1 16667 ; or telnet if your nc doesn't have -C + > PASS changeme + ... + +Use `erc-d-run' instead to start the server from within Emacs." + (unless noninteractive + (error "Command-line func erc-d-serve not run in -batch session")) + (setq erc-d--in-process nil) + (let (port host dialogs erc-d--slow-mo) + (while command-line-args-left + (pcase (pop command-line-args-left) + ("--add-time" (setq erc-d--slow-mo + (string-to-number (pop command-line-args-left)))) + ("--linger" (setq erc-d-linger-secs + (string-to-number (pop command-line-args-left)))) + ("--host" (setq host (pop command-line-args-left))) + ("--port" (setq port (string-to-number (pop command-line-args-left)))) + (dialog (push dialog dialogs)))) + (setq dialogs (mapcar #'erc-d-u--massage-canned-name dialogs)) + (when erc-d--slow-mo + (message "Slow mo is ON")) + (apply #'erc-d-run (or host "localhost") port nil (nreverse dialogs)))) + +(provide 'erc-d) + +;;; erc-d.el ends here diff --git a/test/lisp/erc/resources/erc-d/resources/basic.eld b/test/lisp/erc/resources/erc-d/resources/basic.eld new file mode 100644 index 00000000000..a020eec3fff --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/basic.eld @@ -0,0 +1,32 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0 ":irc.example.org 003 tester :This server was created just now") + (0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + ;; Just to mix thing's up (force handler to schedule timer) + (0.1 ":irc.example.org 252 tester 0 :IRC Operators online") + (0 ":irc.example.org 253 tester 0 :unregistered connections") + (0 ":irc.example.org 254 tester 1 :channels formed") + (0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0.1 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 5 "MODE tester +i") + (0 ":irc.example.org 221 tester +Zi") + (0 ":irc.example.org 306 tester :You have been marked as being away") + (0 ":tester!~tester@localhost JOIN #chan") + (0 ":irc.example.org 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.example.org 366 alice #chan :End of NAMES list")) + +;; Some comment (to prevent regression) +((mode-chan 1.2 "MODE #chan") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")) diff --git a/test/lisp/erc/resources/erc-d/resources/depleted.eld b/test/lisp/erc/resources/erc-d/resources/depleted.eld new file mode 100644 index 00000000000..e5a7f03efb7 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/depleted.eld @@ -0,0 +1,12 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS :changeme")) + +((~fake 3.2 "FAKE ") + (0.1 ":irc.example.org FAKE irc.example.com :ok")) + +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet tester") + (0 ":irc.example.org 422 tester :MOTD File is missing")) diff --git a/test/lisp/erc/resources/erc-d/resources/drop-a.eld b/test/lisp/erc/resources/erc-d/resources/drop-a.eld new file mode 100644 index 00000000000..2e23eeb20ff --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/drop-a.eld @@ -0,0 +1,4 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS " (? ?:) "a") + (0 "hi")) +((drop 0.01 DROP)) diff --git a/test/lisp/erc/resources/erc-d/resources/drop-b.eld b/test/lisp/erc/resources/erc-d/resources/drop-b.eld new file mode 100644 index 00000000000..facecd5e812 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/drop-b.eld @@ -0,0 +1,4 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS " (? ?:) "b") + (0 "hi")) +((linger 1 LINGER)) diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld new file mode 100644 index 00000000000..36b1cc23081 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld @@ -0,0 +1,33 @@ +;;; -*- mode: lisp-data -*- +((fake 0 "FAKE noop")) + +((nick 1.2 "NICK tester")) + +((user 2.2 "USER user 0 * :tester") + (0. ":irc.barnet.org 001 tester :Welcome to the BAR Network tester") + (0. ":irc.barnet.org 002 tester :Your host is irc.barnet.org") + (0. ":irc.barnet.org 003 tester :This server was created just now") + (0. ":irc.barnet.org 004 tester irc.barnet.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0. ":irc.barnet.org 005 tester MODES NETWORK=BarNet NICKLEN=32 PREFIX=(qaohv)~&@%+ :are supported by this server") + (0. ":irc.barnet.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0. ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0. ":irc.barnet.org 253 tester 0 :unregistered connections") + (0. ":irc.barnet.org 254 tester 1 :channels formed") + (0. ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0. ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0. ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0. ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0. ":irc.barnet.org 221 tester +Zi") + (0. ":irc.barnet.org 306 tester :You have been marked as being away") + (0 ":tester!~u@awyxgybtkx7uq.irc JOIN #chan") + (0 ":irc.barnet.org 353 joe = #chan :+joe!~joe@example.com @%+mike!~mike@example.org") + (0 ":irc.barnet.org 366 joe #chan :End of NAMES list")) + +((mode 1 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620805269") + (0.1 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: Yes, a dozen; and as many to the vantage, as would store the world they played for.") + (0.05 ":mike!~u@awyxgybtkx7uq.irc PRIVMSG #chan :joe: As he regards his aged father's life.") + (0.05 ":joe!~u@awyxgybtkx7uq.irc PRIVMSG #chan :mike: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonour in doing it.")) diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld new file mode 100644 index 00000000000..5dbea50f865 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld @@ -0,0 +1,32 @@ +;;; -*- mode: lisp-data -*- + +((nick 1.2 "NICK tester")) + +((user 2.2 "USER user 0 * :tester") + (0. ":irc.foonet.org 001 tester :Welcome to the FOO Network tester") + (0. ":irc.foonet.org 002 tester :Your host is irc.foonet.org") + (0. ":irc.foonet.org 003 tester :This server was created just now") + (0. ":irc.foonet.org 004 tester irc.foonet.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0. ":irc.foonet.org 005 tester MODES NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ :are supported by this server") + (0. ":irc.foonet.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0. ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0. ":irc.foonet.org 253 tester 0 :unregistered connections") + (0. ":irc.foonet.org 254 tester 1 :channels formed") + (0. ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0. ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0. ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0. ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0. ":irc.foonet.org 221 tester +Zi") + (0. ":irc.foonet.org 306 tester :You have been marked as being away") + (0 ":tester!~u@awyxgybtkx7uq.irc JOIN #chan") + (0 ":irc.foonet.org 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.foonet.org 366 alice #chan :End of NAMES list")) + +((mode 2 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620805269") + (0.1 ":alice!~u@awyxgybtkx7uq.irc PRIVMSG #chan :bob: Yes, a dozen; and as many to the vantage, as would store the world they played for.") + (0.05 ":bob!~u@awyxgybtkx7uq.irc PRIVMSG #chan :alice: As he regards his aged father's life.") + (0.05 ":alice!~u@awyxgybtkx7uq.irc PRIVMSG #chan :bob: It is a rupture that you may easily heal; and the cure of it not only saves your brother, but keeps you from dishonour in doing it.")) diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-stub.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-stub.eld new file mode 100644 index 00000000000..d93313023d0 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/dynamic-stub.eld @@ -0,0 +1,4 @@ +;;; -*- mode: lisp-data -*- +((pass 10.0 "PASS " (? ?:) token ":changeme")) + +((fake 0 "FAKE")) diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic.eld b/test/lisp/erc/resources/erc-d/resources/dynamic.eld new file mode 100644 index 00000000000..459b6e52bfe --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/dynamic.eld @@ -0,0 +1,30 @@ +;;; -*- mode: lisp-data -*- +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 2.2 "NICK tester")) + +((user 2.2 "USER " user " " (ignored digit "*") " :" realname) + (0.0 ":" dom " 001 " nick " :Welcome to the Internet Relay Network tester") + (0.0 ":" dom " 002 " nick " :Your host is " dom) + (0.0 ":" dom " 003 " nick " :This server was created just now") + (0.0 ":" dom " 004 " nick " " dom " BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.0 ":" dom " 005 " nick " MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0.0 ":" dom " 251 " nick " :There are 3 users and 0 invisible on 1 server(s)") + (0.0 ":" dom " 252 " nick " 0 :IRC Operators online") + (0.0 ":" dom " 253 " nick " 0 :unregistered connections") + (0.0 ":" dom " 254 " nick " 1 :channels formed") + (0.0 ":" dom " 255 " nick " :I have 3 clients and 0 servers") + (0.0 ":" dom " 265 " nick " 3 3 :Current local users 3, max 3") + (0.0 ":" dom " 266 " nick " 3 3 :Current global users 3, max 3") + (0.0 ":" dom " 422 " nick " :MOTD File is missing")) + +((mode-user 2.2 "MODE tester +i") + (0.0 ":" dom " 221 " nick " +Zi") + + (0.0 ":" dom " 306 " nick " :You have been marked as being away") + (0.0 ":" nick "!~" nick "@localhost JOIN #chan") + (0.0 ":" dom " 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0.0 ":" dom " 366 alice #chan :End of NAMES list")) + +((mode 2.2 "MODE #chan") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :" nick ": hey")) diff --git a/test/lisp/erc/resources/erc-d/resources/eof.eld b/test/lisp/erc/resources/erc-d/resources/eof.eld new file mode 100644 index 00000000000..5da84b2e74f --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/eof.eld @@ -0,0 +1,33 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0 ":irc.example.org 003 tester :This server was created just now") + (0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + ;; Just to mix thing's up (force handler to schedule timer) + (0.1 ":irc.example.org 252 tester 0 :IRC Operators online") + (0 ":irc.example.org 253 tester 0 :unregistered connections") + (0 ":irc.example.org 254 tester 1 :channels formed") + (0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.example.org 221 tester +Zi") + (0 ":irc.example.org 306 tester :You have been marked as being away") + (0 ":tester!~tester@localhost JOIN #chan") + (0 ":irc.example.org 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.example.org 366 alice #chan :End of NAMES list")) + +((mode-chan 1.2 "MODE #chan") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")) + +((eof 1.0 EOF)) diff --git a/test/lisp/erc/resources/erc-d/resources/foreign.eld b/test/lisp/erc/resources/erc-d/resources/foreign.eld new file mode 100644 index 00000000000..64a5dca8b10 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/foreign.eld @@ -0,0 +1,5 @@ +;;; -*- mode: lisp-data -*- +((one 5 "ONE one") + (0 "echo ONE one")) +((two 5 "TWO two") + (0 "echo TWO two")) diff --git a/test/lisp/erc/resources/erc-d/resources/fuzzy.eld b/test/lisp/erc/resources/erc-d/resources/fuzzy.eld new file mode 100644 index 00000000000..0504b6a6682 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/fuzzy.eld @@ -0,0 +1,42 @@ +;;; -*- mode: lisp-data -*- +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.5 "USER user 0 * :tester") + (0.0 "@time=" now " :irc.org 001 tester :Welcome to the Internet Relay Network tester") + (0.0 "@time=" now " :irc.org 002 tester :Your host is irc.org") + (0.0 "@time=" now " :irc.org 003 tester :This server was created just now") + (0.0 "@time=" now " :irc.org 004 tester irc.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.0 "@time=" now " :irc.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+ :are supported by this server") + (0.0 "@time=" now " :irc.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0.0 "@time=" now " :irc.org 252 tester 0 :IRC Operators online") + (0.0 "@time=" now " :irc.org 253 tester 0 :unregistered connections") + (0.0 "@time=" now " :irc.org 254 tester 1 :channels formed") + (0.0 "@time=" now " :irc.org 255 tester :I have 3 clients and 0 servers") + (0.0 "@time=" now " :irc.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 "@time=" now " :irc.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 "@time=" now " :irc.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.0 "@time=" now " :irc.org 221 tester +Zi") + (0.0 "@time=" now " :irc.org 306 tester :You have been marked as being away")) + +((~join-foo 3.2 "JOIN #foo") + (0 "@time=" now " :tester!~tester@localhost JOIN #foo") + (0 "@time=" now " :irc.example.org 353 alice = #foo :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 "@time=" now " :irc.example.org 366 alice #foo :End of NAMES list")) + +((~join-bar 1.2 "JOIN #bar") + (0 "@time=" now " :tester!~tester@localhost JOIN #bar") + (0 "@time=" now " :irc.example.org 353 alice = #bar :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 "@time=" now " :irc.example.org 366 alice #bar :End of NAMES list")) + +((~mode-foo 3.2 "MODE #foo") + (0.0 "@time=" now " :irc.example.org 324 tester #foo +Cint") + (0.0 "@time=" now " :irc.example.org 329 tester #foo 1519850102") + (0.1 "@time=" now " :bob!~bob@example.org PRIVMSG #foo :hey")) + +((mode-bar 10.2 "MODE #bar") + (0.0 "@time=" now " :irc.example.org 324 tester #bar +HMfnrt 50:5h :10:5") + (0.0 "@time=" now " :irc.example.org 329 tester #bar :1602642829") + (0.1 "@time=" now " :alice!~alice@example.com PRIVMSG #bar :hi")) diff --git a/test/lisp/erc/resources/erc-d/resources/incremental.eld b/test/lisp/erc/resources/erc-d/resources/incremental.eld new file mode 100644 index 00000000000..a1b48495ec3 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/incremental.eld @@ -0,0 +1,43 @@ +;;; -*- mode: lisp-data -*- +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0.0 ":irc.foo.net 001 tester :Welcome to the Internet Relay Network tester") + (0.0 ":irc.foo.net 002 tester :Your host is irc.foo.net") + (0.0 ":irc.foo.net 003 tester :This server was created just now") + (0.0 ":irc.foo.net 004 tester irc.foo.net BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.0 ":irc.foo.net 005 tester MODES NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0.0 ":irc.foo.net 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0.0 ":irc.foo.net 252 tester 0 :IRC Operators online") + (0.0 ":irc.foo.net 253 tester 0 :unregistered connections") + (0.0 ":irc.foo.net 254 tester 1 :channels formed") + (0.0 ":irc.foo.net 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.foo.net 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.foo.net 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.foo.net 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.0 ":irc.foo.net 221 tester +Zi") + (0.0 ":irc.foo.net 306 tester :You have been marked as being away")) + +((join 3 "JOIN #foo") + (0 ":tester!~tester@localhost JOIN #foo") + (0 ":irc.foo.net 353 alice = #foo :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.foo.net 366 alice #foo :End of NAMES list")) + +((mode 3 "MODE #foo") + (0.0 ":irc.foo.net 324 tester #foo +Cint") + (0.0 ":irc.foo.net 329 tester #foo 1519850102") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: But, in defense, by mercy, 'tis most just.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: Grows, lives, and dies, in single blessedness.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :Look for me.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: By this hand, it will not kill a fly. But come, now I will be your Rosalind in a more coming-on disposition; and ask me what you will, I will grant it.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: That I must love a loathed enemy.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: As't please your lordship: I'll leave you.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: Then there is no true lover in the forest; else sighing every minute and groaning every hour would detect the lazy foot of Time as well as a clock.") + (0.1 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: His discretion, I am sure, cannot carry his valour, for the goose carries not the fox. It is well: leave it to his discretion, and let us listen to the moon.") + (0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :Done")) + +((hi 10 "PRIVMSG #foo :Hi")) diff --git a/test/lisp/erc/resources/erc-d/resources/irc-parser-tests.eld b/test/lisp/erc/resources/erc-d/resources/irc-parser-tests.eld new file mode 100644 index 00000000000..168569f5481 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/irc-parser-tests.eld @@ -0,0 +1,380 @@ +;;; -*- mode: lisp-data; -*- + +;; https://github.com/DanielOaks/irc-parser-tests +((mask-match + (tests + ((mask . "*@127.0.0.1") + (matches "coolguy!ab@127.0.0.1" "cooldud3!~bc@127.0.0.1") + (fails "coolguy!ab@127.0.0.5" "cooldud3!~d@124.0.0.1")) + ((mask . "cool*@*") + (matches "coolguy!ab@127.0.0.1" "cooldud3!~bc@127.0.0.1" "cool132!ab@example.com") + (fails "koolguy!ab@127.0.0.5" "cooodud3!~d@124.0.0.1")) + ((mask . "cool!*@*") + (matches "cool!guyab@127.0.0.1" "cool!~dudebc@127.0.0.1" "cool!312ab@example.com") + (fails "coolguy!ab@127.0.0.1" "cooldud3!~bc@127.0.0.1" "koolguy!ab@127.0.0.5" "cooodud3!~d@124.0.0.1")) + ((mask . "cool!?username@*") + (matches "cool!ausername@127.0.0.1" "cool!~username@127.0.0.1") + (fails "cool!username@127.0.0.1")) + ((mask . "cool!a?*@*") + (matches "cool!ab@127.0.0.1" "cool!abc@127.0.0.1") + (fails "cool!a@127.0.0.1")) + ((mask . "cool[guy]!*@*") + (matches "cool[guy]!guy@127.0.0.1" "cool[guy]!a@example.com") + (fails "coolg!ab@127.0.0.1" "cool[!ac@127.0.1.1")))) + (msg-join + (tests + ((desc . "Simple test with verb and params.") + (atoms + (verb . "foo") + (params "bar" "baz" "asdf")) + (matches "foo bar baz asdf" "foo bar baz :asdf")) + ((desc . "Simple test with source and no params.") + (atoms + (source . "src") + (verb . "AWAY")) + (matches ":src AWAY")) + ((desc . "Simple test with source and empty trailing param.") + (atoms + (source . "src") + (verb . "AWAY") + (params "")) + (matches ":src AWAY :")) + ((desc . "Simple test with source.") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" "asdf")) + (matches ":coolguy foo bar baz asdf" ":coolguy foo bar baz :asdf")) + ((desc . "Simple test with trailing param.") + (atoms + (verb . "foo") + (params "bar" "baz" "asdf quux")) + (matches "foo bar baz :asdf quux")) + ((desc . "Simple test with empty trailing param.") + (atoms + (verb . "foo") + (params "bar" "baz" "")) + (matches "foo bar baz :")) + ((desc . "Simple test with trailing param containing colon.") + (atoms + (verb . "foo") + (params "bar" "baz" ":asdf")) + (matches "foo bar baz ::asdf")) + ((desc . "Test with source and trailing param.") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" "asdf quux")) + (matches ":coolguy foo bar baz :asdf quux")) + ((desc . "Test with trailing containing beginning+end whitespace.") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" " asdf quux ")) + (matches ":coolguy foo bar baz : asdf quux ")) + ((desc . "Test with trailing containing what looks like another trailing param.") + (atoms + (source . "coolguy") + (verb . "PRIVMSG") + (params "bar" "lol :) ")) + (matches ":coolguy PRIVMSG bar :lol :) ")) + ((desc . "Simple test with source and empty trailing.") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" "")) + (matches ":coolguy foo bar baz :")) + ((desc . "Trailing contains only spaces.") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" " ")) + (matches ":coolguy foo bar baz : ")) + ((desc . "Param containing tab (tab is not considered SPACE for message splitting).") + (atoms + (source . "coolguy") + (verb . "foo") + (params "b ar" "baz")) + (matches ":coolguy foo b ar baz" ":coolguy foo b ar :baz")) + ((desc . "Tag with no value and space-filled trailing.") + (atoms + (tags + (asd . "")) + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" " ")) + (matches "@asd :coolguy foo bar baz : ")) + ((desc . "Tags with escaped values.") + (atoms + (verb . "foo") + (tags + (a . "b\\and\nk") + (d . "gh;764"))) + (matches "@a=b\\\\and\\nk;d=gh\\:764 foo" "@d=gh\\:764;a=b\\\\and\\nk foo")) + ((desc . "Tags with escaped values and params.") + (atoms + (verb . "foo") + (tags + (a . "b\\and\nk") + (d . "gh;764")) + (params "par1" "par2")) + (matches "@a=b\\\\and\\nk;d=gh\\:764 foo par1 par2" "@a=b\\\\and\\nk;d=gh\\:764 foo par1 :par2" "@d=gh\\:764;a=b\\\\and\\nk foo par1 par2" "@d=gh\\:764;a=b\\\\and\\nk foo par1 :par2")) + ((desc . "Tag with long, strange values (including LF and newline).") + (atoms + (tags + (foo . "\\\\;\\s
\n")) + (verb . "COMMAND")) + (matches "@foo=\\\\\\\\\\:\\\\s\\s\\r\\n COMMAND")))) + (msg-split + (tests + ((input . "foo bar baz asdf") + (atoms + (verb . "foo") + (params "bar" "baz" "asdf"))) + ((input . ":coolguy foo bar baz asdf") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" "asdf"))) + ((input . "foo bar baz :asdf quux") + (atoms + (verb . "foo") + (params "bar" "baz" "asdf quux"))) + ((input . "foo bar baz :") + (atoms + (verb . "foo") + (params "bar" "baz" ""))) + ((input . "foo bar baz ::asdf") + (atoms + (verb . "foo") + (params "bar" "baz" ":asdf"))) + ((input . ":coolguy foo bar baz :asdf quux") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" "asdf quux"))) + ((input . ":coolguy foo bar baz : asdf quux ") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" " asdf quux "))) + ((input . ":coolguy PRIVMSG bar :lol :) ") + (atoms + (source . "coolguy") + (verb . "PRIVMSG") + (params "bar" "lol :) "))) + ((input . ":coolguy foo bar baz :") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" ""))) + ((input . ":coolguy foo bar baz : ") + (atoms + (source . "coolguy") + (verb . "foo") + (params "bar" "baz" " "))) + ((input . "@a=b;c=32;k;rt=ql7 foo") + (atoms + (verb . "foo") + (tags + (a . "b") + (c . "32") + (k . "") + (rt . "ql7")))) + ((input . "@a=b\\\\and\\nk;c=72\\s45;d=gh\\:764 foo") + (atoms + (verb . "foo") + (tags + (a . "b\\and\nk") + (c . "72 45") + (d . "gh;764")))) + ((input . "@c;h=;a=b :quux ab cd") + (atoms + (tags + (c . "") + (h . "") + (a . "b")) + (source . "quux") + (verb . "ab") + (params "cd"))) + ((input . ":src JOIN #chan") + (atoms + (source . "src") + (verb . "JOIN") + (params "#chan"))) + ((input . ":src JOIN :#chan") + (atoms + (source . "src") + (verb . "JOIN") + (params "#chan"))) + ((input . ":src AWAY") + (atoms + (source . "src") + (verb . "AWAY"))) + ((input . ":src AWAY ") + (atoms + (source . "src") + (verb . "AWAY"))) + ((input . ":cool guy foo bar baz") + (atoms + (source . "cool guy") + (verb . "foo") + (params "bar" "baz"))) + ((input . ":coolguy!ag@net5work.admin PRIVMSG foo :bar baz") + (atoms + (source . "coolguy!ag@net5work.admin") + (verb . "PRIVMSG") + (params "foo" "bar baz"))) + ((input . ":coolguy!~ag@net05work.admin PRIVMSG foo :bar baz") + (atoms + (source . "coolguy!~ag@net05work.admin") + (verb . "PRIVMSG") + (params "foo" "bar baz"))) + ((input . "@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4= :irc.example.com COMMAND param1 param2 :param3 param3") + (atoms + (tags + (tag1 . "value1") + (tag2 . "") + (vendor1/tag3 . "value2") + (vendor2/tag4 . "")) + (source . "irc.example.com") + (verb . "COMMAND") + (params "param1" "param2" "param3 param3"))) + ((input . ":irc.example.com COMMAND param1 param2 :param3 param3") + (atoms + (source . "irc.example.com") + (verb . "COMMAND") + (params "param1" "param2" "param3 param3"))) + ((input . "@tag1=value1;tag2;vendor1/tag3=value2;vendor2/tag4 COMMAND param1 param2 :param3 param3") + (atoms + (tags + (tag1 . "value1") + (tag2 . "") + (vendor1/tag3 . "value2") + (vendor2/tag4 . "")) + (verb . "COMMAND") + (params "param1" "param2" "param3 param3"))) + ((input . "COMMAND") + (atoms + (verb . "COMMAND"))) + ((input . "@foo=\\\\\\\\\\:\\\\s\\s\\r\\n COMMAND") + (atoms + (tags + (foo . "\\\\;\\s
\n")) + (verb . "COMMAND"))) + ((input . ":gravel.mozilla.org 432 #momo :Erroneous Nickname: Illegal characters") + (atoms + (source . "gravel.mozilla.org") + (verb . "432") + (params "#momo" "Erroneous Nickname: Illegal characters"))) + ((input . ":gravel.mozilla.org MODE #tckk +n ") + (atoms + (source . "gravel.mozilla.org") + (verb . "MODE") + (params "#tckk" "+n"))) + ((input . ":services.esper.net MODE #foo-bar +o foobar ") + (atoms + (source . "services.esper.net") + (verb . "MODE") + (params "#foo-bar" "+o" "foobar"))) + ((input . "@tag1=value\\\\ntest COMMAND") + (atoms + (tags + (tag1 . "value\\ntest")) + (verb . "COMMAND"))) + ((input . "@tag1=value\\1 COMMAND") + (atoms + (tags + (tag1 . "value1")) + (verb . "COMMAND"))) + ((input . "@tag1=value1\\ COMMAND") + (atoms + (tags + (tag1 . "value1")) + (verb . "COMMAND"))) + ((input . "@tag1=1;tag2=3;tag3=4;tag1=5 COMMAND") + (atoms + (tags + (tag1 . "5") + (tag2 . "3") + (tag3 . "4")) + (verb . "COMMAND"))) + ((input . "@tag1=1;tag2=3;tag3=4;tag1=5;vendor/tag2=8 COMMAND") + (atoms + (tags + (tag1 . "5") + (tag2 . "3") + (tag3 . "4") + (vendor/tag2 . "8")) + (verb . "COMMAND"))) + ((input . ":SomeOp MODE #channel :+i") + (atoms + (source . "SomeOp") + (verb . "MODE") + (params "#channel" "+i"))) + ((input . ":SomeOp MODE #channel +oo SomeUser :AnotherUser") + (atoms + (source . "SomeOp") + (verb . "MODE") + (params "#channel" "+oo" "SomeUser" "AnotherUser"))))) + (userhost-split + (tests + ((source . "coolguy") + (atoms + (nick . "coolguy"))) + ((source . "coolguy!ag@127.0.0.1") + (atoms + (nick . "coolguy") + (user . "ag") + (host . "127.0.0.1"))) + ((source . "coolguy!~ag@localhost") + (atoms + (nick . "coolguy") + (user . "~ag") + (host . "localhost"))) + ((source . "coolguy@127.0.0.1") + (atoms + (nick . "coolguy") + (host . "127.0.0.1"))) + ((source . "coolguy!ag") + (atoms + (nick . "coolguy") + (user . "ag"))) + ((source . "coolguy!ag@net5work.admin") + (atoms + (nick . "coolguy") + (user . "ag") + (host . "net5work.admin"))) + ((source . "coolguy!~ag@net05work.admin") + (atoms + (nick . "coolguy") + (user . "~ag") + (host . "net05work.admin"))))) + (validate-hostname + (tests + ((host . "irc.example.com") + (valid . t)) + ((host . "i.coolguy.net") + (valid . t)) + ((host . "irc-srv.net.uk") + (valid . t)) + ((host . "iRC.CooLguY.NeT") + (valid . t)) + ((host . "gsf.ds342.co.uk") + (valid . t)) + ((host . "324.net.uk") + (valid . t)) + ((host . "xn--bcher-kva.ch") + (valid . t)) + ((host . "-lol-.net.uk") + (valid . :false)) + ((host . "-lol.net.uk") + (valid . :false)) + ((host . "_irc._sctp.lol.net.uk") + (valid . :false)) + ((host . "irc") + (valid . :false)) + ((host . "com") + (valid . :false)) + ((host . "") + (valid . :false))))) diff --git a/test/lisp/erc/resources/erc-d/resources/linger-multi-a.eld b/test/lisp/erc/resources/erc-d/resources/linger-multi-a.eld new file mode 100644 index 00000000000..751500537d9 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/linger-multi-a.eld @@ -0,0 +1,3 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS " (? ?:) "a")) +((linger 100 LINGER))
\ No newline at end of file diff --git a/test/lisp/erc/resources/erc-d/resources/linger-multi-b.eld b/test/lisp/erc/resources/erc-d/resources/linger-multi-b.eld new file mode 100644 index 00000000000..c906c9e649b --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/linger-multi-b.eld @@ -0,0 +1,3 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS " (? ?:) "b")) +((linger 1 LINGER)) diff --git a/test/lisp/erc/resources/erc-d/resources/linger.eld b/test/lisp/erc/resources/erc-d/resources/linger.eld new file mode 100644 index 00000000000..36c81a3af4b --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/linger.eld @@ -0,0 +1,33 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0 ":irc.example.org 003 tester :This server was created just now") + (0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + ;; Just to mix thing's up (force handler to schedule timer) + (0.1 ":irc.example.org 252 tester 0 :IRC Operators online") + (0 ":irc.example.org 253 tester 0 :unregistered connections") + (0 ":irc.example.org 254 tester 1 :channels formed") + (0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.example.org 221 tester +Zi") + (0 ":irc.example.org 306 tester :You have been marked as being away") + (0 ":tester!~tester@localhost JOIN #chan") + (0 ":irc.example.org 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.example.org 366 alice #chan :End of NAMES list")) + +((mode-chan 1.2 "MODE #chan") + (0 ":bob!~bob@example.org PRIVMSG #chan :hey")) + +((linger 1.0 LINGER)) diff --git a/test/lisp/erc/resources/erc-d/resources/no-block.eld b/test/lisp/erc/resources/erc-d/resources/no-block.eld new file mode 100644 index 00000000000..2811923d8ac --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/no-block.eld @@ -0,0 +1,55 @@ +;;; -*- mode: lisp-data -*- +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0.0 ":irc.org 001 tester :Welcome to the Internet Relay Network tester") + (0.0 ":irc.org 002 tester :Your host is irc.org") + (0.0 ":irc.org 003 tester :This server was created just now") + (0.0 ":irc.org 004 tester irc.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.0 ":irc.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0.0 ":irc.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0.0 ":irc.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.org 253 tester 0 :unregistered connections") + (0.0 ":irc.org 254 tester 1 :channels formed") + (0.0 ":irc.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.0 ":irc.org 221 tester +Zi") + (0.0 ":irc.org 306 tester :You have been marked as being away")) + +((join-foo 1.2 "JOIN #foo") + (0 ":tester!~tester@localhost JOIN #foo") + (0 ":irc.example.org 353 alice = #foo :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.example.org 366 alice #foo :End of NAMES list")) + +;; This would time out if the mode-foo's outgoing blocked (remove minus signs to see) +((~join-bar 1.5 "JOIN #bar") + (0 ":tester!~tester@localhost JOIN #bar") + (0 ":irc.example.org 353 alice = #bar :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.example.org 366 alice #bar :End of NAMES list")) + +((mode-foo 1.2 "MODE #foo") + (0.0 ":irc.example.org 324 tester #foo +Cint") + (0.0 ":irc.example.org 329 tester #foo 1519850102") + (-0.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: But, in defense, by mercy, 'tis most just.") + (-0.2 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: Grows, lives, and dies, in single blessedness.") + (-0.3 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: For these two hours, Rosalind, I will leave thee.") + (-0.4 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: By this hand, it will not kill a fly. But come, now I will be your Rosalind in a more coming-on disposition; and ask me what you will, I will grant it.") + (-0.5 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: That I must love a loathed enemy.") + (-0.6 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: As't please your lordship: I'll leave you.") + (-0.7 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: Then there is no true lover in the forest; else sighing every minute and groaning every hour would detect the lazy foot of Time as well as a clock.") + (-0.8 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: His discretion, I am sure, cannot carry his valour, for the goose carries not the fox. It is well: leave it to his discretion, and let us listen to the moon.") + (-0.9 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: As living here and you no use of him.") + (-1.0 ":alice!~u@svpn88yjcdj42.irc PRIVMSG #foo :bob: If there be truth in sight, you are my Rosalind.") + (-1.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :alice: That is another's lawful promis'd love.") + (-1.1 ":bob!~u@svpn88yjcdj42.irc PRIVMSG #foo :I am heard.")) + +((mode-bar 1.5 "MODE #bar") + (0.0 ":irc.example.org 324 tester #bar +HMfnrt 50:5h :10:5") + (0.0 ":irc.example.org 329 tester #bar :1602642829") + (0.1 ":alice!~alice@example.com PRIVMSG #bar :hi 123")) diff --git a/test/lisp/erc/resources/erc-d/resources/no-match.eld b/test/lisp/erc/resources/erc-d/resources/no-match.eld new file mode 100644 index 00000000000..d147be1e084 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/no-match.eld @@ -0,0 +1,32 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0 ":irc.example.org 003 tester :This server was created just now") + (0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0 ":irc.example.org 252 tester 0 :IRC Operators online") + (0 ":irc.example.org 253 tester 0 :unregistered connections") + (0 ":irc.example.org 254 tester 1 :channels formed") + (0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.example.org 221 tester +Zi") + (0 ":irc.example.org 306 tester :You have been marked as being away")) + +((join 1.2 "JOIN #chan") + (0 ":tester!~tester@localhost JOIN #chan") + (0 ":irc.example.org 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0 ":irc.example.org 366 alice #chan :End of NAMES list")) + +((mode-chan 0.2 "MODE #chan") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")) diff --git a/test/lisp/erc/resources/erc-d/resources/no-pong.eld b/test/lisp/erc/resources/erc-d/resources/no-pong.eld new file mode 100644 index 00000000000..30cd805d76c --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/no-pong.eld @@ -0,0 +1,27 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((~ping 1.2 "PING " nonce) + (0.1 ":irc.example.org PONG irc.example.com " echo)) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0 ":irc.example.org 003 tester :This server was created just now") + (0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0 ":irc.example.org 252 tester 0 :IRC Operators online") + (0 ":irc.example.org 253 tester 0 :unregistered connections") + (0 ":irc.example.org 254 tester 1 :channels formed") + (0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.example.org 221 tester +Zi") + (0 ":irc.example.org 306 tester :You have been marked as being away")) diff --git a/test/lisp/erc/resources/erc-d/resources/nonstandard.eld b/test/lisp/erc/resources/erc-d/resources/nonstandard.eld new file mode 100644 index 00000000000..c9cd608e6be --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/nonstandard.eld @@ -0,0 +1,6 @@ +;;; -*- mode: lisp-data -*- +((one 1 "ONE one")) +((two 1 "TWO two")) +((blank 1 "")) +((one-space 1 " ")) +((two-spaces 1 " ")) diff --git a/test/lisp/erc/resources/erc-d/resources/proxy-barnet.eld b/test/lisp/erc/resources/erc-d/resources/proxy-barnet.eld new file mode 100644 index 00000000000..e74d20d5b37 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/proxy-barnet.eld @@ -0,0 +1,24 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) network ":changeme")) +((nick 1.2 "NICK tester")) + +((user 1.2 "USER user 0 * :tester") + (0.001 ":" fqdn " 001 tester :Welcome to the BAR Network tester") + (0.002 ":" fqdn " 002 tester :Your host is " fqdn) + (0.003 ":" fqdn " 003 tester :This server was created just now") + (0.004 ":" fqdn " 004 tester " fqdn " BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.005 ":" fqdn " 005 tester MODES NETWORK=" net " NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0.006 ":" fqdn " 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0.007 ":" fqdn " 252 tester 0 :IRC Operators online") + (0.008 ":" fqdn " 253 tester 0 :unregistered connections") + (0.009 ":" fqdn " 254 tester 1 :channels formed") + (0.010 ":" fqdn " 255 tester :I have 3 clients and 0 servers") + (0.011 ":" fqdn " 265 tester 3 3 :Current local users 3, max 3") + (0.012 ":" fqdn " 266 tester 3 3 :Current global users 3, max 3") + (0.013 ":" fqdn " 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.014 ":" fqdn " 221 tester +Zi") + (0.015 ":" fqdn " 306 tester :You have been marked as being away")) diff --git a/test/lisp/erc/resources/erc-d/resources/proxy-foonet.eld b/test/lisp/erc/resources/erc-d/resources/proxy-foonet.eld new file mode 100644 index 00000000000..cc2e9d253c1 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/proxy-foonet.eld @@ -0,0 +1,24 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) network ":changeme")) +((nick 1.2 "NICK tester")) + +((user 2.2 "USER user 0 * :tester") + (0.015 ":" fqdn " 001 tester :Welcome to the FOO Network tester") + (0.014 ":" fqdn " 002 tester :Your host is " fqdn) + (0.013 ":" fqdn " 003 tester :This server was created just now") + (0.012 ":" fqdn " 004 tester " fqdn " BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.011 ":" fqdn " 005 tester MODES NETWORK=" net " NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0.010 ":" fqdn " 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0.009 ":" fqdn " 252 tester 0 :IRC Operators online") + (0.008 ":" fqdn " 253 tester 0 :unregistered connections") + (0.007 ":" fqdn " 254 tester 1 :channels formed") + (0.006 ":" fqdn " 255 tester :I have 3 clients and 0 servers") + (0.005 ":" fqdn " 265 tester 3 3 :Current local users 3, max 3") + (0.004 ":" fqdn " 266 tester 3 3 :Current global users 3, max 3") + (0.003 ":" fqdn " 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.002 ":" fqdn " 221 tester +Zi") + (0.001 ":" fqdn " 306 tester :You have been marked as being away")) diff --git a/test/lisp/erc/resources/erc-d/resources/proxy-solo.eld b/test/lisp/erc/resources/erc-d/resources/proxy-solo.eld new file mode 100644 index 00000000000..af216c80edc --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/proxy-solo.eld @@ -0,0 +1,9 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :" (group (+ alpha)) eos) + (0 ":*status!znc@znc.in NOTICE " nick " :You have no networks configured." + " Use /znc AddNetwork <network> to add one.") + (0 ":irc.znc.in 001 " nick " :Welcome " nick "!")) diff --git a/test/lisp/erc/resources/erc-d/resources/proxy-subprocess.el b/test/lisp/erc/resources/erc-d/resources/proxy-subprocess.el new file mode 100644 index 00000000000..bb8869dff69 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/proxy-subprocess.el @@ -0,0 +1,45 @@ +;;; proxy-subprocess.el --- Example setup file for erc-d -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: +;;; Code: + +(defvar erc-d-tmpl-vars) + +(setq erc-d-tmpl-vars + + (list + (cons 'fqdn (lambda (helper) + (let ((name (funcall helper :dialog-name))) + (funcall helper :set + (if (eq name 'proxy-foonet) + "irc.foo.net" + "irc.bar.net"))))) + + (cons 'net (lambda (helper) + (let ((name (funcall helper :dialog-name))) + (funcall helper :set + (if (eq name 'proxy-foonet) + "FooNet" + "BarNet"))))) + + (cons 'network '(group (+ alpha))))) + +;;; proxy-subprocess.el ends here diff --git a/test/lisp/erc/resources/erc-d/resources/timeout.eld b/test/lisp/erc/resources/erc-d/resources/timeout.eld new file mode 100644 index 00000000000..9cfad4fa8cd --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/timeout.eld @@ -0,0 +1,27 @@ +;;; -*- mode: lisp-data -*- + +((pass 10.0 "PASS " (? ?:) "changeme")) +((nick 0.2 "NICK tester")) + +((user 0.2 "USER user 0 * :tester") + (0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0 ":irc.example.org 003 tester :This server was created just now") + (0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0 ":irc.example.org 252 tester 0 :IRC Operators online") + (0 ":irc.example.org 253 tester 0 :unregistered connections") + (0 ":irc.example.org 254 tester 1 :channels formed") + (0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":irc.example.org 221 tester +Zi") + (0 ":irc.example.org 306 tester :You have been marked as being away")) + +((mode 0.2 "MODE #chan") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")) diff --git a/test/lisp/erc/resources/erc-d/resources/unexpected.eld b/test/lisp/erc/resources/erc-d/resources/unexpected.eld new file mode 100644 index 00000000000..ac0a8fecfa6 --- /dev/null +++ b/test/lisp/erc/resources/erc-d/resources/unexpected.eld @@ -0,0 +1,28 @@ +;;; -*- mode: lisp-data -*- +((t 10.0 "PASS " (? ?:) "changeme")) +((t 0.2 "NICK tester")) + +((t 0.2 "USER user 0 * :tester") + (0.0 ":irc.example.org 001 tester :Welcome to the Internet Relay Network tester") + (0.0 ":irc.example.org 002 tester :Your host is irc.example.org") + (0.0 ":irc.example.org 003 tester :This server was created just now") + (0.0 ":irc.example.org 004 tester irc.example.org BERios CEIRabehiklmnoqstv Iabehkloqv") + (0.0 ":irc.example.org 005 tester MODES NETWORK=ExampleOrg NICKLEN=32 PREFIX=(qaohv)~&@%+" + " :are supported by this server") + (0.0 ":irc.example.org 251 tester :There are 3 users and 0 invisible on 1 server(s)") + (0.0 ":irc.example.org 252 tester 0 :IRC Operators online") + (0.0 ":irc.example.org 253 tester 0 :unregistered connections") + (0.0 ":irc.example.org 254 tester 1 :channels formed") + (0.0 ":irc.example.org 255 tester :I have 3 clients and 0 servers") + (0.0 ":irc.example.org 265 tester 3 3 :Current local users 3, max 3") + (0.0 ":irc.example.org 266 tester 3 3 :Current global users 3, max 3") + (0.0 ":irc.example.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0.0 ":irc.example.org 221 tester +Zi") + + (0.0 ":irc.example.org 306 tester :You have been marked as being away") + (0.0 ":tester!~tester@localhost JOIN #chan") + (0.0 ":irc.example.org 353 alice = #chan :+alice!~alice@example.com @%+bob!~bob@example.org") + (0.0 ":irc.example.org 366 alice #chan :End of NAMES list") + (0.1 ":bob!~bob@example.org PRIVMSG #chan :hey")) diff --git a/test/lisp/erc/resources/erc-scenarios-common.el b/test/lisp/erc/resources/erc-scenarios-common.el new file mode 100644 index 00000000000..bc2cb68cd86 --- /dev/null +++ b/test/lisp/erc/resources/erc-scenarios-common.el @@ -0,0 +1,516 @@ +;;; erc-scenarios-common.el --- Common helpers for ERC scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. +;; +;; This file is part of GNU Emacs. +;; +;; This program is free software: you can redistribute it and/or +;; modify it under the terms of the GNU General Public License as +;; published by the Free Software Foundation, either version 3 of the +;; License, or (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see +;; <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; These are e2e-ish test cases primarily intended to assert core, +;; fundamental behavior expected of any modern IRC client. Tests may +;; also simulate specific scenarios drawn from bug reports. Incoming +;; messages are provided by playback scripts resembling I/O logs. In +;; place of time stamps, they have time deltas, which are used to +;; govern the test server in a fashion reminiscent of music rolls (or +;; the script(1) UNIX program). These scripts can be found in the +;; other directories under test/lisp/erc/resources. +;; +;; Isolation: +;; +;; The set of enabled modules is shared among all tests. The function +;; `erc-update-modules' activates them (as minor modes), but it never +;; deactivates them. So there's no going back, and let-binding +;; `erc-modules' is useless. The safest route is therefore to (1) +;; assume the set of default modules is already activated or will be +;; over the course of the test session and (2) let-bind relevant user +;; options as needed. For example, to limit the damage of +;; `erc-autojoin-channels-alist' to a given test, assume the +;; `erc-join' library has already been loaded or will be on the next +;; call to `erc-open'. And then just let-bind +;; `erc-autojoin-channels-alist' for the duration of the test. +;; +;; Playing nice: +;; +;; Right now, these tests all rely on an ugly fixture macro named +;; `erc-scenarios-common-with-cleanup', which is defined just below. +;; It helps restore (but not really prepare) the environment by +;; destroying any stray processes or buffers named in the first +;; argument, a `let*'-style VAR-LIST. Relying on such a macro is +;; unfortunate because in many ways it actually hampers readability by +;; favoring magic over verbosity. But without it (or something +;; similar), any failing test would cause all subsequent tests in this +;; file to fail like dominoes (making all but the first backtrace +;; useless). +;; +;; Misc: +;; +;; Note that in the following examples, nicknames Alice and Bob are +;; always associated with the fake network FooNet, while nicks Joe and +;; Mike are always on BarNet. (Networks are sometimes downcased.) +;; +;; XXX This file should *not* contain any test cases. + +;;; Code: + +(require 'ert-x) ; cl-lib +(eval-and-compile + (let* ((d (expand-file-name ".." (ert-resource-directory))) + (load-path (cons (concat d "/erc-d") load-path))) + (require 'erc-d-t) + (require 'erc-d))) + +(require 'erc-backend) + +(eval-when-compile (require 'erc-join) + (require 'erc-services)) + +(declare-function erc-network "erc-networks") +(defvar erc-network) + +(defvar erc-scenarios-common--resources-dir + (expand-file-name "../" (ert-resource-directory))) + +;; Teardown is already inhibited when running interactively, which +;; prevents subsequent tests from succeeding, so we might as well +;; treat inspection as the goal. +(unless noninteractive + (setq erc-server-auto-reconnect nil)) + +(defvar erc-scenarios-common-dialog nil) +(defvar erc-scenarios-common-extra-teardown nil) + +(defun erc-scenarios-common--add-silence () + (advice-add #'erc-login :around #'erc-d-t-silence-around) + (advice-add #'erc-handle-login :around #'erc-d-t-silence-around) + (advice-add #'erc-server-connect :around #'erc-d-t-silence-around)) + +(defun erc-scenarios-common--remove-silence () + (advice-remove #'erc-login #'erc-d-t-silence-around) + (advice-remove #'erc-handle-login #'erc-d-t-silence-around) + (advice-remove #'erc-server-connect #'erc-d-t-silence-around)) + +(defun erc-scenarios-common--print-trace () + (when (and (boundp 'trace-buffer) (get-buffer trace-buffer)) + (with-current-buffer trace-buffer + (message "%S" (buffer-string)) + (kill-buffer)))) + +(eval-and-compile + (defun erc-scenarios-common--make-bindings (bindings) + `((erc-d-u-canned-dialog-dir (expand-file-name + (or erc-scenarios-common-dialog + (cadr (assq 'erc-scenarios-common-dialog + ',bindings))) + erc-scenarios-common--resources-dir)) + (erc-d-tmpl-vars `(,@erc-d-tmpl-vars + (quit . ,(erc-quit/part-reason-default)) + (erc-version . ,erc-version))) + (erc-modules (copy-sequence erc-modules)) + (inhibit-interaction t) + (auth-source-do-cache nil) + (erc-auth-source-parameters-join-function nil) + (erc-autojoin-channels-alist nil) + (erc-server-auto-reconnect nil) + (erc-d-linger-secs 10) + ,@bindings))) + +(defmacro erc-scenarios-common-with-cleanup (bindings &rest body) + "Provide boilerplate cleanup tasks after calling BODY with BINDINGS. + +If an `erc-d' process exists, wait for it to start before running BODY. +If `erc-autojoin-mode' mode is bound, restore it during cleanup if +disabled by BODY. Other defaults common to these test cases are added +below and can be overridden, except when wanting the \"real\" default +value, which must be looked up or captured outside of the calling form. + +Dialog resource directories are located by expanding the variable +`erc-scenarios-common-dialog' or its value in BINDINGS." + (declare (indent 1)) + + (let* ((orig-autojoin-mode (make-symbol "orig-autojoin-mode")) + (combined `((,orig-autojoin-mode (bound-and-true-p erc-autojoin-mode)) + ,@(erc-scenarios-common--make-bindings bindings)))) + + `(erc-d-t-with-cleanup (,@combined) + + (ert-info ("Restore autojoin, etc., kill ERC buffers") + (dolist (buf (buffer-list)) + (when-let ((erc-d-u--process-buffer) + (proc (get-buffer-process buf))) + (delete-process proc))) + + (erc-scenarios-common--remove-silence) + + (when erc-scenarios-common-extra-teardown + (ert-info ("Running extra teardown") + (funcall erc-scenarios-common-extra-teardown))) + + (when (and (boundp 'erc-autojoin-mode) + (not (eq erc-autojoin-mode ,orig-autojoin-mode))) + (erc-autojoin-mode (if ,orig-autojoin-mode +1 -1))) + + (when noninteractive + (erc-scenarios-common--print-trace) + (erc-d-t-kill-related-buffers) + (delete-other-windows))) + + (erc-scenarios-common--add-silence) + + (ert-info ("Wait for dumb server") + (dolist (buf (buffer-list)) + (with-current-buffer buf + (when erc-d-u--process-buffer + (erc-d-t-search-for 3 "Starting"))))) + + (ert-info ("Activate erc-debug-irc-protocol") + (unless (and noninteractive (not erc-debug-irc-protocol)) + (erc-toggle-debug-irc-protocol))) + + ,@body))) + +(defun erc-scenarios-common-assert-initial-buf-name (id port) + ;; Assert no limbo period when explicit ID given + (should (string= (if id + (symbol-name id) + (format "127.0.0.1:%d" port)) + (buffer-name)))) + +(defun erc-scenarios-common-buflist (prefix) + "Return list of buffers with names sharing PREFIX." + (let (case-fold-search) + (erc-networks--id-sort-buffers + (delq nil + (mapcar (lambda (b) + (when (string-prefix-p prefix (buffer-name b)) b)) + (buffer-list)))))) + +;; This is more realistic than `erc-send-message' because it runs +;; `erc-pre-send-functions', etc. Keyboard macros may be preferable, +;; but they sometimes experience complications when an earlier test +;; has failed. +(defun erc-scenarios-common-say (str) + (let (erc-accidental-paste-threshold-seconds) + (goto-char erc-input-marker) + (insert str) + (erc-send-current-line))) + + +;;;; Fixtures + +(cl-defun erc-scenarios-common--base-network-id-bouncer + ((&key autop foo-id bar-id after + &aux + (foo-id (and foo-id 'oofnet)) + (bar-id (and bar-id 'rabnet)) + (serv-buf-foo (if foo-id "oofnet" "foonet")) + (serv-buf-bar (if bar-id "rabnet" "barnet")) + (chan-buf-foo (if foo-id "#chan@oofnet" "#chan@foonet")) + (chan-buf-bar (if bar-id "#chan@rabnet" "#chan@barnet"))) + &rest dialogs) + "Ensure retired option `erc-rename-buffers' is now the default behavior. +The option `erc-rename-buffers' is now deprecated and on by default, so +this now just asserts baseline behavior. Originally from scenario +clash-of-chans/rename-buffers as explained in Bug#48598: 28.0.50; +buffer-naming collisions involving bouncers in ERC." + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/netid/bouncer") + (erc-d-t-cleanup-sleep-secs 1) + (erc-server-flood-penalty 0.1) + (dumb-server (apply #'erc-d-run "localhost" t dialogs)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-server-auto-reconnect autop) + erc-server-buffer-foo erc-server-process-foo + erc-server-buffer-bar erc-server-process-bar) + + (ert-info ("Connect to foonet") + (with-current-buffer + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester" + :id foo-id)) + (setq erc-server-process-foo erc-server-process) + (erc-scenarios-common-assert-initial-buf-name foo-id port) + (erc-d-t-wait-for 3 (eq (erc-network) 'foonet)) + (erc-d-t-wait-for 3 (string= (buffer-name) serv-buf-foo)) + (funcall expect 5 "foonet"))) + + (ert-info ("Join #chan@foonet") + (with-current-buffer erc-server-buffer-foo (erc-cmd-JOIN "#chan")) + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 5 "<alice>"))) + + (ert-info ("Connect to barnet") + (with-current-buffer + (setq erc-server-buffer-bar (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester" + :id bar-id)) + (setq erc-server-process-bar erc-server-process) + (erc-scenarios-common-assert-initial-buf-name bar-id port) + (erc-d-t-wait-for 6 (eq (erc-network) 'barnet)) + (erc-d-t-wait-for 3 (string= (buffer-name) serv-buf-bar)) + (funcall expect 5 "barnet"))) + + (ert-info ("Server buffers are unique, no names based on IPs") + (should-not (eq erc-server-buffer-foo erc-server-buffer-bar)) + (should-not (erc-scenarios-common-buflist "127.0.0.1"))) + + (ert-info ("Join #chan@barnet") + (with-current-buffer erc-server-buffer-bar (erc-cmd-JOIN "#chan"))) + + (erc-d-t-wait-for 5 "Exactly 2 #chan-prefixed buffers exist" + (equal (list (get-buffer chan-buf-bar) + (get-buffer chan-buf-foo)) + (erc-scenarios-common-buflist "#chan"))) + + (ert-info ("#chan@<esid> is exclusive to foonet") + (with-current-buffer chan-buf-foo + (erc-d-t-search-for 1 "<bob>") + (erc-d-t-absent-for 0.1 "<joe>") + (should (eq erc-server-process erc-server-process-foo)) + (erc-d-t-search-for 10 "ape is dead") + (erc-d-t-wait-for 5 (not (erc-server-process-alive))))) + + (ert-info ("#chan@<esid> is exclusive to barnet") + (with-current-buffer chan-buf-bar + (erc-d-t-search-for 1 "<joe>") + (erc-d-t-absent-for 0.1 "<bob>") + (erc-d-t-wait-for 5 (eq erc-server-process erc-server-process-bar)) + (erc-d-t-search-for 15 "keeps you from dishonour") + (erc-d-t-wait-for 5 (not (erc-server-process-alive))))) + + (when after (funcall after)))) + +(defun erc-scenarios-common--clash-rename-pass-handler (dialog exchange) + (when (eq (erc-d-dialog-name dialog) 'stub-again) + (let* ((match (erc-d-exchange-match exchange 1)) + (sym (if (string= match "foonet") 'foonet-again 'barnet-again))) + (should (member match (list "foonet" "barnet"))) + (erc-d-load-replacement-dialog dialog sym 1)))) + +(defun erc-scenarios-common--base-network-id-bouncer--reconnect (foo-id bar-id) + (let ((erc-d-tmpl-vars '((token . (group (| "barnet" "foonet"))))) + (erc-d-match-handlers + ;; Auto reconnect is nondeterministic, so let computer decide + (list :pass #'erc-scenarios-common--clash-rename-pass-handler)) + (after + (lambda () + ;; Simulate disconnection and `erc-server-auto-reconnect' + (ert-info ("Reconnect to foonet and barnet back-to-back") + (with-current-buffer (if foo-id "oofnet" "foonet") + (erc-d-t-wait-for 10 (erc-server-process-alive))) + (with-current-buffer (if bar-id "rabnet" "barnet") + (erc-d-t-wait-for 10 (erc-server-process-alive)))) + + (ert-info ("#chan@foonet is exclusive to foonet") + (with-current-buffer (if foo-id "#chan@oofnet" "#chan@foonet") + (erc-d-t-search-for 1 "<alice>") + (erc-d-t-absent-for 0.1 "<joe>") + (erc-d-t-search-for 20 "please your lordship"))) + + (ert-info ("#chan@barnet is exclusive to barnet") + (with-current-buffer (if bar-id "#chan@rabnet" "#chan@barnet") + (erc-d-t-search-for 1 "<joe>") + (erc-d-t-absent-for 0.1 "<bob>") + (erc-d-t-search-for 20 "much in private"))) + + ;; XXX this is important (reconnects overlapped, so we'd get + ;; chan@127.0.0.1:6667) + (should-not (erc-scenarios-common-buflist "127.0.0.1")) + ;; Reconnection order doesn't matter here because session objects + ;; are persisted, meaning original timestamps preserved. + (should (equal (list (get-buffer (if bar-id "#chan@rabnet" + "#chan@barnet")) + (get-buffer (if foo-id "#chan@oofnet" + "#chan@foonet"))) + (erc-scenarios-common-buflist "#chan")))))) + (erc-scenarios-common--base-network-id-bouncer + (list :autop t :foo-id foo-id :bar-id bar-id :after after) + 'foonet-drop 'barnet-drop + 'stub-again 'stub-again + 'foonet-again 'barnet-again))) + +(defun erc-scenarios-common--upstream-reconnect (test &rest dialogs) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/upstream-reconnect") + (erc-d-t-cleanup-sleep-secs 1) + (erc-server-flood-penalty 0.1) + (dumb-server (apply #'erc-d-run "localhost" t dialogs)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester@vanilla/foonet" + :password "changeme" + :full-name "tester") + (erc-scenarios-common-assert-initial-buf-name nil port) + (erc-d-t-wait-for 3 (eq (erc-network) 'foonet)) + (erc-d-t-wait-for 3 (string= (buffer-name) "foonet")) + (funcall expect 5 "foonet"))) + + (ert-info ("Join #chan@foonet") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 5 "<alice>"))) + + (ert-info ("Connect to barnet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester@vanilla/barnet" + :password "changeme" + :full-name "tester") + (erc-scenarios-common-assert-initial-buf-name nil port) + (erc-d-t-wait-for 10 (eq (erc-network) 'barnet)) + (erc-d-t-wait-for 3 (string= (buffer-name) "barnet")) + (funcall expect 5 "barnet"))) + + (ert-info ("Server buffers are unique, no names based on IPs") + (should-not (erc-scenarios-common-buflist "127.0.0.1"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan@foonet")) + (funcall expect 5 "#chan was created on ") + (ert-info ("Joined again #chan@foonet") + (funcall expect 10 "#chan was created on ")) + (funcall expect 10 "My lord, in heart")) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan@barnet")) + (funcall expect 5 "#chan was created on ") + (ert-info ("Joined again #chan@barnet") + (funcall expect 10 "#chan was created on ")) + (funcall expect 10 "Go to; farewell")) + + (funcall test))) + +;; XXX this is okay, but we also need to check that target buffers are +;; already associated with a new process *before* a JOIN is sent by a +;; server's playback burst. This doesn't do that. +;; +;; This *does* check that superfluous JOINs sent by the autojoin +;; module are harmless when they're not acked (superfluous because the +;; bouncer/server intitates the JOIN). + +(defun erc-scenarios-common--join-network-id (foo-reconnector foo-id bar-id) + "Ensure channels rejoined by erc-join.el DTRT. +Originally from scenario clash-of-chans/autojoin as described in +Bug#48598: 28.0.50; buffer-naming collisions involving bouncers in ERC." + (erc-scenarios-common-with-cleanup + ((chan-buf-foo (format "#chan@%s" (or foo-id "foonet"))) + (chan-buf-bar (format "#chan@%s" (or bar-id "barnet"))) + (erc-scenarios-common-dialog "join/network-id") + (erc-d-t-cleanup-sleep-secs 1) + (erc-server-flood-penalty 0.5) + (dumb-server (erc-d-run "localhost" t 'foonet 'barnet 'foonet-again)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + erc-server-buffer-foo erc-server-process-foo + erc-server-buffer-bar erc-server-process-bar) + + (should (memq 'autojoin erc-modules)) + + (ert-info ("Connect to foonet") + (with-current-buffer + (setq erc-server-buffer-foo (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester" + :id foo-id)) + (setq erc-server-process-foo erc-server-process) + (erc-scenarios-common-assert-initial-buf-name foo-id port) + (erc-d-t-wait-for 5 (eq (erc-network) 'foonet)) + (funcall expect 5 "foonet"))) + + (ert-info ("Join #chan, find sentinel, quit") + (with-current-buffer erc-server-buffer-foo (erc-cmd-JOIN "#chan")) + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 5 "vile thing") + (erc-cmd-QUIT ""))) + + (erc-d-t-wait-for 2 "Foonet connection deceased" + (not (erc-server-process-alive erc-server-buffer-foo))) + + (should (equal erc-autojoin-channels-alist + (if foo-id '((oofnet "#chan")) '((foonet "#chan"))))) + + (ert-info ("Connect to barnet") + (with-current-buffer + (setq erc-server-buffer-bar (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester" + :id bar-id)) + (setq erc-server-process-bar erc-server-process) + (erc-d-t-wait-for 5 (eq erc-network 'barnet)) + (should (string= (buffer-name) (if bar-id "rabnet" "barnet"))))) + + (ert-info ("Server buffers are unique, no stray IP-based names") + (should-not (eq erc-server-buffer-foo erc-server-buffer-bar)) + (should-not (erc-scenarios-common-buflist "127.0.0.1"))) + + (ert-info ("Only one #chan buffer exists") + (should (equal (list (get-buffer "#chan")) + (erc-scenarios-common-buflist "#chan")))) + + (ert-info ("#chan is not auto-joined") + (with-current-buffer "#chan" + (erc-d-t-absent-for 0.1 "<joe>") + (should-not (process-live-p erc-server-process)) + (erc-d-t-ensure-for 0.1 "server buffer remains foonet" + (eq erc-server-process erc-server-process-foo)))) + + (with-current-buffer erc-server-buffer-bar + (erc-cmd-JOIN "#chan") + (erc-d-t-wait-for 3 (get-buffer chan-buf-foo)) + (erc-d-t-wait-for 3 (get-buffer chan-buf-bar)) + (with-current-buffer chan-buf-bar + (erc-d-t-wait-for 3 (eq erc-server-process erc-server-process-bar)) + (funcall expect 5 "marry her instantly"))) + + (ert-info ("Reconnect to foonet") + (with-current-buffer (setq erc-server-buffer-foo + (funcall foo-reconnector)) + (should (member (if foo-id '(oofnet "#chan") '(foonet "#chan")) + erc-autojoin-channels-alist)) + (erc-d-t-wait-for 3 (erc-server-process-alive)) + (setq erc-server-process-foo erc-server-process) + (erc-d-t-wait-for 2 (eq erc-network 'foonet)) + (should (string= (buffer-name) (if foo-id "oofnet" "foonet"))) + (funcall expect 5 "foonet"))) + + (ert-info ("#chan@foonet is clean, no cross-contamination") + (with-current-buffer chan-buf-foo + (erc-d-t-wait-for 3 (eq erc-server-process erc-server-process-foo)) + (funcall expect 3 "<bob>") + (erc-d-t-absent-for 0.1 "<joe>") + (funcall expect 10 "not given me"))) + + (ert-info ("All #chan@barnet output received") + (with-current-buffer chan-buf-bar + (funcall expect 10 "hath an uncle here"))))) + +(provide 'erc-scenarios-common) + +;;; erc-scenarios-common.el ends here diff --git a/test/lisp/erc/resources/join/auth-source/foonet.eld b/test/lisp/erc/resources/join/auth-source/foonet.eld new file mode 100644 index 00000000000..32b9e3fa0b6 --- /dev/null +++ b/test/lisp/erc/resources/join/auth-source/foonet.eld @@ -0,0 +1,33 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK dummy")) +((user 1 "USER user 0 * :dummy") + (0.00 ":irc.foonet.org 001 dummy :Welcome to the foonet IRC Network dummy") + (0.01 ":irc.foonet.org 002 dummy :Your host is irc.foonet.org, running version ergo-v2.8.0") + (0.00 ":irc.foonet.org 003 dummy :This server was created Tue, 24 May 2022 05:28:42 UTC") + (0.00 ":irc.foonet.org 004 dummy irc.foonet.org ergo-v2.8.0 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.00 ":irc.foonet.org 005 dummy AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0.01 ":irc.foonet.org 005 dummy MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 dummy draft/CHATHISTORY=100 :are supported by this server") + (0.00 ":irc.foonet.org 251 dummy :There are 0 users and 4 invisible on 1 server(s)") + (0.00 ":irc.foonet.org 252 dummy 0 :IRC Operators online") + (0.00 ":irc.foonet.org 253 dummy 0 :unregistered connections") + (0.00 ":irc.foonet.org 254 dummy 2 :channels formed") + (0.00 ":irc.foonet.org 255 dummy :I have 4 clients and 0 servers") + (0.00 ":irc.foonet.org 265 dummy 4 4 :Current local users 4, max 4") + (0.00 ":irc.foonet.org 266 dummy 4 4 :Current global users 4, max 4") + (0.00 ":irc.foonet.org 422 dummy :MOTD File is missing")) + +((mode 6 "MODE dummy +i") + (0.00 ":irc.foonet.org 221 dummy +i") + (0.00 ":irc.foonet.org NOTICE dummy :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.") + (0.02 ":irc.foonet.org 221 dummy +i")) + +((join 6.47 "JOIN #spam secret") + (0.03 ":dummy!~u@w9rfqveugz722.irc JOIN #spam")) + +((mode 1 "MODE #spam") + (0.01 ":irc.foonet.org 353 dummy = #spam :~tester dummy") + (0.00 ":irc.foonet.org 366 dummy #spam :End of NAMES list") + (0.01 ":irc.foonet.org 324 dummy #spam +knt secret") + (0.03 ":irc.foonet.org 329 dummy #spam 1653370308")) diff --git a/test/lisp/erc/resources/join/legacy/foonet.eld b/test/lisp/erc/resources/join/legacy/foonet.eld new file mode 100644 index 00000000000..344ba7c1daf --- /dev/null +++ b/test/lisp/erc/resources/join/legacy/foonet.eld @@ -0,0 +1,38 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect.")) + +((join 6 "JOIN #chan") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((mode 5 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: And will you, being a man of your breeding, be married under a bush, like a beggar ? Get you to church, and have a good priest that can tell you what marriage is: this fellow will but join you together as they join wainscot; then one of you will prove a shrunk panel, and like green timber, warp, warp.") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Live, and be prosperous; and farewell, good fellow.")) diff --git a/test/lisp/erc/resources/join/network-id/barnet.eld b/test/lisp/erc/resources/join/network-id/barnet.eld new file mode 100644 index 00000000000..e33dd6be29e --- /dev/null +++ b/test/lisp/erc/resources/join/network-id/barnet.eld @@ -0,0 +1,43 @@ +;; -*- mode: lisp-data; -*- +((pass 2 "PASS :barnet:changeme")) +((nick 2 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") + (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.barnet.org 003 tester :This server was created Mon, 10 May 2021 00:58:22 UTC") + (0 ":irc.barnet.org 004 tester irc.barnet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.barnet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.barnet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=barnet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.barnet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.barnet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.barnet.org 252 tester 0 :IRC Operators online") + (0 ":irc.barnet.org 254 tester 1 :channels formed") + (0 ":irc.barnet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.barnet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.barnet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.barnet.org 422 tester :MOTD File is missing")) + +((mode-user 12 "MODE tester +i")) +;; No mode answer + +((join 2 "JOIN #chan") + (0 ":tester!~u@6yximxrnkg65a.irc JOIN #chan") + (0 ":irc.barnet.org 353 tester = #chan :@joe mike tester") + (0 ":irc.barnet.org 366 tester #chan :End of NAMES list") + (0.1 ":joe!~u@6yximxrnkg65a.irc PRIVMSG #chan :tester, welcome!") + (0 ":mike!~u@6yximxrnkg65a.irc PRIVMSG #chan :tester, welcome!")) + +((mode 1 "MODE #chan") + (0 ":irc.barnet.org 324 tester #chan +nt") + (0 ":irc.barnet.org 329 tester #chan 1620608304") + ;; Wait for foonet's buffer playback + (0.1 ":mike!~u@6yximxrnkg65a.irc PRIVMSG #chan :joe: Go take her hence, and marry her instantly.") + (0.1 ":joe!~u@6yximxrnkg65a.irc PRIVMSG #chan :mike: Of all the four, or the three, or the two, or one of the four.") + (0.1 ":mike!~u@6yximxrnkg65a.irc PRIVMSG #chan :joe: And gives the crutch the cradle's infancy.") + (0.1 ":joe!~u@6yximxrnkg65a.irc PRIVMSG #chan :mike: Such is the simplicity of man to hearken after the flesh.") + (0.05 ":mike!~u@6yximxrnkg65a.irc PRIVMSG #chan :joe: The leaf to read them. Let us toward the king.") + (0.05 ":joe!~u@6yximxrnkg65a.irc PRIVMSG #chan :mike: Many can brook the weather that love not the wind.") + (0.05 ":mike!~u@6yximxrnkg65a.irc PRIVMSG #chan :joe: And now, dear maid, be you as free to us.") + (0.00 ":joe!~u@6yximxrnkg65a.irc PRIVMSG #chan :mike: He hath an uncle here in Messina will be very much glad of it.")) + +((linger 3.5 LINGER)) diff --git a/test/lisp/erc/resources/join/network-id/foonet-again.eld b/test/lisp/erc/resources/join/network-id/foonet-again.eld new file mode 100644 index 00000000000..b230eff27c7 --- /dev/null +++ b/test/lisp/erc/resources/join/network-id/foonet-again.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((pass-redux 10 "PASS :foonet:changeme")) +((nick-redux 1 "NICK tester")) + +((user-redux 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Mon, 10 May 2021 00:58:22 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i") + ;; No mode answer ^ + + ;; History + (0 ":tester!~u@q6ddatxcq6txy.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :@alice bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of /NAMES list.") + (0 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0 ":bob!~u@q6ddatxcq6txy.irc PRIVMSG #chan :[02:43:23] alice: And soar with them above a common bound.") + (0 ":alice!~u@q6ddatxcq6txy.irc PRIVMSG #chan :[02:43:27] bob: And be aveng'd on cursed Tamora.") + (0 ":bob!~u@q6ddatxcq6txy.irc PRIVMSG #chan :[02:43:29] alice: He did love her, sir, as a gentleman loves a woman.") + (0 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")) + +;; As a server, we ignore useless join sent by autojoin module +((~join 10 "JOIN #chan")) + +((mode-redux 10 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620608304") + (0.1 ":alice!~u@q6ddatxcq6txy.irc PRIVMSG #chan :bob: Ay, madam, with the swiftest wing of speed.") + (0.1 ":bob!~u@q6ddatxcq6txy.irc PRIVMSG #chan :alice: Five times in that ere once in our five wits.") + (0.1 ":alice!~u@q6ddatxcq6txy.irc PRIVMSG #chan :bob: And bid him come to take his last farewell.") + (0.1 ":bob!~u@q6ddatxcq6txy.irc PRIVMSG #chan :alice: But we are spirits of another sort.") + (0.1 ":alice!~u@q6ddatxcq6txy.irc PRIVMSG #chan :bob: It was not given me, nor I did not buy it.")) + +((linger 6 LINGER)) diff --git a/test/lisp/erc/resources/join/network-id/foonet.eld b/test/lisp/erc/resources/join/network-id/foonet.eld new file mode 100644 index 00000000000..7d63f5f0c6c --- /dev/null +++ b/test/lisp/erc/resources/join/network-id/foonet.eld @@ -0,0 +1,39 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :foonet:changeme")) +((nick 1 "NICK tester")) + +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Mon, 10 May 2021 00:58:22 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=foonet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 10.2 "MODE tester +i")) +;; No mode answer ^ + +((join 3 "JOIN #chan") + (0 ":tester!~u@q6ddatxcq6txy.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :@alice bob tester") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.1 ":bob!~u@q6ddatxcq6txy.irc PRIVMSG #chan :tester, welcome!") + (0 ":alice!~u@q6ddatxcq6txy.irc PRIVMSG #chan :tester, welcome!")) + +((mode 3 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620608304") + (0.1 ":bob!~u@q6ddatxcq6txy.irc PRIVMSG #chan :alice: Pray you, sir, deliver me this paper.") + (0.1 ":alice!~u@q6ddatxcq6txy.irc PRIVMSG #chan :bob: Wake when some vile thing is near.")) + +((quit 3 "QUIT :\2ERC\2")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/join/reconnect/foonet-again.eld b/test/lisp/erc/resources/join/reconnect/foonet-again.eld new file mode 100644 index 00000000000..f1fcc439cc3 --- /dev/null +++ b/test/lisp/erc/resources/join/reconnect/foonet-again.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is still in debug mode.")) + +((~join-chan 12 "JOIN #chan") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list")) + +((~join-spam 12 "JOIN #spam") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #spam") + (0 ":irc.foonet.org 353 tester = #spam :alice tester @bob") + (0 ":irc.foonet.org 366 tester #spam :End of NAMES list")) + +((~mode-chan 4 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.")) + +((mode-spam 4 "MODE #spam") + (0 ":irc.foonet.org 324 tester #spam +nt") + (0 ":irc.foonet.org 329 tester #spam 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #spam :alice: Signior Iachimo will not from it. Pray, let us follow 'em.") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #spam :bob: Our queen and all her elves come here anon.")) diff --git a/test/lisp/erc/resources/join/reconnect/foonet.eld b/test/lisp/erc/resources/join/reconnect/foonet.eld new file mode 100644 index 00000000000..efb269f5ae5 --- /dev/null +++ b/test/lisp/erc/resources/join/reconnect/foonet.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((pass 1 "PASS :changeme")) +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version oragono-2.6.0-7481bf0385b95b16") + (0 ":irc.foonet.org 003 tester :This server was created Tue, 04 May 2021 05:06:18 UTC") + (0 ":irc.foonet.org 004 tester irc.foonet.org oragono-2.6.0-7481bf0385b95b16 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0 ":irc.foonet.org 005 tester AWAYLEN=390 BOT=B CASEMAPPING=ascii CHANLIMIT=#:100 CHANMODES=Ibe,k,fl,CEMRUimnstu CHANNELLEN=64 CHANTYPES=# ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX KICKLEN=390 :are supported by this server") + (0 ":irc.foonet.org 005 tester MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=FooNet NICKLEN=32 PREFIX=(qaohv)~&@%+ STATUSMSG=~&@%+ TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:4,TAGMSG:4,NOTICE:4,MONITOR:100 TOPICLEN=390 UTF8MAPPING=rfc8265 UTF8ONLY WHOX :are supported by this server") + (0 ":irc.foonet.org 005 tester draft/CHATHISTORY=100 :are supported by this server") + (0 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0 ":irc.foonet.org 254 tester 1 :channels formed") + (0 ":irc.foonet.org 255 tester :I have 3 clients and 0 servers") + (0 ":irc.foonet.org 265 tester 3 3 :Current local users 3, max 3") + (0 ":irc.foonet.org 266 tester 3 3 :Current global users 3, max 3") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 3.2 "MODE tester +i") + (0 ":irc.foonet.org 221 tester +i") + (0 ":irc.foonet.org NOTICE tester :This server is in debug mode.") + + (0 ":tester!~u@9g6b728983yd2.irc JOIN #chan") + (0 ":irc.foonet.org 353 tester = #chan :alice tester @bob") + (0 ":irc.foonet.org 366 tester #chan :End of NAMES list") + + (0 ":tester!~u@9g6b728983yd2.irc JOIN #spam") + (0 ":irc.foonet.org 353 tester = #spam :alice tester @bob") + (0 ":irc.foonet.org 366 tester #spam :End of NAMES list")) + +((mode-chan 4 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!")) + +((mode-spam 4 "MODE #spam") + (0 ":irc.foonet.org 324 tester #spam +nt") + (0 ":irc.foonet.org 329 tester #spam 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #spam :tester, welcome!") + (0.1 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #spam :tester, welcome!")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/networks/announced-missing/foonet.eld b/test/lisp/erc/resources/networks/announced-missing/foonet.eld new file mode 100644 index 00000000000..79b0fb462a8 --- /dev/null +++ b/test/lisp/erc/resources/networks/announced-missing/foonet.eld @@ -0,0 +1,8 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0 ":irc.foonet.org 001 tester :Welcome to the FooNet Internet Relay Chat Network tester") + (0 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode-user 1.2 "MODE tester +i") + (0 ":tester MODE tester :+Zi")) diff --git a/test/lisp/erc/resources/services/auth-source/libera.eld b/test/lisp/erc/resources/services/auth-source/libera.eld new file mode 100644 index 00000000000..c8dbc9d425a --- /dev/null +++ b/test/lisp/erc/resources/services/auth-source/libera.eld @@ -0,0 +1,49 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.26 ":zirconium.libera.chat NOTICE * :*** Checking Ident") + (0.01 ":zirconium.libera.chat NOTICE * :*** Looking up your hostname...") + (0.01 ":zirconium.libera.chat NOTICE * :*** No Ident response") + (0.02 ":zirconium.libera.chat NOTICE * :*** Found your hostname: static-198-54-131-100.cust.tzulo.com") + (0.02 ":zirconium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester") + (0.01 ":zirconium.libera.chat 002 tester :Your host is zirconium.libera.chat[46.16.175.175/6697], running version solanum-1.0-dev") + (0.03 ":zirconium.libera.chat 003 tester :This server was created Wed Jun 9 2021 at 01:38:28 UTC") + (0.02 ":zirconium.libera.chat 004 tester zirconium.libera.chat solanum-1.0-dev DGQRSZaghilopsuwz CFILMPQSbcefgijklmnopqrstuvz bkloveqjfI") + (0.00 ":zirconium.libera.chat 005 tester ETRACE WHOX FNC MONITOR=100 SAFELIST ELIST=CTU CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz :are supported by this server") + (0.03 ":zirconium.libera.chat 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server") + (0.02 ":zirconium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz CLIENTVER=3.0 :are supported by this server") + (0.02 ":zirconium.libera.chat 251 tester :There are 68 users and 37640 invisible on 25 servers") + (0.00 ":zirconium.libera.chat 252 tester 36 :IRC Operators online") + (0.01 ":zirconium.libera.chat 253 tester 5 :unknown connection(s)") + (0.00 ":zirconium.libera.chat 254 tester 19341 :channels formed") + (0.01 ":zirconium.libera.chat 255 tester :I have 3321 clients and 1 servers") + (0.01 ":zirconium.libera.chat 265 tester 3321 4289 :Current local users 3321, max 4289") + (0.00 ":zirconium.libera.chat 266 tester 37708 38929 :Current global users 37708, max 38929") + (0.01 ":zirconium.libera.chat 250 tester :Highest connection count: 4290 (4289 clients) (38580 connections received)") + (0.21 ":zirconium.libera.chat 375 tester :- zirconium.libera.chat Message of the Day - ") + (0.00 ":zirconium.libera.chat 372 tester :- This server provided by Seeweb <https://www.seeweb.it/>") + (0.01 ":zirconium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for") + (0.01 ":zirconium.libera.chat 372 tester :- free & open-source software and peer directed projects.") + (0.00 ":zirconium.libera.chat 372 tester :- ") + (0.00 ":zirconium.libera.chat 372 tester :- Use of Libera Chat is governed by our network policies.") + (0.00 ":zirconium.libera.chat 372 tester :- ") + (0.01 ":zirconium.libera.chat 372 tester :- Please visit us in #libera for questions and support.") + (0.01 ":zirconium.libera.chat 372 tester :- ") + (0.01 ":zirconium.libera.chat 372 tester :- Website and documentation: https://libera.chat") + (0.01 ":zirconium.libera.chat 372 tester :- Webchat: https://web.libera.chat") + (0.01 ":zirconium.libera.chat 372 tester :- Network policies: https://libera.chat/policies") + (0.01 ":zirconium.libera.chat 372 tester :- Email: support@libera.chat") + (0.00 ":zirconium.libera.chat 376 tester :End of /MOTD command.")) + +((mode-user 1.2 "MODE tester +i") + (0.02 ":tester MODE tester :+Zi") + (0.02 ":NickServ!NickServ@services.libera.chat NOTICE tester :This nickname is registered. Please choose a different nickname, or identify via \2/msg NickServ IDENTIFY tester <password>\2")) + +((privmsg 2 "PRIVMSG NickServ :IDENTIFY changeme") + (0.96 ":NickServ!NickServ@services.libera.chat NOTICE tester :You are now identified for \2tester\2.") + (0.25 ":NickServ!NickServ@services.libera.chat NOTICE tester :Last login from: \2~tester@school.edu/tester\2 on Jun 18 01:15:56 2021 +0000.")) + +((quit 5 "QUIT :\2ERC\2") + (0.19 ":tester!~user@static-198-54-131-100.cust.tzulo.com QUIT :Client Quit")) + +((linger 1 LINGER)) diff --git a/test/lisp/erc/resources/services/password/libera.eld b/test/lisp/erc/resources/services/password/libera.eld new file mode 100644 index 00000000000..c8dbc9d425a --- /dev/null +++ b/test/lisp/erc/resources/services/password/libera.eld @@ -0,0 +1,49 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.26 ":zirconium.libera.chat NOTICE * :*** Checking Ident") + (0.01 ":zirconium.libera.chat NOTICE * :*** Looking up your hostname...") + (0.01 ":zirconium.libera.chat NOTICE * :*** No Ident response") + (0.02 ":zirconium.libera.chat NOTICE * :*** Found your hostname: static-198-54-131-100.cust.tzulo.com") + (0.02 ":zirconium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester") + (0.01 ":zirconium.libera.chat 002 tester :Your host is zirconium.libera.chat[46.16.175.175/6697], running version solanum-1.0-dev") + (0.03 ":zirconium.libera.chat 003 tester :This server was created Wed Jun 9 2021 at 01:38:28 UTC") + (0.02 ":zirconium.libera.chat 004 tester zirconium.libera.chat solanum-1.0-dev DGQRSZaghilopsuwz CFILMPQSbcefgijklmnopqrstuvz bkloveqjfI") + (0.00 ":zirconium.libera.chat 005 tester ETRACE WHOX FNC MONITOR=100 SAFELIST ELIST=CTU CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstuz :are supported by this server") + (0.03 ":zirconium.libera.chat 005 tester CHANLIMIT=#:250 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=Libera.Chat STATUSMSG=@+ CASEMAPPING=rfc1459 NICKLEN=16 MAXNICKLEN=16 CHANNELLEN=50 TOPICLEN=390 DEAF=D :are supported by this server") + (0.02 ":zirconium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz CLIENTVER=3.0 :are supported by this server") + (0.02 ":zirconium.libera.chat 251 tester :There are 68 users and 37640 invisible on 25 servers") + (0.00 ":zirconium.libera.chat 252 tester 36 :IRC Operators online") + (0.01 ":zirconium.libera.chat 253 tester 5 :unknown connection(s)") + (0.00 ":zirconium.libera.chat 254 tester 19341 :channels formed") + (0.01 ":zirconium.libera.chat 255 tester :I have 3321 clients and 1 servers") + (0.01 ":zirconium.libera.chat 265 tester 3321 4289 :Current local users 3321, max 4289") + (0.00 ":zirconium.libera.chat 266 tester 37708 38929 :Current global users 37708, max 38929") + (0.01 ":zirconium.libera.chat 250 tester :Highest connection count: 4290 (4289 clients) (38580 connections received)") + (0.21 ":zirconium.libera.chat 375 tester :- zirconium.libera.chat Message of the Day - ") + (0.00 ":zirconium.libera.chat 372 tester :- This server provided by Seeweb <https://www.seeweb.it/>") + (0.01 ":zirconium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for") + (0.01 ":zirconium.libera.chat 372 tester :- free & open-source software and peer directed projects.") + (0.00 ":zirconium.libera.chat 372 tester :- ") + (0.00 ":zirconium.libera.chat 372 tester :- Use of Libera Chat is governed by our network policies.") + (0.00 ":zirconium.libera.chat 372 tester :- ") + (0.01 ":zirconium.libera.chat 372 tester :- Please visit us in #libera for questions and support.") + (0.01 ":zirconium.libera.chat 372 tester :- ") + (0.01 ":zirconium.libera.chat 372 tester :- Website and documentation: https://libera.chat") + (0.01 ":zirconium.libera.chat 372 tester :- Webchat: https://web.libera.chat") + (0.01 ":zirconium.libera.chat 372 tester :- Network policies: https://libera.chat/policies") + (0.01 ":zirconium.libera.chat 372 tester :- Email: support@libera.chat") + (0.00 ":zirconium.libera.chat 376 tester :End of /MOTD command.")) + +((mode-user 1.2 "MODE tester +i") + (0.02 ":tester MODE tester :+Zi") + (0.02 ":NickServ!NickServ@services.libera.chat NOTICE tester :This nickname is registered. Please choose a different nickname, or identify via \2/msg NickServ IDENTIFY tester <password>\2")) + +((privmsg 2 "PRIVMSG NickServ :IDENTIFY changeme") + (0.96 ":NickServ!NickServ@services.libera.chat NOTICE tester :You are now identified for \2tester\2.") + (0.25 ":NickServ!NickServ@services.libera.chat NOTICE tester :Last login from: \2~tester@school.edu/tester\2 on Jun 18 01:15:56 2021 +0000.")) + +((quit 5 "QUIT :\2ERC\2") + (0.19 ":tester!~user@static-198-54-131-100.cust.tzulo.com QUIT :Client Quit")) + +((linger 1 LINGER)) |