branch: externals/vc-jj commit b75160031a17ec9fe85e9fd3740278f5329d90d0 Author: Rudi Schlatte <r...@constantly.at> Commit: Wojciech Siewierski <vi...@noreply.codeberg.org>
Add some tests Also bind `default-directory` before calling `file-relative-name` instead of passing a second argument `root` to that function, since a relative filename gets expanded against default-directory before being relativized against `root`. If default-directory was not equal to `root`, the resulting filename would name a file outside of `root`. Found by a failing test :) --- vc-jj-tests.el | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vc-jj.el | 18 +++++------ 2 files changed, 104 insertions(+), 9 deletions(-) diff --git a/vc-jj-tests.el b/vc-jj-tests.el new file mode 100644 index 0000000000..3a72ecf254 --- /dev/null +++ b/vc-jj-tests.el @@ -0,0 +1,95 @@ +;;; vc-jj-tests.el --- tests for vc-jj.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Rudolf Schlatte + +;; Author: Rudolf Schlatte <r...@constantly.at> + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; + +;;; Code: + +(require 'ert-x) +(require 'vc) +(require 'vc-dir) +(require 'vc-jj) + +(defmacro vc-jj-test--with-repo (name &rest body) + "Initialize a repository in a temporary directory and evaluate BODY. + +The current directory will be set to the top of that repository; NAME +will be bound to that directory's file name. Once BODY exits, the +directory will be deleted. + +Some environment variables that control jj's behavior will be set +for the duration of BODY." + (declare (indent 1)) + `(ert-with-temp-directory ,name + (let ((default-directory ,name) + ;; Note: if we need reproducible repository state, use + ;; JJ_RANDOMNESS_SEED=12345 when calling `jj git init', and + ;; increase its value by 1 or call `jj config set --repo + ;; debug.randomness-seed 12346' etc. between each jj call + ;; afterwards -- see + ;; https://github.com/jj-vcs/jj/blob/d79c7a0dd5b8f9d3d6f9436452dcf0e1600b0b14/cli/tests/common/test_environment.rs#L115 + ;; for other relevant environment variables. + (process-environment (append '("JJ_EMAIL=j...@example.com" + "JJ_USER=john") + process-environment))) + (vc-create-repo 'jj) + ,@body))) + +(ert-deftest vc-jj-test-add-file () + (vc-jj-test--with-repo repo + (write-region "New file" nil "README") + (should (vc-jj--file-tracked "README")) + (should (vc-jj--file-added "README")) + (should (not (vc-jj--file-modified "README"))) + (should (not (vc-jj--file-conflicted "README"))) + (should (eq (vc-state "README" 'jj) 'added)))) + +(ert-deftest vc-jj-test-added-tracked () + (vc-jj-test--with-repo repo + (write-region "In first commit" nil "first-file") + (vc-jj-checkin '("first-file") "First commit") + (write-region "In second commit" nil "second-file") + (should (eq (vc-jj-state "second-file") 'added)) + (should (eq (vc-jj-state "first-file") 'up-to-date)))) + +(ert-deftest vc-jj-test-conflict () + (vc-jj-test--with-repo repo + (let (branch-1 branch-2 branch-merged) + ;; the root change id is always zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz + (shell-command "jj new zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz") + (setq branch-1 (vc-jj-working-revision ".")) + (write-region "Unconflicted" nil "unconflicted.txt") + (write-region "Branch 1" nil "conflicted.txt") + (make-directory "subdir") + (write-region "Branch 1" nil "subdir/conflicted.txt") + (shell-command "jj new zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz") + (setq branch-2 (vc-jj-working-revision ".")) + (write-region "Unconflicted" nil "unconflicted.txt") + (write-region "Branch 2" nil "conflicted.txt") + (make-directory "subdir") + (write-region "Branch 2" nil "subdir/conflicted.txt") + (shell-command (concat "jj new " branch-1 " " branch-2)) + (should (eq (vc-jj-state "unconflicted.txt") 'up-to-date)) + (should (eq (vc-jj-state "conflicted.txt") 'conflict)) + (should (eq (vc-jj-state "subdir/conflicted.txt") 'conflict))))) + +(provide 'vc-jj-tests) +;;; vc-jj-tests.el ends here diff --git a/vc-jj.el b/vc-jj.el index 6a2111473e..c1599de20f 100644 --- a/vc-jj.el +++ b/vc-jj.el @@ -87,15 +87,15 @@ (unless (not (file-exists-p default-directory)) (with-demoted-errors "Error: %S" (when-let ((root (vc-jj-root file))) - (let ((relative (file-relative-name file root)) - (default-directory root)) + (let* ((default-directory root) + (relative (file-relative-name file))) (vc-jj--file-tracked relative))))))) (defun vc-jj-state (file) "JJ implementation of `vc-state' for FILE." (when-let ((root (vc-jj-root file))) - (let ((relative (file-relative-name file root)) - (default-directory root)) + (let* ((default-directory root) + (relative (file-relative-name file))) (cond ((vc-jj--file-conflicted relative) 'conflict) @@ -121,11 +121,11 @@ The list is passed to UPDATE-FUNCTION." (seq-filter (lambda (file) (string-prefix-p "M " file)) changed-files))) ;; The output of `jj resolve --list' is a list of file names - ;; plus a conflict description -- rather than trying to be - ;; fancy and parsing each line (and getting bugs with file - ;; names with spaces), use `string-prefix-p' later. Also, - ;; the command errors when there are no conflicts. - (conflicted (ignore-errors (process-lines "jj" "resolve" "--list")))) + ;; plus a conflict description per line -- rather than trying + ;; to be fancy and parsing each line (and getting bugs with + ;; file names with spaces), use `string-prefix-p' later. + ;; Also, the command errors when there are no conflicts. + (conflicted (process-lines-ignore-status "jj" "resolve" "--list"))) (let ((result (mapcar (lambda (file)