1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
;;; track-changes-tests.el --- tests for emacs-lisp/track-changes.el -*- lexical-binding:t -*-
;; Copyright (C) 2024-2025 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 'track-changes)
(require 'cl-lib)
(require 'ert)
(defun track-changes-tests--random-word ()
(let ((chars ()))
(dotimes (_ (1+ (random 12)))
(push (+ ?A (random (1+ (- ?z ?A)))) chars))
(apply #'string chars)))
(defvar track-changes-tests--random-verbose nil)
(defun track-changes-tests--message (&rest args)
(when track-changes-tests--random-verbose (apply #'message args)))
(defvar track-changes-tests--random-seed
(let ((seed (number-to-string (random (expt 2 24)))))
(message "Random seed = %S" seed)
seed))
(ert-deftest track-changes-tests--random ()
;; Keep 2 buffers in sync with a third one as we make random
;; changes to that 3rd one.
;; We have 3 trackers: a "normal" one which we sync
;; at random intervals, one which syncs via the "disjoint" signal,
;; plus a third one which verifies that "nobefore" gets
;; information consistent with the "normal" tracker.
(with-temp-buffer
(random track-changes-tests--random-seed)
(dotimes (_ 100)
(insert (track-changes-tests--random-word) "\n"))
(let* ((buf1 (generate-new-buffer " *tc1*"))
(buf2 (generate-new-buffer " *tc2*"))
(char-counts (make-vector 2 0))
(sync-counts (make-vector 2 0))
(print-escape-newlines t)
(file (make-temp-file "tc"))
(id1 (track-changes-register #'ignore))
(id3 (track-changes-register #'ignore :nobefore t))
(sync
(lambda (id buf n)
(track-changes-tests--message "!! SYNC %d !!" n)
(track-changes-fetch
id (lambda (beg end before)
(when (eq n 1)
(track-changes-fetch
id3 (lambda (beg3 end3 before3)
(should (eq beg3 beg))
(should (eq end3 end))
(should (eq before3
(if (symbolp before)
before (length before)))))))
(cl-incf (aref sync-counts (1- n)))
(cl-incf (aref char-counts (1- n)) (- end beg))
(let ((after (buffer-substring beg end)))
(track-changes-tests--message
"Sync:\n %S\n=> %S\nat %d .. %d"
before after beg end)
(with-current-buffer buf
(if (eq before 'error)
(erase-buffer)
(should (equal before
(buffer-substring
beg (+ beg (length before)))))
(delete-region beg (+ beg (length before))))
(goto-char beg)
(insert after)))
(should (equal (buffer-string)
(with-current-buffer buf
(buffer-string))))))))
(id2 (track-changes-register
(lambda (id2 &optional distance)
(when distance
(track-changes-tests--message "Disjoint distance: %d"
distance)
(funcall sync id2 buf2 2)))
:disjoint t)))
(write-region (point-min) (point-max) file)
(insert-into-buffer buf1)
(insert-into-buffer buf2)
(should (equal (buffer-hash) (buffer-hash buf1)))
(should (equal (buffer-hash) (buffer-hash buf2)))
(message "seeding with: %S" track-changes-tests--random-seed)
(dotimes (_ 1000)
(pcase (random 15)
(0
(track-changes-tests--message "Manual sync1")
(funcall sync id1 buf1 1))
(1
(track-changes-tests--message "Manual sync2")
(funcall sync id2 buf2 2))
((pred (< _ 5))
(let* ((beg (+ (point-min) (random (1+ (buffer-size)))))
(end (min (+ beg (1+ (random 100))) (point-max))))
(track-changes-tests--message "Fill %d .. %d" beg end)
(fill-region-as-paragraph beg end)))
((pred (< _ 8))
(let* ((beg (+ (point-min) (random (1+ (buffer-size)))))
(end (min (+ beg (1+ (random 12))) (point-max))))
(track-changes-tests--message "Delete %S at %d .. %d"
(buffer-substring beg end) beg end)
(delete-region beg end)))
((and 8 (guard (= (random 50) 0)))
(track-changes-tests--message "Silent insertion")
(let ((inhibit-modification-hooks t))
(insert "a")))
((and 8 (guard (= (random 10) 0)))
(track-changes-tests--message "Revert")
(insert-file-contents file nil nil nil 'replace))
((and 8 (guard (= (random 3) 0)))
(let* ((beg (+ (point-min) (random (1+ (buffer-size)))))
(end (min (+ beg (1+ (random 12))) (point-max)))
(after (eq (random 2) 0)))
(track-changes-tests--message "Bogus %S %d .. %d"
(if after 'after 'before) beg end)
(if after
(run-hook-with-args 'after-change-functions
beg end (- end beg))
(run-hook-with-args 'before-change-functions beg end))))
(_
(goto-char (+ (point-min) (random (1+ (buffer-size)))))
(let ((word (track-changes-tests--random-word)))
(track-changes-tests--message "insert %S at %d" word (point))
(insert word "\n")))))
(message "SCOREs: default: %d/%d=%d disjoint: %d/%d=%d"
(aref char-counts 0) (aref sync-counts 0)
(/ (aref char-counts 0) (aref sync-counts 0))
(aref char-counts 1) (aref sync-counts 1)
(/ (aref char-counts 1) (aref sync-counts 1))))))
;;; track-changes-tests.el ends here
|