[PATCH v2] emacs: bad regexp @ `notmuch-search-process-filter'
authorPieter Praet <pieter@praet.org>
Mon, 11 Jul 2011 20:43:13 +0000 (22:43 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:39:05 +0000 (09:39 -0800)
f7/b93dff3236b198b27cc2bc190f5c4836f40a69 [new file with mode: 0644]

diff --git a/f7/b93dff3236b198b27cc2bc190f5c4836f40a69 b/f7/b93dff3236b198b27cc2bc190f5c4836f40a69
new file mode 100644 (file)
index 0000000..9c41d47
--- /dev/null
@@ -0,0 +1,518 @@
+Return-Path: <pieter@praet.org>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 24935431FD0\r
+       for <notmuch@notmuchmail.org>; Mon, 11 Jul 2011 13:43:26 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id koDr0Kmwy0I5 for <notmuch@notmuchmail.org>;\r
+       Mon, 11 Jul 2011 13:43:23 -0700 (PDT)\r
+Received: from mail-wy0-f181.google.com (mail-wy0-f181.google.com\r
+       [74.125.82.181]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 6E8AA431FB6\r
+       for <notmuch@notmuchmail.org>; Mon, 11 Jul 2011 13:43:23 -0700 (PDT)\r
+Received: by wyh22 with SMTP id 22so3208041wyh.26\r
+       for <notmuch@notmuchmail.org>; Mon, 11 Jul 2011 13:43:22 -0700 (PDT)\r
+Received: by 10.216.235.134 with SMTP id u6mr3358534weq.99.1310417001892;\r
+       Mon, 11 Jul 2011 13:43:21 -0700 (PDT)\r
+Received: from localhost ([109.131.120.63])\r
+       by mx.google.com with ESMTPS id gb1sm10317138wbb.54.2011.07.11.13.43.18\r
+       (version=TLSv1/SSLv3 cipher=OTHER);\r
+       Mon, 11 Jul 2011 13:43:20 -0700 (PDT)\r
+From: Pieter Praet <pieter@praet.org>\r
+To: Austin Clements <amdragon@MIT.EDU>\r
+Subject: [PATCH v2] emacs: bad regexp @ `notmuch-search-process-filter'\r
+Date: Mon, 11 Jul 2011 22:43:13 +0200\r
+Message-Id: <1310416993-31031-1-git-send-email-pieter@praet.org>\r
+X-Mailer: git-send-email 1.7.5.4\r
+In-Reply-To: <20110705214234.GA15360@mit.edu>\r
+References: <20110705214234.GA15360@mit.edu>\r
+Cc: Notmuch Mail <notmuch@notmuchmail.org>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Mon, 11 Jul 2011 20:43:26 -0000\r
+\r
+\r
+TL;DR: I can haz regex pl0x?\r
+\r
+\r
+I've updated the regex a bit to prevent it from choking on the whole\r
+"parens in subject vs. parens around tags vs. parens around matching\r
+Message-Id's" deal, but it still causes errors in the search buffer due\r
+to the list of Message-Id's being cut off at a seemingly arbitrary point,\r
+and this for *different* results on pretty much every refresh.\r
+\r
+All tests pass, except for the ones I've mentioned before [1] (which\r
+don't test anything tagging-related, just `notmuch-show'), though even\r
+those fail to fail consistently :<. This variability can't be related to\r
+residual files, as I always run the test suite like this:\r
+\r
+  rm -rf /dev/shm/notmuch/* && make test OPTIONS="--root=/dev/shm/notmuch"\r
+\r
+\r
+So, to keep this on track for 0.7 whilst keeping myself from having to\r
+send spammerific amounts of patches, I've squashed the whole deal in\r
+this single patch. Don't worry though, it's all quite grokkable:\r
+\r
+\r
+notmuch-search.c\r
+\r
+- Make `notmuch search' *only* append results with their Message-Id's\r
+  when supplied with the "--output=summary-ids" option, to prevent a\r
+  slew of failing tests obscuring the relevant ones.\r
+\r
+\r
+emacs/notmuch.el\r
+\r
+- Make `notmuch-search' run the notmuch binary with the "--output=summary-ids"\r
+  option, to receive search results appended with their lists of Message-Id's.\r
+\r
+- Update the regex @ `notmuch-search-process-filter' to include a new atom,\r
+  which matches the list of Message-Id's at the end of every search result\r
+  returned by the notmuch binary.\r
+  To each individual result in the search buffer, this matched string is\r
+  added as a text property called `notmuch-search-msgids'.\r
+\r
+- Add 2 functions to return the `notmuch-search-msgids' property of search\r
+  results: `notmuch-search-find-msgids', `notmuch-search-find-msgids-region'.\r
+\r
+- Add a function to stash the Message-Id's of (a region of) search results:\r
+  `notmuch-search-stash-msgids', bound to "m" in `notmuch-search-stash-map'.\r
+  Mainly for testing purposes.\r
+\r
+- Merge `notmuch-call-notmuch-process' into `notmuch-tag':\r
+  `notmuch-tag' was the only thing making use of `notmuch-call-notmuch-process',\r
+  and the extra layer of abstraction would complicate making `notmuch-tag' send\r
+  arguments on stdin (see next point).\r
+\r
+- Make `notmuch-tag' send its query string on stdin:\r
+  Instead of providing the query string as a (potentially very long) command\r
+  line argument, `notmuch-tag' now dumps it into a temporary buffer, which\r
+  `call-process-region' sends to `notmuch-command' on stdin.\r
+  This is needed to circumvent "$command: arg list too long" errors due\r
+  to command line argument length limitations imposed by the kernel (ARG_MAX).\r
+\r
+- Fix the actual bug(s) this patch series is intended to address by making\r
+  the tagging functions procure their targets from the `notmuch-search-msgids'\r
+  property with `notmuch-search-find-msgids-region', instead of using x:\r
+  - `notmuch-search-add-tag-region'    (x = `notmuch-search-find-thread-id-region')\r
+  - `notmuch-search-remove-tag-region' (x = `notmuch-search-find-thread-id-region')\r
+  - `notmuch-search-remove-tag'        (x = `notmuch-search-find-thread-id-region')\r
+  - `notmuch-search-operate-all'       (x = `notmuch-search-query-string')\r
+\r
+\r
+test/emacs, test/emacs-search-operate-all, test/notmuch-test\r
+\r
+- Expand the test suite to also cover:\r
+  - Tagging messages with `notmuch-search-operate-all'.\r
+  - Tagging messages to which a reply is sent. For this I also needed to\r
+    correct the title of an existing test, and add a test for sending\r
+    replies from within Emacs.\r
+\r
+\r
+Side note:\r
+  After playing around with Austin's new patch for a bit, I've come to the\r
+  conclusion that making a clear distinction between matched and unmatched\r
+  messages in the binary's output *is* the way to go, but in the case of\r
+  `notmuch-search-operate-all', this capability shouldn't be leveraged.\r
+\r
+  The way `notmuch-search-operate-all' currently works, i.e. operate on\r
+  matched *messages* instead of matched *threads*, is not only counter-\r
+  intuitive (same as it would be for `notmuch-search-add-tag' and\r
+  `notmuch-search-remove-tag' [2]), but semantically incorrect as well:\r
+  Its name implies operating on *all* that is visible in the current\r
+  buffer, instead of only a subset.\r
+\r
+\r
+Peace\r
+\r
+[1] id:"1310307099-25197-1-git-send-email-pieter@praet.org"\r
+[2] id:"e8c5fbf4-4dfa-461a-8f5c-6c696291a270@email.android.com"\r
+\r
+\r
+Signed-off-by: Pieter Praet <pieter@praet.org>\r
+---\r
+ emacs/notmuch.el              |   93 ++++++++++++++++++++++++++++-------------\r
+ notmuch-search.c              |    6 ++-\r
+ test/emacs                    |   49 +++++++++++++++++++++-\r
+ test/emacs-search-operate-all |   29 +++++++++++++\r
+ test/notmuch-test             |    1 +\r
+ 5 files changed, 147 insertions(+), 31 deletions(-)\r
+ create mode 100755 test/emacs-search-operate-all\r
+\r
+diff --git a/emacs/notmuch.el b/emacs/notmuch.el\r
+index f11ec24..400adcc 100644\r
+--- a/emacs/notmuch.el\r
++++ b/emacs/notmuch.el\r
+@@ -226,6 +226,7 @@ For a mouse binding, return nil."\r
+ (defvar notmuch-search-stash-map\r
+   (let ((map (make-sparse-keymap)))\r
+     (define-key map "i" 'notmuch-search-stash-thread-id)\r
++    (define-key map "m" 'notmuch-search-stash-msgids)\r
+     map)\r
+   "Submap for stash commands")\r
+ (fset 'notmuch-search-stash-map notmuch-search-stash-map)\r
+@@ -235,6 +236,19 @@ For a mouse binding, return nil."\r
+   (interactive)\r
+   (notmuch-common-do-stash (notmuch-search-find-thread-id)))\r
\r
++(defun notmuch-search-stash-msgids ()\r
++  "Copy all Message-ID's in currently selected thread(s) to kill-ring."\r
++  (interactive)\r
++  (save-excursion\r
++    (if (region-active-p)\r
++        (let* ((beg (region-beginning))\r
++               (end (region-end)))\r
++          (notmuch-common-do-stash\r
++           (mapconcat 'identity\r
++                      (notmuch-search-find-msgids-region beg end)\r
++                      " or ")))\r
++      (notmuch-common-do-stash (notmuch-search-find-msgids)))))\r
++\r
+ (defvar notmuch-search-query-string)\r
+ (defvar notmuch-search-target-thread)\r
+ (defvar notmuch-search-target-line)\r
+@@ -402,6 +416,14 @@ Complete list of currently available key bindings:\r
+   "Return a list of threads for the current region"\r
+   (notmuch-search-properties-in-region 'notmuch-search-thread-id beg end))\r
\r
++(defun notmuch-search-find-msgids ()\r
++  "Return all Message-Id's for the current thread"\r
++  (get-text-property (point) 'notmuch-search-msgids))\r
++\r
++(defun notmuch-search-find-msgids-region (beg end)\r
++  "Return a list of all Message-Id's for the threads in the current region"\r
++  (notmuch-search-properties-in-region 'notmuch-search-msgids beg end))\r
++\r
+ (defun notmuch-search-find-authors ()\r
+   "Return the authors for the current thread"\r
+   (get-text-property (point) 'notmuch-search-authors))\r
+@@ -448,23 +470,6 @@ Complete list of currently available key bindings:\r
+   (let ((message-id (notmuch-search-find-thread-id)))\r
+     (notmuch-mua-new-reply message-id prompt-for-sender)))\r
\r
+-(defun notmuch-call-notmuch-process (&rest args)\r
+-  "Synchronously invoke \"notmuch\" with the given list of arguments.\r
+-\r
+-Output from the process will be presented to the user as an error\r
+-and will also appear in a buffer named \"*Notmuch errors*\"."\r
+-  (let ((error-buffer (get-buffer-create "*Notmuch errors*")))\r
+-    (with-current-buffer error-buffer\r
+-      (erase-buffer))\r
+-    (if (eq (apply 'call-process notmuch-command nil error-buffer nil args) 0)\r
+-      (point)\r
+-      (progn\r
+-      (with-current-buffer error-buffer\r
+-        (let ((beg (point-min))\r
+-              (end (- (point-max) 1)))\r
+-          (error (buffer-substring beg end))\r
+-          ))))))\r
+-\r
+ (defun notmuch-tag (query &rest tags)\r
+   "Add/remove tags in TAGS to messages matching QUERY.\r
\r
+@@ -476,8 +481,32 @@ messages instead of running (notmuch-call-notmuch-process \"tag\" ..)\r
+ directly, so that hooks specified in notmuch-before-tag-hook and\r
+ notmuch-after-tag-hook will be run."\r
+   (run-hooks 'notmuch-before-tag-hook)\r
+-  (apply 'notmuch-call-notmuch-process\r
+-       (append (list "tag") tags (list "--" query)))\r
++\r
++  (let ((query-buffer (get-buffer-create "*Notmuch query*"))\r
++        (error-buffer (get-buffer-create "*Notmuch errors*")))\r
++    (with-current-buffer error-buffer\r
++      (erase-buffer))\r
++\r
++    (with-current-buffer query-buffer\r
++        (erase-buffer)\r
++        (insert query)\r
++\r
++    (if (eq\r
++       (apply 'call-process-region\r
++              (append\r
++               (list (point-min) (point-max) notmuch-command nil error-buffer nil)\r
++               (list "tag" "--stdin") tags))\r
++       0)\r
++      (point)\r
++      (progn\r
++      (with-current-buffer error-buffer\r
++        (let ((beg (point-min))\r
++              (end (- (point-max) 1)))\r
++          (error (buffer-substring beg end))\r
++          )))))\r
++\r
++    (kill-buffer query-buffer))\r
++\r
+   (run-hooks 'notmuch-after-tag-hook))\r
\r
+ (defcustom notmuch-before-tag-hook nil\r
+@@ -541,7 +570,7 @@ the messages that were tagged"\r
+   (notmuch-search-add-tag-region tag (point) (point)))\r
\r
+ (defun notmuch-search-add-tag-region (tag beg end)\r
+-  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or ")))\r
++  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-msgids-region beg end) " or ")))\r
+     (notmuch-tag search-id-string (concat "+" tag))\r
+     (save-excursion\r
+       (let ((last-line (line-number-at-pos end))\r
+@@ -555,7 +584,7 @@ the messages that were tagged"\r
+   (notmuch-search-remove-tag-region tag (point) (point)))\r
\r
+ (defun notmuch-search-remove-tag-region (tag beg end)\r
+-  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or ")))\r
++  (let ((search-id-string (mapconcat 'identity (notmuch-search-find-msgids-region beg end) " or ")))\r
+     (notmuch-tag search-id-string (concat "-" tag))\r
+     (save-excursion\r
+       (let ((last-line (line-number-at-pos end))\r
+@@ -589,9 +618,9 @@ thread or threads in the current region."\r
+         "Tag to remove: "\r
+         (if (region-active-p)\r
+             (mapconcat 'identity\r
+-                       (notmuch-search-find-thread-id-region (region-beginning) (region-end))\r
++                       (notmuch-search-find-msgids-region (region-beginning) (region-end))\r
+                        " ")\r
+-          (notmuch-search-find-thread-id)))))\r
++          (notmuch-search-find-msgids)))))\r
+   (save-excursion\r
+     (if (region-active-p)\r
+       (let* ((beg (region-beginning))\r
+@@ -801,13 +830,14 @@ non-authors is found, assume that all of the authors match."\r
+             (while more\r
+               (while (and (< line (length string)) (= (elt string line) ?\n))\r
+                 (setq line (1+ line)))\r
+-              (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\([^][]*\\) \\(\\[[0-9/]*\\]\\) \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)\r
++              (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\([^][]*\\) \\(\\[[0-9/]*\\]\\) \\([^;]*\\); \\(.*\\) (\\([^()]*\\)) \\((.*\\(or.*\\)*\\)$" string line)\r
+                   (let* ((thread-id (match-string 1 string))\r
+                          (date (match-string 2 string))\r
+                          (count (match-string 3 string))\r
+                          (authors (match-string 4 string))\r
+                          (subject (match-string 5 string))\r
+                          (tags (match-string 6 string))\r
++                         (msgids (match-string 7 string))\r
+                          (tag-list (if tags (save-match-data (split-string tags)))))\r
+                     (goto-char (point-max))\r
+                     (if (/= (match-beginning 1) line)\r
+@@ -816,6 +846,7 @@ non-authors is found, assume that all of the authors match."\r
+                       (notmuch-search-show-result date count authors subject tags)\r
+                       (notmuch-search-color-line beg (point-marker) tag-list)\r
+                       (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id)\r
++                      (put-text-property beg (point-marker) 'notmuch-search-msgids msgids)\r
+                       (put-text-property beg (point-marker) 'notmuch-search-authors authors)\r
+                       (put-text-property beg (point-marker) 'notmuch-search-subject subject)\r
+                       (if (string= thread-id notmuch-search-target-thread)\r
+@@ -834,10 +865,10 @@ non-authors is found, assume that all of the authors match."\r
+       (delete-process proc))))\r
\r
+ (defun notmuch-search-operate-all (action)\r
+-  "Add/remove tags from all matching messages.\r
++  "Add/remove tags to/from all threads in current search buffer.\r
\r
+-This command adds or removes tags from all messages matching the\r
+-current search terms. When called interactively, this command\r
++This command adds or removes tags from all threads displayed in\r
++the current search buffer. When called interactively, this command\r
+ will prompt for tags to be added or removed. Tags prefixed with\r
+ '+' will be added and tags prefixed with '-' will be removed.\r
\r
+@@ -845,7 +876,10 @@ Each character of the tag name may consist of alphanumeric\r
+ characters as well as `_.+-'.\r
+ "\r
+   (interactive "sOperation (+add -drop): notmuch tag ")\r
+-  (let ((action-split (split-string action " +")))\r
++  (let ((action-split (split-string action " +"))\r
++        (msgids (mapconcat 'identity\r
++                           (notmuch-search-find-msgids-region (point-min) (- (point-max) 2))\r
++                           " or ")))\r
+     ;; Perform some validation\r
+     (let ((words action-split))\r
+       (when (null words) (error "No operation given"))\r
+@@ -853,7 +887,7 @@ characters as well as `_.+-'.\r
+       (unless (string-match-p "^[-+][-+_.[:word:]]+$" (car words))\r
+         (error "Action must be of the form `+thistag -that_tag'"))\r
+       (setq words (cdr words))))\r
+-    (apply 'notmuch-tag notmuch-search-query-string action-split)))\r
++    (apply 'notmuch-tag msgids action-split)))\r
\r
+ (defun notmuch-search-buffer-title (query)\r
+   "Returns the title for a buffer with notmuch search results."\r
+@@ -913,6 +947,7 @@ The optional parameters are used as follows:\r
+       (let ((proc (start-process\r
+                    "notmuch-search" buffer\r
+                    notmuch-command "search"\r
++                   "--output=summary-ids"\r
+                    (if oldest-first\r
+                        "--sort=oldest-first"\r
+                      "--sort=newest-first")\r
+diff --git a/notmuch-search.c b/notmuch-search.c\r
+index 2288eb7..b3af88b 100644\r
+--- a/notmuch-search.c\r
++++ b/notmuch-search.c\r
+@@ -22,6 +22,7 @@\r
\r
+ typedef enum {\r
+     OUTPUT_SUMMARY,\r
++    OUTPUT_SUMMARY_IDS,\r
+     OUTPUT_THREADS,\r
+     OUTPUT_MESSAGES,\r
+     OUTPUT_FILES,\r
+@@ -274,7 +275,7 @@ do_search_threads (const search_format_t *format,\r
\r
+           fputs (format->tag_end, stdout);\r
\r
+-          if (format == &format_text) {\r
++          if (format == &format_text && output == OUTPUT_SUMMARY_IDS) {\r
+               notmuch_messages_t *toplevel;\r
+               const char *first;\r
\r
+@@ -462,6 +463,8 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
+           opt = argv[i] + sizeof ("--output=") - 1;\r
+           if (strcmp (opt, "summary") == 0) {\r
+               output = OUTPUT_SUMMARY;\r
++          } else if (strcmp (opt, "summary-ids") == 0) {\r
++              output = OUTPUT_SUMMARY_IDS;\r
+           } else if (strcmp (opt, "threads") == 0) {\r
+               output = OUTPUT_THREADS;\r
+           } else if (strcmp (opt, "messages") == 0) {\r
+@@ -513,6 +516,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
+     switch (output) {\r
+     default:\r
+     case OUTPUT_SUMMARY:\r
++    case OUTPUT_SUMMARY_IDS:\r
+     case OUTPUT_THREADS:\r
+       ret = do_search_threads (format, query, sort, output);\r
+       break;\r
+diff --git a/test/emacs b/test/emacs\r
+index 53f455a..6479c4e 100755\r
+--- a/test/emacs\r
++++ b/test/emacs\r
+@@ -239,7 +239,7 @@ Subject:\r
+ EOF\r
+ test_expect_equal_file OUTPUT EXPECTED\r
\r
+-test_begin_subtest "Reply within emacs"\r
++test_begin_subtest "Compose reply in emacs"\r
+ test_emacs '(notmuch-search "subject:\"testing message sent via SMTP\"")\r
+           (notmuch-test-wait)\r
+           (notmuch-search-reply-to-thread)\r
+@@ -257,6 +257,53 @@ On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> w\r
+ EOF\r
+ test_expect_equal_file OUTPUT EXPECTED\r
\r
++test_begin_subtest "Send reply from within Emacs"\r
++$TEST_DIRECTORY/smtp-dummy sent_message &\r
++smtp_dummy_pid=$!\r
++test_emacs \\r
++'(let ((message-send-mail-function '\''message-smtpmail-send-it)\r
++       (smtpmail-smtp-server "localhost")\r
++       (smtpmail-smtp-service "25025"))\r
++  (notmuch-search "subject:\"testing message sent via SMTP\"")\r
++  (notmuch-test-wait)\r
++  (notmuch-search-reply-to-thread)\r
++  (message-goto-to)\r
++  (message-goto-body)\r
++  (end-of-buffer)\r
++  (newline)\r
++  (insert "Reply to a message via Emacs with fake SMTP")\r
++  (message-send-and-exit))' >/dev/null 2>&1\r
++wait ${smtp_dummy_pid}\r
++notmuch new >/dev/null\r
++sed \\r
++    -e s',^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' \\r
++    -e s',^Message-ID: <.*>$,Message-ID: <XXX>,' \\r
++    -e s',^In-Reply-To: <.*>$,In-Reply-To: <XXX>,' \\r
++    -e s',^References: <.*>$,References: <XXX>,' \\r
++    -e s',^Date: .*$,Date: Fri\, 29 Mar 1974 10:05:00 -0000,' < sent_message >OUTPUT\r
++cat <<EOF >EXPECTED\r
++From: Notmuch Test Suite <test_suite@notmuchmail.org>\r
++To: user@example.com\r
++Subject: Re: Testing message sent via SMTP\r
++In-Reply-To: <XXX>\r
++References: <XXX>\r
++User-Agent: Notmuch/XXX Emacs/XXX\r
++Date: Fri, 29 Mar 1974 10:05:00 -0000\r
++Message-ID: <XXX>\r
++MIME-Version: 1.0\r
++Content-Type: text/plain; charset=us-ascii\r
++\r
++On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:\r
++> This is a test that messages are sent via SMTP\r
++\r
++Reply to a message via Emacs with fake SMTP\r
++EOF\r
++test_expect_equal_file OUTPUT EXPECTED\r
++\r
++test_begin_subtest "Verify that 'replied' tag is added to reply's parent message."\r
++output=$(notmuch search 'tag:replied' | notmuch_search_sanitize)\r
++test_expect_equal "$output" "thread:XXX   2000-01-01 [1/2] Notmuch Test Suite; Testing message sent via SMTP (inbox replied)"\r
++\r
+ test_begin_subtest "Save attachment from within emacs using notmuch-show-save-attachments"\r
+ # save as archive to test that Emacs does not re-compress .gz\r
+ test_emacs '(let ((standard-input "\"attachment1.gz\""))\r
+diff --git a/test/emacs-search-operate-all b/test/emacs-search-operate-all\r
+new file mode 100755\r
+index 0000000..48326c8\r
+--- /dev/null\r
++++ b/test/emacs-search-operate-all\r
+@@ -0,0 +1,29 @@\r
++#!/usr/bin/env bash\r
++\r
++test_description="emacs interface"\r
++. test-lib.sh\r
++\r
++EXPECTED=$TEST_DIRECTORY/emacs.expected-output\r
++\r
++add_email_corpus\r
++\r
++test_begin_subtest "Add/remove tags to/from all matching threads."\r
++test_emacs '(notmuch-search "tag:inbox AND tags")\r
++          (notmuch-test-wait)\r
++          (notmuch-search-operate-all "+matching -inbox")\r
++          (notmuch-search "tag:matching AND NOT tag:inbox")\r
++          (notmuch-test-wait)\r
++          (test-output)'\r
++cat <<EOF >EXPECTED\r
++  2009-11-18 [2/2]   Ingmar Vanhassel, Carl Worth  [notmuch] [PATCH] Typsos (matching unread)\r
++  2009-11-18 [3/3]   Adrian Perez de Castro, Keith Packard, Carl Worth  [notmuch] Introducing myself (matching signed unread)\r
++  2009-11-18 [3/3]   Israel Herraiz, Keith Packard, Carl Worth   [notmuch] New to the list (matching unread)\r
++  2009-11-18 [2/2]   Keith Packard, Carl Worth    [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (matching unread)\r
++  2009-11-18 [2/2]   Keith Packard, Alexander Botero-Lowry    [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (matching unread)\r
++  2009-11-18 [1/1]   Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (matching unread)\r
++  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (matching unread)\r
++End of search results.\r
++EOF\r
++test_expect_equal_file OUTPUT EXPECTED\r
++\r
++test_done\r
+diff --git a/test/notmuch-test b/test/notmuch-test\r
+index 79e6267..bafbcb1 100755\r
+--- a/test/notmuch-test\r
++++ b/test/notmuch-test\r
+@@ -38,6 +38,7 @@ TESTS="\r
+   encoding\r
+   emacs\r
+   emacs-large-search-buffer\r
++  emacs-search-operate-all\r
+   maildir-sync\r
+   crypto\r
+   symbol-hiding\r
+-- \r
+1.7.5.4\r
+\r