From: David Bremner Date: Mon, 23 Nov 2015 12:40:40 +0000 (-0400) Subject: Merge branch 'release' X-Git-Tag: 0.22_rc0~97 X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=commitdiff_plain;h=bceb6516cee170d3ad4b620826d48e90f05a12b1;hp=4c7a592d49266d588e717c232bc7aacbe4e16bfb Merge branch 'release' Merge bugfixes applied directly to release --- diff --git a/devel/release-checks.sh b/devel/release-checks.sh index 8604a9f7..5a7578b8 100755 --- a/devel/release-checks.sh +++ b/devel/release-checks.sh @@ -175,6 +175,21 @@ case $news_date in append_emsg "Date '$news_date' in NEWS file is not in format (yyyy-mm-dd)" esac +year=`exec date +%Y` +echo -n "Checking that copyright in documentation contains 2009-$year... " +# Read the value of variable `copyright' defined in 'doc/conf.py'. +# As __file__ is not defined when python command is given from command line, +# it is defined before contents of 'doc/conf.py' (which dereferences __file__) +# is executed. +copyrightline=`exec python -c "with open('doc/conf.py') as cf: __file__ = ''; exec(cf.read()); print(copyright)"` +case $copyrightline in + *2009-$year*) + echo Yes. ;; + *) + echo No. + append_emsg "The copyright in doc/conf.py line '$copyrightline' does not contain '2009-$year'" +esac + if [ -n "$emsgs" ] then echo diff --git a/emacs/Makefile.local b/emacs/Makefile.local index 1109cfa6..2d6aedbd 100644 --- a/emacs/Makefile.local +++ b/emacs/Makefile.local @@ -20,6 +20,7 @@ emacs_sources := \ $(dir)/notmuch-print.el \ $(dir)/notmuch-version.el \ $(dir)/notmuch-jump.el \ + $(dir)/notmuch-company.el $(dir)/notmuch-version.el: $(dir)/Makefile.local version.stamp $(dir)/notmuch-version.el: $(srcdir)/$(dir)/notmuch-version.el.tmpl @@ -52,6 +53,10 @@ $(dir)/.eldeps: $(dir)/Makefile.local $(dir)/make-deps.el $(emacs_sources) $(dir)/.eldeps.x: $(dir)/.eldeps @cmp -s $^ $@ || cp $^ $@ -include $(dir)/.eldeps.x + +# Add the one dependency make-deps.el does not have visibility to. +$(dir)/notmuch-lib.elc: $(dir)/notmuch-version.elc + endif CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp $(dir)/.eldeps.x diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index fde3c1b2..49e24023 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -20,14 +20,21 @@ ;; Authors: David Edmondson (require 'message) - +(require 'notmuch-parser) +(require 'notmuch-lib) +(require 'notmuch-company) ;; +(declare-function company-manual-begin "company") -(defcustom notmuch-address-command "notmuch-addresses" +(defcustom notmuch-address-command 'internal "The command which generates possible addresses. It must take a single argument and output a list of possible matches, one per -line." - :type 'string +line. The default value of `internal' uses built-in address +completion." + :type '(radio + (const :tag "Use internal address completion" internal) + (const :tag "Disable address completion" nil) + (string :tag "Use external completion command" "notmuch-addresses")) :group 'notmuch-send :group 'notmuch-external) @@ -42,53 +49,105 @@ to know how address selection is made by default." :group 'notmuch-send :group 'notmuch-external) +(defvar notmuch-address-last-harvest 0 + "Time of last address harvest") + +(defvar notmuch-address-completions (make-hash-table :test 'equal) + "Hash of email addresses for completion during email composition. + This variable is set by calling `notmuch-address-harvest'.") + +(defvar notmuch-address-full-harvest-finished nil + "t indicates that full completion address harvesting has been +finished") + (defun notmuch-address-selection-function (prompt collection initial-input) "Call (`completing-read' PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)" (completing-read prompt collection nil nil initial-input 'notmuch-address-history)) -(defvar notmuch-address-message-alist-member - '("^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):" - . notmuch-address-expand-name)) +(defvar notmuch-address-completion-headers-regexp + "^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):") (defvar notmuch-address-history nil) (defun notmuch-address-message-insinuate () - (unless (memq notmuch-address-message-alist-member message-completion-alist) - (setq message-completion-alist - (push notmuch-address-message-alist-member message-completion-alist)))) + (message "calling notmuch-address-message-insinuate is no longer needed")) + +(defcustom notmuch-address-use-company t + "If available, use company mode for address completion" + :type 'boolean + :group 'notmuch-send) + +(defun notmuch-address-setup () + (let* ((use-company (and notmuch-address-use-company + (eq notmuch-address-command 'internal) + (require 'company nil t))) + (pair (cons notmuch-address-completion-headers-regexp + (if use-company + #'company-manual-begin + #'notmuch-address-expand-name)))) + (when use-company + (notmuch-company-setup)) + (unless (memq pair message-completion-alist) + (setq message-completion-alist + (push pair message-completion-alist))))) + +(defun notmuch-address-matching (substring) + "Returns a list of completion candidates matching SUBSTRING. +The candidates are taken from `notmuch-address-completions'." + (let ((candidates) + (re (regexp-quote substring))) + (maphash (lambda (key val) + (when (string-match re key) + (push key candidates))) + notmuch-address-completions) + candidates)) (defun notmuch-address-options (original) - (process-lines notmuch-address-command original)) + "Returns a list of completion candidates. Uses either +elisp-based implementation or older implementation requiring +external commands." + (cond + ((eq notmuch-address-command 'internal) + (when (not notmuch-address-full-harvest-finished) + ;; First, run quick synchronous harvest based on what the user + ;; entered so far + (notmuch-address-harvest (format "to:%s*" original) t)) + (prog1 (notmuch-address-matching original) + ;; Then start the (potentially long-running) full asynchronous harvest if necessary + (notmuch-address-harvest-trigger))) + (t + (process-lines notmuch-address-command original)))) (defun notmuch-address-expand-name () - (let* ((end (point)) - (beg (save-excursion - (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") - (goto-char (match-end 0)) - (point))) - (orig (buffer-substring-no-properties beg end)) - (completion-ignore-case t) - (options (with-temp-message "Looking for completion candidates..." - (notmuch-address-options orig))) - (num-options (length options)) - (chosen (cond - ((eq num-options 0) - nil) - ((eq num-options 1) - (car options)) - (t - (funcall notmuch-address-selection-function - (format "Address (%s matches): " num-options) - (cdr options) (car options)))))) - (if chosen - (progn - (push chosen notmuch-address-history) - (delete-region beg end) - (insert chosen)) - (message "No matches.") - (ding)))) + (when notmuch-address-command + (let* ((end (point)) + (beg (save-excursion + (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") + (goto-char (match-end 0)) + (point))) + (orig (buffer-substring-no-properties beg end)) + (completion-ignore-case t) + (options (with-temp-message "Looking for completion candidates..." + (notmuch-address-options orig))) + (num-options (length options)) + (chosen (cond + ((eq num-options 0) + nil) + ((eq num-options 1) + (car options)) + (t + (funcall notmuch-address-selection-function + (format "Address (%s matches): " num-options) + (cdr options) (car options)))))) + (if chosen + (progn + (push chosen notmuch-address-history) + (delete-region beg end) + (insert chosen)) + (message "No matches.") + (ding))))) ;; Copied from `w3m-which-command'. (defun notmuch-address-locate-command (command) @@ -109,10 +168,82 @@ to know how address selection is made by default." (not (file-directory-p bin)))) (throw 'found-command bin)))))))) -;; If we can find the program specified by `notmuch-address-command', -;; insinuate ourselves into `message-mode'. -(when (notmuch-address-locate-command notmuch-address-command) - (notmuch-address-message-insinuate)) +(defun notmuch-address-harvest-addr (result) + (let ((name-addr (plist-get result :name-addr))) + (puthash name-addr t notmuch-address-completions))) + +(defun notmuch-address-harvest-handle-result (obj) + (notmuch-address-harvest-addr obj)) + +(defun notmuch-address-harvest-filter (proc string) + (when (buffer-live-p (process-buffer proc)) + (with-current-buffer (process-buffer proc) + (save-excursion + (goto-char (point-max)) + (insert string)) + (notmuch-sexp-parse-partial-list + 'notmuch-address-harvest-handle-result (process-buffer proc))))) + +(defvar notmuch-address-harvest-procs '(nil . nil) + "The currently running harvests. + +The car is a partial harvest, and the cdr is a full harvest") + +(defun notmuch-address-harvest (&optional filter-query synchronous callback) + "Collect addresses completion candidates. It queries the +notmuch database for all messages sent by the user optionally +matching FILTER-QUERY (if not nil). It collects the destination +addresses from those messages and stores them in +`notmuch-address-completions'. Address harvesting may take some +time so the address collection runs asynchronously unless +SYNCHRONOUS is t. In case of asynchronous execution, CALLBACK is +called when harvesting finishes." + (let* ((from-me-query (mapconcat (lambda (x) (concat "from:" x)) (notmuch-user-emails) " or ")) + (query (if filter-query + (format "(%s) and (%s)" from-me-query filter-query) + from-me-query)) + (args `("address" "--format=sexp" "--format-version=2" + "--output=recipients" + "--deduplicate=address" + ,query))) + (if synchronous + (mapc #'notmuch-address-harvest-addr + (apply 'notmuch-call-notmuch-sexp args)) + ;; Asynchronous + (let* ((current-proc (if filter-query + (car notmuch-address-harvest-procs) + (cdr notmuch-address-harvest-procs))) + (proc-name (format "notmuch-address-%s-harvest" + (if filter-query "partial" "full"))) + (proc-buf (concat " *" proc-name "*"))) + ;; Kill any existing process + (when current-proc + (kill-buffer (process-buffer current-proc))) ; this also kills the process + + (setq current-proc + (apply 'notmuch-start-notmuch proc-name proc-buf + callback ; process sentinel + args)) + (set-process-filter current-proc 'notmuch-address-harvest-filter) + (set-process-query-on-exit-flag current-proc nil) + (if filter-query + (setcar notmuch-address-harvest-procs current-proc) + (setcdr notmuch-address-harvest-procs current-proc))))) + ;; return value + nil) + +(defun notmuch-address-harvest-trigger () + (let ((now (float-time))) + (when (> (- now notmuch-address-last-harvest) 86400) + (setq notmuch-address-last-harvest now) + (notmuch-address-harvest nil nil + (lambda (proc event) + ;; If harvest fails, we want to try + ;; again when the trigger is next + ;; called + (if (string= event "finished\n") + (setq notmuch-address-full-harvest-finished t) + (setq notmuch-address-last-harvest 0))))))) ;; diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el new file mode 100644 index 00000000..add3161b --- /dev/null +++ b/emacs/notmuch-company.el @@ -0,0 +1,86 @@ +;; notmuch-company.el --- Mail address completion for notmuch via company-mode -*- lexical-binding: t -*- + +;; Authors: Trevor Jim +;; Michal Sojka +;; +;; Keywords: mail, completion + +;; 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 . + +;;; Commentary: + +;; To enable this, install company mode (https://company-mode.github.io/) +;; +;; NB company-minimum-prefix-length defaults to 3 so you don't get +;; completion unless you type 3 characters + +;;; Code: + +(eval-when-compile (require 'cl)) + +(defvar notmuch-company-last-prefix nil) +(make-variable-buffer-local 'notmuch-company-last-prefix) +(declare-function company-begin-backend "company") +(declare-function company-grab "company") +(declare-function company-mode "company") +(declare-function company-manual-begin "company") +(defvar company-backends) + +(declare-function notmuch-address-harvest "notmuch-address") +(declare-function notmuch-address-harvest-trigger "notmuch-address") +(declare-function notmuch-address-matching "notmuch-address") +(defvar notmuch-address-full-harvest-finished) +(defvar notmuch-address-completion-headers-regexp) + +;;;###autoload +(defun notmuch-company-setup () + (company-mode) + (make-local-variable 'company-backends) + (setq company-backends '(notmuch-company))) + +;;;###autoload +(defun notmuch-company (command &optional arg &rest _ignore) + "`company-mode' completion back-end for `notmuch'." + (interactive (list 'interactive)) + (require 'company) + (let ((case-fold-search t) + (completion-ignore-case t)) + (case command + (interactive (company-begin-backend 'notmuch-company)) + (prefix (and (derived-mode-p 'message-mode) + (looking-back (concat notmuch-address-completion-headers-regexp ".*") + (line-beginning-position)) + (setq notmuch-company-last-prefix (company-grab "[:,][ \t]*\\(.*\\)" 1 (point-at-bol))))) + (candidates (cond + (notmuch-address-full-harvest-finished + ;; Update harvested addressed from time to time + (notmuch-address-harvest-trigger) + (notmuch-address-matching arg)) + (t + (cons :async + (lambda (callback) + ;; First run quick asynchronous harvest based on what the user entered so far + (notmuch-address-harvest + (format "to:%s*" arg) nil + (lambda (_proc _event) + (funcall callback (notmuch-address-matching arg)) + ;; Then start the (potentially long-running) full asynchronous harvest if necessary + (notmuch-address-harvest-trigger)))))))) + (match (if (string-match notmuch-company-last-prefix arg) + (match-end 0) + 0)) + (no-cache t)))) + + +(provide 'notmuch-company) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index dedcbeb8..89c01a57 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -232,6 +232,9 @@ on the command line, and then retry your notmuch command"))) "Return the user.other_email value (as a list) from the notmuch configuration." (split-string (notmuch-config-get "user.other_email") "\n" t)) +(defun notmuch-user-emails () + (cons (notmuch-user-primary-email) (notmuch-user-other-email))) + (defun notmuch-poll () "Run \"notmuch new\" or an external script to import mail. diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 57465b20..466edd7e 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -269,7 +269,11 @@ Note that these functions use `mail-citation-hook' if that is non-nil." (set-buffer-modified-p nil)) (define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]" - "Notmuch message composition mode. Mostly like `message-mode'") + "Notmuch message composition mode. Mostly like `message-mode'" + (when notmuch-address-command + (notmuch-address-setup))) + +(put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify) (define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit) (define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 49fd198a..3345878f 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -353,8 +353,6 @@ operation on the contents of the current buffer." 'message-header-cc) ((looking-at "[Ss]ubject:") 'message-header-subject) - ((looking-at "[Ff]rom:") - 'message-header-from) (t 'message-header-other)))) @@ -1880,12 +1878,15 @@ to show, nil otherwise." "View the original source of the current message." (interactive) (let* ((id (notmuch-show-get-message-id)) - (buf (get-buffer-create (concat "*notmuch-raw-" id "*")))) - (let ((coding-system-for-read 'no-conversion)) - (call-process notmuch-command nil buf nil "show" "--format=raw" id)) + (buf (get-buffer-create (concat "*notmuch-raw-" id "*"))) + (inhibit-read-only t)) (switch-to-buffer buf) + (erase-buffer) + (let ((coding-system-for-read 'no-conversion)) + (call-process notmuch-command nil t nil "show" "--format=raw" id)) (goto-char (point-min)) (set-buffer-modified-p nil) + (setq buffer-read-only t) (view-buffer buf 'kill-buffer-if-not-modified))) (put 'notmuch-show-pipe-message 'notmuch-doc diff --git a/lib/database.cc b/lib/database.cc index 5e86955d..3b342f13 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1635,6 +1635,9 @@ notmuch_database_begin_atomic (notmuch_database_t *notmuch) notmuch->atomic_nesting > 0) goto DONE; + if (notmuch_database_needs_upgrade(notmuch)) + return NOTMUCH_STATUS_UPGRADE_REQUIRED; + try { (static_cast (notmuch->xapian_db))->begin_transaction (false); } catch (const Xapian::Error &error) { diff --git a/lib/index.cc b/lib/index.cc index e81aa819..f166aefd 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -377,7 +377,8 @@ _index_mime_part (notmuch_message_t *message, disposition = g_mime_object_get_content_disposition (part); if (disposition && - strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0) + strcasecmp (g_mime_content_disposition_get_disposition (disposition), + GMIME_DISPOSITION_ATTACHMENT) == 0) { const char *filename = g_mime_part_get_filename (GMIME_PART (part)); diff --git a/lib/notmuch.h b/lib/notmuch.h index 85b56bf1..310a8b8a 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1752,7 +1752,7 @@ notmuch_filenames_t * notmuch_directory_get_child_files (notmuch_directory_t *directory); /** - * Get a notmuch_filenams_t iterator listing all the filenames of + * Get a notmuch_filenames_t iterator listing all the filenames of * sub-directories in the database within the given directory. * * The returned filenames will be the basename-entries only (not diff --git a/notmuch-emacs-mua b/notmuch-emacs-mua index 016fa126..4404cd7c 100755 --- a/notmuch-emacs-mua +++ b/notmuch-emacs-mua @@ -30,8 +30,8 @@ escape () printf -v $2 '%s' "${__escape_arg__//\"/\\\"}" } -EMACS=${EMACS-emacs} -EMACSCLIENT=${EMACSCLIENT-emacsclient} +EMACS=${EMACS:-emacs} +EMACSCLIENT=${EMACSCLIENT:-emacsclient} PRINT_ONLY= NO_WINDOW= diff --git a/notmuch-reply.c b/notmuch-reply.c index 13571429..6df54fc9 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -80,7 +80,8 @@ format_part_reply (mime_node_t *node) show_text_part_content (node->part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY); g_object_unref(stream_stdout); } else if (disposition && - strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0) { + strcasecmp (g_mime_content_disposition_get_disposition (disposition), + GMIME_DISPOSITION_ATTACHMENT) == 0) { const char *filename = g_mime_part_get_filename (GMIME_PART (node->part)); printf ("Attachment: %s (%s)\n", filename, g_mime_content_type_to_string (content_type)); diff --git a/notmuch-show.c b/notmuch-show.c index 5a83c605..87e52bbc 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -456,7 +456,8 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node, g_mime_part_get_filename (GMIME_PART (node->part)) : NULL; if (disposition && - strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0) + strcasecmp (g_mime_content_disposition_get_disposition (disposition), + GMIME_DISPOSITION_ATTACHMENT) == 0) part_type = "attachment"; else part_type = "part"; diff --git a/notmuch-tag.c b/notmuch-tag.c index c020cb6f..0d153282 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -237,10 +237,6 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[]) fprintf (stderr, "Can't specify both cmdline and stdin!\n"); return EXIT_FAILURE; } - if (remove_all) { - fprintf (stderr, "Can't specify both --remove-all and --batch\n"); - return EXIT_FAILURE; - } } else { tag_ops = tag_op_list_create (config); if (tag_ops == NULL) { diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh index 821d3933..8adcabce 100755 --- a/test/T150-tagging.sh +++ b/test/T150-tagging.sh @@ -38,6 +38,17 @@ test_expect_equal "$output" "\ thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One () thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (tag5 tag6 unread)" +test_begin_subtest "Remove all with batch" +notmuch tag +tag1 One +notmuch tag --remove-all --batch < OUTPUT notmuch restore --format=batch-tag < backup.tags test_expect_equal_file batch.expected OUTPUT +test_begin_subtest "--batch --input --remove-all" +notmuch dump --format=batch-tag > backup.tags +notmuch tag +foo +bar -- One +notmuch tag +tag7 -- Two +notmuch tag --batch --input=batch.in --remove-all +notmuch search \* | notmuch_search_sanitize > OUTPUT +notmuch restore --format=batch-tag < backup.tags +cat > batch_removeall.expected < EXPECTED notmuch tag --batch < ${MAIL_DIR}/content-disposition +Return-path: +Envelope-to: david@tethera.net +Delivery-date: Sun, 04 Oct 2015 09:16:03 -0300 +Received: from gitolite.debian.net ([87.98.215.224]) + by yantan.tethera.net with esmtps (TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128) + (Exim 4.80) + (envelope-from ) + id 1ZiiCx-0007iz-RK + for david@tethera.net; Sun, 04 Oct 2015 09:16:03 -0300 +Received: from remotemail by gitolite.debian.net with local (Exim 4.80) + (envelope-from ) + id 1ZiiC8-0002Rz-Uf; Sun, 04 Oct 2015 12:15:12 +0000 +Received: (nullmailer pid 28621 invoked by uid 1000); Sun, 04 Oct 2015 + 12:14:53 -0000 +From: David Bremner +To: David Bremner +Subject: test attachment +User-Agent: Notmuch/0.20.2+93~g33c8777 (http://notmuchmail.org) Emacs/24.5.1 + (x86_64-pc-linux-gnu) +Date: Sun, 04 Oct 2015 09:14:53 -0300 +Message-ID: <87io6m96f6.fsf@zancas.localnet> +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="=-=-=" + +--=-=-= +Content-Type: text/plain +Content-Disposition: ATTACHMENT; filename=hello.txt +Content-Description: this is a very exciting file + +hello + +--=-=-= +Content-Type: text/plain + + +world + +--=-=-=-- + +EOF +NOTMUCH_NEW + +cat < EXPECTED +attachment +inbox +unread +EOF + +notmuch search --output=tags id:87io6m96f6.fsf@zancas.localnet > OUTPUT +test_expect_equal_file EXPECTED OUTPUT test_done diff --git a/test/T580-thread-search.sh b/test/T580-thread-search.sh new file mode 100755 index 00000000..6f7106db --- /dev/null +++ b/test/T580-thread-search.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2015 David Bremner +# + +test_description='test of searching by thread-id' + +. ./test-lib.sh || exit 1 + +add_email_corpus + +test_begin_subtest "Every message is found in exactly one thread" + +count=0 +success=0 +for id in $(notmuch search --output=messages '*'); do + count=$((count +1)) + matches=$(notmuch search --output=threads "$id" | wc -l) + if [ "$matches" = 1 ]; then + success=$((success + 1)) + fi +done + +test_expect_equal "$count" "$success" + +test_begin_subtest "roundtripping message-ids via thread-ids" + +count=0 +success=0 +for id in $(notmuch search --output=messages '*'); do + count=$((count +1)) + thread=$(notmuch search --output=threads "$id") + matched=$(notmuch search --output=messages "$thread" | grep "$id") + if [ "$matched" = "$id" ]; then + success=$((success + 1)) + fi +done + +test_expect_equal "$count" "$success" + + +test_done