emacs/test/lisp/shadowfile-tests.el

948 lines
35 KiB
EmacsLisp

;;; shadowfile-tests.el --- Tests of shadowfile -*- lexical-binding:t -*-
;; Copyright (C) 2018-2024 Free Software Foundation, Inc.
;; Author: Michael Albinus <michael.albinus@gmx.de>
;; 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:
;; Some of the tests require access to a remote host files. Since
;; this could be problematic, a mock-up connection method "mock" is
;; used. Emulating a remote connection, it simply calls "sh -i".
;; Tramp's file name handlers still run, so this test is sufficient
;; except for connection establishing.
;; If you want to test a real Tramp connection, set
;; $REMOTE_TEMPORARY_FILE_DIRECTORY to a suitable value in order to
;; overwrite the default value. If you want to skip tests accessing a
;; remote host, set this environment variable to "/dev/null" or
;; whatever is appropriate on your system.
;; A whole test run can be performed calling the command `shadowfile-test-all'.
;;; Code:
(require 'tramp)
(require 'ert-x)
(require 'shadowfile)
(setq auth-source-save-behavior nil
password-cache-expiry nil
shadow-debug (or (getenv "EMACS_HYDRA_CI") (getenv "EMACS_EMBA_CI"))
;; When the remote user id is 0, Tramp refuses unsafe temporary files.
tramp-allow-unsafe-temporary-files
(or tramp-allow-unsafe-temporary-files noninteractive)
tramp-cache-read-persistent-data t ;; For auth-sources.
tramp-persistency-file-name nil
tramp-verbose 0
;; On macOS, `temporary-file-directory' is a symlinked directory.
temporary-file-directory (file-truename temporary-file-directory)
ert-remote-temporary-file-directory
(ignore-errors (file-truename ert-remote-temporary-file-directory)))
(defconst shadow-test-info-file
(expand-file-name "shadows_test" temporary-file-directory)
"File to keep shadow information in during tests.")
(defconst shadow-test-todo-file
(expand-file-name "shadow_todo_test" temporary-file-directory)
"File to store the list of uncopied shadows in during tests.")
(defun shadow--tests-cleanup ()
"Reset all `shadowfile' internals."
;; Cleanup Tramp.
(tramp-cleanup-connection
(tramp-dissect-file-name ert-remote-temporary-file-directory) t t)
;; Delete auto-saved files.
(with-current-buffer (find-file-noselect shadow-info-file 'nowarn)
(ignore-errors (delete-file (make-auto-save-file-name)))
(set-buffer-modified-p nil)
(kill-buffer))
(with-current-buffer (find-file-noselect shadow-todo-file 'nowarn)
(ignore-errors (delete-file (make-auto-save-file-name)))
(set-buffer-modified-p nil)
(kill-buffer))
;; Delete buffers.
(ignore-errors
(with-current-buffer shadow-info-buffer
(set-buffer-modified-p nil)
(kill-buffer)))
(ignore-errors
(with-current-buffer shadow-todo-buffer
(set-buffer-modified-p nil)
(kill-buffer)))
;; Delete files.
(ignore-errors (delete-file shadow-info-file))
(ignore-errors (delete-file shadow-todo-file))
;; Reset variables.
(shadow-invalidate-hashtable)
(setq shadow-info-buffer nil
shadow-todo-buffer nil
shadow-files-to-copy nil))
(ert-deftest shadow-test00-clusters ()
"Check cluster definitions.
Per definition, all files are identical on the different hosts of
a cluster (or site). This is not tested here; it must be
guaranteed by the originator of a cluster definition."
:tags '(:expensive-test)
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((text-quoting-style 'grave) ;; We inspect the *Messages* buffer!
(inhibit-message t)
(shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters
cluster primary regexp mocked-input)
(unwind-protect
;; We must mock `read-from-minibuffer' and `read-string', in
;; order to avoid interactive arguments.
(cl-letf* (((symbol-function #'read-from-minibuffer)
(lambda (&rest _args) (pop mocked-input)))
((symbol-function #'read-string)
(lambda (&rest _args) (pop mocked-input))))
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define a cluster.
(setq cluster "cluster"
primary shadow-system-name
regexp (shadow-regexp-superquote primary)
mocked-input `(,cluster ,primary ,regexp))
(call-interactively #'shadow-define-cluster)
(should
(string-equal
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
(should
(string-equal
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
(should
(string-equal
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
(should-not (shadow-get-cluster "non-existent-cluster-name"))
;; Test `shadow-set-cluster' and `make-shadow-cluster'.
(shadow-set-cluster cluster primary regexp)
(should
(equal (shadow-get-cluster cluster)
(make-shadow-cluster
:name cluster :primary primary :regexp regexp)))
;; The primary must be either `shadow-system-name', or a remote file.
(setq ;; The second "cluster" is wrong.
mocked-input `(,cluster ,cluster ,primary ,regexp))
(with-current-buffer (messages-buffer)
(narrow-to-region (point-max) (point-max)))
(call-interactively #'shadow-define-cluster)
(should
(string-match
(regexp-quote "Not a valid primary!")
(with-current-buffer (messages-buffer) (buffer-string))))
;; The first cluster definition is still valid.
(should
(string-equal
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
(should
(string-equal
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
(should
(string-equal
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
;; The regexp must match the primary name.
(setq ;; The second "cluster" is wrong.
mocked-input `(,cluster ,primary ,cluster ,regexp))
(with-current-buffer (messages-buffer)
(narrow-to-region (point-max) (point-max)))
(call-interactively #'shadow-define-cluster)
(should
(string-match
(regexp-quote "Regexp doesn't include the primary host!")
(with-current-buffer (messages-buffer) (buffer-string))))
;; The first cluster definition is still valid.
(should
(string-equal
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
(should
(string-equal
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
(should
(string-equal
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
;; Redefine the cluster.
(setq primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary)
mocked-input `(,cluster ,primary ,regexp))
(call-interactively #'shadow-define-cluster)
(should
(string-equal
(shadow-cluster-name (shadow-get-cluster cluster)) cluster))
(should
(string-equal
(shadow-cluster-primary (shadow-get-cluster cluster)) primary))
(should
(string-equal
(shadow-cluster-regexp (shadow-get-cluster cluster)) regexp))
;; Test `shadow-set-cluster' and `make-shadow-cluster'.
(shadow-set-cluster cluster primary regexp)
(should
(equal (shadow-get-cluster cluster)
(make-shadow-cluster
:name cluster :primary primary :regexp regexp))))
;; Cleanup.
(with-current-buffer (messages-buffer) (widen))
(shadow--tests-cleanup))))
(ert-deftest shadow-test01-sites ()
"Check site definitions.
Per definition, all files are identical on the different hosts of
a cluster (or site). This is not tested here; it must be
guaranteed by the originator of a cluster definition."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters
cluster1 cluster2 primary1 primary2 regexp1 regexp2 mocked-input)
(unwind-protect
;; We must mock `read-from-minibuffer' and `read-string', in
;; order to avoid interactive arguments.
(cl-letf* (((symbol-function #'read-from-minibuffer)
(lambda (&rest _args) (pop mocked-input)))
((symbol-function #'read-string)
(lambda (&rest _args) (pop mocked-input))))
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define a cluster.
(setq cluster1 "cluster1"
primary1 shadow-system-name
regexp1 (shadow-regexp-superquote primary1))
(shadow-set-cluster cluster1 primary1 regexp1)
;; A site is either a cluster identification, or a primary host.
(should (string-equal cluster1 (shadow-site-name cluster1)))
(should (string-equal primary1 (shadow-name-site primary1)))
(should
(string-equal (format "/%s:" cluster1) (shadow-name-site cluster1)))
(should (string-equal (system-name) (shadow-site-name primary1)))
(should
(string-equal
(file-remote-p ert-remote-temporary-file-directory)
(shadow-name-site
(file-remote-p ert-remote-temporary-file-directory))))
(should
(string-equal
(file-remote-p ert-remote-temporary-file-directory)
(shadow-site-name
(file-remote-p ert-remote-temporary-file-directory))))
(should (equal (shadow-site-cluster cluster1)
(shadow-get-cluster cluster1)))
(should (equal (shadow-site-cluster (shadow-name-site cluster1))
(shadow-get-cluster cluster1)))
(should (equal (shadow-site-cluster primary1)
(shadow-get-cluster cluster1)))
(should (equal (shadow-site-cluster (shadow-site-name primary1))
(shadow-get-cluster cluster1)))
(should (string-equal (shadow-site-primary cluster1) primary1))
(should (string-equal (shadow-site-primary primary1) primary1))
;; `shadow-read-site' accepts "cluster", "/cluster:",
;; "system", "/system:". It shall reject bad site names.
(setq mocked-input
`(,cluster1 ,(shadow-name-site cluster1)
,primary1 ,(shadow-site-name primary1)
,shadow-system-name "" "bad" "/bad:"))
(should (string-equal (shadow-read-site) cluster1))
(should (string-equal (shadow-read-site) (shadow-name-site cluster1)))
(should (string-equal (shadow-read-site) primary1))
(should (string-equal (shadow-read-site) (shadow-site-name primary1)))
(should (string-equal (shadow-read-site) shadow-system-name))
(should-not (shadow-read-site)) ; ""
(should-not (shadow-read-site)) ; "bad"
(should-not (shadow-read-site)) ; "/bad:"
(should-error (shadow-read-site)) ; no input at all
;; Define a second cluster.
(setq cluster2 "cluster2"
primary2 (file-remote-p ert-remote-temporary-file-directory)
regexp2 (format "^\\(%s\\|%s\\)$" shadow-system-name primary2))
(shadow-set-cluster cluster2 primary2 regexp2)
;; `shadow-site-match' shall know all different kind of site names.
(should (shadow-site-match cluster1 cluster1))
(should (shadow-site-match primary1 primary1))
(should (shadow-site-match cluster1 primary1))
(should (shadow-site-match primary1 cluster1))
(should (shadow-site-match cluster2 cluster2))
(should (shadow-site-match primary2 primary2))
(should (shadow-site-match cluster2 primary2))
(should (shadow-site-match primary2 cluster2))
;; The regexp of `cluster2' matches the primary of
;; `cluster1'. Not vice versa.
(should (shadow-site-match cluster2 cluster1))
(should-not (shadow-site-match cluster1 cluster2))
;; If we use the primaries of a cluster, it doesn't match.
(should-not
(shadow-site-match (shadow-site-primary cluster2) cluster1))
(should-not
(shadow-site-match (shadow-site-primary cluster1) cluster2)))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test02-files ()
"Check file manipulation functions."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters
cluster primary regexp file hup)
(unwind-protect
(progn
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define a cluster.
(setq cluster "cluster"
primary shadow-system-name
regexp (shadow-regexp-superquote primary)
file (make-temp-name
(expand-file-name
"shadowfile-tests" temporary-file-directory)))
(shadow-set-cluster cluster primary regexp)
;; The constant structure to compare with.
(setq hup (make-tramp-file-name :host (system-name) :localname file))
;; The structure a local file is transformed in.
(should (equal (shadow-parse-name file) hup))
(should (equal (shadow-parse-name (concat "/" cluster ":" file)) hup))
(should (equal (shadow-parse-name (concat primary file)) hup))
;; A local file name is kept.
(should
(string-equal (shadow-local-file file) file))
;; A file on this cluster is also local.
(should
(string-equal
(shadow-local-file (concat "/" cluster ":" file)) file))
;; A file on the primary host is also local.
(should
(string-equal (shadow-local-file (concat primary file)) file))
;; Redefine the cluster.
(setq primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster primary regexp)
;; The structure of the local file is still the same.
(should (equal (shadow-parse-name file) hup))
;; The cluster name must be used.
(setf (tramp-file-name-host hup) cluster)
(should (equal (shadow-parse-name (concat "/" cluster ":" file)) hup))
;; The structure of a remote file is different.
(should
(equal (shadow-parse-name (concat primary file))
(tramp-dissect-file-name (concat primary file))))
;; A local file is still local.
(should (shadow-local-file file))
;; A file on this cluster is not local.
(should-not (shadow-local-file (concat "/" cluster ":" file)))
;; A file on the primary host is not local.
(should-not (shadow-local-file (concat primary file)))
;; There's no error on wrong FILE.
(should-not (shadow-local-file nil)))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test03-expand-cluster-in-file-name ()
"Check canonical file name of a cluster or site."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters
cluster primary regexp file1 file2)
(unwind-protect
(progn
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define a cluster.
(setq cluster "cluster"
primary shadow-system-name
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster primary regexp)
(setq file1
(make-temp-name
(expand-file-name "shadowfile-tests" temporary-file-directory))
file2
(make-temp-name
(expand-file-name
"shadowfile-tests" ert-remote-temporary-file-directory)))
;; A local file name is kept.
(should
(string-equal (shadow-expand-cluster-in-file-name file1) file1))
;; A remote file is kept.
(should
(string-equal (shadow-expand-cluster-in-file-name file2) file2))
;; A cluster name is expanded to the primary name.
(should
(string-equal
(shadow-expand-cluster-in-file-name (format "/%s:%s" cluster file1))
(shadow-expand-cluster-in-file-name (concat primary file1))))
;; A primary name is expanded if it is a local file name.
(should
(string-equal
(shadow-expand-cluster-in-file-name (concat primary file1)) file1))
;; Redefine the cluster.
(setq primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster primary regexp)
;; A cluster name is expanded to the primary name.
(should
(string-equal
(shadow-expand-cluster-in-file-name (format "/%s:%s" cluster file1))
(shadow-expand-cluster-in-file-name (concat primary file1))))
;; A primary name is not expanded if it isn't is a local file name.
(should
(string-equal
(shadow-expand-cluster-in-file-name (concat primary file1))
(concat primary file1))))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test04-contract-file-name ()
"Check canonical file name of a cluster or site."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters
cluster primary regexp file)
(unwind-protect
(progn
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define a cluster.
(setq cluster "cluster"
primary shadow-system-name
regexp (shadow-regexp-superquote primary)
file (make-temp-name
(expand-file-name
"shadowfile-tests" temporary-file-directory)))
(shadow-set-cluster cluster primary regexp)
;; The cluster name is prepended for local files.
(should
(string-equal
(shadow-contract-file-name file) (concat "/cluster:" file)))
;; A cluster file name is preserved.
(should
(string-equal
(shadow-contract-file-name (concat "/cluster:" file))
(concat "/cluster:" file)))
;; `shadow-system-name' is mapped to the cluster.
(should
(string-equal
(shadow-contract-file-name (concat shadow-system-name file))
(concat "/cluster:" file)))
;; Redefine the cluster.
(setq primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster primary regexp)
;; A remote file name is mapped to the cluster.
(should
(string-equal
(shadow-contract-file-name
(concat (file-remote-p ert-remote-temporary-file-directory) file))
(concat "/cluster:" file))))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test05-file-match ()
"Check `shadow-same-site' and `shadow-file-match'."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters
cluster primary regexp file)
(unwind-protect
(progn
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define a cluster.
(setq cluster "cluster"
primary shadow-system-name
regexp (shadow-regexp-superquote primary)
file (make-temp-name
(expand-file-name
"shadowfile-tests" temporary-file-directory)))
(shadow-set-cluster cluster primary regexp)
(should (shadow-same-site (shadow-parse-name "/cluster:") file))
(should
(shadow-same-site (shadow-parse-name shadow-system-name) file))
(should (shadow-same-site (shadow-parse-name file) file))
(should
(shadow-file-match
(shadow-parse-name (concat "/cluster:" file)) file))
(should
(shadow-file-match
(shadow-parse-name (concat shadow-system-name file)) file))
(should (shadow-file-match (shadow-parse-name file) file))
;; Redefine the cluster.
(setq primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster primary regexp)
(should
(shadow-file-match
(shadow-parse-name
(concat (file-remote-p ert-remote-temporary-file-directory) file))
file)))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test06-literal-groups ()
"Check literal group definitions."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters shadow-literal-groups
cluster1 cluster2 primary regexp file1 file2 mocked-input)
(unwind-protect
;; We must mock `read-from-minibuffer' and `read-string', in
;; order to avoid interactive arguments.
(cl-letf* (((symbol-function #'read-from-minibuffer)
(lambda (&rest _args) (pop mocked-input)))
((symbol-function #'read-string)
(lambda (&rest _args) (pop mocked-input))))
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define clusters.
(setq cluster1 "cluster1"
primary shadow-system-name
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster1 primary regexp)
(setq cluster2 "cluster2"
primary (file-remote-p ert-remote-temporary-file-directory)
regexp (format "^\\(%s\\|%s\\)$" shadow-system-name primary))
(shadow-set-cluster cluster2 primary regexp)
;; Define a literal group.
(setq file1
(make-temp-name
(expand-file-name "shadowfile-tests" temporary-file-directory))
file2
(make-temp-name
(expand-file-name
"shadowfile-tests" ert-remote-temporary-file-directory))
mocked-input
`(,cluster1 ,file1 ,cluster2 ,file2
,primary ,file1 ,(kbd "RET")))
(with-temp-buffer
(set-visited-file-name file1)
(call-interactively #'shadow-define-literal-group)
(set-buffer-modified-p nil))
;; `shadow-literal-groups' is a list of lists.
(should (consp shadow-literal-groups))
(should (consp (car shadow-literal-groups)))
(should-not (cdr shadow-literal-groups))
(should (member (format "/%s:%s" cluster1 (file-local-name file1))
(car shadow-literal-groups)))
(should (member (format "/%s:%s" cluster2 (file-local-name file2))
(car shadow-literal-groups)))
;; Bug#49596.
(should (member (concat primary file1) (car shadow-literal-groups)))
;; Error handling.
(setq shadow-literal-groups nil)
;; There's no `buffer-file-name'.
(with-temp-buffer
(call-interactively #'shadow-define-literal-group)
(set-buffer-modified-p nil))
(should-not shadow-literal-groups)
;; Define an empty literal group.
(setq mocked-input `(,(kbd "RET")))
(with-temp-buffer
(set-visited-file-name file1)
(call-interactively #'shadow-define-literal-group)
(set-buffer-modified-p nil))
(should-not shadow-literal-groups)
;; Use a non-existing site name.
(setq mocked-input `("foo" ,(kbd "RET")))
(with-temp-buffer
(set-visited-file-name file1)
(call-interactively #'shadow-define-literal-group)
(set-buffer-modified-p nil))
(should-not shadow-literal-groups))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test07-regexp-groups ()
"Check regexp group definitions."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(let ((shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
shadow-clusters shadow-regexp-groups
cluster1 cluster2 primary regexp file mocked-input)
(unwind-protect
;; We must mock `read-from-minibuffer' and `read-string', in
;; order to avoid interactive arguments.
(cl-letf* (((symbol-function #'read-from-minibuffer)
(lambda (&rest _args) (pop mocked-input)))
((symbol-function #'read-string)
(lambda (&rest _args) (pop mocked-input))))
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define clusters.
(setq cluster1 "cluster1"
primary shadow-system-name
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster1 primary regexp)
(setq cluster2 "cluster2"
primary (file-remote-p ert-remote-temporary-file-directory)
regexp (format "^\\(%s\\|%s\\)$" shadow-system-name primary))
(shadow-set-cluster cluster2 primary regexp)
;; Define a regexp group.
(setq file
(make-temp-name
(expand-file-name "shadowfile-tests" temporary-file-directory))
mocked-input `(,(shadow-regexp-superquote file)
,cluster1 ,cluster2 ,(kbd "RET")))
(with-temp-buffer
(set-visited-file-name nil)
(call-interactively #'shadow-define-regexp-group)
(set-buffer-modified-p nil))
;; `shadow-regexp-groups' is a list of lists.
(should (consp shadow-regexp-groups))
(should (consp (car shadow-regexp-groups)))
(should-not (cdr shadow-regexp-groups))
(should
(member
(concat
(shadow-site-primary cluster1) (shadow-regexp-superquote file))
(car shadow-regexp-groups)))
(should
(member
(concat
(shadow-site-primary cluster2) (shadow-regexp-superquote file))
(car shadow-regexp-groups))))
;; Cleanup.
(shadow--tests-cleanup))))
(ert-deftest shadow-test08-shadow-todo ()
"Check that needed shadows are added to todo."
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(skip-unless (file-writable-p ert-remote-temporary-file-directory))
(let ((backup-inhibited t)
create-lockfiles
(shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
(shadow-inhibit-message t)
shadow-clusters shadow-literal-groups shadow-regexp-groups
shadow-files-to-copy
cluster1 cluster2 primary regexp file)
(unwind-protect
(progn
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
(when shadow-debug
(message
"%s %s %s %s %s"
temporary-file-directory
ert-remote-temporary-file-directory
shadow-homedir shadow-info-file shadow-todo-file))
;; Define clusters.
(setq cluster1 "cluster1"
primary shadow-system-name
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster1 primary regexp)
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s %s %s"
cluster1 primary regexp shadow-clusters))
(setq cluster2 "cluster2"
primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster2 primary regexp)
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s %s %s"
cluster2 primary regexp shadow-clusters))
;; Define a literal group.
(setq file
(make-temp-name
(expand-file-name "shadowfile-tests" temporary-file-directory))
shadow-literal-groups
`((,(concat "/cluster1:" file) ,(concat "/cluster2:" file))))
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s" file shadow-literal-groups))
;; Save file from "cluster1" definition.
(with-temp-buffer
(set-visited-file-name file)
(insert "foo")
(save-buffer))
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s"
(cons file (shadow-contract-file-name (concat "/cluster2:" file)))
shadow-files-to-copy))
(should
(member
(cons file (shadow-contract-file-name (concat "/cluster2:" file)))
shadow-files-to-copy))
;; Save file from "cluster2" definition.
(with-temp-buffer
(set-visited-file-name (concat (shadow-site-primary cluster2) file))
(insert "foo")
(save-buffer))
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s"
(cons
(concat (shadow-site-primary cluster2) file)
(shadow-contract-file-name (concat "/cluster1:" file)))
shadow-files-to-copy))
(should
(member
(cons
(concat (shadow-site-primary cluster2) file)
(shadow-contract-file-name (concat "/cluster1:" file)))
shadow-files-to-copy))
;; Define a regexp group.
(setq shadow-files-to-copy nil
shadow-regexp-groups
`((,(concat (shadow-site-primary cluster1)
(shadow-regexp-superquote file))
,(concat (shadow-site-primary cluster2)
(shadow-regexp-superquote file)))))
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s" file shadow-regexp-groups))
;; Save file from "cluster1" definition.
(with-temp-buffer
(set-visited-file-name file)
(insert "foo")
(save-buffer))
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s"
(cons file (shadow-contract-file-name (concat "/cluster2:" file)))
shadow-files-to-copy))
(should
(member
(cons file (shadow-contract-file-name (concat "/cluster2:" file)))
shadow-files-to-copy))
;; Save file from "cluster2" definition.
(with-temp-buffer
(set-visited-file-name (concat (shadow-site-primary cluster2) file))
(insert "foo")
(save-buffer))
(when shadow-debug
(message
"shadow-test08-shadow-todo: %s %s"
(cons
(concat (shadow-site-primary cluster2) file)
(shadow-contract-file-name (concat "/cluster1:" file)))
shadow-files-to-copy))
(should
(member
(cons
(concat (shadow-site-primary cluster2) file)
(shadow-contract-file-name (concat "/cluster1:" file)))
shadow-files-to-copy)))
;; Cleanup.
(dolist (elt `(,file ,(concat (shadow-site-primary cluster2) file)))
(ignore-errors
(with-current-buffer (get-file-buffer elt)
(set-buffer-modified-p nil)
(kill-buffer)))
(ignore-errors (delete-file elt)))
(shadow--tests-cleanup))))
(ert-deftest shadow-test09-shadow-copy-files ()
"Check that needed shadow files are copied."
:tags '(:expensive-test)
(skip-when (memq system-type '(windows-nt ms-dos)))
(skip-unless (file-remote-p ert-remote-temporary-file-directory))
(skip-unless (file-writable-p ert-remote-temporary-file-directory))
(let ((backup-inhibited t)
create-lockfiles
(shadow-info-file shadow-test-info-file)
(shadow-todo-file shadow-test-todo-file)
(shadow-inhibit-message t)
(shadow-noquery t)
shadow-clusters shadow-files-to-copy
cluster1 cluster2 primary regexp file mocked-input)
(unwind-protect
(progn
;; Cleanup & initialize.
(shadow--tests-cleanup)
(shadow-initialize)
;; Define clusters.
(setq cluster1 "cluster1"
primary shadow-system-name
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster1 primary regexp)
(setq cluster2 "cluster2"
primary (file-remote-p ert-remote-temporary-file-directory)
regexp (shadow-regexp-superquote primary))
(shadow-set-cluster cluster2 primary regexp)
;; Define files to copy.
(setq file
(make-temp-name
(expand-file-name "shadowfile-tests" temporary-file-directory))
shadow-literal-groups
`((,(concat "/cluster1:" file) ,(concat "/cluster2:" file)))
shadow-regexp-groups
`((,(concat (shadow-site-primary cluster1)
(shadow-regexp-superquote file))
,(concat (shadow-site-primary cluster2)
(shadow-regexp-superquote file))))
mocked-input `(,(concat (shadow-site-primary cluster2) file)
,file))
;; Save files.
(with-temp-buffer
(set-visited-file-name file)
(insert "foo")
(save-buffer))
(with-temp-buffer
(set-visited-file-name (concat (shadow-site-primary cluster2) file))
(insert "foo")
(save-buffer))
;; We must mock `write-region', in order to check proper
;; action.
(add-function
:before (symbol-function #'write-region)
(lambda (&rest _args)
(when (and (buffer-file-name) mocked-input)
(should (equal (buffer-file-name) (pop mocked-input)))))
'((name . "write-region-mock")))
;; Copy the files.
(shadow-copy-files 'noquery)
(should-not shadow-files-to-copy)
(with-current-buffer shadow-todo-buffer
(goto-char (point-min))
(should
(looking-at (regexp-quote "(setq shadow-files-to-copy nil)")))))
;; Cleanup.
(remove-function (symbol-function #'write-region) "write-region-mock")
(dolist (elt `(,file ,(concat (shadow-site-primary cluster2) file)))
(ignore-errors
(with-current-buffer (get-file-buffer elt)
(set-buffer-modified-p nil)
(kill-buffer)))
(ignore-errors (delete-file elt)))
(shadow--tests-cleanup))))
(defun shadowfile-test-all (&optional interactive)
"Run all tests for \\[shadowfile]."
(interactive "p")
(if interactive
(ert-run-tests-interactively "^shadowfile-")
(ert-run-tests-batch "^shadowfile-")))
(provide 'shadowfile-tests)
;;; shadowfile-tests.el ends here