aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Kangas2020-02-05 13:12:01 +0100
committerStefan Kangas2020-02-05 13:18:12 +0100
commitaea12d4903136c057bb14d3fd7683bf7a4e1eff6 (patch)
tree183e578c697d5b95191df9384f714c999f84a7e7
parent196da3017bc9b2fc6fecff0c0ce560e6c46b8a72 (diff)
downloademacs-aea12d4903136c057bb14d3fd7683bf7a4e1eff6.tar.gz
emacs-aea12d4903136c057bb14d3fd7683bf7a4e1eff6.zip
Add new filter commands to Package Menu (Bug#38424)
* lisp/emacs-lisp/package.el (package-menu-filter-by-version) (package-menu-filter-by-status, package-menu-filter-by-archive): New filter commands. (package-menu--filter-by): New helper function. (package-menu-filter-by-keyword, package-menu-filter-by-name): Use the above helper function. (package-menu-mode-menu): (package-menu-mode-map): Update menu to include new filter commands. * doc/emacs/package.texi (Package Menu): Document the new commands and re-arrange the sort order of commands to be closer to the one in describe-major-mode. * etc/NEWS: Announce the new commands. * lisp/emacs-lisp/package.el (package-menu--display): New function extracted from.... (package-menu--generate): ...here. * test/lisp/emacs-lisp/package-tests.el (with-package-menu-test): New macro. (package-test-update-listing, package-test-list-filter-by-name) (package-test-list-filter-clear): Use above macro. (package-test-list-filter-by-archive) (package-test-list-filter-by-keyword) (package-test-list-filter-by-status) (package-test-list-filter-by-version-=) (package-test-list-filter-by-version-<) (package-test-list-filter-by-version->): New tests. (package-test-filter-by-version): New helper function.
-rw-r--r--doc/emacs/package.texi63
-rw-r--r--etc/NEWS14
-rw-r--r--lisp/emacs-lisp/package.el219
-rw-r--r--test/lisp/emacs-lisp/package-tests.el117
4 files changed, 319 insertions, 94 deletions
diff --git a/doc/emacs/package.texi b/doc/emacs/package.texi
index 1cac7f9b4b6..360fc980e4a 100644
--- a/doc/emacs/package.texi
+++ b/doc/emacs/package.texi
@@ -151,27 +151,6 @@ Refresh the package list (@code{revert-buffer}). This fetches the
151list of available packages from the package archive again, and 151list of available packages from the package archive again, and
152redisplays the package list. 152redisplays the package list.
153 153
154@item / k
155@kindex / k @r{(Package Menu)}
156@findex package-menu-filter-by-keyword
157Filter the package list by keyword
158(@code{package-menu-filter-by-keyword}). This prompts for a keyword
159(e.g., @samp{games}), then shows only the packages that relate to that
160keyword.
161
162@item / n
163@kindex / n @r{(Package Menu)}
164@findex package-menu-filter-by-name
165Filter the package list by name (@code{package-menu-filter-by-name}).
166This prompts for a string, then shows only the packages whose names
167match a regexp with that value.
168
169@item / /
170@kindex / / @r{(Package Menu)}
171@findex package-menu-clear-filter
172Clear filter currently applied to the package list
173(@code{package-menu-clear-filter}).
174
175@item H 154@item H
176@kindex H @r{(Package Menu)} 155@kindex H @r{(Package Menu)}
177@findex package-menu-hide-package 156@findex package-menu-hide-package
@@ -183,6 +162,48 @@ Permanently hide packages that match a regexp
183@findex package-menu-toggle-hiding 162@findex package-menu-toggle-hiding
184Toggle visibility of old versions of packages and also of versions 163Toggle visibility of old versions of packages and also of versions
185from lower-priority archives (@code{package-menu-toggle-hiding}). 164from lower-priority archives (@code{package-menu-toggle-hiding}).
165
166@item / a
167@kindex / a @r{(Package Menu)}
168@findex package-menu-filter-by-archive
169Filter package list by archive (@code{package-menu-filter-by-archive}).
170This prompts for a package archive (e.g., @samp{gnu}), then shows only
171packages from that archive.
172
173@item / k
174@kindex / k @r{(Package Menu)}
175@findex package-menu-filter-by-keyword
176Filter package list by keyword (@code{package-menu-filter-by-keyword}).
177This prompts for a keyword (e.g., @samp{games}), then shows only
178packages with that keyword.
179
180@item / n
181@kindex / n @r{(Package Menu)}
182@findex package-menu-filter-by-name
183Filter package list by name (@code{package-menu-filter-by-name}).
184This prompts for a regular expression, then shows only packages
185with names matching that regexp.
186
187@item / s
188@kindex / s @r{(Package Menu)}
189@findex package-menu-filter-by-status
190Filter package list by status (@code{package-menu-filter-by-status}).
191This prompts for one or more statuses (e.g., @samp{available}), then
192shows only packages with matching status.
193
194@item / v
195@kindex / v @r{(Package Menu)}
196@findex package-menu-filter-by-version
197Filter package list by version (@code{package-menu-filter-by-version}).
198This prompts first for one of the qualifiers @samp{<}, @samp{>} or
199@samp{=}, and then a package version, and shows packages that has a
200lower, equal or higher version than the one specified.
201
202@item / /
203@kindex / / @r{(Package Menu)}
204@findex package-menu-filter-clear
205Clear filter currently applied to the package list
206(@code{package-menu-filter-clear}).
186@end table 207@end table
187 208
188@noindent 209@noindent
diff --git a/etc/NEWS b/etc/NEWS
index 093b54bdd46..6ab6a8aab3b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -120,6 +120,20 @@ like cell phones, tablets or cameras.
120*** Pcase 'map' pattern added keyword symbols abbreviation. 120*** Pcase 'map' pattern added keyword symbols abbreviation.
121A pattern like '(map :sym)' binds the map's value for ':sym' to 'sym', 121A pattern like '(map :sym)' binds the map's value for ':sym' to 'sym',
122equivalent to '(map (:sym sym))'. 122equivalent to '(map (:sym sym))'.
123** Package
124
125+++
126*** New functions to filter the package list.
127The filter command key bindings are as follows:
128
129key binding
130--- -------
131/ a package-menu-filter-by-archive
132/ k package-menu-filter-by-keyword
133/ n package-menu-filter-by-name
134/ s package-menu-filter-by-status
135/ v package-menu-filter-by-version
136/ / package-menu-filter-clear
123 137
124 138
125* New Modes and Packages in Emacs 28.1 139* New Modes and Packages in Emacs 28.1
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index a9508c1bdc5..f14ef7919ea 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -2679,15 +2679,18 @@ either a full name or nil, and EMAIL is a valid email address."
2679 (define-key map "i" 'package-menu-mark-install) 2679 (define-key map "i" 'package-menu-mark-install)
2680 (define-key map "U" 'package-menu-mark-upgrades) 2680 (define-key map "U" 'package-menu-mark-upgrades)
2681 (define-key map "r" 'revert-buffer) 2681 (define-key map "r" 'revert-buffer)
2682 (define-key map (kbd "/ k") 'package-menu-filter-by-keyword)
2683 (define-key map (kbd "/ n") 'package-menu-filter-by-name)
2684 (define-key map (kbd "/ /") 'package-menu-clear-filter)
2685 (define-key map "~" 'package-menu-mark-obsolete-for-deletion) 2682 (define-key map "~" 'package-menu-mark-obsolete-for-deletion)
2686 (define-key map "x" 'package-menu-execute) 2683 (define-key map "x" 'package-menu-execute)
2687 (define-key map "h" 'package-menu-quick-help) 2684 (define-key map "h" 'package-menu-quick-help)
2688 (define-key map "H" #'package-menu-hide-package) 2685 (define-key map "H" #'package-menu-hide-package)
2689 (define-key map "?" 'package-menu-describe-package) 2686 (define-key map "?" 'package-menu-describe-package)
2690 (define-key map "(" #'package-menu-toggle-hiding) 2687 (define-key map "(" #'package-menu-toggle-hiding)
2688 (define-key map (kbd "/ /") 'package-menu-clear-filter)
2689 (define-key map (kbd "/ a") 'package-menu-filter-by-archive)
2690 (define-key map (kbd "/ k") 'package-menu-filter-by-keyword)
2691 (define-key map (kbd "/ n") 'package-menu-filter-by-name)
2692 (define-key map (kbd "/ s") 'package-menu-filter-by-status)
2693 (define-key map (kbd "/ v") 'package-menu-filter-by-version)
2691 map) 2694 map)
2692 "Local keymap for `package-menu-mode' buffers.") 2695 "Local keymap for `package-menu-mode' buffers.")
2693 2696
@@ -2714,8 +2717,11 @@ either a full name or nil, and EMAIL is a valid email address."
2714 2717
2715 "--" 2718 "--"
2716 ("Filter Packages" 2719 ("Filter Packages"
2720 ["Filter by Archive" package-menu-filter-by-archive :help "Filter packages by archive"]
2717 ["Filter by Keyword" package-menu-filter-by-keyword :help "Filter packages by keyword"] 2721 ["Filter by Keyword" package-menu-filter-by-keyword :help "Filter packages by keyword"]
2718 ["Filter by Name" package-menu-filter-by-name :help "Filter packages by name"] 2722 ["Filter by Name" package-menu-filter-by-name :help "Filter packages by name"]
2723 ["Filter by Status" package-menu-filter-by-status :help "Filter packages by status"]
2724 ["Filter by Version" package-menu-filter-by-version :help "Filter packages by version"]
2719 ["Clear Filter" package-menu-clear-filter :help "Clear package list filter"]) 2725 ["Clear Filter" package-menu-clear-filter :help "Clear package list filter"])
2720 2726
2721 ["Hide by Regexp" package-menu-hide-package :help "Permanently hide all packages matching a regexp"] 2727 ["Hide by Regexp" package-menu-hide-package :help "Permanently hide all packages matching a regexp"]
@@ -3021,22 +3027,31 @@ When none are given, the package matches."
3021 found) 3027 found)
3022 t)) 3028 t))
3023 3029
3024(defun package-menu--generate (remember-pos packages &optional keywords) 3030(defun package-menu--display (remember-pos suffix)
3025 "Populate the Package Menu. 3031 "Display the Package Menu.
3026If REMEMBER-POS is non-nil, keep point on the same entry. 3032If REMEMBER-POS is non-nil, keep point on the same entry.
3033
3034If SUFFIX is non-nil, append that to \"Package\" for the first
3035column in the header line."
3036 (setf (car (aref tabulated-list-format 0))
3037 (if suffix
3038 (concat "Package[" suffix "]")
3039 "Package"))
3040 (tabulated-list-init-header)
3041 (tabulated-list-print remember-pos))
3042
3043(defun package-menu--generate (remember-pos &optional packages keywords)
3044 "Populate and display the Package Menu.
3027PACKAGES should be t, which means to display all known packages, 3045PACKAGES should be t, which means to display all known packages,
3028or a list of package names (symbols) to display. 3046or a list of package names (symbols) to display.
3029 3047
3030With KEYWORDS given, only packages with those keywords are 3048With KEYWORDS given, only packages with those keywords are
3031shown." 3049shown."
3032 (package-menu--refresh packages keywords) 3050 (package-menu--refresh packages keywords)
3033 (setf (car (aref tabulated-list-format 0)) 3051 (package-menu--display remember-pos
3034 (if keywords 3052 (when keywords
3035 (let ((filters (mapconcat #'identity keywords ","))) 3053 (let ((filters (mapconcat #'identity keywords ",")))
3036 (concat "Package[" filters "]")) 3054 (concat "Package[" filters "]")))))
3037 "Package"))
3038 (tabulated-list-init-header)
3039 (tabulated-list-print remember-pos))
3040 3055
3041(defun package-menu--print-info (pkg) 3056(defun package-menu--print-info (pkg)
3042 "Return a package entry suitable for `tabulated-list-entries'. 3057 "Return a package entry suitable for `tabulated-list-entries'.
@@ -3673,45 +3688,160 @@ shown."
3673 (select-window win) 3688 (select-window win)
3674 (switch-to-buffer buf)))) 3689 (switch-to-buffer buf))))
3675 3690
3691(defun package-menu--filter-by (predicate suffix)
3692 "Filter \"*Packages*\" buffer by PREDICATE and add SUFFIX to header.
3693PREDICATE is a function which will be called with one argument, a
3694`package-desc' object, and returns t if that object should be
3695listed in the Package Menu.
3696
3697SUFFIX is passed on to `package-menu--display' and is added to
3698the header line of the first column."
3699 ;; Update `tabulated-list-entries' so that it contains all
3700 ;; packages before searching.
3701 (package-menu--refresh t nil)
3702 (let (found-entries)
3703 (dolist (entry tabulated-list-entries)
3704 (when (funcall predicate (car entry))
3705 (push entry found-entries)))
3706 (if found-entries
3707 (progn
3708 (setq tabulated-list-entries found-entries)
3709 (package-menu--display t suffix))
3710 (user-error "No packages found"))))
3711
3712(defun package-menu-filter-by-archive (archive)
3713 "Filter the \"*Packages*\" buffer by ARCHIVE.
3714Display only packages from package archive ARCHIVE.
3715
3716When called interactively, prompt for ARCHIVE, which can be a
3717comma-separated string. If ARCHIVE is empty, show all packages.
3718
3719When called from Lisp, ARCHIVE can be a string or a list of
3720strings. If ARCHIVE is nil or the empty string, show all
3721packages."
3722 (interactive (list (completing-read-multiple
3723 "Filter by archive (comma separated): "
3724 (mapcar #'car package-archives))))
3725 (package--ensure-package-menu-mode)
3726 (let ((re (if (listp archive)
3727 (regexp-opt archive)
3728 archive)))
3729 (package-menu--filter-by (lambda (pkg-desc)
3730 (let ((pkg-archive (package-desc-archive pkg-desc)))
3731 (and pkg-archive
3732 (string-match-p re pkg-archive))))
3733 (concat "archive:" (if (listp archive)
3734 (string-join archive ",")
3735 archive)))))
3736
3676(defun package-menu-filter-by-keyword (keyword) 3737(defun package-menu-filter-by-keyword (keyword)
3677 "Filter the \"*Packages*\" buffer by KEYWORD. 3738 "Filter the \"*Packages*\" buffer by KEYWORD.
3678Show only those items that relate to the specified KEYWORD. 3739Display only packages with specified KEYWORD.
3679 3740
3680KEYWORD can be a string or a list of strings. If it is a list, a 3741When called interactively, prompt for KEYWORD, which can be a
3681package will be displayed if it matches any of the keywords. 3742comma-separated string. If KEYWORD is empty, show all packages.
3682Interactively, it is a list of strings separated by commas. 3743
3683 3744When called from Lisp, KEYWORD can be a string or a list of
3684KEYWORD can also be used to filter by status or archive name by 3745strings. If KEYWORD is nil or the empty string, show all
3685using keywords like \"arc:gnu\" and \"status:available\". 3746packages."
3686Statuses available include \"incompat\", \"available\", 3747 (interactive (list (completing-read-multiple
3687\"built-in\" and \"installed\"." 3748 "Keywords (comma separated): "
3688 (interactive 3749 (package-all-keywords))))
3689 (list (completing-read-multiple 3750 (when (stringp keyword)
3690 "Keywords (comma separated): " (package-all-keywords)))) 3751 (setq keyword (list keyword)))
3691 (package--ensure-package-menu-mode) 3752 (package--ensure-package-menu-mode)
3692 (package-show-package-list t (if (stringp keyword) 3753 (if (not keyword)
3693 (list keyword) 3754 (package-menu--generate t t)
3694 keyword))) 3755 (package-menu--filter-by (lambda (pkg-desc)
3756 (package--has-keyword-p pkg-desc keyword))
3757 (concat "keyword:" (string-join keyword ",")))))
3695 3758
3696(defun package-menu-filter-by-name (name) 3759(defun package-menu-filter-by-name (name)
3697 "Filter the \"*Packages*\" buffer by NAME. 3760 "Filter the \"*Packages*\" buffer by NAME regexp.
3698Show only those items whose name matches the regular expression 3761Display only packages with name that matches regexp NAME.
3699NAME. If NAME is nil or the empty string, show all packages." 3762
3700 (interactive (list (read-from-minibuffer "Filter by name (regexp): "))) 3763When called interactively, prompt for NAME.
3764
3765If NAME is nil or the empty string, show all packages."
3766 (interactive (list (read-regexp "Filter by name (regexp)")))
3701 (package--ensure-package-menu-mode) 3767 (package--ensure-package-menu-mode)
3702 (if (or (not name) (string-empty-p name)) 3768 (if (or (not name) (string-empty-p name))
3703 (package-show-package-list t nil) 3769 (package-menu--generate t t)
3704 ;; Update `tabulated-list-entries' so that it contains all 3770 (package-menu--filter-by (lambda (pkg-desc)
3705 ;; packages before searching. 3771 (string-match-p name (symbol-name
3706 (package-menu--refresh t nil) 3772 (package-desc-name pkg-desc))))
3707 (let (matched) 3773 (format "name:%s" name))))
3708 (dolist (entry tabulated-list-entries) 3774
3709 (let* ((pkg-name (package-desc-name (car entry)))) 3775(defun package-menu-filter-by-status (status)
3710 (when (string-match name (symbol-name pkg-name)) 3776 "Filter the \"*Packages*\" buffer by STATUS.
3711 (push pkg-name matched)))) 3777Display only packages with specified STATUS.
3712 (if matched 3778
3713 (package-show-package-list matched nil) 3779When called interactively, prompt for STATUS, which can be a
3714 (user-error "No packages found"))))) 3780comma-separated string. If STATUS is empty, show all packages.
3781
3782When called from Lisp, STATUS can be a string or a list of
3783strings. If STATUS is nil or the empty string, show all
3784packages."
3785 (interactive (list (completing-read "Filter by status: "
3786 '("avail-obso"
3787 "available"
3788 "built-in"
3789 "dependency"
3790 "disabled"
3791 "external"
3792 "held"
3793 "incompat"
3794 "installed"
3795 "new"
3796 "unsigned"))))
3797 (package--ensure-package-menu-mode)
3798 (if (or (not status) (string-empty-p status))
3799 (package-menu--generate t t)
3800 (package-menu--filter-by (lambda (pkg-desc)
3801 (string-match-p status (package-desc-status pkg-desc)))
3802 (format "status:%s" status))))
3803
3804(defun package-menu-filter-by-version (version predicate)
3805 "Filter the \"*Packages*\" buffer by VERSION and PREDICATE.
3806Display only packages with a matching version.
3807
3808When called interactively, prompt for one of the qualifiers `<',
3809`>' or `=', and a package version. Show only packages that has a
3810lower (`<'), equal (`=') or higher (`>') version than the
3811specified one.
3812
3813When called from Lisp, VERSION should be a version string and
3814PREDICATE should be the symbol `=', `<' or `>'.
3815
3816If VERSION is nil or the empty string, show all packages."
3817 (interactive (let ((choice (intern
3818 (char-to-string
3819 (read-char-choice
3820 "Filter by version? [Type =, <, > or q] "
3821 '(?< ?> ?= ?q))))))
3822 (if (eq choice 'q)
3823 '(quit nil)
3824 (list (read-from-minibuffer
3825 (concat "Filter by version ("
3826 (pcase choice
3827 ('= "= equal to")
3828 ('< "< less than")
3829 ('> "> greater than"))
3830 "): "))
3831 choice))))
3832 (unless (equal predicate 'quit)
3833 (if (or (not version) (string-empty-p version))
3834 (package-menu--generate t t)
3835 (package-menu--filter-by
3836 (let ((fun (pcase predicate
3837 ('= 'version-list-=)
3838 ('< 'version-list-<)
3839 ('> '(lambda (a b) (not (version-list-<= a b))))
3840 (_ (error "Unknown predicate: %s" predicate))))
3841 (ver (version-to-list version)))
3842 (lambda (pkg-desc)
3843 (funcall fun (package-desc-version pkg-desc) ver)))
3844 (format "versions:%s%s" predicate version)))))
3715 3845
3716(defun package-menu-clear-filter () 3846(defun package-menu-clear-filter ()
3717 "Clear any filter currently applied to the \"*Packages*\" buffer." 3847 "Clear any filter currently applied to the \"*Packages*\" buffer."
@@ -3760,6 +3890,7 @@ The return value is a string (or nil in case we can't find it)."
3760 (or (lm-header "package-version") 3890 (or (lm-header "package-version")
3761 (lm-header "version"))))))))) 3891 (lm-header "version")))))))))
3762 3892
3893
3763;;;; Quickstart: precompute activation actions for faster start up. 3894;;;; Quickstart: precompute activation actions for faster start up.
3764 3895
3765;; Activating packages via `package-initialize' is costly: for N installed 3896;; Activating packages via `package-initialize' is costly: for N installed
diff --git a/test/lisp/emacs-lisp/package-tests.el b/test/lisp/emacs-lisp/package-tests.el
index 7d354d6ecde..adf917aef46 100644
--- a/test/lisp/emacs-lisp/package-tests.el
+++ b/test/lisp/emacs-lisp/package-tests.el
@@ -349,43 +349,102 @@ Must called from within a `tar-mode' buffer."
349 (goto-char (point-min)) 349 (goto-char (point-min))
350 (should (re-search-forward re nil t))))))) 350 (should (re-search-forward re nil t)))))))
351 351
352
353;;; Package Menu tests
354
355(defmacro with-package-menu-test (&rest body)
356 "Set up Package Menu (\"*Packages*\") buffer for testing."
357 (declare (indent 0) (debug (([&rest form]) body)))
358 `(with-package-test ()
359 (let ((buf (package-list-packages)))
360 (unwind-protect
361 (progn ,@body)
362 (kill-buffer buf)))))
363
352(ert-deftest package-test-update-listing () 364(ert-deftest package-test-update-listing ()
353 "Ensure installed package status is updated." 365 "Ensure installed package status is updated."
354 (with-package-test () 366 (with-package-menu-test
355 (let ((buf (package-list-packages))) 367 (search-forward-regexp "^ +simple-single")
356 (search-forward-regexp "^ +simple-single") 368 (package-menu-mark-install)
357 (package-menu-mark-install) 369 (package-menu-execute)
358 (package-menu-execute) 370 (run-hooks 'post-command-hook)
359 (run-hooks 'post-command-hook) 371 (should (package-installed-p 'simple-single))
360 (should (package-installed-p 'simple-single)) 372 (switch-to-buffer "*Packages*")
361 (switch-to-buffer "*Packages*") 373 (goto-char (point-min))
362 (goto-char (point-min)) 374 (should (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+installed" nil t))
363 (should (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+installed" nil t)) 375 (goto-char (point-min))
364 (goto-char (point-min)) 376 (should-not (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+\\(available\\|new\\)" nil t))))
365 (should-not (re-search-forward "^\\s-+simple-single\\s-+1.3\\s-+\\(available\\|new\\)" nil t)) 377
366 (kill-buffer buf)))) 378(ert-deftest package-test-list-filter-by-archive ()
379 "Ensure package list is filtered correctly by archive version."
380 (with-package-menu-test
381 ;; TODO: Add another package archive to test filtering, because
382 ;; the testing environment currently only has one.
383 (package-menu-filter-by-archive "gnu")
384 (goto-char (point-min))
385 (should (looking-at "^\\s-+multi-file"))
386 (should (= (count-lines (point-min) (point-max)) 4))
387 (should-error (package-menu-filter-by-archive "non-existent archive"))))
388
389(ert-deftest package-test-list-filter-by-keyword ()
390 "Ensure package list is filtered correctly by package keyword."
391 (with-package-menu-test
392 (package-menu-filter-by-keyword "frobnicate")
393 (goto-char (point-min))
394 (should (re-search-forward "^\\s-+simple-single" nil t))
395 (should (= (count-lines (point-min) (point-max)) 1))
396 (should-error (package-menu-filter-by-keyword "non-existent-keyword"))))
367 397
368(ert-deftest package-test-list-filter-by-name () 398(ert-deftest package-test-list-filter-by-name ()
369 "Ensure package list is filtered correctly by package name." 399 "Ensure package list is filtered correctly by package name."
370 (with-package-test () 400 (with-package-menu-test ()
371 (let ((buf (package-list-packages))) 401 (package-menu-filter-by-name "tetris")
372 (package-menu-filter-by-name "tetris") 402 (goto-char (point-min))
373 (goto-char (point-min)) 403 (should (re-search-forward "^\\s-+tetris" nil t))
374 (should (re-search-forward "^\\s-+tetris" nil t)) 404 (should (= (count-lines (point-min) (point-max)) 1))))
375 (should (= (count-lines (point-min) (point-max)) 1)) 405
376 (kill-buffer buf)))) 406(ert-deftest package-test-list-filter-by-status ()
407 "Ensure package list is filtered correctly by package status."
408 (with-package-menu-test
409 (package-menu-filter-by-status "available")
410 (goto-char (point-min))
411 (should (re-search-forward "^\\s-+multi-file" nil t))
412 (should (= (count-lines (point-min) (point-max)) 4))
413 ;; No installed packages in default environment.
414 (should-error (package-menu-filter-by-status "installed"))))
415
416(ert-deftest package-test-list-filter-by-version ()
417 (with-package-menu-test
418 (should-error (package-menu-filter-by-version "1.1" 'unknown-symbol))) )
419
420(defun package-test-filter-by-version (version predicate name)
421 (with-package-menu-test
422 (package-menu-filter-by-version version predicate)
423 (goto-char (point-min))
424 ;; We just check that the given package is included in the
425 ;; listing. One could be more ambitious.
426 (should (re-search-forward name))))
427
428(ert-deftest package-test-list-filter-by-version-= ()
429 "Ensure package list is filtered correctly by package version (=)."
430 (package-test-filter-by-version "1.1" '= "^\\s-+simple-two-depend"))
431
432(ert-deftest package-test-list-filter-by-version-< ()
433 "Ensure package list is filtered correctly by package version (<)."
434 (package-test-filter-by-version "1.2" '< "^\\s-+simple-two-depend"))
435
436(ert-deftest package-test-list-filter-by-version-> ()
437 "Ensure package list is filtered correctly by package version (>)."
438 (package-test-filter-by-version "1.0" '> "^\\s-+simple-two-depend"))
377 439
378(ert-deftest package-test-list-clear-filter () 440(ert-deftest package-test-list-clear-filter ()
379 "Ensure package list filter is cleared correctly." 441 "Ensure package list filter is cleared correctly."
380 (with-package-test () 442 (with-package-menu-test
381 (let ((buf (package-list-packages))) 443 (let ((num-packages (count-lines (point-min) (point-max))))
382 (let ((num-packages (count-lines (point-min) (point-max)))) 444 (package-menu-filter-by-name "tetris")
383 (should (> num-packages 1)) 445 (should (= (count-lines (point-min) (point-max)) 1))
384 (package-menu-filter-by-name "tetris") 446 (package-menu-clear-filter)
385 (should (= (count-lines (point-min) (point-max)) 1)) 447 (should (= (count-lines (point-min) (point-max)) num-packages)))))
386 (package-menu-clear-filter)
387 (should (= (count-lines (point-min) (point-max)) num-packages)))
388 (kill-buffer buf))))
389 448
390(ert-deftest package-test-update-archives () 449(ert-deftest package-test-update-archives ()
391 "Test updating package archives." 450 "Test updating package archives."