diff options
Diffstat (limited to 'test/lisp/erc')
124 files changed, 10162 insertions, 796 deletions
diff --git a/test/lisp/erc/erc-button-tests.el b/test/lisp/erc/erc-button-tests.el new file mode 100644 index 00000000000..be11b76bd2e --- /dev/null +++ b/test/lisp/erc/erc-button-tests.el @@ -0,0 +1,308 @@ +;;; erc-button-tests.el --- Tests for erc-button -*- lexical-binding:t -*- + +;; Copyright (C) 2023 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) ; cl-lib +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-tests-common))) + +(require 'erc-button) + +(ert-deftest erc-button-alist--url () + (erc-tests-common-init-server-proc "sleep" "1") + (with-current-buffer (erc--open-target "#chan") + (let ((verify + (lambda (p url) + (should (equal (get-text-property p 'erc-data) (list url))) + (should (equal (get-text-property p 'mouse-face) 'highlight)) + (should (eq (get-text-property p 'font-lock-face) 'erc-button)) + (should (eq (get-text-property p 'erc-callback) + 'browse-url-button-open-url))))) + (goto-char (point-min)) + + ;; Most common (unbracketed) + (erc-display-message nil nil (current-buffer) + "Foo https://example.com bar.") + (search-forward "https") + (funcall verify (point) "https://example.com") + + ;; The <URL: form> still works despite being removed in ERC 5.6. + (erc-display-message nil nil (current-buffer) + "Foo <URL: https://gnu.org> bar.") + (search-forward "https") + (funcall verify (point) "https://gnu.org") + + ;; Bracketed + (erc-display-message nil nil (current-buffer) "Foo <ftp://gnu.org> bar.") + (search-forward "ftp") + (funcall verify (point) "ftp://gnu.org")) + + (when noninteractive + (kill-buffer)))) + +(defvar erc-button-tests--form nil) +(defvar erc-button-tests--some-var nil) + +(defun erc-button-tests--form (&rest rest) + (push rest erc-button-tests--form) + (apply #'erc-button-add-button rest)) + +(defun erc-button-tests--erc-button-alist--function-as-form (func) + (erc-tests-common-init-server-proc "sleep" "1") + + (with-current-buffer (erc--open-target "#chan") + (let* ((erc-button-tests--form nil) + (entry (list (rx "+1") 0 func #'ignore 0)) + (erc-button-alist (cons entry erc-button-alist))) + + (erc-display-message nil 'notice (current-buffer) "Foo bar baz") + (erc-display-message nil nil (current-buffer) "+1") + (erc-display-message nil 'notice (current-buffer) "Spam") + (should (equal (pop erc-button-tests--form) + '(53 55 ignore nil ("+1") "\\+1"))) + (should-not erc-button-tests--form) + (goto-char (point-min)) + (search-forward "+") + (should (equal (get-text-property (point) 'erc-data) '("+1"))) + (should (equal (get-text-property (point) 'mouse-face) 'highlight)) + (should (eq (get-text-property (point) 'font-lock-face) 'erc-button)) + (should (eq (get-text-property (point) 'erc-callback) 'ignore))) + + (when noninteractive + (kill-buffer)))) + +(ert-deftest erc-button-alist--function-as-form () + (erc-button-tests--erc-button-alist--function-as-form + #'erc-button-tests--form) + + (erc-button-tests--erc-button-alist--function-as-form + (symbol-function #'erc-button-tests--form)) + + (erc-button-tests--erc-button-alist--function-as-form + (lambda (&rest r) (push r erc-button-tests--form) + (apply #'erc-button-add-button r)))) + +(defun erc-button-tests--erc-button-alist--nil-form (form) + (erc-tests-common-init-server-proc "sleep" "1") + + (with-current-buffer (erc--open-target "#chan") + (let* ((erc-button-tests--form nil) + (entry (list (rx "+1") 0 form #'ignore 0)) + (erc-button-alist (cons entry erc-button-alist))) + + (erc-display-message nil 'notice (current-buffer) "Foo bar baz") + (erc-display-message nil nil (current-buffer) "+1") + (erc-display-message nil 'notice (current-buffer) "Spam") + (should-not erc-button-tests--form) + (goto-char (point-min)) + (search-forward "+") + (should-not (get-text-property (point) 'erc-data)) + (should-not (get-text-property (point) 'mouse-face)) + (should-not (get-text-property (point) 'font-lock-face)) + (should-not (get-text-property (point) 'erc-callback))) + + (when noninteractive + (kill-buffer)))) + +(ert-deftest erc-button-alist--nil-form () + (erc-button-tests--erc-button-alist--nil-form nil) + (erc-button-tests--erc-button-alist--nil-form 'erc-button-tests--some-var)) + +(defun erc-button-tests--insert-privmsg (speaker &rest msg-parts) + (declare (indent 1)) + (let ((msg (erc-format-privmessage speaker + (apply #'concat msg-parts) nil t))) + (erc-display-message nil nil (current-buffer) msg))) + +(defun erc-button-tests--populate (test) + (let ((inhibit-message noninteractive) + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (with-current-buffer + (cl-letf + (((symbol-function 'erc-server-connect) + (lambda (&rest _) + (setq erc-server-process + (start-process "sleep" (current-buffer) "sleep" "1")) + (set-process-query-on-exit-flag erc-server-process nil)))) + + (erc-open "localhost" 6667 "tester" "Tester" 'connect + nil nil nil nil nil "tester" 'foonet)) + + (with-current-buffer (erc--open-target "#chan") + (erc-update-channel-member + "#chan" "alice" "alice" t nil nil nil nil nil "fake" "~u" nil nil t) + + (erc-update-channel-member + "#chan" "bob" "bob" t nil nil nil nil nil "fake" "~u" nil nil t) + + (erc-display-message + nil 'notice (current-buffer) + (concat "This server is in debug mode and is logging all user I/O. " + "Blah alice (1) bob (2) blah.")) + + (funcall test)) + + (when noninteractive + (kill-buffer "#chan") + (kill-buffer))))) + +(ert-deftest erc-button-next () + (erc-button-tests--populate + (lambda () + (erc-button-tests--insert-privmsg "alice" + "(3) bob (4) come, you are a tedious fool: to the purpose.") + + (erc-button-tests--insert-privmsg "bob" + "(5) alice (6) Come me to what was done to her.") + + (should (= erc-input-marker (point))) + + ;; Break out of input area + (erc-button-previous 1) + (should (looking-at (rx "alice (6)"))) + + ;; No next button + (should-error (erc-button-next 1) :type 'user-error) + (should (looking-at (rx "alice (6)"))) + + ;; Next with negative arg is equivalent to previous + (erc-button-next -1) + (should (looking-at (rx "bob> (5)"))) + + ;; One past end of button + (forward-char 3) + (should (looking-at (rx "> (5)"))) + (should-not (get-text-property (point) 'erc-callback)) + (erc-button-previous 1) + (should (looking-at (rx "bob> (5)"))) + + ;; At end of button + (forward-char 2) + (should (looking-at (rx "b> (5)"))) + (erc-button-previous 1) + (should (looking-at (rx "bob (4)"))) + + ;; Skip multiple buttons back + (erc-button-previous 2) + (should (looking-at (rx "bob (2)"))) + + ;; Skip multiple buttons forward + (erc-button-next 2) + (should (looking-at (rx "bob (4)"))) + + ;; No error as long as some progress made + (erc-button-previous 100) + (should (looking-at (rx "alice (1)"))) + + ;; Error when no progress made + (should-error (erc-button-previous 1) :type 'user-error) + (should (looking-at (rx "alice (1)")))))) + +;; See also `erc-scenarios-networks-announced-missing' in +;; erc-scenarios-misc.el for a more realistic example. +(ert-deftest erc-button--display-error-notice-with-keys () + (with-current-buffer (get-buffer-create "*fake*") + (let ((mode erc-button-mode) + (inhibit-message noninteractive) + erc-modules + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (erc-tests-common-prep-for-insertion) + (erc-tests-common-init-server-proc "sleep" "1") + + (erc-button-mode +1) + (should (equal (erc-button--display-error-notice-with-keys + "If \\[erc-bol] fails, " + "see \\[erc-bug] or `erc-mode-map'.") + "*** If C-a fails, see M-x erc-bug or `erc-mode-map'.")) + (goto-char (point-min)) + + (ert-info ("Keymap substitution succeeds") + (erc-button-next 1) + (should (looking-at "C-a")) + (should (eq (get-text-property (point) 'mouse-face) 'highlight)) + (erc-button-press-button) + (with-current-buffer "*Help*" + (goto-char (point-min)) + (should (search-forward "erc-bol" nil t))) + (erc-button-next 1) + ;; End of interval correct + (erc-button-previous 1) + (should (looking-at "C-a fails"))) + + (ert-info ("Extended command mapping succeeds") + (erc-button-next 1) + (should (looking-at "M-x erc-bug")) + (erc-button-press-button) + (should (eq (get-text-property (point) 'mouse-face) 'highlight)) + (with-current-buffer "*Help*" + (goto-char (point-min)) + (should (search-forward "erc-bug" nil t)))) + + (ert-info ("Symbol-description face preserved") ; mutated by d-e-n-w-k + (erc-button-next 1) + (should (equal (get-text-property (point) 'font-lock-face) + '(erc-button erc-error-face erc-notice-face))) + (should (eq (get-text-property (point) 'mouse-face) 'highlight)) + (should (eq erc-button-face 'erc-button))) ; extent evaporates + + (ert-info ("Format when trailing args include non-strings") + (should (equal (erc-button--display-error-notice-with-keys + "abc" " %d def" " 45%s" 123 '\6) + "*** abc 123 def 456"))) + + (ert-info ("Respects buffer as first argument when given") + (should (equal (erc-button--display-error-notice-with-keys + (make-erc-response) "abc") ; compat + "*** abc")) + (should (equal (erc-button--display-error-notice-with-keys + (current-buffer) "abc") + "*** abc"))) + + (ert-info ("Accounts for nil members when concatenating") + (should (equal (erc-button--display-error-notice-with-keys + "a" nil) + "*** a")) + (should (equal (erc-button--display-error-notice-with-keys + "a" nil " b") + "*** a b")) + (should (equal (erc-button--display-error-notice-with-keys + "a: %d" nil 1) + "*** a: 1")) + (should (equal (erc-button--display-error-notice-with-keys + "a: %d %s" 1 nil) + "*** a: 1 nil")) + (should (equal (erc-button--display-error-notice-with-keys + "a: " "%d %s" 1 nil) + "*** a: 1 nil")) + (should (equal (erc-button--display-error-notice-with-keys + "a: " nil "%d %s" 1 nil) + "*** a: 1 nil"))) + + (when noninteractive + (unless mode + (erc-button-mode -1)) + (kill-buffer "*Help*") + (kill-buffer))))) + +;;; erc-button-tests.el ends here diff --git a/test/lisp/erc/erc-dcc-tests.el b/test/lisp/erc/erc-dcc-tests.el index ce28c5e8420..a2fb0392727 100644 --- a/test/lisp/erc/erc-dcc-tests.el +++ b/test/lisp/erc/erc-dcc-tests.el @@ -57,9 +57,8 @@ (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") + (erc--initialize-markers (point) nil) (set-process-query-on-exit-flag erc-server-process nil) (should-not erc-dcc-list) (erc-ctcp-query-DCC erc-server-process @@ -100,17 +99,19 @@ (ert-deftest erc-dcc-handle-ctcp-send--turbo () (erc-dcc-tests--dcc-handle-ctcp-send t)) -(ert-deftest erc-dcc-do-GET-command () +(defun erc-dcc-tests--erc-dcc-do-GET-command (file &optional sep nuh) + (unless nuh (setq nuh "tester!~tester@fake.irc")) (with-temp-buffer (let* ((proc (start-process "fake" (current-buffer) "sleep" "10")) - (elt (list :nick "tester!~tester@fake.irc" + (elt (list :nick nuh :type 'GET :peer nil :parent proc :ip "127.0.0.1" :port "9899" - :file "foo.bin" + :file file :size 1405135128)) + (nic (erc-extract-nick nuh)) (erc-dcc-list (list elt)) ;; erc-accidental-paste-threshold-seconds @@ -119,53 +120,63 @@ calls) (erc-mode) (setq erc-server-process proc - erc-input-marker (make-marker) - erc-insert-marker (make-marker) erc-server-current-nick "dummy") + (erc--initialize-markers (point) nil) (set-process-query-on-exit-flag proc nil) (cl-letf (((symbol-function 'read-file-name) - (lambda (&rest _) "foo.bin")) + (lambda (&rest _) file)) ((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") + (insert "/dcc GET " nic " " (or sep "") (prin1-to-string file)) (erc-send-current-line) (should-not (plist-member (car erc-dcc-list) :turbo)) - (should (equal (pop calls) (list elt "foo.bin" proc)))) + (should (equal (pop calls) (list elt file 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") + (insert "/dcc GET -t " nic " " (or sep "") (prin1-to-string file)) (erc-send-current-line) (should (eq t (plist-get (car erc-dcc-list) :turbo))) - (should (equal (pop calls) (list elt "foo.bin" proc)))) + (should (equal (pop calls) (list elt file 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") + (insert "/dcc GET " nic " -t " (or sep "") (prin1-to-string file)) (erc-send-current-line) (should (eq t (plist-get (car erc-dcc-list) :turbo))) - (should (equal (pop calls) (list elt "foo.bin" proc)))) + (should (equal (pop calls) (list elt file 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") + (insert "/dcc GET " nic " " (prin1-to-string file) " -t" (or sep "")) (erc-send-current-line) - (should (eq t (plist-get (car erc-dcc-list) :turbo))) - (should (equal (pop calls) (list elt "foo.bin" proc)))))))) + (should (eq (if sep nil t) (plist-get (car erc-dcc-list) :turbo))) + (should (equal (pop calls) (if sep nil (list elt file proc))))))))) + +(ert-deftest erc-dcc-do-GET-command () + (erc-dcc-tests--erc-dcc-do-GET-command "foo.bin") + (erc-dcc-tests--erc-dcc-do-GET-command "foo - file.bin") + (erc-dcc-tests--erc-dcc-do-GET-command "foo -t file.bin") + (erc-dcc-tests--erc-dcc-do-GET-command "-t" "-- ") -(defun erc-dcc-tests--pcomplete-common (test-fn) + ;; Regression involving pipe character in nickname. + (let ((nuh "test|r!~test|r@fake.irc")) + (erc-dcc-tests--erc-dcc-do-GET-command "foo.bin" nil nuh) + (erc-dcc-tests--erc-dcc-do-GET-command "foo - file.bin" nil nuh) + (erc-dcc-tests--erc-dcc-do-GET-command "foo -t file.bin" nil nuh) + (erc-dcc-tests--erc-dcc-do-GET-command "-t" "-- " nuh))) + +(defun erc-dcc-tests--pcomplete-common (test-fn &optional file) (with-current-buffer (get-buffer-create "*erc-dcc-do-GET-command*") (let* ((inhibit-message noninteractive) (proc (start-process "fake" (current-buffer) "sleep" "10")) @@ -175,7 +186,7 @@ :parent proc :ip "127.0.0.1" :port "9899" - :file "foo.bin" + :file (or file "foo.bin") :size 1405135128)) ;; erc-accidental-paste-threshold-seconds @@ -211,6 +222,20 @@ (beginning-of-line) (should (search-forward "/dcc get tester foo.bin" nil t)))))) +(ert-deftest pcomplete/erc-mode/DCC--get-quoted () + (erc-dcc-tests--pcomplete-common + (lambda () + (insert "/dcc get ") + (call-interactively #'completion-at-point) + (save-excursion + (beginning-of-line) + (should (search-forward "/dcc get tester" nil t))) + (call-interactively #'completion-at-point) + (save-excursion + (beginning-of-line) + (should (search-forward "/dcc get tester \"foo bar.bin\"" nil t)))) + "foo bar.bin")) + (ert-deftest pcomplete/erc-mode/DCC--get-1flag () (erc-dcc-tests--pcomplete-common (lambda () @@ -282,4 +307,23 @@ (beginning-of-line) (should (search-forward "/dcc get -t -s tester foo.bin" nil t)))))) +(ert-deftest pcomplete/erc-mode/DCC--get-sep () + (erc-dcc-tests--pcomplete-common + (lambda () + (insert "/dcc get ") + (call-interactively #'completion-at-point) + (save-excursion + (beginning-of-line) + (should (search-forward "/dcc get tester" nil t))) + (insert "-") + (call-interactively #'completion-at-point) + (save-excursion + (beginning-of-line) + (should (search-forward "/dcc get tester -- " nil t))) + (call-interactively #'completion-at-point) + (save-excursion + (beginning-of-line) + (should (search-forward "/dcc get tester -- -t" nil t)))) + "-t")) + ;;; erc-dcc-tests.el ends here diff --git a/test/lisp/erc/erc-fill-tests.el b/test/lisp/erc/erc-fill-tests.el new file mode 100644 index 00000000000..df83466cbc3 --- /dev/null +++ b/test/lisp/erc/erc-fill-tests.el @@ -0,0 +1,452 @@ +;;; erc-fill-tests.el --- Tests for erc-fill -*- lexical-binding:t -*- + +;; Copyright (C) 2023 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: + +;; FIXME these tests are brittle and error prone. Replace with +;; scenarios. + +;;; Code: +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-tests-common))) + +(require 'erc-fill) + +(defvar erc-fill-tests--buffers nil) +(defvar erc-fill-tests--current-time-value nil) + +(cl-defmethod erc-stamp--current-time + (&context (erc-fill-tests--current-time-value integer)) + erc-fill-tests--current-time-value) + +(defun erc-fill-tests--insert-privmsg (speaker &rest msg-parts) + (declare (indent 1)) + (let* ((erc--msg-prop-overrides `((erc--msg . msg))) + (msg (erc-format-privmessage speaker + (apply #'concat msg-parts) nil t)) + (parsed (make-erc-response :unparsed (format ":%s PRIVMSG #chan :%s" + speaker msg) + :sender speaker + :command "PRIVMSG" + :command-args (list "#chan" msg) + :contents msg))) + (erc-display-message parsed nil (current-buffer) msg))) + +(defun erc-fill-tests--wrap-populate (test) + (let ((original-window-buffer (window-buffer (selected-window))) + (erc-stamp--tz t) + (erc-fill-function 'erc-fill-wrap) + (pre-command-hook pre-command-hook) + (inhibit-message noninteractive) + (erc-fill-tests--current-time-value 0) + erc-insert-post-hook + extended-command-history + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (cl-letf (((symbol-function 'erc-server-connect) + (lambda (&rest _) + (erc-tests-common-init-server-proc "sleep" "1")))) + (with-current-buffer + (car (push (erc-open "localhost" 6667 "tester" "Tester" 'connect + nil nil nil nil nil "tester" 'foonet) + erc-fill-tests--buffers)) + (setq erc-network 'foonet + erc-server-connected t) + (with-current-buffer (erc--open-target "#chan") + (set-window-buffer (selected-window) (current-buffer)) + + (erc-update-channel-member + "#chan" "alice" "alice" t nil nil nil nil nil "fake" "~u" nil nil t) + + (erc-update-channel-member + "#chan" "bob" "bob" t nil nil nil nil nil "fake" "~u" nil nil t) + + (erc-display-message + nil 'notice (current-buffer) + (concat "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.")) + + (erc-fill-tests--insert-privmsg "alice" + "bob: come, you are a tedious fool: to the purpose. " + "What was done to Elbow's wife, that he hath cause to complain of?" + " Come me to what was done to her.") + + ;; Introduce an artificial gap in properties `line-prefix' and + ;; `wrap-prefix' and later ensure they're not incremented twice. + (save-excursion + (forward-line -1) + (search-forward "? ") + (with-silent-modifications + (remove-text-properties (1- (point)) (point) + '(line-prefix t wrap-prefix t)))) + + (erc-fill-tests--insert-privmsg "bob" + "alice: Either your unparagoned mistress is dead, " + "or she's outprized by a trifle.") + + ;; Defend against non-local exits from `ert-skip' + (unwind-protect + (funcall test) + (when set-transient-map-timer + (timer-event-handler set-transient-map-timer)) + (set-window-buffer (selected-window) original-window-buffer) + (when (or noninteractive (getenv "ERC_TESTS_GRAPHICAL")) + (erc-tests-common-kill-buffers erc-fill-tests--buffers) + (setq erc-fill-tests--buffers nil)))))))) + +(defun erc-fill-tests--wrap-check-prefixes (&rest prefixes) + ;; Check that prefix props are applied over correct intervals. + (save-excursion + (goto-char (point-min)) + (dolist (prefix prefixes) + (should (search-forward prefix nil t)) + (should (get-text-property (pos-bol) 'line-prefix)) + (should (get-text-property (1- (pos-eol)) 'line-prefix)) + (should-not (get-text-property (pos-eol) 'line-prefix)) + ;; Spans entire line uninterrupted. + (let* ((val (get-text-property (pos-bol) 'line-prefix)) + (end (text-property-not-all (pos-bol) (point-max) + 'line-prefix val))) + (when (and (/= end (pos-eol)) (= ?? (char-before end))) + (setq end (text-property-not-all (1+ end) (point-max) + 'line-prefix val))) + (should (eq end (pos-eol)))) + (should (equal (get-text-property (pos-bol) 'wrap-prefix) + '(space :width erc-fill--wrap-value))) + (should-not (get-text-property (pos-eol) 'wrap-prefix)) + (should (equal (get-text-property (1- (pos-eol)) 'wrap-prefix) + '(space :width erc-fill--wrap-value)))))) + +;; On graphical displays, echo .graphic >> .git/info/exclude +(defvar erc-fill-tests--graphic-dir "fill/snapshots/.graphic/") + +(defun erc-fill-tests--compare (name) + (let ((dir (expand-file-name (if (display-graphic-p) + erc-fill-tests--graphic-dir + "fill/snapshots/" ) + (ert-resource-directory))) + (transform-fn (lambda (got) + (string-replace "erc-fill--wrap-value" + (number-to-string erc-fill--wrap-value) + got))) + (buffer-setup-fn (lambda () + (push (current-buffer) erc-fill-tests--buffers)))) + (erc-tests-common-snapshot-compare name dir transform-fn buffer-setup-fn))) + +;; To inspect variable pitch, set `erc-mode-hook' to +;; +;; (lambda () (face-remap-add-relative 'default :family "Sans Serif")) +;; +;; or similar. + +(ert-deftest erc-fill-wrap--monospace () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (unless (>= emacs-major-version 29) + (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'")) + + (let ((erc-prompt (lambda () "ABC>"))) + (erc-fill-tests--wrap-populate + + (lambda () + (should (= erc-fill--wrap-value 27)) + (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ") + (erc-fill-tests--compare "monospace-01-start") + + (ert-info ("Shift right by one (plus)") + ;; Args are all `erc-fill-wrap-nudge' +1 because interactive "p" + (ert-with-message-capture messages + ;; M-x erc-fill-wrap-nudge RET = + (ert-simulate-command '(erc-fill-wrap-nudge 2)) + (should (string-match (rx "for further adjustment") messages))) + (should (= erc-fill--wrap-value 29)) + (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ") + (erc-fill-tests--compare "monospace-02-right")) + + (ert-info ("Shift left by five") + ;; "M-x erc-fill-wrap-nudge RET -----" + (ert-simulate-command '(erc-fill-wrap-nudge -4)) + (should (= erc-fill--wrap-value 25)) + (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ") + (erc-fill-tests--compare "monospace-03-left")) + + (ert-info ("Reset") + ;; M-x erc-fill-wrap-nudge RET 0 + (ert-simulate-command '(erc-fill-wrap-nudge 0)) + (should (= erc-fill--wrap-value 27)) + (erc-fill-tests--wrap-check-prefixes "*** " "<alice> " "<bob> ") + (erc-fill-tests--compare "monospace-04-reset")) + + (erc--assert-input-bounds))))) + +(defun erc-fill-tests--simulate-refill () + ;; Simulate `erc-fill-wrap-refill-buffer' synchronously and without + ;; a progress reporter. + (save-excursion + (with-silent-modifications + (erc-fill--wrap-rejigger-region (point-min) erc-insert-marker nil nil)))) + +(ert-deftest erc-fill-wrap--merge () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (unless (>= emacs-major-version 29) + (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'")) + + (erc-fill-tests--wrap-populate + + (lambda () + (erc-update-channel-member + "#chan" "Dummy" "Dummy" t nil nil nil nil nil "fake" "~u" nil nil t) + + ;; Set this here so that the first few messages are from 1970. + ;; Following the current date stamp, the speaker isn't merged + ;; even though it's continued: "<bob> zero." + (let ((erc-fill-tests--current-time-value 1680332400)) + (erc-fill-tests--insert-privmsg "bob" "zero.") + (erc-fill-tests--insert-privmsg "alice" "one.") + (erc-fill-tests--insert-privmsg "alice" "two.") + (erc-fill-tests--insert-privmsg "bob" "three.") + (erc-fill-tests--insert-privmsg "bob" "four.") + (erc-fill-tests--insert-privmsg "Dummy" "five.") + (erc-fill-tests--insert-privmsg "Dummy" "six.")) + + (should (= erc-fill--wrap-value 27)) + (erc-fill-tests--wrap-check-prefixes + "*** " "<alice> " "<bob> " + "<bob> " "<alice> " "<alice> " "<bob> " "<bob> " "<Dummy> " "<Dummy> ") + (erc-fill-tests--compare "merge-01-start") + + (ert-info ("Shift right by one (plus)") + (ert-simulate-command '(erc-fill-wrap-nudge 2)) + (should (= erc-fill--wrap-value 29)) + (erc-fill-tests--wrap-check-prefixes + "*** " "<alice> " "<bob> " + "<bob> " "<alice> " "<alice> " "<bob> " "<bob> " "<Dummy> " "<Dummy> ") + (erc-fill-tests--compare "merge-02-right") + + (ert-info ("Command `erc-fill-wrap-refill-buffer' is idempotent") + (kill-buffer (pop erc-fill-tests--buffers)) + (erc-fill-tests--simulate-refill) ; idempotent + (erc-fill-tests--compare "merge-02-right")))))) + +(defun erc-fill-wrap-tests--merge-action (compare-file) + (unless (>= emacs-major-version 29) + (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'")) + + (erc-fill-tests--wrap-populate + + (lambda () + ;; Allow prior messages to be from 1970. + (let ((erc-fill-tests--current-time-value 1680332400)) + (erc-fill-tests--insert-privmsg "bob" "zero.") + (erc-fill-tests--insert-privmsg "bob" "0.5") + + (erc-process-ctcp-query + erc-server-process + (make-erc-response + :unparsed ":bob!~u@fake PRIVMSG #chan :\1ACTION one.\1" + :sender "bob!~u@fake" + :command "PRIVMSG" + :command-args '("#chan" "\1ACTION one.\1") + :contents "\1ACTION one.\1") + "bob" "~u" "fake") + + (erc-fill-tests--insert-privmsg "bob" "two.") + (erc-fill-tests--insert-privmsg "bob" "2.5") + + ;; Compat switch to opt out of overhanging speaker. + (let (erc-fill--wrap-action-dedent-p) + (erc-process-ctcp-query + erc-server-process + (make-erc-response + :unparsed ":bob!~u@fake PRIVMSG #chan :\1ACTION three\1" + :sender "bob!~u@fake" :command "PRIVMSG" + :command-args '("#chan" "\1ACTION three\1") + :contents "\1ACTION three\1") + "bob" "~u" "fake")) + + (erc-fill-tests--insert-privmsg "bob" "four.")) + + (should (= erc-fill--wrap-value 27)) + (erc-fill-tests--wrap-check-prefixes + "*** " "<alice> " "<bob> " "<bob> " "* bob " "<bob> " "* " "<bob> ") + (erc-fill-tests--compare compare-file)))) + +(ert-deftest erc-fill-wrap--merge-action () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (erc-fill-wrap-tests--merge-action "merge-wrap-01")) + +(ert-deftest erc-fill-wrap--merge-action/indicator-pre () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (let ((erc-fill-wrap-merge-indicator '(pre ?> shadow))) + (erc-fill-wrap-tests--merge-action "merge-wrap-indicator-pre-01"))) + +;; One crucial thing this test asserts is that the indicator is +;; omitted when the previous line ends in a stamp. +(ert-deftest erc-fill-wrap--merge-action/indicator-post () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (let ((erc-fill-wrap-merge-indicator '(post ?~ shadow))) + (erc-fill-wrap-tests--merge-action "merge-wrap-indicator-post-01"))) + +(ert-deftest erc-fill-line-spacing () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (unless (>= emacs-major-version 29) + (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'")) + + (let ((erc-fill-line-spacing 0.5)) + (erc-fill-tests--wrap-populate + (lambda () + (erc-fill-tests--insert-privmsg "bob" "This buffer is for text.") + (erc-display-message nil 'notice (current-buffer) "one two three") + (erc-display-message nil 'notice (current-buffer) "four five six") + (erc-fill-tests--insert-privmsg "bob" "Somebody stop me") + (erc-fill-tests--compare "spacing-01-mono"))))) + +(ert-deftest erc-fill-wrap-visual-keys--body () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (erc-fill-tests--wrap-populate + + (lambda () + (ert-info ("Value: non-input") + (should (eq erc-fill--wrap-visual-keys 'non-input)) + (goto-char (point-min)) + (should (search-forward "that he hath" nil t)) + (execute-kbd-macro "\C-a") + (should-not (looking-at (rx "<alice> "))) + (execute-kbd-macro "\C-e") + (should (search-backward "tedious fool" nil t)) + (should-not (looking-back "done to her\\.")) + (forward-char) + (execute-kbd-macro "\C-e") + (should (search-forward "done to her." nil t))) + + (ert-info ("Value: nil") + (execute-kbd-macro "\C-ca") + (should-not erc-fill--wrap-visual-keys) + (goto-char (point-min)) + (should (search-forward "in debug mode" nil t)) + (execute-kbd-macro "\C-a") + (should (looking-at (rx "*** "))) + (execute-kbd-macro "\C-e") + (should (eql ?\] (char-before (point))))) + + (ert-info ("Value: t") + (execute-kbd-macro "\C-ca") + (should (eq erc-fill--wrap-visual-keys t)) + (goto-char (point-min)) + (should (search-forward "that he hath" nil t)) + (execute-kbd-macro "\C-a") + (should-not (looking-at (rx "<alice> "))) + (should (search-backward "tedious fool" nil t)) + (execute-kbd-macro "\C-e") + (should-not (looking-back (rx "done to her\\."))) + (should (search-forward "done to her." nil t)) + (execute-kbd-macro "\C-a") + (should-not (looking-at (rx "<alice> "))))))) + +(ert-deftest erc-fill-wrap-visual-keys--prompt () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (erc-fill-tests--wrap-populate + + (lambda () + (set-window-buffer (selected-window) (current-buffer)) + (goto-char erc-input-marker) + (insert "This buffer is for text that is not saved, and for Lisp " + "evaluation. To create a file, visit it with C-x C-f and " + "enter text in its buffer.") + + (ert-info ("Value: non-input") + (should (eq erc-fill--wrap-visual-keys 'non-input)) + (execute-kbd-macro "\C-a") + (should (looking-at "This buffer")) + (execute-kbd-macro "\C-e") + (should (looking-back "its buffer\\.")) + (execute-kbd-macro "\C-a") + (execute-kbd-macro "\C-k") + (should (eobp))) + + (ert-info ("Value: nil") ; same + (execute-kbd-macro "\C-ca") + (should-not erc-fill--wrap-visual-keys) + (execute-kbd-macro "\C-y") + (should (looking-back "its buffer\\.")) + (execute-kbd-macro "\C-a") + (should (looking-at "This buffer")) + (execute-kbd-macro "\C-k") + (should (eobp))) + + (ert-info ("Value: non-input") + (execute-kbd-macro "\C-ca") + (should (eq erc-fill--wrap-visual-keys t)) + (execute-kbd-macro "\C-y") + (execute-kbd-macro "\C-a") + (should-not (looking-at "This buffer")) + (execute-kbd-macro "\C-p") + (should-not (looking-back "its buffer\\.")) + (should (search-forward "its buffer." nil t)) + (should (search-backward "ERC> " nil t)) + (execute-kbd-macro "\C-a"))))) + +(ert-deftest erc-fill--left-hand-stamps () + :tags `(:unstable + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (unless (>= emacs-major-version 29) + (ert-skip "Emacs version too low, missing `buffer-text-pixel-size'")) + + (let ((erc-timestamp-only-if-changed-flag nil) + (erc-insert-timestamp-function #'erc-insert-timestamp-left)) + (erc-fill-tests--wrap-populate + (lambda () + (should (= 8 left-margin-width)) + (pcase-let ((`((margin left-margin) ,displayed) + (get-text-property erc-insert-marker 'display))) + (should (equal-including-properties + displayed #(" ERC>" 4 8 + ( read-only t + front-sticky t + field erc-prompt + erc-prompt t + rear-nonsticky t + font-lock-face erc-prompt-face))))) + (erc-fill-tests--compare "stamps-left-01") + + (ert-info ("Shrink left margin by 1 col") + (erc-stamp--adjust-margin -1) + (with-silent-modifications (erc--refresh-prompt)) + (should (= 7 left-margin-width)) + (pcase-let ((`((margin left-margin) ,displayed) + (get-text-property erc-insert-marker 'display))) + (should (equal-including-properties + displayed #(" ERC>" 3 7 + ( read-only t + front-sticky t + field erc-prompt + erc-prompt t + rear-nonsticky t + font-lock-face erc-prompt-face)))))))))) + +;;; erc-fill-tests.el ends here diff --git a/test/lisp/erc/erc-goodies-tests.el b/test/lisp/erc/erc-goodies-tests.el new file mode 100644 index 00000000000..bdd197fa5cb --- /dev/null +++ b/test/lisp/erc/erc-goodies-tests.el @@ -0,0 +1,444 @@ +;;; erc-goodies-tests.el --- Tests for erc-goodies -*- lexical-binding:t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-tests-common))) + +(require 'erc-goodies) + +(defun erc-goodies-tests--assert-face (beg end-str present &optional absent) + (setq beg (+ beg (point-min))) + (let ((end (+ beg (1- (length end-str))))) + (while (and beg (< beg end)) + (let* ((val (get-text-property beg 'font-lock-face)) + (ft (flatten-tree (ensure-list val)))) + (dolist (p (ensure-list present)) + (if (consp p) + (should (member p val)) + (should (memq p ft)))) + (dolist (a (ensure-list absent)) + (if (consp a) + (should-not (member a val)) + (should-not (memq a ft)))) + (setq beg (text-property-not-all beg (point-max) + 'font-lock-face val)))))) + +;; These are from the "Examples" section of +;; https://modern.ircdocs.horse/formatting.html + +(ert-deftest erc-controls-highlight--examples () + (should (eq t erc-interpret-controls-p)) + (let ((erc-insert-modify-hook '(erc-controls-highlight)) + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq-local erc-interpret-mirc-color t) + (erc--initialize-markers (point) nil) + + (let* ((m "I love \C-c3IRC!\C-c It is the \C-c7best protocol ever!") + (msg (erc-format-privmessage "bob" m nil t))) + (erc-display-message nil nil (current-buffer) msg)) + (forward-line -1) + (should (search-forward "<bob> " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (erc-goodies-tests--assert-face + 0 "I love" 'erc-default-face 'fg:erc-color-face3) + (erc-goodies-tests--assert-face + 7 " IRC!" 'fg:erc-color-face3) + (erc-goodies-tests--assert-face + 11 " It is the " 'erc-default-face 'fg:erc-color-face7) + (erc-goodies-tests--assert-face + 22 "best protocol ever!" 'fg:erc-color-face7)) + + (let* ((m "This is a \C-]\C-c13,9cool \C-cmessage") + (msg (erc-format-privmessage "alice" m nil t))) + (erc-display-message nil nil (current-buffer) msg)) + (should (search-forward "<alice> " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (erc-goodies-tests--assert-face + 0 "this is a " 'erc-default-face 'erc-italic-face) + (erc-goodies-tests--assert-face + 10 "cool " '(erc-italic-face fg:erc-color-face13 bg:erc-color-face9)) + (erc-goodies-tests--assert-face + 15 "message" 'erc-italic-face + '(fg:erc-color-face13 bg:erc-color-face9))) + + (let* ((m "IRC \C-bis \C-c4,12so \C-cgreat\C-o!") + (msg (erc-format-privmessage "bob" m nil t))) + (erc-display-message nil nil (current-buffer) msg)) + (should (search-forward "<bob> " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (erc-goodies-tests--assert-face + 0 "IRC " 'erc-default-face 'erc-bold-face) + (erc-goodies-tests--assert-face + 4 "is " 'erc-bold-face '(fg:erc-color-face4 bg:erc-color-face12)) + (erc-goodies-tests--assert-face + 7 "so " '(erc-bold-face fg:erc-color-face4 bg:erc-color-face12)) + (erc-goodies-tests--assert-face + 10 "great" 'erc-bold-face '(fg:erc-color-face4 bg:erc-color-face12)) + (erc-goodies-tests--assert-face + 15 "!" 'erc-default-face 'erc-bold-face)) + + (let* ((m (concat "Rules: Don't spam 5\C-c13,8,6\C-c,7,8, " + "and especially not \C-b9\C-b\C-]!")) + (msg (erc-format-privmessage "alice" m nil t))) + (erc-display-message nil nil (current-buffer) msg)) + (should (search-forward "<alice> " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (erc-goodies-tests--assert-face + 0 "Rules: Don't spam 5" 'erc-default-face + '(fg:erc-color-face13 bg:erc-color-face8)) + (erc-goodies-tests--assert-face + 19 ",6" '(fg:erc-color-face13 bg:erc-color-face8)) + (erc-goodies-tests--assert-face + 21 ",7,8, and especially not " 'erc-default-face + '(fg:erc-color-face13 bg:erc-color-face8 erc-bold-face)) + (erc-goodies-tests--assert-face + 44 "9" 'erc-bold-face 'erc-italic-face) + (erc-goodies-tests--assert-face + 45 "!" 'erc-italic-face 'erc-bold-face)) + + (when noninteractive + (kill-buffer))))) + +;; Like the test above, this is most intuitive when run interactively. +;; Hovering over the redacted area should reveal its underlying text +;; in a high-contrast face. + +(ert-deftest erc-controls-highlight--inverse () + (should (eq t erc-interpret-controls-p)) + (let ((erc-insert-modify-hook '(erc-controls-highlight)) + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq-local erc-interpret-mirc-color t) + (erc--initialize-markers (point) nil) + + (let* ((m "Spoiler: \C-c0,0Hello\C-c1,1World!") + (msg (erc-format-privmessage "bob" m nil t))) + (erc-display-message nil nil (current-buffer) msg)) + (forward-line -1) + (should (search-forward "<bob> " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (should (eq (get-text-property (+ 9 (point)) 'mouse-face) + 'erc-inverse-face)) + (should (eq (get-text-property (1- (pos-eol)) 'mouse-face) + 'erc-inverse-face)) + (erc-goodies-tests--assert-face + 0 "Spoiler: " 'erc-default-face + '(fg:erc-color-face0 bg:erc-color-face0)) + (erc-goodies-tests--assert-face + 9 "Hello" '(erc-spoiler-face) + '( fg:erc-color-face0 bg:erc-color-face0 + fg:erc-color-face1 bg:erc-color-face1)) + (erc-goodies-tests--assert-face + 18 " World" '(erc-spoiler-face) + '( fg:erc-color-face0 bg:erc-color-face0 + fg:erc-color-face1 bg:erc-color-face1 ))) + (when noninteractive + (kill-buffer))))) + +(defvar erc-goodies-tests--motd + ;; This is from ergo's MOTD + '((":- - this is \2bold text\17.") + (":- - this is \35italics text\17.") + (":- - this is \0034red\3 and \0032blue\3 text.") + (":- - this is \0034,12red text with a light blue background\3.") + (":- - this is a normal escaped dollarsign: $") + (":- ") + (":- " + "\0031,0 00 \0030,1 01 \0030,2 02 \0030,3 03 " + "\0031,4 04 \0030,5 05 \0030,6 06 \0031,7 07 ") + (":- " + "\0031,8 08 \0031,9 09 \0030,10 10 \0031,11 11 " + "\0030,12 12 \0031,13 13 \0031,14 14 \0031,15 15 ") + (":- ") + (":- " + "\0030,16 16 \0030,17 17 \0030,18 18 \0030,19 19 " + "\0030,20 20 \0030,21 21 \0030,22 22 \0030,23 23 " + "\0030,24 24 \0030,25 25 \0030,26 26 \0030,27 27 ") + (":- " + "\0030,28 28 \0030,29 29 \0030,30 30 \0030,31 31 " + "\0030,32 32 \0030,33 33 \0030,34 34 \0030,35 35 " + "\0030,36 36 \0030,37 37 \0030,38 38 \0030,39 39 ") + (":- " + "\0030,40 40 \0030,41 41 \0030,42 42 \0030,43 43 " + "\0030,44 44 \0030,45 45 \0030,46 46 \0030,47 47 " + "\0030,48 48 \0030,49 49 \0030,50 50 \0030,51 51 ") + (":- " + "\0030,52 52 \0030,53 53 \0031,54 54 \0031,55 55 " + "\0031,56 56 \0031,57 57 \0031,58 58 \0030,59 59 " + "\0030,60 60 \0030,61 61 \0030,62 62 \0030,63 63 ") + (":- " + "\0030,64 64 \0031,65 65 \0031,66 66 \0031,67 67 " + "\0031,68 68 \0031,69 69 \0031,70 70 \0031,71 71 " + "\0030,72 72 \0030,73 73 \0030,74 74 \0030,75 75 ") + (":- " + "\0031,76 76 \0031,77 77 \0031,78 78 \0031,79 79 " + "\0031,80 80 \0031,81 81 \0031,82 82 \0031,83 83 " + "\0031,84 84 \0031,85 85 \0031,86 86 \0031,87 87 ") + (":- " + "\0030,88 88 \0030,89 89 \0030,90 90 \0030,91 91 " + "\0030,92 92 \0030,93 93 \0030,94 94 \0030,95 95 " + "\0031,96 96 \0031,97 97 \0031,98 98 \399,99 99 ") + (":- "))) + +(ert-deftest erc-controls-highlight--motd () + (should (eq t erc-interpret-controls-p)) + (let ((erc-insert-modify-hook '(erc-controls-highlight)) + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (with-current-buffer (get-buffer-create "#chan") + (erc-mode) + (setq-local erc-interpret-mirc-color t) + (erc--initialize-markers (point) nil) + + (dolist (parts erc-goodies-tests--motd) + (erc-display-message nil 'notice (current-buffer) (string-join parts))) + + ;; Spot check + (goto-char (point-min)) + (should (search-forward " 16 " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (erc-goodies-tests--assert-face + 0 " 17 " '(fg:erc-color-face0 (:background "#472100"))) + (erc-goodies-tests--assert-face + 4 " 18 " '(fg:erc-color-face0 (:background "#474700")) + '((:background "#472100")))) + + (should (search-forward " 71 " nil t)) + (save-restriction + (narrow-to-region (point) (pos-eol)) + (erc-goodies-tests--assert-face + 0 " 72 " '(fg:erc-color-face0 (:background "#5959ff"))) + (erc-goodies-tests--assert-face + 4 " 73 " '(fg:erc-color-face0 (:background "#c459ff")) + '((:background "#5959ff")))) + + (goto-char (point-min)) + (when noninteractive + (kill-buffer))))) + + +;; Among other things, this test also asserts that a local module's +;; minor-mode toggle is allowed to disable its mode variable as +;; needed. + +(defun erc-goodies-tests--assert-kp-indicator-on () + (should erc--keep-place-indicator-overlay) + (should (local-variable-p 'window-buffer-change-functions)) + (should window-configuration-change-hook) + (should (memq 'erc-keep-place erc-insert-pre-hook)) + (should (eq erc-keep-place-mode + (not (local-variable-p 'erc-insert-pre-hook))))) + +(defun erc-goodies-tests--assert-kp-indicator-off () + (should-not (local-variable-p 'erc-insert-pre-hook)) + (should-not (local-variable-p 'window-buffer-change-functions)) + (should-not erc--keep-place-indicator-overlay)) + +(defun erc-goodies-tests--kp-indicator-populate () + (erc-display-message nil 'notice (current-buffer) + "This buffer is for text that is not saved") + (erc-display-message nil 'notice (current-buffer) + "and for lisp evaluation") + (should (search-forward "saved" nil t)) + (erc-keep-place-move nil) + (goto-char erc-input-marker)) + +(defun erc-goodies-tests--keep-place-indicator (test) + (with-current-buffer (get-buffer-create "*erc-keep-place-indicator-mode*") + (erc-mode) + (erc--initialize-markers (point) nil) + (setq erc-server-process + (start-process "sleep" (current-buffer) "sleep" "1")) + (set-process-query-on-exit-flag erc-server-process nil) + (let (erc-connect-pre-hook + erc-modules) + + (ert-info ("Clean slate") + (erc-goodies-tests--assert-kp-indicator-off) + (should-not erc-keep-place-mode) + (should-not (memq 'keep-place erc-modules))) + + (funcall test)) + + (when noninteractive + (erc-keep-place-indicator-mode -1) + (erc-keep-place-mode -1) + (should-not (member 'erc-keep-place + (default-value 'erc-insert-pre-hook))) + (should-not (local-variable-p 'erc-insert-pre-hook)) + (kill-buffer)))) + +(ert-deftest erc-keep-place-indicator-mode--no-global () + (erc-goodies-tests--keep-place-indicator + (lambda () + + (ert-info ("Value t") + (should (eq erc-keep-place-indicator-buffer-type t)) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-on) + (goto-char (point-min))) + + (erc-keep-place-indicator-mode -1) + (erc-goodies-tests--assert-kp-indicator-off) + + (ert-info ("Value `target'") + (let ((erc-keep-place-indicator-buffer-type 'target)) + ;; No-op because server buffer. + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-off) + ;; Spoof target buffer (no longer no-op). + (setq erc--target (erc--target-from-string "#chan")) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-on))) + + (erc-keep-place-indicator-mode -1) + (erc-goodies-tests--assert-kp-indicator-off) + + (ert-info ("Value `server'") + (let ((erc-keep-place-indicator-buffer-type 'server)) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-off) + (setq erc--target nil) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-on))) + + ;; Populate buffer + (erc-goodies-tests--kp-indicator-populate) + + (ert-info ("Indicator survives reconnect") + (let ((erc--server-reconnecting (buffer-local-variables))) + (cl-letf (((symbol-function 'erc-server-connect) #'ignore)) + (erc-open "localhost" 6667 "tester" "Tester" 'connect + nil nil nil nil nil "tester" nil))) + (erc-goodies-tests--assert-kp-indicator-on) + (should (= (point) erc-input-marker)) + (goto-char (overlay-start erc--keep-place-indicator-overlay)) + (should (looking-at (rx "*** This buffer is for text"))))))) + +(ert-deftest erc-keep-place-indicator-mode--global () + (erc-goodies-tests--keep-place-indicator + (lambda () + + (push 'keep-place erc-modules) + + (ert-info ("Value t") + (should (eq erc-keep-place-indicator-buffer-type t)) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-on) + ;; Local module activates global `keep-place'. + (should erc-keep-place-mode) + ;; Does not register local version of hook (otherwise would run + ;; twice). + (should-not (local-variable-p 'erc-insert-pre-hook)) + (goto-char (point-min))) + + (erc-keep-place-indicator-mode -1) + (erc-goodies-tests--assert-kp-indicator-off) + (should erc-keep-place-mode) + (should (member 'erc-keep-place erc-insert-pre-hook)) + + (ert-info ("Value `target'") + (let ((erc-keep-place-indicator-buffer-type 'target)) + ;; No-op because server buffer. + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-off) + ;; Does not interfere with global activation state. + (should erc-keep-place-mode) + (should (member 'erc-keep-place erc-insert-pre-hook)) + ;; Morph into a target buffer (no longer no-op). + (setq erc--target (erc--target-from-string "#chan")) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-on) + ;; Does not register local version of hook. + (should-not (local-variable-p 'erc-insert-pre-hook)))) + + (erc-keep-place-indicator-mode -1) + (erc-goodies-tests--assert-kp-indicator-off) + (should erc-keep-place-mode) + (should (member 'erc-keep-place erc-insert-pre-hook)) + + (ert-info ("Value `server'") + (let ((erc-keep-place-indicator-buffer-type 'server)) + ;; No-op because we're now a target buffer. + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-off) + (should erc-keep-place-mode) + (should (member 'erc-keep-place erc-insert-pre-hook)) + ;; Back to server. + (setq erc--target nil) + (erc-keep-place-indicator-mode +1) + (erc-goodies-tests--assert-kp-indicator-on) + (should-not (local-variable-p 'erc-insert-pre-hook)))) + + (ert-info ("Local adapts to global toggle") + (erc-keep-place-mode -1) + (should-not (member 'erc-keep-place + (default-value 'erc-insert-pre-hook))) + (should (member 'erc-keep-place erc-insert-pre-hook)) + (erc-goodies-tests--assert-kp-indicator-on) + (erc-keep-place-mode +1) + (should (member 'erc-keep-place (default-value 'erc-insert-pre-hook))) + (should-not (local-variable-p 'erc-insert-pre-hook)) + (erc-goodies-tests--assert-kp-indicator-on)) + + ;; Populate buffer + (erc-goodies-tests--kp-indicator-populate) + + (ert-info ("Indicator survives reconnect") + (let ((erc--server-reconnecting (buffer-local-variables))) + (cl-letf (((symbol-function 'erc-server-connect) #'ignore)) + (erc-open "localhost" 6667 "tester" "Tester" 'connect + nil nil nil nil nil "tester" nil))) + (erc-goodies-tests--assert-kp-indicator-on) + (should erc-keep-place-mode) + (should (member 'erc-keep-place erc-insert-pre-hook)) + (should (= (point) erc-input-marker)) + (goto-char (overlay-start erc--keep-place-indicator-overlay)) + (should (looking-at (rx "*** This buffer is for text"))))))) + +(ert-deftest erc--get-inserted-msg-beg/readonly () + (erc-tests-common-assert-get-inserted-msg-readonly-with + #'erc-tests-common-assert-get-inserted-msg/basic + (lambda (arg) (should (= 3 (erc--get-inserted-msg-beg arg)))))) + +(ert-deftest erc--get-inserted-msg-end/readonly () + (erc-tests-common-assert-get-inserted-msg-readonly-with + #'erc-tests-common-assert-get-inserted-msg/basic + (lambda (arg) (should (= 11 (erc--get-inserted-msg-end arg)))))) + +(ert-deftest erc--get-inserted-msg-bounds/readonly () + (erc-tests-common-assert-get-inserted-msg-readonly-with + #'erc-tests-common-assert-get-inserted-msg/basic + (lambda (arg) + (should (equal '(3 . 11) (erc--get-inserted-msg-bounds arg)))))) + + +;;; erc-goodies-tests.el ends here diff --git a/test/lisp/erc/erc-networks-tests.el b/test/lisp/erc/erc-networks-tests.el index 370502f62d6..d8d8c6fa9cd 100644 --- a/test/lisp/erc/erc-networks-tests.el +++ b/test/lisp/erc/erc-networks-tests.el @@ -20,25 +20,21 @@ ;;; Code: (require 'ert-x) ; cl-lib -(require 'erc) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-tests-common))) (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)) +(defun erc-networks-tests--create-live-proc () + (erc-tests-common-init-server-proc "sleep" "1")) ;; 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)))) + (erc-tests-common-kill-buffers)) (defun erc-networks-tests--bufnames (prefix) (let* ((case-fold-search) @@ -623,11 +619,6 @@ :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))) @@ -1206,7 +1197,7 @@ calls) (erc-mode) - (cl-letf (((symbol-function 'erc-display-line) + (cl-letf (((symbol-function 'erc--route-insertion) (lambda (&rest r) (push r calls)))) (ert-info ("Signals when `erc-server-announced-name' unset") @@ -1233,10 +1224,7 @@ :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--initialize-markers (point) nil) (erc-networks--ensure-announced erc-server-process parsed) (goto-char (point-min)) @@ -1277,9 +1265,9 @@ (with-current-buffer old-buf (erc-mode) (insert "*** Old buf") + (erc--initialize-markers (point) nil) (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))) @@ -1328,10 +1316,10 @@ erc-reuse-buffers) (with-current-buffer old-buf (erc-mode) + (erc--initialize-markers (point) nil) (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") @@ -1377,10 +1365,10 @@ (with-current-buffer old-buf (erc-mode) + (erc--initialize-markers (point) nil) (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))) @@ -1415,10 +1403,10 @@ (with-current-buffer old-buf (erc-mode) + (erc--initialize-markers (point) nil) (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") @@ -1450,14 +1438,16 @@ (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 + ;; + old-proc) ; live (with-current-buffer old-buf (erc-mode) + (setq old-proc (erc-networks-tests--create-live-proc)) + (erc--initialize-markers (point) nil) (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))) @@ -1473,12 +1463,15 @@ (ert-info ("New buffer rejected, abandoned, not killed") (with-current-buffer (get-buffer-create "irc.foonet.org") (erc-mode) + (erc--initialize-markers (point) nil) (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)) + (set-process-sentinel erc-server-process #'ignore) + (erc-display-message nil 'notice (current-buffer) "notice") + (with-silent-modifications + (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")) @@ -1508,10 +1501,10 @@ (with-current-buffer old-buf (erc-mode) (insert "*** Old buf") + (erc--initialize-markers (point) nil) (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)) @@ -1560,10 +1553,10 @@ (with-current-buffer old-buf (erc-mode) (insert "*** Old buf") + (erc--initialize-markers (point) nil) (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)) @@ -1750,4 +1743,22 @@ (should (eq (erc-networks--determine) erc-networks--name-missing-sentinel)))) +(ert-deftest erc-ports-list () + (with-suppressed-warnings ((obsolete erc-server-alist)) + (let* ((srv (assoc "Libera.Chat: Random server" erc-server-alist))) + (should (equal (erc-ports-list (nth 3 srv)) + '(6665 6666 6667 8000 8001 8002))) + (should (equal (erc-ports-list (nth 4 srv)) + '(6697 7000 7070)))) + + (let* ((srv (assoc "Libera.Chat: Random Europe server" erc-server-alist))) + (should (equal (erc-ports-list (nth 3 srv)) '(6667))) + (should (equal (erc-ports-list (nth 4 srv)) '(6697)))) + + (let* ((srv (assoc "OFTC: Random server" erc-server-alist))) + (should (equal (erc-ports-list (nth 3 srv)) + '(6667 6668 6669 6670 7000))) + (should (equal (erc-ports-list (nth 4 srv)) + '(6697 9999)))))) + ;;; erc-networks-tests.el ends here diff --git a/test/lisp/erc/erc-nicks-tests.el b/test/lisp/erc/erc-nicks-tests.el new file mode 100644 index 00000000000..54882278139 --- /dev/null +++ b/test/lisp/erc/erc-nicks-tests.el @@ -0,0 +1,571 @@ +;;; erc-nicks-tests.el --- Tests for erc-nicks -*- lexical-binding:t -*- + +;; Copyright (C) 2023 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: + +;; Unlike most of ERC's tests, the ones in this file can be run +;; interactively in the same session. + +;; TODO: +;; +;; * Add mock session (or scenario) with buffer snapshots, like those +;; in erc-fill-tests.el. (Should probably move helpers to a common +;; library under ./resources.) + +;;; Code: + +(require 'ert-x) +(require 'erc-nicks) + +;; This function replicates the behavior of older "invert" strategy +;; implementations from EmacsWiki, etc. The values for the lower and +;; upper bounds (0.33 and 0.66) are likewise inherited. See +;; `erc-nicks--invert-classic--dark' below for one reason its results +;; may not be plainly obvious. +(defun erc-nicks-tests--invert-classic (color) + (if (pcase (erc-nicks--bg-mode) + ('dark (< (erc-nicks--get-luminance color) (/ 1 3.0))) + ('light (> (erc-nicks--get-luminance color) (/ 2 3.0)))) + (list (- 1.0 (nth 0 color)) (- 1.0 (nth 1 color)) (- 1.0 (nth 2 color))) + color)) + + +(ert-deftest erc-nicks--get-luminance () + (should (eql 0.0 (erc-nicks--get-luminance "black"))) + (should (eql 1.0 (erc-nicks--get-luminance "white"))) + (should (eql 21.0 (/ (+ 0.05 1.0) (+ 0.05 0.0)))) + + ;; RGB floats from a `display-graphic-p' session. + (let ((a (erc-nicks--get-luminance ; #9439ad + '(0.5803921568627451 0.2235294117647059 0.6784313725490196))) + (b (erc-nicks--get-luminance ; #ae54c7 + '(0.6823529411764706 0.32941176470588235 0.7803921568627451))) + (c (erc-nicks--get-luminance ; #d19ddf + '(0.8196078431372549 0.615686274509804 0.8745098039215686))) + (d (erc-nicks--get-luminance ; #f5e8f8 + '(0.9607843137254902 0.9098039215686274 0.9725490196078431)))) + ;; Low, med, high contrast comparisons against known values from + ;; an external source. + (should (eql 1.42 (/ (round (* 100 (/ (+ 0.05 b) (+ 0.05 a)))) 100.0))) + (should (eql 2.78 (/ (round (* 100 (/ (+ 0.05 c) (+ 0.05 a)))) 100.0))) + (should (eql 5.16 (/ (round (* 100 (/ (+ 0.05 d) (+ 0.05 a)))) 100.0))))) + +(ert-deftest erc-nicks-invert--classic () + (let ((convert (lambda (n) (apply #'color-rgb-to-hex + (erc-nicks-tests--invert-classic + (color-name-to-rgb n)))))) + (let ((erc-nicks--bg-mode-value 'dark)) + (should (equal (funcall convert "white") "#ffffffffffff")) + (should (equal (funcall convert "black") "#ffffffffffff")) + (should (equal (funcall convert "green") "#0000ffff0000"))) + (let ((erc-nicks--bg-mode-value 'light)) + (should (equal (funcall convert "white") "#000000000000")) + (should (equal (funcall convert "black") "#000000000000")) + (should (equal (funcall convert "green") "#ffff0000ffff"))))) + +(ert-deftest erc-nicks--get-contrast () + (should (= 21.0 (erc-nicks--get-contrast "white" "black"))) + (should (= 21.0 (erc-nicks--get-contrast "black" "white"))) + (should (= 1.0 (erc-nicks--get-contrast "black" "black"))) + (should (= 1.0 (erc-nicks--get-contrast "white" "white")))) + +(defun erc-nicks-tests--print-contrast (fn color) + (let* ((erc-nicks-color-adjustments (list fn)) + (result (erc-nicks--reduce color)) + (start (point))) + (insert (format "%16s%-16s%16s%-16s\n" + (concat color "-") + (concat ">" result) + (concat color " ") + (concat " " result))) + (put-text-property (+ start 32) (+ start 48) 'face + (list :background color :foreground result)) + (put-text-property (+ start 48) (+ start 64) 'face + (list :background result :foreground color)) + result)) + +(ert-deftest erc-nicks--invert-classic--light () + (let ((erc-nicks--bg-luminance 1.0) + (erc-nicks--bg-mode-value 'light) + (show (lambda (c) (erc-nicks-tests--print-contrast + #'erc-nicks-tests--invert-classic c)))) + + (with-current-buffer (get-buffer-create + "*erc-nicks--invert-classic--light*") + (should (equal "#000000000000" (funcall show "white"))) + (should (equal "#000000000000" (funcall show "black"))) + (should (equal "#ffff00000000" (funcall show "red"))) + (should (equal "#ffff0000ffff" (funcall show "green"))) ; magenta + (should (equal "#00000000ffff" (funcall show "blue"))) + + (unless noninteractive + (should (equal "#bbbbbbbbbbbb" (funcall show "#bbbbbbbbbbbb"))) + (should (equal "#cccccccccccc" (funcall show "#cccccccccccc"))) + (should (equal "#222122212221" (funcall show "#dddddddddddd"))) + (should (equal "#111011101110" (funcall show "#eeeeeeeeeeee")))) + + (when noninteractive + (kill-buffer))))) + +;; This shows that the output can be darker (have less contrast) than +;; the input. +(ert-deftest erc-nicks--invert-classic--dark () + (let ((erc-nicks--bg-luminance 0.0) + (erc-nicks--bg-mode-value 'dark) + (show (lambda (c) (erc-nicks-tests--print-contrast + #'erc-nicks-tests--invert-classic c)))) + + (with-current-buffer (get-buffer-create + "*erc-nicks--invert-classic--dark*") + (should (equal "#ffffffffffff" (funcall show "white"))) + (should (equal "#ffffffffffff" (funcall show "black"))) + (should (equal "#0000ffffffff" (funcall show "red"))) ; cyan + (should (equal "#0000ffff0000" (funcall show "green"))) + (should (equal "#ffffffff0000" (funcall show "blue"))) ; yellow + + (unless noninteractive + (should (equal "#aaaaaaaaaaaa" (funcall show "#555555555555"))) + (should (equal "#999999999999" (funcall show "#666666666666"))) + (should (equal "#888888888888" (funcall show "#777777777777"))) + (should (equal "#777777777777" (funcall show "#888888888888"))) + (should (equal "#666666666666" (funcall show "#999999999999"))) + (should (equal "#aaaaaaaaaaaa" (funcall show "#aaaaaaaaaaaa")))) + + (when noninteractive + (kill-buffer))))) + +;; These are the same as the legacy version but work in terms of +;; contrast ratios. Converting the original bounds to contrast ratios +;; (assuming pure white and black backgrounds) gives: +;; +;; min-lum of 0.33 ~~> 1.465 +;; max-lum of 0.66 ~~> 7.666 +;; +(ert-deftest erc-nicks-invert--light () + (let ((erc-nicks--bg-luminance 1.0) + (erc-nicks--bg-mode-value 'light) + (erc-nicks-contrast-range '(1.465)) + (show (lambda (c) (erc-nicks-tests--print-contrast + #'erc-nicks-invert c)))) + + (with-current-buffer (get-buffer-create + "*erc-nicks--invert-classic--light*") + (should (equal "#000000000000" (funcall show "white"))) + (should (equal "#000000000000" (funcall show "black"))) + (should (equal "#ffff00000000" (funcall show "red"))) + (should (equal "#ffff0000ffff" (funcall show "green"))) ; magenta + (should (equal "#00000000ffff" (funcall show "blue"))) + + (unless noninteractive + (should (equal "#bbbbbbbbbbbb" (funcall show "#bbbbbbbbbbbb"))) + (should (equal "#cccccccccccc" (funcall show "#cccccccccccc"))) + (should (equal "#222122212221" (funcall show "#dddddddddddd"))) + (should (equal "#111011101110" (funcall show "#eeeeeeeeeeee")))) + + (when noninteractive + (kill-buffer))))) + +(ert-deftest erc-nicks-invert--dark () + (let ((erc-nicks--bg-luminance 0.0) + (erc-nicks--bg-mode-value 'dark) + (erc-nicks-contrast-range '(7.666)) + (show (lambda (c) (erc-nicks-tests--print-contrast + #'erc-nicks-invert c)))) + + (with-current-buffer (get-buffer-create "*erc-nicks-invert--dark*") + (should (equal "#ffffffffffff" (funcall show "white"))) + (should (equal "#ffffffffffff" (funcall show "black"))) + (should (equal "#0000ffffffff" (funcall show "red"))) ; cyan + (should (equal "#0000ffff0000" (funcall show "green"))) + (should (equal "#ffffffff0000" (funcall show "blue"))) ; yellow + + (unless noninteractive + (should (equal "#aaaaaaaaaaaa" (funcall show "#555555555555"))) + (should (equal "#999999999999" (funcall show "#666666666666"))) + (should (equal "#888888888888" (funcall show "#777777777777"))) + (should (equal "#888888888888" (funcall show "#888888888888"))) + (should (equal "#999999999999" (funcall show "#999999999999")))) + + (when noninteractive + (kill-buffer))))) + +(ert-deftest erc-nicks-add-contrast () + (let ((erc-nicks--bg-luminance 1.0) + (erc-nicks--bg-mode-value 'light) + (erc-nicks--fg-rgb '(0.0 0.0 0.0)) + (erc-nicks-bg-color "white") + (erc-nicks-contrast-range '(3.5)) + (show (lambda (c) (erc-nicks-tests--print-contrast + #'erc-nicks-add-contrast c)))) + + (with-current-buffer (get-buffer-create "*erc-nicks-add-contrast*") + (should (equal "#893a893a893a" (funcall show "white"))) + (should (equal "#893a893a893a" (funcall show "#893a893a893a"))) + (should (equal "#000000000000" (funcall show "black"))) + (should (equal "#ffff00000000" (funcall show "red"))) + (should (equal "#0000a12e0000" (funcall show "green"))) + (should (equal "#00000000ffff" (funcall show "blue"))) + + ;; When the input is already near the desired ratio, the result + ;; may not be in bounds, only close. But the difference is + ;; usually imperceptible. + (unless noninteractive + ;; Well inside (light slate gray) + (should (equal "#777788889999" (funcall show "#777788889999"))) + ;; Slightly outside -> just outside + (should (equal "#7c498bd39b5c" (funcall show "#88889999aaaa"))) + ;; Just outside -> just inside + (should (equal "#7bcc8b479ac0" (funcall show "#7c498bd39b5c"))) + ;; Just inside + (should (equal "#7bcc8b479ac0" (funcall show "#7bcc8b479ac0")))) + + (when noninteractive + (kill-buffer))))) + +(ert-deftest erc-nicks-cap-contrast () + (should (= 12.5 (cdr erc-nicks-contrast-range))) + (let ((erc-nicks--bg-luminance 1.0) + (erc-nicks--bg-mode-value 'light) + (erc-nicks--fg-rgb '(0.0 0.0 0.0)) + (erc-nicks-bg-color "white") + (show (lambda (c) (erc-nicks-tests--print-contrast + #'erc-nicks-cap-contrast c)))) + + (with-current-buffer (get-buffer-create "*erc-nicks-remove-contrast*") + (should (equal (funcall show "black") "#34e534e534e5" )) ; 21.0 -> 12.14 + (should ; 12.32 -> 12.32 (same) + (equal (funcall show "#34e534e534e5") "#34e534e534e5")) + (should (equal (funcall show "white") "#ffffffffffff")) + + (unless noninteractive + (should (equal (funcall show "DarkRed") "#8b8b00000000")) + (should (equal (funcall show "DarkGreen") "#000064640000")) + ;; 15.29 -> 12.38 + (should (equal (funcall show "DarkBlue") "#1cf11cf198b5")) + + ;; 12.50 -> 12.22 + (should (equal (funcall show "#33e033e033e0") "#34ab34ab34ab")) + ;; 12.57 -> 12.28 + (should (equal (funcall show "#338033803380") "#344c344c344c")) + ;; 12.67 -> 12.37 + (should (equal (funcall show "#330033003300") "#33cc33cc33cc"))) + + (when noninteractive + (kill-buffer))))) + +(ert-deftest erc-nicks--skip-p () + ;; Baseline + (should-not (erc-nicks--skip-p 'bold nil 10000000)) + (should-not (erc-nicks--skip-p '(bold) nil 10000000)) + (should-not (erc-nicks--skip-p nil '(bold) 10000000)) + (should-not (erc-nicks--skip-p 'bold '(bold) 0)) + (should-not (erc-nicks--skip-p '(bold) '(bold) 0)) + (should-not (erc-nicks--skip-p 'bold '(foo bold) 0)) + (should-not (erc-nicks--skip-p '((:inherit bold)) '(bold) 1)) + (should (erc-nicks--skip-p 'bold '(bold) 1)) + (should (erc-nicks--skip-p 'bold '(fake bold) 1)) + (should (erc-nicks--skip-p 'bold '(foo bar bold) 1)) + (should (erc-nicks--skip-p '(bold) '(bold) 1)) + (should (erc-nicks--skip-p '((bold)) '(bold) 1)) + (should (erc-nicks--skip-p '((((bold)))) '(bold) 1)) + (should (erc-nicks--skip-p '(bold) '(foo bold) 1)) + (should (erc-nicks--skip-p '(:inherit bold) '((:inherit bold)) 1)) + (should (erc-nicks--skip-p '((:inherit bold)) '((:inherit bold)) 1)) + (should (erc-nicks--skip-p '(((:inherit bold))) '((:inherit bold)) 1)) + + ;; Composed + (should-not (erc-nicks--skip-p '(italic bold) '(bold) 1)) + (should-not (erc-nicks--skip-p '((italic) bold) '(bold) 1)) + (should-not (erc-nicks--skip-p '(italic (bold)) '(bold) 1)) + (should (erc-nicks--skip-p '(italic bold) '(bold) 2)) + (should (erc-nicks--skip-p '((italic) bold) '(bold) 2)) + (should (erc-nicks--skip-p '(italic (bold)) '(bold) 2)) + + (should-not (erc-nicks--skip-p '(italic default bold) '(bold) 2)) + (should-not (erc-nicks--skip-p '((default italic) bold) '(bold) 2)) + (should-not (erc-nicks--skip-p '(italic (default bold)) '(bold) 2)) + (should-not (erc-nicks--skip-p '((default italic) (bold shadow)) '(bold) 2)) + (should (erc-nicks--skip-p '((default italic) bold) '(bold) 3)) + (should (erc-nicks--skip-p '(italic (default bold)) '(bold) 3)) + (should (erc-nicks--skip-p '((default italic) (bold shadow)) '(bold) 3)) + (should (erc-nicks--skip-p '(italic (default (bold shadow))) '(bold) 3))) + +(ert-deftest erc-nicks--trim () + (should (equal (erc-nicks--trim "Bob`") "bob")) + (should (equal (erc-nicks--trim "Bob``") "bob")) + + ;; `erc--casemapping-rfc1459' + (let ((erc-nicks-ignore-chars "^")) + (should (equal (erc-nicks--trim "Bob~") "bob^")) + (should (equal (erc-nicks--trim "Bob^") "bob")))) + +(defvar erc-nicks-tests--fake-face-list nil) + +;; Since we can't delete faces, mock `face-list' to only return those +;; in `erc-nicks--face-table' created by the current test. +(defun erc-nicks-tests--face-list () + (let ((table (buffer-local-value 'erc-nicks--face-table + (get-buffer "foonet"))) + out) + (maphash (lambda (k v) + (when (member k erc-nicks-tests--fake-face-list) + (push v out))) + table) + (nreverse out))) + +(defun erc-nicks-tests--create-session (test alice bob) + (should-not (memq 'nicks erc-modules)) + (advice-add 'face-list :override #'erc-nicks-tests--face-list) + (let ((erc-modules (cons 'nicks erc-modules)) + (inhibit-message noninteractive) + (erc-nicks-tests--fake-face-list + (list (downcase alice) (downcase bob))) + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (with-current-buffer + (cl-letf + (((symbol-function 'erc-server-connect) + (lambda (&rest _) + (setq erc-server-process + (start-process "sleep" (current-buffer) "sleep" "1")) + (set-process-query-on-exit-flag erc-server-process nil)))) + + (erc-open "localhost" 6667 "tester" "Tester" 'connect + nil nil nil nil nil "tester")) + + (let ((inhibit-message noninteractive)) + (dolist (line (split-string "\ +:irc.foonet.org 004 tester irc.foonet.org irc.d abc 123 456 +:irc.foonet.org 005 tester NETWORK=foonet :are supported +:irc.foonet.org 376 tester :End of /MOTD command." + "\n")) + (erc-parse-server-response erc-server-process line))) + + (with-current-buffer (erc--open-target "#chan") + (erc-update-channel-member + "#chan" alice alice t nil nil nil nil nil "fake" "~u" nil nil t) + + (erc-update-channel-member + "#chan" bob bob t nil nil nil nil nil "fake" "~u" nil nil t) + + (erc-display-message + nil 'notice (current-buffer) + (concat "This server is in debug mode and is logging all user I/O. " + "Blah " alice " (1) " bob " (2) blah.")) + + (erc-display-message nil nil (current-buffer) + (erc-format-privmessage bob "Hi Alice" nil t)) + + (erc-display-message nil nil (current-buffer) + (erc-format-privmessage alice "Hi Bob" nil t))) + + (funcall test) + + (when noninteractive + (kill-buffer "#chan") + (when (get-buffer " *Custom-Work*") + (kill-buffer " *Custom-Work*")) + (kill-buffer)))) + (advice-remove 'face-list #'erc-nicks-tests--face-list)) + +(ert-deftest erc-nicks-list-faces () + (erc-nicks-tests--create-session + (lambda () + (erc-nicks-list-faces) + (let ((table (buffer-local-value 'erc-nicks--face-table + (get-buffer "foonet"))) + calls) + (cl-letf (((symbol-function 'erc-nicks--list-faces-help-button-action) + (lambda (&rest r) (push r calls)))) + (with-current-buffer "*Faces*" + (set-window-buffer (selected-window) (current-buffer)) + (goto-char (point-min)) + + (ert-info ("Clicking on face link runs action function") + (forward-button 1) + (should (looking-at "erc-nicks-alice1-face")) + (push-button) + (should (eq (car (car calls)) (gethash "alice1" table)))) + + (ert-info ("Clicking on sample text describes face") + (forward-button 1) + (should (looking-at (rx "#" (+ xdigit)))) + (push-button) + (should (search-forward-regexp + (rx "Foreground: #" (group (+ xdigit)) eol))) + (forward-button 2) ; skip Inherit:... + (push-button)) + + (ert-info ("First entry's sample is rendered correctly") + (let ((hex (match-string 1))) + (should (looking-at (concat "#" hex))) + (goto-char (button-end (point))) + (should (looking-back " foonet")) + (should (eq (button-get (1- (point)) 'face) (car (pop calls)))) + (should-not calls))) + + (ert-info ("Clicking on another entry's face link runs action") + (forward-button 1) + (should (looking-at "erc-nicks-bob1-face")) + (push-button) + (should (eq (car (car calls)) (gethash "bob1" table)))) + + (ert-info ("Second entry's sample is rendered correctly") + (forward-button 1) + (should (looking-at (rx "#" (+ xdigit)))) + (goto-char (button-end (point))) + (should (looking-back " foonet")) + (should (eq (button-get (1- (point)) 'face) (car (pop calls)))) + (should-not calls)) + + (when noninteractive + (kill-buffer)))))) + "Alice1" "Bob1")) + +(ert-deftest erc-nicks-customize-face () + (unless (>= emacs-major-version 28) + (ert-skip "Face link required in customize-face buffers")) + (erc-nicks-tests--create-session + (lambda () + (erc-nicks-list-faces) + (with-current-buffer "*Faces*" + (set-window-buffer (selected-window) (current-buffer)) + (goto-char (point-min)) + + (ert-info ("Clicking on face link runs action function") + (forward-button 1) + (should (looking-at "erc-nicks-alice2")) + (ert-simulate-keys "y\r" + (call-interactively #'push-button nil))) + + (with-current-buffer "*Customize Face: Erc Nicks Alice2@Foonet Face*" + (should (search-forward "Erc Nicks Alice2@Foonet Face" nil t)) + (widget-button-press (1- (point)))) + + (with-current-buffer "*New face erc-nicks-alice2@foonet-face*" + (goto-char (point-min)) + (should (search-forward "(use-package erc-nicks" nil t)) + (should (search-forward ":foreground \"#" nil t)) + (when noninteractive + (kill-buffer))) + + (with-current-buffer "*Customize Face: Erc Nicks Alice2@Foonet Face*" + (should (search-forward "Foreground: #" nil t)) + (when noninteractive + (kill-buffer))) + + (when noninteractive + (kill-buffer)))) + "Alice2" "Bob2")) + +(ert-deftest erc-nicks--gen-key-from-format-spec () + (let ((erc-network 'OFTC) + (erc-nicks-key-suffix-format "@%-012n") + (erc-server-current-nick "tester")) + (should (equal (erc-nicks--gen-key-from-format-spec "bob") + "bob@OFTC00000000"))) + + (let ((erc-network 'Libera.Chat) + (erc-nicks-key-suffix-format "@%-012n") + (erc-server-current-nick "tester")) + (should (equal (erc-nicks--gen-key-from-format-spec "bob") + "bob@Libera.Chat0"))) + + (let* ((erc-network 'Libera.Chat) + (erc-nicks-key-suffix-format "@%n/%m") + (erc-server-current-nick "tester")) + (should (equal (erc-nicks--gen-key-from-format-spec "bob") + "bob@Libera.Chat/tester")))) + +(ert-deftest erc-nicks--create-culled-pool () + (let ((erc-nicks--bg-luminance 1.0) + (erc-nicks--bg-mode-value 'light) + (erc-nicks--fg-rgb '(0.0 0.0 0.0)) + (erc-nicks-bg-color "white") + ;; + (erc-nicks--colors-rejects '(t))) + + ;; Reject + (should-not (erc-nicks--create-culled-pool '(erc-nicks-invert) '("white"))) + (should (equal (pop erc-nicks--colors-rejects) "white")) ; too close + (should-not + (erc-nicks--create-culled-pool '(erc-nicks-cap-contrast) '("black"))) + (should (equal (pop erc-nicks--colors-rejects) "black")) ; too far + (should-not + (erc-nicks--create-culled-pool '(erc-nicks-ensaturate) '("white"))) + (should (equal (pop erc-nicks--colors-rejects) "white")) ; lacks color + (should-not + (erc-nicks--create-culled-pool '(erc-nicks-ensaturate) '("red"))) + (should (equal (pop erc-nicks--colors-rejects) "red")) ; too much color + + ;; Safe + (should (equal (erc-nicks--create-culled-pool '(erc-nicks-invert) + '("black")) + '("black"))) + (should (equal (erc-nicks--create-culled-pool '(erc-nicks-add-contrast) + '("black")) + '("black"))) + (should (equal (erc-nicks--create-culled-pool '(erc-nicks-cap-contrast) + '("white")) + '("white"))) + (let ((erc-nicks-saturation-range '(0.5 . 1.0))) + (should (equal (erc-nicks--create-culled-pool '(erc-nicks-ensaturate) + '("green")) + '("green")))) + (let ((erc-nicks-saturation-range '(0.0 . 0.5))) + (should (equal (erc-nicks--create-culled-pool '(erc-nicks-ensaturate) + '("gray")) + '("gray")))) + (unless noninteractive + (should (equal (erc-nicks--create-culled-pool '(erc-nicks-ensaturate) + '("firebrick")) + '("firebrick")))) + (should (equal erc-nicks--colors-rejects '(t))))) + +(ert-deftest erc-nicks--create-coerced-pool () + (let ((erc-nicks--bg-luminance 1.0) + (erc-nicks--bg-mode-value 'light) + (erc-nicks--fg-rgb '(0.0 0.0 0.0)) + (erc-nicks-bg-color "white") + (num-colors (length (defined-colors))) + ;; + (erc-nicks--colors-rejects '(t))) + + ;; Deduplication. + (when (= 8 num-colors) + (should (equal (erc-nicks--create-coerced-pool '(erc-nicks-ensaturate) + '("#ee0000" "#f80000")) + '("red"))) + (should (equal (pop erc-nicks--colors-rejects) "#f80000"))) + + ;; "Coercion" in Xterm. + (unless noninteractive + (when (= 665 num-colors) + (pcase-dolist (`(,adjustments ,candidates ,result) + '(((erc-nicks-invert) ("white") ("gray10")) + ((erc-nicks-cap-contrast) ("black") ("gray20")) + ((erc-nicks-ensaturate) ("white") ("lavenderblush2")) + ((erc-nicks-ensaturate) ("red") ("firebrick")))) + (should (equal (erc-nicks--create-coerced-pool adjustments + candidates) + result))))) + + (should (equal erc-nicks--colors-rejects '(t))))) + +;;; erc-nicks-tests.el ends here diff --git a/test/lisp/erc/erc-scenarios-auth-source.el b/test/lisp/erc/erc-scenarios-auth-source.el index b25acf2fbd8..f0a7a4cbaca 100644 --- a/test/lisp/erc/erc-scenarios-auth-source.el +++ b/test/lisp/erc/erc-scenarios-auth-source.el @@ -56,7 +56,7 @@ (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)))))) + (erc-d-t-wait-for 10 (eq erc-network 'FooNet)))))) (ert-deftest erc-scenarios-base-auth-source-server--dialed () :tags '(:expensive-test) diff --git a/test/lisp/erc/erc-scenarios-base-association.el b/test/lisp/erc/erc-scenarios-base-association.el index 07b71c3ac14..deac0e0cac7 100644 --- a/test/lisp/erc/erc-scenarios-base-association.el +++ b/test/lisp/erc/erc-scenarios-base-association.el @@ -78,7 +78,7 @@ (with-current-buffer "#chan@foonet" (funcall expect 3 "bob") (funcall expect 3 "was created on") - (funcall expect 3 "prosperous"))) + (funcall expect 10 "prosperous"))) (ert-info ("All #chan@barnet output consumed") (with-current-buffer "#chan@barnet" diff --git a/test/lisp/erc/erc-scenarios-base-attach.el b/test/lisp/erc/erc-scenarios-base-attach.el new file mode 100644 index 00000000000..29f5bd2ddd8 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-attach.el @@ -0,0 +1,191 @@ +;;; erc-scenarios-base-attach.el --- Reattach scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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: + +;; See also: `erc-scenarios-base-channel-buffer-revival'. +;; +;; ERC 5.5 silently dropped support for the ancient option +;; `erc-query-on-unjoined-chan-privmsg' because the tangled logic in +;; and around the function `erc-auto-query' made it difficult to +;; divine its purpose. +;; +;; Based on the name, it was thought this option likely involved +;; controlling the creation of query buffers for unsolicited messages +;; from users with whom you don't share a common channel. However, +;; additional spelunking has recently revealed that it was instead +;; meant to service a feature offered by most bouncers that sends +;; PRIVMSGs directed at a channel you're no longer in and that you +;; haven't received a(nother) JOIN message for. IOW, this is meant to +;; support the following sequence of events: +;; +;; 1. /detach #chan +;; 2. kill buffer #chan or reconnect in new Emacs session +;; 3. /playbuffer #chan +;; +;; Note that the above slash commands are bouncer-specific aliases. +;; +;; Interested users can find more info by looking at this change set +;; from the ancient CVS repo: +;; +;; Author: Mario Lang <mlang@delysid.org> +;; AuthorDate: Mon Nov 26 18:33:19 2001 +0000 +;; +;; * new function erc-BBDB-NICK to handle nickname annotation ... +;; * Applied antifuchs/mhp patches, the latest on erc-help, unmodified +;; * New variable: erc-reuse-buffers default to t. +;; * Modified erc-generate-new-buffer-name to use it. it checks if +;; server and port are the same, then one can assume that's the same +;; channel/query target again. + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--enabled () + :tags '(:expensive-test) + (should erc-ensure-target-buffer-on-privmsg) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/channel-buffer-revival") + (dumb-server (erc-d-run "localhost" t 'reattach)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (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 "tester@vanilla/foonet:changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet")) + (erc-cmd-MSG "*status playbuffer #chan")) + + (ert-info ("Playback appears in buffer #chan") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 10 "Buffer Playback...") + (funcall expect 10 "Was I a child") + (funcall expect 10 "Thou counterfeit'st most lively") + (funcall expect 10 "Playback Complete"))) + + (with-current-buffer "foonet" + (erc-cmd-MSG "*status attach #chan")) + + (ert-info ("Live output from #chan after more playback") + (with-current-buffer "#chan" + (funcall expect 10 "Buffer Playback...") + (funcall expect 10 "With what it loathes") + (funcall expect 10 "Not by his breath") + (funcall expect 10 "Playback Complete") + (funcall expect 10 "Ay, and the captain") + (erc-scenarios-common-say "bob: hi") + (funcall expect 10 "Pawn me to this"))))) + +(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--disabled () + :tags '(:expensive-test) + (should erc-ensure-target-buffer-on-privmsg) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/channel-buffer-revival") + (dumb-server (erc-d-run "localhost" t 'reattach)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-ensure-target-buffer-on-privmsg nil) ; off + (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 "tester@vanilla/foonet:changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet")) + (erc-cmd-MSG "*status playbuffer #chan") + (ert-info ("Playback appears in buffer server buffer") + (erc-d-t-ensure-for -1 (not (get-buffer "#chan"))) + (funcall expect 10 "Buffer Playback...") + (funcall expect 10 "Was I a child") + (funcall expect 10 "Thou counterfeit'st most lively") + (funcall expect 10 "Playback Complete")) + (should-not (get-buffer "#chan")) + (erc-cmd-MSG "*status attach #chan")) + + (ert-info ("Buffer #chan joined") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 10 "Buffer Playback...") + (funcall expect 10 "With what it loathes") + (funcall expect 10 "Not by his breath") + (funcall expect 10 "Playback Complete") + (funcall expect 10 "Ay, and the captain") + (erc-scenarios-common-say "bob: hi") + (funcall expect 10 "Pawn me to this"))))) + + +;; We omit the `enabled' case for queries because it's the default for +;; this option and already covered many times over by other tests in +;; this directory. + +(ert-deftest erc-scenarios-base-attach--ensure-target-buffer--disabled-query () + :tags '(:expensive-test) + (should erc-ensure-target-buffer-on-privmsg) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/queries") + (dumb-server (erc-d-run "localhost" t 'non-erc)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter)) + (erc-ensure-target-buffer-on-privmsg nil) + (erc-server-flood-penalty 0.1)) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :full-name "tester") + (erc-scenarios-common-assert-initial-buf-name nil port) + (erc-d-t-wait-for 5 (eq erc-network 'foonet)) + (funcall expect 15 "debug mode"))) + + (ert-info ("User dummy's greeting appears in server buffer") + (erc-d-t-wait-for -1 (get-buffer "dummy")) + (with-current-buffer "foonet" + (funcall expect 5 "hi") + + (ert-info ("Option being nil doesn't queries we create") + (with-current-buffer (erc-cmd-QUERY "nitwit") + (should (equal (buffer-name) "nitwit")) + (erc-scenarios-common-say "hola") + (funcall expect 5 "ciao"))) + + (erc-scenarios-common-say "howdy") + (funcall expect 5 "no target") + (erc-cmd-MSG "dummy howdy") + (funcall expect 5 "bye") + (erc-cmd-QUIT ""))))) + +;;; erc-scenarios-base-attach.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-auto-recon.el b/test/lisp/erc/erc-scenarios-base-auto-recon.el new file mode 100644 index 00000000000..40e2c23408b --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-auto-recon.el @@ -0,0 +1,141 @@ +;;; erc-scenarios-base-auto-recon.el --- auto-recon scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(defun erc-scenarios-base-auto-recon--get-unused-port () + (let ((server (make-network-process :name "*erc-scenarios-base-auto-recon*" + :host "localhost" + :service t + :server t))) + (delete-process server) + (process-contact server :service))) + +;; This demos one possible flavor of intermittent service. +;; It may end up needing to be marked :unstable. + +(ert-deftest erc-scenarios-base-auto-recon-unavailable () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-server-flood-penalty 0.1) + (port (erc-scenarios-base-auto-recon--get-unused-port)) + (erc--server-reconnect-timeout-scale-function (lambda (_) 1)) + (erc-server-auto-reconnect t) + (erc-server-reconnect-function #'erc-server-delayed-check-reconnect) + (expect (erc-d-t-make-expecter)) + (erc-scenarios-common-dialog "base/reconnect") + (dumb-server nil)) + + (ert-info ("Dialing fails: nobody home") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (erc-d-t-wait-for 10 (not (erc-server-process-alive))) + (erc-d-t-wait-for 10 erc--server-reconnect-timer) + (funcall expect 10 "Opening connection") + (funcall expect 10 "failed") + + (ert-info ("Reconnect function freezes attempts at 1") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (funcall expect 10 "nobody home") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (funcall expect 10 "nobody home")))) + + (ert-info ("Service appears") + (setq dumb-server (erc-d-run "localhost" port + 'just-eof 'unexpected-disconnect)) + (with-current-buffer (format "127.0.0.1:%d" port) + (funcall expect 10 "server is in debug mode") + (should (equal (buffer-name) "FooNet")))) + + (ert-info ("Service interrupted, reconnect starts again") + (with-current-buffer "FooNet" + (funcall expect 10 "failed") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")))) + + (ert-info ("Service restored") + (delete-process dumb-server) + (setq dumb-server (erc-d-run "localhost" port + 'just-eof 'unexpected-disconnect)) + (with-current-buffer "FooNet" + (funcall expect 10 "server is in debug mode"))) + + (ert-info ("Service interrupted a third time, reconnect starts yet again") + (with-current-buffer "FooNet" + (funcall expect 10 "failed") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (erc-cmd-RECONNECT "cancel") + (funcall expect 10 "canceled"))))) + +;; In this test, a listener accepts but doesn't respond to any messages. + +(ert-deftest erc-scenarios-base-auto-recon-no-proto () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-server-flood-penalty 0.1) + (erc-scenarios-common-dialog "base/reconnect") + (erc-d-auto-pong nil) + (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect)) + (port (process-contact dumb-server :service)) + (erc--server-reconnect-timeout-scale-function (lambda (_) 1)) + (erc--server-reconnect-timeout-check 0.5) + (erc-server-auto-reconnect t) + (erc-server-reconnect-function #'erc-server-delayed-check-reconnect) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Session succeeds but cut short") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "server is in debug mode") + (should (equal (buffer-name) "FooNet")) + (erc-d-t-wait-for 10 erc--server-reconnect-timer) + (delete-process dumb-server) + (funcall expect 10 "failed") + + (ert-info ("Reconnect function freezes attempts at 1") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (funcall expect 10 "nobody home") + (funcall expect 10 "timed out while dialing") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (funcall expect 10 "nobody home")))) + + (ert-info ("Service restored") + (setq dumb-server (erc-d-run "localhost" port + 'just-ping + 'ping-pong + 'unexpected-disconnect)) + (with-current-buffer "FooNet" + (funcall expect 30 "server is in debug mode"))) + + (ert-info ("Service interrupted again, reconnect starts again") + (with-current-buffer "FooNet" + (funcall expect 10 "failed") + (funcall expect 10 '(: "reconnecting" (+ nonl) "attempt 1/2")) + (erc-cmd-RECONNECT "cancel") + (funcall expect 10 "canceled"))))) + +;;; erc-scenarios-base-auto-recon.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-buffer-display.el b/test/lisp/erc/erc-scenarios-base-buffer-display.el new file mode 100644 index 00000000000..889f274b8b1 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-buffer-display.el @@ -0,0 +1,249 @@ +;;; erc-scenarios-base-buffer-display.el --- Buffer display scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile (require 'erc-join)) + +;; These first couple `erc-auto-reconnect-display' tests used to live +;; in erc-scenarios-base-reconnect but have since been renamed. Note +;; that these are somewhat difficult to reason about because the user +;; joins a second channel after reconnecting, and the first is +;; controlled by `autojoin'. + +(defun erc-scenarios-base-buffer-display--reconnect-common + (assert-server assert-chan assert-rest) + (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) + + (should (memq 'autojoin erc-modules)) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (funcall assert-server expect) + (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 assert-chan expect) + (funcall expect 10 "welcome") + (funcall expect 10 "welcome"))) + + (ert-info ("Server buffer shows connection failed") + (with-current-buffer "FooNet" + (funcall expect 10 "Connection failed! Re-establishing"))) + + (should (equal erc-autojoin-channels-alist '((FooNet "#chan")))) + (delete-other-windows) + (pop-to-buffer-same-window "*Messages*") + + (ert-info ("Wait for auto reconnect") + (with-current-buffer "FooNet" (funcall expect 10 "still in debug mode"))) + + (ert-info ("Lone window still shows messages buffer") + (should (eq (window-buffer) (messages-buffer))) + (should (frame-root-window-p (selected-window)))) + + (funcall assert-rest expect) + + (ert-info ("Wait for activity to recommence in both 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"))))) + +;; Interactively issuing a slash command resets the auto-reconnect +;; count, making ERC ignore the option `erc-auto-reconnect-display' +;; when next displaying a newly set up buffer. In the case of a +;; /JOIN, the option `erc-interactive-display' takes precedence. +(ert-deftest erc-scenarios-base-buffer-display--defwin-recbury-intbuf () + :tags '(:expensive-test) + (should (eq erc-buffer-display 'bury)) + (should (eq erc-interactive-display 'window)) + (should-not erc-auto-reconnect-display) + + (let ((erc-buffer-display 'window) ; defwin + (erc-interactive-display 'buffer) ; intbuf + (erc-auto-reconnect-display 'bury)) ; recbury + + (erc-scenarios-base-buffer-display--reconnect-common + + (lambda (_) + (ert-info ("New server buffer appears in a selected split") + (should (eq (window-buffer) (current-buffer))) + (should-not (frame-root-window-p (selected-window))))) + + (lambda (_) + (ert-info ("New channel buffer appears in other window") + (should (eq (window-buffer) (current-buffer))) ; selected + (should (equal (get-buffer "FooNet") (window-buffer (next-window)))))) + + (lambda (expect) + ;; If we /JOIN #spam now, we'll cancel the auto-reconnect + ;; timer, and "#chan" may well pop up in a split before we can + ;; verify that the lone window displays #spam (a race, IOW). + (ert-info ("Autojoined channel #chan buried on JOIN") + (with-current-buffer "#chan" + (funcall expect 10 "You have joined channel #chan")) + (should (frame-root-window-p (selected-window))) + (should (eq (window-buffer) (messages-buffer)))) + + (with-current-buffer "FooNet" (erc-scenarios-common-say "/JOIN #spam")) + + (ert-info ("A /JOIN ignores `erc-auto-reconnect-display'") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam")) + (should (eq (window-buffer) (get-buffer "#spam"))) + ;; Option `erc-interactive-display' being `buffer' means + ;; Emacs reuses the selected window (no split). + (should (frame-root-window-p (selected-window))))))))) + +(ert-deftest erc-scenarios-base-buffer-display--defwino-recbury-intbuf () + :tags '(:expensive-test) + (should (eq erc-buffer-display 'bury)) + (should (eq erc-interactive-display 'window)) + (should-not erc-auto-reconnect-display) + + (let ((erc-buffer-display 'window-noselect) ; defwino + (erc-auto-reconnect-display 'bury) + (erc-interactive-display 'buffer)) + (erc-scenarios-base-buffer-display--reconnect-common + + (lambda (_) + ;; Selected window shows some non-ERC buffer. New server + ;; buffer appears in another window (other side of split). + (should-not (frame-root-window-p (selected-window))) + (should-not (eq (window-buffer) (current-buffer))) + (with-current-buffer (window-buffer) + (should-not (derived-mode-p 'erc-mode))) + (should (eq (current-buffer) (window-buffer (next-window))))) + + (lambda (_) + (should-not (frame-root-window-p (selected-window))) + ;; Current split likely shows scratch. + (with-current-buffer (window-buffer) + (should-not (derived-mode-p 'erc-mode))) + (should (eq (current-buffer) (window-buffer (next-window))))) + + (lambda (_) + ;; A JOIN command sent from lisp code is "non-interactive" and + ;; doesn't reset the auto-reconnect count, so ERC treats the + ;; response as possibly server-initiated or otherwise the + ;; result of an autojoin and continues to favor + ;; `erc-auto-reconnect-display'. + (ert-info ("Join chan non-interactively and open a /QUERY") + (with-current-buffer "FooNet" + (erc-cmd-JOIN "#spam") ; "non-interactive" according to ERC + (erc-scenarios-common-say "/QUERY bob") ; resets count + (should (eq (window-buffer) (get-buffer "bob"))) + (should (frame-root-window-p (selected-window))))) + + ;; The /QUERY above resets the count, and `erc-buffer-display' + ;; again decides how #spam is displayed. + (ert-info ("Newly joined chan ignores `erc-auto-reconnect-display'") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam")) + (should (eq (window-buffer) (get-buffer "bob"))) + (should-not (frame-root-window-p (selected-window))) ; noselect + (should (eq (current-buffer) (window-buffer (next-window)))))))))) + +(ert-deftest erc-scenarios-base-buffer-display--count-reset-timeout () + :tags '(:expensive-test) + (should (eq erc-buffer-display 'bury)) + (should (eq erc-interactive-display 'window)) + (should (eq erc-auto-reconnect-display-timeout 10)) + (should-not erc-auto-reconnect-display) + + (let ((erc-buffer-display 'window-noselect) + (erc-auto-reconnect-display 'bury) + (erc-interactive-display 'buffer) + (erc-auto-reconnect-display-timeout 0.5)) + (erc-scenarios-base-buffer-display--reconnect-common + #'ignore #'ignore ; These two are identical to the previous test. + + (lambda (_) + (with-current-buffer "FooNet" + (erc-d-t-wait-for 1 erc--server-reconnect-display-timer)) + + ;; A non-interactive JOIN command doesn't signal that we're + ;; done auto-reconnecting. + (ert-info ("Join channel #spam non-interactively") + (with-current-buffer "FooNet" + (erc-d-t-wait-for 1 (null erc--server-reconnect-display-timer)) + (erc-cmd-JOIN "#spam"))) ; not processed as a /JOIN + + (ert-info ("Option `erc-auto-reconnect-display' ignored w/o timer") + (should (eq (window-buffer) (messages-buffer))) + (erc-d-t-wait-for 10 (get-buffer "#spam")) + ;; If `erc-auto-reconnect-display-timeout' were left alone, + ;; this would be (frame-root-window-p #<window 1 on scratch*>). + (should-not (frame-root-window-p (selected-window))) + (should (eq (get-buffer "#spam") (window-buffer (next-window))))))))) + +;; This shows that the option `erc-interactive-display' overrides +;; `erc-join-buffer' during cold opens and interactive /JOINs. + +(ert-deftest erc-scenarios-base-buffer-display--interactive-default () + :tags '(:expensive-test) + (should (eq erc-join-buffer 'bury)) + (should (eq erc-interactive-display 'window)) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "join/legacy") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service)) + (url (format "tester:changeme@127.0.0.1:%d\r\r" port)) + (expect (erc-d-t-make-expecter)) + (erc-server-flood-penalty 0.1) + (erc-server-auto-reconnect t) + (erc-user-full-name "tester")) + + (ert-info ("Connect to foonet") + (with-current-buffer (let (inhibit-interaction) + (ert-simulate-keys url + (call-interactively #'erc))) + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + + (erc-d-t-wait-for 10 "Server buffer shown" + (eq (window-buffer) (current-buffer))) + (funcall expect 10 "debug mode") + (erc-scenarios-common-say "/JOIN #chan"))) + + (ert-info ("Wait for output in #chan") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 10 "welcome") + (erc-d-t-ensure-for 3 "Channel #chan shown" + (eq (window-buffer) (current-buffer))) + (funcall expect 10 "be prosperous"))))) + +;;; erc-scenarios-base-buffer-display.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-chan-modes.el b/test/lisp/erc/erc-scenarios-base-chan-modes.el new file mode 100644 index 00000000000..9c63d8aff8e --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-chan-modes.el @@ -0,0 +1,84 @@ +;;; erc-scenarios-base-chan-modes.el --- Channel mode scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +;; This asserts that a bug present in ERC 5.4+ is now absent. +;; Previously, ERC would attempt to parse a nullary channel mode as if +;; it were a status prefix update, which led to a wrong-type error. +;; This test does not address similar collisions with unary modes, +;; such as "MODE +q foo!*@*", but it should. +(ert-deftest erc-scenarios-base-chan-modes--plus-q () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/modes") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'chan-changed)) + (erc-modules (cons 'fill-wrap erc-modules)) + (erc-autojoin-channels-alist '((Libera.Chat "#chan"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to Libera.Chat") + (with-current-buffer (erc :server "127.0.0.1" + :port (process-contact dumb-server :service) + :nick "tester" + :full-name "tester") + (funcall expect 5 "changed mode"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (should-not erc-channel-key) + (should-not erc-channel-user-limit) + + (ert-info ("Receive notice that mode has changed") + (erc-d-t-wait-for 10 (equal erc-channel-modes '("n" "t"))) + (erc-scenarios-common-say "ready before") + (funcall expect 10 "<Chad> before") + (funcall expect 10 " has changed mode for #chan to +Qu") + (erc-d-t-wait-for 10 (equal erc-channel-modes '("Q" "n" "t" "u")))) + + (ert-info ("Key stored locally") + (erc-scenarios-common-say "ready key") + (funcall expect 10 "<Chad> doing key") + (funcall expect 10 " has changed mode for #chan to +k hunter2") + (should (equal erc-channel-key "hunter2"))) + + (ert-info ("Limit stored locally") + (erc-scenarios-common-say "ready limit") + (funcall expect 10 "<Chad> doing limit") + (funcall expect 10 " has changed mode for #chan to +l 3") + (erc-d-t-wait-for 10 (eql erc-channel-user-limit 3)) + (should (equal erc-channel-modes '("Q" "n" "t" "u")))) + + (ert-info ("Modes removed and local state deletion succeeds") + (erc-scenarios-common-say "ready drop") + (funcall expect 10 "<Chad> dropping") + (funcall expect 10 " has changed mode for #chan to -lu") + (funcall expect 10 " has changed mode for #chan to -Qk *") + (erc-d-t-wait-for 10 (equal erc-channel-modes '("n" "t")))) + + (should-not erc-channel-key) + (should-not erc-channel-user-limit) + (funcall expect 10 "<Chad> after")))) + +;;; erc-scenarios-base-chan-modes.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-local-module-modes.el b/test/lisp/erc/erc-scenarios-base-local-module-modes.el new file mode 100644 index 00000000000..7b91e28dc83 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-local-module-modes.el @@ -0,0 +1,211 @@ +;;; erc-scenarios-base-local-module-modes.el --- More local-mod ERC tests -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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: + +;; A local module doubles as a minor mode whose mode variable and +;; associated local data can withstand service disruptions. +;; Unfortunately, the current implementation is too unwieldy to be +;; made public because it doesn't perform any of the boiler plate +;; needed to save and restore buffer-local and "network-local" copies +;; of user options. Ultimately, a user-friendly framework must fill +;; this void if third-party local modules are ever to become +;; practical. +;; +;; The following tests all use `sasl' because, as of ERC 5.5, it's the +;; only local module. + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-sasl) + +;; After quitting a session for which `sasl' is enabled, you +;; disconnect and toggle `erc-sasl-mode' off. You then reconnect +;; using an alternate nickname. You again disconnect and reconnect, +;; this time immediately, and the mode stays disabled. Finally, you +;; once again disconnect, toggle the mode back on, and reconnect. You +;; are authenticated successfully, just like in the initial session. +;; +;; This is meant to show that a user's local mode settings persist +;; between sessions. It also happens to show (in round four, below) +;; that a server renicking a user on 001 after a 903 is handled just +;; like a user-initiated renick, although this is not the main thrust. + +(ert-deftest erc-scenarios-base-local-module-modes--reconnect () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/local-modules") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'first 'second 'third 'fourth)) + (port (process-contact dumb-server :service)) + (erc-modules (cons 'sasl erc-modules)) + (expect (erc-d-t-make-expecter)) + (server-buffer-name (format "127.0.0.1:%d" port))) + + (ert-info ("Round one, initial authentication succeeds as expected") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) server-buffer-name)) + (funcall expect 10 "You are now logged in as tester")) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet")) + (funcall expect 10 "This server is in debug mode") + (erc-cmd-JOIN "#chan") + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 20 "She is Lavinia, therefore must")) + + (erc-cmd-QUIT "") + (funcall expect 10 "finished"))) + + (ert-info ("Round two, nick rejected, alternate granted") + (with-current-buffer "foonet" + + (ert-info ("Toggle mode off, reconnect") + (erc-sasl-mode -1) + (erc-cmd-RECONNECT)) + + (funcall expect 10 "User modes for tester`") + (should-not (cdr (erc-scenarios-common-buflist "foonet"))) + (should (equal (buffer-name) "foonet")) + (should-not (cdr (erc-scenarios-common-buflist "#chan"))) + + (with-current-buffer "#chan" + (funcall expect 10 "Some enigma, some riddle")) + + (erc-cmd-QUIT "") + (funcall expect 10 "finished"))) + + (ert-info ("Round three, send alternate nick initially") + (with-current-buffer "foonet" + + (ert-info ("Keep mode off, reconnect") + (should-not erc-sasl-mode) + (should (local-variable-p 'erc-sasl-mode)) + (erc-cmd-RECONNECT)) + + (funcall expect 10 "User modes for tester`") + (should-not (cdr (erc-scenarios-common-buflist "foonet"))) + (should (equal (buffer-name) "foonet")) + (should-not (cdr (erc-scenarios-common-buflist "#chan"))) + + (with-current-buffer "#chan" + (funcall expect 10 "Let our reciprocal vows be remembered.")) + + (erc-cmd-QUIT "") + (funcall expect 10 "finished"))) + + (ert-info ("Round four, authenticated successfully again") + (with-current-buffer "foonet" + + (ert-info ("Toggle mode on, reconnect") + (should-not erc-sasl-mode) + (should (local-variable-p 'erc-sasl-mode)) + (erc-sasl-mode +1) + (erc-cmd-RECONNECT)) + + (funcall expect 10 "User modes for tester") + (should-not (cdr (erc-scenarios-common-buflist "foonet"))) + (should (equal (buffer-name) "foonet")) + (should-not (cdr (erc-scenarios-common-buflist "#chan"))) + + (with-current-buffer "#chan" + (funcall expect 10 "Well met; good morrow, Titus and Hortensius.")) + + (erc-cmd-QUIT ""))))) + +;; In contrast to the mode-persistence test above, this one +;; demonstrates that a user reinvoking an entry point declares their +;; intention to reset local-module state for the server buffer. +;; Whether a local-module's state variable is also reset in target +;; buffers up to the module. That is, by default, they're left alone. + +(ert-deftest erc-scenarios-base-local-module-modes--entrypoint () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/local-modules") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'first 'first)) + (port (process-contact dumb-server :service)) + (erc-modules (cons 'sasl erc-modules)) + (expect (erc-d-t-make-expecter)) + (server-buffer-name (format "127.0.0.1:%d" port))) + + (ert-info ("Round one, initial authentication succeeds as expected") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) server-buffer-name)) + (funcall expect 10 "You are now logged in as tester")) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet")) + (funcall expect 10 "This server is in debug mode") + (erc-cmd-JOIN "#chan") + + (ert-info ("Toggle local-module off in target buffer") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 20 "She is Lavinia, therefore must") + (erc-sasl-mode -1))) + + (erc-cmd-QUIT "") + (funcall expect 10 "finished") + + (ert-info ("Toggle mode off") + (erc-sasl-mode -1) + (should (local-variable-p 'erc-sasl-mode))))) + + (ert-info ("Reconnecting via entry point discards `erc-sasl-mode' value.") + ;; If you were to /RECONNECT here, no PASS changeme would be + ;; sent instead of CAP SASL, resulting in a failure. + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :password "changeme" + :full-name "tester") + (should (string= (buffer-name) server-buffer-name)) + (funcall expect 10 "You are now logged in as tester") + + (erc-d-t-wait-for 10 (equal (buffer-name) "foonet")) + (funcall expect 10 "User modes for tester") + (should erc-sasl-mode)) ; obviously + + ;; No other foonet buffer exists, e.g., foonet<2> + (should-not (cdr (erc-scenarios-common-buflist "foonet"))) + + (ert-info ("Target buffer retains local-module state") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 20 "She is Lavinia, therefore must") + (should-not erc-sasl-mode) + (should (local-variable-p 'erc-sasl-mode)) + (erc-cmd-QUIT "")))))) + +;;; erc-scenarios-base-local-module-modes.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-local-modules.el b/test/lisp/erc/erc-scenarios-base-local-modules.el index 6a48da0d574..9604c6ea17c 100644 --- a/test/lisp/erc/erc-scenarios-base-local-modules.el +++ b/test/lisp/erc/erc-scenarios-base-local-modules.el @@ -82,105 +82,6 @@ (erc-cmd-QUIT "") (funcall expect 10 "finished"))))) -;; After quitting a session for which `sasl' is enabled, you -;; disconnect and toggle `erc-sasl-mode' off. You then reconnect -;; using an alternate nickname. You again disconnect and reconnect, -;; this time immediately, and the mode stays disabled. Finally, you -;; once again disconnect, toggle the mode back on, and reconnect. You -;; are authenticated successfully, just like in the initial session. -;; -;; This is meant to show that a user's local mode settings persist -;; between sessions. It also happens to show (in round four, below) -;; that a server renicking a user on 001 after a 903 is handled just -;; like a user-initiated renick, although this is not the main thrust. - -(ert-deftest erc-scenarios-base-local-modules--mode-persistence () - :tags '(:expensive-test) - (erc-scenarios-common-with-cleanup - ((erc-scenarios-common-dialog "base/local-modules") - (erc-server-flood-penalty 0.1) - (dumb-server (erc-d-run "localhost" t 'first 'second 'third 'fourth)) - (port (process-contact dumb-server :service)) - (erc-modules (cons 'sasl erc-modules)) - (expect (erc-d-t-make-expecter)) - (server-buffer-name (format "127.0.0.1:%d" port))) - - (ert-info ("Round one, initial authentication succeeds as expected") - (with-current-buffer (erc :server "127.0.0.1" - :port port - :nick "tester" - :user "tester" - :password "changeme" - :full-name "tester") - (should (string= (buffer-name) server-buffer-name)) - (funcall expect 10 "You are now logged in as tester")) - - (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "foonet")) - (funcall expect 10 "This server is in debug mode") - (erc-cmd-JOIN "#chan") - - (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) - (funcall expect 20 "She is Lavinia, therefore must")) - - (erc-cmd-QUIT "") - (funcall expect 10 "finished"))) - - (ert-info ("Round two, nick rejected, alternate granted") - (with-current-buffer "foonet" - - (ert-info ("Toggle mode off, reconnect") - (erc-sasl-mode -1) - (erc-cmd-RECONNECT)) - - (funcall expect 10 "User modes for tester`") - (should-not (cdr (erc-scenarios-common-buflist "foonet"))) - (should (equal (buffer-name) "foonet")) - (should-not (cdr (erc-scenarios-common-buflist "#chan"))) - - (with-current-buffer "#chan" - (funcall expect 10 "Some enigma, some riddle")) - - (erc-cmd-QUIT "") - (funcall expect 10 "finished"))) - - (ert-info ("Round three, send alternate nick initially") - (with-current-buffer "foonet" - - (ert-info ("Keep mode off, reconnect") - (should-not erc-sasl-mode) - (should (local-variable-p 'erc-sasl-mode)) - (erc-cmd-RECONNECT)) - - (funcall expect 10 "User modes for tester`") - (should-not (cdr (erc-scenarios-common-buflist "foonet"))) - (should (equal (buffer-name) "foonet")) - (should-not (cdr (erc-scenarios-common-buflist "#chan"))) - - (with-current-buffer "#chan" - (funcall expect 10 "Let our reciprocal vows be remembered.")) - - (erc-cmd-QUIT "") - (funcall expect 10 "finished"))) - - (ert-info ("Round four, authenticated successfully again") - (with-current-buffer "foonet" - - (ert-info ("Toggle mode on, reconnect") - (should-not erc-sasl-mode) - (should (local-variable-p 'erc-sasl-mode)) - (erc-sasl-mode +1) - (erc-cmd-RECONNECT)) - - (funcall expect 10 "User modes for tester") - (should-not (cdr (erc-scenarios-common-buflist "foonet"))) - (should (equal (buffer-name) "foonet")) - (should-not (cdr (erc-scenarios-common-buflist "#chan"))) - - (with-current-buffer "#chan" - (funcall expect 10 "Well met; good morrow, Titus and Hortensius.")) - - (erc-cmd-QUIT ""))))) - ;; For local modules, the twin toggle commands `erc-FOO-enable' and ;; `erc-FOO-disable' affect all buffers of a connection, whereas ;; `erc-FOO-mode' continues to operate only on the current buffer. diff --git a/test/lisp/erc/erc-scenarios-base-misc-regressions.el b/test/lisp/erc/erc-scenarios-base-misc-regressions.el index 7a16b8f57c1..df2aa8e82ec 100644 --- a/test/lisp/erc/erc-scenarios-base-misc-regressions.el +++ b/test/lisp/erc/erc-scenarios-base-misc-regressions.el @@ -77,7 +77,7 @@ Originally from scenario rebuffed/gapless as explained in Bug#48598: (with-current-buffer (erc-d-t-wait-for 20 (get-buffer "#bar")) (funcall expect 10 "was created on") - (funcall expect 2 "his second fit")) + (funcall expect 10 "his second fit")) (with-current-buffer (erc-d-t-wait-for 20 (get-buffer "#foo")) (funcall expect 10 "was created on") @@ -108,7 +108,7 @@ Originally from scenario rebuffed/gapless as explained in Bug#48598: (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")) + (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"))) diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el b/test/lisp/erc/erc-scenarios-base-reconnect.el index 04aa3802259..6f968b9fcbc 100644 --- a/test/lisp/erc/erc-scenarios-base-reconnect.el +++ b/test/lisp/erc/erc-scenarios-base-reconnect.el @@ -65,95 +65,6 @@ (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 @@ -260,7 +171,7 @@ (funcall expect 2 "Canceled") (funcall expect 3 "Opening connection") (funcall expect 2 "Password incorrect") - (funcall expect 2 "Connection failed!") + (funcall expect 10 "Connection failed!") (funcall expect 2 "Re-establishing connection")) (ert-info ("Explicitly cancel timer") (erc-cmd-RECONNECT "cancel") diff --git a/test/lisp/erc/erc-scenarios-base-renick.el b/test/lisp/erc/erc-scenarios-base-renick.el index d454c8ce3a5..ca22728b152 100644 --- a/test/lisp/erc/erc-scenarios-base-renick.el +++ b/test/lisp/erc/erc-scenarios-base-renick.el @@ -173,7 +173,7 @@ (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")) + (erc-d-t-wait-for 10 (get-buffer "foonet")) (ert-info ("Joined by bouncer to #foo, pal persent") (with-current-buffer (erc-d-t-wait-for 1 (get-buffer "#foo")) @@ -267,7 +267,7 @@ (ert-info ("Sync convo for rando@foonet") (with-current-buffer "rando@foonet" - (funcall expect 1 "u are dumb") + (funcall expect 10 "u are dumb") (erc-scenarios-common-say "not so"))) (ert-info ("Sync convo for rando@barnet") @@ -275,8 +275,8 @@ (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")) + (erc-d-t-wait-for 10 (get-buffer "frenemy@foonet")) + (erc-d-t-wait-for 10 (get-buffer "frenemy@barnet")) (should-not (get-buffer "rando@foonet")) (should-not (get-buffer "rando@barnet")) diff --git a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el index 6d6fe0536f5..f07b7024bf3 100644 --- a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el +++ b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el @@ -124,6 +124,7 @@ Adapted from scenario clash-of-chans/uniquify described in Bug#48598: (erc-d-t-search-for 1 "shake my sword") (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"))) (ert-info ("Part #chan@barnet") @@ -139,6 +140,7 @@ Adapted from scenario clash-of-chans/uniquify described in Bug#48598: (get-buffer "#chan/127.0.0.1<3>")) (ert-info ("Activity continues in new, <n>-suffixed #chan@foonet buffer") + ;; The first /JOIN did not cause the same buffer to be reused. (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>" diff --git a/test/lisp/erc/erc-scenarios-base-send-message.el b/test/lisp/erc/erc-scenarios-base-send-message.el new file mode 100644 index 00000000000..bf9e0f5ae3a --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-send-message.el @@ -0,0 +1,126 @@ +;;; erc-scenarios-base-send-message.el --- `send-message' scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2022-2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +;; So-called "noncommands" are those that massage input submitted at +;; the prompt and send it on behalf of the user. + +(ert-deftest erc-scenarios-base-send-message--noncommands () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/send-message") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'noncommands)) + (erc-modules (cons 'fill-wrap erc-modules)) + (erc-autojoin-channels-alist '((foonet "#chan"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port (process-contact dumb-server :service) + :nick "tester" + :full-name "tester") + (funcall expect 5 "debug mode"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (ert-info ("Send CTCP ACTION") + (funcall expect 10 "<bob> alice: For hands, to do Rome") + (erc-scenarios-common-say "/me sad") + (funcall expect 10 "* tester sad")) + + (ert-info ("Send literal command") + (funcall expect 10 "<alice> bob: Spotted, detested") + (erc-scenarios-common-say "/say /me sad") + (funcall expect 10 "<tester> /me sad")) + + (ert-info ("\"Nested\" `noncommands'") + + (ert-info ("Send version via /SV") + (funcall expect 10 "<bob> Marcus, my brother!") + (erc-scenarios-common-say "/sv") + (funcall expect 10 "<tester> I'm using ERC")) + + (ert-info ("Send module list via /SM") + (funcall expect 10 "<bob> alice: You still wrangle") + (erc-scenarios-common-say "/sm") + (funcall expect 10 "<tester> I'm using the following modules: ") + (funcall expect 10 "<alice> No, not till Thursday;")))))) + + +;; This asserts that the `command-indicator' module only inserts +;; prompt-like prefixes for normal slash commands, like /JOIN. + +(ert-deftest erc-scenarios-base-send-message--command-indicator () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/send-message") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'noncommands)) + (erc-modules `(command-indicator fill-wrap ,@erc-modules)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port (process-contact dumb-server :service) + :nick "tester" + :full-name "tester") + (funcall expect 5 "debug mode") + (erc-scenarios-common-say "/join #chan") + (funcall expect 10 "ERC> /join #chan"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (ert-info ("Prompt absent for CTCP ACTION") + (funcall expect 10 "<bob> alice: For hands, to do Rome") + (erc-scenarios-common-say "/me sad") + (funcall expect -0.1 "ERC> /me sad") + (funcall expect 10 "* tester sad")) + + (ert-info ("Prompt absent for literal command") + (funcall expect 10 "<alice> bob: Spotted, detested") + (erc-scenarios-common-say "/say /me sad") + (funcall expect -0.1 "ERC> /say /me sad") + (funcall expect 10 "<tester> /me sad")) + + (ert-info ("Prompt absent for /SV") + (funcall expect 10 "<bob> Marcus, my brother!") + (erc-scenarios-common-say "/sv") + (funcall expect -0.1 "ERC> /sv") + (funcall expect 10 "<tester> I'm using ERC")) + + (ert-info ("Prompt absent module list via /SM") + (funcall expect 10 "<bob> alice: You still wrangle") + (erc-scenarios-common-say "/sm") + (funcall expect -0.1 "ERC> /sm") + (funcall expect 10 "<tester> I'm using the following modules: ") + (funcall expect 10 "<alice> No, not till Thursday;")) + + (ert-info ("Prompt present for /QUIT in issuing buffer") + (erc-scenarios-common-say "/quit") + (funcall expect 10 "ERC> /quit")) + + (with-current-buffer "foonet" + (funcall expect 10 "ERC finished"))))) + +;;; erc-scenarios-base-send-message.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-split-line.el b/test/lisp/erc/erc-scenarios-base-split-line.el new file mode 100644 index 00000000000..f6d888c1f28 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-split-line.el @@ -0,0 +1,202 @@ +;;; erc-scenarios-base-split-line.el --- ERC line splitting -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-split-line--koi8-r () + :tags '(:expensive-test) + (should (equal erc-split-line-length 440)) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/flood") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'koi8-r)) + (erc-encoding-coding-alist '(("#koi8" . cyrillic-koi8))) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to server") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "debug mode") + (erc-cmd-JOIN "#koi8"))) + + (with-current-buffer (erc-d-t-wait-for 8 (get-buffer "#koi8")) + (funcall expect 10 "короче теперь") + (ert-info ("Message well within `erc-split-line-length'") + (erc-scenarios-common-say + (concat + "короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно")) + (funcall expect 1 "<tester>") + (funcall expect -0.1 "<tester>")) + + (ert-info ("Message over `erc-split-line-length'") + (erc-scenarios-common-say + (concat + "короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " будет разрыв строки непонятно где")) + (funcall expect 1 "<tester>") + (funcall expect 1 "<tester> разрыв"))) + + (with-current-buffer "foonet" + (erc-cmd-QUIT "") + (funcall expect 10 "finished")))) + +(ert-deftest erc-scenarios-base-split-line--ascii () + :tags '(:expensive-test) + (should (equal erc-split-line-length 440)) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/flood") + (msg-432 (string-join (make-list 18 "twenty-three characters") " ")) + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'ascii)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to server") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "debug mode") + (erc-cmd-JOIN "#ascii"))) + + (with-current-buffer (erc-d-t-wait-for 8 (get-buffer "#ascii")) + (ert-info ("Message with spaces fits exactly") + (funcall expect 10 "Welcome") + (should (= (length (concat msg-432 " 12345678")) 440)) + (erc-scenarios-common-say (concat msg-432 " 12345678")) + (funcall expect 1 "<tester>") + ;; Sent in a single go, hence no second <speaker>. + (funcall expect -0.1 "<tester>") + (funcall expect 0.1 "12345678")) + + (ert-info ("Message with spaces too long.") + (erc-scenarios-common-say (concat msg-432 " 123456789")) + (funcall expect 1 "<tester>") + ;; Sent in two passes, split at last word. + (funcall expect 0.1 "<tester> 123456789")) + + (ert-info ("Message sans spaces fits exactly") + (erc-scenarios-common-say (make-string 440 ?x)) + (funcall expect 1 "<tester>") + ;; Sent in a single go, hence no second <speaker>. + (funcall expect -0.1 "<tester>")) + + (ert-info ("Message sans spaces too long.") + (erc-scenarios-common-say (concat (make-string 440 ?y) "z")) + (funcall expect 1 "<tester>") + ;; Sent in two passes, split at last word. + (funcall expect 0.1 "<tester> z")) + + (ert-info ("Rejected when escape-hatch set") + (let ((erc--reject-unbreakable-lines t)) + (should-error + (erc-scenarios-common-say + (concat + "https://mail.example.org/verify?token=" + (string-join (make-list 18 "twenty-three_characters") "_"))))))) + + (with-current-buffer "foonet" + (erc-cmd-QUIT "") + (funcall expect 10 "finished")))) + +(ert-deftest erc-scenarios-base-split-line--utf-8 () + :tags '(:expensive-test) + (unless (> emacs-major-version 27) + (ert-skip "No emojis in Emacs 27")) + + (should (equal erc-split-line-length 440)) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/flood") + (msg-432 (string-join (make-list 18 "twenty-three characters") " ")) + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'utf-8)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to server") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "debug mode") + (erc-cmd-JOIN "#utf-8"))) + + (with-current-buffer (erc-d-t-wait-for 8 (get-buffer "#utf-8")) + (funcall expect 10 "Welcome") + + (ert-info ("Message with spaces over `erc-split-line-length'") + (erc-scenarios-common-say + (concat + "короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " короче теперь если по русски написать все четко или все равно" + " будет разрыв строки непонятно где" + " будет разрыв строки непонятно где")) + (funcall expect 1 "<tester> короче") + (funcall expect 1 "<tester> все") + (funcall expect 1 "<tester> разрыв") + (funcall expect 1 "Entirely honour")) + + (ert-info ("Message sans spaces over `erc-split-line-length'") + (erc-scenarios-common-say + (concat "話說天下大勢,分久必合,合久必分:周末七國分爭,并入於秦。" + "及秦滅之後,楚、漢分爭,又并入於漢。漢朝自高祖斬白蛇而起義," + "一統天下。後來光武中興,傳至獻帝,遂分為三國。推其致亂之由," + "殆始於桓、靈二帝。桓帝禁錮善類,崇信宦官。及桓帝崩,靈帝即位," + "大將軍竇武、太傅陳蕃,共相輔佐。時有宦官曹節等弄權,竇武、陳蕃謀誅之," + "作事不密,反為所害。中涓自此愈橫")) + (funcall expect 1 "<tester>") + ;; Sent in two passes, split at last word. + (funcall expect 0.1 "<tester> 竇武") + (funcall expect 1 "this prey out")) + + ;; Combining emojis are respected. + (ert-info ("Message sans spaces over small `erc-split-line-length'") + (let ((erc-split-line-length 100)) + (erc-scenarios-common-say + "будет разрыв строки непонятно где🏁🚩🎌🏴🏳️🏳️🌈🏳️⚧️🏴☠️")) + (funcall expect 1 "<tester>") + (funcall expect 1 "<tester> 🏳️🌈"))) + + (with-current-buffer "foonet" + (erc-cmd-QUIT "") + (funcall expect 10 "finished")))) + +;;; erc-scenarios-base-split-line.el ends here diff --git a/test/lisp/erc/erc-scenarios-base-statusmsg.el b/test/lisp/erc/erc-scenarios-base-statusmsg.el new file mode 100644 index 00000000000..80582e0cf80 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-base-statusmsg.el @@ -0,0 +1,103 @@ +;;; erc-scenarios-base-statusmsg.el --- statusmsg tests -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-base-statusmsg () + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/display-message") + (dumb-server (erc-d-run "localhost" t 'statusmsg)) + (erc-autojoin-channels-alist '((foonet "#mine"))) + (erc-modules (cons 'fill-wrap erc-modules)) + (port (process-contact dumb-server :service)) + (erc-show-speaker-membership-status nil) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :full-name "tester") + (funcall expect 5 "This server is in debug mode"))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#mine")) + + (ert-info ("Receive status messages unprefixed") + (funcall expect 5 "+dummy") + (funcall expect 5 "(dummy+) hello") + (should (eq 'statusmsg (erc--get-inserted-msg-prop 'erc--msg))) + (should (equal "dummy" (erc--get-inserted-msg-prop 'erc--spkr))) + (should (eq (get-text-property (1- (point)) 'font-lock-face) + 'erc-default-face)) + (funcall expect 5 "(dummy+) there") + (should (equal "" (get-text-property (pos-bol) 'display))) + + ;; CTCP ACTION + (funcall expect 5 "* (dummy+) sad") + (should (eq 'ctcp-action-statusmsg + (erc--get-inserted-msg-prop 'erc--msg))) + (should (eq (get-text-property (1- (point)) 'font-lock-face) + 'erc-action-face)) + (funcall expect 5 "* (dummy+) glad") + (should (equal "" (get-text-property (pos-bol) 'display)))) + + (ert-info ("Send status messages") + ;; We don't have `echo-message' yet, so ERC doesn't currently + ;; insert commands like "/msg +#mine foo". + (let ((erc-default-recipients '("+#mine"))) + (erc-send-message "howdy")) + (funcall expect 5 "(@tester+) howdy") + (should (eq 'statusmsg-input (erc--get-inserted-msg-prop 'erc--msg))) + (should (equal "tester" (erc--get-inserted-msg-prop 'erc--spkr))) + (should (eq (get-text-property (1- (point)) 'font-lock-face) + 'erc-input-face)) + (let ((erc-default-recipients '("+#mine"))) + (erc-send-message "tenderfoot")) + (funcall expect 5 "(@tester+) tenderfoot") + (should (equal "" (get-text-property (pos-bol) 'display))) + + ;; Simulate some "echoed" CTCP ACTION messages since we don't + ;; actually support that yet. + (funcall expect 5 "* (@tester+) mad") + (should (eq 'ctcp-action-statusmsg-input + (erc--get-inserted-msg-prop 'erc--msg))) + (should (equal (get-text-property (1- (point)) 'font-lock-face) + '(erc-input-face erc-action-face))) + (funcall expect 5 "* (@tester+) chad") + (should (equal "" (get-text-property (pos-bol) 'display)))) + + (ert-info ("Receive status messages prefixed") + (setq erc-show-speaker-membership-status t) + (erc-scenarios-common-say "/me ready") ; sync + (funcall expect 5 "* @tester ready") + (funcall expect 5 "(+dummy+) okie") + + ;; CTCP ACTION + (funcall expect 5 "* (+dummy+) dokie") + (funcall expect 5 "* +dummy out"))))) + +;;; erc-scenarios-base-statusmsg.el ends here diff --git a/test/lisp/erc/erc-scenarios-display-message.el b/test/lisp/erc/erc-scenarios-display-message.el new file mode 100644 index 00000000000..91b82889f3e --- /dev/null +++ b/test/lisp/erc/erc-scenarios-display-message.el @@ -0,0 +1,63 @@ +;;; erc-scenarios-display-message.el --- erc-display-message -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-display-message--multibuf () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/display-message") + (dumb-server (erc-d-run "localhost" t 'multibuf)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-modules (cons 'fill-wrap erc-modules)) + (erc-autojoin-channels-alist '((foonet "#chan"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to foonet") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "debug mode"))) + + (ert-info ("User dummy is a member of #chan") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "dummy"))) + + (ert-info ("Dummy's QUIT notice in query contains metadata props") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "dummy")) + (funcall expect 10 "<dummy> hi") + (funcall expect 10 "*** dummy (~u@rdjcgiwfuwqmc.irc) has quit") + (should (eq 'QUIT (get-text-property (match-beginning 0) 'erc--msg))))) + + (ert-info ("Dummy's QUIT notice in #chan contains metadata props") + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "*** dummy (~u@rdjcgiwfuwqmc.irc) has quit") + (should (eq 'QUIT (get-text-property (match-beginning 0) 'erc--msg))))) + + (with-current-buffer "foonet" + (erc-cmd-QUIT "")))) + +;;; erc-scenarios-display-message.el ends here diff --git a/test/lisp/erc/erc-scenarios-internal.el b/test/lisp/erc/erc-scenarios-internal.el index 79d6a02ac64..6911bcc9aac 100644 --- a/test/lisp/erc/erc-scenarios-internal.el +++ b/test/lisp/erc/erc-scenarios-internal.el @@ -24,8 +24,37 @@ (when (and (getenv "EMACS_TEST_DIRECTORY") (getenv "EMACS_TEST_JUNIT_REPORT")) (setq ert-load-file-name (or (macroexp-file-name) buffer-file-name))) - (let ((load-path (cons (expand-file-name "erc-d" (ert-resource-directory)) - load-path))) - (load "erc-d-tests" nil 'silent))) + (let ((load-path `(,(expand-file-name "erc-d" (ert-resource-directory)) + ,(ert-resource-directory) + ,@load-path))) + ;; Run all tests in ./resources/erc-d/erc-d-tests.el. + (load "erc-d-tests" nil 'silent) + (require 'erc-tests-common))) + +;; Run all tests tagged `:erc--graphical' in an "interactive" +;; subprocess. Time out after 90 seconds. +(ert-deftest erc-scenarios-internal--run-graphical-all () + :tags '(:expensive-test :unstable) + (unless (and (getenv "ERC_TESTS_GRAPHICAL_ALL") + (not (getenv "ERC_TESTS_GRAPHICAL")) + (not (getenv "CI"))) + (ert-skip "Environmental conditions unmet")) + + (let* ((default-directory (expand-file-name "../" (ert-resource-directory))) + (libs (directory-files default-directory 'full (rx ".el" eot))) + (process-environment (cons "ERC_TESTS_GRAPHICAL=1" + process-environment)) + (program '(progn (ert (quote (tag :erc--graphical))) + (with-current-buffer ert--output-buffer-name + (kill-emacs (ert--stats-failed-unexpected + ert--results-stats))))) + (proc (erc-tests-common-create-subprocess program + '( "-L" "." "-l" "ert") + libs))) + + (erc-d-t-wait-for 90 "interactive tests to complete" + (not (process-live-p proc))) + + (should (zerop (process-exit-status proc))))) ;;; erc-scenarios-internal.el ends here diff --git a/test/lisp/erc/erc-scenarios-join-display-context.el b/test/lisp/erc/erc-scenarios-join-display-context.el new file mode 100644 index 00000000000..32b782d2af1 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-join-display-context.el @@ -0,0 +1,66 @@ +;;; erc-scenarios-join-display-context.el --- buffer-display autojoin ctx -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(ert-deftest erc-scenarios-join-display-context--errors () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "join/buffer-display") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'mode-context)) + (port (process-contact dumb-server :service)) + (erc-buffer-display (lambda (buf action) + (when (equal + (alist-get 'erc-autojoin-mode action) + "#chan") + (pop-to-buffer buf)))) + (erc-autojoin-channels-alist '((foonet "#chan" "#spam" "#foo"))) + (expect (erc-d-t-make-expecter))) + + (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))) + ;; FIXME test for effect rather than inspecting interval variables. + (erc-d-t-wait-for 10 (equal erc-join--requested-channels + '("#foo" "#spam" "#chan"))) + (funcall expect 10 "Max occupancy for channel #spam exceeded") + (funcall expect 10 "Channel #foo is invitation only"))) + + (ert-info ("New #chan buffer displayed in new window") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (should (eq (window-buffer) (current-buffer))) + (funcall expect 10 "#chan was created on"))) + + ;; FIXME find a less dishonest way to do this than inspecting + ;; interval variables. + (ert-info ("Ensure channels no longer tracked") + (should-not erc-join--requested-channels)))) + +;;; erc-scenarios-join-display-context.el ends here diff --git a/test/lisp/erc/erc-scenarios-keep-place-indicator.el b/test/lisp/erc/erc-scenarios-keep-place-indicator.el new file mode 100644 index 00000000000..8ebef5404c1 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-keep-place-indicator.el @@ -0,0 +1,141 @@ +;;; erc-scenarios-keep-place-indicator.el --- erc-keep-place-indicator-mode -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +;; This test shows that the indicator does not update when at least +;; one window remains. When the last window showing a buffer switches +;; away, the indicator is updated if it's earlier in the buffer. +(ert-deftest erc-scenarios-keep-place-indicator--follow () + :tags `(:expensive-test + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (when (version< emacs-version "29") (ert-skip "Times out")) + ;; XXX verify that this continues to be the case ^. + + (should-not erc-scrolltobottom-all) + (should-not erc-scrolltobottom-mode) + (should-not erc-keep-place-mode) + + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "keep-place") + (dumb-server (erc-d-run "localhost" t 'follow)) + (port (process-contact dumb-server :service)) + (erc-modules `( keep-place-indicator scrolltobottom fill-wrap + ,@erc-modules)) + (erc-keep-place-indicator-follow t) + (erc-scrolltobottom-all t) + (erc-server-flood-penalty 0.1) + (erc-autojoin-channels-alist '((foonet "#chan" "#spam"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester" + :user "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below) + (funcall expect 10 "<bob> tester, welcome!") + (recenter 0) + (other-window 1) + (funcall expect 10 "<alice> tester, welcome!") + (recenter 0) + (should (= 2 (length (window-list)))) + + (ert-info ("Last window to switch away has point earlier in buffer") + ;; Lower window, with point later in buffer, switches away first. + (switch-to-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))) ; lower + (other-window 1) + (switch-to-buffer "#spam") ; upper + (erc-scenarios-common-say "one") + (funcall expect 10 "Ay, the heads") + + ;; Overlay has moved to upper window start. + (switch-to-buffer "#chan") + (redisplay) ; force overlay to update + (save-excursion + (goto-char (window-point)) + (should (looking-back (rx "<bob> tester, welcome!"))) + (should (= (pos-bol) (window-start))) + (should (= (overlay-start erc--keep-place-indicator-overlay) + (pos-bol)))) + ;; Lower window is still centered at start. + (other-window 1) + (switch-to-buffer "#chan") + (save-excursion + (goto-char (window-point)) + (should (looking-back (rx "<alice> tester, welcome!"))) + (should (= (pos-bol) (window-start))))) + + (ert-info ("Last window to switch away has point later in buffer") + ;; Lower window advances. + (funcall expect 10 "<bob> alice: Since you can cog") + (recenter 0) + (redisplay) ; force ^ to appear on first line + + (other-window 1) ; upper still at indicator, swtiches first + (switch-to-buffer "#spam") + (other-window 1) + (switch-to-buffer "#spam") ; lower follows, speaks to sync + (erc-scenarios-common-say "two") + (funcall expect 10 "<bob> Cause they take") + (goto-char (point-max)) + + ;; Upper switches back first, finds indicator gone. + (other-window 1) + (switch-to-buffer "#chan") + (save-excursion + (goto-char (window-point)) + (should (looking-back (rx "<bob> tester, welcome!"))) + (should (= (pos-bol) (window-start))) + (should (> (overlay-start erc--keep-place-indicator-overlay) + (pos-eol)))) + + ;; Lower window follows, window-start preserved. + (other-window 1) + (switch-to-buffer "#chan") + (save-excursion + (goto-char (window-point)) + (should (looking-back (rx "you can cog"))) + (should (= (pos-bol) (window-start))) + (should (= (overlay-start erc--keep-place-indicator-overlay) + (pos-bol))))) + + (ert-info ("description") + (erc-send-input-line "#spam" "three") + (save-excursion (erc-d-t-search-for 10 "Ready")) + (switch-to-buffer "#spam") + (should (< (point) erc-input-marker)))) + + (erc-keep-place-mode -1) + (erc-scrolltobottom-mode -1))) + +;;; erc-scenarios-keep-place-indicator.el ends here diff --git a/test/lisp/erc/erc-scenarios-log.el b/test/lisp/erc/erc-scenarios-log.el new file mode 100644 index 00000000000..cff88d59c85 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-log.el @@ -0,0 +1,264 @@ +;;; erc-scenarios-log.el --- erc-log scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-log) +(require 'erc-truncate) + +(defvar erc-timestamp-format-left) + +(ert-deftest erc-scenarios-log--kill-hook () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bouncer-history") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (tempdir (make-temp-file "erc-tests-log." t nil nil)) + (erc-log-channels-directory tempdir) + (erc-modules (cons 'log erc-modules)) + (port (process-contact dumb-server :service)) + (logfile (expand-file-name (format "#chan!tester@127.0.0.1:%d.txt" port) + tempdir)) + (erc-server-flood-penalty 0.1) + (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 "foonet:changeme" + :full-name "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (funcall expect 5 "foonet"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "was created on") + (funcall expect 10 "please your lordship") + (with-current-buffer "foonet" + (delete-process erc-server-process) + (funcall expect 5 "failed")) + (should-not (file-exists-p logfile)) + (kill-buffer) + (should (file-exists-p logfile))) + + (with-temp-buffer + (insert-file-contents logfile) + (funcall expect 1 "You have joined") + (funcall expect 1 "Playback Complete.") + (funcall expect 1 "please your lordship")) + + (erc-log-mode -1) + (if noninteractive + (delete-directory tempdir :recursive) + (add-hook 'kill-emacs-hook + (lambda () (delete-directory tempdir :recursive)))))) + +;; This shows that, in addition to truncating the buffer, /clear also +;; syncs the log. + +(ert-deftest erc-scenarios-log--clear-stamp () + :tags '(:expensive-test) + (require 'erc-stamp) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bouncer-history") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (tempdir (make-temp-file "erc-tests-log." t nil nil)) + (erc-log-channels-directory tempdir) + (erc-modules (cons 'log erc-modules)) + (erc-timestamp-format-left "\n[%a %b %e %Y @@STAMP@@]\n") + (port (process-contact dumb-server :service)) + (logfile (expand-file-name (format "#chan!tester@127.0.0.1:%d.txt" port) + tempdir)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (unless noninteractive + (add-hook 'kill-emacs-hook + (lambda () (delete-directory tempdir :recursive)))) + + (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))) + (funcall expect 5 "foonet"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "@@STAMP@@") + (funcall expect 10 "Grows, lives") + (should-not (file-exists-p logfile)) + (goto-char (point-max)) + (erc-cmd-CLEAR) + (should (file-exists-p logfile)) + (funcall expect 10 "please your lordship") + (ert-info ("Buffer truncated") + (goto-char (point-min)) + (funcall expect 10 "@@STAMP@@" (point)) ; reset + (funcall expect -0.1 "Grows, lives") + (funcall expect 1 "For these two"))) + + (ert-info ("Current contents saved") + (with-temp-buffer + (insert-file-contents logfile) + (funcall expect 1 "@@STAMP@@") + (funcall expect 1 "You have joined") + (funcall expect 1 "Playback Complete.") + (funcall expect 1 "Grows, lives") + (funcall expect -0.01 "please your lordship"))) + + (ert-info ("Remainder saved, timestamp printed when option non-nil") + (with-current-buffer "foonet" + (delete-process erc-server-process) + (funcall expect 5 "failed")) + (kill-buffer "#chan") + (with-temp-buffer + (insert-file-contents logfile) + (funcall expect 1 "@@STAMP@@") + (funcall expect 1 "Grows, lives") + (funcall expect -0.01 "@@STAMP@@") + (forward-line 1) ; no blank, no timestamp + (should (looking-at (rx "<bob> alice: For these two hours,"))) + (funcall expect 1 "please your lordship"))) + + (erc-log-mode -1) + (when noninteractive (delete-directory tempdir :recursive)))) + +(ert-deftest erc-scenarios-log--truncate () + :tags '(:expensive-test :unstable) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bouncer-history") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (tempdir (make-temp-file "erc-tests-log." t nil nil)) + (erc-log-channels-directory tempdir) + (erc-modules (cons 'truncate (cons 'log erc-modules))) + (erc-max-buffer-size 512) + (port (process-contact dumb-server :service)) + (logchan (expand-file-name (format "#chan!tester@127.0.0.1:%d.txt" port) + tempdir)) + (logserv (expand-file-name + (format "127.0.0.1:%d!tester@127.0.0.1:%d.txt" port port) + tempdir)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (unless noninteractive + (add-hook 'kill-emacs-hook + (lambda () (delete-directory tempdir :recursive)))) + + (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))) + (should-not (file-exists-p logserv)) + (should-not (file-exists-p logchan)) + (funcall expect 10 "*** MAXLIST=beI:60") + (should (= (pos-bol) (point-min))) + (should (file-exists-p logserv)))) + + (ert-info ("Log file ahead of truncation point") + ;; Log contains lines still present in buffer. + (with-temp-buffer + (insert-file-contents logserv) + (funcall expect 10 "*** MAXLIST=beI:60"))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "please your lordship") + (should (file-exists-p logchan)) + (funcall expect -0.1 "[07:04:37] alice: Here," (point-min))) + + (ert-info ("Log ahead of truncation point") + (with-temp-buffer + (insert-file-contents logchan) + (funcall expect 1 "You have joined") + (funcall expect 1 "[07:04:37] alice: Here,") + (funcall expect 1 "loathed enemy") + (funcall expect -0.1 "please your lordship"))) + + (erc-log-mode -1) + (erc-truncate-mode -1) + (when noninteractive (delete-directory tempdir :recursive)))) + +(defvar erc-insert-timestamp-function) +(declare-function erc-insert-timestamp-left "erc-stamp" (string)) + +(ert-deftest erc-scenarios-log--save-buffer-in-logs/truncate-on-save () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/assoc/bouncer-history") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (tempdir (make-temp-file "erc-tests-log." t nil nil)) + (erc-log-channels-directory tempdir) + (erc-modules (cons 'log erc-modules)) + (port (process-contact dumb-server :service)) + (erc-truncate-buffer-on-save t) + (logchan (expand-file-name (format "#chan!tester@127.0.0.1:%d.txt" port) + tempdir)) + (erc-server-flood-penalty 0.1) + (erc-insert-timestamp-function #'erc-insert-timestamp-left) + (expect (erc-d-t-make-expecter))) + + (unless noninteractive + (add-hook 'kill-emacs-hook + (lambda () (delete-directory tempdir :recursive)))) + + (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))))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (funcall expect 10 "<someone> [07:04:10] hi everyone") + (should-not (file-exists-p logchan)) + ;; Simulate an M-x erc-save-buffer-in-logs RET + (cl-letf (((symbol-function 'called-interactively-p) #'always)) + (call-interactively #'erc-save-buffer-in-logs)) + (should (file-exists-p logchan)) + (funcall expect 10 "<alice> bob: As't please your lordship") + (erc-save-buffer-in-logs) + ;; Not truncated when called by lisp code. + (should (> (buffer-size) 400))) + + (ert-info ("No double entries") + (with-temp-buffer + (insert-file-contents logchan) + (funcall expect 0.1 "hi everyone") + (funcall expect -0.1 "hi everyone") + (funcall expect 0.1 "Playback Complete") + (funcall expect -0.1 "Playback Complete") + (funcall expect 10 "<alice> bob: As't"))) + + (erc-log-mode -1) + (when noninteractive (delete-directory tempdir :recursive)))) + +;;; erc-scenarios-log.el ends here diff --git a/test/lisp/erc/erc-scenarios-match.el b/test/lisp/erc/erc-scenarios-match.el new file mode 100644 index 00000000000..b18c0a4bd17 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-match.el @@ -0,0 +1,555 @@ +;;; erc-scenarios-match.el --- Misc `erc-match' scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(eval-when-compile + (require 'erc-join) + (require 'erc-match)) + +(require 'erc-stamp) +(require 'erc-fill) + +;; This defends against a regression in which all matching by the +;; `erc-match-message' fails when `erc-add-timestamp' precedes it in +;; `erc-insert-modify-hook'. Basically, `erc-match-message' used to +;; expect an `erc-parsed' text property on the first character in a +;; message, which doesn't exist, when the message content is prefixed +;; by a leading timestamp. + +(ert-deftest erc-scenarios-match--stamp-left-current-nick () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-insert-timestamp-function 'erc-insert-timestamp-left) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + ;; Module `timestamp' follows `match' in insertion hooks. + (should (memq 'erc-add-timestamp + (memq 'erc-match-message + (default-value 'erc-insert-modify-hook)))) + ;; The "match type" is `current-nick'. + (funcall expect 5 "tester") + (should (eq (get-text-property (1- (point)) 'font-lock-face) + 'erc-current-nick-face)))))) + +;; When hacking on tests that use this fixture, it's best to run it +;; interactively, and visually inspect the output with various +;; combinations of: +;; +;; M-x erc-match-toggle-hidden-fools RET +;; M-x erc-toggle-timestamps RET +;; +(defun erc-scenarios-match--invisible-stamp (hiddenp visiblep) + (unless noninteractive + (kill-new "erc-match-toggle-hidden-fools")) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "join/legacy") + (dumb-server (erc-d-run "localhost" t 'foonet)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-timestamp-only-if-changed-flag nil) + (erc-fools '("bob")) + (erc-text-matched-hook '(erc-hide-fools)) + (erc-autojoin-channels-alist '((FooNet "#chan"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :password "changeme" + :nick "tester") + ;; Module `timestamp' follows `match' in insertion hooks. + (should (memq 'erc-add-timestamp + (memq 'erc-match-message + (default-value 'erc-insert-modify-hook)))) + (funcall expect 5 "This server is in debug mode"))) + + (ert-info ("Ensure lines featuring \"bob\" are invisible") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (should (funcall expect 10 "<bob> tester, welcome!")) + (ert-info ("<bob> tester, welcome!") (funcall hiddenp)) + + ;; Alice's is the only one visible. + (should (funcall expect 10 "<alice> tester, welcome!")) + (ert-info ("<alice> tester, welcome!") (funcall visiblep)) + + (should (funcall expect 10 "<bob> alice: But, as it seems")) + (ert-info ("<bob> alice: But, as it seems") (funcall hiddenp)) + + (should (funcall expect 10 "<alice> bob: Well, this is the forest")) + (ert-info ("<alice> bob: Well, this is the forest") (funcall hiddenp)) + + (should (funcall expect 10 "<alice> bob: And will you")) + (ert-info ("<alice> bob: And will you") (funcall hiddenp)) + + (should (funcall expect 10 "<bob> alice: Live, and be prosperous")) + (ert-info ("<bob> alice: Live, and be prosperous") (funcall hiddenp)) + + (should (funcall expect 10 "ERC>")) + (should-not (get-text-property (pos-bol) 'invisible)) + (should-not (get-text-property (point) 'invisible)))))) + +;; This asserts that when stamps appear before a message, registered +;; invisibility properties owned by modules span the entire message. +(ert-deftest erc-scenarios-match--stamp-left-fools-invisible () + :tags '(:expensive-test) + (let ((erc-insert-timestamp-function #'erc-insert-timestamp-left)) + (erc-scenarios-match--invisible-stamp + + (lambda () + ;; This is a time-stamped message. + (should (eq (field-at-pos (pos-bol)) 'erc-timestamp)) + + ;; Leading stamp has combined `invisible' property value. + (should (equal (get-text-property (pos-bol) 'invisible) + '(match-fools timestamp))) + + ;; Message proper has the `invisible' property `match-fools'. + (let ((msg-beg (next-single-property-change (pos-bol) 'invisible))) + (should (eq (get-text-property msg-beg 'invisible) 'match-fools)) + (should (>= (next-single-property-change msg-beg 'invisible nil) + (pos-eol))))) + + (lambda () + ;; This is a time-stamped message. + (should (eq (field-at-pos (pos-bol)) 'erc-timestamp)) + (should (get-text-property (pos-bol) 'invisible)) + + ;; The entire message proper is visible. + (let ((msg-beg (next-single-property-change (pos-bol) 'invisible))) + (should + (= (next-single-property-change msg-beg 'invisible nil (pos-eol)) + (pos-eol)))))))) + +;; In most cases, `erc-hide-fools' makes line endings invisible. +(defun erc-scenarios-match--stamp-right-fools-invisible () + (let ((erc-insert-timestamp-function #'erc-insert-timestamp-right)) + (erc-scenarios-match--invisible-stamp + + (lambda () + (pcase-let ((`(,beg . ,end) (erc--get-inserted-msg-bounds))) + ;; The end of the message is a newline. + (should (= ?\n (char-after end))) + + ;; Every message has a trailing time stamp. + (should (eq (field-at-pos (1- end)) 'erc-timestamp)) + + ;; Stamps have a combined `invisible' property value. + (should (equal (get-text-property (1- end) 'invisible) + '(match-fools timestamp))) + + ;; The final newline is hidden by `match', not `stamps' + (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p)) + (if erc-legacy-invisible-bounds-p + (should (eq (get-text-property end 'invisible) 'match-fools)) + (should (eq (get-text-property beg 'invisible) 'match-fools)) + (should-not (get-text-property end 'invisible)))) + + ;; The message proper has the `invisible' property `match-fools', + ;; and it starts after the preceding newline. + (should (eq (get-text-property (pos-bol) 'invisible) 'match-fools)) + + ;; It ends just before the timestamp. + (let ((msg-end (next-single-property-change (pos-bol) 'invisible))) + (should (equal (get-text-property msg-end 'invisible) + '(match-fools timestamp))) + + ;; Stamp's `invisible' property extends throughout the stamp + ;; and ends before the trailing newline. + (should (= (next-single-property-change msg-end 'invisible) end))))) + + (lambda () + (let ((end (erc--get-inserted-msg-end (point)))) + ;; This message has a time stamp like all the others. + (should (eq (field-at-pos (1- end)) 'erc-timestamp)) + + ;; The entire message proper is visible. + (should-not (get-text-property (pos-bol) 'invisible)) + (let ((inv-beg (next-single-property-change (pos-bol) 'invisible))) + (should (eq (get-text-property inv-beg 'invisible) + 'timestamp)))))))) + +(ert-deftest erc-scenarios-match--stamp-right-fools-invisible () + :tags '(:expensive-test) + (erc-scenarios-match--stamp-right-fools-invisible)) + +(ert-deftest erc-scenarios-match--stamp-right-fools-invisible--nooffset () + :tags '(:expensive-test) + (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p)) + (should-not erc-legacy-invisible-bounds-p) + (let ((erc-legacy-invisible-bounds-p t)) + (erc-scenarios-match--stamp-right-fools-invisible)))) + +;; This asserts that when `erc-fill-wrap-mode' is enabled, ERC hides +;; the preceding message's line ending. +(ert-deftest erc-scenarios-match--stamp-right-invisible-fill-wrap () + :tags '(:expensive-test) + (let ((erc-insert-timestamp-function #'erc-insert-timestamp-right) + (erc-fill-function #'erc-fill-wrap)) + (erc-scenarios-match--invisible-stamp + + (lambda () + ;; Every message has a trailing time stamp. + (should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp)) + + ;; Stamps appear in the right margin. + (should (equal (car (get-text-property (1- (pos-eol)) 'display)) + '(margin right-margin))) + + ;; Stamps have a combined `invisible' property value. + (should (equal (get-text-property (1- (pos-eol)) 'invisible) + '(match-fools timestamp))) + + ;; The message proper has the `invisible' property `match-fools', + ;; which starts at the preceding newline... + (should (eq (get-text-property (1- (pos-bol)) 'invisible) 'match-fools)) + + ;; ... and ends just before the timestamp. + (let ((msgend (next-single-property-change (1- (pos-bol)) 'invisible))) + (should (equal (get-text-property msgend 'invisible) + '(match-fools timestamp))) + + ;; The newline before `erc-insert-marker' is still visible. + (should-not (get-text-property (pos-eol) 'invisible)) + (should (= (next-single-property-change msgend 'invisible) + (pos-eol))))) + + (lambda () + ;; This message has a time stamp like all the others. + (should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp)) + + ;; Unlike hidden messages, the preceding newline is visible. + (should-not (get-text-property (1- (pos-bol)) 'invisible)) + + ;; The entire message proper is visible. + (let ((inv-beg (next-single-property-change (1- (pos-bol)) 'invisible))) + (should (eq (get-text-property inv-beg 'invisible) 'timestamp))))))) + +(defun erc-scenarios-match--fill-wrap-stamp-dedented-p (point) + (pcase (get-text-property point 'line-prefix) + (`(space :width (- erc-fill--wrap-value (,n))) + (if (display-graphic-p) (< 100 n 200) (< 10 n 30))) + (`(space :width (- erc-fill--wrap-value ,n)) + (< 10 n 30)))) + +(ert-deftest erc-scenarios-match--hide-fools/stamp-both/fill-wrap () + + ;; Rewind the clock to known date artificially. We should probably + ;; use a ticks/hz cons on 29+. + (let ((erc-stamp--current-time 704591940) + (erc-stamp--tz t) + (erc-fill-function #'erc-fill-wrap) + (bob-utterance-counter 0)) + + (erc-scenarios-match--invisible-stamp + + (lambda () + (ert-info ("Baseline check") + ;; False date printed initially before anyone speaks. + (when (zerop bob-utterance-counter) + (save-excursion + (goto-char (point-min)) + (search-forward "[Wed Apr 29 1992]") + ;; First stamp in a buffer is not invisible from previous + ;; newline (before stamp's own leading newline). + (should (= 4 (match-beginning 0))) + (should (get-text-property 3 'invisible)) + (should-not (get-text-property 2 'invisible)) + (should (erc-scenarios-match--fill-wrap-stamp-dedented-p 4)) + (search-forward "[23:59]")))) + + (ert-info ("Line endings in Bob's messages are invisible") + ;; The message proper has the `invisible' property `match-fools'. + (should (eq (get-text-property (pos-bol) 'invisible) 'match-fools)) + (pcase-let ((`(,mbeg . ,mend) (erc--get-inserted-msg-bounds))) + (should (= (char-after mend) ?\n)) + (should-not (field-at-pos mend)) + (should-not (field-at-pos mbeg)) + + (when (= bob-utterance-counter 1) + (let ((right-stamp (field-end mbeg))) + (should (eq 'erc-timestamp (field-at-pos right-stamp))) + (should (= mend (field-end right-stamp))) + (should (eq (field-at-pos (1- mend)) 'erc-timestamp)))) + + ;; The `erc--ts' property is present in prop stack. + (should (get-text-property (pos-bol) 'erc--ts)) + (should-not (next-single-property-change (1+ (pos-bol)) 'erc--ts)) + + ;; Line ending has the `invisible' property `match-fools'. + (should (eq (get-text-property mbeg 'invisible) 'match-fools)) + (should-not (get-text-property mend 'invisible)))) + + ;; Only the message right after Alice speaks contains stamps. + (when (= 1 bob-utterance-counter) + + (ert-info ("Date stamp occupying previous line is invisible") + (should (eq 'match-fools (get-text-property (point) 'invisible))) + (save-excursion + (forward-line -1) + (goto-char (pos-bol)) + (should (looking-at (rx "[Mon May 4 1992]"))) + (ert-info ("Stamp's NL `invisible' as fool, not timestamp") + (let ((end (match-end 0))) + (should (eq (char-after end) ?\n)) + (should (eq 'timestamp + (get-text-property (1- end) 'invisible))) + (should (eq 'match-fools + (get-text-property end 'invisible))))) + (should (erc-scenarios-match--fill-wrap-stamp-dedented-p (point))) + ;; Date stamp has a combined `invisible' property value + ;; that starts at the previous message's trailing newline + ;; and extends until the start of the message proper. + (should (equal ?\n (char-before (point)))) + (should (equal ?\n (char-before (1- (point))))) + (let ((val (get-text-property (- (point) 2) 'invisible))) + (should (equal val 'timestamp)) + (should (= (text-property-not-all (- (point) 2) (point-max) + 'invisible val) + (pos-eol)))))) + + (ert-info ("Current message's RHS stamp is hidden") + ;; Right stamp has `match-fools' property. + (save-excursion + (should-not (field-at-pos (point))) + (should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp))) + + ;; Stamp invisibility starts where message's ends. + (let ((msgend (next-single-property-change (pos-bol) 'invisible))) + ;; Stamp has a combined `invisible' property value. + (should (equal (get-text-property msgend 'invisible) + '(match-fools timestamp))) + + ;; Combined `invisible' property spans entire timestamp. + (should (= (next-single-property-change msgend 'invisible) + (pos-eol)))))) + + (cl-incf bob-utterance-counter)) + + ;; Alice. + (lambda () + ;; Set clock ahead a week or so. + (setq erc-stamp--current-time 704962800) + + ;; This message has no time stamp and is completely visible. + (should-not (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp)) + (should-not (next-single-property-change (pos-bol) 'invisible)))))) + +;; This asserts that speaker hiding by `erc-fill-wrap-merge' doesn't +;; take place after a series of hidden fool messages with an +;; intervening outgoing message followed immediately by a non-fool +;; message from the last non-hidden speaker (other than the user). +(ert-deftest erc-scenarios-match--hide-fools/stamp-both/fill-wrap/speak () + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "match/fools") + (erc-stamp--current-time 704591940) + (dumb-server (erc-d-run "localhost" t 'fill-wrap)) + (erc-stamp--tz t) + (erc-fill-function #'erc-fill-wrap) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (erc-timestamp-only-if-changed-flag nil) + (erc-fools '("bob")) + (erc-text-matched-hook '(erc-hide-fools)) + (erc-autojoin-channels-alist '((FooNet "#chan"))) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :password "changeme" + :nick "tester") + ;; Module `timestamp' follows `match' in insertion hooks. + (should (memq 'erc-add-timestamp + (memq 'erc-match-message + (default-value 'erc-insert-modify-hook)))) + (funcall expect 5 "This server is in debug mode"))) + + (ert-info ("Ensure lines featuring \"bob\" are invisible") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (should (funcall expect 10 "<alice> None better than")) + (should (funcall expect 10 "<alice> bob: Still we went")) + (should (funcall expect 10 "<bob> alice: Give me your hand")) + (erc-scenarios-common-say "hey") + (should (funcall expect 10 "<bob> You have paid the heavens")) + (should (funcall expect 10 "<alice> bob: In the sick air")) + (should (funcall expect 10 "<alice> The web of our life")) + + ;; Regression (see leading comment). + (should-not (equal "" (get-text-property (pos-bol) 'display))) + + ;; No remaining meta-data positions, no more timestamps. + (should-not (next-single-property-change (1+ (pos-bol)) 'erc--ts)) + ;; No remaining invisible messages. + (should-not (text-property-not-all (pos-bol) erc-insert-marker + 'invisible nil)) + + (should (funcall expect 10 "ERC>")) + (should-not (get-text-property (pos-bol) 'invisible)) + (should-not (get-text-property (point) 'invisible)))))) + +(defun erc-scenarios-match--stamp-both-invisible-fill-static (assert-ds) + (should (eq erc-insert-timestamp-function + #'erc-insert-timestamp-left-and-right)) + + ;; Rewind the clock to known date artificially. + (let ((erc-stamp--current-time 704591940) + (erc-stamp--tz t) + (erc-fill-function #'erc-fill-static) + (bob-utterance-counter 0)) + + (erc-scenarios-match--invisible-stamp + + (lambda () + (ert-info ("Baseline check") + ;; False date printed initially before anyone speaks. + (when (zerop bob-utterance-counter) + (save-excursion + (goto-char (point-min)) + (search-forward "[Wed Apr 29 1992]") + (search-forward "[23:59]")))) + + (ert-info ("Line endings in Bob's messages are invisible") + ;; The message proper has the `invisible' property `match-fools'. + (should (eq (get-text-property (pos-bol) 'invisible) 'match-fools)) + (pcase-let ((`(,mbeg . ,mend) (erc--get-inserted-msg-bounds))) + + (should (= (char-after mend) ?\n)) + (should-not (field-at-pos mbeg)) + (should-not (field-at-pos mend)) + (when (= 1 bob-utterance-counter) + ;; For Bob's stamped message, check newline after stamp. + (should (eq (field-at-pos (field-end mbeg)) 'erc-timestamp)) + (should (eq (field-at-pos (1- mend)) 'erc-timestamp))) + + ;; The `erc--ts' property is present in the message's + ;; width 1 prop collection at its first char. + (should (get-text-property (pos-bol) 'erc--ts)) + (should-not (next-single-property-change (1+ (pos-bol)) 'erc--ts)) + + ;; Line ending has the `invisible' property `match-fools'. + (should (= (char-after mend) ?\n)) + (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p)) + (if erc-legacy-invisible-bounds-p + (should (eq (get-text-property mend 'invisible) 'match-fools)) + (should (eq (get-text-property mbeg 'invisible) 'match-fools)) + (should-not (get-text-property mend 'invisible)))))) + + ;; Only the message right after Alice speaks contains stamps. + (when (= 1 bob-utterance-counter) + + (ert-info ("Date stamp occupying previous line is invisible") + (save-excursion + (forward-line -1) + (goto-char (pos-bol)) + (should (looking-at (rx "[Mon May 4 1992]"))) + (should (= ?\n (char-after (- (point) 2)))) ; welcome!\n + (funcall assert-ds))) ; "assert date stamp" + + (ert-info ("Folding preserved despite invisibility") + ;; Message has a trailing time stamp, but it's been folded + ;; over to the next line. + (should-not (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp)) + (save-excursion + (forward-line) + (should (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp))) + + ;; Stamp invisibility starts where message's ends. + (let ((msgend (next-single-property-change (pos-bol) 'invisible))) + ;; Stamp has a combined `invisible' property value. + (should (equal (get-text-property msgend 'invisible) + '(match-fools timestamp))) + + ;; Combined `invisible' property spans entire timestamp. + (should (= (next-single-property-change msgend 'invisible) + (save-excursion (forward-line) (pos-eol))))))) + + (cl-incf bob-utterance-counter)) + + ;; Alice. + (lambda () + ;; Set clock ahead a week or so. + (setq erc-stamp--current-time 704962800) + + ;; This message has no time stamp and is completely visible. + (should-not (eq (field-at-pos (1- (pos-eol))) 'erc-timestamp)) + (should-not (next-single-property-change (pos-bol) 'invisible)))))) + +(ert-deftest erc-scenarios-match--stamp-both-invisible-fill-static () + :tags '(:expensive-test) + (erc-scenarios-match--stamp-both-invisible-fill-static + + (lambda () + ;; Date stamp has an `invisible' property that starts from the + ;; newline delimiting the current and previous messages and + ;; extends until the stamp's final newline. It is not combined + ;; with the old value, `match-fools'. + (let ((delim-pos (- (point) 2))) + (should (equal 'timestamp (get-text-property delim-pos 'invisible))) + ;; Stamp-only invisibility ends before its last newline. + (should (= (text-property-not-all delim-pos (point-max) + 'invisible 'timestamp) + (match-end 0))))))) ; pos-eol + +(ert-deftest erc-scenarios-match--stamp-both-invisible-fill-static--nooffset () + :tags '(:expensive-test) + (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p)) + (should-not erc-legacy-invisible-bounds-p) + + (let ((erc-legacy-invisible-bounds-p t)) + (erc-scenarios-match--stamp-both-invisible-fill-static + + (lambda () + ;; Date stamp has an `invisible' property that covers its + ;; format string exactly. It is not combined with the old + ;; value, `match-fools'. + (let ((delim-prev (- (point) 2))) + (should-not (get-text-property delim-prev 'invisible)) + (should (eq 'erc-timestamp (field-at-pos (point)))) + (should (= (next-single-property-change delim-prev 'invisible) + (field-beginning (point)))) + (should (equal 'timestamp + (get-text-property (1- (point)) 'invisible))) + ;; Field stops before final newline because the date stamp + ;; is (now, as of ERC 5.6) its own standalone message. + (should (= ?\n (char-after (field-end (point))))) + ;; Stamp-only invisibility includes last newline. + (should (= (text-property-not-all (1- (point)) (point-max) + 'invisible 'timestamp) + (1+ (field-end (point))))))))))) + +;;; erc-scenarios-match.el ends here diff --git a/test/lisp/erc/erc-scenarios-misc-commands.el b/test/lisp/erc/erc-scenarios-misc-commands.el new file mode 100644 index 00000000000..b96782cf29c --- /dev/null +++ b/test/lisp/erc/erc-scenarios-misc-commands.el @@ -0,0 +1,126 @@ +;;; erc-scenarios-misc-commands.el --- Misc commands for ERC -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +;; This defends against a partial regression in which an /MOTD caused +;; 376 and 422 handlers in erc-networks to run. + +(ert-deftest erc-scenarios-misc-commands--MOTD () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "commands") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'motd)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to server") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "This is the default Ergo MOTD") + (funcall expect 10 "debug mode"))) + + (ert-info ("Send plain MOTD") + (with-current-buffer "foonet" + (erc-cmd-MOTD) + (funcall expect -0.2 "Unexpected state detected") + (funcall expect 10 "This is the default Ergo MOTD"))) + + (ert-info ("Send MOTD with known target") + (with-current-buffer "foonet" + (erc-scenarios-common-say "/MOTD irc1.foonet.org") + (funcall expect -0.2 "Unexpected state detected") + (funcall expect 10 "This is the default Ergo MOTD"))) + + (ert-info ("Send MOTD with erroneous target") + (with-current-buffer "foonet" + (erc-scenarios-common-say "/MOTD fake.foonet.org") + (funcall expect -0.2 "Unexpected state detected") + (funcall expect 10 "No such server") + ;; Message may show up before the handler runs. + (erc-d-t-wait-for 10 + (not (local-variable-p 'erc-server-402-functions))) + (should-not (local-variable-p 'erc-server-376-functions)) + (should-not (local-variable-p 'erc-server-422-functions)) + (erc-cmd-QUIT ""))))) + + +(ert-deftest erc-scenarios-misc-commands--SQUERY () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "commands") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'squery)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to server") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :full-name "tester") + (funcall expect 10 "Your connection is secure"))) + + (ert-info ("Send SQUERY") + (with-current-buffer "IRCnet" + (erc-scenarios-common-say "/SQUERY alis help list") + (funcall expect -0.1 "Incorrect arguments") + (funcall expect 10 "See also: HELP EXAMPLES"))))) + +;; Note that as of ERC 5.6, there is no actual slash-command function +;; named `erc-cmd-vhost'. At the moment, this test merely exists to +;; assert that the `erc-server-396' response handler updates the rolls +;; correctly. +(ert-deftest erc-scenarios-misc-commands--VHOST () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "commands") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'vhost)) + ;; As of ERC 5.6, we must join a channel before ERC adds itself + ;; to `erc-server-users'. Without such an entry, there's + ;; nothing to update when the 396 arrives. + (erc-autojoin-channels-alist '((foonet "#chan"))) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to server") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "changeme" + :full-name "tester") + (funcall expect 10 "debug mode"))) + + (ert-info ("Send VHOST") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (erc-scenarios-common-say "/VHOST tester changeme") + (funcall expect 10 "visible host") + (should (string= (erc-server-user-host (erc-get-server-user "tester")) + "some.host.test.cc")))))) + +;;; erc-scenarios-misc-commands.el ends here diff --git a/test/lisp/erc/erc-scenarios-misc.el b/test/lisp/erc/erc-scenarios-misc.el index eb1ff6a046a..8f6042de5c2 100644 --- a/test/lisp/erc/erc-scenarios-misc.el +++ b/test/lisp/erc/erc-scenarios-misc.el @@ -75,7 +75,7 @@ (ert-info ("All output sent") (with-current-buffer "#chan/foonet" - (funcall expect 8 "Some man or other")) + (funcall expect 16 "Some man or other")) (with-current-buffer "#chan/barnet" (funcall expect 10 "That's he that was Othello"))))) @@ -205,4 +205,38 @@ (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) (funcall expect 10 "welcome"))))) +;; Ensure that ERC does not attempt to switch to a killed server +;; buffer via `erc-track-switch-buffer'. + +(declare-function erc-track-switch-buffer "erc-track" (arg)) +(defvar erc-track-mode) + +(ert-deftest erc-scenarios-base-kill-server-track () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "networks/merge-server") + (dumb-server (erc-d-run "localhost" t 'track)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester") + (should (string= (buffer-name) (format "127.0.0.1:%d" port))) + (should erc-track-mode) + (funcall expect 5 "changed mode for tester") + (erc-cmd-JOIN "#chan"))) + + (ert-info ("Join channel and kill server buffer") + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) + (funcall expect 5 "The hour that fools should ask")) + (with-current-buffer "FooNet" + (set-process-query-on-exit-flag erc-server-process nil) + (kill-buffer)) + (should-not (eq (current-buffer) (get-buffer "#chan"))) ; *temp* + (ert-simulate-command '(erc-track-switch-buffer 1)) ; No longer signals + (should (eq (current-buffer) (get-buffer "#chan")))))) + ;;; erc-scenarios-misc.el ends here diff --git a/test/lisp/erc/erc-scenarios-prompt-format.el b/test/lisp/erc/erc-scenarios-prompt-format.el new file mode 100644 index 00000000000..7eccb859dbc --- /dev/null +++ b/test/lisp/erc/erc-scenarios-prompt-format.el @@ -0,0 +1,117 @@ +;;; erc-scenarios-prompt-format.el --- erc-prompt-format-mode -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(defvar erc-fill-wrap-align-prompt) +(defvar erc-fill-wrap-use-pixels) + +(defun erc-scenarios-prompt-format--assert (needle &rest props) + (save-excursion + (goto-char erc-insert-marker) + (should (search-forward needle nil t)) + (pcase-dolist (`(,k . ,v) props) + (should (equal (get-text-property (point) k) v))))) + +;; This makes assertions about the option `erc-fill-wrap-align-prompt' +;; as well as the standard value of `erc-prompt-format'. One minor +;; omission is that this doesn't check behavior in query buffers. +(ert-deftest erc-scenarios-prompt-format () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/modes") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'chan-changed)) + (erc-modules (cons 'fill-wrap erc-modules)) + (erc-fill-wrap-align-prompt t) + (erc-fill-wrap-use-pixels nil) + (erc-prompt #'erc-prompt-format) + (erc-autojoin-channels-alist '((Libera.Chat "#chan"))) + (expect (erc-d-t-make-expecter)) + ;; Collect samples of `line-prefix' to verify deltas as the + ;; prompt grows and shrinks. + (line-prefixes nil) + (stash-pfx (lambda () + (pcase (get-text-property erc-insert-marker 'line-prefix) + (`(space :width (- erc-fill--wrap-value ,n)) + (car (push n line-prefixes))))))) + + (ert-info ("Connect to Libera.Chat") + (with-current-buffer (erc :server "127.0.0.1" + :port (process-contact dumb-server :service) + :nick "tester" + :full-name "tester") + (funcall expect 5 "Welcome to the Libera.Chat") + (funcall stash-pfx) + (funcall expect 5 "changed mode") + ;; New prompt is shorter than default with placeholders, like + ;; "(foo?)(bar?)" (assuming we win the inherent race). + (should (>= (car line-prefixes) (funcall stash-pfx))) + (erc-scenarios-prompt-format--assert "user-" '(display . ("Ziw"))))) + + (with-current-buffer (erc-d-t-wait-for 5 (get-buffer "#chan")) + (should-not erc-channel-key) + (should-not erc-channel-user-limit) + + (ert-info ("Receive notice that mode has changed") + (erc-d-t-wait-for 10 (equal erc-channel-modes '("n" "t"))) + (funcall stash-pfx) + (erc-scenarios-common-say "ready before") + (funcall expect 10 " has changed mode for #chan to +Qu") + (erc-d-t-wait-for 10 (equal erc-channel-modes '("Q" "n" "t" "u"))) + ;; Prompt is longer now, so too is the `line-prefix' subtrahend. + (should (< (car line-prefixes) (funcall stash-pfx))) + (erc-scenarios-prompt-format--assert "Qntu") + (erc-scenarios-prompt-format--assert "#chan>")) + + (ert-info ("Key stored locally") + (erc-scenarios-common-say "ready key") + (funcall expect 10 " has changed mode for #chan to +k hunter2") + ;; Prompt has grown by 1. + (should (< (car line-prefixes) (funcall stash-pfx))) + (erc-scenarios-prompt-format--assert "Qkntu")) + + (ert-info ("Limit stored locally") + (erc-scenarios-common-say "ready limit") + (funcall expect 10 " has changed mode for #chan to +l 3") + (erc-d-t-wait-for 10 (eql erc-channel-user-limit 3)) + (should (equal erc-channel-modes '("Q" "n" "t" "u"))) + ;; Prompt has grown by 1 again. + (should (< (car line-prefixes) (funcall stash-pfx))) + (erc-scenarios-prompt-format--assert "Qklntu")) + + (ert-info ("Modes removed and local state deletion succeeds") + (erc-scenarios-common-say "ready drop") + (funcall expect 10 " has changed mode for #chan to -lu") + (funcall expect 10 " has changed mode for #chan to -Qk *") + (erc-d-t-wait-for 10 (equal erc-channel-modes '("n" "t"))) + ;; Prompt has shrunk. + (should (> (car line-prefixes) (funcall stash-pfx))) + (erc-scenarios-prompt-format--assert "nt")) + + (should-not erc-channel-key) + (should-not erc-channel-user-limit) + (funcall expect 10 "<Chad> after")))) + +;;; erc-scenarios-prompt-format.el ends here diff --git a/test/lisp/erc/erc-scenarios-sasl.el b/test/lisp/erc/erc-scenarios-sasl.el index 2a3bfe7bda1..e070c675446 100644 --- a/test/lisp/erc/erc-scenarios-sasl.el +++ b/test/lisp/erc/erc-scenarios-sasl.el @@ -51,6 +51,70 @@ ;; Regression "\0\0\0\0 ..." caused by (fillarray passphrase 0) (should (string= erc-sasl-password "password123")))))) +;; The user's unreasonably long password is apportioned into chunks on +;; the way out the door. + +(ert-deftest erc-scenarios-sasl--plain-overlong-split () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "sasl") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'plain-overlong-split)) + (port (process-contact dumb-server :service)) + (erc-modules (cons 'sasl erc-modules)) + (erc-sasl-password + (concat + "Est ut beatae omnis ipsam. " + "Quis fugiat deleniti totam qui. " + "Ipsum quam a dolorum tempora velit laborum odit. " + "Et saepe voluptate sed cumque vel. " + "Voluptas sint ab pariatur libero veritatis corrupti. " + "Vero iure omnis ullam. " + "Vero beatae dolores facere fugiat ipsam. " + "Ea est pariatur minima nobis sunt aut ut. " + "Dolores ut laudantium maiores temporibus voluptates. " + "Reiciendis impedit omnis et unde delectus quas ab. " + "Quae eligendi necessitatibus doloribus " + "molestias tempora magnam assumenda.")) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "emersion" + :user "emersion" + :full-name "emersion") + (funcall expect 10 "This server is in debug mode") + (erc-cmd-QUIT ""))))) + +(ert-deftest erc-scenarios-sasl--plain-overlong-aligned () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "sasl") + (erc-server-flood-penalty 0.1) + (dumb-server (erc-d-run "localhost" t 'plain-overlong-aligned)) + (port (process-contact dumb-server :service)) + (erc-modules (cons 'sasl erc-modules)) + (erc-sasl-password + (concat + "Est ut beatae omnis ipsam. " + "Quis fugiat deleniti totam qui. " + "Ipsum quam a dolorum tempora velit laborum odit. " + "Et saepe voluptate sed cumque vel. " + "Voluptas sint ab pariatur libero veritatis corrupti. " + "Vero iure omnis ullam. Vero beatae dolores facere fugiat ipsam. " + "Ea est pariatur minima nobis")) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "emersion" + :user "emersion" + :full-name "emersion") + (funcall expect 10 "This server is in debug mode") + (erc-cmd-QUIT ""))))) + (ert-deftest erc-scenarios-sasl--external () :tags '(:expensive-test) (erc-scenarios-common-with-cleanup @@ -85,23 +149,26 @@ (erc-modules (cons 'sasl erc-modules)) (erc-sasl-password "wrong") (erc-sasl-mechanism 'plain) - (expect (erc-d-t-make-expecter)) - (buf nil)) + (erc--warnings-buffer-name "*ERC test warnings*") + (warnings-buffer (get-buffer-create erc--warnings-buffer-name)) + (inhibit-message noninteractive) + (expect (erc-d-t-make-expecter))) - (ert-info ("Connect") - (setq buf (erc :server "127.0.0.1" - :port port - :nick "tester" - :user "tester" - :full-name "tester")) - (let ((err (should-error - (with-current-buffer buf - (funcall expect 20 "Connection failed!"))))) - (should (string-search "please review" (cadr err))) - (with-current-buffer buf - (funcall expect 10 "Opening connection") - (funcall expect 20 "SASL authentication failed") - (should-not (erc-server-process-alive))))))) + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :full-name "tester") + (funcall expect 10 "Opening connection") + (funcall expect 20 "SASL authentication failed") + (funcall expect 20 "Connection failed!") + (should-not (erc-server-process-alive))) + + (with-current-buffer warnings-buffer + (funcall expect 10 "please review SASL settings"))) + + (when noninteractive + (should-not (get-buffer "*ERC test warnings*")))) (defun erc-scenarios--common--sasl (mech) (erc-scenarios-common-with-cleanup diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el new file mode 100644 index 00000000000..e99a05526f3 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom-relaxed.el @@ -0,0 +1,140 @@ +;;; erc-scenarios-scrolltobottom-relaxed.el --- erc-scrolltobottom-all relaxed -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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/>. + +;; TODO assert behavior of prompt input spanning multiple lines, with +;; and without line endings. + +;;; Code: + +(require 'ert-x) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +(ert-deftest erc-scenarios-scrolltobottom--relaxed () + :tags `(:expensive-test + ,@(and (getenv "ERC_TESTS_GRAPHICAL") '(:erc--graphical))) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "scrolltobottom") + (dumb-server (erc-d-run "localhost" t 'help)) + (port (process-contact dumb-server :service)) + (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) + (erc-scrolltobottom-all 'relaxed) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter)) + lower upper) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer "foonet" + (should (looking-at " and")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below 15) + (recenter 0) + + (ert-info ("Moving into prompt does not trigger scroll") + (with-selected-window (next-window) + (should-not (erc-scenarios-common--at-win-end-p)) + (recenter 0) + (goto-char (1- erc-insert-marker)) + (execute-kbd-macro "\C-n") + (should-not (erc-scenarios-common--at-win-end-p)) + (should (= (point) (point-max))) + (setq lower (count-screen-lines (window-start) (window-point))))) + + (ert-info ("Module `move-to-prompt' still works") + ;; Prompt is somewhere in the middle of the window. + (should (erc-scenarios-common--above-win-end-p)) + (should-not (= (point-max) (point))) + ;; Hitting a self-insert key triggers `move-to-prompt' but not + ;; a scroll (to bottom). + (execute-kbd-macro "hi") + ;; Prompt and input appear on same line. + (should (= (point-max) (point))) + (setq upper (count-screen-lines (window-start) (window-point))) + (should-not (= upper (window-body-height)))) + + (ert-info ("Command `recenter-top-bottom' allowed at prompt") + ;; Hitting C-l recenters the window. + (should (= upper (count-screen-lines (window-start) (window-point)))) + (let ((lines (list upper))) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (erc-scenarios-common--recenter-top-bottom) + (push (count-screen-lines (window-start) (window-point)) lines) + (setq lines (delete-dups lines)) + (should (= (length lines) 4)))) + + (ert-info ("Command `beginning-of-buffer' allowed at prompt") + ;; Hitting C-< goes to beginning of buffer. + (execute-kbd-macro "\M-<") + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New message doesn't trigger scroll when away from prompt") + ;; Arriving insertions don't trigger a scroll when away from the + ;; prompt. New output not seen. + (erc-cmd-MSG "NickServ help register") + (save-excursion (erc-d-t-search-for 10 "End of NickServ")) + (should (= 1 (point))) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New insertion keeps prompt stationary in other window") + (let ((w (next-window))) + ;; We're at prompt and completely stationary. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (= lower (count-screen-lines (window-start w) (window-point w)))) + (erc-d-t-ensure-for 0.5 + (= lower (count-screen-lines (window-start w) + (window-point w)))))) + + (should (= 2 (length (window-list)))) + (ert-info ("New message does not trigger a scroll when at prompt") + ;; Recenter so prompt is above rather than at window's end. + (funcall expect 10 "End of NickServ HELP") + (recenter 0) + (set-window-point nil (point-max)) + (setq upper (count-screen-lines (window-start) (window-point))) + ;; Prompt is somewhere in the middle of the window. + (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p)) + (erc-scenarios-common-say "/msg NickServ help identify") + ;; New arriving messages don't move prompt. + (erc-d-t-ensure-for 1 + (= upper (count-screen-lines (window-start) (window-point)))) + (funcall expect 10 "IDENTIFY lets you login"))))) + +;;; erc-scenarios-scrolltobottom-relaxed.el ends here diff --git a/test/lisp/erc/erc-scenarios-scrolltobottom.el b/test/lisp/erc/erc-scenarios-scrolltobottom.el new file mode 100644 index 00000000000..25b5c09577f --- /dev/null +++ b/test/lisp/erc/erc-scenarios-scrolltobottom.el @@ -0,0 +1,68 @@ +;;; erc-scenarios-scrolltobottom.el --- erc-scrolltobottom-mode -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-goodies) + +;; These two actually seem to run fine on Emacs 28, but skip them for +;; now to stay in sync with `erc-scenarios-scrolltobottom--relaxed'. + +(ert-deftest erc-scenarios-scrolltobottom--normal () + :tags `(:expensive-test ,@(and (getenv "ERC_TESTS_GRAPHICAL") + '(:erc--graphical))) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (erc-scenarios-common-scrolltobottom--normal + (lambda () + (ert-info ("New insertion doesn't anchor prompt in other window") + (let ((w (next-window))) + ;; We're at prompt but not aligned to bottom. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (not (erc-scenarios-common--at-win-end-p w)))))))) + +(ert-deftest erc-scenarios-scrolltobottom--all () + :tags `(:expensive-test ,@(and (getenv "ERC_TESTS_GRAPHICAL") + '(:erc--graphical))) + (when (version< emacs-version "29") (ert-skip "Times out")) + + (should-not erc-scrolltobottom-all) + + (let ((erc-scrolltobottom-all t)) + + (erc-scenarios-common-scrolltobottom--normal + (lambda () + (ert-info ("New insertion anchors prompt in other window") + (let ((w (next-window))) + ;; We're at prompt and aligned to bottom. + (should (>= (window-point w) erc-input-marker)) + (erc-d-t-wait-for 10 + (erc-scenarios-common--at-win-end-p w)) + (erc-d-t-ensure-for 0.5 + (erc-scenarios-common--at-win-end-p w)))))))) + +;;; erc-scenarios-scrolltobottom.el ends here diff --git a/test/lisp/erc/erc-scenarios-services-misc.el b/test/lisp/erc/erc-scenarios-services-misc.el index e79c5fc75db..ab4a97c5724 100644 --- a/test/lisp/erc/erc-scenarios-services-misc.el +++ b/test/lisp/erc/erc-scenarios-services-misc.el @@ -143,4 +143,109 @@ (erc-services-mode -1))) +;; The server rejects your nick during registration, so ERC acquires a +;; placeholder and successfully renicks once the connection is up. +;; See also `erc-scenarios-base-renick-self-auto'. + +(ert-deftest erc-scenarios-services-misc--reconnect-retry-nick () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-server-flood-penalty 0.1) + (erc-scenarios-common-dialog "services/regain") + (dumb-server (erc-d-run "localhost" t 'reconnect-retry + 'reconnect-retry-again)) + (port (process-contact dumb-server :service)) + (erc-server-auto-reconnect t) + (erc-modules `(services-regain sasl ,@erc-modules)) + (erc-services-regain-alist + '((Libera.Chat . erc-services-retry-nick-on-connect))) + (expect (erc-d-t-make-expecter))) + + ;; FIXME figure out and explain why this is so. + (should (featurep 'erc-services)) + + (ert-info ("Session succeeds but cut short") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :user "tester" + :password "changeme" + :full-name "tester") + (funcall expect 10 "Last login from") + (erc-cmd-JOIN "#test"))) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#test")) + (funcall expect 10 "was created on")) + + (ert-info ("Service restored") + (with-current-buffer "Libera.Chat" + (erc-d-t-wait-for 10 erc--server-reconnect-timer) + (funcall expect 10 "Connection failed!") + (funcall expect 10 "already in use") + (funcall expect 10 "changed mode for tester`") + (funcall expect 10 "Last login from") + (funcall expect 10 "Your new nickname is tester"))) + + (with-current-buffer (get-buffer "#test") + (funcall expect 10 "tester ") + (funcall expect 10 "was created on")))) + +;; This only asserts that the handler fires and issues the right +;; NickServ command, but it doesn't accurately recreate a +;; disconnection, but it probably should. +(ert-deftest erc-scenarios-services-misc--regain-command () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-server-flood-penalty 0.1) + (erc-scenarios-common-dialog "services/regain") + (dumb-server (erc-d-run "localhost" t 'taken-regain)) + (port (process-contact dumb-server :service)) + (erc-server-auto-reconnect t) + (erc-modules `(services-regain sasl ,@erc-modules)) + (erc-services-regain-alist + '((ExampleNet . erc-services-issue-regain))) + (expect (erc-d-t-make-expecter))) + + (should (featurep 'erc-services)) ; see note in prior test + + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "dummy" + :user "tester" + :password "changeme" + :full-name "tester" + :id 'ExampleNet) + (funcall expect 10 "dummy is already in use, trying dummy`") + (funcall expect 10 "You are now logged in as tester") + (funcall expect 10 "-NickServ- dummy has been regained.") + (funcall expect 10 "*** Your new nickname is dummy") + ;; Works with "given" `:id'. + (should (and (erc-network) (not (eq (erc-network) 'ExampleNet))))))) + +(ert-deftest erc-scenarios-services-misc--ghost-and-retry-nick () + :tags '(:expensive-test) + (erc-scenarios-common-with-cleanup + ((erc-server-flood-penalty 0.1) + (erc-scenarios-common-dialog "services/regain") + (dumb-server (erc-d-run "localhost" t 'taken-ghost)) + (port (process-contact dumb-server :service)) + (erc-server-auto-reconnect t) + (erc-modules `(services-regain sasl ,@erc-modules)) + (erc-services-regain-alist + '((FooNet . erc-services-issue-ghost-and-retry-nick))) + (expect (erc-d-t-make-expecter))) + + (should (featurep 'erc-services)) ; see note in prior test + + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "dummy" + :user "tester" + :password "changeme" + :full-name "tester") + (funcall expect 10 "dummy is already in use, trying dummy`") + (funcall expect 10 "You are now logged in as tester") + (funcall expect 10 "-NickServ- dummy has been ghosted.") + (funcall expect 10 "*** Your new nickname is dummy")))) + ;;; erc-scenarios-services-misc.el ends here diff --git a/test/lisp/erc/erc-scenarios-stamp.el b/test/lisp/erc/erc-scenarios-stamp.el new file mode 100644 index 00000000000..e4788f78654 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-stamp.el @@ -0,0 +1,181 @@ +;;; erc-scenarios-stamp.el --- Misc `erc-stamp' scenarios -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-stamp) + +(defvar erc-scenarios-stamp--user-marker nil) + +(defun erc-scenarios-stamp--on-post-modify () + (when-let (((erc--check-msg-prop 'erc--cmd 4))) + (set-marker erc-scenarios-stamp--user-marker (point-max)) + (ert-info ("User marker correctly placed at `erc-insert-marker'") + (should (= ?\n (char-before erc-scenarios-stamp--user-marker))) + (should (= erc-scenarios-stamp--user-marker erc-insert-marker)) + (save-excursion + (goto-char erc-scenarios-stamp--user-marker) + ;; The raw message ends in " Iabefhkloqv". However, + ;; `erc-server-004' only prints up to the 5th parameter. + (should (looking-back "CEIMRUabefhiklmnoqstuv\n")))))) + +(ert-deftest erc-scenarios-stamp--left/display-margin-mode () + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect)) + (port (process-contact dumb-server :service)) + (erc-scenarios-stamp--user-marker (make-marker)) + (erc-stamp--current-time 704591940) + (erc-stamp--tz t) + (erc-server-flood-penalty 0.1) + (erc-insert-timestamp-function #'erc-insert-timestamp-left) + (erc-modules (cons 'fill-wrap erc-modules)) + (erc-timestamp-only-if-changed-flag nil) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + + (add-hook 'erc-insert-post-hook #'erc-scenarios-stamp--on-post-modify + nil t) + (funcall expect 5 "This server is in debug mode") + + (ert-info ("Stamps appear in left margin and are invisible") + (should (eq 'erc-timestamp (field-at-pos (pos-bol)))) + (should (= (pos-bol) (field-beginning (pos-bol)))) + (should (eq 'query-notice (get-text-property (pos-bol) 'erc--msg))) + (should (eq 'NOTICE (get-text-property (pos-bol) 'erc--cmd))) + (should (= ?- (char-after (field-end (pos-bol))))) + (should (equal (get-text-property (1+ (field-end (pos-bol))) + 'erc--speaker) + "irc.foonet.org")) + (should (pcase (get-text-property (pos-bol) 'display) + (`((margin left-margin) ,s) + (eq 'timestamp (get-text-property 0 'invisible s)))))) + + ;; We set a third-party marker at the end of 004's message (on + ;; then "\n"), post-insertion. + (ert-info ("User markers untouched by subsequent message left stamp") + (save-excursion + (goto-char erc-scenarios-stamp--user-marker) + (should (looking-back "CEIMRUabefhiklmnoqstuv\n")) + (should (looking-at (rx "["))))))))) + +(ert-deftest erc-scenarios-stamp--legacy-date-stamps () + (with-suppressed-warnings ((obsolete erc-stamp-prepend-date-stamps-p)) + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (erc-stamp-prepend-date-stamps-p t) + (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect)) + (port (process-contact dumb-server :service)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 5 "Opening connection") + (goto-char (1- (match-beginning 0))) + (should (eq 'erc-timestamp (field-at-pos (point)))) + (should (eq 'unknown (erc--get-inserted-msg-prop 'erc--msg))) + ;; Force redraw of date stamp. + (setq erc-timestamp-last-inserted-left nil) + + (funcall expect 5 "This server is in debug mode") + (while (and (zerop (forward-line -1)) + (not (eq 'erc-timestamp (field-at-pos (point)))))) + (should (erc--get-inserted-msg-prop 'erc--cmd))))))) + +;; This user-owned hook member places a marker on the first message in +;; a buffer. Inserting a date stamp in front of it shouldn't move the +;; marker. +(defun erc-scenarios-stamp--on-insert-modify () + (unless (marker-position erc-scenarios-stamp--user-marker) + (set-marker erc-scenarios-stamp--user-marker (point-min)) + (save-excursion + (goto-char erc-scenarios-stamp--user-marker) + (should (looking-at "Opening")))) + + ;; Sometime after the first message ("Opening connection.."), assert + ;; that the marker we just placed hasn't moved. + (when (erc--check-msg-prop 'erc--cmd 2) + (save-restriction + (widen) + (ert-info ("Date stamp preserves opening user marker") + (goto-char erc-scenarios-stamp--user-marker) + (should-not (eq 'erc-timestamp (field-at-pos (point)))) + (should (looking-at "Opening")) + (should (eq 'unknown (get-text-property (point) 'erc--msg)))))) + + ;; On 003 ("*** This server was created on"), clear state to force a + ;; new date stamp on the next message. + (when (erc--check-msg-prop 'erc--cmd 3) + (setq erc-timestamp-last-inserted-left nil) + (set-marker erc-scenarios-stamp--user-marker erc-insert-marker))) + +(ert-deftest erc-scenarios-stamp--date-mode/left-and-right () + + (should (eq erc-insert-timestamp-function + #'erc-insert-timestamp-left-and-right)) + + (erc-scenarios-common-with-cleanup + ((erc-scenarios-common-dialog "base/reconnect") + (dumb-server (erc-d-run "localhost" t 'unexpected-disconnect)) + (port (process-contact dumb-server :service)) + (erc-scenarios-stamp--user-marker (make-marker)) + (erc-server-flood-penalty 0.1) + (erc-modules (if (zerop (random 2)) + (cons 'fill-wrap erc-modules) + erc-modules)) + (expect (erc-d-t-make-expecter)) + (erc-mode-hook + (cons (lambda () + (add-hook 'erc-insert-modify-hook + #'erc-scenarios-stamp--on-insert-modify -99 t)) + erc-mode-hook))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + + (funcall expect 5 "Welcome to the foonet") + (funcall expect 5 "*** AWAYLEN=390") + + (ert-info ("Date stamp preserves other user marker") + (goto-char erc-scenarios-stamp--user-marker) + (should-not (eq 'erc-timestamp (field-at-pos (point)))) + (should (looking-at (rx "*** irc.foonet.org oragono"))) + (should (eq 's004 (get-text-property (point) 'erc--msg)))) + + (funcall expect 5 "This server is in debug mode"))))) + +;;; erc-scenarios-stamp.el ends here diff --git a/test/lisp/erc/erc-scenarios-status-sidebar.el b/test/lisp/erc/erc-scenarios-status-sidebar.el new file mode 100644 index 00000000000..d447817e307 --- /dev/null +++ b/test/lisp/erc/erc-scenarios-status-sidebar.el @@ -0,0 +1,174 @@ +;;; erc-scenarios-status-sidebar.el --- erc-sidebar/speedbar tests -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-scenarios-common))) + +(require 'erc-status-sidebar) + + +(ert-deftest erc-scenarios-status-sidebar--bufbar () + :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-modules `(bufbar ,@erc-modules)) + (dumb-server (erc-d-run "localhost" t 'foonet 'barnet)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to two different endpoints") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester") + (funcall expect 10 "MOTD File is missing")) + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester") + (funcall expect 10 "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") + (ert-info ("Activity marker is in the right spot") + (let ((obuf (window-buffer))) ; *scratch* + (set-window-buffer (selected-window) "#foo") + (erc-d-t-wait-for 5 + (erc-status-sidebar-refresh) + (with-current-buffer "*ERC Status*" + (and (marker-position erc-status-sidebar--active-marker) + (goto-char erc-status-sidebar--active-marker) + ;; The " [N]" suffix disappears because it's selected + (search-forward "#foo" (pos-eol) t)))) + (set-window-buffer (selected-window) obuf)))) + + (with-current-buffer (erc-d-t-wait-for 20 (get-buffer "*ERC Status*")) + (ert-info ("Hierarchy printed correctly") + (funcall expect 10 "barnet [") + (funcall expect 10 "#bar [") + (funcall expect 10 "foonet [") + (funcall expect 10 "#foo"))) + + (with-current-buffer "#foo" + (ert-info ("Core toggle and kill commands work") + ;; Avoid using API, e.g., `erc-status-sidebar-buffer-exists-p', + ;; etc. for testing commands that call those same functions. + (should (get-buffer-window "*ERC Status*")) + (erc-bufbar-mode -1) + (should-not (get-buffer-window "*ERC Status*")) + (erc-status-sidebar-kill) + (should-not (get-buffer "*ERC Status*")))))) + +;; We can't currently run this on EMBA because it needs a usable +;; terminal, and we lack a fixture for that. Please try running this +;; test interactively with both graphical Emacs and non. +(declare-function erc-nickbar-mode "erc-speedbar" (arg)) +(declare-function erc-speedbar--get-timers "erc-speedbar" nil) +(declare-function speedbar-timer-fn "speedbar" nil) +(defvar erc-nickbar-mode) +(defvar speedbar-buffer) + +(ert-deftest erc-scenarios-status-sidebar--nickbar () + :tags `(:expensive-test :unstable ,@(and (getenv "ERC_TESTS_GRAPHICAL") + '(:erc--graphical))) + (when noninteractive (ert-skip "Interactive only")) + + (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-modules `(nickbar ,@erc-modules)) + (dumb-server (erc-d-run "localhost" t 'foonet 'barnet)) + (port (process-contact dumb-server :service)) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect to two different endpoints") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "foonet:changeme" + :full-name "tester") + (funcall expect 10 "MOTD File is missing")) + (with-current-buffer (erc :server "127.0.0.1" + :port port + :nick "tester" + :password "barnet:changeme" + :full-name "tester") + (funcall expect 10 "marked as being away"))) + + (erc-d-t-wait-for 20 (get-buffer "#bar")) + (with-current-buffer (pop-to-buffer "#bar") + (funcall expect 10 "was created on") + (funcall expect 2 "his second fit") + (erc-d-t-wait-for 10 (and speedbar-buffer (get-buffer speedbar-buffer))) + (speedbar-timer-fn) + (with-current-buffer speedbar-buffer + (funcall expect 10 "#bar (3)") + (funcall expect 10 '(| "@mike" "joe")) + (funcall expect 10 '(| "@mike" "joe")) + (funcall expect 10 "tester"))) + + (erc-d-t-wait-for 20 (get-buffer "#foo")) + (with-current-buffer (pop-to-buffer "#foo") + (delete-other-windows) + (funcall expect 10 "was created on") + (funcall expect 2 "no use of him") + (speedbar-timer-fn) + (with-current-buffer speedbar-buffer + (funcall expect 10 "#foo (3)") + (funcall expect 10 '(| "alice" "@bob")) + (funcall expect 10 '(| "alice" "@bob")) + (funcall expect 10 "tester"))) + + (with-current-buffer "#foo" + (ert-info ("Core toggle and kill commands work") + ;; Avoid using API, e.g., `erc-status-sidebar-buffer-exists-p', + ;; etc. for testing commands that call those same functions. + (call-interactively #'erc-nickbar-mode) + (should-not erc-nickbar-mode) + (should-not (and speedbar-buffer + (get-buffer-window speedbar-buffer))) + (should speedbar-buffer) + + (erc-nickbar-mode +1) + (should (and speedbar-buffer + (get-buffer-window speedbar-buffer))) + (should (get-buffer " SPEEDBAR")) + (erc-nickbar-mode -1) + (should-not (get-buffer " SPEEDBAR")) + (should-not erc-nickbar-mode) + (should-not (cdr (frame-list))))) + + (should-not (erc-speedbar--get-timers)))) + +;;; erc-scenarios-status-sidebar.el ends here diff --git a/test/lisp/erc/erc-services-tests.el b/test/lisp/erc/erc-services-tests.el index 2302ca83df6..9bafba98dc6 100644 --- a/test/lisp/erc/erc-services-tests.el +++ b/test/lisp/erc/erc-services-tests.el @@ -212,39 +212,32 @@ (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"))) +(defvar erc-services-tests--auth-source-plstore-standard-announced "\ +;;; public entries -*- mode: plstore -*- +((\"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\")) +;;; secret entries +((\"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") - + :text erc-services-tests--auth-source-plstore-standard-announced (let ((auth-sources (list plstore-file)) (auth-source-do-cache nil)) (erc-services-tests--auth-source-standard @@ -254,14 +247,7 @@ (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") - + :text erc-services-tests--auth-source-plstore-standard-announced (let ((auth-sources (list plstore-file)) (auth-source-do-cache nil)) (erc-services-tests--auth-source-announced @@ -271,29 +257,33 @@ (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") + :text "\ +;;; public entries -*- mode: plstore -*- +((\"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\") + (\"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\")) +;;; secret entries +((\"ba950d38118a76d71f9f0591bb373d6cb366a512\" :secret \"bar\") + (\"7f17ca445d11158065e911a6d0f4cbf52ca250e3\" :secret \"baz\") + (\"fcd3c8bd6daf4509de0ad6ee98e744ce0fca9377\" :secret \"foo\") + (\"1b3fab249a8dff77a4d8fe7eb4b0171b25cc711a\" :secret \"spam\") + (\"6cbcdc39476b8cfcca6f3e9a7876f41ec3f708cc\" :secret \"42\") + (\"a33e2b3bd2d6f33995a4b88710a594a100c5e41d\" :secret \"sesame\") + (\"ab2fd349b2b7d6a9215bb35a92d054261b0b1537\" :secret \"456\") + (\"61a6bd552059494f479ff720e8de33e22574650a\" :secret \"123\"))" (let ((auth-sources (list plstore-file)) (auth-source-do-cache nil)) @@ -303,17 +293,24 @@ ;; 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")]) +(defvar erc-services-tests--auth-source-json-standard-announced "\ +[{\"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 + :text erc-services-tests--auth-source-json-standard-announced :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)))) @@ -321,10 +318,7 @@ (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)) - + :text erc-services-tests--auth-source-json-standard-announced (let ((auth-sources (list plstore-file)) (auth-source-do-cache nil)) (erc-services-tests--auth-source-announced #'erc-auth-source-search)))) @@ -332,16 +326,36 @@ (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")]))) - + :text "\ +[{\"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\"}, + {\"host\": \"GNU.chat\", + \"user\": \"#chan\", + \"port\": \"6697\", + \"secret\": \"spam\"}, + {\"host\": \"my.gnu.org\", + \"user\": \"#fsf\", + \"port\": \"irc\", + \"secret\": \"42\"}, + {\"host\": \"irc.gnu.org\", + \"port\": \"6667\", + \"secret\": \"sesame\"}, + {\"host\": \"MyHost\", + \"port\": \"irc\", + \"secret\": \"456\"}, + {\"host\": \"MyHost\", + \"port\": \"6667\", + \"secret\": \"123\"}]" (let ((auth-sources (list json-file)) (auth-source-do-cache nil)) (erc-services-tests--auth-source-overrides #'erc-auth-source-search)))) @@ -370,6 +384,14 @@ ("#chan@my.gnu.org:irc" . "baz") ("#chan@GNU.chat:irc" . "foo"))) +(defun erc-services-tests--secrets-search-items (entries _ &rest r) + (mapcan (lambda (s) + (and (seq-every-p (pcase-lambda (`(,k . ,v)) + (equal v (alist-get k (cdr s)))) + (map-pairs r)) + (list (car s)))) + entries)) + (ert-deftest erc--auth-source-search--secrets-standard () (skip-unless (bound-and-true-p secrets-enabled)) (let ((auth-sources '("secrets:Test")) @@ -378,18 +400,12 @@ (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))) + (apply-partially #'erc-services-tests--secrets-search-items + entries)) ((symbol-function 'secrets-get-secret) - (lambda (col label) - (should (equal col "Test")) - (assoc-default label secrets))) + (lambda (_ label) (assoc-default label secrets))) ((symbol-function 'secrets-get-attributes) - (lambda (col label) - (should (equal col "Test")) - (assoc-default label entries)))) + (lambda (_ label) (assoc-default label entries)))) (erc-services-tests--auth-source-standard #'erc-auth-source-search)))) @@ -401,18 +417,12 @@ (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))) + (apply-partially #'erc-services-tests--secrets-search-items + entries)) ((symbol-function 'secrets-get-secret) - (lambda (col label) - (should (equal col "Test")) - (assoc-default label secrets))) + (lambda (_ label) (assoc-default label secrets))) ((symbol-function 'secrets-get-attributes) - (lambda (col label) - (should (equal col "Test")) - (assoc-default label entries)))) + (lambda (_ label) (assoc-default label entries)))) (erc-services-tests--auth-source-announced #'erc-auth-source-search)))) @@ -444,17 +454,12 @@ ("MyHost:6667" . "123")))) (cl-letf (((symbol-function 'secrets-search-items) - (lambda (col &rest _) - (should (equal col "Test")) - (map-keys entries))) + (apply-partially #'erc-services-tests--secrets-search-items + entries)) ((symbol-function 'secrets-get-secret) - (lambda (col label) - (should (equal col "Test")) - (assoc-default label secrets))) + (lambda (_ label) (assoc-default label secrets))) ((symbol-function 'secrets-get-attributes) - (lambda (col label) - (should (equal col "Test")) - (assoc-default label entries)))) + (lambda (_ label) (assoc-default label entries)))) (erc-services-tests--auth-source-overrides #'erc-auth-source-search)))) diff --git a/test/lisp/erc/erc-stamp-tests.el b/test/lisp/erc/erc-stamp-tests.el new file mode 100644 index 00000000000..3f17e36e002 --- /dev/null +++ b/test/lisp/erc/erc-stamp-tests.el @@ -0,0 +1,352 @@ +;;; erc-stamp-tests.el --- Tests for erc-stamp. -*- lexical-binding:t -*- + +;; Copyright (C) 2023 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) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-tests-common))) + +(require 'erc-stamp) +(require 'erc-goodies) ; for `erc-make-read-only' + +;; These display-oriented tests are brittle because many factors +;; influence how text properties are applied. We should just +;; rework these into full scenarios. + +(defun erc-stamp-tests--insert-right (test) + (let ((val (list 0 0)) + (erc-insert-modify-hook '(erc-add-timestamp)) + (erc-insert-post-hook '(erc-make-read-only)) ; see comment above + (erc-timestamp-only-if-changed-flag nil) + ;; + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + + (advice-add 'erc-format-timestamp :filter-args + (lambda (args) (cons (cl-incf (cadr val) 60) (cdr args))) + '((name . ert-deftest--erc-timestamp-use-align-to))) + + (with-current-buffer (get-buffer-create "*erc-stamp-tests--insert-right*") + (erc-mode) + (erc-munge-invisibility-spec) + (erc--initialize-markers (point) nil) + (erc-tests-common-init-server-proc "sleep" "1") + + (funcall test) + + (when noninteractive + (kill-buffer))) + + (advice-remove 'erc-format-timestamp + 'ert-deftest--erc-timestamp-use-align-to))) + +(defun erc-stamp-tests--use-align-to--nil (compat) + (erc-stamp-tests--insert-right + (lambda () + + (ert-info ("nil, normal") + (let ((erc-timestamp-use-align-to nil)) + (erc-display-message nil 'notice (current-buffer) "begin")) + (goto-char (point-min)) + (should (search-forward-regexp + (rx "begin" (+ "\t") (* " ") "[") nil t)) + ;; Field includes intervening spaces + (should (eql ?n (char-before (field-beginning (point))))) + ;; Timestamp extends to the end of the line + (should (eql ?\n (char-after (field-end (point)))))) + + ;; The option `erc-timestamp-right-column' is normally nil by + ;; default, but it's a convenient stand in for a sufficiently + ;; small `erc-fill-column' (we can force a line break without + ;; involving that module). + (should-not erc-timestamp-right-column) + + (ert-info ("nil, overlong (hard wrap)") + (let ((erc-timestamp-use-align-to nil) + (erc-timestamp-right-column 20)) + (erc-display-message nil 'notice (current-buffer) + "twenty characters")) + (should (search-forward-regexp (rx bol (+ "\t") (* " ") "[") nil t)) + ;; Field includes leading whitespace. + (should (eql (if compat ?\[ ?\n) + (char-after (field-beginning (point))))) + ;; Timestamp extends to the end of the line. + (should (eql ?\n (char-after (field-end (point))))))))) + +(ert-deftest erc-timestamp-use-align-to--nil () + (ert-info ("Field starts on stamp text (compat)") + (let ((erc-stamp--omit-properties-on-folded-lines t)) + (erc-stamp-tests--use-align-to--nil 'compat))) + (ert-info ("Field includes leaidng white space") + (erc-stamp-tests--use-align-to--nil nil))) + +(defun erc-stamp-tests--use-align-to--t (compat) + (erc-stamp-tests--insert-right + (lambda () + + (ert-info ("t, normal") + (let ((erc-timestamp-use-align-to t)) + (let ((msg (erc-format-privmessage "bob" "msg one" nil t))) + (erc-display-message nil nil (current-buffer) msg))) + (goto-char (point-min)) + ;; Exactly two spaces, one from format, one added by erc-stamp. + (should (search-forward "msg one [" nil t)) + ;; Field covers space between. + (should (eql ?e (char-before (field-beginning (point))))) + (should (eql ?\n (char-after (field-end (point)))))) + + (ert-info ("t, overlong (hard wrap)") + (let ((erc-timestamp-use-align-to t) + (erc-timestamp-right-column 20)) + (let ((msg (erc-format-privmessage "bob" "tttt wwww oooo" nil t))) + (erc-display-message nil nil (current-buffer) msg))) + ;; Indented to pos (this is arguably a bug). + (should (search-forward-regexp (rx bol (+ "\t") (* " ") "[") nil t)) + ;; Field includes leading space. + (should (eql (if compat ?\[ ?\n) (char-after (field-beginning (point))))) + (should (eql ?\n (char-after (field-end (point))))))))) + +(ert-deftest erc-timestamp-use-align-to--t () + (ert-info ("Field starts on stamp text (compat)") + (let ((erc-stamp--omit-properties-on-folded-lines t)) + (erc-stamp-tests--use-align-to--t 'compat))) + (ert-info ("Field includes leaidng white space") + (erc-stamp-tests--use-align-to--t nil))) + +(ert-deftest erc-timestamp-use-align-to--integer () + (erc-stamp-tests--insert-right + (lambda () + + (ert-info ("integer, normal") + (let ((erc-timestamp-use-align-to 1)) + (let ((msg (erc-format-privmessage "bob" "msg one" nil t))) + (erc-display-message nil nil (current-buffer) msg))) + (goto-char (point-min)) + ;; Space not added because included in format string. + (should (search-forward "msg one [" nil t)) + ;; Field covers space between. + (should (eql ?e (char-before (field-beginning (point))))) + (should (eql ?\n (char-after (field-end (point)))))) + + (ert-info ("integer, overlong (hard wrap)") + (let ((erc-timestamp-use-align-to 1) + (erc-timestamp-right-column 20)) + (let ((msg (erc-format-privmessage "bob" "tttt wwww oooo" nil t))) + (erc-display-message nil nil (current-buffer) msg))) + ;; No hard wrap + (should (search-forward "oooo [" nil t)) + ;; Field starts at leading space. + (should (eql ?\s (char-after (field-beginning (point))))) + (should (eql ?\n (char-after (field-end (point))))))))) + +(ert-deftest erc-stamp--display-margin-mode--right () + (erc-stamp-tests--insert-right + (lambda () + (erc-stamp--display-margin-mode +1) + + (ert-info ("margin, normal") + (let ((erc-timestamp-use-align-to 'margin)) + (let ((msg (erc-format-privmessage "bob" "msg one" nil t))) + (put-text-property 0 (length msg) 'wrap-prefix 10 msg) + (erc-display-message nil nil (current-buffer) msg))) + (goto-char (point-min)) + ;; Space not added (treated as opaque string). + (should (search-forward "msg one[" nil t)) + ;; Field covers stamp alone + (should (eql ?e (char-before (field-beginning (point))))) + ;; Vanity props extended + (should (get-text-property (field-beginning (point)) 'wrap-prefix)) + (should (get-text-property (1+ (field-beginning (point))) 'wrap-prefix)) + (should (get-text-property (1- (field-end (point))) 'wrap-prefix)) + (should (eql ?\n (char-after (field-end (point)))))) + + (ert-info ("margin, overlong (hard wrap)") + (let ((erc-timestamp-use-align-to 'margin) + (erc-timestamp-right-column 20)) + (let ((msg (erc-format-privmessage "bob" "tttt wwww oooo" nil t))) + (erc-display-message nil nil (current-buffer) msg))) + ;; No hard wrap + (should (search-forward "oooo[" nil t)) + ;; Field starts at format string (right bracket) + (should (eql ?\[ (char-after (field-beginning (point))))) + (should (eql ?\n (char-after (field-end (point))))))))) + +;; This concerns a proposed partial reversal of the changes resulting +;; from: +;; +;; 24.1.50; Wrong behavior of move-end-of-line in ERC (Bug#11706) +;; +;; Perhaps core behavior has changed since this bug was reported, but +;; C-e stopping one char short of EOL no longer seems a problem. +;; However, invoking C-n (`next-line') exhibits a similar effect. +;; When point is in a stamp or near the beginning of a line, issuing a +;; C-n puts point one past the start of the message (i.e., two chars +;; beyond the timestamp's closing "]". Dropping the invisible +;; property when timestamps are hidden does indeed prevent this, but +;; it's also a lasting commitment. The docs mention that it's +;; pointless to pair the old `intangible' property with `invisible' +;; and suggest users look at `cursor-intangible-mode'. Turning off +;; the latter does indeed do the trick as does decrementing the end of +;; the `cursor-intangible' interval so that, in addition to C-n +;; working, a C-f from before the timestamp doesn't overshoot. This +;; appears to be the case whether `erc-hide-timestamps' is enabled or +;; not, but it may be inadvisable for some reason (a hack) and +;; therefore warrants further investigation. +;; +;; Note some striking omissions here: +;; +;; 1. a lack of `fill' module integration (we simulate it by +;; making lines short enough to not wrap) +;; 2. functions like `line-move' behave differently when +;; `noninteractive' +;; 3. no actual test assertions involving `cursor-sensor' movement +;; even though that's a huge ingredient + +(ert-deftest erc-timestamp-intangible--left () + (let ((erc-timestamp-only-if-changed-flag nil) + (erc-timestamp-intangible t) ; default changed to nil in 2014 + (erc-hide-timestamps t) + (erc-insert-timestamp-function 'erc-insert-timestamp-left) + (erc-insert-modify-hook '(erc-make-read-only erc-add-timestamp)) + msg + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (should (not cursor-sensor-inhibit)) + + (erc-mode) + (erc-tests-common-init-server-proc "true") + (with-current-buffer (get-buffer-create "*erc-timestamp-intangible*") + (erc-mode) + (erc--initialize-markers (point) nil) + (erc-munge-invisibility-spec) + (erc-display-message nil 'notice (current-buffer) "Welcome") + ;; + ;; Pretend `fill' is active and that these lines are + ;; folded. Otherwise, there's an annoying issue on wrapped lines + ;; (when visual-line-mode is off and stamps are visible) where + ;; C-e sends you to the end of the previous line. + (setq msg "Lorem ipsum dolor sit amet") + (erc-display-message nil nil (current-buffer) + (erc-format-privmessage "alyssa" msg nil t)) + (erc-display-message nil 'notice (current-buffer) "Home") + (goto-char (point-min)) + + ;; EOL is actually EOL (Bug#11706) + + (ert-info ("Notice before stamp, C-e") ; first line/stamp + (should (search-forward "Welcome" nil t)) + (ert-simulate-command '(erc-bol)) + (should (looking-at (rx "["))) + (let ((end (pos-eol))) ; `line-end-position' fails because fields + (ert-simulate-command '(move-end-of-line 1)) + (should (= end (point))))) + + (ert-info ("Privmsg before stamp, C-e") + (should (search-forward "Lorem" nil t)) + (goto-char (pos-bol)) + (should (looking-at (rx "["))) + (let ((end (pos-eol))) + (ert-simulate-command '(move-end-of-line 1)) + (should (= end (point))))) + + (ert-info ("Privmsg first line, C-e") + (goto-char (pos-bol)) + (should (search-forward "ipsum" nil t)) + (let ((end (pos-eol))) + (ert-simulate-command '(move-end-of-line 1)) + (should (= end (point))))) + + (when noninteractive + (kill-buffer))))) + +(ert-deftest erc-echo-timestamp () + :tags (and (null (getenv "CI")) '(:unstable)) + + (should-not erc-echo-timestamps) + (should-not erc-stamp--last-stamp) + (insert (propertize "a" 'erc--ts 433483200 'erc--msg 'msg) "bc") + (goto-char (point-min)) + (let ((inhibit-message t) + (erc-echo-timestamp-format "%Y-%m-%d %H:%M:%S %Z") + (erc-echo-timestamp-zone (list (* 60 60 -4) "EDT"))) + + ;; No-op when non-interactive and option is nil + (should-not (erc--echo-ts-csf nil nil 'entered)) + (should-not erc-stamp--last-stamp) + + ;; Non-interactive (cursor sensor function) + (let ((erc-echo-timestamps t)) + (should (equal (erc--echo-ts-csf nil nil 'entered) + "1983-09-27 00:00:00 EDT"))) + (should (= 433483200 erc-stamp--last-stamp)) + + ;; Interactive + (should (equal (call-interactively #'erc-echo-timestamp) + "1983-09-27 00:00:00 EDT")) + ;; Interactive with zone + (let ((current-prefix-arg '(4))) + (should (member (call-interactively #'erc-echo-timestamp) + '("1983-09-27 04:00:00 GMT" + "1983-09-27 04:00:00 UTC")))) + (let ((current-prefix-arg -7)) + (should (equal (call-interactively #'erc-echo-timestamp) + "1983-09-26 21:00:00 -07"))))) + +(defun erc-stamp-tests--assert-get-inserted-msg/stamp (test-fn) + (let ((erc-insert-modify-hook erc-insert-modify-hook) + (erc-insert-timestamp-function 'erc-insert-timestamp-right) + (erc-timestamp-use-align-to 0) + (erc-timestamp-format "[00:00]")) + (cl-pushnew 'erc-add-timestamp erc-insert-modify-hook) + (erc-tests-common-get-inserted-msg-setup)) + (goto-char 19) + (should (looking-back (rx "<bob> hi [00:00]"))) + (erc-tests-common-assert-get-inserted-msg 3 19 test-fn)) + +(ert-deftest erc--get-inserted-msg-beg/stamp () + (erc-stamp-tests--assert-get-inserted-msg/stamp + (lambda (arg) (should (= 3 (erc--get-inserted-msg-beg arg)))))) + +(ert-deftest erc--get-inserted-msg-beg/readonly/stamp () + (erc-tests-common-assert-get-inserted-msg-readonly-with + #'erc-stamp-tests--assert-get-inserted-msg/stamp + (lambda (arg) (should (= 3 (erc--get-inserted-msg-beg arg)))))) + +(ert-deftest erc--get-inserted-msg-end/stamp () + (erc-stamp-tests--assert-get-inserted-msg/stamp + (lambda (arg) (should (= 19 (erc--get-inserted-msg-end arg)))))) + +(ert-deftest erc--get-inserted-msg-end/readonly/stamp () + (erc-tests-common-assert-get-inserted-msg-readonly-with + #'erc-stamp-tests--assert-get-inserted-msg/stamp + (lambda (arg) (should (= 19 (erc--get-inserted-msg-end arg)))))) + +(ert-deftest erc--get-inserted-msg-bounds/stamp () + (erc-stamp-tests--assert-get-inserted-msg/stamp + (lambda (arg) + (should (equal '(3 . 19) (erc--get-inserted-msg-bounds arg)))))) + +(ert-deftest erc--get-inserted-msg-bounds/readonly/stamp () + (erc-tests-common-assert-get-inserted-msg-readonly-with + #'erc-stamp-tests--assert-get-inserted-msg/stamp + (lambda (arg) + (should (equal '(3 . 19) (erc--get-inserted-msg-bounds arg)))))) + +;;; erc-stamp-tests.el ends here diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el index 9a72671ad97..2cd47ec3f89 100644 --- a/test/lisp/erc/erc-tests.el +++ b/test/lisp/erc/erc-tests.el @@ -22,7 +22,10 @@ ;;; Code: (require 'ert-x) -(require 'erc) +(eval-and-compile + (let ((load-path (cons (ert-resource-directory) load-path))) + (require 'erc-tests-common))) + (require 'erc-ring) (ert-deftest erc--read-time-period () @@ -69,26 +72,25 @@ (with-current-buffer (get-buffer-create "#foo") (erc-mode) (setq erc-server-process proc-exnet) - (setq erc-default-recipients '("#foo"))) + (setq erc--target (erc--target-from-string "#foo"))) (with-current-buffer (get-buffer-create "#spam") (erc-mode) (setq erc-server-process proc-onet) - (setq erc-default-recipients '("#spam"))) + (setq erc--target (erc--target-from-string "#spam"))) (with-current-buffer (get-buffer-create "#bar") (erc-mode) (setq erc-server-process proc-onet) - (setq erc-default-recipients '("#bar"))) + (setq erc--target (erc--target-from-string "#bar"))) (with-current-buffer (get-buffer-create "#baz") (erc-mode) (setq erc-server-process proc-exnet) - (setq erc-default-recipients '("#baz"))) + (setq erc--target (erc--target-from-string "#baz"))) (should (eq (get-buffer-process "ExampleNet") proc-exnet)) - (erc-with-all-buffers-of-server (get-buffer-process "ExampleNet") - nil + (erc-with-all-buffers-of-server (get-buffer-process "ExampleNet") nil (kill-buffer)) (should-not (get-buffer "ExampleNet")) @@ -102,8 +104,7 @@ (calls 0) (get-test (lambda () (cl-incf calls) test))) - (erc-with-all-buffers-of-server proc-onet - (funcall get-test) + (erc-with-all-buffers-of-server proc-onet (funcall get-test) (kill-buffer)) (should (= calls 1))) @@ -113,36 +114,66 @@ (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-with-server-buffer () + (setq erc-away 1) + (erc-tests-common-init-server-proc "sleep" "1") + + (let (mockingp calls) + (advice-add 'buffer-local-value :after + (lambda (&rest r) (when mockingp (push r calls))) + '((name . erc-with-server-buffer))) + + (should (= 1 (prog2 (setq mockingp t) + (erc-with-server-buffer erc-away) + (setq mockingp nil)))) + + (should (equal (pop calls) (list 'erc-away (current-buffer)))) + + (should (= 1 (prog2 (setq mockingp t) + (erc-with-server-buffer (ignore 'me) erc-away) + (setq mockingp nil)))) + (should-not calls) + + (advice-remove 'buffer-local-value 'erc-with-server-buffer))) + +(ert-deftest erc--with-dependent-type-match () + (should (equal (macroexpand-1 + '(erc--with-dependent-type-match (repeat face) erc-match)) + '(backquote-list* + 'repeat :match (lambda (w v) + (require 'erc-match) + (widget-editable-list-match w v)) + '(face))))) + +(ert-deftest erc--doarray () + (let ((array "abcdefg") + out) + ;; No return form. + (should-not (erc--doarray (c array) (push c out))) + (should (equal out '(?g ?f ?e ?d ?c ?b ?a))) + + ;; Return form evaluated upon completion. + (setq out nil) + (should (= 42 (erc--doarray (c array (+ 39 (length out))) + (when (cl-evenp c) (push c out))))) + (should (equal out '(?f ?d ?b))))) (ert-deftest erc-hide-prompt () - (let (erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (let ((erc-hide-prompt erc-hide-prompt) + ;; + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) (with-current-buffer (get-buffer-create "ServNet") - (erc-tests--send-prep) + (erc-tests-common-prep-for-insertion) (goto-char erc-insert-marker) (should (looking-at-p (regexp-quote erc-prompt))) - (erc-tests--set-fake-server-process "sleep" "1") + (erc-tests-common-init-server-proc "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) + (erc-tests-common-prep-for-insertion) (goto-char erc-insert-marker) (should (looking-at-p (regexp-quote erc-prompt))) (setq erc-server-process (buffer-local-value 'erc-server-process @@ -150,7 +181,7 @@ erc--target (erc--target-from-string "#chan"))) (with-current-buffer (get-buffer-create "bob") - (erc-tests--send-prep) + (erc-tests-common-prep-for-insertion) (goto-char erc-insert-marker) (should (looking-at-p (regexp-quote erc-prompt))) (setq erc-server-process (buffer-local-value 'erc-server-process @@ -162,101 +193,291 @@ (with-current-buffer "ServNet" (should (= (point) erc-insert-marker)) (erc--hide-prompt erc-server-process) - (should (string= ">" (get-text-property (point) 'display)))) + (should (string= ">" (get-char-property (point) 'display)))) (with-current-buffer "#chan" (goto-char erc-insert-marker) - (should (string= ">" (get-text-property (point) 'display))) + (should (string= ">" (get-char-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 (get-char-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 (string= ">" (get-char-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 (get-char-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 (get-char-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)))) + (should-not (get-char-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)))) + (should (eq (get-text-property erc-insert-marker 'erc-prompt) 'hidden)) + (should (string= ">" (get-char-property erc-insert-marker 'display)))) (with-current-buffer "#chan" - (should-not (get-text-property erc-insert-marker 'display))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "bob" - (should-not (get-text-property erc-insert-marker 'display))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "ServNet" (erc--unhide-prompt) - (should-not (get-text-property erc-insert-marker 'display)))) + (should (eq (get-text-property erc-insert-marker 'erc-prompt) t)) + (should-not (get-char-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))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "bob" - (should-not (get-text-property erc-insert-marker 'display))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "#chan" - (should (string= ">" (get-text-property erc-insert-marker 'display))) + (should (string= ">" (get-char-property erc-insert-marker 'display))) + (should (eq (get-text-property erc-insert-marker 'erc-prompt) 'hidden)) (erc--unhide-prompt) - (should-not (get-text-property erc-insert-marker 'display)))) + (should (eq (get-text-property erc-insert-marker 'erc-prompt) t)) + (should-not (get-char-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))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "bob" - (should (string= ">" (get-text-property erc-insert-marker 'display))) + (should (string= ">" (get-char-property erc-insert-marker 'display))) + (should (eq (get-text-property erc-insert-marker 'erc-prompt) 'hidden)) (erc--unhide-prompt) - (should-not (get-text-property erc-insert-marker 'display))) + (should (eq (get-text-property erc-insert-marker 'erc-prompt) t)) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "#chan" - (should-not (get-text-property erc-insert-marker 'display)))) + (should-not (get-char-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))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "bob" - (should-not (get-text-property erc-insert-marker 'display))) + (should-not (get-char-property erc-insert-marker 'display))) (with-current-buffer "#chan" - (should-not (get-text-property erc-insert-marker 'display)) + (should-not (get-char-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)))) + (should-not (get-char-property erc-insert-marker 'display)))) (when noninteractive (kill-buffer "#chan") (kill-buffer "bob") (kill-buffer "ServNet")))) +(ert-deftest erc--refresh-prompt () + (let* ((counter 0) + (erc-prompt (lambda () + (format "%s %d>" + (erc-format-target-and/or-network) + (cl-incf counter)))) + erc-accidental-paste-threshold-seconds + erc-insert-modify-hook + (erc-modules (remq 'stamp erc-modules)) + (erc-send-input-line-function #'ignore) + (erc--input-review-functions erc--input-review-functions) + erc-send-completed-hook) + + (ert-info ("Server buffer") + (with-current-buffer (get-buffer-create "ServNet") + (erc-tests-common-prep-for-insertion) + (goto-char erc-insert-marker) + (should (looking-at-p "ServNet 3>")) + (erc-tests-common-init-server-proc "sleep" "1") + (set-process-sentinel erc-server-process #'ignore) + (setq erc-network 'ServNet + erc-server-current-nick "tester" + erc-networks--id (erc-networks--id-create nil) + erc-server-users (make-hash-table :test 'equal)) + (set-process-query-on-exit-flag erc-server-process nil) + ;; Incoming message redraws prompt + (erc-display-message nil 'notice nil "Welcome") + (should (looking-at-p (rx "*** Welcome"))) + (forward-line) + (should (looking-at-p "ServNet 4>")) + ;; Say something + (goto-char erc-input-marker) + (insert "Howdy") + (erc-send-current-line) + (save-excursion (forward-line -1) + (should (looking-at (rx "*** No target"))) + (forward-line -1) + (should (looking-at "<tester> Howdy"))) + (should (looking-back "ServNet 6> ")) + (should (= erc-input-marker (point))) + ;; Space after prompt is unpropertized + (should (get-text-property (1- erc-input-marker) 'erc-prompt)) + (should-not (get-text-property erc-input-marker 'erc-prompt)) + ;; No sign of old prompts + (save-excursion + (goto-char (point-min)) + (should-not (search-forward (rx (any "3-5") ">") nil t))))) + + (ert-info ("Channel buffer") + (with-current-buffer (get-buffer-create "#chan") + (erc-tests-common-prep-for-insertion) + (goto-char erc-insert-marker) + (should (looking-at-p "#chan 9>")) + (goto-char erc-input-marker) + (setq erc-server-process (buffer-local-value 'erc-server-process + (get-buffer "ServNet")) + erc-networks--id (erc-with-server-buffer erc-networks--id) + erc--target (erc--target-from-string "#chan") + erc-default-recipients (list "#chan") + erc-channel-users (make-hash-table :test 'equal)) + (erc-update-current-channel-member "alice" "alice") + (erc-update-current-channel-member "bob" "bob") + (erc-update-current-channel-member "tester" "tester") + (erc-display-message nil nil (current-buffer) + (erc-format-privmessage "alice" "Hi" nil t)) + (should (looking-back "#chan@ServNet 10> ")) + (goto-char erc-input-marker) + (insert "Howdy") + (erc-send-current-line) + (save-excursion (forward-line -1) + (should (looking-at "<tester> Howdy"))) + (should (looking-back "#chan@ServNet 11> ")) + (should (= (point) erc-input-marker)) + (insert "/query bob") + (let (erc-modules) + (erc-send-current-line)) + ;; Last command not inserted + (save-excursion (forward-line -1) + (should (looking-at "<tester> Howdy"))) + ;; Query does not redraw (nor /help, only message input) + (should (looking-back "#chan@ServNet 11> ")) + ;; No sign of old prompts + (save-excursion + (goto-char (point-min)) + (should-not (search-forward (rx (or "9" "10") ">") nil t))))) + + (ert-info ("Query buffer") + (with-current-buffer (get-buffer "bob") + (goto-char erc-insert-marker) + (should (looking-at-p "bob@ServNet 14>")) + (goto-char erc-input-marker) + (erc-display-message nil nil (current-buffer) + (erc-format-privmessage "bob" "Hi" nil t)) + (should (looking-back "bob@ServNet 15> ")) + (goto-char erc-input-marker) + (insert "Howdy") + (erc-send-current-line) + (save-excursion (forward-line -1) + (should (looking-at "<tester> Howdy"))) + (should (looking-back "bob@ServNet 16> ")) + ;; No sign of old prompts + (save-excursion + (goto-char (point-min)) + (should-not (search-forward (rx (or "14" "15") ">") nil t))))) + + (when noninteractive + (kill-buffer "#chan") + (kill-buffer "bob") + (kill-buffer "ServNet")))) + +(ert-deftest erc--initialize-markers () + (let ((proc (start-process "true" (current-buffer) "true")) + erc-modules + erc-connect-pre-hook + erc-insert-modify-hook + erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (set-process-query-on-exit-flag proc nil) + (erc-mode) + (setq erc-server-process proc + erc-networks--id (erc-networks--id-create 'foonet)) + (erc-open "localhost" 6667 "tester" "Tester" nil + "fake" nil "#chan" proc nil "user" nil) + (with-current-buffer (should (get-buffer "#chan")) + (should (= ?\n (char-after 1))) + (should (= ?E (char-after erc-insert-marker))) + (should (= 3 (marker-position erc-insert-marker))) + (should (= 8 (marker-position erc-input-marker))) + (should (= 8 (point-max))) + (should (= 8 (point))) + ;; These prompt properties are a continual source of confusion. + ;; Including the literal defaults here can hopefully serve as a + ;; quick reference for anyone operating in that area. + (should (equal (buffer-string) + #("\n\nERC> " + 2 6 ( font-lock-face erc-prompt-face + rear-nonsticky t + erc-prompt t + field erc-prompt + front-sticky t + read-only t) + 6 7 ( rear-nonsticky t + erc-prompt t + field erc-prompt + front-sticky t + read-only t)))) + + ;; Simulate some activity by inserting some text before and + ;; after the prompt (multiline). + (erc-display-error-notice nil "Welcome") + (goto-char (point-max)) + (insert "Hello\nWorld") + (goto-char 3) + (should (looking-at-p (regexp-quote "*** Welcome")))) + + (ert-info ("Reconnect") + (with-current-buffer (erc-server-buffer) + (erc-open "localhost" 6667 "tester" "Tester" nil + "fake" nil "#chan" proc nil "user" nil)) + (should-not (get-buffer "#chan<2>"))) + + (ert-info ("Existing prompt respected") + (with-current-buffer (should (get-buffer "#chan")) + (should (= ?\n (char-after 1))) + (should (= ?E (char-after erc-insert-marker))) + (should (= 15 (marker-position erc-insert-marker))) + (should (= 20 (marker-position erc-input-marker))) + (should (= 3 (point))) ; point restored + (should (equal (buffer-string) + #("\n\n*** Welcome\nERC> Hello\nWorld" + 2 13 (font-lock-face erc-error-face) + 14 18 ( font-lock-face erc-prompt-face + rear-nonsticky t + erc-prompt t + field erc-prompt + front-sticky t + read-only t) + 18 19 ( rear-nonsticky t + erc-prompt t + field erc-prompt + front-sticky t + read-only t)))) + (when noninteractive + (kill-buffer)))))) + (ert-deftest erc--switch-to-buffer () (defvar erc-modified-channels-alist) ; lisp/erc/erc-track.el @@ -314,6 +535,80 @@ (dolist (b '("server" "other" "#chan" "#foo" "#fake")) (kill-buffer b)))) +(ert-deftest erc-setup-buffer--custom-action () + (erc-mode) + (erc-tests-common-init-server-proc "sleep" "1") + (setq erc--server-last-reconnect-count 0) + (let ((owin (selected-window)) + (obuf (window-buffer)) + (mbuf (messages-buffer)) + calls) + (cl-letf (((symbol-function 'switch-to-buffer) ; regression + (lambda (&rest r) (push (cons 'switch-to-buffer r) calls))) + ((symbol-function 'erc--test-fun) + (lambda (&rest r) (push (cons 'erc--test-fun r) calls))) + ((symbol-function 'display-buffer) + (lambda (&rest r) (push (cons 'display-buffer r) calls)))) + + ;; Baseline + (let ((erc-join-buffer 'bury)) + (erc-setup-buffer mbuf) + (should-not calls)) + + (should-not erc--display-context) + + ;; `display-buffer' + (let ((erc--display-context '((erc-buffer-display . 1))) + (erc-join-buffer 'erc--test-fun)) + (erc-setup-buffer mbuf) + (should (equal `(erc--test-fun ,mbuf (nil (erc-buffer-display . 1))) + (pop calls))) + (should-not calls)) + + ;; `pop-to-buffer' with `erc-auto-reconnect-display' + (let* ((erc--server-last-reconnect-count 1) + (erc--display-context '((erc-buffer-display . 1))) + (erc-auto-reconnect-display 'erc--test-fun)) + (erc-setup-buffer mbuf) + (should (equal `(erc--test-fun ,mbuf + (nil (erc-auto-reconnect-display . t) + (erc-buffer-display . 1))) + (pop calls))) + (should-not calls))) + + ;; Mimic simplistic version of example in "(erc) display-buffer". + (when (>= emacs-major-version 29) + (let ((proc erc-server-process)) + (with-temp-buffer + (should-not (eq (window-buffer) (current-buffer))) + (erc-mode) + (setq erc-server-process proc) + + (cl-letf (((symbol-function 'erc--test-fun-p) + (lambda (buf action) + (should (eql 1 (alist-get 'erc-buffer-display action))) + (push (cons 'erc--test-fun-p buf) calls))) + ((symbol-function 'action-fn) + (lambda (buf action) + (should (eql 1 (alist-get 'erc-buffer-display action))) + (should (eql 42 (alist-get 'foo action))) + (push (cons 'action-fn buf) calls) + (selected-window)))) + + (let ((erc--display-context '((erc-buffer-display . 1))) + (display-buffer-alist + `(((and (major-mode . erc-mode) erc--test-fun-p) + action-fn (foo . 42)))) + (erc-buffer-display 'display-buffer)) + + (erc-setup-buffer (current-buffer)) + (should (equal 'action-fn (car (pop calls)))) + (should (equal 'erc--test-fun-p (car (pop calls)))) + (should-not calls)))))) + + (should (eq owin (selected-window))) + (should (eq obuf (window-buffer))))) + (ert-deftest erc-lurker-maybe-trim () (let (erc-lurker-trim-nicks (erc-lurker-ignore-chars "_`")) @@ -327,6 +622,339 @@ (setq erc-lurker-ignore-chars "_-`") ; set of chars, not character alts (should (string= "nick" (erc-lurker-maybe-trim "nick-_`"))))) +(ert-deftest erc-parse-user () + (should (equal '("" "" "") (erc-parse-user "!@"))) + (should (equal '("" "!" "") (erc-parse-user "!!@"))) + (should (equal '("" "" "@") (erc-parse-user "!@@"))) + (should (equal '("" "!" "@") (erc-parse-user "!!@@"))) + + (should (equal '("abc" "" "") (erc-parse-user "abc"))) + (should (equal '("" "123" "fake") (erc-parse-user "!123@fake"))) + (should (equal '("abc" "" "123") (erc-parse-user "abc!123"))) + + (should (equal '("abc" "123" "fake") (erc-parse-user "abc!123@fake"))) + (should (equal '("abc" "!123" "@xy") (erc-parse-user "abc!!123@@xy"))) + + (should (equal '("de" "fg" "xy") (erc-parse-user "abc\nde!fg@xy")))) + +(ert-deftest erc--parse-nuh () + (should (equal '(nil nil nil) (erc--parse-nuh "!@"))) + (should (equal '(nil nil nil) (erc--parse-nuh "@"))) + (should (equal '(nil nil nil) (erc--parse-nuh "!"))) + (should (equal '(nil "!" nil) (erc--parse-nuh "!!@"))) + (should (equal '(nil "@" nil) (erc--parse-nuh "!@@"))) + (should (equal '(nil "!@" nil) (erc--parse-nuh "!!@@"))) + + (should (equal '("abc" nil nil) (erc--parse-nuh "abc!"))) + (should (equal '(nil "abc" nil) (erc--parse-nuh "abc@"))) + (should (equal '(nil "abc" nil) (erc--parse-nuh "!abc@"))) + + (should (equal '("abc" "123" "fake") (erc--parse-nuh "abc!123@fake"))) + (should (equal '("abc" "!123@" "xy") (erc--parse-nuh "abc!!123@@xy"))) + + ;; Missing leading components. + (should (equal '(nil "abc" "123") (erc--parse-nuh "abc@123"))) + (should (equal '(nil "123" "fake") (erc--parse-nuh "!123@fake"))) + (should (equal '(nil nil "gnu.org") (erc--parse-nuh "@gnu.org"))) + + ;; Host "wins" over nick and user (sans "@"). + (should (equal '(nil nil "abc") (erc--parse-nuh "abc"))) + (should (equal '(nil nil "gnu.org") (erc--parse-nuh "gnu.org"))) + (should (equal '(nil nil "gnu.org") (erc--parse-nuh "!gnu.org"))) + (should (equal '("abc" nil "123") (erc--parse-nuh "abc!123"))) + + ;; No fallback behavior. + (should-not (erc--parse-nuh "abc\nde!fg@xy"))) + +(ert-deftest erc--parsed-prefix () + (erc-tests-common-make-server-buf (buffer-name)) + + ;; Uses fallback values when no PREFIX parameter yet received, thus + ;; ensuring caller can use slot accessors immediately instead of + ;; checking if null beforehand. + (should-not erc--parsed-prefix) + (should (equal (erc--parsed-prefix) + #s(erc--parsed-prefix nil "qaohv" "~&@%+" + ((?q . ?~) (?a . ?&) + (?o . ?@) (?h . ?%) (?v . ?+))))) + (let ((cached (should erc--parsed-prefix))) + (should (eq (erc--parsed-prefix) cached))) + + ;; Cache broken. (Notice not setting `erc--parsed-prefix' to nil). + (setq erc-server-parameters '(("PREFIX" . "(ov)@+"))) + + (let ((proc erc-server-process) + (expected '((?o . ?@) (?v . ?+))) + cached) + + (with-temp-buffer + (erc-mode) + (setq erc-server-process proc) + (should (equal expected + (erc--parsed-prefix-alist (erc--parsed-prefix))))) + + (should (equal expected (erc--parsed-prefix-alist erc--parsed-prefix))) + (setq cached erc--parsed-prefix) + (should (equal cached + #s(erc--parsed-prefix ("(ov)@+") "ov" "@+" + ((?o . ?@) (?v . ?+))))) + ;; Second target buffer reuses cached value. + (with-temp-buffer + (erc-mode) + (setq erc-server-process proc) + (should (eq cached (erc--parsed-prefix)))) + + ;; New value computed when cache broken. + (puthash 'PREFIX (list "(qh)~%") erc--isupport-params) + (with-temp-buffer + (erc-mode) + (setq erc-server-process proc) + (should-not (eq cached (erc--parsed-prefix))) + (should (equal (erc--parsed-prefix-alist + (erc-with-server-buffer erc--parsed-prefix)) + '((?q . ?~) (?h . ?%))))))) + +;; This exists as a reference to assert legacy behavior in order to +;; preserve and incorporate it as a fallback in the 5.6+ replacement. +(ert-deftest erc-parse-modes () + (with-suppressed-warnings ((obsolete erc-parse-modes)) + (should (equal (erc-parse-modes "+u") '(("u") nil nil))) + (should (equal (erc-parse-modes "-u") '(nil ("u") nil))) + (should (equal (erc-parse-modes "+o bob") '(nil nil (("o" on "bob"))))) + (should (equal (erc-parse-modes "-o bob") '(nil nil (("o" off "bob"))))) + (should (equal (erc-parse-modes "+uo bob") '(("u") nil (("o" on "bob"))))) + (should (equal (erc-parse-modes "+o-u bob") '(nil ("u") (("o" on "bob"))))) + (should (equal (erc-parse-modes "+uo-tv bob alice") + '(("u") ("t") (("o" on "bob") ("v" off "alice"))))) + + (ert-info ("Modes of type B are always grouped as unary") + (should (equal (erc-parse-modes "+k h2") '(nil nil (("k" on "h2"))))) + ;; Channel key args are thrown away. + (should (equal (erc-parse-modes "-k *") '(nil nil (("k" off nil)))))) + + (ert-info ("Modes of type C are grouped as unary even when disabling") + (should (equal (erc-parse-modes "+l 3") '(nil nil (("l" on "3"))))) + (should (equal (erc-parse-modes "-l") '(nil nil (("l" off nil)))))))) + +(ert-deftest erc--update-channel-modes () + (erc-mode) + (setq erc-channel-users (make-hash-table :test #'equal) + erc-server-users (make-hash-table :test #'equal) + erc--isupport-params (make-hash-table) + erc--target (erc--target-from-string "#test")) + (erc-tests-common-init-server-proc "sleep" "1") + + (let ((orig-handle-fn (symbol-function 'erc--handle-channel-mode)) + calls) + (cl-letf (((symbol-function 'erc--handle-channel-mode) + (lambda (&rest r) (push r calls) (apply orig-handle-fn r))) + ((symbol-function 'erc-update-mode-line) #'ignore)) + + (ert-info ("Unknown user not created") + (erc--update-channel-modes "+o" "bob") + (should-not (erc-get-channel-user "bob"))) + + (ert-info ("Status updated when user known") + (puthash "bob" (cons (erc-add-server-user + "bob" (make-erc-server-user + :nickname "bob" + :buffers (list (current-buffer)))) + (make-erc-channel-user)) + erc-channel-users) + ;; Also asserts fallback behavior for traditional prefixes. + (should-not (erc-channel-user-op-p "bob")) + (erc--update-channel-modes "+o" "bob") + (should (erc-channel-user-op-p "bob")) + (erc--update-channel-modes "-o" "bob") ; status revoked + (should-not (erc-channel-user-op-p "bob"))) + + (ert-info ("Unknown nullary added and removed") + (should-not erc--channel-modes) + (should-not erc-channel-modes) + (erc--update-channel-modes "+u") + (should (equal erc-channel-modes '("u"))) + (should (eq t (gethash ?u erc--channel-modes))) + (should (equal (pop calls) '(?d ?u t nil))) + (erc--update-channel-modes "-u") + (should (equal (pop calls) '(?d ?u nil nil))) + (should-not (gethash ?u erc--channel-modes)) + (should-not erc-channel-modes) + (should-not calls)) + + (ert-info ("Fallback for Type B includes mode letter k") + (erc--update-channel-modes "+k" "h2") + (should (equal (pop calls) '(?b ?k t "h2"))) + (should-not erc-channel-modes) + (should (equal "h2" (gethash ?k erc--channel-modes))) + (erc--update-channel-modes "-k" "*") + (should (equal (pop calls) '(?b ?k nil "*"))) + (should-not calls) + (should-not (gethash ?k erc--channel-modes)) + (should-not erc-channel-modes)) + + (ert-info ("Fallback for Type C includes mode letter l") + (erc--update-channel-modes "+l" "3") + (should (equal (pop calls) '(?c ?l t "3"))) + (should-not erc-channel-modes) + (should (equal "3" (gethash ?l erc--channel-modes))) + (erc--update-channel-modes "-l" nil) + (should (equal (pop calls) '(?c ?l nil nil))) + (should-not (gethash ?l erc--channel-modes)) + (should-not erc-channel-modes)) + + (ert-info ("Advertised supersedes heuristics") + (setq erc-server-parameters + '(("PREFIX" . "(ov)@+") + ;; Add phony 5th type for this CHANMODES value for + ;; robustness in case some server gets creative. + ("CHANMODES" . "eIbq,k,flj,CFLMPQRSTcgimnprstuz,FAKE"))) + (erc--update-channel-modes "+qu" "fool!*@*") + (should (equal (pop calls) '(?d ?u t nil))) + (should (equal (pop calls) '(?a ?q t "fool!*@*"))) + (should (equal 1 (gethash ?q erc--channel-modes))) + (should (eq t (gethash ?u erc--channel-modes))) + (should (equal erc-channel-modes '("u"))) + (should-not (erc-channel-user-owner-p "bob")) + + ;; Remove fool!*@* from list mode "q". + (erc--update-channel-modes "-uq" "fool!*@*") + (should (equal (pop calls) '(?a ?q nil "fool!*@*"))) + (should (equal (pop calls) '(?d ?u nil nil))) + (should-not (gethash ?u erc--channel-modes)) + (should-not erc-channel-modes) + (should (equal 0 (gethash ?q erc--channel-modes)))) + + (should-not calls)))) + +(ert-deftest erc--channel-modes () + :tags (and (null (getenv "CI")) '(:unstable)) + + (setq erc--isupport-params (make-hash-table) + erc--target (erc--target-from-string "#test") + erc-server-parameters + '(("CHANMODES" . "eIbq,k,flj,CFLMPQRSTcgimnprstuz"))) + + (erc-tests-common-init-server-proc "sleep" "1") + + (cl-letf (((symbol-function 'erc-update-mode-line) #'ignore)) + (erc--update-channel-modes "+bltk" "fool!*@*" "3" "h2")) + + (should (equal (erc--channel-modes 'string) "klt")) + (should (equal (erc--channel-modes 'strings) '("k" "l" "t"))) + (should (equal (erc--channel-modes) '((?k . "h2") (?l . "3") (?t)))) + (should (equal (erc--channel-modes 3 ",") "klt h2,3")) + + ;; The function this tests behaves differently in different + ;; environments. For example, on one GNU Linux system, it returns + ;; truncation ellipsis when run interactively. Rather than have + ;; hard-to-read "nondeterministic" comparisons against sets of + ;; acceptable values, we use separate tests. + (when (display-graphic-p) (ert-pass)) + + ;; Truncation cache populated and used. + (let ((cache (erc--channel-mode-types-shortargs erc--channel-mode-types)) + first-run) + (should (zerop (hash-table-count cache))) + (should (equal (erc--channel-modes 1 ",") "klt h,3")) + (should (equal (setq first-run (map-pairs cache)) '(((1 ?k "h2") . "h")))) + + ;; Second call uses cache. + (cl-letf (((symbol-function 'truncate-string-to-width) + (lambda (&rest _) (ert-fail "Shouldn't run")))) + (should (equal (erc--channel-modes 1 ",") "klt h,3"))) + + ;; Same key for only entry matches that of first result. + (should (pcase (map-pairs cache) + ((and '(((1 ?k "h2") . "h")) second-run) + (eq (pcase first-run (`((,k . ,_)) k)) + (pcase second-run (`((,k . ,_)) k))))))) + + (should (equal (erc--channel-modes 0 ",") "klt ,")) + (should (equal (erc--channel-modes 2) "klt h2 3")) + (should (equal (erc--channel-modes 1) "klt h 3")) + (should (equal (erc--channel-modes 0) "klt "))) ; 2 spaces + +(ert-deftest erc--channel-modes/graphic-p () + :tags `(:unstable ,@(and (getenv "ERC_TESTS_GRAPHICAL") + '(:erc--graphical))) + (unless (display-graphic-p) (ert-skip "See non-/graphic-p variant")) + + (erc-tests-common-init-server-proc "sleep" "1") + (setq erc--isupport-params (make-hash-table) + erc--target (erc--target-from-string "#test") + erc-server-parameters + '(("CHANMODES" . "eIbq,k,flj,CFLMPQRSTcgimnprstuz"))) + + (cl-letf (((symbol-function 'erc-update-mode-line) #'ignore)) + (erc--update-channel-modes "+bltk" "fool!*@*" "3" "hun2")) + + ;; Truncation cache populated and used. + (let ((cache (erc--channel-mode-types-shortargs erc--channel-mode-types)) + first-run) + (should (zerop (hash-table-count cache))) + (should (equal (erc--channel-modes 2 ",") "klt h…,3" )) + (should (equal (setq first-run (map-pairs cache)) + '(((2 ?k "hun2") . "h…")))) + + ;; Second call uses cache. + (cl-letf (((symbol-function 'truncate-string-to-width) + (lambda (&rest _) (ert-fail "Shouldn't run")))) + (should (equal (erc--channel-modes 2 ",") "klt h…,3" ))) + + ;; Same key for only entry matches that of first result. + (should (pcase (map-pairs cache) + ((and `(((2 ?k "hun2") . "h…")) second-run) + (eq (pcase first-run (`((,k . ,_)) k)) + (pcase second-run (`((,k . ,_)) k))))))) + + ;; A max length of 0 is nonsensical anyway, so skip those. + (should (equal (erc--channel-modes 3) "klt hu… 3")) + (should (equal (erc--channel-modes 2) "klt h… 3")) + (should (equal (erc--channel-modes 1) "klt … 3"))) + +(ert-deftest erc--update-user-modes () + (let ((erc--user-modes (list ?a))) + (should (equal (erc--update-user-modes "+a") '(?a))) + (should (equal (erc--update-user-modes "-b") '(?a))) + (should (equal erc--user-modes '(?a)))) + + (let ((erc--user-modes (list ?b))) + (should (equal (erc--update-user-modes "+ac") '(?a ?b ?c))) + (should (equal (erc--update-user-modes "+a-bc") '(?a))) + (should (equal erc--user-modes '(?a))))) + +(ert-deftest erc--user-modes () + (let ((erc--user-modes '(?a ?b))) + (should (equal (erc--user-modes) '(?a ?b))) + (should (equal (erc--user-modes 'string) "ab")) + (should (equal (erc--user-modes 'strings) '("a" "b"))))) + +(ert-deftest erc--parse-user-modes () + (should (equal (erc--parse-user-modes "a" '(?a)) '(() ()))) + (should (equal (erc--parse-user-modes "+a" '(?a)) '(() ()))) + (should (equal (erc--parse-user-modes "a" '()) '((?a) ()))) + (should (equal (erc--parse-user-modes "+a" '()) '((?a) ()))) + (should (equal (erc--parse-user-modes "-a" '()) '(() ()))) + (should (equal (erc--parse-user-modes "-a" '(?a)) '(() (?a)))) + + (should (equal (erc--parse-user-modes "+a-b" '(?a)) '(() ()))) + (should (equal (erc--parse-user-modes "+a-b" '(?b)) '((?a) (?b)))) + (should (equal (erc--parse-user-modes "+ab-c" '(?b)) '((?a) ()))) + (should (equal (erc--parse-user-modes "+ab-c" '(?b ?c)) '((?a) (?c)))) + (should (equal (erc--parse-user-modes "+a-c+b" '(?b ?c)) '((?a) (?c)))) + (should (equal (erc--parse-user-modes "-c+ab" '(?b ?c)) '((?a) (?c)))) + + ;; Param `extrap' returns groups of redundant chars. + (should (equal (erc--parse-user-modes "+a" '() t) '((?a) () () ()))) + (should (equal (erc--parse-user-modes "+a" '(?a) t) '(() () (?a) ()))) + (should (equal (erc--parse-user-modes "-a" '() t) '(() () () (?a)))) + (should (equal (erc--parse-user-modes "-a" '(?a) t) '(() (?a) () ()))) + + (should (equal (erc--parse-user-modes "+a-b" '(?a) t) '(() () (?a) (?b)))) + (should (equal (erc--parse-user-modes "-b+a" '(?a) t) '(() () (?a) (?b)))) + (should (equal (erc--parse-user-modes "+a-b" '(?b) t) '((?a) (?b) () ()))) + (should (equal (erc--parse-user-modes "-b+a" '(?b) t) '((?a) (?b) () ())))) + (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"))) @@ -447,6 +1075,27 @@ (should (equal (erc-downcase "Tilde~") "tilde~" )) (should (equal (erc-downcase "\\O/") "|o/" ))))) +(ert-deftest erc-channel-p () + (let ((erc--isupport-params (make-hash-table)) + erc-server-parameters) + + (should (erc-channel-p "#chan")) + (should (erc-channel-p "##chan")) + (should (erc-channel-p "&chan")) + (should (erc-channel-p "+chan")) + (should (erc-channel-p "!chan")) + (should-not (erc-channel-p "@chan")) + + (push '("CHANTYPES" . "#&@+!") erc-server-parameters) + + (should (erc-channel-p "!chan")) + (should (erc-channel-p "#chan")) + + (with-current-buffer (get-buffer-create "#chan") + (setq erc--target (erc--target-from-string "#chan"))) + (should (erc-channel-p (get-buffer "#chan")))) + (kill-buffer "#chan")) + (ert-deftest erc--valid-local-channel-p () (ert-info ("Local channels not supported") (let ((erc--isupport-params (make-hash-table))) @@ -459,9 +1108,21 @@ (should-not (erc--valid-local-channel-p "#chan")) (should (erc--valid-local-channel-p "&local"))))) +(ert-deftest erc--restore-initialize-priors () + (should (pcase (macroexpand-1 '(erc--restore-initialize-priors erc-my-mode + foo (ignore 1 2 3) + bar #'spam + baz nil)) + (`(let* ((,p (or erc--server-reconnecting erc--target-priors)) + (,q (and ,p (alist-get 'erc-my-mode ,p)))) + (setq foo (if ,q (alist-get 'foo ,p) (ignore 1 2 3)) + bar (if ,q (alist-get 'bar ,p) #'spam) + baz (if ,q (alist-get 'baz ,p) nil))) + t)))) + (ert-deftest erc--target-from-string () (should (equal (erc--target-from-string "#chan") - #s(erc--target-channel "#chan" \#chan))) + #s(erc--target-channel "#chan" \#chan nil))) (should (equal (erc--target-from-string "Bob") #s(erc--target "Bob" bob))) @@ -469,7 +1130,51 @@ (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))))) + #s(erc--target-channel-local "&Bitlbee" &bitlbee nil))))) + +(ert-deftest erc--modify-local-map () + (when (and (bound-and-true-p erc-irccontrols-mode) + (fboundp 'erc-irccontrols-mode)) + (erc-irccontrols-mode -1)) + (when (and (bound-and-true-p erc-match-mode) + (fboundp 'erc-match-mode)) + (erc-match-mode -1)) + (let* (calls + (inhibit-message noninteractive) + (cmd-foo (lambda () (interactive) (push 'foo calls))) + (cmd-bar (lambda () (interactive) (push 'bar calls)))) + + (ert-info ("Add non-existing") + (erc--modify-local-map t "C-c C-c" cmd-foo "C-c C-k" cmd-bar) + (with-temp-buffer + (set-window-buffer (selected-window) (current-buffer)) + (use-local-map erc-mode-map) + (execute-kbd-macro "\C-c\C-c") + (execute-kbd-macro "\C-c\C-k")) + (should (equal calls '(bar foo)))) + (setq calls nil) + + (ert-info ("Add existing") ; Attempt to swap definitions fails + (erc--modify-local-map t "C-c C-c" cmd-bar "C-c C-k" cmd-foo) + (with-temp-buffer + (set-window-buffer (selected-window) (current-buffer)) + (use-local-map erc-mode-map) + (execute-kbd-macro "\C-c\C-c") + (execute-kbd-macro "\C-c\C-k")) + (should (equal calls '(bar foo)))) + (setq calls nil) + + (ert-info ("Remove existing") + (ert-with-message-capture messages + (erc--modify-local-map nil "C-c C-c" cmd-foo "C-c C-k" cmd-bar) + (with-temp-buffer + (set-window-buffer (selected-window) (current-buffer)) + (use-local-map erc-mode-map) + (execute-kbd-macro "\C-c\C-c") + (execute-kbd-macro "\C-c\C-k")) + (should (string-search "C-c C-c is undefined" messages)) + (should (string-search "C-c C-k is undefined" messages)) + (should-not calls))))) (ert-deftest erc-ring-previous-command-base-case () (ert-info ("Create ring when nonexistent and do nothing") @@ -484,18 +1189,19 @@ (ert-deftest erc-ring-previous-command () (with-current-buffer (get-buffer-create "*#fake*") (erc-mode) - (erc-tests--send-prep) + (erc-tests-common-prep-for-insertion) + (setq erc-server-current-nick "tester") (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) + (setq-local erc--input-review-functions erc--input-review-functions) + (add-hook 'erc--input-review-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")))) + (erc-display-message + nil 'notice (current-buffer) "echo: one\n"))) ((symbol-function 'erc-command-no-process-p) (lambda (&rest _) t))) (ert-info ("Create ring, populate, recall") @@ -591,6 +1297,48 @@ (kill-buffer "*erc-protocol*") (should-not erc-debug-irc-protocol))) +(ert-deftest erc--split-line () + (let ((erc-default-recipients '("#chan")) + (erc-split-line-length 10)) + (should (equal (erc--split-line "") '(""))) + (should (equal (erc--split-line "0123456789") '("0123456789"))) + (should (equal (erc--split-line "0123456789a") '("0123456789" "a"))) + + (should (equal (erc--split-line "0123456789 ") '("0123456789" " "))) + (should (equal (erc--split-line "01234567 89") '("01234567 " "89"))) + (should (equal (erc--split-line "0123456 789") '("0123456 " "789"))) + (should (equal (erc--split-line "0 123456789") '("0 " "123456789"))) + (should (equal (erc--split-line " 0123456789") '(" " "0123456789"))) + (should (equal (erc--split-line "012345678 9a") '("012345678 " "9a"))) + (should (equal (erc--split-line "0123456789 a") '("0123456789" " a"))) + + ;; UTF-8 vs. KOI-8 + (should (= 10 (string-bytes "Русск"))) ; utf-8 + (should (equal (erc--split-line "Русск") '("Русск"))) + (should (equal (erc--split-line "РусскийТекст") '("Русск" "ийТек" "ст"))) + (should (equal (erc--split-line "Русский Текст") '("Русск" "ий " "Текст"))) + (let ((erc-encoding-coding-alist '(("#chan" . cyrillic-koi8)))) + (should (equal (erc--split-line "Русск") '("Русск"))) + (should (equal (erc--split-line "РусскийТекст") '("РусскийТек" "ст"))) + (should (equal (erc--split-line "Русский Текст") '("Русский " "Текст")))) + + ;; UTF-8 vs. Latin 1 + (should (= 17 (string-bytes "Hyvää päivää"))) + (should (equal (erc--split-line "Hyvää päivää") '("Hyvää " "päivää"))) + (should (equal (erc--split-line "HyvääPäivää") '("HyvääPä" "ivää"))) + (let ((erc-encoding-coding-alist '(("#chan" . latin-1)))) + (should (equal (erc--split-line "Hyvää päivää") '("Hyvää " "päivää"))) + (should (equal (erc--split-line "HyvääPäivää") '("HyvääPäivä" "ä")))) + + ;; Combining characters + (should (= 10 (string-bytes "Åström"))) + (should (equal (erc--split-line "_Åström") '("_Åströ" "m"))) + (should (equal (erc--split-line "__Åström") '("__Åstr" "öm"))) + (should (equal (erc--split-line "___Åström") '("___Åstr" "öm"))) + (when (> emacs-major-version 27) + (should (equal (erc--split-line "🏁🚩🎌🏴🏳️🏳️🌈🏳️⚧️🏴☠️") + '("🏁🚩" "🎌🏴" "🏳️" "🏳️🌈" "🏳️⚧️" "🏴☠️")))))) + (ert-deftest erc--input-line-delim-regexp () (let ((p erc--input-line-delim-regexp)) ;; none @@ -622,64 +1370,8 @@ (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 + (erc-tests-common-with-process-input-spy (lambda (next) (ert-info ("Errors when point not in prompt area") ; actually just dings @@ -691,9 +1383,9 @@ (ert-info ("Input remains untouched") (should (save-excursion (erc-bol) (looking-at "/msg #chan hi"))))) - (ert-info ("Errors when no process running") + (ert-info ("Errors when server buffer absent") (let ((e (should-error (erc-send-current-line)))) - (should (equal "ERC: No process running" (cadr e)))) + (should (equal "Server buffer missing" (cadr e)))) (ert-info ("Input remains untouched") (should (save-excursion (erc-bol) (looking-at "/msg #chan hi"))))) @@ -702,7 +1394,7 @@ (delete-region (point) (point-max)) (insert "one\n") (let ((e (should-error (erc-send-current-line)))) - (should (equal "Blank line - ignoring..." (cadr e)))) + (should (string-prefix-p "Trailing line detected" (cadr e)))) (goto-char (point-max)) (ert-info ("Input remains untouched") (should (save-excursion (goto-char erc-input-marker) @@ -714,9 +1406,9 @@ ;; These also indirectly tests `erc-send-input' (ert-deftest erc-send-current-line () - (erc-tests--with-process-input-spy + (erc-tests-common-with-process-input-spy (lambda (next) - (erc-tests--set-fake-server-process "sleep" "1") + (erc-tests-common-init-server-proc "sleep" "1") (should (= 0 erc-last-input-time)) (ert-info ("Simple command") @@ -728,8 +1420,9 @@ (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)))) + ;; The `force' argument is irrelevant here because it can't + ;; influence dispatched handlers, such as `erc-cmd-MSG'. + (should (pcase (funcall next) (`("/msg #chan hi\n" ,_ nil) t)))) (ert-info ("Simple non-command") (insert "hi") @@ -737,15 +1430,147 @@ (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 + ;; Non-commands are forced only when `erc-flood-protect' is + ;; nil, which conflates two orthogonal concerns. (should (equal (funcall next) '("hi\n" nil t)))) (should (consp erc-last-input-time))))) +(ert-deftest erc--discard-trailing-multiline-nulls () + (pcase-dolist (`(,input ,want) '((("") ("")) + (("" "") ("")) + (("a") ("a")) + (("a" "") ("a")) + (("" "a") ("" "a")) + (("" "a" "") ("" "a")))) + (ert-info ((format "Input: %S, want: %S" input want)) + (let ((s (make-erc--input-split :lines input))) + (erc--discard-trailing-multiline-nulls s) + (should (equal (erc--input-split-lines s) want)))))) + +(ert-deftest erc--count-blank-lines () + (pcase-dolist (`(,input ,want) '((() (0 0 0)) + (("") (1 1 0)) + (("" "") (2 1 1)) + (("" "" "") (3 1 2)) + ((" " "") (2 0 1)) + ((" " "" "") (3 0 2)) + (("" " " "") (3 1 1)) + (("" "" " ") (3 2 0)) + (("a") (0 0 0)) + (("a" "") (1 0 1)) + (("a" " " "") (2 0 1)) + (("a" "" "") (2 0 2)) + (("a" "b") (0 0 0)) + (("a" "" "b") (1 1 0)) + (("a" " " "b") (1 0 0)) + (("" "a") (1 1 0)) + ((" " "a") (1 0 0)) + (("" "a" "") (2 1 1)) + (("" " " "a" "" " ") (4 2 0)) + (("" " " "a" "" " " "") (5 2 1)))) + (ert-info ((format "Input: %S, want: %S" input want)) + (should (equal (erc--count-blank-lines input) want))))) + +;; Opt `wb': `erc-warn-about-blank-lines' +;; Opt `sw': `erc-send-whitespace-lines' +;; `s': " \n",`a': "a\n",`b': "b\n" +(defvar erc-tests--check-prompt-input--expect + ;; opts "" " " "\n" "\n " " \n" "\n\n" "a\n" "a\n " "a\n \nb" + '(((+wb -sw) err err err err err err err err err) + ((-wb -sw) nop nop nop nop nop nop nop nop nop) + ((+wb +sw) err (s) (0 s) (1 s s) (s) (0 s) (0 a) (a s) (a s b)) + ((-wb +sw) nop (s) (s) (s s) (s) (s) (a) (a s) (a s b)))) + +;; Help messages echoed (not IRC message) was emitted +(defvar erc-tests--check-prompt-input-messages + '("Stripping" "Padding")) + +(ert-deftest erc--check-prompt-input-for-multiline-blanks () + (erc-tests-common-with-process-input-spy + (lambda (next) + (erc-tests-common-init-server-proc "sleep" "10") + (should-not erc-send-whitespace-lines) + (should erc-warn-about-blank-lines) + + (pcase-dolist (`((,wb ,sw) . ,ex) erc-tests--check-prompt-input--expect) + (let ((print-escape-newlines t) + (erc-warn-about-blank-lines (eq wb '+wb)) + (erc-send-whitespace-lines (eq sw '+sw)) + (samples '("" " " "\n" "\n " " \n" "\n\n" + "a\n" "a\n " "a\n \nb"))) + (setq ex `(,@ex (a) (a b)) ; baseline, same for all combos + samples `(,@samples "a" "a\nb")) + (dolist (input samples) + (insert input) + (ert-info ((format "Opts: %S, Input: %S, want: %S" + (list wb sw) input (car ex))) + (ert-with-message-capture messages + (pcase-exhaustive (pop ex) + ('err (let ((e (should-error (erc-send-current-line)))) + (should (string-match (rx (| "trailing" "blank")) + (cadr e)))) + (should (equal (erc-user-input) input)) + (should-not (funcall next))) + ('nop (erc-send-current-line) + (should (equal (erc-user-input) input)) + (should-not (funcall next))) + ('clr (erc-send-current-line) + (should (string-empty-p (erc-user-input))) + (should-not (funcall next))) + ((and (pred consp) v) + (erc-send-current-line) + (should (string-empty-p (erc-user-input))) + (setq v (reverse v)) ; don't use `nreverse' here + (while v + (pcase (pop v) + ((and (pred integerp) n) + (should (string-search + (nth n erc-tests--check-prompt-input-messages) + messages))) + ('s (should (equal " \n" (car (funcall next))))) + ('a (should (equal "a\n" (car (funcall next))))) + ('b (should (equal "b\n" (car (funcall next))))))) + (should-not (funcall next)))))) + (delete-region erc-input-marker (point-max)))))))) + +(ert-deftest erc--check-prompt-input-for-multiline-blanks/explanations () + (should erc-warn-about-blank-lines) + (should-not erc-send-whitespace-lines) + + (let ((erc-send-whitespace-lines t)) + (pcase-dolist (`(,input ,msg) + '((("") "Padding (1) blank line") + (("" " ") "Padding (1) blank line") + ((" " "") "Stripping (1) blank line") + (("a" "") "Stripping (1) blank line") + (("" "") "Stripping (1) and padding (1) blank lines") + (("" "" "") "Stripping (2) and padding (1) blank lines") + (("" "a" "" "b" "" "c" "" "") + "Stripping (2) and padding (3) blank lines"))) + (ert-info ((format "Input: %S, Msg: %S" input msg)) + (let (erc--check-prompt-explanation) + (should-not (erc--check-prompt-input-for-multiline-blanks nil input)) + (should (equal (list msg) erc--check-prompt-explanation)))))) + + (pcase-dolist (`(,input ,msg) + '((("") "Blank line detected") + (("" " ") "2 blank lines detected") + ((" " "") "2 blank (1 trailing) lines detected") + (("a" "") "Trailing line detected") + (("" "") "2 blank (1 trailing) lines detected") + (("a" "" "") "2 trailing lines detected") + (("" "a" "" "b" "" "c" "" "") + "5 blank (2 trailing) lines detected"))) + (ert-info ((format "Input: %S, Msg: %S" input msg)) + (let ((rv (erc--check-prompt-input-for-multiline-blanks nil input))) + (should (equal (concat msg " (see `erc-send-whitespace-lines')") + rv )))))) + (ert-deftest erc-send-whitespace-lines () - (erc-tests--with-process-input-spy + (erc-tests-common-with-process-input-spy (lambda (next) - (erc-tests--set-fake-server-process "sleep" "1") + (erc-tests-common-init-server-proc "sleep" "1") (setq-local erc-send-whitespace-lines t) (ert-info ("Multiline hunk with blank line correctly split") @@ -758,7 +1583,7 @@ (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) '(" \n" nil t))) (should (equal (funcall next) '("one\n" nil t)))) (ert-info ("Multiline hunk with trailing newline filtered") @@ -780,18 +1605,21 @@ (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"))) + (dolist (p '("/a b" "/a b\n" "/a b\n\n" "/a b\n\n\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 (pcase (funcall next) (`(,cmd ,_ nil) (equal cmd "/a b\n")))) + (should-not (funcall next)))) + + (ert-info ("Multiline command with non-blanks errors") + (dolist (p '("/a b\nc\n\n" "/a b\n/c\n\n" "/a b\n\nc\n\n" + "/a\n c\n" "/a\nb\n" "/a\n/b\n" "/a \n \n")) + (insert p) + (should-error (erc-send-current-line)) + (goto-char erc-input-marker) + (delete-region (point) (point-max)) (should-not (funcall next)))) (ert-info ("Multiline hunk with trailing whitespace not filtered") @@ -809,13 +1637,14 @@ (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" ""))) + ;; Does not trim trailing blanks. + (should (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" ""))) (should (erc--check-prompt-input-for-excess-lines "" '("a" "b" "c"))))) (ert-info ("With `erc-ask-about-multiline-input'") @@ -836,14 +1665,12 @@ (erc-default-recipients '("#chan")) calls) (with-temp-buffer + (erc-tests-common-init-server-proc "sleep" "1") (cl-letf (((symbol-function 'erc-cmd-MSG) (lambda (line) (push line calls) + (should erc--called-as-input-p) (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)) @@ -896,6 +1723,487 @@ (should-not calls)))))) +(ert-deftest erc--get-inserted-msg-beg/basic () + (erc-tests-common-assert-get-inserted-msg/basic + (lambda (arg) (should (= 3 (erc--get-inserted-msg-beg arg)))))) + +(ert-deftest erc--get-inserted-msg-end/basic () + (erc-tests-common-assert-get-inserted-msg/basic + (lambda (arg) (should (= 11 (erc--get-inserted-msg-end arg)))))) + +(ert-deftest erc--get-inserted-msg-bounds/basic () + (erc-tests-common-assert-get-inserted-msg/basic + (lambda (arg) + (should (equal '(3 . 11) (erc--get-inserted-msg-bounds arg)))))) + +(ert-deftest erc--delete-inserted-message () + (erc-mode) + (erc--initialize-markers (point) nil) + ;; Put unique invisible properties on the line endings. + (erc-display-message nil 'notice nil "one") + (put-text-property (1- erc-insert-marker) erc-insert-marker 'invisible 'a) + (let ((erc--msg-prop-overrides '((erc--msg . datestamp) (erc--ts . 0)))) + (erc-display-message nil nil nil + (propertize "\n[date]" 'field 'erc-timestamp))) + (put-text-property (1- erc-insert-marker) erc-insert-marker 'invisible 'b) + (erc-display-message nil 'notice nil "two") + + (ert-info ("Date stamp deleted cleanly") + (goto-char 11) + (should (looking-at (rx "\n[date]"))) + (should (eq 'datestamp (get-text-property (point) 'erc--msg))) + (should (eq (point) (field-beginning (1+ (point))))) + + (erc--delete-inserted-message (point)) + + ;; Preceding line ending clobbered, replaced by trailing. + (should (looking-back (rx "*** one\n"))) + (should (looking-at (rx "*** two"))) + (should (eq 'b (get-text-property (1- (point)) 'invisible)))) + + (ert-info ("Markers at pos-bol preserved") + (erc-display-message nil 'notice nil "three") + (should (looking-at (rx "*** two"))) + + (let ((m (point-marker)) + (n (point-marker)) + (p (point))) + (set-marker-insertion-type m t) + (goto-char (point-max)) + (erc--delete-inserted-message p) + (should (= (marker-position n) p)) + (should (= (marker-position m) p)) + (goto-char p) + (set-marker m nil) + (set-marker n nil) + (should (looking-back (rx "*** one\n"))) + (should (looking-at (rx "*** three"))))) + + (ert-info ("Compat") + (erc-display-message nil 'notice nil "four") + (should (looking-at (rx "*** three\n"))) + (with-suppressed-warnings ((obsolete erc-legacy-invisible-bounds-p)) + (let ((erc-legacy-invisible-bounds-p t)) + (erc--delete-inserted-message (point)))) + (should (looking-at (rx "*** four\n")))) + + (ert-info ("Deleting most recent message preserves markers") + (let ((m (point-marker)) + (n (point-marker)) + (p (point))) + (should (equal "*** four\n" (buffer-substring p erc-insert-marker))) + (set-marker-insertion-type m t) + (goto-char (point-max)) + (erc--delete-inserted-message p) + (should (= (marker-position m) p)) + (should (= (marker-position n) p)) + (goto-char p) + (should (looking-back (rx "*** one\n"))) + (should (looking-at erc-prompt)) + (erc--assert-input-bounds) + + ;; However, `m' is now forever "trapped" at `erc-insert-marker'. + (erc-display-message nil 'notice nil "two") + (should (= m erc-insert-marker)) + (goto-char n) + (should (looking-at (rx "*** two\n"))) + (set-marker m nil) + (set-marker n nil)))) + +(ert-deftest erc--order-text-properties-from-hash () + (let ((table (map-into '((a . 1) + (erc--ts . 0) + (erc--msg . s005) + (b . 2) + (erc--cmd . 5) + (erc--spkr . "X") + (c . 3)) + 'hash-table))) + (with-temp-buffer + (erc-mode) + (insert "abc\n") + (add-text-properties 1 2 (erc--order-text-properties-from-hash table)) + (should (equal '( erc--msg s005 + erc--spkr "X" + erc--ts 0 + erc--cmd 5 + a 1 + b 2 + c 3) + (text-properties-at (point-min))))))) + +(ert-deftest erc--check-msg-prop () + (let ((erc--msg-props (map-into '((a . 1) (b . x)) 'hash-table))) + (should (eq 1 (erc--check-msg-prop 'a))) + (should (erc--check-msg-prop 'a 1)) + (should-not (erc--check-msg-prop 'a 2)) + + (should (eq 'x (erc--check-msg-prop 'b))) + (should (erc--check-msg-prop 'b 'x)) + (should-not (erc--check-msg-prop 'b 1)) + + (should (erc--check-msg-prop 'a '(1 42))) + (should-not (erc--check-msg-prop 'a '(2 42))) + + (let ((props '(42 x))) + (should (erc--check-msg-prop 'b props))) + (let ((v '(42 y))) + (should-not (erc--check-msg-prop 'b v))))) + +(ert-deftest erc--merge-prop () + (with-current-buffer (get-buffer-create "*erc-test*") + ;; Baseline. + (insert "abc\n") + (erc--merge-prop 1 3 'erc-test 'x) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" 0 2 (erc-test x)))) + (erc--merge-prop 1 3 'erc-test 'y) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" 0 2 (erc-test (y x))))) + + ;; Multiple intervals. + (goto-char (point-min)) + (insert "def\n") + (erc--merge-prop 1 2 'erc-test 'x) + (erc--merge-prop 2 3 'erc-test 'y) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) + #("def" 0 1 (erc-test x) 1 2 (erc-test y)))) + (erc--merge-prop 1 3 'erc-test 'z) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) + #("def" 0 1 (erc-test (z x)) 1 2 (erc-test (z y))))) + + ;; New val as list. + (goto-char (point-min)) + (insert "ghi\n") + (erc--merge-prop 2 3 'erc-test '(y z)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("ghi" 1 2 (erc-test (y z))))) + (erc--merge-prop 1 3 'erc-test '(w x)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) + #("ghi" 0 1 (erc-test (w x)) 1 2 (erc-test (w x y z))))) + + ;; Flag `erc--merge-prop-behind-p'. + (goto-char (point-min)) + (insert "jkl\n") + (erc--merge-prop 2 3 'erc-test '(y z)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("jkl" 1 2 (erc-test (y z))))) + (let ((erc--merge-prop-behind-p t)) + (erc--merge-prop 1 3 'erc-test '(w x))) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) + #("jkl" 0 1 (erc-test (w x)) 1 2 (erc-test (y z w x))))) + + (when noninteractive + (kill-buffer)))) + +(ert-deftest erc--remove-from-prop-value-list () + (with-current-buffer (get-buffer-create "*erc-test*") + ;; Non-list match. + (insert "abc\n") + (put-text-property 1 2 'erc-test 'a) + (put-text-property 2 3 'erc-test 'b) + (put-text-property 3 4 'erc-test 'c) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" + 0 1 (erc-test a) + 1 2 (erc-test b) + 2 3 (erc-test c)))) + + (erc--remove-from-prop-value-list 1 4 'erc-test 'b) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" + 0 1 (erc-test a) + 2 3 (erc-test c)))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'a) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" 2 3 (erc-test c)))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'c) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) "abc")) + + ;; List match. + (goto-char (point-min)) + (insert "def\n") + (put-text-property 1 2 'erc-test '(d x)) + (put-text-property 2 3 'erc-test '(e y)) + (put-text-property 3 4 'erc-test '(f z)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("def" + 0 1 (erc-test (d x)) + 1 2 (erc-test (e y)) + 2 3 (erc-test (f z))))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'y) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("def" + 0 1 (erc-test (d x)) + 1 2 (erc-test e) + 2 3 (erc-test (f z))))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'd) + (erc--remove-from-prop-value-list 1 4 'erc-test 'f) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("def" + 0 1 (erc-test x) + 1 2 (erc-test e) + 2 3 (erc-test z)))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'e) + (erc--remove-from-prop-value-list 1 4 'erc-test 'z) + (erc--remove-from-prop-value-list 1 4 'erc-test 'x) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) "def")) + + ;; List match. + (goto-char (point-min)) + (insert "ghi\n") + (put-text-property 1 2 'erc-test '(g x)) + (put-text-property 2 3 'erc-test '(h x)) + (put-text-property 3 4 'erc-test '(i y)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("ghi" + 0 1 (erc-test (g x)) + 1 2 (erc-test (h x)) + 2 3 (erc-test (i y))))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'x) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("ghi" + 0 1 (erc-test g) + 1 2 (erc-test h) + 2 3 (erc-test (i y))))) + (erc--remove-from-prop-value-list 1 2 'erc-test 'g) ; narrowed + (erc--remove-from-prop-value-list 3 4 'erc-test 'i) ; narrowed + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("ghi" + 1 2 (erc-test h) + 2 3 (erc-test y)))) + + ;; Pathological (,c) case (hopefully not created by ERC) + (goto-char (point-min)) + (insert "jkl\n") + (put-text-property 1 2 'erc-test '(j x)) + (put-text-property 2 3 'erc-test '(k)) + (put-text-property 3 4 'erc-test '(k)) + (erc--remove-from-prop-value-list 1 4 'erc-test 'k) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("jkl" 0 1 (erc-test (j x))))) + + (when noninteractive + (kill-buffer)))) + +(ert-deftest erc--remove-from-prop-value-list/many () + (with-current-buffer (get-buffer-create "*erc-test*") + ;; Non-list match. + (insert "abc\n") + (put-text-property 1 2 'erc-test 'a) + (put-text-property 2 3 'erc-test 'b) + (put-text-property 3 4 'erc-test 'c) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" + 0 1 (erc-test a) + 1 2 (erc-test b) + 2 3 (erc-test c)))) + + (erc--remove-from-prop-value-list 1 4 'erc-test '(a b)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" 2 3 (erc-test c)))) + (erc--remove-from-prop-value-list 1 4 'erc-test 'a) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("abc" 2 3 (erc-test c)))) + (erc--remove-from-prop-value-list 1 4 'erc-test '(c)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) "abc")) + + ;; List match. + (goto-char (point-min)) + (insert "def\n") + (put-text-property 1 2 'erc-test '(d x y)) + (put-text-property 2 3 'erc-test '(e y)) + (put-text-property 3 4 'erc-test '(f z)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("def" + 0 1 (erc-test (d x y)) + 1 2 (erc-test (e y)) + 2 3 (erc-test (f z))))) + (erc--remove-from-prop-value-list 1 4 'erc-test '(d y f)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("def" + 0 1 (erc-test x) + 1 2 (erc-test e) + 2 3 (erc-test z)))) + (erc--remove-from-prop-value-list 1 4 'erc-test '(e z x)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) "def")) + + ;; Narrowed beg. + (goto-char (point-min)) + (insert "ghi\n") + (put-text-property 1 2 'erc-test '(g x)) + (put-text-property 2 3 'erc-test '(h x)) + (put-text-property 3 4 'erc-test '(i x)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("ghi" + 0 1 (erc-test (g x)) + 1 2 (erc-test (h x)) + 2 3 (erc-test (i x))))) + (erc--remove-from-prop-value-list 1 3 'erc-test '(x g i)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("ghi" + 1 2 (erc-test h) + 2 3 (erc-test (i x))))) + + ;; Narrowed middle. + (goto-char (point-min)) + (insert "jkl\n") + (put-text-property 1 2 'erc-test '(j x)) + (put-text-property 2 3 'erc-test '(k)) + (put-text-property 3 4 'erc-test '(l y z)) + (erc--remove-from-prop-value-list 3 4 'erc-test '(k x y z)) + (should (erc-tests-common-equal-with-props + (buffer-substring 1 4) #("jkl" + 0 1 (erc-test (j x)) + 1 2 (erc-test (k)) + 2 3 (erc-test l)))) + + (when noninteractive + (kill-buffer)))) + +(ert-deftest erc--split-string-shell-cmd () + + ;; Leading and trailing space + (should (equal (erc--split-string-shell-cmd "1 2 3") '("1" "2" "3"))) + (should (equal (erc--split-string-shell-cmd " 1 2 3 ") '("1" "2" "3"))) + + ;; Empty string + (should (equal (erc--split-string-shell-cmd "\"\"") '(""))) + (should (equal (erc--split-string-shell-cmd " \"\" ") '(""))) + (should (equal (erc--split-string-shell-cmd "1 \"\"") '("1" ""))) + (should (equal (erc--split-string-shell-cmd "1 \"\" ") '("1" ""))) + (should (equal (erc--split-string-shell-cmd "\"\" 1") '("" "1"))) + (should (equal (erc--split-string-shell-cmd " \"\" 1") '("" "1"))) + + (should (equal (erc--split-string-shell-cmd "''") '(""))) + (should (equal (erc--split-string-shell-cmd " '' ") '(""))) + (should (equal (erc--split-string-shell-cmd "1 ''") '("1" ""))) + (should (equal (erc--split-string-shell-cmd "1 '' ") '("1" ""))) + (should (equal (erc--split-string-shell-cmd "'' 1") '("" "1"))) + (should (equal (erc--split-string-shell-cmd " '' 1") '("" "1"))) + + ;; Backslash + (should (equal (erc--split-string-shell-cmd "\\ ") '(" "))) + (should (equal (erc--split-string-shell-cmd " \\ ") '(" "))) + (should (equal (erc--split-string-shell-cmd "1\\ ") '("1 "))) + (should (equal (erc--split-string-shell-cmd "1\\ 2") '("1 2"))) + + ;; Embedded + (should (equal (erc--split-string-shell-cmd "\"\\\"\"") '("\""))) + (should (equal (erc--split-string-shell-cmd "1 \"2 \\\" \\\" 3\"") + '("1" "2 \" \" 3"))) + (should (equal (erc--split-string-shell-cmd "1 \"2 ' ' 3\"") + '("1" "2 ' ' 3"))) + (should (equal (erc--split-string-shell-cmd "1 '2 \" \" 3'") + '("1" "2 \" \" 3"))) + (should (equal (erc--split-string-shell-cmd "1 '2 \\ 3'") + '("1" "2 \\ 3"))) + (should (equal (erc--split-string-shell-cmd "1 \"2 \\\\ 3\"") + '("1" "2 \\ 3"))) ; see comment re ^ + + ;; Realistic + (should (equal (erc--split-string-shell-cmd "GET bob \"my file.txt\"") + '("GET" "bob" "my file.txt"))) + (should (equal (erc--split-string-shell-cmd "GET EXAMPLE|bob \"my file.txt\"") + '("GET" "EXAMPLE|bob" "my file.txt")))) ; regression + + +;; The behavior of `erc-pre-send-functions' differs between versions +;; in how hook members see and influence a trailing newline that's +;; part of the original prompt submission: +;; +;; 5.4: both seen and sent +;; 5.5: seen but not sent* +;; 5.6: neither seen nor sent* +;; +;; * requires `erc-send-whitespace-lines' for hook to run +;; +;; Two aspects that have remained consistent are +;; +;; - a final nonempty line in any submission is always sent +;; - a trailing newline appended by a hook member is always sent +;; +;; The last bullet would seem to contradict the "not sent" behavior of +;; 5.5 and 5.6, but what's actually happening is that exactly one +;; trailing newline is culled, so anything added always goes through. +;; Also, in ERC 5.6, all empty lines are actually padded, but this is +;; merely incidental WRT the above. +;; +;; Note that this test doesn't run any input-prep hooks and thus can't +;; account for the "seen" dimension noted above. + +(ert-deftest erc--run-send-hooks () + (with-suppressed-warnings ((obsolete erc-send-this) + (obsolete erc-send-pre-hook)) + (should erc-insert-this) + (should erc-send-this) ; populates `erc--input-split-sendp' + + (let (erc-pre-send-functions erc-send-pre-hook) + + (ert-info ("String preserved, lines rewritten, empties padded") + (setq erc-pre-send-functions + (lambda (o) (setf (erc-input-string o) "bar\n\nbaz\n"))) + (should (pcase (erc--run-send-hooks (make-erc--input-split + :string "foo" :lines '("foo"))) + ((cl-struct erc--input-split + (string "foo") (sendp 't) (insertp 't) + (lines '("bar" " " "baz" " ")) (cmdp 'nil)) + t)))) + + (ert-info ("Multiline commands rejected") + (should-error (erc--run-send-hooks (make-erc--input-split + :string "/mycmd foo" + :lines '("/mycmd foo") + :cmdp t)))) + + (ert-info ("Single-line commands pass") + (setq erc-pre-send-functions + (lambda (o) (setf (erc-input-sendp o) nil + (erc-input-string o) "/mycmd bar"))) + (should (pcase (erc--run-send-hooks (make-erc--input-split + :string "/mycmd foo" + :lines '("/mycmd foo") + :cmdp t)) + ((cl-struct erc--input-split + (string "/mycmd foo") (sendp 'nil) (insertp 't) + (lines '("/mycmd bar")) (cmdp 't)) + t)))) + + (ert-info ("Legacy hook respected, special vars confined") + (setq erc-send-pre-hook (lambda (_) (setq erc-send-this nil)) + erc-pre-send-functions (lambda (o) ; propagates + (should-not (erc-input-sendp o)))) + (should (pcase (erc--run-send-hooks (make-erc--input-split + :string "foo" :lines '("foo"))) + ((cl-struct erc--input-split + (string "foo") (sendp 'nil) (insertp 't) + (lines '("foo")) (cmdp 'nil)) + t))) + (should erc-send-this)) + + (ert-info ("Request to resplit honored") + (setq erc-send-pre-hook nil + erc-pre-send-functions + (lambda (o) (setf (erc-input-string o) "foo bar baz" + (erc-input-refoldp o) t))) + (let* ((split (make-erc--input-split :string "foo" :lines '("foo"))) + (erc--current-line-input-split split) + (erc-split-line-length 8)) + (should + (pcase (erc--run-send-hooks split) + ((cl-struct erc--input-split + (string "foo") (sendp 't) (insertp 't) + (lines '("foo bar " "baz")) (cmdp 'nil)) + t)))))))) + ;; Note: if adding an erc-backend-tests.el, please relocate this there. (ert-deftest erc-message () @@ -904,7 +2212,8 @@ 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))) + (lambda (_ _ _ msg &rest args) + (push (apply #'erc-format-message msg args) calls))) ((symbol-function 'erc-server-send) (lambda (line _) (push line calls))) ((symbol-function 'erc-server-buffer) @@ -923,6 +2232,7 @@ (erc-mode) (setq erc-server-process (buffer-local-value 'erc-server-process (get-buffer "ExampleNet")) + erc--target (erc--target-from-string "#chan") erc-default-recipients '("#chan") erc-channel-users (make-hash-table :test 'equal) erc-network 'ExampleNet) @@ -945,7 +2255,7 @@ (should-not erc-server-last-peers) (erc-message "PRIVMSG" ". hi") (should-not erc-server-last-peers) - (should (eq 'no-target (pop calls))) + (should (equal "No target" (pop calls))) (erc-message "PRIVMSG" ", hi") (should-not erc-server-last-peers) (should (string-match "alice :hi" (pop calls))))) @@ -978,6 +2288,333 @@ (kill-buffer "ExampleNet") (kill-buffer "#chan"))) +(ert-deftest erc-get-channel-membership-prefix () + (ert-info ("Uses default prefixes when `erc--parsed-prefix' not available") + (should-not (erc--parsed-prefix)) + ;; Baseline. + (should-not (erc-get-channel-membership-prefix nil)) + (should (equal (erc-get-channel-membership-prefix "Bob") "")) + (should (equal (erc-get-channel-membership-prefix (make-erc-channel-user)) + "")) + ;; Defaults. + (should + (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix (make-erc-channel-user :owner t)) + #("~" 0 1 (help-echo "owner")))) + (should + (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix (make-erc-channel-user :admin t)) + #("&" 0 1 (help-echo "admin")))) + (should + (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix (make-erc-channel-user :op t)) + #("@" 0 1 (help-echo "operator")))) + (should + (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix (make-erc-channel-user :halfop t)) + #("%" 0 1 (help-echo "half-op")))) + (should + (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix (make-erc-channel-user :voice t)) + #("+" 0 1 (help-echo "voice"))))) + + (ert-info ("Uses advertised prefixes when `erc--parsed-prefix' is available") + (erc-tests-common-make-server-buf (buffer-name)) + (push '("PREFIX" . "(ov)@+") erc-server-parameters) + (should (erc--parsed-prefix)) + + (with-current-buffer (erc--open-target "#chan") + (erc-update-current-channel-member "Bob" nil t nil nil 'on) + + ;; Baseline. + (should-not (erc-get-channel-membership-prefix nil)) + (should (string-empty-p (erc-get-channel-membership-prefix + (make-erc-channel-user)))) + + ;; Defaults. + (should (string-empty-p (erc-get-channel-membership-prefix + (make-erc-channel-user :owner t)))) + (should (string-empty-p (erc-get-channel-membership-prefix + (make-erc-channel-user :admin t)))) + (should (string-empty-p (erc-get-channel-membership-prefix + (make-erc-channel-user :halfop t)))) + + (should (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix "Bob") + #("@" 0 1 (help-echo "operator")))) + (should (erc-tests-common-equal-with-props + (erc-get-channel-membership-prefix + (make-erc-channel-user :voice t)) + #("+" 0 1 (help-echo "voice")))) + + (kill-buffer)))) + +;; This is an adapter that uses formatting templates from the +;; `-speaker' catalog to mimic `erc-format-privmessage', for testing +;; purposes. +(defun erc-tests--format-privmessage (nick msg privp msgp &optional inputp pfx) + (let ((erc-current-message-catalog erc--message-speaker-catalog)) + (apply #'erc-format-message + (erc--determine-speaker-message-format-args nick msg privp msgp + inputp nil pfx)))) + +;; This asserts that `erc--determine-speaker-message-format-args' +;; behaves identically to `erc-format-privmessage', the function whose +;; role it basically replaced. +(ert-deftest erc--determine-speaker-message-format-args () + ;; Basic PRIVMSG. + (let ((expect #("<bob> oh my" + 0 1 (font-lock-face erc-default-face) + 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face) + 4 11 (font-lock-face erc-default-face))) + (args (list (concat "bob") (concat "oh my") nil 'msgp))) + (should (erc-tests-common-equal-with-props + (apply #'erc-format-privmessage args) + expect)) + (should (erc-tests-common-equal-with-props + (apply #'erc-tests--format-privmessage args) + expect))) + + ;; Basic NOTICE. + (let ((expect #("-bob- oh my" + 0 1 (font-lock-face erc-default-face) + 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face) + 4 11 (font-lock-face erc-default-face))) + (args (list (copy-sequence "bob") (copy-sequence "oh my") nil nil))) + (should (erc-tests-common-equal-with-props + (apply #'erc-format-privmessage args) + expect)) + (should (erc-tests-common-equal-with-props + (apply #'erc-tests--format-privmessage args) + expect))) + + ;; Status-prefixed PRIVMSG. + (let* ((expect + #("<@Bob> oh my" + 0 1 (font-lock-face erc-default-face) + 1 2 (font-lock-face erc-nick-prefix-face help-echo "operator") + 2 5 (erc--speaker "Bob" font-lock-face erc-nick-default-face) + 5 12 (font-lock-face erc-default-face))) + (user (make-erc-server-user :nickname (copy-sequence "Bob"))) + (cuser (make-erc-channel-user :op t)) + (erc-channel-users (make-hash-table :test #'equal))) + (puthash "bob" (cons user cuser) erc-channel-users) + + (with-suppressed-warnings ((obsolete erc-format-@nick)) + (should (erc-tests-common-equal-with-props + (erc-format-privmessage (erc-format-@nick user cuser) + (copy-sequence "oh my") + nil 'msgp) + expect))) + (let ((nick "Bob") + (msg "oh my")) + (should (erc-tests-common-equal-with-props + (erc-tests--format-privmessage nick msg nil 'msgp nil cuser) + expect)) ; overloaded on PREFIX arg + (should (erc-tests-common-equal-with-props + (erc-tests--format-privmessage nick msg nil 'msgp nil t) + expect)) + ;; The new version makes a copy instead of adding properties to + ;; the input. + (should-not + (text-property-not-all 0 (length nick) 'font-lock-face nil nick)) + (should-not + (text-property-not-all 0 (length msg) 'font-lock-face nil msg))))) + +(ert-deftest erc--determine-speaker-message-format-args/queries-as-channel () + (should erc-format-query-as-channel-p) + + (with-current-buffer (get-buffer-create "bob") + (erc-mode) + (setq erc--target (erc--target-from-string "alice")) + + (insert "PRIVMSG\n" + (erc-tests--format-privmessage "bob" "oh my" 'queryp 'msgp)) + (should (erc-tests-common-equal-with-props + #("<bob> oh my" + 0 1 (font-lock-face erc-default-face) + 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face) + 4 11 (font-lock-face erc-default-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (insert "\nNOTICE\n" + (erc-tests--format-privmessage "bob" "oh my" 'queryp nil)) + (should (erc-tests-common-equal-with-props + #("-bob- oh my" + 0 1 (font-lock-face erc-default-face) + 1 4 (erc--speaker "bob" font-lock-face erc-nick-default-face) + 4 11 (font-lock-face erc-default-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (insert "\nInput PRIVMSG\n" + (erc-tests--format-privmessage "bob" "oh my" + 'queryp 'privmsgp 'inputp)) + (should (erc-tests-common-equal-with-props + #("<bob> oh my" + 0 1 (font-lock-face erc-default-face) + 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face) + 4 6 (font-lock-face erc-default-face) + 6 11 (font-lock-face erc-input-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (insert "\nInput NOTICE\n" + (erc-tests--format-privmessage "bob" "oh my" 'queryp nil 'inputp)) + (should (erc-tests-common-equal-with-props + #("-bob- oh my" + 0 1 (font-lock-face erc-default-face) + 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face) + 4 6 (font-lock-face erc-default-face) + 6 11 (font-lock-face erc-input-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (when noninteractive (kill-buffer)))) + +(ert-deftest erc--determine-speaker-message-format-args/queries () + (should erc-format-query-as-channel-p) + + (with-current-buffer (get-buffer-create "bob") + (erc-mode) + (setq-local erc-format-query-as-channel-p nil) + (setq erc--target (erc--target-from-string "alice")) + + (insert "PRIVMSG\n" + (erc-tests--format-privmessage "bob" "oh my" 'queryp 'msgp)) + (should (erc-tests-common-equal-with-props + #("*bob* oh my" + 0 1 (font-lock-face erc-direct-msg-face) + 1 4 (erc--speaker "bob" font-lock-face erc-nick-msg-face) + 4 11 (font-lock-face erc-direct-msg-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (insert "\nNOTICE\n" + (erc-tests--format-privmessage "bob" "oh my" 'queryp nil)) + (should (erc-tests-common-equal-with-props + #("-bob- oh my" + 0 1 (font-lock-face erc-direct-msg-face) + 1 4 (erc--speaker "bob" font-lock-face erc-nick-msg-face) + 4 11 (font-lock-face erc-direct-msg-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (insert "\nInput PRIVMSG\n" + (erc-tests--format-privmessage "bob" "oh my" + 'queryp 'privmsgp 'inputp)) + (should (erc-tests-common-equal-with-props + #("*bob* oh my" + 0 1 (font-lock-face erc-direct-msg-face) + 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face) + 4 6 (font-lock-face erc-direct-msg-face) + 6 11 (font-lock-face erc-input-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (insert "\nInput NOTICE\n" + (erc-tests--format-privmessage "bob" "oh my" 'queryp nil 'inputp)) + (should (erc-tests-common-equal-with-props + #("-bob- oh my" + 0 1 (font-lock-face erc-direct-msg-face) + 1 4 (erc--speaker "bob" font-lock-face erc-my-nick-face) + 4 6 (font-lock-face erc-direct-msg-face) + 6 11 (font-lock-face erc-input-face)) + (buffer-substring (pos-bol) (pos-eol)))) + + (when noninteractive (kill-buffer)))) + +(defun erc-tests--format-my-nick (message) + (concat (erc-format-my-nick) + (propertize message 'font-lock-face 'erc-input-face))) + +;; This tests that the default behavior of the replacement formatting +;; function for prompt input, `erc--format-speaker-input-message' +;; matches that of the original being replaced, `erc-format-my-nick', +;; though it only handled the speaker portion. +(ert-deftest erc--format-speaker-input-message () + ;; No status prefix. + (let ((erc-server-current-nick "tester") + (expect #("<tester> oh my" + 0 1 (font-lock-face erc-default-face) + 1 7 (font-lock-face erc-my-nick-face erc--speaker "tester") + 7 9 (font-lock-face erc-default-face) + 9 14 (font-lock-face erc-input-face)))) + (should (equal (erc-tests--format-my-nick "oh my") expect)) + (should (equal (erc--format-speaker-input-message "oh my") expect))) + + ;; With channel-operator status prefix. + (let* ((erc-server-current-nick "tester") + (cmem (cons (make-erc-server-user :nickname "tester") + (make-erc-channel-user :op t))) + (erc-channel-users (map-into (list "tester" cmem) + '(hash-table :test equal))) + (expect #("<@tester> oh my" + 0 1 (font-lock-face erc-default-face) + 1 2 (font-lock-face erc-my-nick-prefix-face) + 2 5 (font-lock-face erc-my-nick-face erc--speaker "bob") + 5 7 (font-lock-face erc-default-face) + 7 12 (font-lock-face erc-input-face)))) + (should (equal (erc-tests--format-my-nick "oh my") expect)) + (should (equal (erc--format-speaker-input-message "oh my") expect)))) + +(ert-deftest erc--route-insertion () + (erc-tests-common-prep-for-insertion) + (erc-tests-common-init-server-proc "sleep" "1") + (setq erc-networks--id (erc-networks--id-create 'foonet)) + + (let* ((erc-modules) ; for `erc--open-target' + (server-buffer (current-buffer)) + (spam-buffer (save-excursion (erc--open-target "#spam"))) + (chan-buffer (save-excursion (erc--open-target "#chan"))) + calls) + (cl-letf (((symbol-function 'erc-insert-line) + (lambda (&rest r) (push (cons 'line-1 r) calls)))) + + (with-current-buffer chan-buffer + + (ert-info ("Null `buffer' routes to live server-buffer") + (erc--route-insertion "null" nil) + (should (equal (pop calls) `(line-1 "null" ,server-buffer))) + (should-not calls)) + + (ert-info ("Cons `buffer' routes to live members") + ;; Copies a let-bound `erc--msg-props' before mutating. + (let* ((table (map-into '(erc--msg msg) 'hash-table)) + (erc--msg-props table)) + (erc--route-insertion "cons" (list server-buffer spam-buffer)) + (should-not (eq table erc--msg-props))) + (should (equal (pop calls) `(line-1 "cons" ,spam-buffer))) + (should (equal (pop calls) `(line-1 "cons" ,server-buffer))) + (should-not calls)) + + (ert-info ("Variant `all' inserts in all session buffers") + (erc--route-insertion "all" 'all) + (should (equal (pop calls) `(line-1 "all" ,chan-buffer))) + (should (equal (pop calls) `(line-1 "all" ,spam-buffer))) + (should (equal (pop calls) `(line-1 "all" ,server-buffer))) + (should-not calls)) + + (ert-info ("Variant `active' routes to active buffer if alive") + (should (eq chan-buffer (erc-with-server-buffer erc-active-buffer))) + (erc-set-active-buffer spam-buffer) + (erc--route-insertion "act" 'active) + (should (equal (pop calls) `(line-1 "act" ,spam-buffer))) + (should (eq (erc-active-buffer) spam-buffer)) + (should-not calls)) + + (ert-info ("Variant `active' falls back to current buffer") + (should (eq spam-buffer (erc-active-buffer))) + (kill-buffer "#spam") + (erc--route-insertion "nact" 'active) + (should (equal (pop calls) `(line-1 "nact" ,server-buffer))) + (should (eq (erc-with-server-buffer erc-active-buffer) + server-buffer)) + (should-not calls)) + + (ert-info ("Dead single buffer defaults to live server-buffer") + (should-not (get-buffer "#spam")) + (erc--route-insertion "dead" 'spam-buffer) + (should (equal (pop calls) `(line-1 "dead" ,server-buffer))) + (should-not calls)))) + + (should-not (buffer-live-p spam-buffer)) + (kill-buffer chan-buffer))) + (defvar erc-tests--ipv6-examples '("1:2:3:4:5:6:7:8" "::ffff:10.0.0.1" "::ffff:1.2.3.4" "::ffff:0.0.0.0" @@ -999,32 +2636,71 @@ (should (string-match erc--server-connect-dumb-ipv6-regexp (concat "[" a "]"))))) +(ert-deftest erc--with-entrypoint-environment () + (let ((env '((erc-join-buffer . foo) + (erc-server-connect-function . bar)))) + (erc--with-entrypoint-environment env + (should (eq erc-join-buffer 'foo)) + (should (eq erc-server-connect-function 'bar))))) + (ert-deftest erc-select-read-args () - (ert-info ("Does not default to TLS") - (should (equal (ert-simulate-keys "\r\r\r\r" + (ert-info ("Prompts for switch to TLS by default") + (should (equal (ert-simulate-keys "\r\r\r\ry\r" (erc-select-read-args)) (list :server "irc.libera.chat" - :port 6667 + :port 6697 :nick (user-login-name) - :password nil)))) + '&interactive-env + '((erc-server-connect-function . erc-open-tls-stream) + (erc-join-buffer . window)))))) + + (ert-info ("Switches to TLS when port matches default TLS port") + (should (equal (ert-simulate-keys "irc.gnu.org\r6697\r\r\r" + (erc-select-read-args)) + (list :server "irc.gnu.org" + :port 6697 + :nick (user-login-name) + '&interactive-env + '((erc-server-connect-function . erc-open-tls-stream) + (erc-join-buffer . window)))))) + + (ert-info ("Switches to TLS when URL is ircs://") + (let ((erc--display-context '((erc-interactive-display . erc)))) + (should (equal (ert-simulate-keys "ircs://irc.gnu.org\r\r\r\r" + (erc-select-read-args)) + (list :server "irc.gnu.org" + :port 6697 + :nick (user-login-name) + '&interactive-env + '((erc-server-connect-function + . erc-open-tls-stream) + (erc--display-context + . ((erc-interactive-display . erc))) + (erc-join-buffer . window))))))) + + (setq-local erc-interactive-display nil) ; cheat to save space + + (ert-info ("Opt out of non-TLS warning manually") + (should (equal (ert-simulate-keys "\r\r\r\rn\r" + (erc-select-read-args)) + (list :server "irc.libera.chat" + :port 6667 + :nick (user-login-name))))) (ert-info ("Override default TLS") (should (equal (ert-simulate-keys "irc://irc.libera.chat\r\r\r\r" (erc-select-read-args)) (list :server "irc.libera.chat" :port 6667 - :nick (user-login-name) - :password nil)))) + :nick (user-login-name))))) (ert-info ("Address includes port") - (should (equal (ert-simulate-keys - "localhost:6667\rnick\r\r" + (should (equal (ert-simulate-keys "localhost:6667\rnick\r\r" (erc-select-read-args)) (list :server "localhost" :port 6667 - :nick "nick" - :password nil)))) + :nick "nick")))) (ert-info ("Address includes nick, password skipped via option") (should (equal (ert-simulate-keys "nick@localhost:6667\r" @@ -1032,8 +2708,7 @@ (erc-select-read-args))) (list :server "localhost" :port 6667 - :nick "nick" - :password nil)))) + :nick "nick")))) (ert-info ("Address includes nick and password") (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r\r" @@ -1048,37 +2723,55 @@ (erc-select-read-args)) (list :server "[::1]" :port 6667 - :nick (user-login-name) - :password nil)))) + :nick (user-login-name))))) (ert-info ("IPv6 address with port") (should (equal (ert-simulate-keys "[::1]:6667\r\r\r" (erc-select-read-args)) (list :server "[::1]" :port 6667 - :nick (user-login-name) - :password nil)))) + :nick (user-login-name))))) (ert-info ("IPv6 address includes nick") (should (equal (ert-simulate-keys "nick@[::1]:6667\r\r" (erc-select-read-args)) (list :server "[::1]" :port 6667 + :nick "nick")))) + + (ert-info ("Extra args use URL nick by default") + (should (equal (ert-simulate-keys "nick:sesame@localhost:6667\r\r\r\r" + (let ((current-prefix-arg '(4))) + (erc-select-read-args))) + (list :server "localhost" + :port 6667 :nick "nick" - :password nil))))) + :user "nick" + :password "sesame" + :full-name "nick"))))) (ert-deftest erc-tls () - (let (calls) + (let (calls env) (cl-letf (((symbol-function 'user-login-name) (lambda (&optional _) "tester")) ((symbol-function 'erc-open) - (lambda (&rest r) (push r calls)))) + (lambda (&rest r) + (push `((erc-join-buffer ,erc-join-buffer) + (erc--display-context ,@erc--display-context) + (erc-server-connect-function + ,erc-server-connect-function)) + env) + (push r calls)))) (ert-info ("Defaults") (erc-tls) (should (equal (pop calls) '("irc.libera.chat" 6697 "tester" "unknown" t - nil nil nil nil nil "user" nil)))) + nil nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer bury) + (erc--display-context (erc-buffer-display . erc-tls)) + (erc-server-connect-function erc-open-tls-stream))))) (ert-info ("Full") (erc-tls :server "irc.gnu.org" @@ -1091,7 +2784,11 @@ :id 'GNU.org) (should (equal (pop calls) '("irc.gnu.org" 7000 "bob" "Bob's Name" t - "bob:changeme" nil nil nil t "bobo" GNU.org)))) + "bob:changeme" nil nil nil t "bobo" GNU.org))) + (should (equal (pop env) + '((erc-join-buffer bury) + (erc--display-context (erc-buffer-display . erc-tls)) + (erc-server-connect-function erc-open-tls-stream))))) ;; Values are often nil when called by lisp code, which leads to ;; null params. This is why `erc-open' recomputes almost @@ -1107,31 +2804,141 @@ :password "bob:changeme")) (should (equal (pop calls) '(nil 7000 nil "Bob's Name" t - "bob:changeme" nil nil nil nil "bobo" nil))))))) - -(defun erc-tests--make-server-buf (name) - (with-current-buffer (get-buffer-create name) - (erc-mode) - (setq erc-server-process (start-process "sleep" (current-buffer) - "sleep" "1") - erc-session-server (concat "irc." name ".org") - erc-session-port 6667 - erc-network (intern name)) - (set-process-query-on-exit-flag erc-server-process nil) - (current-buffer))) - -(defun erc-tests--make-client-buf (server name) - (unless (bufferp server) - (setq server (get-buffer server))) - (with-current-buffer (get-buffer-create name) - (erc-mode) - (setq erc--target (erc--target-from-string name)) - (dolist (v '(erc-server-process - erc-session-server - erc-session-port - erc-network)) - (set v (buffer-local-value v server))) - (current-buffer))) + "bob:changeme" nil nil nil nil "bobo" nil))) + (should (equal (pop env) + '((erc-join-buffer bury) + (erc--display-context (erc-buffer-display . erc-tls)) + (erc-server-connect-function erc-open-tls-stream))))) + + (ert-info ("Interactive") + (ert-simulate-keys "nick:sesame@localhost:6667\r\r" + (call-interactively #'erc-tls)) + (should (equal (pop calls) + '("localhost" 6667 "nick" "unknown" t "sesame" + nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer window) + (erc--display-context + (erc-interactive-display . erc-tls)) + (erc-server-connect-function erc-open-tls-stream))))) + + (ert-info ("Custom connect function") + (let ((erc-server-connect-function 'my-connect-func)) + (erc-tls) + (should (equal (pop calls) + '("irc.libera.chat" 6697 "tester" "unknown" t + nil nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer bury) + (erc--display-context + (erc-buffer-display . erc-tls)) + (erc-server-connect-function my-connect-func)))))) + + (ert-info ("Advised default function overlooked") ; intentional + (advice-add 'erc-server-connect-function :around #'ignore + '((name . erc-tests--erc-tls))) + (erc-tls) + (should (equal (pop calls) + '("irc.libera.chat" 6697 "tester" "unknown" t + nil nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer bury) + (erc--display-context (erc-buffer-display . erc-tls)) + (erc-server-connect-function erc-open-tls-stream)))) + (advice-remove 'erc-server-connect-function 'erc-tests--erc-tls)) + + (ert-info ("Advised non-default function honored") + (let ((f (lambda (&rest r) (ignore r)))) + (cl-letf (((symbol-value 'erc-server-connect-function) f)) + (advice-add 'erc-server-connect-function :around #'ignore + '((name . erc-tests--erc-tls))) + (erc-tls) + (should (equal (pop calls) + '("irc.libera.chat" 6697 "tester" "unknown" t + nil nil nil nil nil "user" nil))) + (should (equal (pop env) `((erc-join-buffer bury) + (erc--display-context + (erc-buffer-display . erc-tls)) + (erc-server-connect-function ,f)))) + (advice-remove 'erc-server-connect-function + 'erc-tests--erc-tls))))))) + +;; See `erc-select-read-args' above for argument parsing. +;; This only tests the "hidden" arguments. + +(ert-deftest erc--interactive () + (let (calls env) + (cl-letf (((symbol-function 'user-login-name) + (lambda (&optional _) "tester")) + ((symbol-function 'erc-open) + (lambda (&rest r) + (push `((erc-join-buffer ,erc-join-buffer) + (erc--display-context ,@erc--display-context) + (erc-server-connect-function + ,erc-server-connect-function)) + env) + (push r calls)))) + + (ert-info ("Default click-through accept TLS upgrade") + (ert-simulate-keys "\r\r\r\ry\r" + (call-interactively #'erc)) + (should (equal (pop calls) + '("irc.libera.chat" 6697 "tester" "unknown" t nil + nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer window) + (erc--display-context (erc-interactive-display . erc)) + (erc-server-connect-function erc-open-tls-stream))))) + + (ert-info ("Nick supplied, decline TLS upgrade") + (ert-simulate-keys "\r\rdummy\r\rn\r" + (call-interactively #'erc)) + (should (equal (pop calls) + '("irc.libera.chat" 6667 "dummy" "unknown" t nil + nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer window) + (erc--display-context (erc-interactive-display . erc)) + (erc-server-connect-function + erc-open-network-stream)))))))) + +(ert-deftest erc-server-select () + (let (calls env) + (cl-letf (((symbol-function 'user-login-name) + (lambda (&optional _) "tester")) + ((symbol-function 'erc-open) + (lambda (&rest r) + (push `((erc-join-buffer ,erc-join-buffer) + (erc--display-context ,@erc--display-context) + (erc-server-connect-function + ,erc-server-connect-function)) + env) + (push r calls)))) + + (ert-info ("Selects Libera.Chat Europe, automatic TSL") + (ert-simulate-keys "Libera.Chat\rirc.eu.\t\r\r\r" + (with-suppressed-warnings ((obsolete erc-server-select)) + (call-interactively #'erc-server-select))) + (should (equal (pop calls) + '("irc.eu.libera.chat" 6697 "tester" "unknown" t nil + nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer window) + (erc--display-context (erc-interactive-display . erc)) + (erc-server-connect-function erc-open-tls-stream))))) + + (ert-info ("Selects entry that doesn't support TLS") + (ert-simulate-keys "IRCnet\rirc.fr.\t\rdummy\r\r" + (with-suppressed-warnings ((obsolete erc-server-select)) + (call-interactively #'erc-server-select))) + (should (equal (pop calls) + '("irc.fr.ircnet.net" 6667 "dummy" "unknown" t nil + nil nil nil nil "user" nil))) + (should (equal (pop env) + '((erc-join-buffer window) + (erc--display-context (erc-interactive-display . erc)) + (erc-server-connect-function + erc-open-network-stream)))))))) (ert-deftest erc-handle-irc-url () (let* (calls @@ -1146,10 +2953,10 @@ (cl-letf (((symbol-function 'erc-cmd-JOIN) (lambda (&rest r) (push r calls)))) - (with-current-buffer (erc-tests--make-server-buf "foonet") + (with-current-buffer (erc-tests-common-make-server-buf "foonet") (setq rvbuf (current-buffer))) - (erc-tests--make-server-buf "barnet") - (erc-tests--make-server-buf "baznet") + (erc-tests-common-make-server-buf "barnet") + (erc-tests-common-make-server-buf "baznet") (ert-info ("Unknown network") (erc-handle-irc-url "irc.foonet.org" 6667 "#chan" nil nil "irc") @@ -1173,7 +2980,8 @@ (should-not calls)) (ert-info ("Known network, existing chan with key") - (erc-tests--make-client-buf "foonet" "#chan") + (save-excursion + (with-current-buffer "foonet" (erc--open-target "#chan"))) (erc-handle-irc-url "irc.foonet.org" nil "#chan?sec" nil nil "irc") (should (equal '("#chan" "sec") (pop calls))) (should-not calls)) @@ -1186,7 +2994,7 @@ (ert-info ("Unknown network, connect, chan") (with-current-buffer "foonet" (should-not (local-variable-p 'erc-after-connect))) - (setq rvbuf (lambda () (erc-tests--make-server-buf "gnu"))) + (setq rvbuf (lambda () (erc-tests-common-make-server-buf "gnu"))) (erc-handle-irc-url "irc.gnu.org" nil "#spam" nil nil "irc") (should (equal '("irc" :server "irc.gnu.org") (pop calls))) (should-not calls) @@ -1198,10 +3006,142 @@ (should-not calls)))) (when noninteractive - (kill-buffer "foonet") - (kill-buffer "barnet") - (kill-buffer "baznet") - (kill-buffer "#chan"))) + (erc-tests-common-kill-buffers))) + +(ert-deftest erc-channel-user () + ;; Traditional and alternate constructor swapped for compatibility. + (should (= 0 (erc-channel-user-status (erc-channel-user--make)))) + (should-not (erc-channel-user-last-message-time (erc-channel-user--make))) + + (should (= 42 (erc-channel-user-last-message-time + (make-erc-channel-user :last-message-time 42)))) + + (should (zerop (erc-channel-user-status (make-erc-channel-user)))) + + (let ((u (make-erc-channel-user))) + + (ert-info ("Add voice status to user") + (should (= 0 (erc-channel-user-status u))) + (should-not (erc-channel-user-voice u)) + (should (eq t (setf (erc-channel-user-voice u) t))) + (should (eq t (erc-channel-user-voice u)))) + + (ert-info ("Add op status to user") + (should (= 1 (erc-channel-user-status u))) + (should-not (erc-channel-user-op u)) + (should (eq t (setf (erc-channel-user-op u) t))) + (should (eq t (erc-channel-user-op u)))) + + (ert-info ("Add owner status to user") + (should (= 5 (erc-channel-user-status u))) + (should-not (erc-channel-user-owner u)) + (should (eq t (setf (erc-channel-user-owner u) t))) + (should (eq t (erc-channel-user-owner u)))) + + (ert-info ("Remove owner status from user") + (should (= 21 (erc-channel-user-status u))) + (should-not (setf (erc-channel-user-owner u) nil)) + (should-not (erc-channel-user-owner u))) + + (ert-info ("Remove op status from user") + (should (= 5 (erc-channel-user-status u))) + (should-not (setf (erc-channel-user-op u) nil)) + (should-not (erc-channel-user-op u))) + + (ert-info ("Remove voice status from user") + (should (= 1 (erc-channel-user-status u))) + (should-not (setf (erc-channel-user-voice u) nil)) + (should-not (erc-channel-user-voice u))) + + (ert-info ("Remove voice status from zeroed user") + (should (= 0 (erc-channel-user-status u))) + (should-not (setf (erc-channel-user-voice u) nil)) + (should-not (erc-channel-user-voice u)) + (should (= 0 (erc-channel-user-status u)))))) + +(defconst erc-tests--modules + '( autoaway autojoin bufbar button capab-identify + command-indicator completion dcc fill identd + imenu irccontrols keep-place list log match menu move-to-prompt netsplit + networks nickbar nicks noncommands notifications notify page readonly + replace ring sasl scrolltobottom services smiley sound + spelling stamp track truncate unmorse xdcc)) + +;; Ensure that `:initialize' doesn't change the ordering of the +;; members because otherwise the widget's state is "edited". + +(ert-deftest erc-modules--initialize () + ;; This is `custom--standard-value' from Emacs 28. + (should (equal (eval (car (get 'erc-modules 'standard-value)) t) + erc-modules))) + +;; Ensure the `:initialize' function for `erc-modules' successfully +;; tags all built-in modules with the internal property `erc--module'. + +(ert-deftest erc-modules--internal-property () + (let (ours) + (mapatoms (lambda (s) + (when-let ((v (get s 'erc--module)) + ((eq v s))) + (push s ours)))) + (should (equal (sort ours #'string-lessp) erc-tests--modules)))) + +(ert-deftest erc--normalize-module-symbol () + (dolist (mod erc-tests--modules) + (should (eq (erc--normalize-module-symbol mod) mod))) + (should (eq (erc--normalize-module-symbol 'pcomplete) 'completion)) + (should (eq (erc--normalize-module-symbol 'Completion) 'completion)) + (should (eq (erc--normalize-module-symbol 'ctcp-page) 'page)) + (should (eq (erc--normalize-module-symbol 'ctcp-sound) 'sound)) + (should (eq (erc--normalize-module-symbol 'timestamp) 'stamp)) + (should (eq (erc--normalize-module-symbol 'nickserv) 'services))) + +(defun erc-tests--assert-printed-in-subprocess (code expected) + (let ((proc (erc-tests-common-create-subprocess code '("-batch") nil))) + (while (accept-process-output proc 10)) + (goto-char (point-min)) + (unless (equal (read (current-buffer)) expected) + (message "Expected: %S\nGot: %s" expected (buffer-string)) + (ert-fail "Mismatch")))) + +;; Worrying about which library a module comes from is mostly not +;; worth the hassle so long as ERC can find its minor mode. However, +;; bugs involving multiple modules living in the same library may slip +;; by because a module's loading problems may remain hidden on account +;; of its place in the default ordering. + +(ert-deftest erc--find-mode () + (erc-tests--assert-printed-in-subprocess + `(let ((mods (mapcar #'cadddr (cdddr (get 'erc-modules 'custom-type)))) + moded) + (setq mods (sort mods (lambda (a b) (if (zerop (random 2)) a b)))) + (dolist (mod mods) + (unless (keywordp mod) + (push (if-let ((mode (erc--find-mode mod))) mod (list :missing mod)) + moded))) + (message "%S" + (sort moded (lambda (a b) + (string< (symbol-name a) (symbol-name b)))))) + erc-tests--modules)) + +(ert-deftest erc--essential-hook-ordering () + (erc-tests--assert-printed-in-subprocess + '(progn + (erc-update-modules) + (message "%S" + (list :erc-insert-modify-hook erc-insert-modify-hook + :erc-send-modify-hook erc-send-modify-hook))) + + '( :erc-insert-modify-hook (erc-controls-highlight ; 0 + erc-button-add-buttons ; 30 + erc-match-message ; 50 + erc-fill ; 60 + erc-add-timestamp) ; 70 + + :erc-send-modify-hook ( erc-controls-highlight ; 0 + erc-button-add-buttons ; 30 + erc-fill ; 40 + erc-add-timestamp)))) ; 70 (ert-deftest erc-migrate-modules () (should (equal (erc-migrate-modules '(autojoin timestamp button)) @@ -1209,46 +3149,162 @@ ;; Default unchanged (should (equal (erc-migrate-modules erc-modules) erc-modules))) -(ert-deftest erc--update-modules () - (let (calls - erc-modules - erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) - (cl-letf (((symbol-function 'require) - (lambda (s &rest _) (push s calls))) - - ;; Local modules - ((symbol-function 'erc-fake-bar-mode) - (lambda (n) (push (cons 'fake-bar n) calls))) +(ert-deftest erc--find-group () + ;; These two are loaded by default + (should (eq (erc--find-group 'keep-place nil) 'erc)) + (should (eq (erc--find-group 'networks nil) 'erc-networks)) + ;; These are fake + (cl-letf (((get 'erc-bar 'group-documentation) "") + ((get 'baz 'erc-group) 'erc-foo)) + (should (eq (erc--find-group 'foo 'bar) 'erc-bar)) + (should (eq (erc--find-group 'bar 'foo) 'erc-bar)) + (should (eq (erc--find-group 'bar nil) 'erc-bar)) + (should (eq (erc--find-group 'foo nil) 'erc)) + (should (eq (erc--find-group 'fake 'baz) 'erc-foo)))) + +(ert-deftest erc--find-group--real () + :tags '(:unstable) + (require 'erc-services) + (require 'erc-stamp) + (require 'erc-sound) + (require 'erc-page) + (require 'erc-join) + (require 'erc-capab) + (require 'erc-pcomplete) + (should (eq (erc--find-group 'services 'nickserv) 'erc-services)) + (should (eq (erc--find-group 'stamp 'timestamp) 'erc-stamp)) + (should (eq (erc--find-group 'sound 'ctcp-sound) 'erc-sound)) + (should (eq (erc--find-group 'page 'ctcp-page) 'erc-page)) + (should (eq (erc--find-group 'autojoin) 'erc-autojoin)) + (should (eq (erc--find-group 'pcomplete 'Completion) 'erc-pcomplete)) + (should (eq (erc--find-group 'capab-identify) 'erc-capab)) + ;; No group specified. + (should (eq (erc--find-group 'smiley nil) 'erc)) + (should (eq (erc--find-group 'unmorse nil) 'erc))) + +(ert-deftest erc--sort-modules () + (should (equal (erc--sort-modules '(networks foo fill bar fill stamp bar)) + ;; Third-party mods appear in original order. + '(fill networks stamp foo bar)))) + +(defun erc-tests--update-modules (fn) + (let* ((calls nil) + (custom-modes nil) + (on-load nil) + (text-quoting-style 'grave) + + (get-calls (lambda () (prog1 (nreverse calls) (setq calls nil)))) + + (add-onload (lambda (m k v) + (put (intern m) 'erc--feature k) + (push (cons k (lambda () (funcall v m))) on-load))) + + (mk-cmd (lambda (module) + (let ((mode (intern (format "erc-%s-mode" module)))) + (fset mode (lambda (n) (push (cons mode n) calls)))))) + + (mk-builtin (lambda (module-string) + (let ((s (intern module-string))) + (put s 'erc--module s)))) + + (mk-global (lambda (module) + (push (intern (format "erc-%s-mode" module)) + custom-modes)))) - ;; Global modules - ((symbol-function 'erc-fake-foo-mode) - (lambda (n) (push (cons 'fake-foo n) calls))) - ((get 'erc-fake-foo-mode 'standard-value) 'ignore) + (cl-letf (((symbol-function 'require) + (lambda (s &rest _) + ;; Simulate library being loaded, things defined. + (when-let ((h (alist-get s on-load))) (funcall h)) + (push (cons 'req s) calls))) + + ;; Spoof global module detection. + ((symbol-function 'custom-variable-p) + (lambda (v) (memq v custom-modes)))) + + (funcall fn get-calls add-onload mk-cmd mk-builtin mk-global)) + (should-not erc--aberrant-modules))) + +(ert-deftest erc--update-modules/unknown () + (erc-tests--update-modules + + (lambda (get-calls _ mk-cmd _ mk-global) + + (ert-info ("Baseline") + (let* ((erc-modules '(foo)) + (obarray (obarray-make)) + (err (should-error (erc--update-modules erc-modules)))) + (should (equal (cadr err) "`foo' is not a known ERC module")) + (should (equal (mapcar #'prin1-to-string (funcall get-calls)) + '("(req . erc-foo)"))))) + + ;; Module's mode command exists but lacks an associated file. + (ert-info ("Bad autoload flagged as suspect") + (should-not erc--aberrant-modules) + (let* ((erc--aberrant-modules nil) + (obarray (obarray-make)) + (erc-modules (list (intern "foo")))) + + ;; Create a mode-activation command and make mode-var global. + (funcall mk-cmd "foo") + (funcall mk-global "foo") + + ;; No local modules to return. + (should-not (erc--update-modules erc-modules)) + (should (equal (mapcar #'prin1-to-string erc--aberrant-modules) + '("foo"))) + ;; ERC requires the library via prefixed module name. + (should (equal (mapcar #'prin1-to-string (funcall get-calls)) + '("(req . erc-foo)" "(erc-foo-mode . 1)")))))))) + +;; A local module (here, `lo2') lacks a mode toggle, so ERC tries to +;; load its defining library, first via the symbol property +;; `erc--feature', and then via an "erc-" prefixed symbol. +(ert-deftest erc--update-modules/local () + (erc-tests--update-modules + + (lambda (get-calls add-onload mk-cmd mk-builtin mk-global) + + (let* ((obarray (obarray-make 20)) + (erc-modules (mapcar #'intern '("glo" "lo1" "lo2")))) + + ;; Create a global and a local module. + (mapc mk-cmd '("glo" "lo1")) + (mapc mk-builtin '("glo" "lo1")) + (funcall mk-global "glo") + (funcall add-onload "lo2" 'explicit-feature-lib mk-cmd) + + ;; Returns local modules. + (should (equal (mapcar #'symbol-name (erc--update-modules erc-modules)) + '("erc-lo2-mode" "erc-lo1-mode"))) + + ;; Requiring `erc-lo2' defines `erc-lo2-mode'. + (should (equal (mapcar #'prin1-to-string (funcall get-calls)) + `("(erc-glo-mode . 1)" + "(req . explicit-feature-lib)"))))))) + +(ert-deftest erc--update-modules/realistic () + (let ((calls nil) + ;; Module `pcomplete' "resolves" to `completion'. + (erc-modules '(pcomplete autojoin networks))) + (cl-letf (((symbol-function 'require) + (lambda (s &rest _) (push (cons 'req s) calls))) + + ;; Spoof global module detection. + ((symbol-function 'custom-variable-p) + (lambda (v) + (memq v '(erc-autojoin-mode erc-networks-mode + erc-completion-mode)))) + ;; Mock and spy real builtins. ((symbol-function 'erc-autojoin-mode) (lambda (n) (push (cons 'autojoin n) calls))) - ((get 'erc-autojoin-mode 'standard-value) 'ignore) ((symbol-function 'erc-networks-mode) (lambda (n) (push (cons 'networks n) calls))) - ((get 'erc-networks-mode 'standard-value) 'ignore) ((symbol-function 'erc-completion-mode) - (lambda (n) (push (cons 'completion n) calls))) - ((get 'erc-completion-mode 'standard-value) 'ignore)) - - (ert-info ("Local modules") - (setq erc-modules '(fake-foo fake-bar)) - (should (equal (erc--update-modules) '(erc-fake-bar-mode))) - ;; Bar the feature is still required but the mode is not activated - (should (equal (nreverse calls) - '(erc-fake-foo (fake-foo . 1) erc-fake-bar))) - (setq calls nil)) - - (ert-info ("Module name overrides") - (setq erc-modules '(completion autojoin networks)) - (should-not (erc--update-modules)) ; no locals - (should (equal (nreverse calls) '( erc-pcomplete (completion . 1) - erc-join (autojoin . 1) - erc-networks (networks . 1)))) - (setq calls nil))))) + (lambda (n) (push (cons 'completion n) calls)))) + + (should-not (erc--update-modules erc-modules)) ; no locals + (should (equal (nreverse calls) + '((completion . 1) (autojoin . 1) (networks . 1))))))) (ert-deftest erc--merge-local-modes () (cl-letf (((get 'erc-b-mode 'erc-module) 'b) @@ -1276,36 +3332,51 @@ (ert-deftest define-erc-module--global () (let ((global-module '(define-erc-module mname malias - "Some docstring" + "Some docstring." ((ignore a) (ignore b)) ((ignore c) (ignore d))))) - (should (equal (macroexpand global-module) + (should (equal (cl-letf (((symbol-function + 'erc--prepare-custom-module-type) + #'symbol-name)) + (macroexpand global-module)) `(progn (define-minor-mode erc-mname-mode "Toggle ERC mname mode. -With a prefix argument ARG, enable mname if ARG is positive, -and disable it otherwise. If called from Lisp, enable the mode -if ARG is omitted or nil. -Some docstring" +With a prefix argument ARG, enable mname if ARG is positive, and +disable it otherwise. If called from Lisp, enable the mode if +ARG is omitted or nil. + +Some docstring." :global t - :group 'erc-mname - (if erc-mname-mode - (erc-mname-enable) - (erc-mname-disable))) + :group (erc--find-group 'mname 'malias) + :require 'nil + :type "mname" + (let ((erc--module-toggle-prefix-arg arg)) + (if erc-mname-mode + (erc-mname-enable) + (erc-mname-disable)))) (defun erc-mname-enable () "Enable ERC mname mode." (interactive) - (cl-pushnew 'mname erc-modules) + (unless (or erc--inside-mode-toggle-p + (memq 'mname erc-modules)) + (let ((erc--inside-mode-toggle-p t)) + (erc--favor-changed-reverted-modules-state + 'mname #'cons))) (setq erc-mname-mode t) (ignore a) (ignore b)) (defun erc-mname-disable () "Disable ERC mname mode." (interactive) - (setq erc-modules (delq 'mname erc-modules)) + (unless (or erc--inside-mode-toggle-p + (not (memq 'mname erc-modules))) + (let ((erc--inside-mode-toggle-p t)) + (erc--favor-changed-reverted-modules-state + 'mname #'delq))) (setq erc-mname-mode nil) (ignore c) (ignore d)) @@ -1319,7 +3390,7 @@ Some docstring" (ert-deftest define-erc-module--local () (let* ((global-module '(define-erc-module mname nil ; no alias - "Some docstring" + "Some docstring." ((ignore a) (ignore b)) ((ignore c) (ignore d)) 'local)) @@ -1331,19 +3402,22 @@ Some docstring" `(progn (define-minor-mode erc-mname-mode "Toggle ERC mname mode. -With a prefix argument ARG, enable mname if ARG is positive, -and disable it otherwise. If called from Lisp, enable the mode -if ARG is omitted or nil. -Some docstring" +With a prefix argument ARG, enable mname if ARG is positive, and +disable it otherwise. If called from Lisp, enable the mode if +ARG is omitted or nil. + +Some docstring." :global nil - :group 'erc-mname - (if erc-mname-mode - (erc-mname-enable) - (erc-mname-disable))) + :group (erc--find-group 'mname nil) + (let ((erc--module-toggle-prefix-arg arg)) + (if erc-mname-mode + (erc-mname-enable) + (erc-mname-disable)))) (defun erc-mname-enable (&optional ,arg-en) "Enable ERC mname mode. -When called interactively, do so in all buffers for the current connection." +When called interactively, do so in all buffers for the current +connection." (interactive "p") (when (derived-mode-p 'erc-mode) (if ,arg-en @@ -1355,7 +3429,8 @@ When called interactively, do so in all buffers for the current connection." (defun erc-mname-disable (&optional ,arg-dis) "Disable ERC mname mode. -When called interactively, do so in all buffers for the current connection." +When called interactively, do so in all buffers for the current +connection." (interactive "p") (when (derived-mode-p 'erc-mode) (if ,arg-dis @@ -1370,4 +3445,86 @@ When called interactively, do so in all buffers for the current connection." (put 'erc-mname-enable 'definition-name 'mname) (put 'erc-mname-disable 'definition-name 'mname)))))) +(ert-deftest erc-tests-common-string-to-propertized-parts () + :tags '(:unstable) ; only run this locally + (unless (>= emacs-major-version 28) (ert-skip "Missing `object-intervals'")) + + (should (equal (erc-tests-common-string-to-propertized-parts + #("abc" + 0 1 (face default foo 1) + 1 3 (face (default italic) bar "2"))) + '(concat (propertize "a" 'foo 1 'face 'default) + (propertize "bc" 'bar "2" 'face '(default italic))))) + (should (equal #("abc" + 0 1 (face default foo 1) + 1 3 (face (default italic) bar "2")) + (concat (propertize "a" 'foo 1 'face 'default) + (propertize "bc" 'bar "2" 'face '(default italic)))))) + +(ert-deftest erc--make-message-variable-name () + (should (erc--make-message-variable-name 'english 'QUIT 'softp)) + (should (erc--make-message-variable-name 'english 'QUIT nil)) + + (let ((obarray (obarray-make))) + (should-not (erc--make-message-variable-name 'testcat 'testkey 'softp)) + (should (erc--make-message-variable-name 'testcat 'testkey nil)) + (should (intern-soft "erc-message-testcat-testkey" obarray)) + (should-not (erc--make-message-variable-name 'testcat 'testkey 'softp)) + (set (intern "erc-message-testcat-testkey" obarray) "hello world") + (should (equal (symbol-value + (erc--make-message-variable-name 'testcat 'testkey nil)) + "hello world"))) + + ;; Hyphenated (internal catalog). + (let ((obarray (obarray-make))) + (should-not (erc--make-message-variable-name '-testcat 'testkey 'softp)) + (should (erc--make-message-variable-name '-testcat 'testkey nil)) + (should (intern-soft "erc--message-testcat-testkey" obarray)) + (should-not (erc--make-message-variable-name '-testcat 'testkey 'softp)) + (set (intern "erc--message-testcat-testkey" obarray) "hello world") + (should (equal (symbol-value + (erc--make-message-variable-name '-testcat 'testkey nil)) + "hello world")))) + +(ert-deftest erc-retrieve-catalog-entry () + (should (eq 'english erc-current-message-catalog)) + (should (equal (erc-retrieve-catalog-entry 's221) "User modes for %n: %m")) + + ;; Local binding. + (with-temp-buffer + (should (equal (erc-retrieve-catalog-entry 's221) "User modes for %n: %m")) + (setq erc-current-message-catalog 'test) + ;; No catalog named `test'. + (should (equal (erc-retrieve-catalog-entry 's221) "User modes for %n: %m")) + + (let ((obarray (obarray-make))) + (set (intern "erc-message-test-s221") "test 221 val") + (should (equal (erc-retrieve-catalog-entry 's221) "test 221 val")) + (set (intern "erc-message-english-s221") "eng 221 val") + + (let ((erc-current-message-catalog 'english)) + (should (equal (erc-retrieve-catalog-entry 's221) "eng 221 val"))) + + (with-temp-buffer + (should (equal (erc-retrieve-catalog-entry 's221) "eng 221 val")) + (let ((erc-current-message-catalog 'test)) + (should (equal (erc-retrieve-catalog-entry 's221) "test 221 val")))) + + (should (equal (erc-retrieve-catalog-entry 's221) "test 221 val"))) + + (should (equal (erc-retrieve-catalog-entry 's221) "User modes for %n: %m")) + (should (equal erc-current-message-catalog 'test))) + + ;; Default top-level value. + (set-default-toplevel-value 'erc-current-message-catalog 'test-top) + (should (equal (erc-retrieve-catalog-entry 's221) "User modes for %n: %m")) + (set (intern "erc-message-test-top-s221") "test-top 221 val") + (should (equal (erc-retrieve-catalog-entry 's221) "test-top 221 val")) + + (setq erc-current-message-catalog 'test-local) + (should (equal (erc-retrieve-catalog-entry 's221) "test-top 221 val")) + + (makunbound (intern "erc-message-test-top-s221")) + (unintern "erc-message-test-top-s221" obarray)) + ;;; erc-tests.el ends here diff --git a/test/lisp/erc/erc-track-tests.el b/test/lisp/erc/erc-track-tests.el index 3af2333587f..3288c42a42e 100644 --- a/test/lisp/erc/erc-track-tests.el +++ b/test/lisp/erc/erc-track-tests.el @@ -104,6 +104,42 @@ '("#emacs" "#vi")) '("#e" "#v"))) )) +(ert-deftest erc-track--shortened-names () + (let (erc-track--shortened-names + erc-track--shortened-names-current-hash + results) + + (with-memoization (erc-track--shortened-names-get + '("apple" "banana" "cherries")) + '("a" "b" "c")) + (should (integerp (car erc-track--shortened-names))) + (should (equal (cdr erc-track--shortened-names) '("a" "b" "c"))) + (push erc-track--shortened-names results) + + ;; Redundant call doesn't run. + (with-memoization (erc-track--shortened-names-get + '("apple" "banana" "cherries")) + (should-not 'run) + '("a" "b" "c")) + (should (equal erc-track--shortened-names (car results))) + + ;; Change in environment or context forces run. + (with-temp-buffer + (with-memoization (erc-track--shortened-names-get + '("apple" "banana" "cherries")) + '("x" "y" "z"))) + (should (and (integerp (car erc-track--shortened-names)) + (/= (car erc-track--shortened-names) (caar results)))) + (should (equal (cdr erc-track--shortened-names) '("x" "y" "z"))) + (push erc-track--shortened-names results) + + (with-memoization (erc-track--shortened-names-get + '("apple" "banana" "cherries")) + '("1" "2" "3")) + (should (and (integerp (car erc-track--shortened-names)) + (/= (car erc-track--shortened-names) (caar results)))) + (should (equal (cdr erc-track--shortened-names) '("1" "2" "3"))))) + (ert-deftest erc-track--erc-faces-in () "`erc-faces-in' should pick up both 'face and 'font-lock-face properties." (let ((str0 (copy-sequence "is bold")) @@ -120,4 +156,134 @@ (should (erc-faces-in str0)) (should (erc-faces-in str1)) )) +;; This simulates an alternating bold/non-bold [#c] in the mode-line, +;; i.e., an `erc-modified-channels-alist' that vacillates between +;; +;; ((#<buffer #chan> 42 . erc-default-face)) +;; +;; and +;; +;; ((#<buffer #chan> 42 erc-nick-default-face erc-default-face)) +;; +;; This is a fairly typical scenario where consecutive messages +;; feature speaker and addressee button highlighting and otherwise +;; plain message bodies. This mapping of phony to real faces +;; describes the picture in 5.6: +;; +;; `1': (erc-button erc-default-face) ; URL +;; `2': (erc-nick-default-face erc-default-face) ; mention +;; `3': erc-default-face ; body +;; `_': (erc-nick-default-face erc-nick-default-face) ; speaker +;; +;; The `_' represents a commonly occurring face (a <speaker>) that's +;; not present in either option's default (standard) value. It's a +;; no-op from the POV of `erc-track-select-mode-line-face'. + +(ert-deftest erc-track-select-mode-line-face () + + ;; Observed (see key above). + (let ((erc-track-faces-priority-list '(1 2 3)) + (erc-track-faces-normal-list '(1 2 3))) + + (should (equal 2 (erc-track-select-mode-line-face 3 '(2 _ 3)))) + (should (equal 2 (erc-track-select-mode-line-face 2 '(2 _ 3)))) + (should (equal 3 (erc-track-select-mode-line-face 2 '(_ 3)))) + (should (equal 2 (erc-track-select-mode-line-face 3 '(2 3)))) + (should (equal 3 (erc-track-select-mode-line-face 2 '(3)))) + + (should (equal 1 (erc-track-select-mode-line-face 1 '(2 1 3)))) + (should (equal 1 (erc-track-select-mode-line-face 1 '(1 3)))) + (should (equal 1 (erc-track-select-mode-line-face 1 '(1 3 2)))) + (should (equal 1 (erc-track-select-mode-line-face 1 '(3 1))))) + + ;; When the current face outranks all new faces and doesn't appear + ;; among them, it's eligible to be replaced with a fellow "normal" + ;; from those new faces. But if it does appear among them, it's + ;; never replaced. + (let ((erc-track-faces-priority-list '(a b)) + (erc-track-faces-normal-list '(a b))) + + (should (equal 'a (erc-track-select-mode-line-face 'a '(b a)))) + (should (equal 'a (erc-track-select-mode-line-face 'a '(a b)))) + (should (equal 'a (erc-track-select-mode-line-face 'b '(b a)))) + (should (equal 'a (erc-track-select-mode-line-face 'b '(a b)))) + + (should (equal 'a (erc-track-select-mode-line-face 'b '(a)))) + (should (equal 'b (erc-track-select-mode-line-face 'a '(b))))) + + ;; The ordering of the "normal" list doesn't matter. + (let ((erc-track-faces-priority-list '(a b)) + (erc-track-faces-normal-list '(b a))) + + (should (equal 'a (erc-track-select-mode-line-face 'a '(b a)))) + (should (equal 'a (erc-track-select-mode-line-face 'a '(a b)))) + (should (equal 'a (erc-track-select-mode-line-face 'b '(b a)))) + (should (equal 'a (erc-track-select-mode-line-face 'b '(a b)))))) + +(defun erc-track-tests--select-mode-line-face (ranked normals cases) + (setq normals (map-into (mapcar (lambda (f) (cons f t)) normals) + '(hash-table :test equal))) + (pcase-dolist (`(,want ,cur-face ,new-faces) cases) + + (ert-info ((format "Observed: {cur: %S, new: %S, want: %S}" + cur-face new-faces want)) + (setq new-faces (cons (map-into + (mapcar (lambda (f) (cons f t)) new-faces) + '(hash-table :test equal)) + (reverse new-faces))) + (should (equal want (funcall #'erc-track--select-mode-line-face + cur-face new-faces ranked normals)))))) + +;; The main difference between these variants is that with the above, +;; when given alternating lines like +;; +;; CUR NEW CHOICE +;; text (mention $speaker text) => mention +;; mention ($speaker text) => text +;; +;; we see the effect of alternating faces in the indicator. But when +;; given consecutive lines with a similar composition, like +;; +;; text (mention $speaker text) => mention +;; text (mention $speaker text) => mention +;; +;; we lose the effect. With the variant below, we get +;; +;; text (mention $speaker text) => mention +;; text (mention $speaker text) => text +;; + +(ert-deftest erc-track--select-mode-line-face () + (should-not erc-track-ignore-normal-contenders-p) + + ;; These are the same test cases from the previous test. The syntax + ;; is (expected cur-face new-faces). + (erc-track-tests--select-mode-line-face + '(1 2 3) '(1 2 3) + '((2 3 (2 _ 3)) + (3 2 (2 _ 3)) + (3 2 (_ 3)) + (2 3 (2 3)) + (3 2 (3)) + (2 1 (2 1 3)) + (3 1 (1 3)) + (2 1 (1 3 2)) + (3 1 (3 1)))) + + (erc-track-tests--select-mode-line-face + '(a b) '(a b) + '((b a (b a)) + (b a (a b)) + (a b (b a)) + (a b (a b)) + (a b (a)) + (b a (b)))) + + (erc-track-tests--select-mode-line-face + '(a b) '(b a) + '((b a (b a)) + (b a (a b)) + (a b (b a)) + (a b (a b))))) + ;;; 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 index 35a9a570b6d..060f4178723 100644 --- a/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld +++ b/test/lisp/erc/resources/base/assoc/bouncer-history/barnet.eld @@ -17,7 +17,7 @@ (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") +((mode-user 10 "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") @@ -34,7 +34,7 @@ (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") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld b/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld index 58df79e19fa..ecde8adaec4 100644 --- a/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld +++ b/test/lisp/erc/resources/base/assoc/bouncer-history/foonet.eld @@ -17,7 +17,7 @@ (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") +((mode-user 10 "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") @@ -27,6 +27,7 @@ (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 ":someone!~u@abcdefg.irc PRIVMSG #chan :[07:04:10] hi everyone.") (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.") @@ -38,7 +39,7 @@ (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") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/assoc/bumped/again.eld b/test/lisp/erc/resources/base/assoc/bumped/again.eld index ab3c7b06214..aef164b6237 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/again.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/again.eld @@ -1,10 +1,10 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "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`") +((nick 10 "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") @@ -21,10 +21,10 @@ (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") +((mode-user 10 "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") +((privmsg 10 "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 index 5c36e58d9d3..0f7aadac564 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/foisted.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/foisted.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "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") @@ -17,14 +17,14 @@ (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") +((mode-user 10 "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") +((privmsg 10 "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) +((quit 10 "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 index 33e4168ac46..63366d3f576 100644 --- a/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld +++ b/test/lisp/erc/resources/base/assoc/bumped/refoisted.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "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") @@ -22,10 +22,10 @@ (0.01 ":bob!~u@ecnnh95wr67pv.net PRIVMSG dummy :back?") ) -((mode-user 1.2 "MODE dummy +i") +((mode-user 10 "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") +((renick 10 "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/multi-net/barnet.eld b/test/lisp/erc/resources/base/assoc/multi-net/barnet.eld index c62a22a11c7..4c2b1d61e24 100644 --- a/test/lisp/erc/resources/base/assoc/multi-net/barnet.eld +++ b/test/lisp/erc/resources/base/assoc/multi-net/barnet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :changeme")) -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((pass 10 "PASS :changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -18,16 +18,16 @@ (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") +((mode-user 10 "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") +((join 10 "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") +((mode 10 "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!") diff --git a/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld b/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld index f30b7deca11..bfa324642ce 100644 --- a/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld +++ b/test/lisp/erc/resources/base/assoc/multi-net/foonet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :changeme")) -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((pass 10 "PASS :changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -18,16 +18,16 @@ (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") +((mode-user 10 "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") +((join 10 "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") +((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!") diff --git a/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld b/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld index f916fea2374..15bcca2a623 100644 --- a/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld +++ b/test/lisp/erc/resources/base/assoc/reconplay/foonet.eld @@ -1,5 +1,5 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :changeme")) +((pass 10 "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") diff --git a/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld new file mode 100644 index 00000000000..c3791ac3d49 --- /dev/null +++ b/test/lisp/erc/resources/base/channel-buffer-revival/reattach.eld @@ -0,0 +1,56 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :tester@vanilla/foonet:changeme")) +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":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.11.1") + (0.00 ":irc.foonet.org 003 tester :This server was created Thu, 13 Apr 2023 05:55:22 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.00 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.00 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.00 ":irc.foonet.org 251 tester :There are 0 users and 3 invisible on 1 server(s)") + (0.01 ":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 10 "MODE tester +i") + (0.01 ":irc.foonet.org 221 tester +Zi")) + +((privmsg-play 10 "PRIVMSG *status :playbuffer #chan") + (0.05 ":***!znc@znc.in PRIVMSG #chan :Buffer Playback...") + (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:24] alice: Was I a child, to fear I know not what.") + (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:29] bob: My lord, I do confess the ring was hers.") + (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:08:40] alice: My sons would never so dishonour me.") + (0.01 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:54] bob: By the hand of a soldier, I will undertake it.") + (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:09:57] alice: Thou counterfeit'st most lively.") + (0.01 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.")) + +((privmsg-attach 10 "PRIVMSG *status :attach #chan") + (0.01 ":tester!~u@78a58pgahbr24.irc JOIN #chan")) + +((mode-chan 10 "MODE #chan") + (0.01 ":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 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:01] bob: With what it loathes for that which is away.") + (0.00 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:10:30] alice: Ties up my tongue, and will not let me speak.") + (0.00 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:26] bob: They say he is already in the forest of Arden, and a many merry men with him; and there they live like the old Robin Hood of England. They say many young gentlemen flock to him every day, and fleet the time carelessly, as they did in the golden world.") + (0.01 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :[06:11:29] alice: Not by his breath that is more miserable.") + (0.00 ":***!znc@znc.in PRIVMSG #chan :Playback Complete.") + (0.00 ":*status!znc@znc.in PRIVMSG tester :There was 1 channel matching [#chan]") + (0.03 ":*status!znc@znc.in PRIVMSG tester :Attached 1 channel") + (0.00 ":irc.foonet.org 324 tester #chan +Cnt") + (0.00 ":irc.foonet.org 329 tester #chan 1681365340") + (0.03 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Five or six thousand horse, I said,I will say true,or thereabouts, set down, for I'll speak truth.") + (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Riddling confession finds but riddling shrift.") + (0.04 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Ay, and the captain of his horse, Count Rousillon.")) + +((privmsg-bob 10 "PRIVMSG #chan :bob: hi") + (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: But thankful even for hate, that is meant love.") + (0.02 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :tester: Come, come, elder brother, you are too young in this.") + (0.02 ":alice!~u@q2weir96jk3r2.irc PRIVMSG #chan :bob: Sir, we have known together in Orleans.") + (0.05 ":bob!~u@q2weir96jk3r2.irc PRIVMSG #chan :alice: Pawn me to this your honour, she is his.")) diff --git a/test/lisp/erc/resources/base/display-message/multibuf.eld b/test/lisp/erc/resources/base/display-message/multibuf.eld new file mode 100644 index 00000000000..424a687e749 --- /dev/null +++ b/test/lisp/erc/resources/base/display-message/multibuf.eld @@ -0,0 +1,45 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sat, 14 Oct 2023 16:08:20 UTC") + (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.00 ":irc.foonet.org 251 tester :There are 0 users and 5 invisible on 1 server(s)") + (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.00 ":irc.foonet.org 254 tester 2 :channels formed") + (0.00 ":irc.foonet.org 255 tester :I have 5 clients and 0 servers") + (0.00 ":irc.foonet.org 265 tester 5 5 :Current local users 5, max 5") + (0.02 ":irc.foonet.org 266 tester 5 5 :Current global users 5, max 5") + (0.01 ":irc.foonet.org 422 tester :MOTD File is missing") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":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.")) + +((mode 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i")) + +((join 10 "JOIN #chan") + (0.03 ":tester!~u@rdjcgiwfuwqmc.irc JOIN #chan") + (0.03 ":irc.foonet.org 353 tester = #chan :@fsbot bob alice dummy tester") + (0.01 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.00 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :tester, welcome!") + (0.01 ":alice!~u@uee7kge7ua5sy.irc PRIVMSG #chan :tester, welcome!")) + +((mode 10 "MODE #chan") + (0.01 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :alice: Persuade this rude wretch willingly to die.") + (0.01 ":irc.foonet.org 324 tester #chan +Cnt") + (0.01 ":irc.foonet.org 329 tester #chan 1697299707") + (0.03 ":alice!~u@uee7kge7ua5sy.irc PRIVMSG #chan :bob: It might be yours or hers, for aught I know.") + (0.07 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :Would all themselves laugh mortal.") + (0.04 ":dummy!~u@rdjcgiwfuwqmc.irc PRIVMSG tester :hi") + (0.06 ":bob!~u@uee7kge7ua5sy.irc PRIVMSG #chan :alice: It hath pleased the devil drunkenness to give place to the devil wrath; one unperfectness shows me another, to make me frankly despise myself.") + (0.05 ":dummy!~u@rdjcgiwfuwqmc.irc QUIT :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)") + (0.08 ":alice!~u@uee7kge7ua5sy.irc PRIVMSG #chan :You speak of him when he was less furnished than now he is with that which makes him both without and within.")) + +((quit 10 "QUIT :\2ERC\2") + (0.04 ":tester!~u@rdjcgiwfuwqmc.irc QUIT :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)") + (0.02 "ERROR :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)")) diff --git a/test/lisp/erc/resources/base/display-message/statusmsg.eld b/test/lisp/erc/resources/base/display-message/statusmsg.eld new file mode 100644 index 00000000000..7c42117080c --- /dev/null +++ b/test/lisp/erc/resources/base/display-message/statusmsg.eld @@ -0,0 +1,47 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER tester 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.02 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Thu, 07 Dec 2023 08:04:35 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.00 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.00 ":irc.foonet.org 254 tester 2 :channels formed") + (0.00 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.02 ":irc.foonet.org 265 tester 4 5 :Current local users 4, max 5") + (0.00 ":irc.foonet.org 266 tester 4 5 :Current global users 4, max 5") + (0.00 ":irc.foonet.org 422 tester :MOTD File is missing") + (0.00 ":irc.foonet.org 221 tester +i") + (0.00 ":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.")) + +((mode-tester 10 "MODE tester +i")) + +((join-mine 10 "JOIN #mine") + (0.01 ":irc.foonet.org 221 tester +i") + (0.00 ":tester!~u@2jv6nwu4af69s.irc JOIN #mine") + (0.02 ":irc.foonet.org 353 tester = #mine :@tester +dummy") + (0.01 ":irc.foonet.org 366 tester #mine :End of NAMES list")) + +((mode-mine 10 "MODE #mine") + (0.00 ":irc.foonet.org 324 tester #mine +Cnt") + (0.02 ":irc.foonet.org 329 tester #mine 1702026418") + (0.04 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :hello") + (0.03 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :there") + (0.05 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION sad\1") + (0.03 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION glad\1")) + +((privmsg-statusmsg 10 "PRIVMSG +#mine :howdy")) +((privmsg-statusmsg-action 10 "PRIVMSG +#mine :tenderfoot") + ;; These are simulated "echoed messages" + (0.05 ":tester!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION mad\1") + (0.05 ":tester!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION chad\1")) + +((privmsg-prefixed 10 "PRIVMSG #mine :\1ACTION ready\1") + (0.04 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :okie") + (0.05 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG +#mine :\1ACTION dokie\1") + (0.04 ":dummy!~u@2jv6nwu4af69s.irc PRIVMSG #mine :\1ACTION out\1")) diff --git a/test/lisp/erc/resources/base/flood/ascii.eld b/test/lisp/erc/resources/base/flood/ascii.eld new file mode 100644 index 00000000000..a3d127326c3 --- /dev/null +++ b/test/lisp/erc/resources/base/flood/ascii.eld @@ -0,0 +1,49 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sun, 12 Mar 2023 02:30:29 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :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 253 tester 0 :unregistered connections") + (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 375 tester :- irc.foonet.org Message of the day - ") + (0.00 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.") + (0.01 ":irc.foonet.org 372 tester :- ") + (0.02 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md") + (0.00 ":irc.foonet.org 376 tester :End of MOTD command")) + +((mode-tester 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.00 ":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.05 ":irc.foonet.org 221 tester +i")) + +((join-spam 10 "JOIN #ascii") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #ascii") + (0 ":irc.foonet.org 353 tester = #ascii :alice tester @bob") + (0 ":irc.foonet.org 366 tester #ascii :End of NAMES list")) + +((mode-spam 10 "MODE #ascii") + (0 ":irc.foonet.org 324 tester #ascii +nt") + (0 ":irc.foonet.org 329 tester #ascii 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #ascii :tester, welcome!") + (0.0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #ascii :tester, welcome!")) + +((privmsg 10 "PRIVMSG #ascii :twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters 12345678")) +((privmsg 10 "PRIVMSG #ascii :twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters twenty-three characters ")) +((privmsg 10 "PRIVMSG #ascii :123456789")) +((privmsg 10 "PRIVMSG #ascii :xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")) +((privmsg 10 "PRIVMSG #ascii :yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy")) +((privmsg 10 "PRIVMSG #ascii :z")) + +((quit 10 "QUIT :\2ERC\2") + (0.07 ":tester!~u@h3f95zveyc38a.irc QUIT :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)") + (0.01 "ERROR :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)")) diff --git a/test/lisp/erc/resources/base/flood/koi8-r.eld b/test/lisp/erc/resources/base/flood/koi8-r.eld new file mode 100644 index 00000000000..0f10717fc2c --- /dev/null +++ b/test/lisp/erc/resources/base/flood/koi8-r.eld @@ -0,0 +1,47 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sun, 12 Mar 2023 02:30:29 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :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 253 tester 0 :unregistered connections") + (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 375 tester :- irc.foonet.org Message of the day - ") + (0.00 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.") + (0.01 ":irc.foonet.org 372 tester :- ") + (0.02 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md") + (0.00 ":irc.foonet.org 376 tester :End of MOTD command")) + +((mode-tester 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.00 ":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.05 ":irc.foonet.org 221 tester +i")) + +((join-chan 6 "JOIN #koi8") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #koi8") + (0 ":irc.foonet.org 353 tester = #koi8 :alice tester @bob") + (0 ":irc.foonet.org 366 tester #koi8 :End of NAMES list")) + +((mode-chan 8 "MODE #koi8") + (0 ":irc.foonet.org 324 tester #koi8 +nt") + (0 ":irc.foonet.org 329 tester #koi8 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #koi8 :tester, welcome!") + (0.0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #koi8 :tester, welcome!") + (0.0 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #koi8 :\313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317")) + +((privmsg 10 "PRIVMSG #koi8 :\313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317")) +((privmsg 10 "PRIVMSG #koi8 :\313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \313\317\322\317\336\305 \324\305\320\305\322\330 \305\323\314\311 \320\317 \322\325\323\323\313\311 \316\301\320\311\323\301\324\330 \327\323\305 \336\305\324\313\317 \311\314\311 \327\323\305 \322\301\327\316\317 \302\325\304\305\324 ")) +((privmsg 10 "PRIVMSG #koi8 :\322\301\332\322\331\327 \323\324\322\317\313\311 \316\305\320\317\316\321\324\316\317 \307\304\305")) + +((quit 10 "QUIT :\2ERC\2") + (0.07 ":tester!~u@h3f95zveyc38a.irc QUIT :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)") + (0.01 "ERROR :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)")) diff --git a/test/lisp/erc/resources/base/flood/soju.eld b/test/lisp/erc/resources/base/flood/soju.eld index 05266ca9411..9e936499a2d 100644 --- a/test/lisp/erc/resources/base/flood/soju.eld +++ b/test/lisp/erc/resources/base/flood/soju.eld @@ -8,7 +8,7 @@ (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") +((mode 10 "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") diff --git a/test/lisp/erc/resources/base/flood/utf-8.eld b/test/lisp/erc/resources/base/flood/utf-8.eld new file mode 100644 index 00000000000..8e7f8f7eed2 --- /dev/null +++ b/test/lisp/erc/resources/base/flood/utf-8.eld @@ -0,0 +1,54 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sun, 12 Mar 2023 02:30:29 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :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 253 tester 0 :unregistered connections") + (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 375 tester :- irc.foonet.org Message of the day - ") + (0.00 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.") + (0.01 ":irc.foonet.org 372 tester :- ") + (0.02 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md") + (0.00 ":irc.foonet.org 376 tester :End of MOTD command")) + +((mode-tester 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.00 ":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.05 ":irc.foonet.org 221 tester +i")) + +((join-spam 10 "JOIN #utf-8") + (0 ":tester!~u@9g6b728983yd2.irc JOIN #utf-8") + (0 ":irc.foonet.org 353 tester = #utf-8 :alice tester @bob") + (0 ":irc.foonet.org 366 tester #utf-8 :End of NAMES list")) + +((mode-spam 10 "MODE #utf-8") + (0 ":irc.foonet.org 324 tester #utf-8 +nt") + (0 ":irc.foonet.org 329 tester #utf-8 1620104779") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #utf-8 :tester, welcome!") + (0.0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #utf-8 :tester, welcome!")) + +((privmsg-a 10 "PRIVMSG #utf-8 :\320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 \320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 \320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 \320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 ")) +((privmsg-b 10 "PRIVMSG #utf-8 :\320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 \320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 \320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\272\320\276\321\200\320\276\321\207\320\265 \321\202\320\265\320\277\320\265\321\200\321\214 \320\265\321\201\320\273\320\270 \320\277\320\276 \321\200\321\203\321\201\321\201\320\272\320\270 \320\275\320\260\320\277\320\270\321\201\320\260\321\202\321\214 \320\262\321\201\320\265 \321\207\320\265\321\202\320\272\320\276 \320\270\320\273\320\270 \320\262\321\201\320\265 \321\200\320\260\320\262\320\275\320\276 \320\261\321\203\320\264\320\265\321\202 \321\200\320\260\320\267\321\200\321\213\320\262 \321\201\321\202\321\200\320\276\320\272\320\270 \320\275\320\265\320\277\320\276\320\275\321\217\321\202\320\275\320\276 \320\263\320\264\320\265 \320\261\321\203\320\264\320\265\321\202 ")) +((privmsg-c 10 "PRIVMSG #utf-8 :\321\200\320\260\320\267\321\200\321\213\320\262 \321\201\321\202\321\200\320\276\320\272\320\270 \320\275\320\265\320\277\320\276\320\275\321\217\321\202\320\275\320\276 \320\263\320\264\320\265") + (0.1 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #utf-8 :alice: Entirely honour; I would not be delay'd.")) + +((privmsg-g 10 "PRIVMSG #utf-8 :\350\251\261\350\252\252\345\244\251\344\270\213\345\244\247\345\213\242\357\274\214\345\210\206\344\271\205\345\277\205\345\220\210\357\274\214\345\220\210\344\271\205\345\277\205\345\210\206\357\274\232\345\221\250\346\234\253\344\270\203\345\234\213\345\210\206\347\210\255\357\274\214\345\271\266\345\205\245\346\226\274\347\247\246\343\200\202\345\217\212\347\247\246\346\273\205\344\271\213\345\276\214\357\274\214\346\245\232\343\200\201\346\274\242\345\210\206\347\210\255\357\274\214\345\217\210\345\271\266\345\205\245\346\226\274\346\274\242\343\200\202\346\274\242\346\234\235\350\207\252\351\253\230\347\245\226\346\226\254\347\231\275\350\233\207\350\200\214\350\265\267\347\276\251\357\274\214\344\270\200\347\265\261\345\244\251\344\270\213\343\200\202\345\276\214\344\276\206\345\205\211\346\255\246\344\270\255\350\210\210\357\274\214\345\202\263\350\207\263\347\215\273\345\270\235\357\274\214\351\201\202\345\210\206\347\202\272\344\270\211\345\234\213\343\200\202\346\216\250\345\205\266\350\207\264\344\272\202\344\271\213\347\224\261\357\274\214\346\256\206\345\247\213\346\226\274\346\241\223\343\200\201\351\235\210\344\272\214\345\270\235\343\200\202\346\241\223\345\270\235\347\246\201\351\214\256\345\226\204\351\241\236\357\274\214\345\264\207\344\277\241\345\256\246\345\256\230\343\200\202\345\217\212\346\241\223\345\270\235\345\264\251\357\274\214\351\235\210\345\270\235\345\215\263\344\275\215\357\274\214\345\244\247\345\260\207\350\273\215\347\253\207\346\255\246\343\200\201\345\244\252\345\202\205\351\231\263\350\225\203\357\274\214\345\205\261\347\233\270\350\274\224\344\275\220\343\200\202\346\231\202\346\234\211\345\256\246\345\256\230\346\233\271\347\257\200\347\255\211\345\274\204\346\254\212\357\274\214")) +((privmsg-h 10 "PRIVMSG #utf-8 :\347\253\207\346\255\246\343\200\201\351\231\263\350\225\203\350\254\200\350\252\205\344\271\213\357\274\214\344\275\234\344\272\213\344\270\215\345\257\206\357\274\214\345\217\215\347\202\272\346\211\200\345\256\263\343\200\202\344\270\255\346\266\223\350\207\252\346\255\244\346\204\210\346\251\253") + (0.0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #utf-8 :Shall seize this prey out of his father's hands.")) + +((privmsg-d 10 "PRIVMSG #utf-8 :\320\261\321\203\320\264\320\265\321\202\302\240\321\200\320\260\320\267\321\200\321\213\320\262\302\240\321\201\321\202\321\200\320\276\320\272\320\270\302\240\320\275\320\265\320\277\320\276\320\275\321\217\321\202\320\275\320\276\302\240\320\263\320\264\320\265\360\237\217\201\360\237\232\251\360\237\216\214\360\237\217\264\360\237\217\263\357\270\217")) +((privmsg-e 10 "PRIVMSG #utf-8 :\360\237\217\263\357\270\217\342\200\215\360\237\214\210\360\237\217\263\357\270\217\342\200\215\342\232\247\357\270\217\360\237\217\264\342\200\215\342\230\240\357\270\217")) + +((quit 10 "QUIT :\2ERC\2") + (0.07 ":tester!~u@h3f95zveyc38a.irc QUIT :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)") + (0.01 "ERROR :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)")) diff --git a/test/lisp/erc/resources/base/gapless-connect/foonet.eld b/test/lisp/erc/resources/base/gapless-connect/foonet.eld index 4ac4a3e5968..10b742fdb34 100644 --- a/test/lisp/erc/resources/base/gapless-connect/foonet.eld +++ b/test/lisp/erc/resources/base/gapless-connect/foonet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :foonet:changeme")) -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((pass 10 "PASS :foonet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -21,7 +21,7 @@ ;; 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 353 tester = #foo :alice @bob 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.") diff --git a/test/lisp/erc/resources/base/local-modules/first.eld b/test/lisp/erc/resources/base/local-modules/first.eld index f9181a80fb7..4e923270e24 100644 --- a/test/lisp/erc/resources/base/local-modules/first.eld +++ b/test/lisp/erc/resources/base/local-modules/first.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- ((cap 10 "CAP REQ :sasl")) -((nick 1 "NICK tester")) -((user 1 "USER tester 0 * :tester")) +((nick 10 "NICK tester")) +((user 10 "USER tester 0 * :tester")) ((authenticate 5 "AUTHENTICATE PLAIN") (0.0 ":irc.foonet.org CAP * ACK sasl") @@ -11,7 +11,7 @@ (0.0 ":irc.foonet.org 900 * * tester :You are now logged in as tester") (0.01 ":irc.foonet.org 903 * :Authentication successful")) -((cap 3.2 "CAP END") +((cap 10 "CAP END") (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.2 ":irc.foonet.org 003 tester :This server was created Sun, 20 Nov 2022 23:10:36 UTC") diff --git a/test/lisp/erc/resources/base/local-modules/second.eld b/test/lisp/erc/resources/base/local-modules/second.eld index a96103b2aa1..5823d63b874 100644 --- a/test/lisp/erc/resources/base/local-modules/second.eld +++ b/test/lisp/erc/resources/base/local-modules/second.eld @@ -41,7 +41,7 @@ (0.07 ":alice!~u@2fzfcku68ehqa.irc PRIVMSG #chan :bob: To you that know them not. This to my mother.") (0.00 ":bob!~u@2fzfcku68ehqa.irc PRIVMSG #chan :alice: Some enigma, some riddle: come, thy l'envoy; begin.")) -((quit 1 "QUIT :\2ERC\2") +((quit 10 "QUIT :\2ERC\2") (0.03 ":tester`!~u@u9iqi96sfwk9s.irc QUIT")) ((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/local-modules/third.eld b/test/lisp/erc/resources/base/local-modules/third.eld index 19bdd6efcce..e24825c3217 100644 --- a/test/lisp/erc/resources/base/local-modules/third.eld +++ b/test/lisp/erc/resources/base/local-modules/third.eld @@ -37,7 +37,7 @@ (0.00 ":alice!~u@2fzfcku68ehqa.irc PRIVMSG #chan :bob: No remedy, my lord, when walls are so wilful to hear without warning.") (0.01 ":bob!~u@2fzfcku68ehqa.irc PRIVMSG #chan :alice: Let our reciprocal vows be remembered. You have many opportunities to cut him off; if your will want not, time and place will be fruitfully offered. There is nothing done if he return the conqueror; then am I the prisoner, and his bed my gaol; from the loathed warmth whereof deliver me, and supply the place for your labor.")) -((quit 1 "QUIT :\2ERC\2") +((quit 10 "QUIT :\2ERC\2") (0.03 ":tester`!~u@u9iqi96sfwk9s.irc QUIT :Quit")) ((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/modes/chan-changed.eld b/test/lisp/erc/resources/base/modes/chan-changed.eld new file mode 100644 index 00000000000..6cf6596b0b2 --- /dev/null +++ b/test/lisp/erc/resources/base/modes/chan-changed.eld @@ -0,0 +1,55 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.03 ":cadmium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester") + (0.02 ":cadmium.libera.chat 002 tester :Your host is cadmium.libera.chat[103.196.37.95/6697], running version solanum-1.0-dev") + (0.01 ":cadmium.libera.chat 003 tester :This server was created Wed Jan 25 2023 at 10:22:45 UTC") + (0.01 ":cadmium.libera.chat 004 tester cadmium.libera.chat solanum-1.0-dev DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI") + (0.00 ":cadmium.libera.chat 005 tester CALLERID=g WHOX ETRACE FNC SAFELIST ELIST=CMNTU KNOCK MONITOR=100 CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server") + (0.01 ":cadmium.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.01 ":cadmium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz :are supported by this server") + (0.01 ":cadmium.libera.chat 251 tester :There are 70 users and 42996 invisible on 28 servers") + (0.02 ":cadmium.libera.chat 252 tester 38 :IRC Operators online") + (0.01 ":cadmium.libera.chat 253 tester 57 :unknown connection(s)") + (0.01 ":cadmium.libera.chat 254 tester 22912 :channels formed") + (0.01 ":cadmium.libera.chat 255 tester :I have 2499 clients and 1 servers") + (0.01 ":cadmium.libera.chat 265 tester 2499 4187 :Current local users 2499, max 4187") + (0.01 ":cadmium.libera.chat 266 tester 43066 51827 :Current global users 43066, max 51827") + (0.01 ":cadmium.libera.chat 250 tester :Highest connection count: 4188 (4187 clients) (319420 connections received)") + (0.01 ":cadmium.libera.chat 375 tester :- cadmium.libera.chat Message of the Day - ") + (0.01 ":cadmium.libera.chat 372 tester :- This server kindly provided by Mach Dilemma (www.m-d.net)") + (0.01 ":cadmium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for") + (0.00 ":cadmium.libera.chat 372 tester :- Email: support@libera.chat") + (0.00 ":cadmium.libera.chat 376 tester :End of /MOTD command.") + (0.00 ":tester MODE tester :+Ziw")) + +((mode-tester 10 "MODE tester +i")) + +((join-chan 10 "JOIN #chan") + (0.09 ":tester!~tester@127.0.0.1 JOIN #chan")) + +((mode-chan 10 "MODE #chan") + (0.03 ":cadmium.libera.chat 353 tester = #chan :tester @Chad dummy") + (0.02 ":cadmium.libera.chat 366 tester #chan :End of /NAMES list.") + (0.00 ":cadmium.libera.chat 324 tester #chan +nt") + (0.01 ":cadmium.libera.chat 329 tester #chan 1621432263")) + +((privmsg-before 10 "PRIVMSG #chan :ready before") + (0.02 ":Chad!~u@ggpg6r3a68wak.irc PRIVMSG #chan before") + (0.00 ":Chad!~u@ggpg6r3a68wak.irc MODE #chan +Qu")) + +((privmsg-key 10 "PRIVMSG #chan :ready key") + (0.02 ":Chad!~u@ggpg6r3a68wak.irc PRIVMSG #chan :doing key") + (0.00 ":Chad!~u@ggpg6r3a68wak.irc MODE #chan +k hunter2")) + +((privmsg-limit 10 "PRIVMSG #chan :ready limit") + (0.02 ":Chad!~u@ggpg6r3a68wak.irc PRIVMSG #chan :doing limit") + (0.00 ":Chad!~u@ggpg6r3a68wak.irc MODE #chan +l 3")) + +((privmsg-drop 10 "PRIVMSG #chan :ready drop") + (0.02 ":Chad!~u@ggpg6r3a68wak.irc PRIVMSG #chan dropping") + (0.00 ":Chad!~u@ggpg6r3a68wak.irc MODE #chan -lu") + (0.00 ":Chad!~u@ggpg6r3a68wak.irc MODE #chan -Qk *") + (0.02 ":Chad!~u@ggpg6r3a68wak.irc PRIVMSG #chan after")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld index 686a47f68a3..04959954c4f 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet-drop.eld @@ -22,14 +22,14 @@ (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") +((join 10 "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") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld index d0fe3af8ea4..596383c2699 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/barnet.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/barnet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 3 "PASS :barnet:changeme")) -((nick 3 "NICK tester")) -((user 3 "USER user 0 * :tester") +((pass 10 "PASS :barnet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -17,19 +17,19 @@ (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") +((mode-user 10 "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") +((join 10 "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") +((mode 10 "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.") @@ -38,4 +38,4 @@ (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 dishonor in doing it.")) -((linger 1 LINGER)) +((linger 2 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 index b99621cc311..d0445cd1dd5 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/foonet-drop.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet-drop.eld @@ -1,5 +1,5 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :foonet:changeme")) +((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") @@ -22,14 +22,14 @@ (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") +((join 10 "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") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld index b0964fb9537..2e1a3ac27da 100644 --- a/test/lisp/erc/resources/base/netid/bouncer/foonet.eld +++ b/test/lisp/erc/resources/base/netid/bouncer/foonet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 3 "PASS :foonet:changeme")) -((nick 3 "NICK tester")) -((user 3 "USER user 0 * :tester") +((pass 10 "PASS :foonet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -17,19 +17,19 @@ (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") +((mode-user 10 "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") +((join 10 "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") +((mode 10 "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.") @@ -43,4 +43,4 @@ (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)) +((linger 2 LINGER)) diff --git a/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld b/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld index 8e299ec44c0..35906f608b5 100644 --- a/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld +++ b/test/lisp/erc/resources/base/reconnect/aborted-dupe.eld @@ -19,7 +19,7 @@ (-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") +((~mode-user 10 "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.")) diff --git a/test/lisp/erc/resources/base/reconnect/aborted.eld b/test/lisp/erc/resources/base/reconnect/aborted.eld index 5c32070d85f..e3abcdf8415 100644 --- a/test/lisp/erc/resources/base/reconnect/aborted.eld +++ b/test/lisp/erc/resources/base/reconnect/aborted.eld @@ -18,7 +18,7 @@ (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") +((mode-user 10 "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/reconnect/just-eof.eld b/test/lisp/erc/resources/base/reconnect/just-eof.eld new file mode 100644 index 00000000000..c80a39b3170 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/just-eof.eld @@ -0,0 +1,3 @@ +;; -*- mode: lisp-data; -*- +((eof 5 EOF)) +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/reconnect/just-ping.eld b/test/lisp/erc/resources/base/reconnect/just-ping.eld new file mode 100644 index 00000000000..d57888b42d3 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/just-ping.eld @@ -0,0 +1,4 @@ +;; -*- mode: lisp-data; -*- +((ping 20 "PING")) + +((eof 10 EOF)) diff --git a/test/lisp/erc/resources/base/reconnect/options-again.eld b/test/lisp/erc/resources/base/reconnect/options-again.eld index f1fcc439cc3..8a3264fda9c 100644 --- a/test/lisp/erc/resources/base/reconnect/options-again.eld +++ b/test/lisp/erc/resources/base/reconnect/options-again.eld @@ -32,13 +32,13 @@ (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") +((~mode-chan 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 :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") +((mode-spam 20 "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.") diff --git a/test/lisp/erc/resources/base/reconnect/options.eld b/test/lisp/erc/resources/base/reconnect/options.eld index 3b305d85594..e0952a2aece 100644 --- a/test/lisp/erc/resources/base/reconnect/options.eld +++ b/test/lisp/erc/resources/base/reconnect/options.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :changeme")) -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((pass 10 "PASS :changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -18,7 +18,7 @@ (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") +((mode-user 10 "MODE tester +i") (0 ":irc.foonet.org 221 tester +i") (0 ":irc.foonet.org NOTICE tester :This server is in debug mode.") @@ -26,7 +26,7 @@ (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") +((mode-chan 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!") diff --git a/test/lisp/erc/resources/base/reconnect/ping-pong.eld b/test/lisp/erc/resources/base/reconnect/ping-pong.eld new file mode 100644 index 00000000000..b3d36cf6cec --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/ping-pong.eld @@ -0,0 +1,6 @@ +;; -*- mode: lisp-data; -*- +((ping 10 "PING ") + (0 "PONG fake")) + +((eof 10 EOF)) +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/base/reconnect/unexpected-disconnect.eld b/test/lisp/erc/resources/base/reconnect/unexpected-disconnect.eld new file mode 100644 index 00000000000..386d0f4b085 --- /dev/null +++ b/test/lisp/erc/resources/base/reconnect/unexpected-disconnect.eld @@ -0,0 +1,24 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "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 10 "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.")) + +((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 index 0c8cdac0379..c9080cf39e9 100644 --- a/test/lisp/erc/resources/base/renick/queries/bouncer-barnet.eld +++ b/test/lisp/erc/resources/base/renick/queries/bouncer-barnet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 3 "PASS :barnet:changeme")) -((nick 3 "NICK tester")) -((user 3 "USER user 0 * :tester") +((pass 10 "PASS :barnet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -17,7 +17,7 @@ (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") +((mode-user 10 "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") @@ -32,18 +32,18 @@ (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") +((mode 10 "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.") +((privmsg-a 10 "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?") +((privmsg-b 10 "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.") diff --git a/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld b/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld index 162e8bf9655..2421651ebe8 100644 --- a/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld +++ b/test/lisp/erc/resources/base/renick/queries/bouncer-foonet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :foonet:changeme")) -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((pass 10 "PASS :foonet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") @@ -17,7 +17,7 @@ (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") +((mode-user 10 "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") @@ -38,12 +38,12 @@ (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") +((privmsg-a 10 "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") +((privmsg-b 10 "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") diff --git a/test/lisp/erc/resources/base/renick/queries/solo.eld b/test/lisp/erc/resources/base/renick/queries/solo.eld index 12fa7d264e9..fa4c075adac 100644 --- a/test/lisp/erc/resources/base/renick/queries/solo.eld +++ b/test/lisp/erc/resources/base/renick/queries/solo.eld @@ -30,7 +30,7 @@ (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") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/renick/self/qual-chester.eld b/test/lisp/erc/resources/base/renick/self/qual-chester.eld index 75b50fe68bd..a224e0451d7 100644 --- a/test/lisp/erc/resources/base/renick/self/qual-chester.eld +++ b/test/lisp/erc/resources/base/renick/self/qual-chester.eld @@ -18,7 +18,7 @@ (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") +((mode-user 10 "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.")) diff --git a/test/lisp/erc/resources/base/renick/self/qual-tester.eld b/test/lisp/erc/resources/base/renick/self/qual-tester.eld index 25199226658..27061c65223 100644 --- a/test/lisp/erc/resources/base/renick/self/qual-tester.eld +++ b/test/lisp/erc/resources/base/renick/self/qual-tester.eld @@ -18,7 +18,7 @@ (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") +((mode-user 10 "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/reuse-buffers/channel/barnet.eld b/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld index efc2506fd6f..d106a45cf66 100644 --- a/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld +++ b/test/lisp/erc/resources/base/reuse-buffers/channel/barnet.eld @@ -56,7 +56,7 @@ (0.1 ":mike!~u@wvys46tx8tpmk.irc PRIVMSG #chan :tester, welcome!") (0 ":joe!~u@wvys46tx8tpmk.irc PRIVMSG #chan :tester, welcome!")) -((mode 1 "MODE #chan") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld b/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld index a11cfac2e73..603afa2fc3e 100644 --- a/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld +++ b/test/lisp/erc/resources/base/reuse-buffers/channel/foonet.eld @@ -52,7 +52,7 @@ (0.1 ":alice!~u@yppdd5tt4admc.irc PRIVMSG #chan :tester, welcome!") (0 ":bob!~u@yppdd5tt4admc.irc PRIVMSG #chan :tester, welcome!")) -((mode 1 "MODE #chan") +((mode 10 "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.") diff --git a/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld b/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld index cc7aff10076..5b64a58c98f 100644 --- a/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld +++ b/test/lisp/erc/resources/base/reuse-buffers/server/barnet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :barnet:changeme")) -((nick 1 "NICK tester")) -((user 2 "USER user 0 * :tester") +((pass 10 "PASS :barnet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") diff --git a/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld b/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld index 3a846108466..260ff74c20c 100644 --- a/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld +++ b/test/lisp/erc/resources/base/reuse-buffers/server/foonet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- -((pass 1 "PASS :foonet:changeme")) -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((pass 10 "PASS :foonet:changeme")) +((nick 10 "NICK tester")) +((user 10 "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") diff --git a/test/lisp/erc/resources/base/send-message/noncommands.eld b/test/lisp/erc/resources/base/send-message/noncommands.eld new file mode 100644 index 00000000000..ba210bfff6f --- /dev/null +++ b/test/lisp/erc/resources/base/send-message/noncommands.eld @@ -0,0 +1,52 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sun, 12 Nov 2023 17:40:20 UTC") + (0.01 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.02 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.01 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.01 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.01 ":irc.foonet.org 254 tester 2 :channels formed") + (0.01 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.01 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0.01 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0.02 ":irc.foonet.org 422 tester :MOTD File is missing") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":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.")) + +((mode-tester 10 "MODE tester +i")) + +((join-chan 10 "JOIN #chan") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":tester!~u@ggpg6r3a68wak.irc JOIN #chan") + (0.03 ":irc.foonet.org 353 tester = #chan :@fsbot bob alice tester") + (0.01 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.00 ":bob!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :tester, welcome!") + (0.01 ":alice!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :tester, welcome!")) + +((mode-chan 10 "MODE #chan") + (0.00 ":irc.foonet.org 324 tester #chan +Cnt") + (0.02 ":irc.foonet.org 329 tester #chan 1699810829") + (0.01 ":alice!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :bob: To prove him false that says I love thee not.") + (0.02 ":bob!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :alice: For hands, to do Rome service, are but vain.")) + +((privmsg-action 10 "PRIVMSG #chan :\1ACTION sad\1") + (0.07 ":alice!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :bob: Spotted, detested, and abominable.")) + +((privmsg-me 10 "PRIVMSG #chan :/me sad") + (0.03 ":bob!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :Marcus, my brother! 'tis sad Titus calls.")) + +((privmsg-sv 10 "PRIVMSG #chan :I'm using ERC " (+ (not " ")) " with GNU Emacs") + (0.07 ":bob!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :alice: You still wrangle with her, Boyet, and she strikes at the brow.")) + +((privmsg-sm 10 "PRIVMSG #chan :I'm using the following modules: `erc-autojoin-mode', ") + (0.04 ":alice!~u@cjn7mjwx57gbi.irc PRIVMSG #chan :No, not till Thursday; there is time enough.")) + +((quit 10 "QUIT :\2ERC\2") + (0.05 ":tester!~u@ggpg6r3a68wak.irc QUIT :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)") + (0.02 "ERROR :Quit: \2ERC\2 5.x (IRC client for GNU Emacs)")) diff --git a/test/lisp/erc/resources/commands/motd.eld b/test/lisp/erc/resources/commands/motd.eld new file mode 100644 index 00000000000..6d10ee122e2 --- /dev/null +++ b/test/lisp/erc/resources/commands/motd.eld @@ -0,0 +1,48 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Sun, 12 Mar 2023 02:30:29 UTC") + (0.00 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :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 253 tester 0 :unregistered connections") + (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 375 tester :- irc.foonet.org Message of the day - ") + (0.00 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.") + (0.01 ":irc.foonet.org 372 tester :- ") + (0.02 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md") + (0.00 ":irc.foonet.org 376 tester :End of MOTD command")) + +((mode 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.00 ":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.05 ":irc.foonet.org 221 tester +i")) + +((motd-1 10 "MOTD") + (0.08 ":irc.foonet.org 375 tester :- irc.foonet.org Message of the day - ") + (0.02 ":irc.foonet.org 372 tester :- This is the default Ergo MOTD.") + (0.01 ":irc.foonet.org 372 tester :- ") + (0.00 ":irc.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md") + (0.00 ":irc.foonet.org 376 tester :End of MOTD command")) + +((motd-2 10 "MOTD irc1.foonet.org") + (0.08 ":irc1.foonet.org 375 tester :- irc1.foonet.org Message of the day - ") + (0.02 ":irc1.foonet.org 372 tester :- This is the default Ergo MOTD.") + (0.01 ":irc1.foonet.org 372 tester :- ") + (0.00 ":irc1.foonet.org 372 tester :- For more information on using these, see MOTDFORMATTING.md") + (0.00 ":irc1.foonet.org 376 tester :End of MOTD command")) + +((motd-3 10 "MOTD fake.foonet.org") + (0.00 ":irc.foonet.org 402 tester fake.foonet.org :No such server")) + +((quit 10 "QUIT :\2ERC\2") + (0.07 ":tester!~u@h3f95zveyc38a.irc QUIT :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)") + (0.01 "ERROR :Quit: \2ERC\2 5.5 (IRC client for GNU Emacs 30.0.50)")) diff --git a/test/lisp/erc/resources/commands/squery.eld b/test/lisp/erc/resources/commands/squery.eld new file mode 100644 index 00000000000..bcd176e515b --- /dev/null +++ b/test/lisp/erc/resources/commands/squery.eld @@ -0,0 +1,31 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.07 ":ircnet.hostsailor.com 020 * :Please wait while we process your connection.") + (0.03 ":ircnet.hostsailor.com 001 tester :Welcome to the Internet Relay Network tester!~user@93.184.216.34") + (0.02 ":ircnet.hostsailor.com 002 tester :Your host is ircnet.hostsailor.com, running version 2.11.2p3+0PNv1.06") + (0.03 ":ircnet.hostsailor.com 003 tester :This server was created Thu May 20 2021 at 17:13:24 EDT") + (0.01 ":ircnet.hostsailor.com 004 tester ircnet.hostsailor.com 2.11.2p3+0PNv1.06 aoOirw abeiIklmnoOpqrRstv") + (0.00 ":ircnet.hostsailor.com 005 tester RFC2812 PREFIX=(ov)@+ CHANTYPES=#&!+ MODES=3 CHANLIMIT=#&!+:42 NICKLEN=15 TOPICLEN=255 KICKLEN=255 MAXLIST=beIR:64 CHANNELLEN=50 IDCHAN=!:5 CHANMODES=beIR,k,l,imnpstaqrzZ :are supported by this server") + (0.01 ":ircnet.hostsailor.com 005 tester PENALTY FNC EXCEPTS=e INVEX=I CASEMAPPING=ascii NETWORK=IRCnet :are supported by this server") + (0.01 ":ircnet.hostsailor.com 042 tester 0PNHANAWX :your unique ID") + (0.01 ":ircnet.hostsailor.com 251 tester :There are 18711 users and 2 services on 26 servers") + (0.01 ":ircnet.hostsailor.com 252 tester 63 :operators online") + (0.01 ":ircnet.hostsailor.com 253 tester 4 :unknown connections") + (0.01 ":ircnet.hostsailor.com 254 tester 10493 :channels formed") + (0.01 ":ircnet.hostsailor.com 255 tester :I have 933 users, 0 services and 1 servers") + (0.01 ":ircnet.hostsailor.com 265 tester 933 1328 :Current local users 933, max 1328") + (0.01 ":ircnet.hostsailor.com 266 tester 18711 25625 :Current global users 18711, max 25625") + (0.02 ":ircnet.hostsailor.com 375 tester :- ircnet.hostsailor.com Message of the Day - ") + (0.01 ":ircnet.hostsailor.com 372 tester :- 17/11/2023 3:08") + (0.02 ":ircnet.hostsailor.com 376 tester :End of MOTD command.")) + +((mode 10 "MODE tester +i") + (0.00 ":ircnet.hostsailor.com NOTICE tester :Your connection is secure (SSL/TLS).") + (0.01 ":tester MODE tester :+i")) + +((squery 10 "SQUERY alis :help list") + (0.08 ":Alis@hub.uk NOTICE tester :Searches for a channel") + (0.01 ":Alis@hub.uk NOTICE tester :/SQUERY Alis LIST mask [-options]") + (0.04 ":Alis@hub.uk NOTICE tester :[...]") + (0.01 ":Alis@hub.uk NOTICE tester :See also: HELP EXAMPLES")) diff --git a/test/lisp/erc/resources/commands/vhost.eld b/test/lisp/erc/resources/commands/vhost.eld new file mode 100644 index 00000000000..42013198fbc --- /dev/null +++ b/test/lisp/erc/resources/commands/vhost.eld @@ -0,0 +1,40 @@ +;; -*- mode: lisp-data; -*- +((pass 10 "PASS :changeme")) +((nick 10 "NICK tester")) +((user 10 "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 10 "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 10 "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-chan 10 "MODE #chan") + (0 ":irc.foonet.org 324 tester #chan +nt") + (0 ":irc.foonet.org 329 tester #chan 1620104779") + (0 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!") + (0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :tester, welcome!")) + +((vhost 10 "VHOST tester changeme") + (0 ":irc.foonet.org NOTICE tester :Setting your VHost: some.host.test.cc") + (0 ":irc.foonet.org 396 tester some.host.test.cc :is now your displayed host") + (0 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: But, as it seems, did violence on herself.") + (0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Well, this is the forest of Arden.")) diff --git a/test/lisp/erc/resources/dcc/chat/accept.eld b/test/lisp/erc/resources/dcc/chat/accept.eld index a23e9580bcc..463f931d26f 100644 --- a/test/lisp/erc/resources/dcc/chat/accept.eld +++ b/test/lisp/erc/resources/dcc/chat/accept.eld @@ -17,7 +17,7 @@ (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") +((mode-user 10 "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-t.el b/test/lisp/erc/resources/erc-d/erc-d-t.el index 5914419ba61..2dc8398198f 100644 --- a/test/lisp/erc/resources/erc-d/erc-d-t.el +++ b/test/lisp/erc/resources/erc-d/erc-d-t.el @@ -83,6 +83,8 @@ returning." (ignore-errors (kill-buffer buf))))) (sleep-for erc-d-t-cleanup-sleep-secs))))) +(defvar erc-d-t--wait-message-prefix "Awaiting: ") + (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, @@ -99,7 +101,7 @@ be desirable." (let ((inverted (make-symbol "inverted")) (time-out (make-symbol "time-out")) (result (make-symbol "result"))) - `(ert-info ((concat "Awaiting: " ,msg)) + `(ert-info ((concat erc-d-t--wait-message-prefix ,msg)) (let ((,time-out (abs ,max-secs)) (,inverted (< ,max-secs 0)) (,result ',result)) @@ -120,7 +122,8 @@ On failure, emit MSG." (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)))) + `(let ((erc-d-t--wait-message-prefix "Sustaining: ")) + (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. @@ -154,6 +157,7 @@ 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))) + (erc-d-t--wait-message-prefix (and (< timeout 0) "Sustaining: ")) (cb (lambda () (unless pos (push (cons (current-buffer) (setq pos (make-marker))) diff --git a/test/lisp/erc/resources/erc-d/erc-d-tests.el b/test/lisp/erc/resources/erc-d/erc-d-tests.el index edc83c0b0ab..78f87399afb 100644 --- a/test/lisp/erc/resources/erc-d/erc-d-tests.el +++ b/test/lisp/erc/resources/erc-d/erc-d-tests.el @@ -674,7 +674,7 @@ nonzero for this to work." (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")) + (with-current-buffer (erc-d-t-wait-for 10 (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")) diff --git a/test/lisp/erc/resources/erc-d/erc-d-u.el b/test/lisp/erc/resources/erc-d/erc-d-u.el index c735a2c6114..11202f41112 100644 --- a/test/lisp/erc/resources/erc-d/erc-d-u.el +++ b/test/lisp/erc/resources/erc-d/erc-d-u.el @@ -74,6 +74,7 @@ (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) + (buffer-live-p (erc-d-u-scan-d-buf hunks)) (with-current-buffer (erc-d-u-scan-d-buf hunks) (goto-char pos) (condition-case _err diff --git a/test/lisp/erc/resources/erc-d/erc-d.el b/test/lisp/erc/resources/erc-d/erc-d.el index b605930de94..89701442ff6 100644 --- a/test/lisp/erc/resources/erc-d/erc-d.el +++ b/test/lisp/erc/resources/erc-d/erc-d.el @@ -254,7 +254,7 @@ return a replacement.") (ending (process-get process :dialog-ending)) (dialog (make-erc-d-dialog :name name :process process - :queue (make-ring 5) + :queue (make-ring 10) :exchanges (make-ring 10) :match-handlers mat-h :server-fqdn fqdn))) @@ -292,32 +292,27 @@ With int SKIP, advance past that many exchanges." (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--m (process format-string &rest args) + "Output ARGS using FORMAT-STRING to PROCESS's buffer or elsewhere." + (when erc-d--m-debug + (setq format-string (concat (format-time-string "%s.%N: ") format-string))) + (let ((insertp (and process erc-d--in-process)) + (buffer (and process (process-buffer (process-get process :server))))) + (when (and insertp (buffer-live-p buffer)) + (princ (concat (apply #'format format-string args) "\n") buffer)) + (when (or erc-d--m-debug (not insertp)) + (apply #'message format-string args)))) + +(defun erc-d--log (process string &optional outbound) + "Log STRING received from or OUTBOUND to 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))) @@ -455,14 +450,14 @@ including line delimiters." (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)))) + (ring-insert queue (erc-d-i--parse-message line nil)))) (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 +;; . erc-d-u-scan-d) conses, each of which pairs a dialog's 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. ;; diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld index 4994e9c5503..e8feb2e6fd8 100644 --- a/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld +++ b/test/lisp/erc/resources/erc-d/resources/dynamic-barnet.eld @@ -18,14 +18,14 @@ (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") +((mode-user 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") +((mode 3 "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.") diff --git a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld index a47998e7d32..2db750e49da 100644 --- a/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld +++ b/test/lisp/erc/resources/erc-d/resources/dynamic-foonet.eld @@ -17,14 +17,14 @@ (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") +((mode-user 4 "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") +((mode 3 "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.") diff --git a/test/lisp/erc/resources/erc-d/resources/linger.eld b/test/lisp/erc/resources/erc-d/resources/linger.eld index 36c81a3af4b..e456370a800 100644 --- a/test/lisp/erc/resources/erc-d/resources/linger.eld +++ b/test/lisp/erc/resources/erc-d/resources/linger.eld @@ -20,14 +20,14 @@ (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") +((mode-user 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") +((mode-chan 2 "MODE #chan") (0 ":bob!~bob@example.org PRIVMSG #chan :hey")) ((linger 1.0 LINGER)) diff --git a/test/lisp/erc/resources/erc-scenarios-common.el b/test/lisp/erc/resources/erc-scenarios-common.el index 0f06255540d..0ec48d766ef 100644 --- a/test/lisp/erc/resources/erc-scenarios-common.el +++ b/test/lisp/erc/resources/erc-scenarios-common.el @@ -51,7 +51,7 @@ ;; 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 +;; similar), any failing test would cause all subsequent tests in a ;; file to fail like dominoes (making all but the first backtrace ;; useless). ;; @@ -61,6 +61,25 @@ ;; always associated with the fake network FooNet, while nicks Joe and ;; Mike are always on BarNet. (Networks are sometimes downcased.) ;; +;; Environment variables: +;; +;; `ERC_TESTS_GRAPHICAL': Internal variable to unskip those few tests +;; capable of running consecutively while interactive on a graphical +;; display. This triggers both the tests and the suite to commence +;; with teardown activities normally skipped to allow for inspection +;; while interactive. This is also handy when needing to quickly +;; run `ert-results-rerun-test-at-point-debugging-errors' on a +;; failing test because you don't have to go around hunting for and +;; killing associated buffers and processes. +;; +;; `ERC_TESTS_GRAPHICAL_ALL': Currently targets a single "meta" test, +;; `erc-scenarios-internal--run-interactive-all', that runs all +;; tests tagged `:erc--graphical' in an interactive subprocess. +;; +;; `ERC_TESTS_SUBPROCESS': Used internally to detect nested tests. +;; +;; `ERC_D_DEBUG': Tells `erc-d' to emit debugging info to stderr. +;; ;; XXX This file should *not* contain any test cases. ;;; Code: @@ -91,6 +110,7 @@ (defvar erc-scenarios-common-dialog nil) (defvar erc-scenarios-common-extra-teardown nil) +(defvar erc-scenarios-common--graphical-p nil) (defun erc-scenarios-common--add-silence () (advice-add #'erc-login :around #'erc-d-t-silence-around) @@ -110,7 +130,11 @@ (eval-and-compile (defun erc-scenarios-common--make-bindings (bindings) - `((erc-d-u-canned-dialog-dir (expand-file-name + `((erc-scenarios-common--graphical-p + (and (or erc-scenarios-common--graphical-p + (memq :erc--graphical (ert-test-tags (ert-running-test)))) + (not (and noninteractive (ert-skip "Interactive only"))))) + (erc-d-u-canned-dialog-dir (expand-file-name (or erc-scenarios-common-dialog (cadr (assq 'erc-scenarios-common-dialog ',bindings))) @@ -119,8 +143,10 @@ (quit . ,(erc-quit/part-reason-default)) (erc-version . ,erc-version))) (erc-modules (copy-sequence erc-modules)) - (inhibit-interaction t) + (inhibit-interaction noninteractive) (auth-source-do-cache nil) + (timer-list (copy-sequence timer-list)) + (timer-idle-list (copy-sequence timer-idle-list)) (erc-auth-source-parameters-join-function nil) (erc-autojoin-channels-alist nil) (erc-server-auto-reconnect nil) @@ -137,13 +163,19 @@ 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. +When running tests tagged as serially runnable while interactive +and the flag `erc-scenarios-common--graphical-p' is non-nil, run +teardown tasks normally inhibited when interactive. That is, +behave almost as if `noninteractive' were also non-nil, and +ensure buffers and other resources are destroyed on completion. + 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-scenarios-common--make-bindings bindings)))) `(erc-d-t-with-cleanup (,@combined) @@ -163,8 +195,9 @@ Dialog resource directories are located by expanding the variable (not (eq erc-autojoin-mode ,orig-autojoin-mode))) (erc-autojoin-mode (if ,orig-autojoin-mode +1 -1))) - (when noninteractive - (erc-scenarios-common--print-trace) + (when (or noninteractive erc-scenarios-common--graphical-p) + (when noninteractive + (erc-scenarios-common--print-trace)) (erc-d-t-kill-related-buffers) (delete-other-windows))) @@ -177,11 +210,118 @@ Dialog resource directories are located by expanding the variable (erc-d-t-search-for 3 "Starting"))))) (ert-info ("Activate erc-debug-irc-protocol") - (unless (and noninteractive (not erc-debug-irc-protocol)) + (unless (and (or noninteractive erc-scenarios-common--graphical-p) + (not erc-debug-irc-protocol)) (erc-toggle-debug-irc-protocol))) ,@body))) +(defvar erc-scenarios-common--term-size '(34 . 80)) +(declare-function term-char-mode "term" nil) +(declare-function term-line-mode "term" nil) + +;; Much of this concerns accommodating test environments outside of +;; the emacs.git tree, such as CI jobs running ERC's ELPA-package on +;; older Emacsen. See also `erc-tests--assert-printed-in-subprocess'. +(defun erc-scenarios-common--run-in-term (&optional debug) + (require 'term) + (let* ((default-directory (or (getenv "EMACS_TEST_DIRECTORY") + (expand-file-name + ".." erc-scenarios-common--resources-dir))) + ;; In the emacs.git tree, "HOME" will be "/nonexistent", which + ;; is fine because we don't need any ELPA packages. + (process-environment (cons "ERC_TESTS_SUBPROCESS=1" + process-environment)) + (name (ert-test-name (ert-running-test))) + (temp-file (make-temp-file "erc-term-test-")) + (cmd `(let ((stats 1)) + (setq enable-dir-local-variables nil) + (unwind-protect + (setq stats (ert-run-tests-batch ',name)) + (unless ',debug + (let ((buf (with-current-buffer (messages-buffer) + (buffer-string)))) + (with-temp-file ,temp-file + (insert buf))) + (kill-emacs (ert-stats-completed-unexpected stats)))))) + ;; The `ert-test' object in Emacs 29 has a `file-name' field. + (file-name (symbol-file name 'ert--test)) + (default-directory (expand-file-name (file-name-directory file-name))) + (package (if-let* ((found (getenv "ERC_PACKAGE_NAME")) + ((string-prefix-p "erc-" found))) + (intern found) + 'erc)) + (init (and-let* ((found (getenv "ERC_TESTS_INIT")) + (files (split-string found ","))) + (mapcan (lambda (f) (list "-l" f)) files))) + (setup `(progn + ,@(and (not init) (featurep 'compat) + `((require 'package) + (let ((package-load-list + '((compat t) (,package t)))) + (package-initialize)))) + (require 'erc) + (cl-assert (equal erc-version ,erc-version) t))) + ;; Make subprocess terminal bigger than controlling. + (buf (cl-letf (((symbol-function 'window-screen-lines) + (lambda () (car erc-scenarios-common--term-size))) + ((symbol-function 'window-max-chars-per-line) + (lambda () (cdr erc-scenarios-common--term-size)))) + (apply #'make-term (symbol-name name) + (expand-file-name invocation-name invocation-directory) + nil `(,@(or init '("-Q")) "-nw" + "-eval" ,(format "%S" setup) + "-l" ,file-name + "-eval" ,(format "%S" cmd))))) + (proc (get-buffer-process buf)) + (err (lambda () + (with-temp-buffer + (insert-file-contents temp-file) + (message "Subprocess: %s" (buffer-string)) + (delete-file temp-file))))) + (unless noninteractive + (set-window-buffer (selected-window) buf) + (delete-other-windows)) + (with-current-buffer buf + (set-process-query-on-exit-flag proc nil) + (unless noninteractive (term-char-mode)) + (erc-d-t-wait-for 30 (process-live-p proc)) + (while (accept-process-output proc)) + (term-line-mode) + (goto-char (point-min)) + ;; Otherwise gives process exited abnormally with exit-code >0 + (unless (search-forward (format "Process %s finished" name) nil t) + (funcall err) + (ert-fail (when (search-forward "exited" nil t) + (buffer-substring-no-properties (line-beginning-position) + (line-end-position))))) + (delete-file temp-file) + (when noninteractive + (kill-buffer))))) + +(defvar erc-scenarios-common-interactive-debug-term-p nil + "Non-nil means run test in an inferior Emacs, even if interactive.") + +(defmacro erc-scenarios-common-with-noninteractive-in-term (&rest body) + "Run BODY via `erc-scenarios-common-with-cleanup' in a `term' subprocess. +Also do so when `erc-scenarios-common-interactive-debug-term-p' +is non-nil. When debugging, leave the `term-mode' buffer around +for inspection and name it after the test, bounded by asterisks. +When debugging, ensure the test always fails, as a reminder to +disable `erc-scenarios-common-interactive-debug-term-p'. + +See Info node `(emacs) Term Mode' for the various commands." + (declare (indent 1)) + `(if (and (or erc-scenarios-common-interactive-debug-term-p + noninteractive) + (not (getenv "ERC_TESTS_SUBPROCESS"))) + (progn + (when (memq system-type '(windows-nt ms-dos cygwin haiku)) + (ert-skip "System must be UNIX-like")) + (erc-scenarios-common--run-in-term + erc-scenarios-common-interactive-debug-term-p)) + (erc-scenarios-common-with-cleanup ,@body))) + (defun erc-scenarios-common-assert-initial-buf-name (id port) ;; Assert no limbo period when explicit ID given (should (string= (if id @@ -208,9 +348,111 @@ Dialog resource directories are located by expanding the variable (insert str) (erc-send-current-line))) +(defun erc-scenarios-common--at-win-end-p (&optional window) + (= (window-body-height window) + (count-screen-lines (window-start window) (point-max) nil window))) + +(defun erc-scenarios-common--above-win-end-p (&optional window) + (> (window-body-height window) + (count-screen-lines (window-start window) (point-max)))) + +(defun erc-scenarios-common--prompt-past-win-end-p (&optional window) + (< (window-body-height window) + (count-screen-lines (window-start window) (point-max)))) + +(defun erc-scenarios-common--recenter-top-bottom-around (orig &rest args) + (let (this-command last-command) (apply orig args))) + +(defun erc-scenarios-common--recenter-top-bottom () + (advice-add 'recenter-top-bottom + :around #'erc-scenarios-common--recenter-top-bottom-around) + (execute-kbd-macro "\C-l") + (advice-remove 'recenter-top-bottom + #'erc-scenarios-common--recenter-top-bottom-around)) + ;;;; Fixtures +(defun erc-scenarios-common-scrolltobottom--normal (test) + (erc-scenarios-common-with-noninteractive-in-term + ((erc-scenarios-common-dialog "scrolltobottom") + (dumb-server (erc-d-run "localhost" t 'help)) + (port (process-contact dumb-server :service)) + (erc-modules `(scrolltobottom fill-wrap ,@erc-modules)) + (erc-server-flood-penalty 0.1) + (expect (erc-d-t-make-expecter))) + + (ert-info ("Connect") + (with-current-buffer (erc :server "127.0.0.1" + :port port + :full-name "tester" + :nick "tester") + (funcall expect 10 "debug mode"))) + + (with-current-buffer "foonet" + (should (looking-at " and")) + (set-window-buffer nil (current-buffer)) + (delete-other-windows) + (split-window-below 15) + (recenter 0) + + (ert-info ("Moving into prompt in other window triggers scroll") + (with-selected-window (next-window) + (should-not (erc-scenarios-common--at-win-end-p)) + (goto-char (1- erc-insert-marker)) + (execute-kbd-macro "\C-n") + ;; Ensure point is at prompt and aligned to bottom. + (should (erc-scenarios-common--at-win-end-p)))) + + (ert-info ("Module `move-to-prompt' still works") + ;; Prompt is somewhere in the middle of the window. + (should (erc-scenarios-common--above-win-end-p)) + ;; Hitting a self-insert key triggers `move-to-prompt' as well + ;; as a scroll (to bottom). + (execute-kbd-macro "hi") + ;; Prompt and input appear on last line of window. + (should (erc-scenarios-common--at-win-end-p))) + + (ert-info ("Command `recenter-top-bottom' disallowed at prompt") + ;; Hitting C-l does not recenter the window. + (erc-scenarios-common--recenter-top-bottom) + (should (erc-scenarios-common--at-win-end-p)) + (erc-scenarios-common--recenter-top-bottom) + (should (erc-scenarios-common--at-win-end-p))) + + (ert-info ("Command `beginning-of-buffer' allowed at prompt") + ;; Hitting C-< goes to beginning of buffer. + (call-interactively #'beginning-of-buffer) + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (ert-info ("New message doesn't trigger scroll when away from prompt") + ;; Arriving insertions don't trigger a scroll when away from the + ;; prompt. New output not seen. + (erc-cmd-MSG "NickServ help register") + (save-excursion (erc-d-t-search-for 10 "End of NickServ")) + (should (= 1 (point))) + (redisplay) + (should (zerop (count-screen-lines (window-start) (window-point)))) + (should (erc-scenarios-common--prompt-past-win-end-p))) + + (funcall test) + + (ert-info ("New message does trigger a scroll when at prompt") + ;; Recenter so prompt is above rather than at window's end. + (funcall expect 10 "If you are currently logged in") + (recenter 0) + ;; Prompt is somewhere in the middle of the window. + (erc-d-t-wait-for 10 (erc-scenarios-common--above-win-end-p)) + (erc-scenarios-common-say "/msg NickServ help identify") + ;; New arriving messages trigger a snap when inserted. + (erc-d-t-wait-for 10 (erc-scenarios-common--at-win-end-p)) + (funcall expect 10 "IDENTIFY lets you login")) + + (erc-scrolltobottom-mode -1)))) + (cl-defun erc-scenarios-common--base-network-id-bouncer ((&key autop foo-id bar-id after &aux @@ -247,7 +489,7 @@ buffer-naming collisions involving bouncers in ERC." :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 6 (eq (erc-network) 'foonet)) (erc-d-t-wait-for 3 (string= (buffer-name) serv-buf-foo)) (funcall expect 5 "foonet"))) @@ -287,7 +529,7 @@ buffer-naming collisions involving bouncers in ERC." (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-search-for 15 "ape is dead") (erc-d-t-wait-for 5 (not (erc-server-process-alive))))) (ert-info ("#chan@<esid> is exclusive to barnet") @@ -366,7 +608,7 @@ buffer-naming collisions involving bouncers in ERC." :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 6 (eq (erc-network) 'foonet)) (erc-d-t-wait-for 3 (string= (buffer-name) "foonet")) (funcall expect 5 "foonet"))) @@ -446,10 +688,17 @@ Bug#48598: 28.0.50; buffer-naming collisions involving bouncers in ERC." (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-cmd-QUIT "") + + (ert-info ("Prompt hidden in channel buffer upon quitting") + (erc-d-t-wait-for 10 (erc--prompt-hidden-p)) + (should (overlays-in erc-insert-marker erc-input-marker))))) - (erc-d-t-wait-for 2 "Foonet connection deceased" - (not (erc-server-process-alive erc-server-buffer-foo))) + (with-current-buffer erc-server-buffer-foo + (ert-info ("Prompt hidden after process dies in server buffer") + (erc-d-t-wait-for 2 (not (erc-server-process-alive))) + (erc-d-t-wait-for 10 (erc--prompt-hidden-p)) + (should (overlays-in erc-insert-marker erc-input-marker)))) (should (equal erc-autojoin-channels-alist (if foo-id '((oofnet "#chan")) '((foonet "#chan"))))) @@ -498,6 +747,10 @@ Bug#48598: 28.0.50; buffer-naming collisions involving bouncers in ERC." (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"))) + + (ert-info ("Prompt unhidden") + (should-not (erc--prompt-hidden-p)) + (should-not (overlays-in erc-insert-marker erc-input-marker))) (funcall expect 5 "foonet"))) (ert-info ("#chan@foonet is clean, no cross-contamination") @@ -505,7 +758,11 @@ Bug#48598: 28.0.50; buffer-naming collisions involving bouncers in ERC." (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"))) + (funcall expect 30 "not given me") + + (ert-info ("Prompt unhidden") + (should-not (erc--prompt-hidden-p)) + (should-not (overlays-in erc-insert-marker erc-input-marker))))) (ert-info ("All #chan@barnet output received") (with-current-buffer chan-buf-bar diff --git a/test/lisp/erc/resources/erc-tests-common.el b/test/lisp/erc/resources/erc-tests-common.el new file mode 100644 index 00000000000..20b3a56facc --- /dev/null +++ b/test/lisp/erc/resources/erc-tests-common.el @@ -0,0 +1,301 @@ +;;; erc-tests-common.el --- Common helpers for ERC tests -*- lexical-binding: t -*- + +;; Copyright (C) 2023 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: + +;; This file must *not* contain any `ert-deftest' definitions. See +;; top of test/lisp/erc/erc-tests.el for loading example. +;; +;; Environment variables: +;; +;; `ERC_PACKAGE_NAME': Name of the installed ERC package currently +;; running. ERC needs this in order to load the same package in +;; tests that run in a subprocess. Necessary even when the package +;; name is `erc' and not something like `erc-49860'. +;; +;; `ERC_TESTS_INIT': The name of an alternate init file. Mainly for +;; integrations tests involving starter kits. +;; +;; `ERC_TESTS_SNAPSHOT_SAVE': When set, ERC saves the current test's +;; snapshots to disk. +;; + +;;; Code: +(require 'ert-x) +(require 'erc) + + +(defmacro erc-tests-common-equal-with-props (a b) + "Compare strings A and B for equality including text props. +Use `ert-equal-including-properties' on older Emacsen." + (list (if (< emacs-major-version 29) + 'ert-equal-including-properties + 'equal-including-properties) + a b)) + +;; Caller should probably shadow `erc-insert-modify-hook' or populate +;; user tables for erc-button. +;; FIXME explain this comment ^ in more detail or delete. +(defun erc-tests-common-prep-for-insertion () + "Initialize current buffer with essentials for message insertion. +Assume caller intends to use `erc-display-message'." + (erc-mode) + (erc--initialize-markers (point) nil) + (should (= (point) erc-input-marker))) + +(defun erc-tests-common-init-server-proc (&rest args) + "Create a process with `start-process' from ARGS. +Assign the result to `erc-server-process' in the current buffer." + (setq erc-server-process + (apply #'start-process (car args) (current-buffer) args)) + (set-process-query-on-exit-flag erc-server-process nil) + erc-server-process) + +;; After dropping support for Emacs 27, callers can use +;; `get-buffer-create' with INHIBIT-BUFFER-HOOKS. +(defun erc-tests-common-kill-buffers (&rest extra-buffers) + "Kill all ERC buffers and possibly EXTRA-BUFFERS." + (let (erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) + (dolist (buf (erc-buffer-list)) + (kill-buffer buf)) + (named-let doit ((buffers extra-buffers)) + (dolist (buf buffers) + (if (consp buf) (doit buf) (kill-buffer buf)))))) + +(defun erc-tests-common-with-process-input-spy (test-fn) + "Mock `erc-process-input-line' and call TEST-FN. +Shadow `erc--input-review-functions' and `erc-pre-send-functions' +with `erc-add-to-input-ring' removed. Shadow other relevant +variables as nil, and bind `erc-last-input-time' to 0. Also mock +`erc-server-buffer' to return the current buffer. Call TEST-FN +with a utility function that returns the set of arguments most +recently passed to the mocked `erc-process-input-line'. Make +`inhibit-message' non-nil unless running interactively." + (with-current-buffer (get-buffer-create "FakeNet") + (let* ((erc--input-review-functions + (remove 'erc-add-to-input-ring erc--input-review-functions)) + (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-common-prep-for-insertion) + (funcall test-fn (lambda () (pop calls))))) + (when noninteractive (kill-buffer)))) + +(defun erc-tests-common-make-server-buf (&optional name) + "Return a server buffer named NAME, creating it if necessary. +Use NAME for the network and the session server as well." + (unless name + (cl-assert (string-prefix-p " *temp*" (setq name (buffer-name))))) + (with-current-buffer (get-buffer-create name) + (erc-tests-common-prep-for-insertion) + (erc-tests-common-init-server-proc "sleep" "1") + (setq erc-session-server (concat "irc." name ".org") + erc-server-announced-name (concat "west." name ".org") + erc-server-users (make-hash-table :test #'equal) + erc-server-parameters nil + erc--isupport-params (make-hash-table) + erc-session-port 6667 + erc-network (intern name) + erc-networks--id (erc-networks--id-create nil)) + (current-buffer))) + +(defun erc-tests-common-string-to-propertized-parts (string) + "Return a sequence of `propertize' forms for generating STRING. +Expect maintainers manipulating template catalogs to use this +with `pp-eval-last-sexp' or similar to convert back and forth +between literal strings." + `(concat + ,@(mapcar + (pcase-lambda (`(,beg ,end ,plist)) + ;; At the time of writing, `propertize' produces a string + ;; with the order of the input plist reversed. + `(propertize ,(substring-no-properties string beg end) + ,@(let (out) + (while-let ((plist) + (k (pop plist)) + (v (pop plist))) + (push (if (or (consp v) (symbolp v)) `',v v) out) + (push `',k out)) + out))) + (object-intervals string)))) + +(defun erc-tests-common-pp-propertized-parts (arg) + "Convert literal string before point into a `propertize'd form. +For simplicity, assume string evaluates to itself." + (interactive "P") + (let ((sexp (erc-tests-common-string-to-propertized-parts (pp-last-sexp)))) + (if arg (insert (pp-to-string sexp)) (pp-eval-expression sexp)))) + +;; The following utilities are meant to help prepare tests for +;; `erc--get-inserted-msg-bounds' and friends. +(defun erc-tests-common-get-inserted-msg-setup () + (erc-tests-common-prep-for-insertion) + (let ((parsed (make-erc-response :unparsed ":bob PRIVMSG #chan :hi" + :sender "bob" + :command "PRIVMSG" + :command-args (list "#chan" "hi") + :contents "hi")) + (erc--msg-prop-overrides '((erc--ts . 0)))) + (erc-display-message parsed nil (current-buffer) + (erc-format-privmessage "bob" "hi" nil t))) + (goto-char 3) + (should (looking-at "<bob> hi"))) + +;; All these bounds-finding functions take an optional POINT argument. +;; So run each case with and without it at each pos in the message. +(defun erc-tests-common-assert-get-inserted-msg (from to assert-fn) + (dolist (pt-arg '(nil t)) + (dolist (i (number-sequence from to)) + (goto-char i) + (ert-info ((format "At %d (%c) %s param" i (char-after i) + (if pt-arg "with" ""))) + (funcall assert-fn (and pt-arg i)))))) + +(defun erc-tests-common-assert-get-inserted-msg/basic (test-fn) + (erc-tests-common-get-inserted-msg-setup) + (goto-char 11) + (should (looking-back "<bob> hi")) + (erc-tests-common-assert-get-inserted-msg 3 11 test-fn)) + +;; This is a "mixin" and requires a base assertion function, like +;; `erc-tests-common-assert-get-inserted-msg/basic', to work. +(defun erc-tests-common-assert-get-inserted-msg-readonly-with + (assert-fn test-fn) + (defvar erc-readonly-mode) + (defvar erc-readonly-mode-hook) + (let ((erc-readonly-mode nil) + (erc-readonly-mode-hook nil) + (erc-send-post-hook erc-send-post-hook) + (erc-insert-post-hook erc-insert-post-hook)) + (erc-readonly-mode +1) + (funcall assert-fn test-fn))) + + +;;;; Buffer snapshots + +;; Use this variable to generate new snapshots after carefully +;; reviewing the output of *each* snapshot (not just first and last). +;; Obviously, only run one test at a time. +(defvar erc-tests-common-snapshot-save-p (getenv "ERC_TESTS_SNAPSHOT_SAVE")) + +(defun erc-tests-common-snapshot-compare (name dir trans-fn buf-init-fn) + "Compare `buffer-string' to snapshot NAME.eld in DIR, if present. +When non-nil, run TRANS-FN to fiter the current buffer string, +and expect a similar string in return. Call BUF-INIT-FN, when +non-nil, in the preview buffer after inserting the filtered +string." + (let* ((expect-file (file-name-with-extension (expand-file-name name dir) + "eld")) + (erc--own-property-names + (seq-difference `(font-lock-face ,@erc--own-property-names) + `(field display wrap-prefix line-prefix + erc--msg erc--cmd erc--spkr erc--ts erc--ctcp + erc--ephemeral) + #'eq)) + (print-circle t) + (print-escape-newlines t) + (print-escape-nonascii t) + (got (erc--remove-text-properties + (buffer-substring (point-min) erc-insert-marker))) + (repr (funcall (or trans-fn #'identity) (prin1-to-string got)))) + (with-current-buffer (generate-new-buffer name) + (with-silent-modifications + (insert (setq got (read repr)))) + (when buf-init-fn (funcall buf-init-fn)) + (erc-mode)) + ;; LHS is a string, RHS is a symbol. + (if (string= erc-tests-common-snapshot-save-p + (ert-test-name (ert-running-test))) + (let (inhibit-message) + (with-temp-file expect-file + (insert repr)) + ;; Limit writing snapshots to one test at a time. + (message "erc-tests-common-snapshot-compare: wrote %S" expect-file)) + (if (file-exists-p expect-file) + ;; Ensure string-valued properties, like timestamps, aren't + ;; recursive (signals `max-lisp-eval-depth' exceeded). + (named-let assert-equal + ((latest (read repr)) + (expect (read (with-temp-buffer + (insert-file-contents-literally expect-file) + (buffer-string))))) + (pcase latest + ((or "" 'nil) t) + ((pred stringp) + (should (equal-including-properties latest expect)) + (let ((latest-intervals (object-intervals latest)) + (expect-intervals (object-intervals expect))) + (while-let ((l-iv (pop latest-intervals)) + (x-iv (pop expect-intervals)) + (l-tab (map-into (nth 2 l-iv) 'hash-table)) + (x-tab (map-into (nth 2 x-iv) 'hash-table))) + (pcase-dolist (`(,l-k . ,l-v) (map-pairs l-tab)) + (assert-equal l-v (gethash l-k x-tab)) + (remhash l-k x-tab)) + (should (zerop (hash-table-count x-tab)))))) + ((pred sequencep) + (assert-equal (seq-first latest) (seq-first expect)) + (assert-equal (seq-rest latest) (seq-rest expect))) + (_ (should (equal latest expect))))) + (message "Snapshot file missing: %S" expect-file))))) + +(defun erc-tests-common-create-subprocess (code switches libs) + "Return subprocess for running CODE in an inferior Emacs. +Include SWITCHES, like \"-batch\", as well as libs, after +interspersing \"-l\" between members." + (let* ((package (if-let ((found (getenv "ERC_PACKAGE_NAME")) + ((string-prefix-p "erc-" found))) + (intern found) + 'erc)) + ;; For integrations testing with managed configs that use a + ;; different package manager. + (init (and-let* ((found (getenv "ERC_TESTS_INIT")) + (files (split-string found ","))) + (mapcan (lambda (f) (list "-l" f)) files))) + (prog + `(progn + ,@(and (not init) (featurep 'compat) + `((require 'package) + (let ((package-load-list '((compat t) (,package t)))) + (package-initialize)))) + (require 'erc) + (cl-assert (equal erc-version ,erc-version) t) + ,code)) + (proc (apply #'start-process + (symbol-name (ert-test-name (ert-running-test))) + (current-buffer) + (concat invocation-directory invocation-name) + `(,@(or init '("-Q")) + ,@switches + ,@(mapcan (lambda (f) (list "-l" f)) libs) + "-eval" ,(format "%S" prog))))) + (set-process-query-on-exit-flag proc t) + proc)) + +(provide 'erc-tests-common) diff --git a/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld b/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld new file mode 100644 index 00000000000..3c32719a052 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/merge-01-start.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<alice> one.\n<alice> two.\n<bob> three.\n<bob> four.\n<Dummy> five.\n<Dummy> six.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #5# display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--ts 1680332400 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 (8)))) 475 480 (wrap-prefix #1# line-prefix #7#) 480 486 (wrap-prefix #1# line-prefix #7#) 487 488 (erc--msg msg erc--ts 1680332400 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #8=(space :width (- 27 0)) display #9="") 488 493 (wrap-prefix #1# line-prefix #8# display #9#) 493 495 (wrap-prefix #1# line-prefix #8# display #9#) 495 499 (wrap-prefix #1# line-prefix #8#) 500 501 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 501 504 (wrap-prefix #1# line-prefix #10#) 504 512 (wrap-prefix #1# line-prefix #10#) 513 514 (erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #9#) 514 517 (wrap-prefix #1# line-prefix #11# display #9#) 517 519 (wrap-prefix #1# line-prefix #11# display #9#) 519 524 (wrap-prefix #1# line-prefix #11#) 525 526 (erc--msg msg erc--ts 1680332400 erc--spkr "Dummy" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 27 (8)))) 526 531 (wrap-prefix #1# line-prefix #12#) 531 538 (wrap-prefix #1# line-prefix #12#) 539 540 (erc--msg msg erc--ts 1680332400 erc--spkr "Dummy" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 0)) display #9#) 540 545 (wrap-prefix #1# line-prefix #13# display #9#) 545 547 (wrap-prefix #1# line-prefix #13# display #9#) 547 551 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld b/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld new file mode 100644 index 00000000000..e2064b914c4 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/merge-02-right.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<alice> one.\n<alice> two.\n<bob> three.\n<bob> four.\n<Dummy> five.\n<Dummy> six.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (wrap-prefix #1=(space :width 29) line-prefix (space :width (- 29 (18))) field erc-timestamp) 21 22 (wrap-prefix #1# line-prefix #2=(space :width (- 29 (4))) erc--msg notice erc--ts 0) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (wrap-prefix #1# line-prefix #2# field erc-timestamp display (#6=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (wrap-prefix #1# line-prefix #3=(space :width (- 29 (8))) erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix (space :width (- 29 (8)))) 349 350 (wrap-prefix #1# line-prefix #4=(space :width (- 29 (6))) erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (wrap-prefix #1# line-prefix (space :width (- 29 (18))) field erc-timestamp) 455 456 (wrap-prefix #1# line-prefix #5=(space :width (- 29 (6))) erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG) 456 459 (wrap-prefix #1# line-prefix #5#) 459 466 (wrap-prefix #1# line-prefix #5#) 466 473 (wrap-prefix #1# line-prefix #5# field erc-timestamp display (#6# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (wrap-prefix #1# line-prefix #7=(space :width (- 29 (8))) erc--msg msg erc--ts 1680332400 erc--spkr "alice" erc--cmd PRIVMSG) 475 480 (wrap-prefix #1# line-prefix #7#) 480 486 (wrap-prefix #1# line-prefix #7#) 487 488 (wrap-prefix #1# line-prefix #8=(space :width (- 29 0)) erc--msg msg erc--ts 1680332400 erc--spkr "alice" erc--cmd PRIVMSG display #9="") 488 493 (wrap-prefix #1# line-prefix #8# display #9#) 493 495 (wrap-prefix #1# line-prefix #8# display #9#) 495 499 (wrap-prefix #1# line-prefix #8#) 500 501 (wrap-prefix #1# line-prefix #10=(space :width (- 29 (6))) erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG) 501 504 (wrap-prefix #1# line-prefix #10#) 504 512 (wrap-prefix #1# line-prefix #10#) 513 514 (wrap-prefix #1# line-prefix #11=(space :width (- 29 0)) erc--msg msg erc--ts 1680332400 erc--spkr "bob" erc--cmd PRIVMSG display #9#) 514 517 (wrap-prefix #1# line-prefix #11# display #9#) 517 519 (wrap-prefix #1# line-prefix #11# display #9#) 519 524 (wrap-prefix #1# line-prefix #11#) 525 526 (wrap-prefix #1# line-prefix #12=(space :width (- 29 (8))) erc--msg msg erc--ts 1680332400 erc--spkr "Dummy" erc--cmd PRIVMSG) 526 531 (wrap-prefix #1# line-prefix #12#) 531 538 (wrap-prefix #1# line-prefix #12#) 539 540 (wrap-prefix #1# line-prefix #13=(space :width (- 29 0)) erc--msg msg erc--ts 1680332400 erc--spkr "Dummy" erc--cmd PRIVMSG display #9#) 540 545 (wrap-prefix #1# line-prefix #13# display #9#) 545 547 (wrap-prefix #1# line-prefix #13# display #9#) 547 551 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld b/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld new file mode 100644 index 00000000000..feaba85ec90 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/merge-wrap-01.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--spkr "alice" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--spkr "bob" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 0)) display #8="") 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #10#) 499 505 (wrap-prefix #1# line-prefix #10#) 506 507 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #8#) 507 510 (wrap-prefix #1# line-prefix #11# display #8#) 510 512 (wrap-prefix #1# line-prefix #11# display #8#) 512 515 (wrap-prefix #1# line-prefix #11#) 516 517 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #12=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #12#) 518 521 (wrap-prefix #1# line-prefix #12#) 521 527 (wrap-prefix #1# line-prefix #12#) 528 529 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #13#) 532 539 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/merge-wrap-indicator-post-01.eld b/test/lisp/erc/resources/fill/snapshots/merge-wrap-indicator-post-01.eld new file mode 100644 index 00000000000..ed1488c8595 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/merge-wrap-indicator-post-01.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--spkr "alice" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--spkr "bob" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 0)) display #8="") 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #10=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #10#) 499 505 (wrap-prefix #1# line-prefix #10#) 505 506 (display #("~\n" 0 2 (font-lock-face shadow))) 506 507 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 0)) display #8#) 507 510 (wrap-prefix #1# line-prefix #11# display #8#) 510 512 (wrap-prefix #1# line-prefix #11# display #8#) 512 515 (wrap-prefix #1# line-prefix #11#) 516 517 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #12=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #12#) 518 521 (wrap-prefix #1# line-prefix #12#) 521 527 (wrap-prefix #1# line-prefix #12#) 528 529 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #13=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #13#) 532 539 (wrap-prefix #1# line-prefix #13#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/merge-wrap-indicator-pre-01.eld b/test/lisp/erc/resources/fill/snapshots/merge-wrap-indicator-pre-01.eld new file mode 100644 index 00000000000..a3530a6c44d --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/merge-wrap-indicator-pre-01.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n\n[Sat Apr 1 2023]\n<bob> zero.[07:00]\n<bob> 0.5\n* bob one.\n<bob> two.\n<bob> 2.5\n* bob three\n<bob> four.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display (#5=(margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--spkr "alice" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--spkr "bob" erc--ts 0 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 436 437 (erc--msg datestamp erc--ts 1680307200 field erc-timestamp) 437 454 (field erc-timestamp wrap-prefix #1# line-prefix (space :width (- 27 (18)))) 455 456 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #6=(space :width (- 27 (6)))) 456 459 (wrap-prefix #1# line-prefix #6#) 459 466 (wrap-prefix #1# line-prefix #6#) 466 473 (field erc-timestamp wrap-prefix #1# line-prefix #6# display (#5# #("[07:00]" 0 7 (invisible timestamp)))) 474 475 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #7=(space :width (- 27 #10=(2))) display #8=#("> " 0 1 (font-lock-face shadow))) 475 478 (wrap-prefix #1# line-prefix #7# display #8#) 478 480 (wrap-prefix #1# line-prefix #7# display #8#) 480 483 (wrap-prefix #1# line-prefix #7#) 484 485 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 485 486 (wrap-prefix #1# line-prefix #9#) 486 489 (wrap-prefix #1# line-prefix #9#) 489 494 (wrap-prefix #1# line-prefix #9#) 495 496 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #11=(space :width (- 27 (6)))) 496 499 (wrap-prefix #1# line-prefix #11#) 499 505 (wrap-prefix #1# line-prefix #11#) 506 507 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #12=(space :width (- 27 #10#)) display #8#) 507 510 (wrap-prefix #1# line-prefix #12# display #8#) 510 512 (wrap-prefix #1# line-prefix #12# display #8#) 512 515 (wrap-prefix #1# line-prefix #12#) 516 517 (erc--msg ctcp-action erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG erc--ctcp ACTION wrap-prefix #1# line-prefix #13=(space :width (- 27 (2)))) 517 518 (wrap-prefix #1# line-prefix #13#) 518 521 (wrap-prefix #1# line-prefix #13#) 521 527 (wrap-prefix #1# line-prefix #13#) 528 529 (erc--msg msg erc--spkr "bob" erc--ts 1680332400 erc--cmd PRIVMSG wrap-prefix #1# line-prefix #14=(space :width (- 27 (6)))) 529 532 (wrap-prefix #1# line-prefix #14#) 532 539 (wrap-prefix #1# line-prefix #14#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld b/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld new file mode 100644 index 00000000000..c94629cf357 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/monospace-01-start.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld b/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld new file mode 100644 index 00000000000..127c0b29bc9 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/monospace-02-right.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 29) line-prefix (space :width (- 29 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 29 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 29 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 29 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld b/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld new file mode 100644 index 00000000000..a9f3f1d1904 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/monospace-03-left.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 25) line-prefix (space :width (- 25 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 25 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 25 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 25 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld b/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld new file mode 100644 index 00000000000..c94629cf357 --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/monospace-04-reset.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld b/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld new file mode 100644 index 00000000000..754d7989cea --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/spacing-01-mono.eld @@ -0,0 +1 @@ +#("\n\n\n[Thu Jan 1 1970]\n*** 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.[00:00]\n<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n<bob> This buffer is for text.\n*** one two three\n*** four five six\n<bob> Somebody stop me\n" 2 3 (erc--msg datestamp erc--ts 0 field erc-timestamp) 3 20 (field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix (space :width (- 27 (18)))) 21 22 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #2=(space :width (- 27 (4)))) 22 183 (wrap-prefix #1# line-prefix #2#) 183 190 (field erc-timestamp wrap-prefix #1# line-prefix #2# display ((margin right-margin) #("[00:00]" 0 7 (invisible timestamp)))) 190 191 (line-spacing 0.5) 191 192 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #3=(space :width (- 27 (8)))) 192 197 (wrap-prefix #1# line-prefix #3#) 197 199 (wrap-prefix #1# line-prefix #3#) 199 202 (wrap-prefix #1# line-prefix #3#) 202 315 (wrap-prefix #1# line-prefix #3#) 316 348 (wrap-prefix #1# line-prefix #3#) 348 349 (line-spacing 0.5) 349 350 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #4=(space :width (- 27 (6)))) 350 353 (wrap-prefix #1# line-prefix #4#) 353 355 (wrap-prefix #1# line-prefix #4#) 355 360 (wrap-prefix #1# line-prefix #4#) 360 435 (wrap-prefix #1# line-prefix #4#) 435 436 (line-spacing 0.5) 436 437 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #5=(space :width (- 27 0)) display #6="") 437 440 (wrap-prefix #1# line-prefix #5# display #6#) 440 442 (wrap-prefix #1# line-prefix #5# display #6#) 442 466 (wrap-prefix #1# line-prefix #5#) 466 467 (line-spacing 0.5) 467 468 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #7=(space :width (- 27 (4)))) 468 484 (wrap-prefix #1# line-prefix #7#) 485 486 (erc--msg notice erc--ts 0 wrap-prefix #1# line-prefix #8=(space :width (- 27 (4)))) 486 502 (wrap-prefix #1# line-prefix #8#) 502 503 (line-spacing 0.5) 503 504 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG wrap-prefix #1# line-prefix #9=(space :width (- 27 (6)))) 504 507 (wrap-prefix #1# line-prefix #9#) 507 525 (wrap-prefix #1# line-prefix #9#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld b/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld new file mode 100644 index 00000000000..1b22b6c5cfd --- /dev/null +++ b/test/lisp/erc/resources/fill/snapshots/stamps-left-01.eld @@ -0,0 +1 @@ +#("\n\n[00: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.\n[00:00]<alice> bob: come, you are a tedious fool: to the purpose. What was done to Elbow's wife, that he hath cause to complain of? Come me to what was done to her.\n[00:00]<bob> alice: Either your unparagoned mistress is dead, or she's outprized by a trifle.\n" 2 3 (erc--msg notice erc--ts 0 display #3=(#5=(margin left-margin) #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1=(space :width 27) line-prefix #2=(space :width (- 27 (4)))) 3 9 (display #3# field erc-timestamp wrap-prefix #1# line-prefix #2#) 9 171 (wrap-prefix #1# line-prefix #2#) 172 173 (erc--msg msg erc--ts 0 erc--spkr "alice" erc--cmd PRIVMSG display #6=(#5# #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1# line-prefix #4=(space :width (- 27 (8)))) 173 179 (display #6# field erc-timestamp wrap-prefix #1# line-prefix #4#) 179 180 (wrap-prefix #1# line-prefix #4#) 180 185 (wrap-prefix #1# line-prefix #4#) 185 187 (wrap-prefix #1# line-prefix #4#) 187 190 (wrap-prefix #1# line-prefix #4#) 190 303 (wrap-prefix #1# line-prefix #4#) 304 336 (wrap-prefix #1# line-prefix #4#) 337 338 (erc--msg msg erc--ts 0 erc--spkr "bob" erc--cmd PRIVMSG display #8=(#5# #("[00:00]" 0 7 (invisible timestamp font-lock-face erc-timestamp-face))) field erc-timestamp wrap-prefix #1# line-prefix #7=(space :width (- 27 (6)))) 338 344 (display #8# field erc-timestamp wrap-prefix #1# line-prefix #7#) 344 345 (wrap-prefix #1# line-prefix #7#) 345 348 (wrap-prefix #1# line-prefix #7#) 348 350 (wrap-prefix #1# line-prefix #7#) 350 355 (wrap-prefix #1# line-prefix #7#) 355 430 (wrap-prefix #1# line-prefix #7#))
\ No newline at end of file diff --git a/test/lisp/erc/resources/join/buffer-display/mode-context.eld b/test/lisp/erc/resources/join/buffer-display/mode-context.eld new file mode 100644 index 00000000000..6ebbdc7e824 --- /dev/null +++ b/test/lisp/erc/resources/join/buffer-display/mode-context.eld @@ -0,0 +1,38 @@ +;; -*- mode: lisp-data; -*- +((nick 1 "NICK tester")) +((user 1 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":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 Tue, 24 May 2022 05:28:42 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 4 invisible on 1 server(s)") + (0.00 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.00 ":irc.foonet.org 254 tester 2 :channels formed") + (0.00 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.00 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0.00 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0.00 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode 6 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.00 ":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.02 ":irc.foonet.org 221 tester +i")) + +((join-chan 10 "JOIN #chan") + (0.03 ":tester!~u@w9rfqveugz722.irc JOIN #chan")) + +((~mode-chan 10 "MODE #chan") + (0.01 ":irc.foonet.org 353 tester = #chan :@tester") + (0.00 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.01 ":irc.foonet.org 324 tester #chan +nt") + (0.03 ":irc.foonet.org 329 tester #chan 1653370308")) + +((~join-spam 10 "JOIN #spam") + (0.03 ":irc.foonet.org 471 tester #spam :Cannot join channel (+l)")) + +((~join-foo 10 "JOIN #foo") + (0.03 ":irc.foonet.org 473 tester #foo :Cannot join channel (+i)")) diff --git a/test/lisp/erc/resources/join/legacy/foonet.eld b/test/lisp/erc/resources/join/legacy/foonet.eld index 4025094a59c..5c0ea13b6a7 100644 --- a/test/lisp/erc/resources/join/legacy/foonet.eld +++ b/test/lisp/erc/resources/join/legacy/foonet.eld @@ -18,7 +18,7 @@ (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") +((mode-user 10 "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/join/network-id/barnet.eld b/test/lisp/erc/resources/join/network-id/barnet.eld index e33dd6be29e..ad6a7c820a9 100644 --- a/test/lisp/erc/resources/join/network-id/barnet.eld +++ b/test/lisp/erc/resources/join/network-id/barnet.eld @@ -40,4 +40,4 @@ (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)) +((linger 30 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 index b230eff27c7..a8b8a52f87a 100644 --- a/test/lisp/erc/resources/join/network-id/foonet-again.eld +++ b/test/lisp/erc/resources/join/network-id/foonet-again.eld @@ -43,4 +43,4 @@ (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)) +((linger 30 LINGER)) diff --git a/test/lisp/erc/resources/join/network-id/foonet.eld b/test/lisp/erc/resources/join/network-id/foonet.eld index 7d63f5f0c6c..74a107f8144 100644 --- a/test/lisp/erc/resources/join/network-id/foonet.eld +++ b/test/lisp/erc/resources/join/network-id/foonet.eld @@ -1,8 +1,8 @@ ;; -*- mode: lisp-data; -*- ((pass 10 "PASS :foonet:changeme")) -((nick 1 "NICK tester")) +((nick 10 "NICK tester")) -((user 1 "USER user 0 * :tester") +((user 10 "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") diff --git a/test/lisp/erc/resources/keep-place/follow.eld b/test/lisp/erc/resources/keep-place/follow.eld new file mode 100644 index 00000000000..db9352d93be --- /dev/null +++ b/test/lisp/erc/resources/keep-place/follow.eld @@ -0,0 +1,78 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER tester 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Tue, 26 Dec 2023 08:36:35 UTC") + (0.01 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.01 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.00 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.00 ":irc.foonet.org 254 tester 2 :channels formed") + (0.00 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.00 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0.00 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0.03 ":irc.foonet.org 422 tester :MOTD File is missing") + (0.01 ":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.")) + +((mode 10 "MODE tester +i")) + +((join 10 "JOIN #chan") + (0.01 ":irc.foonet.org 221 tester +i") + (0.01 ":tester!~u@p64eqfwvvbxrk.irc JOIN #chan") + (0.03 ":irc.foonet.org 353 tester = #chan :@fsbot bob alice tester") + (0.01 ":irc.foonet.org 366 tester #chan :End of NAMES list") + (0.00 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :tester, welcome!") + (0.01 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :tester, welcome!")) + +((join 10 "JOIN #spam") + (0.00 ":tester!~u@p64eqfwvvbxrk.irc JOIN #spam") + (0.06 ":irc.foonet.org 353 tester = #spam :@fsbot bob alice tester") + (0.01 ":irc.foonet.org 366 tester #spam :End of NAMES list") + (0.03 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #spam :tester, welcome!") + (0.01 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #spam :tester, welcome!")) + +((mode 10 "MODE #chan") + (0.00 ":irc.foonet.org 324 tester #chan +Cnt") + (0.02 ":irc.foonet.org 329 tester #chan 1703579802") + (0.02 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :bob: Madam, my lord is gone, for ever gone.") + (0.10 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :The kinder we, to give them thanks for nothing.")) + +((mode 10 "MODE #spam") + (0.00 ":irc.foonet.org 324 tester #spam +Cnt") + (0.02 ":irc.foonet.org 329 tester #spam 1703579805") + (0.02 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :Most manifest, and not denied by himself.") + (0.02 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :alice: To bed, to bed: there's knocking at the gate. Come, come, come, come, give me your hand. What's done cannot be undone.") + (0.02 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :bob: And what I spake, I spake it to my face.") + (0.08 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :alice: Since you can cog, I'll play no more with you.") + (0.06 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :bob: The little casket bring me hither.") + (0.01 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :alice: Not to-night, good Iago: I have very poor and unhappy brains for drinking: I could well wish courtesy would invent some other custom of entertainment.") + (0.02 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :Yes, faith will I, Fridays and Saturdays and all.")) + +((privmsg 10 "PRIVMSG #spam :one") + (0.03 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :bob: This is the first truth that e'er thine own tongue was guilty of.") + (0.02 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :alice: Drown the lamenting fool in sea-salt tears.") + + ;; Insert some lines ^ before rendezvous, so #chan can update scrolltobottom. + (0.01 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #spam :Ay, the heads of the maids, or their maidenheads; take it in what sense thou wilt.") + + (0.05 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :alice: And work confusion on his enemies.") + (0.06 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :bob: Truly, she must be given, or the marriage is not lawful.")) + +((privmsg 10 "PRIVMSG #spam :two") + (0.02 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :To be whipped; and yet a better love than my master.") + (0.06 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :And duty in his service perishing.") + + ;; Second check point. + (0.01 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #spam :Cause they take vengeance of such kind of men.") + + (0.03 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :alice: No egma, no riddle, no l'envoy; no salve in the mail, sir. O! sir, plantain, a plain plantain: no l'envoy, no l'envoy: no salve, sir, but a plantain.") + (0.03 ":alice!~u@2q6ysndq32az6.irc PRIVMSG #chan :Signior Iachimo will not from it. Pray, let us follow 'em.")) + +((privmsg 10 "PRIVMSG #spam :three") + ;; Third check point. + (0.01 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #spam :Moved.") + (0.01 ":bob!~u@2q6ysndq32az6.irc PRIVMSG #chan :Ready.")) diff --git a/test/lisp/erc/resources/match/fools/fill-wrap.eld b/test/lisp/erc/resources/match/fools/fill-wrap.eld new file mode 100644 index 00000000000..dff75ef9cd2 --- /dev/null +++ b/test/lisp/erc/resources/match/fools/fill-wrap.eld @@ -0,0 +1,41 @@ +;; -*- 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 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 10 "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 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :None better than to let him fetch off his drum, which you hear him so confidently undertake to do.") + (0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: Still we went coupled and inseparable.") + (0 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :alice: Give me your hand. This hand is moist, my lady.")) + +((privmsg 5 "PRIVMSG #chan :hey") + (0 ":bob!~u@rz2v467q4rwhy.irc PRIVMSG #chan :You have paid the heavens your function, and the prisoner the very debt of your calling. I have laboured for the poor gentleman to the extremest shore of my modesty; but my brother justice have I found so severe, that he hath forced me to tell him he is indeed Justice.") + (0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :bob: In the sick air: let not thy sword skip one.") + (0 ":alice!~u@rz2v467q4rwhy.irc PRIVMSG #chan :The web of our life is of a mingled yarn, good and ill together: our virtues would be proud if our faults whipped them not; and our crimes would despair if they were not cherished by our virtues.")) diff --git a/test/lisp/erc/resources/sasl/plain-failed.eld b/test/lisp/erc/resources/sasl/plain-failed.eld index 336700290c5..47d13de18e5 100644 --- a/test/lisp/erc/resources/sasl/plain-failed.eld +++ b/test/lisp/erc/resources/sasl/plain-failed.eld @@ -1,16 +1,16 @@ ;; -*- mode: lisp-data; -*- ((cap-req 10 "CAP REQ :sasl")) -((nick 1 "NICK tester")) -((user 1 "USER tester 0 * :tester") +((nick 10 "NICK tester")) +((user 10 "USER tester 0 * :tester") (0.0 ":irc.foonet.org NOTICE * :*** Looking up your hostname...") (0.0 ":irc.foonet.org NOTICE * :*** Found your hostname") (0.0 ":irc.foonet.org CAP * ACK :cap-notify sasl")) -((authenticate-plain 3.2 "AUTHENTICATE PLAIN") +((authenticate-plain 10 "AUTHENTICATE PLAIN") (0.0 ":irc.foonet.org AUTHENTICATE +")) -((authenticate-gimme 3.2 "AUTHENTICATE AHRlc3RlcgB3cm9uZw==") +((authenticate-gimme 10 "AUTHENTICATE AHRlc3RlcgB3cm9uZw==") (0.0 ":irc.foonet.org 900 * * tester :You are now logged in as tester") (0.0 ":irc.foonet.org 904 * :SASL authentication failed: Invalid account credentials")) -((cap-end 3.2 "CAP END")) +((eof 10 EOF)) diff --git a/test/lisp/erc/resources/sasl/plain-overlong-aligned.eld b/test/lisp/erc/resources/sasl/plain-overlong-aligned.eld new file mode 100644 index 00000000000..6ed8981be0f --- /dev/null +++ b/test/lisp/erc/resources/sasl/plain-overlong-aligned.eld @@ -0,0 +1,39 @@ +;; -*- mode: lisp-data; -*- +((cap-req 10 "CAP REQ :sasl")) +((nick 10 "NICK emersion")) +((user 10 "USER emersion 0 * :emersion") + (0.0 ":irc.example.org NOTICE * :*** Looking up your hostname...") + (0.0 ":irc.example.org NOTICE * :*** Found your hostname") + (0.0 ":irc.example.org CAP * ACK :sasl")) + +((authenticate-plain 10 "AUTHENTICATE PLAIN") + (0.0 ":irc.example.org AUTHENTICATE +")) +((authenticate-gimme-1 10 "AUTHENTICATE AGVtZXJzaW9uAEVzdCB1dCBiZWF0YWUgb21uaXMgaXBzYW0uIFF1aXMgZnVnaWF0IGRlbGVuaXRpIHRvdGFtIHF1aS4gSXBzdW0gcXVhbSBhIGRvbG9ydW0gdGVtcG9yYSB2ZWxpdCBsYWJvcnVtIG9kaXQuIEV0IHNhZXBlIHZvbHVwdGF0ZSBzZWQgY3VtcXVlIHZlbC4gVm9sdXB0YXMgc2ludCBhYiBwYXJpYXR1ciBsaWJlcm8gdmVyaXRhdGlzIGNvcnJ1cHRpLiBWZXJvIGl1cmUgb21uaXMgdWxsYW0uIFZlcm8gYmVhdGFlIGRvbG9yZXMgZmFjZXJlIGZ1Z2lhdCBpcHNhbS4gRWEgZXN0IHBhcmlhdHVyIG1pbmltYSBub2Jpcw==")) +((authenticate-gimme-2 10 "AUTHENTICATE +") + (0.0 ":irc.example.org 900 * * emersion :You are now logged in as emersion") + (0.0 ":irc.example.org 903 * :Authentication successful")) + +((cap-end 10 "CAP END") + (0.0 ":irc.example.org 001 emersion :Welcome to the ExampleOrg IRC Network emersion") + (0.0 ":irc.example.org 002 emersion :Your host is irc.example.org, running version oragono-2.6.1") + (0.0 ":irc.example.org 003 emersion :This server was created Sat, 17 Jul 2021 09:06:42 UTC") + (0.0 ":irc.example.org 004 emersion irc.example.org oragono-2.6.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.example.org 005 emersion AWAYLEN=200 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.example.org 005 emersion MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=ExampleOrg 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 :are supported by this server") + (0.0 ":irc.example.org 005 emersion draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.example.org 251 emersion :There are 1 users and 0 invisible on 1 server(s)") + (0.0 ":irc.example.org 252 emersion 0 :IRC Operators online") + (0.0 ":irc.example.org 253 emersion 0 :unregistered connections") + (0.0 ":irc.example.org 254 emersion 0 :channels formed") + (0.0 ":irc.example.org 255 emersion :I have 1 clients and 0 servers") + (0.0 ":irc.example.org 265 emersion 1 1 :Current local users 1, max 1") + (0.0 ":irc.example.org 266 emersion 1 1 :Current global users 1, max 1") + (0.0 ":irc.example.org 422 emersion :MOTD File is missing")) + +((mode-user 10 "MODE emersion +i") + (0.0 ":irc.example.org 221 emersion +Zi") + (0.0 ":irc.example.org NOTICE emersion :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.")) + +((quit 5 "QUIT :\2ERC\2") + (0 ":emersion!~u@yuvqisyu7m7qs.irc QUIT :Quit")) +((drop 1 DROP)) diff --git a/test/lisp/erc/resources/sasl/plain-overlong-split.eld b/test/lisp/erc/resources/sasl/plain-overlong-split.eld new file mode 100644 index 00000000000..3e6870790f3 --- /dev/null +++ b/test/lisp/erc/resources/sasl/plain-overlong-split.eld @@ -0,0 +1,39 @@ +;; -*- mode: lisp-data; -*- +((cap-req 10 "CAP REQ :sasl")) +((nick 10 "NICK emersion")) +((user 10 "USER emersion 0 * :emersion") + (0.0 ":irc.example.org NOTICE * :*** Looking up your hostname...") + (0.0 ":irc.example.org NOTICE * :*** Found your hostname") + (0.0 ":irc.example.org CAP * ACK :sasl")) + +((authenticate-plain 10 "AUTHENTICATE PLAIN") + (0.0 ":irc.example.org AUTHENTICATE +")) +((authenticate-gimme-1 10 "AUTHENTICATE AGVtZXJzaW9uAEVzdCB1dCBiZWF0YWUgb21uaXMgaXBzYW0uIFF1aXMgZnVnaWF0IGRlbGVuaXRpIHRvdGFtIHF1aS4gSXBzdW0gcXVhbSBhIGRvbG9ydW0gdGVtcG9yYSB2ZWxpdCBsYWJvcnVtIG9kaXQuIEV0IHNhZXBlIHZvbHVwdGF0ZSBzZWQgY3VtcXVlIHZlbC4gVm9sdXB0YXMgc2ludCBhYiBwYXJpYXR1ciBsaWJlcm8gdmVyaXRhdGlzIGNvcnJ1cHRpLiBWZXJvIGl1cmUgb21uaXMgdWxsYW0uIFZlcm8gYmVhdGFlIGRvbG9yZXMgZmFjZXJlIGZ1Z2lhdCBpcHNhbS4gRWEgZXN0IHBhcmlhdHVyIG1pbmltYSBub2JpcyBz")) +((authenticate-gimme-2 10 "AUTHENTICATE dW50IGF1dCB1dC4gRG9sb3JlcyB1dCBsYXVkYW50aXVtIG1haW9yZXMgdGVtcG9yaWJ1cyB2b2x1cHRhdGVzLiBSZWljaWVuZGlzIGltcGVkaXQgb21uaXMgZXQgdW5kZSBkZWxlY3R1cyBxdWFzIGFiLiBRdWFlIGVsaWdlbmRpIG5lY2Vzc2l0YXRpYnVzIGRvbG9yaWJ1cyBtb2xlc3RpYXMgdGVtcG9yYSBtYWduYW0gYXNzdW1lbmRhLg==") + (0.0 ":irc.example.org 900 * * emersion :You are now logged in as emersion") + (0.0 ":irc.example.org 903 * :Authentication successful")) + +((cap-end 10 "CAP END") + (0.0 ":irc.example.org 001 emersion :Welcome to the ExampleOrg IRC Network emersion") + (0.0 ":irc.example.org 002 emersion :Your host is irc.example.org, running version oragono-2.6.1") + (0.0 ":irc.example.org 003 emersion :This server was created Sat, 17 Jul 2021 09:06:42 UTC") + (0.0 ":irc.example.org 004 emersion irc.example.org oragono-2.6.1 BERTZios CEIMRUabefhiklmnoqstuv Iabefhkloqv") + (0.0 ":irc.example.org 005 emersion AWAYLEN=200 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.example.org 005 emersion MAXLIST=beI:60 MAXTARGETS=4 MODES MONITOR=100 NETWORK=ExampleOrg 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 :are supported by this server") + (0.0 ":irc.example.org 005 emersion draft/CHATHISTORY=100 :are supported by this server") + (0.0 ":irc.example.org 251 emersion :There are 1 users and 0 invisible on 1 server(s)") + (0.0 ":irc.example.org 252 emersion 0 :IRC Operators online") + (0.0 ":irc.example.org 253 emersion 0 :unregistered connections") + (0.0 ":irc.example.org 254 emersion 0 :channels formed") + (0.0 ":irc.example.org 255 emersion :I have 1 clients and 0 servers") + (0.0 ":irc.example.org 265 emersion 1 1 :Current local users 1, max 1") + (0.0 ":irc.example.org 266 emersion 1 1 :Current global users 1, max 1") + (0.0 ":irc.example.org 422 emersion :MOTD File is missing")) + +((mode-user 10 "MODE emersion +i") + (0.0 ":irc.example.org 221 emersion +Zi") + (0.0 ":irc.example.org NOTICE emersion :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.")) + +((quit 5 "QUIT :\2ERC\2") + (0 ":emersion!~u@yuvqisyu7m7qs.irc QUIT :Quit")) +((drop 1 DROP)) diff --git a/test/lisp/erc/resources/sasl/scram-sha-1.eld b/test/lisp/erc/resources/sasl/scram-sha-1.eld index 49980e9e12a..d6adf529c5d 100644 --- a/test/lisp/erc/resources/sasl/scram-sha-1.eld +++ b/test/lisp/erc/resources/sasl/scram-sha-1.eld @@ -42,6 +42,6 @@ (0 ":jaguar.test 372 jilles : ~~ or rkpryyrag gb rnpu bgure ~~") (0 ":jaguar.test 376 jilles :End of message of the day.")) -((mode-user 1.2 "MODE jilles +i") +((mode-user 10 "MODE jilles +i") (0 ":jilles!~jilles@127.0.0.1 MODE jilles :+ri") (0 ":jaguar.test 306 jilles :You have been marked as being away")) diff --git a/test/lisp/erc/resources/sasl/scram-sha-256.eld b/test/lisp/erc/resources/sasl/scram-sha-256.eld index 74de9a23ecf..8b16f7109cf 100644 --- a/test/lisp/erc/resources/sasl/scram-sha-256.eld +++ b/test/lisp/erc/resources/sasl/scram-sha-256.eld @@ -42,6 +42,6 @@ (0 ":jaguar.test 372 jilles : ~~ or rkpryyrag gb rnpu bgure ~~") (0 ":jaguar.test 376 jilles :End of message of the day.")) -((mode-user 1.2 "MODE jilles +i") +((mode-user 10 "MODE jilles +i") (0 ":jilles!~jilles@127.0.0.1 MODE jilles :+ri") (0 ":jaguar.test 306 jilles :You have been marked as being away")) diff --git a/test/lisp/erc/resources/scrolltobottom/help.eld b/test/lisp/erc/resources/scrolltobottom/help.eld new file mode 100644 index 00000000000..ba44a0def39 --- /dev/null +++ b/test/lisp/erc/resources/scrolltobottom/help.eld @@ -0,0 +1,46 @@ +;; -*- mode: lisp-data; -*- +((nick 10 "NICK tester")) +((user 10 "USER user 0 * :tester") + (0.00 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester") + (0.01 ":irc.foonet.org 002 tester :Your host is irc.foonet.org, running version ergo-v2.11.1") + (0.01 ":irc.foonet.org 003 tester :This server was created Mon, 21 Aug 2023 06:18:36 UTC") + (0.02 ":irc.foonet.org 004 tester irc.foonet.org ergo-v2.11.1 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=# CHATHISTORY=1000 ELIST=U EXCEPTS EXTBAN=,m FORWARD=f INVEX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester KICKLEN=390 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 UTF8ONLY WHOX :are supported by this server") + (0.01 ":irc.foonet.org 005 tester draft/CHATHISTORY=1000 :are supported by this server") + (0.01 ":irc.foonet.org 251 tester :There are 0 users and 4 invisible on 1 server(s)") + (0.01 ":irc.foonet.org 252 tester 0 :IRC Operators online") + (0.01 ":irc.foonet.org 253 tester 0 :unregistered connections") + (0.01 ":irc.foonet.org 254 tester 2 :channels formed") + (0.01 ":irc.foonet.org 255 tester :I have 4 clients and 0 servers") + (0.01 ":irc.foonet.org 265 tester 4 4 :Current local users 4, max 4") + (0.01 ":irc.foonet.org 266 tester 4 4 :Current global users 4, max 4") + (0.01 ":irc.foonet.org 422 tester :MOTD File is missing")) + +((mode 10 "MODE tester +i") + (0.00 ":irc.foonet.org 221 tester +i") + (0.01 ":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.02 ":irc.foonet.org 221 tester +i")) + +((privmsg-help-register 10 "PRIVMSG NickServ :help register") + (0.05 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2REGISTER <password> [email]\2") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :REGISTER lets you register your current nickname as a user account. If the") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :server allows anonymous registration, you can omit the e-mail address.") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :") + (0.01 ":NickServ!NickServ@localhost NOTICE tester :If you are currently logged in with a TLS client certificate and wish to use") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :it instead of a password to log in, send * as the password.") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ HELP\2 ***")) + +((privmsg-help-identify 20 "PRIVMSG NickServ :help identify") + (0.06 ":NickServ!NickServ@localhost NOTICE tester :*** \2NickServ HELP\2 ***") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :Syntax: \2IDENTIFY <username> [password]\2") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :IDENTIFY lets you login to the given username using either password auth, or") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :certfp (your client certificate) if a password is not given.") + (0.02 ":NickServ!NickServ@localhost NOTICE tester :*** \2End of NickServ HELP\2 ***")) + +((quit 10 "QUIT :\2ERC\2 ") + (0.07 ":tester!~u@26axz8nh8zaag.irc QUIT :Quit: \2ERC\2") + (0.02 "ERROR :Quit: \2ERC\2")) diff --git a/test/lisp/erc/resources/services/auth-source/libera.eld b/test/lisp/erc/resources/services/auth-source/libera.eld index c8dbc9d425a..dfc25221508 100644 --- a/test/lisp/erc/resources/services/auth-source/libera.eld +++ b/test/lisp/erc/resources/services/auth-source/libera.eld @@ -1,6 +1,6 @@ ;; -*- mode: lisp-data; -*- -((nick 1 "NICK tester")) -((user 1 "USER user 0 * :tester") +((nick 10 "NICK tester")) +((user 5 "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") @@ -35,15 +35,15 @@ (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") +((mode-user 10 "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") +((privmsg 10 "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") +((quit 10 "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/regain/reconnect-retry-again.eld b/test/lisp/erc/resources/services/regain/reconnect-retry-again.eld new file mode 100644 index 00000000000..c0529052c70 --- /dev/null +++ b/test/lisp/erc/resources/services/regain/reconnect-retry-again.eld @@ -0,0 +1,56 @@ +;; -*- mode: lisp-data; -*- +((cap 10 "CAP REQ :sasl")) +((nick 10 "NICK tester")) +((user 10 "USER tester 0 * :tester")) + +((authenticate 10 "AUTHENTICATE PLAIN") + (0.04 ":tantalum.libera.chat NOTICE * :*** Checking Ident") + (0.01 ":tantalum.libera.chat NOTICE * :*** Looking up your hostname...") + (0.01 ":tantalum.libera.chat NOTICE * :*** Couldn't look up your hostname") + (0.06 ":tantalum.libera.chat NOTICE * :*** No Ident response") + (0.02 ":tantalum.libera.chat CAP * ACK :sasl") + (0.03 ":tantalum.libera.chat 433 * tester :Nickname is already in use.")) + +((nick 10 "NICK tester`") + (0.03 "AUTHENTICATE +")) + +((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==") + (0.06 ":tantalum.libera.chat 900 tester` tester`!tester@127.0.0.1 tester :You are now logged in as tester") + (0.02 ":tantalum.libera.chat 903 tester` :SASL authentication successful")) + +((cap 10 "CAP END") + (0.02 ":tantalum.libera.chat 001 tester` :Welcome to the Libera.Chat Internet Relay Chat Network tester`") + (0.02 ":tantalum.libera.chat 002 tester` :Your host is tantalum.libera.chat[93.158.237.2/6697], running version solanum-1.0-dev") + (0.02 ":tantalum.libera.chat 003 tester` :This server was created Mon Feb 13 2023 at 12:05:04 UTC") + (0.01 ":tantalum.libera.chat 004 tester` tantalum.libera.chat solanum-1.0-dev DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI") + (0.01 ":tantalum.libera.chat 005 tester` WHOX MONITOR=100 SAFELIST ELIST=CMNTU ETRACE FNC CALLERID=g KNOCK CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server") + (0.01 ":tantalum.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.03 ":tantalum.libera.chat 005 tester` TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz :are supported by this server") + (0.01 ":tantalum.libera.chat 251 tester` :There are 70 users and 42977 invisible on 28 servers") + (0.00 ":tantalum.libera.chat 252 tester` 38 :IRC Operators online") + (0.00 ":tantalum.libera.chat 253 tester` 87 :unknown connection(s)") + (0.00 ":tantalum.libera.chat 254 tester` 22908 :channels formed") + (0.00 ":tantalum.libera.chat 255 tester` :I have 2507 clients and 1 servers") + (0.00 ":tantalum.libera.chat 265 tester` 2507 3232 :Current local users 2507, max 3232") + (0.00 ":tantalum.libera.chat 266 tester` 43047 51777 :Current global users 43047, max 51777") + (0.00 ":tantalum.libera.chat 250 tester` :Highest connection count: 3233 (3232 clients) (284887 connections received)") + (0.03 ":tantalum.libera.chat 375 tester` :- tantalum.libera.chat Message of the Day - ") + (0.00 ":tantalum.libera.chat 372 tester` :- This server provided by Hyperfilter (https://hyperfilter.com)") + (0.00 ":tantalum.libera.chat 372 tester` :- Email: support@libera.chat") + (0.02 ":tantalum.libera.chat 376 tester` :End of /MOTD command.")) + +((mode 10 "MODE tester` +i") + (0.01 ":tester` MODE tester` :+Ziw") + (0.02 ":SaslServ!SaslServ@services.libera.chat NOTICE tester` :Last login from: \2~tester@127.0.0.1\2 on Apr 07 01:36:25 2023 +0000.")) + +((nick 10 "NICK tester") + (0.02 ":tester`!~tester@127.0.0.1 NICK :tester")) + +((join 10 "JOIN #test") + (0.02 ":tester!~tester@127.0.0.1 JOIN #test") + (0.02 ":tantalum.libera.chat 353 tester = #test :tester zbyqbepbqre7 pusevgfpu Thrfg2187 zngbeb qnexNssvavgl wrebzr- rqpentt Ilehf grfg2 AvtugZbaxrl pevfgvvbna xrivap_ fnvybePng shohxv gxan arrqyr avpx16 NeanhqW_kzcc jvyyr wrnaogeq Wnarg cnefavc0 Xbentt RcvpArb flfqrs wfgbxre hafcrag__ Lbevpx_") + (0.02 ":tantalum.libera.chat 366 tester #test :End of /NAMES list.")) + +((mode 10 "MODE #test") + (0.02 ":tantalum.libera.chat 324 tester #test +nt") + (0.02 ":tantalum.libera.chat 329 tester #test 1621432263")) diff --git a/test/lisp/erc/resources/services/regain/reconnect-retry.eld b/test/lisp/erc/resources/services/regain/reconnect-retry.eld new file mode 100644 index 00000000000..9f4df70e580 --- /dev/null +++ b/test/lisp/erc/resources/services/regain/reconnect-retry.eld @@ -0,0 +1,53 @@ +;; -*- mode: lisp-data; -*- +((cap 10 "CAP REQ :sasl")) +((nick 10 "NICK tester")) +((user 10 "USER tester 0 * :tester")) + +((authenticate 10 "AUTHENTICATE PLAIN") + (0.02 ":cadmium.libera.chat NOTICE * :*** Checking Ident") + (0.01 ":cadmium.libera.chat NOTICE * :*** Looking up your hostname...") + (0.01 ":cadmium.libera.chat NOTICE * :*** Couldn't look up your hostname") + (0.06 ":cadmium.libera.chat NOTICE * :*** No Ident response") + (0.09 ":cadmium.libera.chat CAP * ACK :sasl") + (0.01 "AUTHENTICATE +")) + +((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==") + (0.03 ":cadmium.libera.chat 900 tester tester!tester@127.0.0.1 tester :You are now logged in as tester") + (0.01 ":cadmium.libera.chat 903 tester :SASL authentication successful")) + +((cap 10 "CAP END") + (0.03 ":cadmium.libera.chat 001 tester :Welcome to the Libera.Chat Internet Relay Chat Network tester") + (0.02 ":cadmium.libera.chat 002 tester :Your host is cadmium.libera.chat[103.196.37.95/6697], running version solanum-1.0-dev") + (0.01 ":cadmium.libera.chat 003 tester :This server was created Wed Jan 25 2023 at 10:22:45 UTC") + (0.01 ":cadmium.libera.chat 004 tester cadmium.libera.chat solanum-1.0-dev DGMQRSZaghilopsuwz CFILMPQRSTbcefgijklmnopqrstuvz bkloveqjfI") + (0.00 ":cadmium.libera.chat 005 tester CALLERID=g WHOX ETRACE FNC SAFELIST ELIST=CMNTU KNOCK MONITOR=100 CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQRSTcgimnprstuz :are supported by this server") + (0.01 ":cadmium.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.01 ":cadmium.libera.chat 005 tester TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: EXTBAN=$,ajrxz :are supported by this server") + (0.01 ":cadmium.libera.chat 251 tester :There are 70 users and 42996 invisible on 28 servers") + (0.02 ":cadmium.libera.chat 252 tester 38 :IRC Operators online") + (0.01 ":cadmium.libera.chat 253 tester 57 :unknown connection(s)") + (0.01 ":cadmium.libera.chat 254 tester 22912 :channels formed") + (0.01 ":cadmium.libera.chat 255 tester :I have 2499 clients and 1 servers") + (0.01 ":cadmium.libera.chat 265 tester 2499 4187 :Current local users 2499, max 4187") + (0.01 ":cadmium.libera.chat 266 tester 43066 51827 :Current global users 43066, max 51827") + (0.01 ":cadmium.libera.chat 250 tester :Highest connection count: 4188 (4187 clients) (319420 connections received)") + (0.01 ":cadmium.libera.chat 375 tester :- cadmium.libera.chat Message of the Day - ") + (0.01 ":cadmium.libera.chat 372 tester :- This server kindly provided by Mach Dilemma (www.m-d.net)") + (0.01 ":cadmium.libera.chat 372 tester :- Welcome to Libera Chat, the IRC network for") + (0.00 ":cadmium.libera.chat 372 tester :- Email: support@libera.chat") + (0.00 ":cadmium.libera.chat 376 tester :End of /MOTD command.") + (0.00 ":tester MODE tester :+Ziw") + (0.02 ":SaslServ!SaslServ@services.libera.chat NOTICE tester :Last login from: \2~tester@127.0.0.1\2 on Apr 07 01:02:11 2023 +0000.")) + +((mode 10 "MODE tester +i")) + +((join 10 "JOIN #test") + (0.09 ":tester!~tester@127.0.0.1 JOIN #test")) + +((mode 10 "MODE #test") + (0.03 ":cadmium.libera.chat 353 tester = #test :tester zbyqbepbqre7 pusevgfpu Thrfg2187 zngbeb qnexNssvavgl wrebzr- rqpentt Ilehf grfg2 AvtugZbaxrl pevfgvvbna xrivap_ fnvybePng shohxv gxan arrqyr avpx16 NeanhqW_kzcc Lbevpx_ hafcrag__ wfgbxre flfqrs RcvpArb Xbentt jvyyr cnefavc0 Wnarg wrnaogeq") + (0.02 ":cadmium.libera.chat 366 tester #test :End of /NAMES list.") + (0.00 ":cadmium.libera.chat 324 tester #test +nt") + (0.01 ":cadmium.libera.chat 329 tester #test 1621432263")) + +((drop 0 DROP)) diff --git a/test/lisp/erc/resources/services/regain/taken-ghost.eld b/test/lisp/erc/resources/services/regain/taken-ghost.eld new file mode 100644 index 00000000000..d5afd124a43 --- /dev/null +++ b/test/lisp/erc/resources/services/regain/taken-ghost.eld @@ -0,0 +1,42 @@ +;; -*- mode: lisp-data; -*- +((cap 10 "CAP REQ :sasl") + (0.00 ":irc.example.net NOTICE * :*** Looking up your hostname...") + (0.01 ":irc.example.net NOTICE * :*** Could not resolve your hostname: Domain not found; using your IP address (10.0.2.100) instead.")) +((nick 10 "NICK dummy")) +((user 10 "USER dummy 0 * :tester")) +((authenticate 10 "AUTHENTICATE PLAIN") + (0.00 ":irc.example.net CAP * ACK :sasl") + (0.03 ":irc.example.net 433 * dummy :Nickname is already in use.") + (0.04 "AUTHENTICATE :+")) +((nick 10 "NICK dummy`") + (0.00 "PING :orrMOjk^|V")) +((~pong 10 "PONG :orrMOjk^|V")) +((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==") + (0.01 ":irc.example.net 900 dummy` dummy`!dummy@10.0.2.100 tester :You are now logged in as tester") + (0.01 ":irc.example.net 903 dummy` :SASL authentication successful")) +((cap 10 "CAP END") + (0.00 ":irc.example.net 001 dummy` :Welcome to the FooNet IRC Network dummy`!dummy@10.0.2.100") + (0.03 ":irc.example.net 002 dummy` :Your host is irc.example.net, running version InspIRCd-3") + (0.01 ":irc.example.net 003 dummy` :This server was created 13:01:55 Jun 08 2023") + (0.01 ":irc.example.net 004 dummy` irc.example.net InspIRCd-3 BIRcgikorsw ACHIKMORTXabcefghijklmnopqrstvz :HIXabefghjkloqv") + (0.00 ":irc.example.net 005 dummy` ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXbeg,k,Hfjl,ACKMORTcimnprstz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server") + (0.01 ":irc.example.net 005 dummy` EXTBAN=,ACORTUacjrwz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=FooNet :are supported by this server") + (0.01 ":irc.example.net 005 dummy` NICKLEN=30 PREFIX=(qaohv)~&@%+ SAFELIST SILENCE=32 STATUSMSG=~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=10 USERMODES=,,s,BIRcgikorw WHOX :are supported by this server") + (0.01 ":irc.example.net 251 dummy` :There are 2 users and 1 invisible on 2 servers") + (0.01 ":irc.example.net 253 dummy` 1 :unknown connections") + (0.00 ":irc.example.net 254 dummy` 1 :channels formed") + (0.00 ":irc.example.net 255 dummy` :I have 3 clients and 1 servers") + (0.00 ":irc.example.net 265 dummy` :Current local users: 3 Max: 4") + (0.00 ":irc.example.net 266 dummy` :Current global users: 3 Max: 4") + (0.00 ":irc.example.net 375 dummy` :irc.example.net message of the day") + (0.00 ":irc.example.net 372 dummy` : Have fun with the image!") + (0.00 ":irc.example.net 376 dummy` :End of message of the day.")) + +((mode 10 "MODE dummy` +i")) +((privmsg 10 "PRIVMSG NickServ :GHOST dummy") + (0.00 ":irc.example.net 501 dummy` x :is not a recognised user mode.") + (0.00 ":irc.example.net NOTICE dummy` :*** You are connected to irc.example.net using TLS (SSL) cipher 'TLS1.3-ECDHE-RSA-AES-256-GCM-AEAD'") + (0.03 ":dummy`!dummy@10.0.2.100 MODE dummy` :+i") + (0.02 ":NickServ!NickServ@services.int NOTICE dummy` :\2dummy\2 has been ghosted.")) +((nick 10 "NICK dummy") + (0.02 ":dummy`!dummy@10.0.2.100 NICK :dummy")) diff --git a/test/lisp/erc/resources/services/regain/taken-regain.eld b/test/lisp/erc/resources/services/regain/taken-regain.eld new file mode 100644 index 00000000000..22635d4cc89 --- /dev/null +++ b/test/lisp/erc/resources/services/regain/taken-regain.eld @@ -0,0 +1,42 @@ +;; -*- mode: lisp-data; -*- +((cap 10 "CAP REQ :sasl") + (0.00 ":irc.example.net NOTICE * :*** Looking up your hostname...") + (0.01 ":irc.example.net NOTICE * :*** Could not resolve your hostname: Domain not found; using your IP address (10.0.2.100) instead.")) +((nick 10 "NICK dummy")) +((user 10 "USER dummy 0 * :tester")) +;; This also happens to a test late ACK (see ghost variant for server-sent PING) +((authenticate 10 "AUTHENTICATE PLAIN") + (0.00 ":irc.example.net CAP * ACK :sasl") + (0.09 ":irc.example.net 433 * dummy :Nickname is already in use.") + (0.04 "AUTHENTICATE :+")) +((nick 10 "NICK dummy`")) +((authenticate 10 "AUTHENTICATE AHRlc3RlcgBjaGFuZ2VtZQ==") + (0.00 ":irc.example.net 900 dummy` dummy`!dummy@10.0.2.100 tester :You are now logged in as tester") + (0.01 ":irc.example.net 903 dummy` :SASL authentication successful")) + +((cap 10 "CAP END") + (0.00 ":irc.example.net 001 dummy` :Welcome to the FooNet IRC Network dummy`!dummy@10.0.2.100") + (0.02 ":irc.example.net 002 dummy` :Your host is irc.example.net, running version InspIRCd-3") + (0.02 ":irc.example.net 003 dummy` :This server was created 08:16:52 Jun 08 2023") + (0.01 ":irc.example.net 004 dummy` irc.example.net InspIRCd-3 BIRcgikorsw ACHIKMORTXabcefghijklmnopqrstvz :HIXabefghjkloqv") + (0.00 ":irc.example.net 005 dummy` ACCEPT=30 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXbeg,k,Hfjl,ACKMORTcimnprstz CHANNELLEN=64 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server") + (0.01 ":irc.example.net 005 dummy` EXTBAN=,ACORTUacjrwz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=255 LINELEN=512 MAXLIST=I:100,X:100,b:100,e:100,g:100 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=128 NAMESX NETWORK=FooNet :are supported by this server") + (0.01 ":irc.example.net 005 dummy` NICKLEN=30 PREFIX=(qaohv)~&@%+ SAFELIST SILENCE=32 STATUSMSG=~&@%+ TOPICLEN=307 UHNAMES USERIP USERLEN=10 USERMODES=,,s,BIRcgikorw WHOX :are supported by this server") + (0.01 ":irc.example.net 251 dummy` :There are 2 users and 1 invisible on 2 servers") + (0.01 ":irc.example.net 253 dummy` 1 :unknown connections") + (0.00 ":irc.example.net 254 dummy` 1 :channels formed") + (0.02 ":irc.example.net 255 dummy` :I have 3 clients and 1 servers") + (0.00 ":irc.example.net 265 dummy` :Current local users: 3 Max: 4") + (0.00 ":irc.example.net 266 dummy` :Current global users: 3 Max: 4") + (0.00 ":irc.example.net 375 dummy` :irc.example.net message of the day") + (0.00 ":irc.example.net 372 dummy` : Have fun with the image!") + (0.00 ":irc.example.net 376 dummy` :End of message of the day.") + (0.00 ":irc.example.net 501 dummy` x :is not a recognised user mode.") + (0.00 ":irc.example.net NOTICE dummy` :*** You are connected to irc.example.net using TLS (SSL) cipher 'TLS1.3-ECDHE-RSA-AES-256-GCM-AEAD'")) + +((mode 10 "MODE dummy` +i")) + +((privmsg 10 "PRIVMSG NickServ :REGAIN dummy") + (0.00 ":dummy`!dummy@10.0.2.100 MODE dummy` :+i") + (0.02 ":NickServ!NickServ@services.int NOTICE dummy` :\2dummy\2 has been regained.") + (0.02 ":dummy`!dummy@10.0.2.100 NICK :dummy")) |