;;; erc-tests.el --- Tests for erc. -*- lexical-binding:t -*- ;; Copyright (C) 2020-2021 Free Software Foundation, Inc. ;; Author: Lars Ingebrigtsen ;; 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 . ;;; Code: (require 'ert) (require 'erc) (require 'erc-ring) (require 'erc-networks) (ert-deftest erc--read-time-period () (cl-letf (((symbol-function 'read-string) (lambda (&rest _) ""))) (should (equal (erc--read-time-period "foo: ") nil))) (cl-letf (((symbol-function 'read-string) (lambda (&rest _) " "))) (should (equal (erc--read-time-period "foo: ") nil))) (cl-letf (((symbol-function 'read-string) (lambda (&rest _) " 432 "))) (should (equal (erc--read-time-period "foo: ") 432))) (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "432"))) (should (equal (erc--read-time-period "foo: ") 432))) (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1h"))) (should (equal (erc--read-time-period "foo: ") 3600))) (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1h10s"))) (should (equal (erc--read-time-period "foo: ") 3610))) (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "1d"))) (should (equal (erc--read-time-period "foo: ") 86400)))) (ert-deftest erc-with-all-buffers-of-server () (let (proc-exnet proc-onet erc-kill-channel-hook erc-kill-server-hook erc-kill-buffer-hook) (with-current-buffer (get-buffer-create "OtherNet") (erc-mode) (setq proc-onet (start-process "sleep" (current-buffer) "sleep" "1") erc-server-process proc-onet erc-network 'OtherNet) (set-process-query-on-exit-flag erc-server-process nil)) (with-current-buffer (get-buffer-create "ExampleNet") (erc-mode) (setq proc-exnet (start-process "sleep" (current-buffer) "sleep" "1") erc-server-process proc-exnet erc-network 'ExampleNet) (set-process-query-on-exit-flag erc-server-process nil)) (with-current-buffer (get-buffer-create "#foo") (erc-mode) (setq erc-server-process proc-exnet) (setq erc-default-recipients '("#foo"))) (with-current-buffer (get-buffer-create "#spam") (erc-mode) (setq erc-server-process proc-onet) (setq erc-default-recipients '("#spam"))) (with-current-buffer (get-buffer-create "#bar") (erc-mode) (setq erc-server-process proc-onet) (setq erc-default-recipients '("#bar"))) (with-current-buffer (get-buffer-create "#baz") (erc-mode) (setq erc-server-process proc-exnet) (setq erc-default-recipients '("#baz"))) (should (eq (get-buffer-process "ExampleNet") proc-exnet)) (erc-with-all-buffers-of-server (get-buffer-process "ExampleNet") nil (kill-buffer)) (should-not (get-buffer "ExampleNet")) (should-not (get-buffer "#foo")) (should-not (get-buffer "#baz")) (should (get-buffer "OtherNet")) (should (get-buffer "#bar")) (should (get-buffer "#spam")) (let* ((test (lambda () (not (string= (buffer-name) "#spam")))) (calls 0) (get-test (lambda () (cl-incf calls) test))) (erc-with-all-buffers-of-server proc-onet (funcall get-test) (kill-buffer)) (should (= calls 1))) (should-not (get-buffer "OtherNet")) (should-not (get-buffer "#bar")) (should (get-buffer "#spam")) (kill-buffer "#spam"))) (ert-deftest erc-lurker-maybe-trim () (let (erc-lurker-trim-nicks (erc-lurker-ignore-chars "_`")) (should (string= "nick`" (erc-lurker-maybe-trim "nick`"))) (setq erc-lurker-trim-nicks t) (should (string= "nick" (erc-lurker-maybe-trim "nick`"))) (should (string= "ni`_ck" (erc-lurker-maybe-trim "ni`_ck__``"))) (setq erc-lurker-ignore-chars "_-`") ; set of chars, not character alts (should (string= "nick" (erc-lurker-maybe-trim "nick-_`"))))) (ert-deftest erc-parse-isupport-value () (should (equal (erc-parse-isupport-value "a,b") '("a" "b"))) (should (equal (erc-parse-isupport-value "a,b,c") '("a" "b" "c"))) (should (equal (erc-parse-isupport-value "abc") '("abc"))) (should (equal (erc-parse-isupport-value "\\x20foo") '(" foo"))) (should (equal (erc-parse-isupport-value "foo\\x20") '("foo "))) (should (equal (erc-parse-isupport-value "a\\x20b\\x20c") '("a b c"))) (should (equal (erc-parse-isupport-value "a\\x20b\\x20c\\x20") '("a b c "))) (should (equal (erc-parse-isupport-value "\\x20a\\x20b\\x20c") '(" a b c"))) (should (equal (erc-parse-isupport-value "a\\x20\\x20c") '("a c"))) (should (equal (erc-parse-isupport-value "\\x20\\x20\\x20") '(" "))) (should (equal (erc-parse-isupport-value "\\x5Co/") '("\\o/"))) (should (equal (erc-parse-isupport-value "\\x7F,\\x19") '("\\x7F" "\\x19"))) (should (equal (erc-parse-isupport-value "a\\x2Cb,c") '("a,b" "c")))) (ert-deftest erc-server-005 () (with-suppressed-warnings ((obsolete erc-server-parameters)) (let* ((erc-server-005-functions (copy-sequence erc-server-005-functions)) (hooked 0) (verify #'ignore) (hook (lambda (_ _) (funcall verify) (cl-incf hooked))) erc-server-parameters erc-isupport-parameters erc-timer-hook calls args parsed) (add-hook 'erc-server-005-functions hook 90) (should (eq (cadr erc-server-005-functions) hook)) (cl-letf (((symbol-function 'erc-display-message) (lambda (_ _ _ line) (push line calls)))) (ert-info ("Baseline") (setq args '("tester" "BOT=B" "EXCEPTS" "PREFIX=(ov)@+" "are supp...") parsed (make-erc-response :command-args args :command "005")) (setq verify (lambda () (should (equal erc-server-parameters '(("PREFIX" . "(ov)@+") ("EXCEPTS") ("BOT" . "B")))) (should (equal erc-isupport-parameters '((PREFIX "(ov)@+") (EXCEPTS) (BOT "B")))) (should (string= (pop calls) "BOT=B EXCEPTS PREFIX=(ov)@+ are supp...")) (should (equal args (erc-response.command-args parsed))))) (erc-call-hooks nil parsed)) (ert-info ("Negated, updated") (setq args '("tester" "-EXCEPTS" "PREFIX=(ohv)@%+" "are supported") parsed (make-erc-response :command-args args :command "005")) (setq verify (lambda () (should (equal erc-server-parameters '(("PREFIX" . "(ohv)@%+") ("BOT" . "B")))) (should (equal erc-isupport-parameters '((PREFIX "(ohv)@%+") (BOT "B")))) (should (string= (pop calls) "-EXCEPTS PREFIX=(ohv)@%+ are supported")) (should (equal args (erc-response.command-args parsed))))) (erc-call-hooks nil parsed)) (should (= hooked 2))))) (should-not (cadr erc-server-005-functions))) (ert-deftest erc-downcase () (let ((erc-isupport-parameters '((PREFIX "(ov)@+") (BOT "B")))) (ert-info ("ascii") (setf (alist-get 'CASEMAPPING erc-isupport-parameters) '("ascii")) (should (equal (erc-downcase "Bob[m]`") "bob[m]`")) (should (equal (erc-downcase "Tilde~") "tilde~" )) (should (equal (erc-downcase "\\O/") "\\o/" ))) (ert-info ("rfc1459") (setf (car (alist-get 'CASEMAPPING erc-isupport-parameters)) "rfc1459") (should (equal (erc-downcase "Bob[m]`") "bob{m}`" )) (should (equal (erc-downcase "Tilde~") "tilde^" )) (should (equal (erc-downcase "\\O/") "|o/" ))) (ert-info ("rfc1459-strict") (setf (car (alist-get 'CASEMAPPING erc-isupport-parameters)) "rfc1459-strict") (should (equal (erc-downcase "Bob[m]`") "bob{m}`")) (should (equal (erc-downcase "Tilde~") "tilde~" )) (should (equal (erc-downcase "\\O/") "|o/" ))))) (ert-deftest erc-local-channel-p () (ert-info ("Local channels not supported") (let ((erc-isupport-parameters '((CHANTYPES "#")))) (should-not (erc-valid-local-channel-p "#chan")) (should-not (erc-valid-local-channel-p "&local")))) (ert-info ("Local channels supported") (let ((erc-isupport-parameters '((CHANTYPES "&#")))) (should-not (erc-valid-local-channel-p "#chan")) (should (erc-valid-local-channel-p "&local"))))) (ert-deftest erc--target-from-string () (should (equal (erc--target-from-string "#chan") #s(erc--target "#chan" \#chan t nil))) (should (equal (erc--target-from-string "Bob") #s(erc--target "Bob" bob nil nil))) (let ((erc-isupport-parameters '((CHANTYPES "#&")))) (should (equal (erc--target-from-string "&Bitlbee") #s(erc--target "&Bitlbee" &bitlbee t t))))) (ert-deftest erc-ring-previous-command-base-case () (ert-info ("Create ring when nonexistent and do nothing") (let (erc-input-ring erc-input-ring-index) (erc-previous-command) (should (ring-p erc-input-ring)) (should (zerop (ring-length erc-input-ring))) (should-not erc-input-ring-index))) (should-not erc-input-ring)) (ert-deftest erc-ring-previous-command () (with-current-buffer (get-buffer-create "*#fake*") (erc-mode) (insert "\n\n") (should-not (local-variable-if-set-p 'erc-send-completed-hook)) (set (make-local-variable 'erc-send-completed-hook) nil) ; skip t (globals) (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)) ;; Just in case erc-ring-mode is already on (setq-local erc-pre-send-functions nil) (add-hook 'erc-pre-send-functions #'erc-add-to-input-ring) ;; (cl-letf (((symbol-function 'erc-process-input-line) (lambda (&rest _) (insert-before-markers (erc-display-message-highlight 'notice "echo: one\n")))) ((symbol-function 'erc-command-no-process-p) (lambda (&rest _) t))) (ert-info ("Create ring, populate, recall") (insert "/one") (erc-send-current-line) (should (ring-p erc-input-ring)) (should (zerop (ring-member erc-input-ring "/one"))) ; equal (should (save-excursion (forward-line -1) (goto-char (point-at-bol)) (looking-at-p "[*]+ echo: one"))) (should-not erc-input-ring-index) (erc-bol) (should (looking-at "$")) (erc-previous-command) (erc-bol) (should (looking-at "/one")) (should (zerop erc-input-ring-index))) (ert-info ("Back to one") (should (= (ring-length erc-input-ring) (1+ erc-input-ring-index))) (erc-previous-command) (should-not erc-input-ring-index) (erc-bol) (should (looking-at "$")) (should (equal (ring-ref erc-input-ring 0) "/one"))) (ert-info ("Swap input after prompt with previous (#bug46339)") (insert "abc") (erc-previous-command) (should (= 1 erc-input-ring-index)) (erc-bol) (should (looking-at "/one")) (should (equal (ring-ref erc-input-ring 0) "abc")) (should (equal (ring-ref erc-input-ring 1) "/one")) (erc-next-command) (erc-bol) (should (looking-at "abc"))))) (when noninteractive (kill-buffer "*#fake*"))) (ert-deftest erc-log-irc-protocol () (should-not erc-debug-irc-protocol) (with-temp-buffer (setq erc-server-process (start-process "fake" (current-buffer) "true") erc-server-current-nick "tester" erc-session-server "myproxy.localhost" erc-session-port 6667) (let ((inhibit-message noninteractive)) (erc-toggle-debug-irc-protocol) (erc-log-irc-protocol "PASS changeme\r\n" 'outgoing) (setq erc-server-announced-name "irc.gnu.org") (erc-log-irc-protocol ":irc.gnu.org 001 tester :Welcome") (erc-log-irc-protocol ":irc.gnu.org 002 tester :Your host is irc.gnu.org") (setq erc-network 'FooNet) (erc-log-irc-protocol ":irc.gnu.org 422 tester :MOTD missing") (setq erc-network 'BarNet) (erc-log-irc-protocol ":irc.gnu.org 221 tester +i") (set-process-query-on-exit-flag erc-server-process nil))) (with-current-buffer "*erc-protocol*" (goto-char (point-min)) (search-forward "Version") (search-forward "\r\n\r\n") (search-forward "myproxy.localhost:6667 >> PASS" (line-end-position)) (forward-line) (search-forward "irc.gnu.org << :irc.gnu.org 001" (line-end-position)) (forward-line) (search-forward "irc.gnu.org << :irc.gnu.org 002" (line-end-position)) (forward-line) (search-forward "FooNet << :irc.gnu.org 422" (line-end-position)) (forward-line) (search-forward "BarNet << :irc.gnu.org 221" (line-end-position))) (when noninteractive (kill-buffer "*erc-protocol*") (should-not erc-debug-irc-protocol))) ;; The point of this test is to ensure output is handled identically ;; regardless of whether a command handler is summoned. (ert-deftest erc-process-input-line () (let (erc-server-last-sent-time erc-server-flood-queue (orig-erc-cmd-MSG (symbol-function 'erc-cmd-MSG)) (erc-default-recipients '("#chan")) calls) (with-temp-buffer (cl-letf (((symbol-function 'erc-cmd-MSG) (lambda (line) (push line calls) (funcall orig-erc-cmd-MSG line))) ((symbol-function 'erc-server-buffer) (lambda () (current-buffer))) ((symbol-function 'erc-server-process-alive) (lambda () t)) ((symbol-function 'erc-server-send-queue) #'ignore)) (ert-info ("Dispatch to user command handler") (ert-info ("Baseline") (erc-process-input-line "/msg #chan hi\n") (should (equal (pop calls) " #chan hi")) (should (equal (pop erc-server-flood-queue) '("PRIVMSG #chan :hi\r\n" . utf-8)))) (ert-info ("Quote preserves line intact") (erc-process-input-line "/QUOTE FAKE foo bar\n") (should (equal (pop erc-server-flood-queue) '("FAKE foo bar\r\n" . utf-8)))) (ert-info ("Unknown command respected") (erc-process-input-line "/FAKE foo bar\n") (should (equal (pop erc-server-flood-queue) '("FAKE foo bar\r\n" . utf-8)))) (ert-info ("Spaces preserved") (erc-process-input-line "/msg #chan hi you\n") (should (equal (pop calls) " #chan hi you")) (should (equal (pop erc-server-flood-queue) '("PRIVMSG #chan :hi you\r\n" . utf-8)))) (ert-info ("Empty line honored") (erc-process-input-line "/msg #chan\n") (should (equal (pop calls) " #chan")) (should (equal (pop erc-server-flood-queue) '("PRIVMSG #chan :\r\n" . utf-8))))) (ert-info ("Implicit cmd via `erc-send-input-line-function'") (ert-info ("Baseline") (erc-process-input-line "hi") (should (equal (pop erc-server-flood-queue) '("PRIVMSG #chan :hi\r\n" . utf-8)))) (ert-info ("Spaces preserved") (erc-process-input-line "hi you") (should (equal (pop erc-server-flood-queue) '("PRIVMSG #chan :hi you\r\n" . utf-8)))) (ert-info ("Empty line transmitted without injected-space kludge") (erc-process-input-line "") (should (equal (pop erc-server-flood-queue) '("PRIVMSG #chan :\r\n" . utf-8)))) (should-not calls)))))) ;;; erc-tests.el ends here