test: renamed test scripts to format T\d\d\d-name.sh
authorTomi Ollila <tomi.ollila@iki.fi>
Thu, 9 Jan 2014 15:18:59 +0000 (17:18 +0200)
committerDavid Bremner <david@tethera.net>
Mon, 13 Jan 2014 18:16:46 +0000 (14:16 -0400)
All test scripts to be executed are now named as T\d\d\d-name.sh,
numers in increments of 10.

This eases adding new tests and developers to see which are test scripts
that are executed by test suite and in which order.

106 files changed:
test/T000-basic.sh [new file with mode: 0755]
test/T010-help-test.sh [new file with mode: 0755]
test/T020-compact.sh [new file with mode: 0755]
test/T030-config.sh [new file with mode: 0755]
test/T040-setup.sh [new file with mode: 0755]
test/T050-new.sh [new file with mode: 0755]
test/T060-count.sh [new file with mode: 0755]
test/T070-insert.sh [new file with mode: 0755]
test/T080-search.sh [new file with mode: 0755]
test/T090-search-output.sh [new file with mode: 0755]
test/T100-search-by-folder.sh [new file with mode: 0755]
test/T110-search-position-overlap-bug.sh [new file with mode: 0755]
test/T120-search-insufficient-from-quoting.sh [new file with mode: 0755]
test/T130-search-limiting.sh [new file with mode: 0755]
test/T140-excludes.sh [new file with mode: 0755]
test/T150-tagging.sh [new file with mode: 0755]
test/T160-json.sh [new file with mode: 0755]
test/T170-sexp.sh [new file with mode: 0755]
test/T180-text.sh [new file with mode: 0755]
test/T190-multipart.sh [new file with mode: 0755]
test/T200-thread-naming.sh [new file with mode: 0755]
test/T210-raw.sh [new file with mode: 0755]
test/T220-reply.sh [new file with mode: 0755]
test/T230-reply-to-sender.sh [new file with mode: 0755]
test/T240-dump-restore.sh [new file with mode: 0755]
test/T250-uuencode.sh [new file with mode: 0755]
test/T260-thread-order.sh [new file with mode: 0755]
test/T270-author-order.sh [new file with mode: 0755]
test/T280-from-guessing.sh [new file with mode: 0755]
test/T290-long-id.sh [new file with mode: 0755]
test/T300-encoding.sh [new file with mode: 0755]
test/T310-emacs.sh [new file with mode: 0755]
test/T320-emacs-large-search-buffer.sh [new file with mode: 0755]
test/T330-emacs-subject-to-filename.sh [new file with mode: 0755]
test/T340-maildir-sync.sh [new file with mode: 0755]
test/T350-crypto.sh [new file with mode: 0755]
test/T360-symbol-hiding.sh [new file with mode: 0755]
test/T370-search-folder-coherence.sh [new file with mode: 0755]
test/T380-atomicity.sh [new file with mode: 0755]
test/T390-python.sh [new file with mode: 0755]
test/T400-hooks.sh [new file with mode: 0755]
test/T410-argument-parsing.sh [new file with mode: 0755]
test/T420-emacs-test-functions.sh [new file with mode: 0755]
test/T430-emacs-address-cleaning.sh [new file with mode: 0755]
test/T440-emacs-hello.sh [new file with mode: 0755]
test/T450-emacs-show.sh [new file with mode: 0755]
test/T460-emacs-tree.sh [new file with mode: 0755]
test/T470-missing-headers.sh [new file with mode: 0755]
test/T480-hex-escaping.sh [new file with mode: 0755]
test/T490-parse-time-string.sh [new file with mode: 0755]
test/T500-search-date.sh [new file with mode: 0755]
test/T510-thread-replies.sh [new file with mode: 0755]
test/argument-parsing [deleted file]
test/atomicity [deleted file]
test/author-order [deleted file]
test/basic [deleted file]
test/compact [deleted file]
test/config [deleted file]
test/count [deleted file]
test/crypto [deleted file]
test/dump-restore [deleted file]
test/emacs [deleted file]
test/emacs-address-cleaning [deleted file]
test/emacs-hello [deleted file]
test/emacs-large-search-buffer [deleted file]
test/emacs-show [deleted file]
test/emacs-subject-to-filename [deleted file]
test/emacs-test-functions [deleted file]
test/emacs-tree [deleted file]
test/encoding [deleted file]
test/excludes [deleted file]
test/from-guessing [deleted file]
test/help-test [deleted file]
test/hex-escaping [deleted file]
test/hooks [deleted file]
test/insert [deleted file]
test/json [deleted file]
test/long-id [deleted file]
test/maildir-sync [deleted file]
test/missing-headers [deleted file]
test/multipart [deleted file]
test/new [deleted file]
test/notmuch-test
test/parse-time-string [deleted file]
test/python [deleted file]
test/raw [deleted file]
test/reply [deleted file]
test/reply-to-sender [deleted file]
test/search [deleted file]
test/search-by-folder [deleted file]
test/search-date [deleted file]
test/search-folder-coherence [deleted file]
test/search-insufficient-from-quoting [deleted file]
test/search-limiting [deleted file]
test/search-output [deleted file]
test/search-position-overlap-bug [deleted file]
test/setup [deleted file]
test/sexp [deleted file]
test/symbol-hiding [deleted file]
test/tagging [deleted file]
test/test-lib.sh
test/text [deleted file]
test/thread-naming [deleted file]
test/thread-order [deleted file]
test/thread-replies [deleted file]
test/uuencode [deleted file]

diff --git a/test/T000-basic.sh b/test/T000-basic.sh
new file mode 100755 (executable)
index 0000000..9c94b62
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='the test framework itself.'
+
+################################################################
+# It appears that people try to run tests without building...
+
+if ! test -x ../notmuch
+then
+       echo >&2 'You do not seem to have built notmuch yet.'
+       exit 1
+fi
+
+. ./test-lib.sh
+
+################################################################
+# Test harness
+test_expect_success 'success is reported like this' '
+    :
+'
+test_set_prereq HAVEIT
+haveit=no
+test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
+    test_have_prereq HAVEIT &&
+    haveit=yes
+'
+
+clean=no
+test_expect_success 'tests clean up after themselves' '
+    test_when_finished clean=yes
+'
+
+cleaner=no
+test_expect_code 1 'tests clean up even after a failure' '
+    test_when_finished cleaner=yes &&
+    (exit 1)
+'
+
+if test $clean$cleaner != yesyes
+then
+       say "bug in test framework: cleanup commands do not work reliably"
+       exit 1
+fi
+
+test_expect_code 2 'failure to clean up causes the test to fail' '
+    test_when_finished "(exit 2)"
+'
+
+EXPECTED=$TEST_DIRECTORY/test.expected-output
+suppress_diff_date() {
+    sed -e 's/\(.*\-\-\- test-verbose\.4\.\expected\).*/\1/' \
+       -e 's/\(.*\+\+\+ test-verbose\.4\.\output\).*/\1/'
+}
+
+test_begin_subtest "Ensure that test output is suppressed unless the test fails"
+output=$(cd $TEST_DIRECTORY; NOTMUCH_TEST_QUIET= ./test-verbose 2>&1 | suppress_diff_date)
+expected=$(cat $EXPECTED/test-verbose-no | suppress_diff_date)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Ensure that -v does not suppress test output"
+output=$(cd $TEST_DIRECTORY; NOTMUCH_TEST_QUIET= ./test-verbose -v 2>&1 | suppress_diff_date)
+expected=$(cat $EXPECTED/test-verbose-yes | suppress_diff_date)
+# Do not include the results of test-verbose in totals
+rm $TEST_DIRECTORY/test-results/test-verbose
+rm -r $TEST_DIRECTORY/tmp.test-verbose
+test_expect_equal "$output" "$expected"
+
+
+################################################################
+# Test mail store prepared in test-lib.sh
+
+test_expect_success \
+    'test that mail store was created' \
+    'test -d "${MAIL_DIR}"'
+
+
+find "${MAIL_DIR}" -type f -print >should-be-empty
+test_expect_success \
+    'mail store should be empty' \
+    'cmp -s /dev/null should-be-empty'
+
+test_expect_success \
+    'NOTMUCH_CONFIG is set and points to an existing file' \
+    'test -f "${NOTMUCH_CONFIG}"'
+
+test_expect_success \
+    'PATH is set to this repository' \
+    'test "`echo $PATH|cut -f1 -d: | sed -e 's,/test/valgrind/bin$,,'`" = "`dirname ${TEST_DIRECTORY}`"'
+
+test_done
diff --git a/test/T010-help-test.sh b/test/T010-help-test.sh
new file mode 100755 (executable)
index 0000000..f7df725
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+test_description="online help"
+. ./test-lib.sh
+
+test_expect_success 'notmuch --help' 'notmuch --help'
+test_expect_success 'notmuch --help tag' 'notmuch --help tag'
+test_expect_success 'notmuch help' 'notmuch help'
+test_expect_success 'notmuch help tag' 'notmuch help tag'
+test_expect_success 'notmuch --version' 'notmuch --version'
+
+test_done
diff --git a/test/T020-compact.sh b/test/T020-compact.sh
new file mode 100755 (executable)
index 0000000..ac174ce
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+test_description='"notmuch compact"'
+. ./test-lib.sh
+
+add_message '[subject]=One'
+add_message '[subject]=Two'
+add_message '[subject]=Three'
+
+notmuch tag +tag1 \*
+notmuch tag +tag2 subject:Two
+notmuch tag -tag1 +tag3 subject:Three
+
+test_expect_success "Running compact" "notmuch compact --backup=${TEST_DIRECTORY}/xapian.old"
+
+test_begin_subtest "Compact preserves database"
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag2 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Three (inbox tag3 unread)"
+
+test_expect_success 'Restoring Backup' \
+    'rm -Rf ${MAIL_DIR}/.notmuch/xapian &&
+     mv ${TEST_DIRECTORY}/xapian.old ${MAIL_DIR}/.notmuch/xapian'
+
+test_begin_subtest "Checking restored backup"
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag2 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Three (inbox tag3 unread)"
+
+test_done
diff --git a/test/T030-config.sh b/test/T030-config.sh
new file mode 100755 (executable)
index 0000000..ca4cf33
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+
+test_description='"notmuch config"'
+. ./test-lib.sh
+
+test_begin_subtest "Get string value"
+test_expect_equal "$(notmuch config get user.name)" "Notmuch Test Suite"
+
+test_begin_subtest "Get list value"
+test_expect_equal "$(notmuch config get new.tags)" "\
+unread
+inbox"
+
+test_begin_subtest "Set string value"
+notmuch config set foo.string "this is a string value"
+test_expect_equal "$(notmuch config get foo.string)" "this is a string value"
+
+test_begin_subtest "Set string value again"
+notmuch config set foo.string "this is another string value"
+test_expect_equal "$(notmuch config get foo.string)" "this is another string value"
+
+test_begin_subtest "Set list value"
+notmuch config set foo.list this "is a" "list value"
+test_expect_equal "$(notmuch config get foo.list)" "\
+this
+is a
+list value"
+
+test_begin_subtest "Set list value again"
+notmuch config set foo.list this "is another" "list value"
+test_expect_equal "$(notmuch config get foo.list)" "\
+this
+is another
+list value"
+
+test_begin_subtest "Remove key"
+notmuch config set foo.remove baz
+notmuch config set foo.remove
+test_expect_equal "$(notmuch config get foo.remove)" ""
+
+test_begin_subtest "Remove non-existent key"
+notmuch config set foo.nonexistent
+test_expect_equal "$(notmuch config get foo.nonexistent)" ""
+
+test_begin_subtest "List all items"
+notmuch config set database.path "/canonical/path"
+output=$(notmuch config list)
+test_expect_equal "$output" "\
+database.path=/canonical/path
+user.name=Notmuch Test Suite
+user.primary_email=test_suite@notmuchmail.org
+user.other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org
+new.tags=unread;inbox;
+new.ignore=
+search.exclude_tags=
+maildir.synchronize_flags=true
+foo.string=this is another string value
+foo.list=this;is another;list value;"
+
+test_begin_subtest "Top level --config=FILE option"
+cp "${NOTMUCH_CONFIG}" alt-config
+notmuch --config=alt-config config set user.name "Another Name"
+test_expect_equal "$(notmuch --config=alt-config config get user.name)" \
+    "Another Name"
+
+test_begin_subtest "Top level --config=FILE option changed the right file"
+test_expect_equal "$(notmuch config get user.name)" \
+    "Notmuch Test Suite"
+
+test_begin_subtest "Read config file through a symlink"
+ln -s alt-config alt-config-link
+test_expect_equal "$(notmuch --config=alt-config-link config get user.name)" \
+    "Another Name"
+
+test_begin_subtest "Write config file through a symlink"
+notmuch --config=alt-config-link config set user.name "Link Name"
+test_expect_equal "$(notmuch --config=alt-config-link config get user.name)" \
+    "Link Name"
+
+test_begin_subtest "Writing config file through symlink follows symlink"
+test_expect_equal "$(readlink alt-config-link)" "alt-config"
+
+test_done
diff --git a/test/T040-setup.sh b/test/T040-setup.sh
new file mode 100755 (executable)
index 0000000..124ef1c
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+test_description='"notmuch setup"'
+. ./test-lib.sh
+
+test_begin_subtest "Create a new config interactively"
+notmuch --config=new-notmuch-config > /dev/null <<EOF
+Test Suite
+test.suite@example.com
+another.suite@example.com
+
+/path/to/maildir
+foo bar
+baz
+EOF
+output=$(notmuch --config=new-notmuch-config config list)
+test_expect_equal "$output" "\
+database.path=/path/to/maildir
+user.name=Test Suite
+user.primary_email=test.suite@example.com
+user.other_email=another.suite@example.com;
+new.tags=foo;bar;
+new.ignore=
+search.exclude_tags=baz;
+maildir.synchronize_flags=true"
+
+test_done
diff --git a/test/T050-new.sh b/test/T050-new.sh
new file mode 100755 (executable)
index 0000000..f27423d
--- /dev/null
@@ -0,0 +1,251 @@
+#!/usr/bin/env bash
+test_description='"notmuch new" in several variations'
+. ./test-lib.sh
+
+test_begin_subtest "No new messages"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+
+test_begin_subtest "Single new message"
+generate_message
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Multiple new messages"
+generate_message
+generate_message
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 2 new messages to the database."
+
+
+test_begin_subtest "No new messages (non-empty DB)"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+
+test_begin_subtest "New directories"
+rm -rf "${MAIL_DIR}"/* "${MAIL_DIR}"/.notmuch
+mkdir "${MAIL_DIR}"/def
+mkdir "${MAIL_DIR}"/ghi
+generate_message [dir]=def
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Alternate inode order"
+
+rm -rf "${MAIL_DIR}"/.notmuch
+mv "${MAIL_DIR}"/ghi "${MAIL_DIR}"/abc
+rm "${MAIL_DIR}"/def/*
+generate_message [dir]=abc
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Message moved in"
+rm -rf "${MAIL_DIR}"/* "${MAIL_DIR}"/.notmuch
+generate_message
+tmp_msg_filename=tmp/"$gen_msg_filename"
+mkdir -p "$(dirname "$tmp_msg_filename")"
+mv "$gen_msg_filename" "$tmp_msg_filename"
+notmuch new > /dev/null
+mv "$tmp_msg_filename" "$gen_msg_filename"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Renamed message"
+
+generate_message
+notmuch new > /dev/null
+mv "$gen_msg_filename" "${gen_msg_filename}"-renamed
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected 1 file rename."
+
+
+test_begin_subtest "Deleted message"
+
+rm "${gen_msg_filename}"-renamed
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 1 message."
+
+
+test_begin_subtest "Renamed directory"
+
+generate_message [dir]=dir
+generate_message [dir]=dir
+generate_message [dir]=dir
+
+notmuch new > /dev/null
+
+mv "${MAIL_DIR}"/dir "${MAIL_DIR}"/dir-renamed
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected 3 file renames."
+
+
+test_begin_subtest "Deleted directory"
+
+rm -rf "${MAIL_DIR}"/dir-renamed
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 3 messages."
+
+
+test_begin_subtest "New directory (at end of list)"
+
+generate_message [dir]=zzz
+generate_message [dir]=zzz
+generate_message [dir]=zzz
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 3 new messages to the database."
+
+
+test_begin_subtest "Deleted directory (end of list)"
+
+rm -rf "${MAIL_DIR}"/zzz
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 3 messages."
+
+
+test_begin_subtest "New symlink to directory"
+
+rm -rf "${MAIL_DIR}"/.notmuch
+mv "${MAIL_DIR}" "${TMP_DIRECTORY}"/actual_maildir
+
+mkdir "${MAIL_DIR}"
+ln -s "${TMP_DIRECTORY}"/actual_maildir "${MAIL_DIR}"/symlink
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "New symlink to a file"
+generate_message
+external_msg_filename="${TMP_DIRECTORY}"/external/"$(basename "$gen_msg_filename")"
+mkdir -p "$(dirname "$external_msg_filename")"
+mv "$gen_msg_filename" "$external_msg_filename"
+ln -s "$external_msg_filename" "$gen_msg_filename"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Broken symlink aborts"
+ln -s does-not-exist "${MAIL_DIR}/broken"
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" \
+"Error reading file ${MAIL_DIR}/broken: No such file or directory
+Note: A fatal error was encountered: Something went wrong trying to read or write a file
+No new mail."
+rm "${MAIL_DIR}/broken"
+
+
+test_begin_subtest "New two-level directory"
+
+generate_message [dir]=two/levels
+generate_message [dir]=two/levels
+generate_message [dir]=two/levels
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 3 new messages to the database."
+
+
+test_begin_subtest "Deleted two-level directory"
+
+rm -rf "${MAIL_DIR}"/two
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 3 messages."
+
+test_begin_subtest "Support single-message mbox (deprecated)"
+cat > "${MAIL_DIR}"/mbox_file1 <<EOF
+From test_suite@notmuchmail.org Fri Jan  5 15:43:57 2001
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Test mbox message 1
+
+Body.
+EOF
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" \
+"Warning: ${MAIL_DIR}/mbox_file1 is an mbox containing a single message,
+likely caused by misconfigured mail delivery.  Support for single-message
+mboxes is deprecated and may be removed in the future.
+Added 1 new message to the database."
+
+# This test requires that notmuch new has been run at least once.
+test_begin_subtest "Skip and report non-mail files"
+generate_message
+mkdir -p "${MAIL_DIR}"/.git && touch "${MAIL_DIR}"/.git/config
+touch "${MAIL_DIR}"/ignored_file
+touch "${MAIL_DIR}"/.ignored_hidden_file
+cat > "${MAIL_DIR}"/mbox_file <<EOF
+From test_suite@notmuchmail.org Fri Jan  5 15:43:57 2001
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Test mbox message 1
+
+Body.
+
+From test_suite@notmuchmail.org Fri Jan  5 15:43:57 2001
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Test mbox message 2
+
+Body 2.
+EOF
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" \
+"Note: Ignoring non-mail file: ${MAIL_DIR}/.git/config
+Note: Ignoring non-mail file: ${MAIL_DIR}/.ignored_hidden_file
+Note: Ignoring non-mail file: ${MAIL_DIR}/ignored_file
+Note: Ignoring non-mail file: ${MAIL_DIR}/mbox_file
+Added 1 new message to the database."
+rm "${MAIL_DIR}"/mbox_file
+
+test_begin_subtest "Ignore files and directories specified in new.ignore"
+generate_message
+notmuch config set new.ignore .git ignored_file .ignored_hidden_file
+touch "${MAIL_DIR}"/.git # change .git's mtime for notmuch new to rescan.
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Ignore files and directories specified in new.ignore (multiple occurrences)"
+notmuch config set new.ignore .git ignored_file .ignored_hidden_file
+notmuch new > /dev/null # ensure that files/folders will be printed in ASCII order.
+touch "${MAIL_DIR}"/.git # change .git's mtime for notmuch new to rescan.
+touch "${MAIL_DIR}"      # likewise for MAIL_DIR
+mkdir -p "${MAIL_DIR}"/one/two/three/.git
+touch "${MAIL_DIR}"/{one,one/two,one/two/three}/ignored_file
+output=$(NOTMUCH_NEW --debug 2>&1 | sort)
+test_expect_equal "$output" \
+"(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/.git
+(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
+(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/ignored_file
+(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/ignored_file
+(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
+(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
+(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/ignored_file
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/.git
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/ignored_file
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/ignored_file
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
+(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/ignored_file
+No new mail."
+
+
+test_begin_subtest "Don't stop for ignored broken symlinks"
+notmuch config set new.ignore .git ignored_file .ignored_hidden_file broken_link
+ln -s i_do_not_exist "${MAIL_DIR}"/broken_link
+output=$(NOTMUCH_NEW 2>&1)
+test_expect_equal "$output" "No new mail."
+
+test_done
diff --git a/test/T060-count.sh b/test/T060-count.sh
new file mode 100755 (executable)
index 0000000..da86c8c
--- /dev/null
@@ -0,0 +1,97 @@
+#!/usr/bin/env bash
+test_description='"notmuch count" for messages and threads'
+. ./test-lib.sh
+
+add_email_corpus
+
+# Note: The 'wc -l' results below are wrapped in arithmetic evaluation
+# $((...)) to strip whitespace. This is for portability, as 'wc -l'
+# emits whitespace on some BSD variants.
+
+test_begin_subtest "message count is the default for notmuch count"
+test_expect_equal \
+    "$((`notmuch search --output=messages '*' | wc -l`))" \
+    "`notmuch count '*'`"
+
+test_begin_subtest "message count with --output=messages"
+test_expect_equal \
+    "$((`notmuch search --output=messages '*' | wc -l`))" \
+    "`notmuch count --output=messages '*'`"
+
+test_begin_subtest "thread count with --output=threads"
+test_expect_equal \
+    "$((`notmuch search --output=threads '*' | wc -l`))" \
+    "`notmuch count --output=threads '*'`"
+
+test_begin_subtest "thread count is the default for notmuch search"
+test_expect_equal \
+    "$((`notmuch search '*' | wc -l`))" \
+    "`notmuch count --output=threads '*'`"
+
+test_begin_subtest "files count"
+test_expect_equal \
+    "$((`notmuch search --output=files '*' | wc -l`))" \
+    "`notmuch count --output=files '*'`"
+
+test_begin_subtest "files count for a duplicate message-id"
+test_expect_equal \
+    "2" \
+    "`notmuch count --output=files id:20091117232137.GA7669@griffis1.net`"
+
+test_begin_subtest "count with no matching messages"
+test_expect_equal \
+    "0" \
+    "`notmuch count --output=messages from:cworth and not from:cworth`"
+
+test_begin_subtest "count with no matching threads"
+test_expect_equal \
+    "0" \
+    "`notmuch count --output=threads from:cworth and not from:cworth`"
+
+test_begin_subtest "message count is the default for batch count"
+notmuch count --batch >OUTPUT <<EOF
+
+from:cworth
+EOF
+notmuch count --output=messages >EXPECTED
+notmuch count --output=messages from:cworth >>EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "batch message count"
+notmuch count --batch --output=messages >OUTPUT <<EOF
+from:cworth
+
+tag:inbox
+EOF
+notmuch count --output=messages from:cworth >EXPECTED
+notmuch count --output=messages >>EXPECTED
+notmuch count --output=messages tag:inbox >>EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "batch thread count"
+notmuch count --batch --output=threads >OUTPUT <<EOF
+
+from:cworth
+from:cworth and not from:cworth
+foo
+EOF
+notmuch count --output=threads >EXPECTED
+notmuch count --output=threads from:cworth >>EXPECTED
+notmuch count --output=threads from:cworth and not from:cworth >>EXPECTED
+notmuch count --output=threads foo >>EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "batch message count with input file"
+cat >INPUT <<EOF
+from:cworth
+
+tag:inbox
+EOF
+notmuch count --input=INPUT --output=messages >OUTPUT
+notmuch count --output=messages from:cworth >EXPECTED
+notmuch count --output=messages >>EXPECTED
+notmuch count --output=messages tag:inbox >>EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
+
+test_done
diff --git a/test/T070-insert.sh b/test/T070-insert.sh
new file mode 100755 (executable)
index 0000000..e8dc4c0
--- /dev/null
@@ -0,0 +1,167 @@
+#!/usr/bin/env bash
+test_description='"notmuch insert"'
+. ./test-lib.sh
+
+# Create directories and database before inserting.
+mkdir -p "$MAIL_DIR"/{cur,new,tmp}
+mkdir -p "$MAIL_DIR"/Drafts/{cur,new,tmp}
+notmuch new > /dev/null
+
+# We use generate_message to create the temporary message files.
+# They happen to be in the mail directory already but that is okay
+# since we do not call notmuch new hereafter.
+
+gen_insert_msg() {
+    generate_message \
+       "[subject]=\"insert-subject\"" \
+       "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" \
+       "[body]=\"insert-message\""
+}
+
+test_expect_code 1 "Insert zero-length file" \
+    "notmuch insert < /dev/null"
+
+# This test is a proxy for other errors that may occur while trying to
+# add a message to the notmuch database, e.g. database locked.
+test_expect_code 0 "Insert non-message" \
+    "echo bad_message | notmuch insert"
+
+test_begin_subtest "Database empty so far"
+test_expect_equal "0" "`notmuch count --output=messages '*'`"
+
+test_begin_subtest "Insert message"
+gen_insert_msg
+notmuch insert < "$gen_msg_filename"
+cur_msg_filename=$(notmuch search --output=files "subject:insert-subject")
+test_expect_equal_file "$cur_msg_filename" "$gen_msg_filename"
+
+test_begin_subtest "Insert message adds default tags"
+output=$(notmuch show --format=json "subject:insert-subject")
+expected='[[[{
+ "id": "'"${gen_msg_id}"'",
+ "match": true,
+ "excluded": false,
+ "filename": "'"${cur_msg_filename}"'",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["inbox","unread"],
+ "headers": {
+  "Subject": "insert-subject",
+  "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+  "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+  "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+  "content-type": "text/plain",
+  "content": "insert-message\n"}]},
+ []]]]'
+test_expect_equal_json "$output" "$expected"
+
+test_begin_subtest "Insert duplicate message"
+notmuch insert +duptag -unread < "$gen_msg_filename"
+output=$(notmuch search --output=files "subject:insert-subject" | wc -l)
+test_expect_equal "$output" 2
+
+test_begin_subtest "Duplicate message does not change tags"
+output=$(notmuch search --format=json --output=tags "subject:insert-subject")
+test_expect_equal_json "$output" '["inbox", "unread"]'
+
+test_begin_subtest "Insert message, add tag"
+gen_insert_msg
+notmuch insert +custom < "$gen_msg_filename"
+output=$(notmuch search --output=messages tag:custom)
+test_expect_equal "$output" "id:$gen_msg_id"
+
+test_begin_subtest "Insert message, add/remove tags"
+gen_insert_msg
+notmuch insert +custom -unread < "$gen_msg_filename"
+output=$(notmuch search --output=messages tag:custom NOT tag:unread)
+test_expect_equal "$output" "id:$gen_msg_id"
+
+test_begin_subtest "Insert message with default tags stays in new/"
+gen_insert_msg
+notmuch insert < "$gen_msg_filename"
+output=$(notmuch search --output=files id:$gen_msg_id)
+dirname=$(dirname "$output")
+test_expect_equal "$dirname" "$MAIL_DIR/new"
+
+test_begin_subtest "Insert message with non-maildir synced tags stays in new/"
+gen_insert_msg
+notmuch insert +custom -inbox < "$gen_msg_filename"
+output=$(notmuch search --output=files id:$gen_msg_id)
+dirname=$(dirname "$output")
+test_expect_equal "$dirname" "$MAIL_DIR/new"
+
+test_begin_subtest "Insert message with custom new.tags goes to cur/"
+OLDCONFIG=$(notmuch config get new.tags)
+notmuch config set new.tags test
+gen_insert_msg
+notmuch insert < "$gen_msg_filename"
+output=$(notmuch search --output=files id:$gen_msg_id)
+dirname=$(dirname "$output")
+notmuch config set new.tags $OLDCONFIG
+test_expect_equal "$dirname" "$MAIL_DIR/cur"
+
+# additional check on the previous message
+test_begin_subtest "Insert message with custom new.tags actually gets the tags"
+output=$(notmuch search --output=tags id:$gen_msg_id)
+test_expect_equal "$output" "test"
+
+test_begin_subtest "Insert message with maildir synced tags goes to cur/"
+gen_insert_msg
+notmuch insert +flagged < "$gen_msg_filename"
+output=$(notmuch search --output=files id:$gen_msg_id)
+dirname=$(dirname "$output")
+test_expect_equal "$dirname" "$MAIL_DIR/cur"
+
+test_begin_subtest "Insert message with maildir sync off goes to new/"
+OLDCONFIG=$(notmuch config get maildir.synchronize_flags)
+notmuch config set maildir.synchronize_flags false
+gen_insert_msg
+notmuch insert +flagged < "$gen_msg_filename"
+output=$(notmuch search --output=files id:$gen_msg_id)
+dirname=$(dirname "$output")
+notmuch config set maildir.synchronize_flags $OLDCONFIG
+test_expect_equal "$dirname" "$MAIL_DIR/new"
+
+test_begin_subtest "Insert message into folder"
+gen_insert_msg
+notmuch insert --folder=Drafts < "$gen_msg_filename"
+output=$(notmuch search --output=files folder:Drafts)
+dirname=$(dirname "$output")
+test_expect_equal "$dirname" "$MAIL_DIR/Drafts/new"
+
+test_begin_subtest "Insert message into folder, add/remove tags"
+gen_insert_msg
+notmuch insert --folder=Drafts +draft -unread < "$gen_msg_filename"
+output=$(notmuch search --output=messages folder:Drafts tag:draft NOT tag:unread)
+test_expect_equal "$output" "id:$gen_msg_id"
+
+gen_insert_msg
+test_expect_code 1 "Insert message into non-existent folder" \
+    "notmuch insert --folder=nonesuch < $gen_msg_filename"
+
+test_begin_subtest "Insert message, create folder"
+gen_insert_msg
+notmuch insert --folder=F --create-folder +folder < "$gen_msg_filename"
+output=$(notmuch search --output=files folder:F tag:folder)
+basename=$(basename "$output")
+test_expect_equal_file "$gen_msg_filename" "$MAIL_DIR/F/new/${basename}"
+
+test_begin_subtest "Insert message, create subfolder"
+gen_insert_msg
+notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename"
+output=$(notmuch search --output=files folder:F/G/H/I/J tag:folder)
+basename=$(basename "$output")
+test_expect_equal_file "$gen_msg_filename" "${MAIL_DIR}/F/G/H/I/J/new/${basename}"
+
+test_begin_subtest "Insert message, create existing subfolder"
+gen_insert_msg
+notmuch insert --folder=F/G/H/I/J --create-folder +folder < "$gen_msg_filename"
+output=$(notmuch count folder:F/G/H/I/J tag:folder)
+test_expect_equal "$output" "2"
+
+gen_insert_msg
+test_expect_code 1 "Insert message, create invalid subfolder" \
+    "notmuch insert --folder=../G --create-folder $gen_msg_filename"
+
+test_done
diff --git a/test/T080-search.sh b/test/T080-search.sh
new file mode 100755 (executable)
index 0000000..a7a0b18
--- /dev/null
@@ -0,0 +1,132 @@
+#!/usr/bin/env bash
+test_description='"notmuch search" in several variations'
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "Search body"
+add_message '[subject]="body search"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [body]=bodysearchtest
+output=$(notmuch search bodysearchtest | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)"
+
+test_begin_subtest "Search by from:"
+add_message '[subject]="search by from"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom
+output=$(notmuch search from:searchbyfrom | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)"
+
+test_begin_subtest "Search by to:"
+add_message '[subject]="search by to"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto
+output=$(notmuch search to:searchbyto | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)"
+
+test_begin_subtest "Search by subject:"
+add_message [subject]=subjectsearchtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search subject:subjectsearchtest | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)"
+
+test_begin_subtest "Search by subject (utf-8):"
+add_message [subject]=utf8-sübjéct '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search subject:utf8-sübjéct | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
+
+test_begin_subtest "Search by id:"
+add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search id:${gen_msg_id} | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)"
+
+test_begin_subtest "Search by tag:"
+add_message '[subject]="search by tag"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+notmuch tag +searchbytag id:${gen_msg_id}
+output=$(notmuch search tag:searchbytag | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)"
+
+test_begin_subtest "Search by thread:"
+add_message '[subject]="search by thread"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+thread_id=$(notmuch search id:${gen_msg_id} | sed -e "s/thread:\([a-f0-9]*\).*/\1/")
+output=$(notmuch search thread:${thread_id} | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)"
+
+test_begin_subtest "Search body (phrase)"
+add_message '[subject]="body search (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="body search (phrase)"'
+add_message '[subject]="negative result"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="This phrase should not match the body search"'
+output=$(notmuch search '"body search (phrase)"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)"
+
+test_begin_subtest "Search by from: (address)"
+add_message '[subject]="search by from (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom@example.com
+output=$(notmuch search from:searchbyfrom@example.com | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)"
+
+test_begin_subtest "Search by from: (name)"
+add_message '[subject]="search by from (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[from]="Search By From Name <test@example.com>"'
+output=$(notmuch search from:"Search By From Name" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)"
+
+test_begin_subtest "Search by to: (address)"
+add_message '[subject]="search by to (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto@example.com
+output=$(notmuch search to:searchbyto@example.com | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)"
+
+test_begin_subtest "Search by to: (name)"
+add_message '[subject]="search by to (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[to]="Search By To Name <test@example.com>"'
+output=$(notmuch search to:"Search By To Name" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)"
+
+test_begin_subtest "Search by subject: (phrase)"
+add_message '[subject]="subject search test (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+add_message '[subject]="this phrase should not match the subject search test"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search 'subject:"subject search test (phrase)"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)"
+
+test_begin_subtest 'Search for all messages ("*")'
+notmuch search '*' | notmuch_search_sanitize > OUTPUT
+cat <<EOF >EXPECTED
+thread:XXX   2010-12-29 [1/1] François Boulogne; [aur-general] Guidelines: cp, mkdir vs install (inbox unread)
+thread:XXX   2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread)
+thread:XXX   2009-11-18 [1/1] Chris Wilson; [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread)
+thread:XXX   2009-11-18 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread)
+thread:XXX   2009-11-18 [2/2] Ingmar Vanhassel, Carl Worth; [notmuch] [PATCH] Typsos (inbox unread)
+thread:XXX   2009-11-18 [3/3] Adrian Perez de Castro, Keith Packard, Carl Worth; [notmuch] Introducing myself (inbox signed unread)
+thread:XXX   2009-11-18 [3/3] Israel Herraiz, Keith Packard, Carl Worth; [notmuch] New to the list (inbox unread)
+thread:XXX   2009-11-18 [3/3] Jan Janak, Carl Worth; [notmuch] What a great idea! (inbox unread)
+thread:XXX   2009-11-18 [2/2] Jan Janak, Carl Worth; [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+thread:XXX   2009-11-18 [3/3] Aron Griffis, Keith Packard, Carl Worth; [notmuch] archive (inbox unread)
+thread:XXX   2009-11-18 [2/2] Keith Packard, Carl Worth; [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+thread:XXX   2009-11-18 [7/7] Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
+thread:XXX   2009-11-18 [5/5] Mikhail Gusarov, Carl Worth, Keith Packard; [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+thread:XXX   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 (inbox unread)
+thread:XXX   2009-11-18 [1/1] Alexander Botero-Lowry; [notmuch] request for pull (inbox unread)
+thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
+thread:XXX   2009-11-18 [1/1] Rolland Santimano; [notmuch] Link to mailing list archives ? (inbox unread)
+thread:XXX   2009-11-18 [1/1] Jan Janak; [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (inbox unread)
+thread:XXX   2009-11-18 [1/1] Stewart Smith; [notmuch] [PATCH] count_files: sort directory in inode order before statting (inbox unread)
+thread:XXX   2009-11-18 [1/1] Stewart Smith; [notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread)
+thread:XXX   2009-11-18 [1/1] Stewart Smith; [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (inbox unread)
+thread:XXX   2009-11-18 [2/2] Lars Kellogg-Stedman; [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)
+thread:XXX   2009-11-17 [1/1] Mikhail Gusarov; [notmuch] [PATCH] Handle rename of message file (inbox unread)
+thread:XXX   2009-11-17 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] preliminary FreeBSD support (attachment inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)
+thread:XXX   2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; negative result (inbox unread)
+thread:XXX   2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; this phrase should not match the subject search test (inbox unread)
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Search body (utf-8):"
+add_message '[subject]="utf8-message-body-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="message body utf8: bödý"'
+output=$(notmuch search "bödý" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-message-body-subject (inbox unread)"
+
+test_done
diff --git a/test/T090-search-output.sh b/test/T090-search-output.sh
new file mode 100755 (executable)
index 0000000..5ccfeaf
--- /dev/null
@@ -0,0 +1,405 @@
+#!/usr/bin/env bash
+test_description='various settings for "notmuch search --output="'
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "--output=threads"
+notmuch search --output=threads '*' | sed -e s/thread:.*/thread:THREADID/ >OUTPUT
+cat <<EOF >EXPECTED
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+thread:THREADID
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=threads --format=json"
+notmuch search --format=json --output=threads '*' | sed -e s/\".*\"/\"THREADID\"/ >OUTPUT
+cat <<EOF >EXPECTED
+["THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID",
+"THREADID"]
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--output=messages"
+notmuch search --output=messages '*' >OUTPUT
+cat <<EOF >EXPECTED
+id:4EFC743A.3060609@april.org
+id:877h1wv7mg.fsf@inf-8657.int-evry.fr
+id:1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk
+id:877htoqdbo.fsf@yoom.home.cworth.org
+id:878we4qdqf.fsf@yoom.home.cworth.org
+id:87aaykqe24.fsf@yoom.home.cworth.org
+id:87bpj0qeng.fsf@yoom.home.cworth.org
+id:87fx8cqf8v.fsf@yoom.home.cworth.org
+id:87hbssqfix.fsf@yoom.home.cworth.org
+id:87iqd8qgiz.fsf@yoom.home.cworth.org
+id:87k4xoqgnl.fsf@yoom.home.cworth.org
+id:87ocn0qh6d.fsf@yoom.home.cworth.org
+id:87pr7gqidx.fsf@yoom.home.cworth.org
+id:867hto2p0t.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me
+id:1258532999-9316-1-git-send-email-keithp@keithp.com
+id:86aayk2rbj.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me
+id:86d43g2w3y.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me
+id:ddd65cda0911172214t60d22b63hcfeb5a19ab54a39b@mail.gmail.com
+id:86einw2xof.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me
+id:736613.51770.qm@web113505.mail.gq1.yahoo.com
+id:1258520223-15328-1-git-send-email-jan@ryngle.com
+id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com
+id:1258510940-7018-1-git-send-email-stewart@flamingspork.com
+id:yunzl6kd1w0.fsf@aiko.keithp.com
+id:yun1vjwegii.fsf@aiko.keithp.com
+id:yun3a4cegoa.fsf@aiko.keithp.com
+id:1258509400-32511-1-git-send-email-stewart@flamingspork.com
+id:1258506353-20352-1-git-send-email-stewart@flamingspork.com
+id:20091118010116.GC25380@dottiness.seas.harvard.edu
+id:20091118005829.GB25380@dottiness.seas.harvard.edu
+id:20091118005040.GA25380@dottiness.seas.harvard.edu
+id:cf0c4d610911171623q3e27a0adx802e47039b57604b@mail.gmail.com
+id:1258500222-32066-1-git-send-email-ingmar@exherbo.org
+id:20091117232137.GA7669@griffis1.net
+id:20091118002059.067214ed@hikari
+id:1258498485-sup-142@elly
+id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com
+id:f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com
+id:1258496327-12086-1-git-send-email-jan@ryngle.com
+id:1258493565-13508-1-git-send-email-keithp@keithp.com
+id:yunaayketfm.fsf@aiko.keithp.com
+id:yunbpj0etua.fsf@aiko.keithp.com
+id:1258491078-29658-1-git-send-email-dottedmag@dottedmag.net
+id:87fx8can9z.fsf@vertex.dottedmag
+id:20091117203301.GV3165@dottiness.seas.harvard.edu
+id:87lji4lx9v.fsf@yoom.home.cworth.org
+id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com
+id:87iqd9rn3l.fsf@vertex.dottedmag
+id:20091117190054.GU3165@dottiness.seas.harvard.edu
+id:87lji5cbwo.fsf@yoom.home.cworth.org
+id:1258471718-6781-2-git-send-email-dottedmag@dottedmag.net
+id:1258471718-6781-1-git-send-email-dottedmag@dottedmag.net
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=messages --format=json"
+notmuch search --format=json --output=messages '*' >OUTPUT
+cat <<EOF >EXPECTED
+["4EFC743A.3060609@april.org",
+"877h1wv7mg.fsf@inf-8657.int-evry.fr",
+"1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk",
+"877htoqdbo.fsf@yoom.home.cworth.org",
+"878we4qdqf.fsf@yoom.home.cworth.org",
+"87aaykqe24.fsf@yoom.home.cworth.org",
+"87bpj0qeng.fsf@yoom.home.cworth.org",
+"87fx8cqf8v.fsf@yoom.home.cworth.org",
+"87hbssqfix.fsf@yoom.home.cworth.org",
+"87iqd8qgiz.fsf@yoom.home.cworth.org",
+"87k4xoqgnl.fsf@yoom.home.cworth.org",
+"87ocn0qh6d.fsf@yoom.home.cworth.org",
+"87pr7gqidx.fsf@yoom.home.cworth.org",
+"867hto2p0t.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me",
+"1258532999-9316-1-git-send-email-keithp@keithp.com",
+"86aayk2rbj.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me",
+"86d43g2w3y.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me",
+"ddd65cda0911172214t60d22b63hcfeb5a19ab54a39b@mail.gmail.com",
+"86einw2xof.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me",
+"736613.51770.qm@web113505.mail.gq1.yahoo.com",
+"1258520223-15328-1-git-send-email-jan@ryngle.com",
+"ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com",
+"1258510940-7018-1-git-send-email-stewart@flamingspork.com",
+"yunzl6kd1w0.fsf@aiko.keithp.com",
+"yun1vjwegii.fsf@aiko.keithp.com",
+"yun3a4cegoa.fsf@aiko.keithp.com",
+"1258509400-32511-1-git-send-email-stewart@flamingspork.com",
+"1258506353-20352-1-git-send-email-stewart@flamingspork.com",
+"20091118010116.GC25380@dottiness.seas.harvard.edu",
+"20091118005829.GB25380@dottiness.seas.harvard.edu",
+"20091118005040.GA25380@dottiness.seas.harvard.edu",
+"cf0c4d610911171623q3e27a0adx802e47039b57604b@mail.gmail.com",
+"1258500222-32066-1-git-send-email-ingmar@exherbo.org",
+"20091117232137.GA7669@griffis1.net",
+"20091118002059.067214ed@hikari",
+"1258498485-sup-142@elly",
+"f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com",
+"f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com",
+"1258496327-12086-1-git-send-email-jan@ryngle.com",
+"1258493565-13508-1-git-send-email-keithp@keithp.com",
+"yunaayketfm.fsf@aiko.keithp.com",
+"yunbpj0etua.fsf@aiko.keithp.com",
+"1258491078-29658-1-git-send-email-dottedmag@dottedmag.net",
+"87fx8can9z.fsf@vertex.dottedmag",
+"20091117203301.GV3165@dottiness.seas.harvard.edu",
+"87lji4lx9v.fsf@yoom.home.cworth.org",
+"cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com",
+"87iqd9rn3l.fsf@vertex.dottedmag",
+"20091117190054.GU3165@dottiness.seas.harvard.edu",
+"87lji5cbwo.fsf@yoom.home.cworth.org",
+"1258471718-6781-2-git-send-email-dottedmag@dottedmag.net",
+"1258471718-6781-1-git-send-email-dottedmag@dottedmag.net"]
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=files"
+notmuch search --output=files '*' | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT
+cat <<EOF >EXPECTED
+MAIL_DIR/cur/52:2,
+MAIL_DIR/cur/53:2,
+MAIL_DIR/cur/50:2,
+MAIL_DIR/cur/49:2,
+MAIL_DIR/cur/48:2,
+MAIL_DIR/cur/47:2,
+MAIL_DIR/cur/46:2,
+MAIL_DIR/cur/45:2,
+MAIL_DIR/cur/44:2,
+MAIL_DIR/cur/43:2,
+MAIL_DIR/cur/42:2,
+MAIL_DIR/cur/41:2,
+MAIL_DIR/cur/40:2,
+MAIL_DIR/cur/39:2,
+MAIL_DIR/cur/38:2,
+MAIL_DIR/cur/37:2,
+MAIL_DIR/cur/36:2,
+MAIL_DIR/cur/35:2,
+MAIL_DIR/cur/34:2,
+MAIL_DIR/cur/33:2,
+MAIL_DIR/cur/32:2,
+MAIL_DIR/cur/31:2,
+MAIL_DIR/cur/30:2,
+MAIL_DIR/cur/29:2,
+MAIL_DIR/cur/28:2,
+MAIL_DIR/cur/27:2,
+MAIL_DIR/cur/26:2,
+MAIL_DIR/cur/25:2,
+MAIL_DIR/cur/24:2,
+MAIL_DIR/cur/23:2,
+MAIL_DIR/cur/22:2,
+MAIL_DIR/cur/21:2,
+MAIL_DIR/cur/19:2,
+MAIL_DIR/cur/18:2,
+MAIL_DIR/cur/51:2,
+MAIL_DIR/cur/20:2,
+MAIL_DIR/cur/17:2,
+MAIL_DIR/cur/16:2,
+MAIL_DIR/cur/15:2,
+MAIL_DIR/cur/14:2,
+MAIL_DIR/cur/13:2,
+MAIL_DIR/cur/12:2,
+MAIL_DIR/cur/11:2,
+MAIL_DIR/cur/10:2,
+MAIL_DIR/cur/09:2,
+MAIL_DIR/cur/08:2,
+MAIL_DIR/cur/06:2,
+MAIL_DIR/cur/05:2,
+MAIL_DIR/cur/04:2,
+MAIL_DIR/cur/03:2,
+MAIL_DIR/cur/07:2,
+MAIL_DIR/cur/02:2,
+MAIL_DIR/cur/01:2,
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=files --duplicate=1"
+notmuch search --output=files --duplicate=1 '*' | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT
+cat <<EOF >EXPECTED
+MAIL_DIR/cur/52:2,
+MAIL_DIR/cur/53:2,
+MAIL_DIR/cur/50:2,
+MAIL_DIR/cur/49:2,
+MAIL_DIR/cur/48:2,
+MAIL_DIR/cur/47:2,
+MAIL_DIR/cur/46:2,
+MAIL_DIR/cur/45:2,
+MAIL_DIR/cur/44:2,
+MAIL_DIR/cur/43:2,
+MAIL_DIR/cur/42:2,
+MAIL_DIR/cur/41:2,
+MAIL_DIR/cur/40:2,
+MAIL_DIR/cur/39:2,
+MAIL_DIR/cur/38:2,
+MAIL_DIR/cur/37:2,
+MAIL_DIR/cur/36:2,
+MAIL_DIR/cur/35:2,
+MAIL_DIR/cur/34:2,
+MAIL_DIR/cur/33:2,
+MAIL_DIR/cur/32:2,
+MAIL_DIR/cur/31:2,
+MAIL_DIR/cur/30:2,
+MAIL_DIR/cur/29:2,
+MAIL_DIR/cur/28:2,
+MAIL_DIR/cur/27:2,
+MAIL_DIR/cur/26:2,
+MAIL_DIR/cur/25:2,
+MAIL_DIR/cur/24:2,
+MAIL_DIR/cur/23:2,
+MAIL_DIR/cur/22:2,
+MAIL_DIR/cur/21:2,
+MAIL_DIR/cur/19:2,
+MAIL_DIR/cur/18:2,
+MAIL_DIR/cur/20:2,
+MAIL_DIR/cur/17:2,
+MAIL_DIR/cur/16:2,
+MAIL_DIR/cur/15:2,
+MAIL_DIR/cur/14:2,
+MAIL_DIR/cur/13:2,
+MAIL_DIR/cur/12:2,
+MAIL_DIR/cur/11:2,
+MAIL_DIR/cur/10:2,
+MAIL_DIR/cur/09:2,
+MAIL_DIR/cur/08:2,
+MAIL_DIR/cur/06:2,
+MAIL_DIR/cur/05:2,
+MAIL_DIR/cur/04:2,
+MAIL_DIR/cur/03:2,
+MAIL_DIR/cur/07:2,
+MAIL_DIR/cur/02:2,
+MAIL_DIR/cur/01:2,
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=files --format=json"
+notmuch search --format=json --output=files '*' | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT
+cat <<EOF >EXPECTED
+["MAIL_DIR/cur/52:2,",
+"MAIL_DIR/cur/53:2,",
+"MAIL_DIR/cur/50:2,",
+"MAIL_DIR/cur/49:2,",
+"MAIL_DIR/cur/48:2,",
+"MAIL_DIR/cur/47:2,",
+"MAIL_DIR/cur/46:2,",
+"MAIL_DIR/cur/45:2,",
+"MAIL_DIR/cur/44:2,",
+"MAIL_DIR/cur/43:2,",
+"MAIL_DIR/cur/42:2,",
+"MAIL_DIR/cur/41:2,",
+"MAIL_DIR/cur/40:2,",
+"MAIL_DIR/cur/39:2,",
+"MAIL_DIR/cur/38:2,",
+"MAIL_DIR/cur/37:2,",
+"MAIL_DIR/cur/36:2,",
+"MAIL_DIR/cur/35:2,",
+"MAIL_DIR/cur/34:2,",
+"MAIL_DIR/cur/33:2,",
+"MAIL_DIR/cur/32:2,",
+"MAIL_DIR/cur/31:2,",
+"MAIL_DIR/cur/30:2,",
+"MAIL_DIR/cur/29:2,",
+"MAIL_DIR/cur/28:2,",
+"MAIL_DIR/cur/27:2,",
+"MAIL_DIR/cur/26:2,",
+"MAIL_DIR/cur/25:2,",
+"MAIL_DIR/cur/24:2,",
+"MAIL_DIR/cur/23:2,",
+"MAIL_DIR/cur/22:2,",
+"MAIL_DIR/cur/21:2,",
+"MAIL_DIR/cur/19:2,",
+"MAIL_DIR/cur/18:2,",
+"MAIL_DIR/cur/51:2,",
+"MAIL_DIR/cur/20:2,",
+"MAIL_DIR/cur/17:2,",
+"MAIL_DIR/cur/16:2,",
+"MAIL_DIR/cur/15:2,",
+"MAIL_DIR/cur/14:2,",
+"MAIL_DIR/cur/13:2,",
+"MAIL_DIR/cur/12:2,",
+"MAIL_DIR/cur/11:2,",
+"MAIL_DIR/cur/10:2,",
+"MAIL_DIR/cur/09:2,",
+"MAIL_DIR/cur/08:2,",
+"MAIL_DIR/cur/06:2,",
+"MAIL_DIR/cur/05:2,",
+"MAIL_DIR/cur/04:2,",
+"MAIL_DIR/cur/03:2,",
+"MAIL_DIR/cur/07:2,",
+"MAIL_DIR/cur/02:2,",
+"MAIL_DIR/cur/01:2,"]
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=files --format=json --duplicate=2"
+notmuch search --format=json --output=files --duplicate=2 '*' | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT
+cat <<EOF >EXPECTED
+["MAIL_DIR/cur/51:2,"]
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=tags"
+notmuch search --output=tags '*' >OUTPUT
+cat <<EOF >EXPECTED
+attachment
+inbox
+signed
+unread
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--output=tags --format=json"
+notmuch search --format=json --output=tags '*' >OUTPUT
+cat <<EOF >EXPECTED
+["attachment",
+"inbox",
+"signed",
+"unread"]
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "sanitize output for quoted-printable line-breaks in author and subject"
+add_message "[subject]='two =?ISO-8859-1?Q?line=0A_subject?=
+       headers'"
+notmuch search id:"$gen_msg_id" | notmuch_search_sanitize >OUTPUT
+cat <<EOF >EXPECTED
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; two line? subject headers (inbox unread)
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "search for non-existent message prints nothing"
+notmuch search "no-message-matches-this" > OUTPUT
+echo -n >EXPECTED
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "search --format=json for non-existent message prints proper empty json"
+notmuch search --format=json "no-message-matches-this" > OUTPUT
+echo "[]" >EXPECTED
+test_expect_equal_file OUTPUT EXPECTED
+
+test_done
diff --git a/test/T100-search-by-folder.sh b/test/T100-search-by-folder.sh
new file mode 100755 (executable)
index 0000000..5cc2ca8
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+test_description='"notmuch search" by folder: (with variations)'
+. ./test-lib.sh
+
+add_message '[dir]=bad' '[subject]="To the bone"'
+add_message '[dir]=bad/news' '[subject]="Bears"'
+mkdir -p "${MAIL_DIR}/duplicate/bad/news"
+cp "$gen_msg_filename" "${MAIL_DIR}/duplicate/bad/news"
+
+add_message '[dir]=things' '[subject]="These are a few"'
+add_message '[dir]=things/favorite' '[subject]="Raindrops, whiskers, kettles"'
+add_message '[dir]=things/bad' '[subject]="Bites, stings, sad feelings"'
+
+test_begin_subtest "Single-world folder: specification (multiple results)"
+output=$(notmuch search folder:bad | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
+
+test_begin_subtest "Two-word path to narrow results to one"
+output=$(notmuch search folder:bad/news | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)"
+
+test_begin_subtest "After removing duplicate instance of matching path"
+rm -r "${MAIL_DIR}/bad/news"
+notmuch new
+output=$(notmuch search folder:bad/news | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)"
+
+test_begin_subtest "After rename, old path returns nothing"
+mv "${MAIL_DIR}/duplicate/bad/news" "${MAIL_DIR}/duplicate/bad/olds"
+notmuch new
+output=$(notmuch search folder:bad/news | notmuch_search_sanitize)
+test_expect_equal "$output" ""
+
+test_begin_subtest "After rename, new path returns result"
+output=$(notmuch search folder:bad/olds | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bears (inbox unread)"
+
+test_done
diff --git a/test/T110-search-position-overlap-bug.sh b/test/T110-search-position-overlap-bug.sh
new file mode 100755 (executable)
index 0000000..5da6ad6
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# Test to demonstrate a position overlap bug.
+#
+# At one point, notmuch would index terms incorrectly in the case of
+# calling index_terms multiple times for a single field. The term
+# generator was being reset to position 0 each time. This means that
+# with text such as:
+#
+#      To: a@b.c, x@y.z
+#
+# one could get a bogus match by searching for:
+#
+#      To: a@y.c
+#
+# Thanks to Mark Anderson for reporting the bug, (and providing a nice,
+# minimal test case that inspired what is used here), in
+# id:3wd4o8wa7fx.fsf@testarossa.amd.com
+
+test_description='that notmuch does not overlap term positions'
+. ./test-lib.sh
+
+add_message '[to]="a@b.c, x@y.z"'
+
+test_begin_subtest "Search for a@b.c matches"
+output=$(notmuch search a@b.c | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Test message #1 (inbox unread)"
+
+test_begin_subtest "Search for x@y.z matches"
+output=$(notmuch search x@y.z | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Test message #1 (inbox unread)"
+
+test_begin_subtest "Search for a@y.c must not match"
+output=$(notmuch search a@y.c | notmuch_search_sanitize)
+test_expect_equal "$output" ""
+
+test_done
diff --git a/test/T120-search-insufficient-from-quoting.sh b/test/T120-search-insufficient-from-quoting.sh
new file mode 100755 (executable)
index 0000000..e83ea3d
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+test_description='messages with unquoted . in name'
+. ./test-lib.sh
+
+add_message \
+  '[from]="Some.Name for Someone <bugs@quoting.com>"' \
+  '[subject]="This message needs more quoting on the From line"'
+
+add_message \
+  '[from]="\"Some.Name for Someone\" <bugs@quoting.com>"' \
+  '[subject]="This message has necessary quoting in place"'
+
+add_message \
+  '[from]="No.match Here <filler@mail.com>"' \
+  '[subject]="This message needs more quoting on the From line"'
+
+add_message \
+  '[from]="\"No.match Here\" <filler@mail.com>"' \
+  '[subject]="This message has necessary quoting in place"'
+
+
+test_begin_subtest "Search by first name"
+output=$(notmuch search from:Some.Name | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message needs more quoting on the From line (inbox unread)
+thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message has necessary quoting in place (inbox unread)"
+
+test_begin_subtest "Search by last name:"
+output=$(notmuch search from:Someone | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message needs more quoting on the From line (inbox unread)
+thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message has necessary quoting in place (inbox unread)"
+
+test_begin_subtest "Search by address:"
+output=$(notmuch search from:bugs@quoting.com | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message needs more quoting on the From line (inbox unread)
+thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message has necessary quoting in place (inbox unread)"
+
+test_begin_subtest "Search for all messages:"
+output=$(notmuch search '*' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message needs more quoting on the From line (inbox unread)
+thread:XXX   2001-01-05 [1/1] Some.Name for Someone; This message has necessary quoting in place (inbox unread)
+thread:XXX   2001-01-05 [1/1] No.match Here; This message needs more quoting on the From line (inbox unread)
+thread:XXX   2001-01-05 [1/1] No.match Here; This message has necessary quoting in place (inbox unread)"
+
+test_done
diff --git a/test/T130-search-limiting.sh b/test/T130-search-limiting.sh
new file mode 100755 (executable)
index 0000000..303762c
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+test_description='"notmuch search" --offset and --limit parameters'
+. ./test-lib.sh
+
+add_email_corpus
+
+for outp in messages threads; do
+    test_begin_subtest "${outp}: limit does the right thing"
+    notmuch search --output=${outp} "*" | head -n 20 >expected
+    notmuch search --output=${outp} --limit=20 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: concatenation of limited searches"
+    notmuch search --output=${outp} "*" | head -n 20 >expected
+    notmuch search --output=${outp} --limit=10 "*" >output
+    notmuch search --output=${outp} --limit=10 --offset=10 "*" >>output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: limit larger than result set"
+    N=`notmuch count --output=${outp} "*"`
+    notmuch search --output=${outp} "*" >expected
+    notmuch search --output=${outp} --limit=$((1 + ${N})) "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: limit = 0"
+    test_expect_equal "`notmuch search --output=${outp} --limit=0 "*"`" ""
+
+    test_begin_subtest "${outp}: offset does the right thing"
+    # note: tail -n +N is 1-based
+    notmuch search --output=${outp} "*" | tail -n +21 >expected
+    notmuch search --output=${outp} --offset=20 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: offset = 0"
+    notmuch search --output=${outp} "*" >expected
+    notmuch search --output=${outp} --offset=0 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset"
+    notmuch search --output=${outp} "*" | tail -n 20 >expected
+    notmuch search --output=${outp} --offset=-20 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset"
+    notmuch search --output=${outp} "*" | tail -n 1 >expected
+    notmuch search --output=${outp} --offset=-1 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset combined with limit"
+    notmuch search --output=${outp} "*" | tail -n 20 | head -n 10 >expected
+    notmuch search --output=${outp} --offset=-20 --limit=10 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset combined with equal limit"
+    notmuch search --output=${outp} "*" | tail -n 20 >expected
+    notmuch search --output=${outp} --offset=-20 --limit=20 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset combined with large limit"
+    notmuch search --output=${outp} "*" | tail -n 20 >expected
+    notmuch search --output=${outp} --offset=-20 --limit=50 "*" >output
+    test_expect_equal_file expected output
+
+    test_begin_subtest "${outp}: negative offset larger then results"
+    N=`notmuch count --output=${outp} "*"`
+    notmuch search --output=${outp} "*" >expected
+    notmuch search --output=${outp} --offset=-$((1 + ${N})) "*" >output
+    test_expect_equal_file expected output
+done
+
+test_done
diff --git a/test/T140-excludes.sh b/test/T140-excludes.sh
new file mode 100755 (executable)
index 0000000..8bbbc2d
--- /dev/null
@@ -0,0 +1,445 @@
+#!/usr/bin/env bash
+test_description='"notmuch search, count and show" with excludes in several variations'
+. ./test-lib.sh
+
+# Generates a thread consisting of a top level message and 'length'
+# replies. The subject of the top message 'subject: top message"
+# and the subject of the nth reply in the thread is "subject: reply n"
+generate_thread ()
+{
+    local subject="$1"
+    local length="$2"
+    generate_message '[subject]="'"${subject}: top message"'"' '[body]="'"body of top message"'"'
+    parent_id=$gen_msg_id
+    gen_thread_msg_id[0]=$gen_msg_id
+    for i in `seq 1 $length`
+    do
+       generate_message '[subject]="'"${subject}: reply $i"'"' \
+                        "[in-reply-to]=\<$parent_id\>" \
+                        '[body]="'"body of reply $i"'"'
+       gen_thread_msg_id[$i]=$gen_msg_id
+       parent_id=$gen_msg_id
+    done
+    notmuch new > /dev/null
+    # We cannot retrieve the thread_id until after we have run notmuch new.
+    gen_thread_id=`notmuch search --output=threads id:${gen_thread_msg_id[0]}`
+}
+
+#############################################
+# These are the original search exclude tests.
+
+test_begin_subtest "Search, exclude \"deleted\" messages from search"
+notmuch config set search.exclude_tags deleted
+generate_message '[subject]="Not deleted"'
+not_deleted_id=$gen_msg_id
+generate_message '[subject]="Deleted"'
+notmuch new > /dev/null
+notmuch tag +deleted id:$gen_msg_id
+deleted_id=$gen_msg_id
+output=$(notmuch search subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)"
+
+test_begin_subtest "Search, exclude \"deleted\" messages from message search"
+output=$(notmuch search --output=messages subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "id:$not_deleted_id"
+
+test_begin_subtest "Search, exclude \"deleted\" messages from message search --exclude=false"
+output=$(notmuch search --exclude=false --output=messages subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "id:$not_deleted_id
+id:$deleted_id"
+
+test_begin_subtest "Search, exclude \"deleted\" messages from message search (non-existent exclude-tag)"
+notmuch config set search.exclude_tags deleted non_existent_tag
+output=$(notmuch search --output=messages subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "id:$not_deleted_id"
+notmuch config set search.exclude_tags deleted
+
+test_begin_subtest "Search, exclude \"deleted\" messages from search, overridden"
+output=$(notmuch search subject:deleted and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Deleted (deleted inbox unread)"
+
+test_begin_subtest "Search, exclude \"deleted\" messages from threads"
+add_message '[subject]="Not deleted reply"' '[in-reply-to]="<$gen_msg_id>"'
+output=$(notmuch search subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)
+thread:XXX   2001-01-05 [1/2] Notmuch Test Suite; Not deleted reply (deleted inbox unread)"
+
+test_begin_subtest "Search, don't exclude \"deleted\" messages when --exclude=flag specified"
+output=$(notmuch search --exclude=flag subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)
+thread:XXX   2001-01-05 [1/2] Notmuch Test Suite; Deleted (deleted inbox unread)"
+
+test_begin_subtest "Search, don't exclude \"deleted\" messages from search if not configured"
+notmuch config set search.exclude_tags
+output=$(notmuch search subject:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)
+thread:XXX   2001-01-05 [2/2] Notmuch Test Suite; Deleted (deleted inbox unread)"
+
+
+########################################################
+# We construct some threads for the tests. We use the tag "test" to
+# indicate which messages we will search for.
+
+# A thread of deleted messages; test matches one of them.
+generate_thread "All messages excluded: single match" 5
+notmuch tag +deleted $gen_thread_id
+notmuch tag +test id:${gen_thread_msg_id[2]}
+
+# A thread of deleted messages; test matches two of them.
+generate_thread "All messages excluded: double match" 5
+notmuch tag +deleted $gen_thread_id
+notmuch tag +test id:${gen_thread_msg_id[2]}
+notmuch tag +test id:${gen_thread_msg_id[4]}
+
+# A thread some messages deleted; test only matches a deleted message.
+generate_thread "Some messages excluded: single excluded match" 5
+notmuch tag +deleted +test id:${gen_thread_msg_id[3]}
+
+# A thread some messages deleted; test only matches a non-deleted message.
+generate_thread "Some messages excluded: single non-excluded match" 5
+notmuch tag +deleted id:${gen_thread_msg_id[2]}
+notmuch tag +test id:${gen_thread_msg_id[4]}
+
+# A thread no messages deleted; test matches a message.
+generate_thread "No messages excluded: single match" 5
+notmuch tag +test id:${gen_thread_msg_id[3]}
+
+# Temporarily remove excludes to get list of matching messages
+notmuch config set search.exclude_tags
+matching_message_ids=( `notmuch search --output=messages tag:test` )
+notmuch config set search.exclude_tags deleted
+
+#########################################
+# Notmuch search tests
+
+test_begin_subtest "Search, default exclusion (thread summary)"
+output=$(notmuch search tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single non-excluded match: reply 4 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; No messages excluded: single match: reply 3 (inbox test unread)"
+
+test_begin_subtest "Search, default exclusion (messages)"
+output=$(notmuch search --output=messages tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[4]}
+${matching_message_ids[5]}"
+
+test_begin_subtest "Search, exclude=true (thread summary)"
+output=$(notmuch search --exclude=true tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single non-excluded match: reply 4 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; No messages excluded: single match: reply 3 (inbox test unread)"
+
+test_begin_subtest "Search, exclude=true (messages)"
+output=$(notmuch search --exclude=true --output=messages tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[4]}
+${matching_message_ids[5]}"
+
+test_begin_subtest "Search, exclude=false (thread summary)"
+output=$(notmuch search --exclude=false tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [2/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single non-excluded match: reply 4 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; No messages excluded: single match: reply 3 (inbox test unread)"
+
+test_begin_subtest "Search, exclude=false (messages)"
+output=$(notmuch search --exclude=false --output=messages tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}
+${matching_message_ids[4]}
+${matching_message_ids[5]}"
+
+test_begin_subtest "Search, exclude=flag (thread summary)"
+output=$(notmuch search --exclude=flag tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [0/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [0/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [0/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single non-excluded match: reply 4 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; No messages excluded: single match: reply 3 (inbox test unread)"
+
+test_begin_subtest "Search, exclude=flag (messages)"
+output=$(notmuch search --exclude=flag --output=messages tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}
+${matching_message_ids[4]}
+${matching_message_ids[5]}"
+
+test_begin_subtest "Search, exclude=all (thread summary)"
+output=$(notmuch search --exclude=all tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/5] Notmuch Test Suite; Some messages excluded: single non-excluded match: reply 4 (inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; No messages excluded: single match: reply 3 (inbox test unread)"
+
+test_begin_subtest "Search, exclude=all (messages)"
+output=$(notmuch search --exclude=all --output=messages tag:test | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[4]}
+${matching_message_ids[5]}"
+
+test_begin_subtest "Search, default exclusion: tag in query (thread summary)"
+output=$(notmuch search tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [2/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)"
+
+test_begin_subtest "Search, default exclusion: tag in query (messages)"
+output=$(notmuch search --output=messages tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}"
+
+test_begin_subtest "Search, exclude=true: tag in query (thread summary)"
+output=$(notmuch search --exclude=true tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [2/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)"
+
+test_begin_subtest "Search, exclude=true: tag in query (messages)"
+output=$(notmuch search --exclude=true --output=messages tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}"
+
+test_begin_subtest "Search, exclude=false: tag in query (thread summary)"
+output=$(notmuch search --exclude=false tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [2/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)"
+
+test_begin_subtest "Search, exclude=false: tag in query (messages)"
+output=$(notmuch search --exclude=false --output=messages tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}"
+
+test_begin_subtest "Search, exclude=flag: tag in query (thread summary)"
+output=$(notmuch search --exclude=flag tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [2/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)"
+
+test_begin_subtest "Search, exclude=flag: tag in query (messages)"
+output=$(notmuch search --exclude=flag --output=messages tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}"
+
+test_begin_subtest "Search, exclude=all: tag in query (thread summary)"
+output=$(notmuch search --exclude=all tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; All messages excluded: single match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [2/6] Notmuch Test Suite; All messages excluded: double match: reply 2 (deleted inbox test unread)
+thread:XXX   2001-01-05 [1/6] Notmuch Test Suite; Some messages excluded: single excluded match: reply 3 (deleted inbox test unread)"
+
+test_begin_subtest "Search, exclude=all: tag in query (messages)"
+output=$(notmuch search --exclude=all --output=messages tag:test and tag:deleted | notmuch_search_sanitize)
+test_expect_equal "$output" "${matching_message_ids[0]}
+${matching_message_ids[1]}
+${matching_message_ids[2]}
+${matching_message_ids[3]}"
+
+#########################################################
+# Notmuch count tests
+
+test_begin_subtest "Count, default exclusion (messages)"
+output=$(notmuch count tag:test)
+test_expect_equal "$output" "2"
+
+test_begin_subtest "Count, default exclusion (threads)"
+output=$(notmuch count --output=threads tag:test)
+test_expect_equal "$output" "2"
+
+test_begin_subtest "Count, exclude=true (messages)"
+output=$(notmuch count --exclude=true tag:test)
+test_expect_equal "$output" "2"
+
+test_begin_subtest "Count, exclude=true (threads)"
+output=$(notmuch count --output=threads --exclude=true tag:test)
+test_expect_equal "$output" "2"
+
+test_begin_subtest "Count, exclude=false (messages)"
+output=$(notmuch count --exclude=false tag:test)
+test_expect_equal "$output" "6"
+
+test_begin_subtest "Count, exclude=false (threads)"
+output=$(notmuch count --output=threads --exclude=false tag:test)
+test_expect_equal "$output" "5"
+
+test_begin_subtest "Count, default exclusion: tag in query (messages)"
+output=$(notmuch count tag:test and tag:deleted)
+test_expect_equal "$output" "4"
+
+test_begin_subtest "Count, default exclusion: tag in query (threads)"
+output=$(notmuch count --output=threads tag:test and tag:deleted)
+test_expect_equal "$output" "3"
+
+test_begin_subtest "Count, exclude=true: tag in query (messages)"
+output=$(notmuch count --exclude=true tag:test and tag:deleted)
+test_expect_equal "$output" "4"
+
+test_begin_subtest "Count, exclude=true: tag in query (threads)"
+output=$(notmuch count --output=threads --exclude=true tag:test and tag:deleted)
+test_expect_equal "$output" "3"
+
+test_begin_subtest "Count, exclude=false: tag in query (messages)"
+output=$(notmuch count --exclude=false tag:test and tag:deleted)
+test_expect_equal "$output" "4"
+
+test_begin_subtest "Count, exclude=false: tag in query (threads)"
+output=$(notmuch count --output=threads --exclude=false tag:test and tag:deleted)
+test_expect_equal "$output" "3"
+
+#############################################################
+# Show tests
+
+test_begin_subtest "Show, default exclusion"
+output=$(notmuch show tag:test | notmuch_show_sanitize_all | egrep "Subject:|message{")
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 4
+\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 3"
+
+test_begin_subtest "Show, default exclusion (entire-thread)"
+output=$(notmuch show --entire-thread tag:test | notmuch_show_sanitize_all | egrep "Subject:|message{")
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:1 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 2
+\fmessage{ id:XXXXX depth:3 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 3
+\fmessage{ id:XXXXX depth:4 match:1 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 5
+\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 2
+\fmessage{ id:XXXXX depth:3 match:1 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 3
+\fmessage{ id:XXXXX depth:4 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 5"
+
+test_begin_subtest "Show, exclude=true"
+output=$(notmuch show --exclude=true tag:test | notmuch_show_sanitize_all | egrep "Subject:|message{")
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 4
+\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 3"
+
+test_begin_subtest "Show, exclude=true (entire-thread)"
+output=$(notmuch show --entire-thread --exclude=true tag:test | notmuch_show_sanitize_all | egrep "Subject:|message{")
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:1 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 2
+\fmessage{ id:XXXXX depth:3 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 3
+\fmessage{ id:XXXXX depth:4 match:1 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 5
+\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 2
+\fmessage{ id:XXXXX depth:3 match:1 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 3
+\fmessage{ id:XXXXX depth:4 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 5"
+
+test_begin_subtest "Show, exclude=false"
+output=$(notmuch show --exclude=false tag:test | notmuch_show_sanitize_all  | egrep "Subject:|message{")
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:1 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: reply 2
+\fmessage{ id:XXXXX depth:0 match:1 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 2
+\fmessage{ id:XXXXX depth:1 match:1 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 4
+\fmessage{ id:XXXXX depth:0 match:1 excluded:1 filename:XXXXX
+Subject: Some messages excluded: single excluded match: reply 3
+\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 4
+\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 3"
+
+test_begin_subtest "Show, exclude=false (entire-thread)"
+output=$(notmuch show --entire-thread --exclude=false tag:test | notmuch_show_sanitize_all | egrep "Subject:|message{")
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: reply 1
+\fmessage{ id:XXXXX depth:2 match:1 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: reply 2
+\fmessage{ id:XXXXX depth:3 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: reply 3
+\fmessage{ id:XXXXX depth:4 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: single match: reply 5
+\fmessage{ id:XXXXX depth:0 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 1
+\fmessage{ id:XXXXX depth:2 match:1 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 2
+\fmessage{ id:XXXXX depth:3 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 3
+\fmessage{ id:XXXXX depth:4 match:1 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:1 filename:XXXXX
+Subject: All messages excluded: double match: reply 5
+\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single excluded match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single excluded match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single excluded match: reply 2
+\fmessage{ id:XXXXX depth:3 match:1 excluded:1 filename:XXXXX
+Subject: Some messages excluded: single excluded match: reply 3
+\fmessage{ id:XXXXX depth:4 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single excluded match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single excluded match: reply 5
+\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:1 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 2
+\fmessage{ id:XXXXX depth:3 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 3
+\fmessage{ id:XXXXX depth:4 match:1 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: Some messages excluded: single non-excluded match: reply 5
+\fmessage{ id:XXXXX depth:0 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: top message
+\fmessage{ id:XXXXX depth:1 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 1
+\fmessage{ id:XXXXX depth:2 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 2
+\fmessage{ id:XXXXX depth:3 match:1 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 3
+\fmessage{ id:XXXXX depth:4 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 4
+\fmessage{ id:XXXXX depth:5 match:0 excluded:0 filename:XXXXX
+Subject: No messages excluded: single match: reply 5"
+
+
+test_done
diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh
new file mode 100755 (executable)
index 0000000..dc118f3
--- /dev/null
@@ -0,0 +1,264 @@
+#!/usr/bin/env bash
+test_description='"notmuch tag"'
+. ./test-lib.sh
+
+add_message '[subject]=One'
+add_message '[subject]=Two'
+
+test_begin_subtest "Adding tags"
+notmuch tag +tag1 +tag2 +tag3 \*
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 tag2 tag3 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag2 tag3 unread)"
+
+test_begin_subtest "Removing tags"
+notmuch tag -tag1 -tag2 \*
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag3 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag3 unread)"
+
+test_expect_code 1 "No tag operations" 'notmuch tag One'
+test_expect_code 1 "No query" 'notmuch tag +tag2'
+
+test_begin_subtest "Redundant tagging"
+notmuch tag +tag1 -tag3 One
+notmuch tag +tag1 -tag3 \*
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)"
+
+test_begin_subtest "Remove all"
+notmuch tag --remove-all One
+notmuch tag --remove-all +tag5 +tag6 +unread Two
+output=$(notmuch search \* | notmuch_search_sanitize)
+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 a no-op"
+notmuch tag +inbox +tag1 +unread One
+notmuch tag --remove-all +foo +inbox +tag1 -foo +unread Two
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)"
+
+test_begin_subtest "Special characters in tags"
+notmuch tag +':" ' \*
+notmuch tag -':" ' Two
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)"
+
+test_begin_subtest "Tagging order"
+notmuch tag +tag4 -tag4 One
+notmuch tag -tag4 +tag4 Two
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)"
+
+test_begin_subtest "--batch"
+notmuch tag --batch <<EOF
+# %20 is a space in tag
+-:"%20 -tag1 +tag5 +tag6 -- One
++tag1 -tag1 -tag4 +tag4 -- Two
+-tag6 One
++tag5 Two
+EOF
+output=$(notmuch search \* | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag5 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag5 unread)"
+
+# generate a common input file for the next several tests.
+cat > batch.in  <<EOF
+# %40 is an @ in tag
++%40 -tag5 +tag6 -- One
++tag1 -tag1 -tag4 +tag4 -- Two
+-tag5 +tag6 Two
+EOF
+
+cat > batch.expected <<EOF
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (@ inbox tag6 unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag4 tag6 unread)
+EOF
+
+test_begin_subtest "--input"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
+test_begin_subtest "--batch --input"
+notmuch dump --format=batch-tag > backup.tags
+notmuch tag --batch --input=batch.in
+notmuch search \* | notmuch_search_sanitize > OUTPUT
+notmuch restore --format=batch-tag < backup.tags
+test_expect_equal_file batch.expected OUTPUT
+
+test_begin_subtest "--batch, blank lines and comments"
+notmuch dump | sort > EXPECTED
+notmuch tag --batch <<EOF
+# this line is a comment; the next has only white space
+        
+
+# the previous line is empty
+EOF
+notmuch dump | sort > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: checking error messages'
+notmuch dump --format=batch-tag > BACKUP
+notmuch tag --batch <<EOF 2>OUTPUT
+# the next line has a space
+# this line has no tag operations, but this is permitted in batch format.
+a
++0
++a +b
+# trailing whitespace
++a +b 
++c +d --
+# this is a harmless comment, do not yell about it.
+
+# the previous line was blank; also no yelling please
++%zz -- id:whatever
+# the next non-comment line should report an an empty tag error for
+# batch tagging, but not for restore
++ +e -- id:foo
++- -- id:foo
+EOF
+
+cat <<EOF > EXPECTED
+Warning: no query string [+0]
+Warning: no query string [+a +b]
+Warning: missing query string [+a +b ]
+Warning: no query string after -- [+c +d --]
+Warning: hex decoding of tag %zz failed [+%zz -- id:whatever]
+Warning: empty tag forbidden [+ +e -- id:foo]
+Warning: tag starting with '-' forbidden [+- -- id:foo]
+EOF
+
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: tags with quotes'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch <<EOF
++%22%27%22%27%22%22%27%27 -- One
+-%22%27%22%27%22%22%27%27 -- One
++%22%27%22%22%22%27 -- One
++%22%27%22%27%22%22%27%27 -- Two
+EOF
+
+cat <<EOF > EXPECTED
++%22%27%22%22%22%27 +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite
++%22%27%22%27%22%22%27%27 +inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: tags with punctuation and space'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch <<EOF
++%21@%23%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e -- One
+-%21@%23%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e -- One
++%21@%23%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%20%60%7e -- Two
+-%21@%23%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%20%60%7e -- Two
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e -- One
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e -- Two
+EOF
+
+cat <<EOF > EXPECTED
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e +inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite
++%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: unicode tags'
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch <<EOF
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 -- One
++=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d -- One
++A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27 -- One
++R -- One
++%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 -- One
++%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- One
++L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 -- One
++P%c4%98%2f -- One
++%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d -- One
++%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b -- One
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7  +=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d  +A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27  +R  +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6  +%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d  +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1  +P%c4%98%2f  +%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d  +%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b -- Two
+EOF
+
+cat <<EOF > EXPECTED
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 +=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d +A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 +P%c4%98%2f +R +inbox +tag4 +tag5 +unread +%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d +%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 +%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-002@notmuch-test-suite
++%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 +=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d +A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 +P%c4%98%2f +R +inbox +tag5 +unread +%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d +%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 +%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-001@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "--batch: only space and % needs to be encoded."
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag --batch <<EOF
++winner *
++foo::bar%25 -- (One and Two) or (One and tag:winner)
++found::it -- tag:foo::bar%
+# ignore this line and the next
+
++space%20in%20tags -- Two
+# add tag '(tags)', among other stunts.
++crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
++match*crazy -- tag:crazy{
++some_tag -- id:"this is ""nauty)"""
+EOF
+
+cat <<EOF > EXPECTED
++%23possible%5c +%26are +%28tags%29 +crazy%7b +inbox +match%2acrazy +space%20in%20tags +tag4 +tag5 +unread +winner -- id:msg-002@notmuch-test-suite
++foo%3a%3abar%25 +found%3a%3ait +inbox +tag5 +unread +winner -- id:msg-001@notmuch-test-suite
+EOF
+
+notmuch dump --format=batch-tag | sort > OUTPUT
+notmuch restore --format=batch-tag < BACKUP
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest '--batch: unicode message-ids'
+
+${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
+     --num-messages=100
+
+notmuch dump --format=batch-tag | sed 's/^.* -- /+common_tag -- /' | \
+    sort > EXPECTED
+
+notmuch dump --format=batch-tag | sed 's/^.* -- /  -- /' | \
+    notmuch restore --format=batch-tag
+
+notmuch tag --batch < EXPECTED
+
+notmuch dump --format=batch-tag| \
+    sort > OUTPUT
+
+test_expect_equal_file EXPECTED OUTPUT
+
+test_expect_code 1 "Empty tag names" 'notmuch tag + One'
+
+test_expect_code 1 "Tag name beginning with -" 'notmuch tag +- One'
+
+test_done
diff --git a/test/T160-json.sh b/test/T160-json.sh
new file mode 100755 (executable)
index 0000000..c1cf649
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+test_description="--format=json output"
+. ./test-lib.sh
+
+test_begin_subtest "Show message: json"
+add_message "[subject]=\"json-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[bcc]=\"test_suite+bcc@notmuchmail.org\"" "[reply-to]=\"test_suite+replyto@notmuchmail.org\"" "[body]=\"json-show-message\""
+output=$(notmuch show --format=json "json-show-message")
+test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
+
+# This should be the same output as above.
+test_begin_subtest "Show message: json --body=true"
+output=$(notmuch show --format=json --body=true "json-show-message")
+test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
+
+test_begin_subtest "Show message: json --body=false"
+output=$(notmuch show --format=json --body=false "json-show-message")
+test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}}, []]]]"
+
+test_begin_subtest "Search message: json"
+add_message "[subject]=\"json-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-search-message\""
+output=$(notmuch search --format=json "json-search-message" | notmuch_search_sanitize)
+test_expect_equal_json "$output" "[{\"thread\": \"XXX\",
+ \"timestamp\": 946728000,
+ \"date_relative\": \"2000-01-01\",
+ \"matched\": 1,
+ \"total\": 1,
+ \"authors\": \"Notmuch Test Suite\",
+ \"subject\": \"json-search-subject\",
+ \"query\": [\"id:$gen_msg_id\", null],
+ \"tags\": [\"inbox\",
+ \"unread\"]}]"
+
+test_begin_subtest "Show message: json, utf-8"
+add_message "[subject]=\"json-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""
+output=$(notmuch show --format=json "jsön-show-méssage")
+test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"excluded\": false, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"
+
+test_begin_subtest "Show message: json, inline attachment filename"
+subject='json-show-inline-attachment-filename'
+id="json-show-inline-attachment-filename@notmuchmail.org"
+emacs_fcc_message \
+    "$subject" \
+    'This is a test message with inline attachment with a filename' \
+    "(mml-attach-file \"$TEST_DIRECTORY/README\" nil nil \"inline\")
+     (message-goto-eoh)
+     (insert \"Message-ID: <$id>\n\")"
+output=$(notmuch show --format=json "id:$id")
+filename=$(notmuch search --output=files "id:$id")
+# Get length of README after base64-encoding, minus additional newline.
+attachment_length=$(( $(base64 $TEST_DIRECTORY/README | wc -c) - 1 ))
+test_expect_equal_json "$output" "[[[{\"id\": \"$id\", \"match\": true, \"excluded\": false, \"filename\": \"$filename\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"content-length\": $attachment_length, \"content-transfer-encoding\": \"base64\", \"filename\": \"README\"}]}]}, []]]]"
+
+test_begin_subtest "Search message: json, utf-8"
+add_message "[subject]=\"json-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
+output=$(notmuch search --format=json "jsön-search-méssage" | notmuch_search_sanitize)
+test_expect_equal_json "$output" "[{\"thread\": \"XXX\",
+ \"timestamp\": 946728000,
+ \"date_relative\": \"2000-01-01\",
+ \"matched\": 1,
+ \"total\": 1,
+ \"authors\": \"Notmuch Test Suite\",
+ \"subject\": \"json-search-utf8-body-sübjéct\",
+ \"query\": [\"id:$gen_msg_id\", null],
+ \"tags\": [\"inbox\",
+ \"unread\"]}]"
+
+test_expect_code 20 "Format version: too low" \
+    "notmuch search --format-version=0 \\*"
+
+test_expect_code 21 "Format version: too high" \
+    "notmuch search --format-version=999 \\*"
+
+test_done
diff --git a/test/T170-sexp.sh b/test/T170-sexp.sh
new file mode 100755 (executable)
index 0000000..667e319
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+test_description="--format=sexp output"
+. ./test-lib.sh
+
+test_begin_subtest "Show message: sexp"
+add_message "[subject]=\"sexp-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[bcc]=\"test_suite+bcc@notmuchmail.org\"" "[reply-to]=\"test_suite+replyto@notmuchmail.org\"" "[body]=\"sexp-show-message\""
+output=$(notmuch show --format=sexp "sexp-show-message")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\"))) ())))"
+
+# This should be the same output as above.
+test_begin_subtest "Show message: sexp --body=true"
+output=$(notmuch show --format=sexp --body=true "sexp-show-message")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\"))) ())))"
+
+test_begin_subtest "Show message: sexp --body=false"
+output=$(notmuch show --format=sexp --body=false "sexp-show-message")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+
+test_begin_subtest "Search message: sexp"
+add_message "[subject]=\"sexp-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"sexp-search-message\""
+output=$(notmuch search --format=sexp "sexp-search-message" | notmuch_search_sanitize)
+test_expect_equal "$output" "((:thread \"0000000000000002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))"
+
+test_begin_subtest "Show message: sexp, utf-8"
+add_message "[subject]=\"sexp-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""
+output=$(notmuch show --format=sexp "jsön-show-méssage")
+test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename \"${gen_msg_filename}\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :headers (:Subject \"sexp-show-utf8-body-sübjéct\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"text/plain\" :content \"jsön-show-méssage\n\"))) ())))"
+
+test_begin_subtest "Show message: sexp, inline attachment filename"
+subject='sexp-show-inline-attachment-filename'
+id="sexp-show-inline-attachment-filename@notmuchmail.org"
+emacs_fcc_message \
+    "$subject" \
+    'This is a test message with inline attachment with a filename' \
+    "(mml-attach-file \"$TEST_DIRECTORY/README\" nil nil \"inline\")
+     (message-goto-eoh)
+     (insert \"Message-ID: <$id>\n\")"
+output=$(notmuch show --format=sexp "id:$id")
+filename=$(notmuch search --output=files "id:$id")
+# Get length of README after base64-encoding, minus additional newline.
+attachment_length=$(( $(base64 $TEST_DIRECTORY/README | wc -c) - 1 ))
+test_expect_equal "$output" "((((:id \"$id\" :match t :excluded nil :filename \"$filename\" :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\") :headers (:Subject \"sexp-show-inline-attachment-filename\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"test_suite@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\") :body ((:id 1 :content-type \"multipart/mixed\" :content ((:id 2 :content-type \"text/plain\" :content \"This is a test message with inline attachment with a filename\") (:id 3 :content-type \"application/octet-stream\" :filename \"README\" :content-transfer-encoding \"base64\" :content-length $attachment_length))))) ())))"
+
+test_begin_subtest "Search message: sexp, utf-8"
+add_message "[subject]=\"sexp-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
+output=$(notmuch search --format=sexp "jsön-search-méssage" | notmuch_search_sanitize)
+test_expect_equal "$output" "((:thread \"0000000000000005\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-utf8-body-sübjéct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))"
+
+
+test_done
diff --git a/test/T180-text.sh b/test/T180-text.sh
new file mode 100755 (executable)
index 0000000..b5ccefc
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/env bash
+test_description="--format=text output"
+. ./test-lib.sh
+
+test_begin_subtest "Show message: text"
+add_message "[subject]=\"text-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"text-show-message\""
+output=$(notmuch show --format=text "text-show-message" | notmuch_show_sanitize_all)
+test_expect_equal "$output" "\
+\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2000-01-01) (inbox unread)
+Subject: text-show-subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Sat, 01 Jan 2000 12:00:00 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+text-show-message
+\fpart}
+\fbody}
+\fmessage}"
+
+test_begin_subtest "Search message: text"
+add_message "[subject]=\"text-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"text-search-message\""
+output=$(notmuch search --format=text "text-search-message" | notmuch_search_sanitize)
+test_expect_equal "$output" \
+"thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; text-search-subject (inbox unread)"
+
+test_begin_subtest "Show message: text, utf-8"
+add_message "[subject]=\"text-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"tëxt-show-méssage\""
+output=$(notmuch show --format=text "tëxt-show-méssage" | notmuch_show_sanitize_all)
+test_expect_equal "$output" "\
+\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2000-01-01) (inbox unread)
+Subject: text-show-utf8-body-sübjéct
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Sat, 01 Jan 2000 12:00:00 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+tëxt-show-méssage
+\fpart}
+\fbody}
+\fmessage}"
+
+test_begin_subtest "Search message: text, utf-8"
+add_message "[subject]=\"text-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"tëxt-search-méssage\""
+output=$(notmuch search --format=text "tëxt-search-méssage" | notmuch_search_sanitize)
+test_expect_equal "$output" \
+"thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; text-search-utf8-body-sübjéct (inbox unread)"
+
+add_email_corpus
+
+test_begin_subtest "Search message tags: text0"
+cat <<EOF > EXPECTED
+attachment inbox signed unread
+EOF
+notmuch search --format=text0 --output=tags '*' | xargs -0 | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+# Use tr(1) to convert --output=text0 to --output=text for
+# comparison. Also translate newlines to spaces to fail with more
+# noise if they are present as delimiters instead of null
+# characters. This assumes there are no newlines in the data.
+test_begin_subtest "Compare text vs. text0 for threads"
+notmuch search --format=text --output=threads '*' | notmuch_search_sanitize > EXPECTED
+notmuch search --format=text0 --output=threads '*' | tr "\n\0" " \n" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "Compare text vs. text0 for messages"
+notmuch search --format=text --output=messages '*' | notmuch_search_sanitize > EXPECTED
+notmuch search --format=text0 --output=messages '*' | tr "\n\0" " \n" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "Compare text vs. text0 for files"
+notmuch search --format=text --output=files '*' | notmuch_search_sanitize > EXPECTED
+notmuch search --format=text0 --output=files '*' | tr "\n\0" " \n" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "Compare text vs. text0 for tags"
+notmuch search --format=text --output=tags '*' | notmuch_search_sanitize > EXPECTED
+notmuch search --format=text0 --output=tags '*' | tr "\n\0" " \n" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
diff --git a/test/T190-multipart.sh b/test/T190-multipart.sh
new file mode 100755 (executable)
index 0000000..85cbf67
--- /dev/null
@@ -0,0 +1,730 @@
+#!/usr/bin/env bash
+test_description="output of multipart message"
+. ./test-lib.sh
+
+cat <<EOF > embedded_message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Subject: html message
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu)
+Message-ID: <87liy5ap01.fsf@yoom.home.cworth.org>
+MIME-Version: 1.0
+Content-Type: multipart/alternative; boundary="==-=-=="
+
+--==-=-==
+Content-Type: text/html
+
+<p>This is an embedded message, with a multipart/alternative part.</p>
+
+--==-=-==
+Content-Type: text/plain
+
+This is an embedded message, with a multipart/alternative part.
+
+--==-=-==--
+EOF
+
+cat <<EOF > ${MAIL_DIR}/multipart
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Subject: Multipart message
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu)
+Message-ID: <87liy5ap00.fsf@yoom.home.cworth.org>
+MIME-Version: 1.0
+Content-Type: multipart/signed; boundary="==-=-=";
+       micalg=pgp-sha1; protocol="application/pgp-signature"
+
+--==-=-=
+Content-Type: multipart/mixed; boundary="=-=-="
+
+--=-=-=
+Content-Type: message/rfc822
+Content-Disposition: inline
+
+EOF
+cat embedded_message >> ${MAIL_DIR}/multipart
+cat <<EOF >> ${MAIL_DIR}/multipart
+
+--=-=-=
+Content-Disposition: attachment; filename=attachment
+
+This is a text attachment.
+
+--=-=-=
+
+And this message is signed.
+
+-Carl
+
+--=-=-=--
+
+--==-=-=
+Content-Type: application/pgp-signature
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iEYEARECAAYFAk3SA/gACgkQ6JDdNq8qSWj0sACghqVJEQJUs3yV8zbTzhgnSIcD
+W6cAmQE4dcYrx/LPLtYLZm1jsGauE5hE
+=zkga
+-----END PGP SIGNATURE-----
+--==-=-=--
+EOF
+
+cat <<EOF > ${MAIL_DIR}/base64-part-with-crlf
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Subject: Test message with a BASE64 encoded binary containing CRLF pair
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu)
+Message-ID: <base64-part-with-crlf>
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="==-=-=";
+
+--==-=-=
+
+The attached BASE64-encoded part expands to a binary containing a CRLF
+pair (that is one bye of 0x0D followed by one byte of 0x0A). This is
+designed to ensure that notmuch is not corrupting the output of this
+part by converting the CRLF pair to an LF only (as would be appropriate
+for display of a text part on a Linux system, for example).
+
+The part should be a 3-byte file with the following sequence of 3
+hexadecimal bytes:
+
+       EF 0D 0A
+
+--==-=-=
+Content-Type: application/octet-stream
+Content-Disposition: attachment; filename=crlf.bin
+Content-Transfer-Encoding: base64
+
+7w0K
+--==-=-=--
+EOF
+notmuch new > /dev/null
+
+test_begin_subtest "--format=text --part=0, full message"
+notmuch show --format=text --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fmessage{ id:87liy5ap00.fsf@yoom.home.cworth.org depth:0 match:1 excluded:0 filename:${MAIL_DIR}/multipart
+\fheader{
+Carl Worth <cworth@cworth.org> (2001-01-05) (attachment inbox signed unread)
+Subject: Multipart message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: multipart/signed
+\fpart{ ID: 2, Content-type: multipart/mixed
+\fpart{ ID: 3, Content-type: message/rfc822
+\fheader{
+Subject: html message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 4, Content-type: multipart/alternative
+\fpart{ ID: 5, Content-type: text/html
+Non-text part: text/html
+\fpart}
+\fpart{ ID: 6, Content-type: text/plain
+This is an embedded message, with a multipart/alternative part.
+\fpart}
+\fpart}
+\fbody}
+\fpart}
+\fattachment{ ID: 7, Filename: attachment, Content-type: text/plain
+This is a text attachment.
+\fattachment}
+\fpart{ ID: 8, Content-type: text/plain
+And this message is signed.
+
+-Carl
+\fpart}
+\fpart}
+\fpart{ ID: 9, Content-type: application/pgp-signature
+Non-text part: application/pgp-signature
+\fpart}
+\fpart}
+\fbody}
+\fmessage}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=1, message body"
+notmuch show --format=text --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 1, Content-type: multipart/signed
+\fpart{ ID: 2, Content-type: multipart/mixed
+\fpart{ ID: 3, Content-type: message/rfc822
+\fheader{
+Subject: html message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 4, Content-type: multipart/alternative
+\fpart{ ID: 5, Content-type: text/html
+Non-text part: text/html
+\fpart}
+\fpart{ ID: 6, Content-type: text/plain
+This is an embedded message, with a multipart/alternative part.
+\fpart}
+\fpart}
+\fbody}
+\fpart}
+\fattachment{ ID: 7, Filename: attachment, Content-type: text/plain
+This is a text attachment.
+\fattachment}
+\fpart{ ID: 8, Content-type: text/plain
+And this message is signed.
+
+-Carl
+\fpart}
+\fpart}
+\fpart{ ID: 9, Content-type: application/pgp-signature
+Non-text part: application/pgp-signature
+\fpart}
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=2, multipart/mixed"
+notmuch show --format=text --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 2, Content-type: multipart/mixed
+\fpart{ ID: 3, Content-type: message/rfc822
+\fheader{
+Subject: html message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 4, Content-type: multipart/alternative
+\fpart{ ID: 5, Content-type: text/html
+Non-text part: text/html
+\fpart}
+\fpart{ ID: 6, Content-type: text/plain
+This is an embedded message, with a multipart/alternative part.
+\fpart}
+\fpart}
+\fbody}
+\fpart}
+\fattachment{ ID: 7, Filename: attachment, Content-type: text/plain
+This is a text attachment.
+\fattachment}
+\fpart{ ID: 8, Content-type: text/plain
+And this message is signed.
+
+-Carl
+\fpart}
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=3, rfc822 part"
+notmuch show --format=text --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 3, Content-type: message/rfc822
+\fheader{
+Subject: html message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 4, Content-type: multipart/alternative
+\fpart{ ID: 5, Content-type: text/html
+Non-text part: text/html
+\fpart}
+\fpart{ ID: 6, Content-type: text/plain
+This is an embedded message, with a multipart/alternative part.
+\fpart}
+\fpart}
+\fbody}
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=4, rfc822's multipart"
+notmuch show --format=text --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 4, Content-type: multipart/alternative
+\fpart{ ID: 5, Content-type: text/html
+Non-text part: text/html
+\fpart}
+\fpart{ ID: 6, Content-type: text/plain
+This is an embedded message, with a multipart/alternative part.
+\fpart}
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=5, rfc822's html part"
+notmuch show --format=text --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 5, Content-type: text/html
+Non-text part: text/html
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=6, rfc822's text part"
+notmuch show --format=text --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 6, Content-type: text/plain
+This is an embedded message, with a multipart/alternative part.
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=7, inline attachement"
+notmuch show --format=text --part=7 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fattachment{ ID: 7, Filename: attachment, Content-type: text/plain
+This is a text attachment.
+\fattachment}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=8, plain text part"
+notmuch show --format=text --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 8, Content-type: text/plain
+And this message is signed.
+
+-Carl
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=text --part=9, pgp signature (unverified)"
+notmuch show --format=text --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 9, Content-type: application/pgp-signature
+Non-text part: application/pgp-signature
+\fpart}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_expect_success \
+    "--format=text --part=8, no part, expect error" \
+    "notmuch show --format=text --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org'"
+
+test_begin_subtest "--format=json --part=0, full message"
+notmuch show --format=json --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": "87liy5ap00.fsf@yoom.home.cworth.org", "match": true, "excluded": false, "filename": "${MAIL_DIR}/multipart", "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["attachment","inbox","signed","unread"], "headers": {"Subject": "Multipart message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [
+{"id": 1, "content-type": "multipart/signed", "content": [
+{"id": 2, "content-type": "multipart/mixed", "content": [
+{"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"Subject": "html message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [
+{"id": 4, "content-type": "multipart/alternative", "content": [
+{"id": 5, "content-type": "text/html", "content-length": 71},
+{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}, 
+{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, 
+{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}, 
+{"id": 9, "content-type": "application/pgp-signature", "content-length": 197}]}]}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=1, message body"
+notmuch show --format=json --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 1, "content-type": "multipart/signed", "content": [
+{"id": 2, "content-type": "multipart/mixed", "content": [
+{"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"Subject": "html message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [
+{"id": 4, "content-type": "multipart/alternative", "content": [
+{"id": 5, "content-type": "text/html", "content-length": 71},
+{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}, 
+{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, 
+{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}, 
+{"id": 9, "content-type": "application/pgp-signature", "content-length": 197}]}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=2, multipart/mixed"
+notmuch show --format=json --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 2, "content-type": "multipart/mixed", "content": [
+{"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"Subject": "html message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [
+{"id": 4, "content-type": "multipart/alternative", "content": [
+{"id": 5, "content-type": "text/html", "content-length": 71},
+{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}, 
+{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}, 
+{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=3, rfc822 part"
+notmuch show --format=json --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 3, "content-type": "message/rfc822", "content": [{"headers": {"Subject": "html message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [
+{"id": 4, "content-type": "multipart/alternative", "content": [
+{"id": 5, "content-type": "text/html", "content-length": 71},
+{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=4, rfc822's multipart/alternative"
+notmuch show --format=json --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 4, "content-type": "multipart/alternative", "content": [
+{"id": 5, "content-type": "text/html", "content-length": 71},
+{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=5, rfc822's html part"
+notmuch show --format=json --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 5, "content-type": "text/html", "content-length": 71}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=6, rfc822's text part"
+notmuch show --format=json --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=7, inline attachment"
+notmuch show --format=json --part=7 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 7, "content-type": "text/plain", "filename": "attachment", "content": "This is a text attachment.\n"}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=8, plain text part"
+notmuch show --format=json --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "--format=json --part=9, pgp signature (unverified)"
+notmuch show --format=json --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+{"id": 9, "content-type": "application/pgp-signature", "content-length": 197}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_expect_success \
+    "--format=json --part=10, no part, expect error" \
+    "notmuch show --format=json --part=10 'id:87liy5ap00.fsf@yoom.home.cworth.org'"
+
+test_begin_subtest "--format=raw"
+notmuch show --format=raw 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+test_expect_equal_file OUTPUT "${MAIL_DIR}"/multipart
+
+test_begin_subtest "--format=raw --part=0, full message"
+notmuch show --format=raw --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+test_expect_equal_file OUTPUT "${MAIL_DIR}"/multipart
+
+test_begin_subtest "--format=raw --part=1, message body"
+notmuch show --format=raw --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+test_expect_equal_file OUTPUT "${MAIL_DIR}"/multipart
+
+test_begin_subtest "--format=raw --part=2, multipart/mixed"
+notmuch show --format=raw --part=2 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+Content-Type: multipart/mixed; boundary="=-=-="
+
+--=-=-=
+Content-Type: message/rfc822
+Content-Disposition: inline
+
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Subject: html message
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu)
+Message-ID: <87liy5ap01.fsf@yoom.home.cworth.org>
+MIME-Version: 1.0
+Content-Type: multipart/alternative; boundary="==-=-=="
+
+--==-=-==
+Content-Type: text/html
+
+<p>This is an embedded message, with a multipart/alternative part.</p>
+
+--==-=-==
+Content-Type: text/plain
+
+This is an embedded message, with a multipart/alternative part.
+
+--==-=-==--
+
+--=-=-=
+Content-Disposition: attachment; filename=attachment
+
+This is a text attachment.
+
+--=-=-=
+
+And this message is signed.
+
+-Carl
+
+--=-=-=--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=raw --part=3, rfc822 part"
+notmuch show --format=raw --part=3 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+test_expect_equal_file OUTPUT embedded_message
+
+test_begin_subtest "--format=raw --part=4, rfc822's multipart"
+notmuch show --format=raw --part=4 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Subject: html message
+Date: Fri, 05 Jan 2001 15:42:57 +0000
+User-Agent: Notmuch/0.5 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu)
+Message-ID: <87liy5ap01.fsf@yoom.home.cworth.org>
+MIME-Version: 1.0
+Content-Type: multipart/alternative; boundary="==-=-=="
+
+--==-=-==
+Content-Type: text/html
+
+<p>This is an embedded message, with a multipart/alternative part.</p>
+
+--==-=-==
+Content-Type: text/plain
+
+This is an embedded message, with a multipart/alternative part.
+
+--==-=-==--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=raw --part=5, rfc822's html part"
+notmuch show --format=raw --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+<p>This is an embedded message, with a multipart/alternative part.</p>
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=raw --part=6, rfc822's text part"
+notmuch show --format=raw --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+This is an embedded message, with a multipart/alternative part.
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=raw --part=7, inline attachment"
+notmuch show --format=raw --part=7 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+This is a text attachment.
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=raw --part=8, plain text part"
+notmuch show --format=raw --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+And this message is signed.
+
+-Carl
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "--format=raw --part=9, pgp signature (unverified)"
+notmuch show --format=raw --part=9 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+# output should *not* include newline
+echo >>OUTPUT
+cat <<EOF >EXPECTED
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (GNU/Linux)
+
+iEYEARECAAYFAk3SA/gACgkQ6JDdNq8qSWj0sACghqVJEQJUs3yV8zbTzhgnSIcD
+W6cAmQE4dcYrx/LPLtYLZm1jsGauE5hE
+=zkga
+-----END PGP SIGNATURE-----
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_expect_success \
+    "--format=raw --part=10, no part, expect error" \
+    "notmuch show --format=raw --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org'"
+
+test_begin_subtest "--format=mbox"
+notmuch show --format=mbox 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+printf "From cworth@cworth.org Fri Jan  5 15:43:57 2001\n" >EXPECTED
+cat "${MAIL_DIR}"/multipart >>EXPECTED
+# mbox output is expected to include a blank line
+echo >>EXPECTED
+test_expect_equal_file OUTPUT EXPECTED
+
+test_expect_success \
+    "--format=mbox --part=1, incompatible, expect error" \
+    "! notmuch show --format=mbox --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org'"
+
+test_begin_subtest "'notmuch reply' to a multipart message"
+notmuch reply 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: Multipart message
+To: Carl Worth <cworth@cworth.org>, cworth@cworth.org
+In-Reply-To: <87liy5ap00.fsf@yoom.home.cworth.org>
+References: <87liy5ap00.fsf@yoom.home.cworth.org>
+
+On Fri, 05 Jan 2001 15:43:57 +0000, Carl Worth <cworth@cworth.org> wrote:
+> From: Carl Worth <cworth@cworth.org>
+> To: cworth@cworth.org
+> Subject: html message
+> Date: Fri, 05 Jan 2001 15:42:57 +0000
+>
+Non-text part: text/html
+> This is an embedded message, with a multipart/alternative part.
+> This is a text attachment.
+> And this message is signed.
+> 
+> -Carl
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "'notmuch reply' to a multipart message with json format"
+notmuch reply --format=json 'id:87liy5ap00.fsf@yoom.home.cworth.org' | notmuch_json_show_sanitize >OUTPUT
+notmuch_json_show_sanitize <<EOF >EXPECTED
+{"reply-headers": {"Subject": "Re: Multipart message",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Carl Worth <cworth@cworth.org>, cworth@cworth.org",
+ "In-reply-to": "<87liy5ap00.fsf@yoom.home.cworth.org>",
+ "References": "<87liy5ap00.fsf@yoom.home.cworth.org>"},
+ "original": {"id": "XXXXX",
+ "match": false,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437,
+ "date_relative": "2001-01-05",
+ "tags": ["attachment","inbox","signed","unread"],
+ "headers": {"Subject": "Multipart message",
+ "From": "Carl Worth <cworth@cworth.org>",
+ "To": "cworth@cworth.org",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
+ "body": [{"id": 1,
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "multipart/mixed",
+ "content": [{"id": 3,
+ "content-type": "message/rfc822",
+ "content": [{"headers": {"Subject": "html message",
+ "From": "Carl Worth <cworth@cworth.org>",
+ "To": "cworth@cworth.org",
+ "Date": "Fri, 05 Jan 2001 15:42:57 +0000"},
+ "body": [{"id": 4,
+ "content-type": "multipart/alternative",
+ "content": [{"id": 5,
+ "content-type": "text/html",
+ "content-length": 71},
+ {"id": 6,
+ "content-type": "text/plain",
+ "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]},
+ {"id": 7,
+ "content-type": "text/plain",
+ "filename": "attachment",
+ "content": "This is a text attachment.\n"},
+ {"id": 8,
+ "content-type": "text/plain",
+ "content": "And this message is signed.\n\n-Carl\n"}]},
+ {"id": 9,
+ "content-type": "application/pgp-signature",
+ "content-length": 197}]}]}}
+EOF
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
+
+test_begin_subtest "'notmuch show --part' does not corrupt a part with CRLF pair"
+notmuch show --format=raw --part=3 id:base64-part-with-crlf > crlf.out
+echo -n -e "\xEF\x0D\x0A" > crlf.expected
+test_expect_equal_file crlf.out crlf.expected
+
+
+# The ISO-8859-1 encoding of U+00BD is a single byte: octal 275
+# (Portability note: Dollar-Single ($'...', ANSI C-style escape sequences)
+# quoting works on bash, ksh, zsh, *BSD sh but not on dash, ash nor busybox sh)
+readonly u_00bd_latin1=$'\275'
+
+# The Unicode fraction symbol 1/2 is U+00BD and is encoded
+# in UTF-8 as two bytes: octal 302 275
+readonly u_00bd_utf8=$'\302\275'
+
+cat <<EOF > ${MAIL_DIR}/include-html
+From: A <a@example.com>
+To: B <b@example.com>
+Subject: html message
+Date: Sat, 01 January 2000 00:00:00 +0000
+Message-ID: <htmlmessage>
+MIME-Version: 1.0
+Content-Type: multipart/alternative; boundary="==-=="
+
+--==-==
+Content-Type: text/html; charset=UTF-8
+
+<p>0.5 equals ${u_00bd_utf8}</p>
+
+--==-==
+Content-Type: text/html; charset=ISO-8859-1
+
+<p>0.5 equals ${u_00bd_latin1}</p>
+
+--==-==
+Content-Type: text/plain; charset=UTF-8
+
+0.5 equals ${u_00bd_utf8}
+
+--==-==--
+EOF
+
+notmuch new > /dev/null
+
+cat_expected_head ()
+{
+        cat <<EOF
+[[[{"id": "htmlmessage", "match":true, "excluded": false, "date_relative":"2000-01-01",
+   "timestamp": 946684800,
+   "filename": "${MAIL_DIR}/include-html",
+   "tags": ["inbox", "unread"],
+   "headers": { "Date": "Sat, 01 Jan 2000 00:00:00 +0000", "From": "A <a@example.com>",
+                "Subject": "html message", "To": "B <b@example.com>"},
+   "body": [{
+     "content-type": "multipart/alternative", "id": 1,
+EOF
+}
+
+cat_expected_head > EXPECTED.nohtml
+cat <<EOF >> EXPECTED.nohtml
+"content": [
+  { "id": 2, "content-charset": "UTF-8", "content-length": 21, "content-type": "text/html"},
+  { "id": 3, "content-charset": "ISO-8859-1", "content-length": 20, "content-type": "text/html"},
+  { "id": 4, "content-type": "text/plain", "content": "0.5 equals \\u00bd\\n"}
+]}]},[]]]]
+EOF
+
+# Both the UTF-8 and ISO-8859-1 part should have U+00BD
+cat_expected_head > EXPECTED.withhtml
+cat <<EOF >> EXPECTED.withhtml
+"content": [
+  { "id": 2, "content-type": "text/html", "content": "<p>0.5 equals \\u00bd</p>\\n"},
+  { "id": 3, "content-type": "text/html", "content": "<p>0.5 equals \\u00bd</p>\\n"},
+  { "id": 4, "content-type": "text/plain", "content": "0.5 equals \\u00bd\\n"}
+]}]},[]]]]
+EOF
+
+test_begin_subtest "html parts excluded by default"
+notmuch show --format=json id:htmlmessage > OUTPUT
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED.nohtml)"
+
+test_begin_subtest "html parts included"
+notmuch show --format=json --include-html id:htmlmessage > OUTPUT
+test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED.withhtml)"
+
+test_done
diff --git a/test/T200-thread-naming.sh b/test/T200-thread-naming.sh
new file mode 100755 (executable)
index 0000000..1a1a48f
--- /dev/null
@@ -0,0 +1,180 @@
+#!/usr/bin/env bash
+test_description="naming of threads with changing subject"
+. ./test-lib.sh
+
+test_begin_subtest "Initial thread name (oldest-first search)"
+add_message '[subject]="thread-naming: Initial thread subject"' \
+           '[date]="Fri, 05 Jan 2001 15:43:56 -0000"'
+first=${gen_msg_cnt}
+parent=${gen_msg_id}
+add_message '[subject]="thread-naming: Older changed subject"' \
+           '[date]="Sat, 06 Jan 2001 15:43:56 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+add_message '[subject]="thread-naming: Newer changed subject"' \
+           '[date]="Sun, 07 Jan 2001 15:43:56 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+add_message '[subject]="thread-naming: Final thread subject"' \
+           '[date]="Mon, 08 Jan 2001 15:43:56 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+final=${gen_msg_id}
+output=$(notmuch search --sort=oldest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [4/4] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Initial thread name (newest-first search)"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-08 [4/4] Notmuch Test Suite; thread-naming: Final thread subject (inbox unread)"
+
+# Remove oldest and newest messages from search results
+notmuch tag -inbox id:$parent or id:$final
+
+test_begin_subtest "Changed thread name (oldest-first search)"
+output=$(notmuch search --sort=oldest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-06 [2/4] Notmuch Test Suite; thread-naming: Older changed subject (inbox unread)"
+
+test_begin_subtest "Changed thread name (newest-first search)"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-07 [2/4] Notmuch Test Suite; thread-naming: Newer changed subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Re:)"
+add_message '[subject]="Re: thread-naming: Initial thread subject"' \
+           '[date]="Tue, 09 Jan 2001 15:43:45 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-09 [3/5] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Aw:)"
+add_message '[subject]="Aw: thread-naming: Initial thread subject"' \
+           '[date]="Wed, 10 Jan 2001 15:43:45 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-10 [4/6] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Vs:)"
+add_message '[subject]="Vs: thread-naming: Initial thread subject"' \
+           '[date]="Thu, 11 Jan 2001 15:43:45 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-11 [5/7] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Sv:)"
+add_message '[subject]="Sv: thread-naming: Initial thread subject"' \
+           '[date]="Fri, 12 Jan 2001 15:43:45 -0000"' \
+           "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-12 [6/8] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest 'Test order of messages in "notmuch show"'
+output=$(notmuch show thread-naming | notmuch_show_sanitize)
+test_expect_equal "$output" "\fmessage{ id:msg-$(printf "%03d" $first)@notmuch-test-suite depth:0 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $first)
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (unread)
+Subject: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Fri, 05 Jan 2001 15:43:56 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$first)
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 1)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 1)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-06) (inbox unread)
+Subject: thread-naming: Older changed subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Sat, 06 Jan 2001 15:43:56 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 1)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 2)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 2)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-07) (inbox unread)
+Subject: thread-naming: Newer changed subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Sun, 07 Jan 2001 15:43:56 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 2)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 3)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 3)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-08) (unread)
+Subject: thread-naming: Final thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Mon, 08 Jan 2001 15:43:56 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 3)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 4)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 4)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-09) (inbox unread)
+Subject: Re: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Tue, 09 Jan 2001 15:43:45 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 4)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 5)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 5)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-10) (inbox unread)
+Subject: Aw: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Wed, 10 Jan 2001 15:43:45 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 5)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 6)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 6)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-11) (inbox unread)
+Subject: Vs: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Thu, 11 Jan 2001 15:43:45 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 6)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 7)))@notmuch-test-suite depth:1 match:1 excluded:0 filename:/XXX/mail/msg-$(printf "%03d" $((first + 7)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-12) (inbox unread)
+Subject: Sv: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Fri, 12 Jan 2001 15:43:45 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 7)))
+\fpart}
+\fbody}
+\fmessage}"
+test_done
diff --git a/test/T210-raw.sh b/test/T210-raw.sh
new file mode 100755 (executable)
index 0000000..daf5735
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+test_description='notmuch show --format=raw'
+. ./test-lib.sh
+
+add_message
+add_message
+
+test_begin_subtest "Attempt to show multiple raw messages"
+output=$(notmuch show --format=raw "*" 2>&1)
+test_expect_equal "$output" "Error: search term did not match precisely one message."
+
+test_begin_subtest "Show a raw message"
+output=$(notmuch show --format=raw id:msg-001@notmuch-test-suite | notmuch_date_sanitize)
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Message-Id: <msg-001@notmuch-test-suite>
+Subject: Test message #1
+Date: GENERATED_DATE
+
+This is just a test message (#1)"
+
+test_begin_subtest "Show another raw message"
+output=$(notmuch show --format=raw id:msg-002@notmuch-test-suite | notmuch_date_sanitize)
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Message-Id: <msg-002@notmuch-test-suite>
+Subject: Test message #2
+Date: GENERATED_DATE
+
+This is just a test message (#2)"
+
+test_done
diff --git a/test/T220-reply.sh b/test/T220-reply.sh
new file mode 100755 (executable)
index 0000000..b0d854a
--- /dev/null
@@ -0,0 +1,257 @@
+#!/usr/bin/env bash
+test_description="\"notmuch reply\" in several variations"
+. ./test-lib.sh
+
+test_begin_subtest "Basic reply"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=test_suite@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="basic reply test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> basic reply test"
+
+test_begin_subtest "Multiple recipients"
+add_message '[from]="Sender <sender@example.com>"' \
+           '[to]="test_suite@notmuchmail.org, Someone Else <someone@example.com>"' \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="Multiple recipients"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, Someone Else <someone@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Multiple recipients"
+
+test_begin_subtest "Reply with CC"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=test_suite@notmuchmail.org \
+           '[cc]="Other Parties <cc@example.com>"' \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="reply with CC"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+Cc: Other Parties <cc@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply with CC"
+
+test_begin_subtest "Reply from alternate address"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=test_suite_other@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="reply from alternate address"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply from alternate address"
+
+test_begin_subtest "Reply from address in named group list"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]=group:test_suite@notmuchmail.org,someone@example.com\;' \
+             [cc]=test_suite_other@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="Reply from address in named group list"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, someone@example.com
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Reply from address in named group list"
+
+test_begin_subtest "Support for Reply-To"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=test_suite@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="support for reply-to"' \
+           '[reply-to]="Sender <elsewhere@example.com>"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <elsewhere@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> support for reply-to"
+
+test_begin_subtest "Un-munging Reply-To"
+add_message '[from]="Sender <sender@example.com>"' \
+           '[to]="Some List <list@example.com>"' \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="Un-munging Reply-To"' \
+           '[reply-to]="Evil Munging List <list@example.com>"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, Some List <list@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Un-munging Reply-To"
+
+test_begin_subtest "Message with header of exactly 200 bytes"
+add_message '[subject]="This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="200-byte header"'
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: This subject is exactly 200 bytes in length. Other than its
+ length there is not much of note here. Note that the length of 200 bytes
+ includes the Subject: and Re: prefixes with two spaces
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> 200-byte header"
+
+test_begin_subtest "From guessing: Envelope-To"
+add_message '[from]="Sender <sender@example.com>"' \
+           '[to]="Recipient <recipient@example.com>"' \
+           '[subject]="From guessing"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="From guessing"' \
+           '[header]="Envelope-To: test_suite_other@notmuchmail.org"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: From guessing
+To: Sender <sender@example.com>, Recipient <recipient@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> From guessing"
+
+test_begin_subtest "From guessing: X-Original-To"
+add_message '[from]="Sender <sender@example.com>"' \
+           '[to]="Recipient <recipient@example.com>"' \
+           '[subject]="From guessing"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="From guessing"' \
+           '[header]="X-Original-To: test_suite@otherdomain.org"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@otherdomain.org>
+Subject: Re: From guessing
+To: Sender <sender@example.com>, Recipient <recipient@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> From guessing"
+
+test_begin_subtest "From guessing: Delivered-To"
+add_message '[from]="Sender <sender@example.com>"' \
+           '[to]="Recipient <recipient@example.com>"' \
+           '[subject]="From guessing"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="From guessing"' \
+           '[header]="Delivered-To: test_suite_other@notmuchmail.org"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: From guessing
+To: Sender <sender@example.com>, Recipient <recipient@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> From guessing"
+
+test_begin_subtest "Reply with RFC 2047-encoded headers"
+add_message '[subject]="=?iso-8859-1?q?=e0=df=e7?="' \
+           '[from]="=?utf-8?q?=e2=98=83?= <snowman@example.com>"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="Encoding"'
+
+# GMime happens to change from Q- to B-encoding.  We canonicalize the
+# case of the encoding and charset because different versions of GMime
+# capitalize the encoding differently.
+output=$(notmuch reply id:${gen_msg_id} | perl -pe 's/=\?[^?]+\?[bB]\?/lc($&)/ge')
+test_expect_equal "$output" "\
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: =?iso-8859-1?b?4N/n?=
+To: =?utf-8?b?4piD?= <snowman@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, ☃ <snowman@example.com> wrote:
+> Encoding"
+
+test_begin_subtest "Reply with RFC 2047-encoded headers (JSON)"
+output=$(notmuch reply --format=json id:${gen_msg_id})
+test_expect_equal_json "$output" '
+{
+    "original": {
+        "body": [
+            {
+                "content": "Encoding\n",
+                "content-type": "text/plain",
+                "id": 1
+            }
+        ],
+        "date_relative": "2010-01-05",
+        "excluded": false,
+        "filename": "'${MAIL_DIR}'/msg-012",
+        "headers": {
+            "Date": "Tue, 05 Jan 2010 15:43:56 +0000",
+            "From": "\u2603 <snowman@example.com>",
+            "Subject": "\u00e0\u00df\u00e7",
+            "To": "Notmuch Test Suite <test_suite@notmuchmail.org>"
+        },
+        "id": "'${gen_msg_id}'",
+        "match": false,
+        "tags": [
+            "inbox",
+            "unread"
+        ],
+        "timestamp": 1262706236
+    },
+    "reply-headers": {
+        "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+        "In-reply-to": "<'${gen_msg_id}'>",
+        "References": "<'${gen_msg_id}'>",
+        "Subject": "Re: \u00e0\u00df\u00e7",
+        "To": "\u2603 <snowman@example.com>"
+    }
+}'
+
+
+test_done
diff --git a/test/T230-reply-to-sender.sh b/test/T230-reply-to-sender.sh
new file mode 100755 (executable)
index 0000000..30e5e38
--- /dev/null
@@ -0,0 +1,211 @@
+#!/usr/bin/env bash
+test_description="\"notmuch reply --reply-to=sender\" in several variations"
+. ./test-lib.sh
+
+test_begin_subtest "Basic reply-to-sender"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="basic reply-to-sender test"'
+
+output=$(notmuch reply --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> basic reply-to-sender test"
+
+test_begin_subtest "From Us, Basic reply to message"
+add_message '[from]="Notmuch Test Suite <test_suite@notmuchmail.org>"' \
+            '[to]="Recipient <recipient@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="basic reply-to-from-us test"'
+
+output=$(notmuch reply --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Recipient <recipient@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> basic reply-to-from-us test"
+
+test_begin_subtest "Multiple recipients"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]="test_suite@notmuchmail.org, Someone Else <someone@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="Multiple recipients"'
+
+output=$(notmuch reply  --reply-to=sender  id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Multiple recipients"
+
+test_begin_subtest "From Us, Multiple TO recipients"
+add_message '[from]="Notmuch Test Suite <test_suite@notmuchmail.org>"' \
+            '[to]="Recipient <recipient@example.com>, Someone Else <someone@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="From Us, Multiple TO recipients"'
+
+output=$(notmuch reply  --reply-to=sender  id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Recipient <recipient@example.com>, Someone Else <someone@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> From Us, Multiple TO recipients"
+
+test_begin_subtest "Reply with CC"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite@notmuchmail.org \
+            '[cc]="Other Parties <cc@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="reply with CC"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply with CC"
+
+test_begin_subtest "From Us, Reply with CC"
+add_message '[from]="Notmuch Test Suite <test_suite@notmuchmail.org>"' \
+            '[to]="Recipient <recipient@example.com>"' \
+            '[cc]="Other Parties <cc@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="reply with CC"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Recipient <recipient@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> reply with CC"
+
+test_begin_subtest "From Us, Reply no TO but with CC"
+add_message '[from]="Notmuch Test Suite <test_suite@notmuchmail.org>"' \
+            '[cc]="Other Parties <cc@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="reply with CC"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+Cc: Other Parties <cc@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> reply with CC"
+
+test_begin_subtest "Reply from alternate address"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite_other@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="reply from alternate address"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply from alternate address"
+
+test_begin_subtest "Support for Reply-To"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="support for reply-to"' \
+            '[reply-to]="Sender <elsewhere@example.com>"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <elsewhere@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> support for reply-to"
+
+test_begin_subtest "Support for Reply-To with multiple recipients"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]="test_suite@notmuchmail.org, Someone Else <someone@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="support for reply-to with multiple recipients"' \
+            '[reply-to]="Sender <elsewhere@example.com>"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <elsewhere@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> support for reply-to with multiple recipients"
+
+test_begin_subtest "Un-munging Reply-To"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]="Some List <list@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="Un-munging Reply-To"' \
+            '[reply-to]="Evil Munging List <list@example.com>"'
+
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Un-munging Reply-To"
+
+test_begin_subtest "Message with header of exactly 200 bytes"
+add_message '[subject]="This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces"' \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="200-byte header"'
+output=$(notmuch reply  --reply-to=sender id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: This subject is exactly 200 bytes in length. Other than its
+ length there is not much of note here. Note that the length of 200 bytes
+ includes the Subject: and Re: prefixes with two spaces
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> 200-byte header"
+test_done
diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh
new file mode 100755 (executable)
index 0000000..0004438
--- /dev/null
@@ -0,0 +1,293 @@
+#!/usr/bin/env bash
+test_description="\"notmuch dump\" and \"notmuch restore\""
+. ./test-lib.sh
+
+add_email_corpus
+
+test_expect_success 'Dumping all tags' \
+  'generate_message &&
+  notmuch new &&
+  notmuch dump > dump.expected'
+
+# The use of from:cworth is rather arbitrary: it matches some of the
+# email corpus' messages, but not all of them.
+
+test_expect_success 'Dumping all tags II' \
+  'notmuch tag +ABC +DEF -- from:cworth &&
+  notmuch dump > dump-ABC_DEF.expected &&
+  ! cmp dump.expected dump-ABC_DEF.expected'
+
+test_expect_success 'Clearing all tags' \
+  'sed -e "s/(\([^(]*\))$/()/" < dump.expected > clear.expected &&
+  notmuch restore --input=clear.expected &&
+  notmuch dump > clear.actual &&
+  test_cmp clear.expected clear.actual'
+
+test_expect_success 'Accumulate original tags' \
+  'notmuch tag +ABC +DEF -- from:cworth &&
+  notmuch restore --accumulate < dump.expected &&
+  notmuch dump > dump.actual &&
+  test_cmp dump-ABC_DEF.expected dump.actual'
+
+test_expect_success 'Restoring original tags' \
+  'notmuch restore --input=dump.expected &&
+  notmuch dump > dump.actual &&
+  test_cmp dump.expected dump.actual'
+
+test_expect_success 'Restore with nothing to do' \
+  'notmuch restore < dump.expected &&
+  notmuch dump > dump.actual &&
+  test_cmp dump.expected dump.actual'
+
+test_expect_success 'Accumulate with existing tags' \
+  'notmuch restore --accumulate --input=dump.expected &&
+  notmuch dump > dump.actual &&
+  test_cmp dump.expected dump.actual'
+
+test_expect_success 'Accumulate with no tags' \
+  'notmuch restore --accumulate < clear.expected &&
+  notmuch dump > dump.actual &&
+  test_cmp dump.expected dump.actual'
+
+test_expect_success 'Accumulate with new tags' \
+  'notmuch restore --input=dump.expected &&
+  notmuch restore --accumulate --input=dump-ABC_DEF.expected &&
+  notmuch dump >  OUTPUT.$test_count &&
+  notmuch restore --input=dump.expected &&
+  test_cmp dump-ABC_DEF.expected OUTPUT.$test_count'
+
+# notmuch restore currently only considers the first argument.
+test_expect_success 'Invalid restore invocation' \
+  'test_must_fail notmuch restore --input=dump.expected another_one'
+
+test_begin_subtest "dump --output=outfile"
+notmuch dump --output=dump-outfile.actual
+test_expect_equal_file dump.expected dump-outfile.actual
+
+test_begin_subtest "dump --output=outfile --"
+notmuch dump --output=dump-1-arg-dash.actual --
+test_expect_equal_file dump.expected dump-1-arg-dash.actual
+
+# Note, we assume all messages from cworth have a message-id
+# containing cworth.org
+
+grep 'cworth[.]org' dump.expected > dump-cworth.expected
+
+test_begin_subtest "dump -- from:cworth"
+notmuch dump -- from:cworth > dump-dash-cworth.actual
+test_expect_equal_file dump-cworth.expected dump-dash-cworth.actual
+
+test_begin_subtest "dump --output=outfile from:cworth"
+notmuch dump --output=dump-outfile-cworth.actual from:cworth
+test_expect_equal_file dump-cworth.expected dump-outfile-cworth.actual
+
+test_begin_subtest "dump --output=outfile -- from:cworth"
+notmuch dump --output=dump-outfile-dash-inbox.actual -- from:cworth
+test_expect_equal_file dump-cworth.expected dump-outfile-dash-inbox.actual
+
+test_begin_subtest "Check for a safe set of message-ids"
+notmuch search --output=messages from:cworth | sed s/^id:// > EXPECTED
+notmuch search --output=messages from:cworth | sed s/^id:// |\
+       $TEST_DIRECTORY/hex-xcode --direction=encode > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "format=batch-tag, dump sanity check."
+notmuch dump --format=sup from:cworth | cut -f1 -d' ' | \
+    sort > EXPECTED.$test_count
+notmuch dump --format=batch-tag from:cworth | sed 's/^.*-- id://' | \
+    sort > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest "format=batch-tag, # round-trip"
+notmuch dump --format=sup | sort > EXPECTED.$test_count
+notmuch dump --format=batch-tag | notmuch restore --format=batch-tag
+notmuch dump --format=sup | sort > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest "format=batch-tag, # blank lines and comments"
+notmuch dump --format=batch-tag| sort > EXPECTED.$test_count
+notmuch restore <<EOF
+# this line is a comment; the next has only white space
+        
+
+# the previous line is empty
+EOF
+notmuch dump --format=batch-tag | sort > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest "format=batch-tag, # reverse-round-trip empty tag"
+cat <<EOF >EXPECTED.$test_count
++ -- id:20091117232137.GA7669@griffis1.net
+EOF
+notmuch restore --format=batch-tag < EXPECTED.$test_count
+notmuch dump --format=batch-tag id:20091117232137.GA7669@griffis1.net > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+tag1='comic_swear=$&^%$^%\\//-+$^%$'
+enc1=$($TEST_DIRECTORY/hex-xcode --direction=encode "$tag1")
+
+tag2=$(printf 'this\n tag\t has\n spaces')
+enc2=$($TEST_DIRECTORY/hex-xcode --direction=encode "$tag2")
+
+enc3='%c3%91%c3%a5%c3%b0%c3%a3%c3%a5%c3%a9-%c3%8f%c3%8a'
+tag3=$($TEST_DIRECTORY/hex-xcode --direction=decode $enc3)
+
+notmuch dump --format=batch-tag > BACKUP
+
+notmuch tag +"$tag1" +"$tag2" +"$tag3" -inbox -unread "*"
+
+# initial segment of file used for several tests below.
+cat <<EOF > comments-and-blanks
+# this is a comment
+
+# next line has leading whitespace
+       
+
+EOF
+
+test_begin_subtest 'restoring empty file is not an error'
+notmuch restore < /dev/null 2>OUTPUT.$test_count
+cp /dev/null EXPECTED
+test_expect_equal_file EXPECTED OUTPUT.$test_count
+
+test_begin_subtest 'file of comments and blank lines is not an error'
+notmuch restore --input=comments-and-blanks
+ret_val=$?
+test_expect_equal "$ret_val" "0"
+
+cp comments-and-blanks leading-comments-blanks-batch-tag
+echo "+some_tag -- id:yun1vjwegii.fsf@aiko.keithp.com" \
+    >> leading-comments-blanks-batch-tag
+
+test_begin_subtest 'detect format=batch-tag with leading comments and blanks'
+notmuch restore --input=leading-comments-blanks-batch-tag
+notmuch search --output=tags id:yun1vjwegii.fsf@aiko.keithp.com > OUTPUT.$test_count
+echo "some_tag" > EXPECTED
+test_expect_equal_file EXPECTED OUTPUT.$test_count
+
+cp comments-and-blanks leading-comments-blanks-sup
+echo "yun1vjwegii.fsf@aiko.keithp.com (another_tag)" \
+    >> leading-comments-blanks-sup
+
+test_begin_subtest 'detect format=sup with leading comments and blanks'
+notmuch restore --input=leading-comments-blanks-sup
+notmuch search --output=tags id:yun1vjwegii.fsf@aiko.keithp.com > OUTPUT.$test_count
+echo "another_tag" > EXPECTED
+test_expect_equal_file EXPECTED OUTPUT.$test_count
+
+test_begin_subtest 'format=batch-tag, round trip with strange tags'
+notmuch dump --format=batch-tag > EXPECTED.$test_count
+notmuch dump --format=batch-tag | notmuch restore --format=batch-tag
+notmuch dump --format=batch-tag > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest 'format=batch-tag, checking encoded output'
+notmuch dump --format=batch-tag -- from:cworth |\
+        awk "{ print \"+$enc1 +$enc2 +$enc3 -- \" \$5 }" > EXPECTED.$test_count
+notmuch dump --format=batch-tag -- from:cworth  > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest 'restoring sane tags'
+notmuch restore --format=batch-tag < BACKUP
+notmuch dump --format=batch-tag > OUTPUT.$test_count
+test_expect_equal_file BACKUP OUTPUT.$test_count
+
+test_begin_subtest 'format=batch-tag, restore=auto'
+notmuch dump --format=batch-tag > EXPECTED.$test_count
+notmuch tag -inbox -unread "*"
+notmuch restore --format=auto < EXPECTED.$test_count
+notmuch dump --format=batch-tag > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest 'format=sup, restore=auto'
+notmuch dump --format=sup > EXPECTED.$test_count
+notmuch tag -inbox -unread "*"
+notmuch restore --format=auto < EXPECTED.$test_count
+notmuch dump --format=sup > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest 'format=batch-tag, restore=default'
+notmuch dump --format=batch-tag > EXPECTED.$test_count
+notmuch tag -inbox -unread "*"
+notmuch restore < EXPECTED.$test_count
+notmuch dump --format=batch-tag > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest 'format=sup, restore=default'
+notmuch dump --format=sup > EXPECTED.$test_count
+notmuch tag -inbox -unread "*"
+notmuch restore < EXPECTED.$test_count
+notmuch dump --format=sup > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest 'restore: checking error messages'
+notmuch restore <<EOF 2>OUTPUT
+# the next line has a space
+a
++0
++a +b
+# trailing whitespace
++a +b 
++c +d --
+# this is a harmless comment, do not yell about it.
+
+# the previous line was blank; also no yelling please
++%zz -- id:whatever
++e +f id:"
++e +f tag:abc
+# the next non-comment line should report an an empty tag error for
+# batch tagging, but not for restore
++ +e -- id:20091117232137.GA7669@griffis1.net
+# valid id, but warning about missing message
++e id:missing_message_id
+# exercise parser
++e -- id:some)stuff
++e -- id:some stuff
++e -- id:some"stuff
++e -- id:"a_message_id_with""_a_quote"
++e -- id:"a message id with spaces"
++e --  id:an_id_with_leading_and_trailing_ws \
+
+EOF
+
+cat <<EOF > EXPECTED
+Warning: cannot parse query: a (skipping)
+Warning: no query string [+0]
+Warning: no query string [+a +b]
+Warning: missing query string [+a +b ]
+Warning: no query string after -- [+c +d --]
+Warning: hex decoding of tag %zz failed [+%zz -- id:whatever]
+Warning: cannot parse query: id:" (skipping)
+Warning: not an id query: tag:abc (skipping)
+Warning: cannot apply tags to missing message: missing_message_id
+Warning: cannot parse query: id:some)stuff (skipping)
+Warning: cannot parse query: id:some stuff (skipping)
+Warning: cannot apply tags to missing message: some"stuff
+Warning: cannot apply tags to missing message: a_message_id_with"_a_quote
+Warning: cannot apply tags to missing message: a message id with spaces
+Warning: cannot apply tags to missing message: an_id_with_leading_and_trailing_ws
+EOF
+
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest 'roundtripping random message-ids and tags'
+
+    ${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
+                       --num-messages=100
+
+     notmuch dump --format=batch-tag| \
+        sort > EXPECTED.$test_count
+
+     notmuch tag +this_tag_is_very_unlikely_to_be_random '*'
+
+     notmuch restore --format=batch-tag < EXPECTED.$test_count
+
+     notmuch dump --format=batch-tag| \
+        sort > OUTPUT.$test_count
+
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_done
+
+# Note the database is "poisoned" for sup format at this point.
diff --git a/test/T250-uuencode.sh b/test/T250-uuencode.sh
new file mode 100755 (executable)
index 0000000..b3e1ac1
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+test_description="handling of uuencoded data"
+. ./test-lib.sh
+
+add_message [subject]=uuencodetest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' \
+'[body]="This message is used to ensure that notmuch correctly handles a
+message containing a block of uuencoded data. First, we have a marker
+this content beforeuudata . Then we begin the uuencoded data itself:
+
+begin 644 bogus-uuencoded-data
+M0123456789012345678901234567890123456789012345678901234567890
+MOBVIOUSLY, THIS IS NOT ANY SORT OF USEFUL UUENCODED DATA.    
+MINSTEAD THIS IS JUST A WAY TO ENSURE THAT THIS BLOCK OF DATA 
+MIS CORRECTLY IGNORED WHEN NOTMUCH CREATES ITS INDEX. SO WE   
+MINCLUDE A DURINGUUDATA MARKER THAT SHOULD NOT RESULT IN ANY  
+MSEARCH RESULT.                                               
+\\\`
+end
+
+Finally, we have our afteruudata marker as well."'
+
+test_begin_subtest "Ensure content before uu data is indexed"
+output=$(notmuch search beforeuudata | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; uuencodetest (inbox unread)"
+
+test_begin_subtest "Ensure uu data is not indexed"
+output=$(notmuch search DURINGUUDATA | notmuch_search_sanitize)
+test_expect_equal "$output" ""
+
+test_begin_subtest "Ensure content after uu data is indexed"
+output=$(notmuch search afteruudata | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; uuencodetest (inbox unread)"
+
+test_done
diff --git a/test/T260-thread-order.sh b/test/T260-thread-order.sh
new file mode 100755 (executable)
index 0000000..6c3a4b3
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+test_description="threading when messages received out of order"
+. ./test-lib.sh
+
+test_begin_subtest "Adding initial child message"
+generate_message [body]=foo "[in-reply-to]=\<parent-id\>" [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching returns the message"
+output=$(notmuch search foo | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+
+test_begin_subtest "Adding second child message"
+generate_message [body]=foo "[in-reply-to]=\<parent-id\>" [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching returns both messages in one thread"
+output=$(notmuch search foo | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [2/2] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+
+test_begin_subtest "Adding parent message"
+generate_message [body]=foo [id]=parent-id [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching returns all three messages in one thread"
+output=$(notmuch search foo | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [3/3] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+
+test_done
diff --git a/test/T270-author-order.sh b/test/T270-author-order.sh
new file mode 100755 (executable)
index 0000000..6ffeffc
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+test_description="author reordering;"
+. ./test-lib.sh
+
+test_begin_subtest "Adding parent message"
+generate_message [body]=findme [id]=new-parent-id [subject]=author-reorder-threadtest '[from]="User <user@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Adding initial child message"
+generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User1 <user1@example.com>"' '[date]="Sat, 01 Jan 2000 12:01:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Adding second child message"
+generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User2 <user2@example.com>"' '[date]="Sat, 01 Jan 2000 12:02:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching when all three messages match"
+output=$(notmuch search findme | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [3/3] User, User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching when two messages match"
+output=$(notmuch search User1 or User2 | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [2/3] User1, User2| User; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching when only one message matches"
+output=$(notmuch search User2 | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/3] User2| User, User1; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching when only first message matches"
+output=$(notmuch search User | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/3] User| User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Adding duplicate author"
+generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User1 <user1@example.com>"' '[date]="Sat, 01 Jan 2000 12:03:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching when all four messages match"
+output=$(notmuch search findme | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [4/4] User, User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Adding non-monotonic child message"
+generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User0 <user0@example.com>"' '[date]="Sat, 01 Jan 2000 11:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching non-monotonic messages (oldest-first)"
+output=$(notmuch search --sort=oldest-first findme | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [5/5] User0, User, User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching non-monotonic messages (newest-first)"
+output=$(notmuch search --sort=newest-first findme | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [5/5] User0, User, User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_done
diff --git a/test/T280-from-guessing.sh b/test/T280-from-guessing.sh
new file mode 100755 (executable)
index 0000000..6dfaa40
--- /dev/null
@@ -0,0 +1,217 @@
+#!/usr/bin/env bash
+test_description="From line heuristics (with multiple configured addresses)"
+. ./test-lib.sh
+
+test_begin_subtest "Magic from guessing (nothing to go on)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Envelope-to:)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[header]="Envelope-To: test_suite_other@notmuchmail.org"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (X-Original-To:)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[header]="X-Original-To: test_suite_other@notmuchmail.org"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: .. for ..)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.notmuchmail.org (some MTA) with ESMTP id 12345678
+       for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: domain)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.otherdomain.org (some MTA) with ESMTP id 12345678
+       Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@otherdomain.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (multiple Received: headers)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           "[header]=\"Received: from extraneous.example.com (extraneous.example.com [1.1.1.1])
+Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.otherdomain.org (some MTA) with ESMTP id 12345678
+       for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)
+Received: from extraneous.example.com (extraneous.example.com [1.1.1.1])\"" \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output="$(notmuch reply id:${gen_msg_id})"
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Testing From line heuristics (with single configured address)"
+sed -i -e "s/^other_email.*//" "${NOTMUCH_CONFIG}"
+test_expect_equal '' ''
+
+test_begin_subtest "Magic from guessing (nothing to go on)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Envelope-to:)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[header]="Envelope-To: test_suite_other@notmuchmail.org"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (X-Original-To:)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           '[header]="X-Original-To: test_suite_other@notmuchmail.org"' \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: .. for ..)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.notmuchmail.org (some MTA) with ESMTP id 12345678
+       for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: domain)"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=mailinglist@notmuchmail.org \
+            [subject]=notmuch-reply-test \
+           "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.otherdomain.org (some MTA) with ESMTP id 12345678
+       Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+           '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+           '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_done
diff --git a/test/T290-long-id.sh b/test/T290-long-id.sh
new file mode 100755 (executable)
index 0000000..85e620f
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+test_description="messages with ridiculously-long message IDs"
+. ./test-lib.sh
+
+test_begin_subtest "Referencing long ID before adding"
+generate_message '[subject]="Reference of ridiculously-long message ID"' \
+                "[references]=\<abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-\>"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Adding message with long ID"
+generate_message '[subject]="A ridiculously-long message ID"' \
+                "[id]=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Referencing long ID after adding"
+generate_message '[subject]="Reply to ridiculously-long message ID"' \
+                "[in-reply-to]=\<abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-\>"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Ensure all messages were threaded together"
+output=$(notmuch search 'subject:"a ridiculously-long message ID"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/3] Notmuch Test Suite; A ridiculously-long message ID (inbox unread)"
+
+test_done
diff --git a/test/T300-encoding.sh b/test/T300-encoding.sh
new file mode 100755 (executable)
index 0000000..b6c86bf
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+test_description="encoding issues"
+. ./test-lib.sh
+
+test_begin_subtest "Message with text of unknown charset"
+add_message '[content-type]="text/plain; charset=unknown-8bit"' \
+           "[body]=irrelevant"
+output=$(notmuch show id:${gen_msg_id} 2>&1 | notmuch_show_sanitize_all)
+test_expect_equal "$output" "\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (inbox unread)
+Subject: Message with text of unknown charset
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: GENERATED_DATE
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+irrelevant
+\fpart}
+\fbody}
+\fmessage}"
+
+test_begin_subtest "Search for ISO-8859-2 encoded message"
+add_message '[content-type]="text/plain; charset=iso-8859-2"' \
+            '[content-transfer-encoding]=8bit' \
+            '[subject]="ISO-8859-2 encoded message"' \
+            "[body]=$'Czech word tu\350\362\341\350\350\355 means pinguin\'s.'" # ISO-8859-2 characters are generated by shell's escape sequences
+output=$(notmuch search tučňáččí 2>&1 | notmuch_show_sanitize_all)
+test_expect_equal "$output" "thread:0000000000000002   2001-01-05 [1/1] Notmuch Test Suite; ISO-8859-2 encoded message (inbox unread)"
+
+test_begin_subtest "RFC 2047 encoded word with spaces"
+add_message '[subject]="=?utf-8?q?encoded word with spaces?="'
+output=$(notmuch search id:${gen_msg_id} 2>&1 | notmuch_show_sanitize)
+test_expect_equal "$output" "thread:0000000000000003   2001-01-05 [1/1] Notmuch Test Suite; encoded word with spaces (inbox unread)"
+
+test_begin_subtest "RFC 2047 encoded words back to back"
+add_message '[subject]="=?utf-8?q?encoded-words-back?==?utf-8?q?to-back?="'
+output=$(notmuch search id:${gen_msg_id} 2>&1 | notmuch_show_sanitize)
+test_expect_equal "$output" "thread:0000000000000004   2001-01-05 [1/1] Notmuch Test Suite; encoded-words-backto-back (inbox unread)"
+
+test_begin_subtest "RFC 2047 encoded words without space before or after"
+add_message '[subject]="=?utf-8?q?encoded?=word without=?utf-8?q?space?=" '
+output=$(notmuch search id:${gen_msg_id} 2>&1 | notmuch_show_sanitize)
+test_expect_equal "$output" "thread:0000000000000005   2001-01-05 [1/1] Notmuch Test Suite; encodedword withoutspace (inbox unread)"
+
+test_done
diff --git a/test/T310-emacs.sh b/test/T310-emacs.sh
new file mode 100755 (executable)
index 0000000..863219d
--- /dev/null
@@ -0,0 +1,952 @@
+#!/usr/bin/env bash
+
+test_description="emacs interface"
+. ./test-lib.sh
+
+EXPECTED=$TEST_DIRECTORY/emacs.expected-output
+
+add_email_corpus
+
+test_begin_subtest "Basic notmuch-hello view in emacs"
+test_emacs '(notmuch-hello)
+           (test-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello
+
+test_begin_subtest "Saved search with 0 results"
+test_emacs '(let ((notmuch-show-empty-saved-searches t)
+                 (notmuch-saved-searches
+                  '\''(("inbox" . "tag:inbox")
+                       ("unread" . "tag:unread")
+                       ("empty" . "tag:doesnotexist"))))
+             (notmuch-hello)
+             (test-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-with-empty
+
+test_begin_subtest "No saved searches displayed (all with 0 results)"
+test_emacs '(let ((notmuch-saved-searches
+                  '\''(("empty" . "tag:doesnotexist"))))
+             (notmuch-hello)
+             (test-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
+
+test_begin_subtest "Basic notmuch-search view in emacs"
+test_emacs '(notmuch-search "tag:inbox")
+           (notmuch-test-wait)
+           (test-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox
+
+test_begin_subtest "Incremental parsing of search results"
+test_emacs "(ad-enable-advice 'notmuch-search-process-filter 'around 'pessimal)
+           (ad-activate 'notmuch-search-process-filter)
+           (notmuch-search \"tag:inbox\")
+           (notmuch-test-wait)
+           (ad-disable-advice 'notmuch-search-process-filter 'around 'pessimal)
+           (ad-activate 'notmuch-search-process-filter)
+           (test-output)"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox
+
+test_begin_subtest "Navigation of notmuch-hello to search results"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (test-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-view-inbox
+
+test_begin_subtest "Basic notmuch-show view in emacs"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(notmuch-show \"$maildir_storage_thread\")
+           (test-output)"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
+
+test_begin_subtest "Basic notmuch-show view in emacs default indentation"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(let ((notmuch-show-indent-messages-width 1))
+             (notmuch-show \"$maildir_storage_thread\")
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
+
+test_begin_subtest "Basic notmuch-show view in emacs without indentation"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(let ((notmuch-show-indent-messages-width 0))
+             (notmuch-show \"$maildir_storage_thread\")
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage-without-indentation
+
+test_begin_subtest "Basic notmuch-show view in emacs with fourfold indentation"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(let ((notmuch-show-indent-messages-width 4))
+             (notmuch-show \"$maildir_storage_thread\")
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage-with-fourfold-indentation
+
+test_begin_subtest "notmuch-show for message with invalid From"
+add_message "[subject]=\"message-with-invalid-from\"" \
+           "[from]=\"\\\"Invalid \\\" From\\\" <test_suite@notmuchmail.org>\""
+thread=$(notmuch search --output=threads subject:message-with-invalid-from)
+test_emacs "(notmuch-show \"$thread\")
+           (test-output \"OUTPUT.raw\")"
+cat <<EOF >EXPECTED
+"Invalid " (2001-01-05) (inbox)
+Subject: message-with-invalid-from
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: GENERATED_DATE
+
+This is just a test message (#1)
+EOF
+notmuch_date_sanitize < OUTPUT.raw > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Navigation of notmuch-search to thread view"
+test_emacs '(notmuch-search "tag:inbox")
+           (notmuch-test-wait)
+           (goto-char (point-min))
+           (re-search-forward "Working with Maildir")
+           (notmuch-search-show-thread)
+           (notmuch-test-wait)
+           (test-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
+
+test_begin_subtest "Add tag from search view"
+os_x_darwin_thread=$(notmuch search --output=threads id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com)
+test_emacs "(notmuch-search \"$os_x_darwin_thread\")
+           (notmuch-test-wait)
+           (execute-kbd-macro \"+tag-from-search-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-search-view unread)"
+
+test_begin_subtest "Remove tag from search view"
+test_emacs "(notmuch-search \"$os_x_darwin_thread\")
+           (notmuch-test-wait)
+           (execute-kbd-macro \"-tag-from-search-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "Add tag (large query)"
+# We use a long query to force us into batch mode and use a funny tag
+# that requires escaping for batch tagging.
+test_emacs "(notmuch-tag (concat \"$os_x_darwin_thread\" \" or \" (make-string notmuch-tag-argument-limit ?x)) (list \"+tag-from-%-large-query\"))"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-%-large-query unread)"
+notmuch tag -tag-from-%-large-query $os_x_darwin_thread
+
+test_begin_subtest "notmuch-show: add single tag to single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+           (execute-kbd-macro \"+tag-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-show-view unread)"
+
+test_begin_subtest "notmuch-show: remove single tag from single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+           (execute-kbd-macro \"-tag-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "notmuch-show: add multiple tags to single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+           (execute-kbd-macro \"+tag1-from-show-view +tag2-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag1-from-show-view tag2-from-show-view unread)"
+
+test_begin_subtest "notmuch-show: remove multiple tags from single message"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\")
+           (execute-kbd-macro \"-tag1-from-show-view -tag2-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "Message with .. in Message-Id:"
+add_message [id]=123..456@example '[subject]="Message with .. in Message-Id"'
+test_emacs '(notmuch-search "id:\"123..456@example\"")
+           (notmuch-test-wait)
+           (execute-kbd-macro "+search-add")
+           (execute-kbd-macro "+search-remove")
+           (execute-kbd-macro "-search-remove")
+           (notmuch-show "id:\"123..456@example\"")
+           (notmuch-test-wait)
+           (execute-kbd-macro "+show-add")
+           (execute-kbd-macro "+show-remove")
+           (execute-kbd-macro "-show-remove")'
+output=$(notmuch search 'id:"123..456@example"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message with .. in Message-Id (inbox search-add show-add)"
+
+test_begin_subtest "Message with quote in Message-Id:"
+add_message '[id]="\"quote\"@example"' '[subject]="Message with quote in Message-Id"'
+test_emacs '(notmuch-search "subject:\"Message with quote\"")
+           (notmuch-test-wait)
+           (execute-kbd-macro "+search-add")
+            (notmuch-search-show-thread)
+           (notmuch-test-wait)
+           (execute-kbd-macro "+show-add")'
+output=$(notmuch search 'id:"""quote""@example"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message with quote in Message-Id (inbox search-add show-add)"
+
+test_begin_subtest "Sending a message via (fake) SMTP"
+emacs_deliver_message \
+    'Testing message sent via SMTP' \
+    'This is a test that messages are sent via SMTP' \
+    '(message-goto-to)
+     (kill-whole-line)
+     (insert "To: user@example.com\n")'
+sed \
+    -e s',^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' \
+    -e s',^Message-ID: <.*>$,Message-ID: <XXX>,' \
+    -e s',^\(Content-Type: text/plain\); charset=us-ascii$,\1,' < sent_message >OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: user@example.com
+Subject: Testing message sent via SMTP
+Date: 01 Jan 2000 12:00:00 -0000
+User-Agent: Notmuch/XXX Emacs/XXX
+Message-ID: <XXX>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+This is a test that messages are sent via SMTP
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Verify that sent messages are saved/searchable (via FCC)"
+notmuch new > /dev/null
+output=$(notmuch search 'subject:"testing message sent via SMTP"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; Testing message sent via SMTP (inbox)"
+
+test_begin_subtest "notmuch-fcc-dirs set to nil"
+test_emacs "(let ((notmuch-fcc-dirs nil))
+             (notmuch-mua-mail)
+             (test-output))"
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: 
+--text follows this line--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+# Make another FCC maildir specific for the next test
+mkdir -p mail/sent-string/cur
+mkdir -p mail/sent-string/new
+mkdir -p mail/sent-string/tmp
+
+test_begin_subtest "notmuch-fcc-dirs set to a string"
+test_emacs "(let ((notmuch-fcc-dirs \"sent-string\"))
+             (notmuch-mua-mail)
+             (test-output))"
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: 
+Fcc: ${MAIL_DIR}/sent-string
+--text follows this line--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+# Make more FCC maildirs specific for the next test
+mkdir -p mail/sent-list-match/cur
+mkdir -p mail/sent-list-match/new
+mkdir -p mail/sent-list-match/tmp
+mkdir -p mail/failure/cur
+mkdir -p mail/failure/new
+mkdir -p mail/failure/tmp
+
+test_begin_subtest "notmuch-fcc-dirs set to a list (with match)"
+test_emacs "(let ((notmuch-fcc-dirs
+                  '((\"notmuchmail.org\" . \"sent-list-match\")
+                    (\".*\" . \"failure\"))))
+             (notmuch-mua-mail)
+             (test-output))"
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: 
+Fcc: ${MAIL_DIR}/sent-list-match
+--text follows this line--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+# Make another FCC maildir specific for the next test
+mkdir -p mail/sent-list-catch-all/cur
+mkdir -p mail/sent-list-catch-all/new
+mkdir -p mail/sent-list-catch-all/tmp
+
+test_begin_subtest "notmuch-fcc-dirs set to a list (catch-all)"
+test_emacs "(let ((notmuch-fcc-dirs
+                  '((\"example.com\" . \"failure\")
+                    (\".*\" . \"sent-list-catch-all\"))))
+             (notmuch-mua-mail)
+             (test-output))"
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: 
+Fcc: ${MAIL_DIR}/sent-list-catch-all
+--text follows this line--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "notmuch-fcc-dirs set to a list (no match)"
+test_emacs "(let ((notmuch-fcc-dirs
+                  '((\"example.com\" . \"failure\")
+                    (\"nomatchhere.net\" . \"failure\"))))
+             (notmuch-mua-mail)
+             (test-output))"
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: 
+--text follows this line--
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Reply within emacs"
+test_emacs '(let ((message-hidden-headers ''()))
+           (notmuch-search "subject:\"testing message sent via SMTP\"")
+           (notmuch-test-wait)
+           (notmuch-search-reply-to-thread)
+           (test-output))'
+sed -i -e 's/^In-Reply-To: <.*>$/In-Reply-To: <XXX>/' OUTPUT
+sed -i -e 's/^References: <.*>$/References: <XXX>/' OUTPUT
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: user@example.com
+Subject: Re: Testing message sent via SMTP
+In-Reply-To: <XXX>
+Fcc: ${MAIL_DIR}/sent
+References: <XXX>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Notmuch Test Suite <test_suite@notmuchmail.org> writes:
+
+> This is a test that messages are sent via SMTP
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Reply from alternate address within emacs"
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=test_suite_other@notmuchmail.org
+
+test_emacs "(let ((message-hidden-headers '()))
+           (notmuch-search \"id:\\\"${gen_msg_id}\\\"\")
+           (notmuch-test-wait)
+           (notmuch-search-reply-to-thread)
+           (test-output))"
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+To: Sender <sender@example.com>
+Subject: Re: ${test_subtest_name}
+In-Reply-To: <${gen_msg_id}>
+Fcc: ${MAIL_DIR}/sent
+References: <${gen_msg_id}>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Sender <sender@example.com> writes:
+
+> This is just a test message (#${gen_msg_cnt})
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Reply from address in named group list within emacs"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]=group:test_suite@notmuchmail.org,someone@example.com\;' \
+             [cc]=test_suite_other@notmuchmail.org
+
+test_emacs "(let ((message-hidden-headers '()))
+           (notmuch-search \"id:\\\"${gen_msg_id}\\\"\")
+           (notmuch-test-wait)
+           (notmuch-search-reply-to-thread)
+           (test-output))"
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Sender <sender@example.com>, someone@example.com
+Subject: Re: ${test_subtest_name}
+In-Reply-To: <${gen_msg_id}>
+Fcc: ${MAIL_DIR}/sent
+References: <${gen_msg_id}>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Sender <sender@example.com> writes:
+
+> This is just a test message (#${gen_msg_cnt})
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Reply within emacs to a multipart/mixed message"
+test_emacs '(let ((message-hidden-headers ''()))
+           (notmuch-show "id:20091118002059.067214ed@hikari")
+               (notmuch-show-reply)
+               (test-output))'
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Adrian Perez de Castro <aperez@igalia.com>, notmuch@notmuchmail.org
+Subject: Re: [notmuch] Introducing myself
+In-Reply-To: <20091118002059.067214ed@hikari>
+Fcc: ${MAIL_DIR}/sent
+References: <20091118002059.067214ed@hikari>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Adrian Perez de Castro <aperez@igalia.com> writes:
+
+> Hello to all,
+>
+> I have just heard about Not Much today in some random Linux-related news
+> site (LWN?), my name is Adrian Perez and I work as systems administrator
+> (although I can do some code as well :P). I have always thought that the
+> ideas behind Sup were great, but after some time using it, I got tired of
+> the oddities that it has. I also do not like doing things like having to
+> install Ruby just for reading and sorting mails. Some time ago I thought
+> about doing something like Not Much and in fact I played a bit with the
+> Python+Xapian and the Python+Whoosh combinations, because I find relaxing
+> to code things in Python when I am not working and also it is installed
+> by default on most distribution. I got to have some mailboxes indexed and
+> basic searching working a couple of months ago. Lately I have been very
+> busy and had no time for coding, and them... boom! Not Much appears -- and
+> it is almost exactly what I was trying to do, but faster. I have been
+> playing a bit with Not Much today, and I think it has potential.
+>
+> Also, I would like to share one idea I had in mind, that you might find
+> interesting: One thing I have found very annoying is having to re-tag my
+> mail when the indexes get b0rked (it happened a couple of times to me while
+> using Sup), so I was planning to mails as read/unread and adding the tags
+> not just to the index, but to the mail text itself, e.g. by adding a
+> "X-Tags" header field or by reusing the "Keywords" one. This way, the index
+> could be totally recreated by re-reading the mail directories, and this
+> would also allow to a tools like OfflineIMAP [1] to get the mails into a
+> local maildir, tagging and indexing the mails with the e-mail reader and
+> then syncing back the messages with the "X-Tags" header to the IMAP server.
+> This would allow to use the mail reader from a different computer and still
+> have everything tagged finely.
+>
+> Best regards,
+>
+>
+> ---
+> [1] http://software.complete.org/software/projects/show/offlineimap
+>
+> -- 
+> Adrian Perez de Castro <aperez@igalia.com>
+> Igalia - Free Software Engineering
+> _______________________________________________
+> notmuch mailing list
+> notmuch@notmuchmail.org
+> http://notmuchmail.org/mailman/listinfo/notmuch
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Reply within emacs to a multipart/alternative message"
+test_emacs '(let ((message-hidden-headers ''()))
+           (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
+               (notmuch-show-reply)
+               (test-output))'
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Alex Botero-Lowry <alex.boterolowry@gmail.com>, notmuch@notmuchmail.org
+Subject: Re: [notmuch] preliminary FreeBSD support
+In-Reply-To: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
+Fcc: ${MAIL_DIR}/sent
+References: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Alex Botero-Lowry <alex.boterolowry@gmail.com> writes:
+
+> I saw the announcement this morning, and was very excited, as I had been
+> hoping sup would be turned into a library,
+> since I like the concept more than the UI (I'd rather an emacs interface).
+>
+> I did a preliminary compile which worked out fine, but
+> sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on
+> FreeBSD, so notmuch_config_open segfaulted.
+>
+> Attached is a patch that supplies a default buffer size of 64 in cases where
+> -1 is returned.
+>
+> http://www.opengroup.org/austin/docs/austin_328.txt - seems to indicate this
+> is acceptable behavior,
+> and http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically
+> uses 64 as the
+> buffer size.
+> _______________________________________________
+> notmuch mailing list
+> notmuch@notmuchmail.org
+> http://notmuchmail.org/mailman/listinfo/notmuch
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Reply within emacs to an html-only message"
+add_message '[content-type]="text/html"' \
+           '[body]="Hi,<br />This is an <b>HTML</b> test message.<br /><br />OK?"'
+test_emacs "(let ((message-hidden-headers '()) (mm-text-html-renderer 'html2text))
+           (notmuch-show \"id:${gen_msg_id}\")
+           (notmuch-show-reply)
+           (test-output))"
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: Re: Reply within emacs to an html-only message
+In-Reply-To: <${gen_msg_id}>
+Fcc: ${MAIL_DIR}/sent
+References: <${gen_msg_id}>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Notmuch Test Suite <test_suite@notmuchmail.org> writes:
+
+> Hi,This is an HTML test message.OK?
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Quote MML tags in reply"
+message_id='test-emacs-mml-quoting@message.id'
+add_message [id]="$message_id" \
+           "[subject]='$test_subtest_name'" \
+           '[body]="<#part disposition=inline>"'
+test_emacs "(let ((message-hidden-headers '()))
+             (notmuch-show \"id:$message_id\")
+             (notmuch-show-reply)
+             (test-output))"
+sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: 
+Subject: Re: Quote MML tags in reply
+In-Reply-To: <test-emacs-mml-quoting@message.id>
+Fcc: ${MAIL_DIR}/sent
+References: <test-emacs-mml-quoting@message.id>
+User-Agent: Notmuch/XXX Emacs/XXX
+--text follows this line--
+Notmuch Test Suite <test_suite@notmuchmail.org> writes:
+
+> <#!part disposition=inline>
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Save attachment from within emacs using notmuch-show-save-attachments"
+# save as archive to test that Emacs does not re-compress .gz
+test_emacs '(let ((standard-input "\"attachment1.gz\""))
+             (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
+             (notmuch-show-save-attachments))'
+test_expect_equal_file attachment1.gz "$EXPECTED/attachment"
+
+test_begin_subtest "Save attachment from within emacs using notmuch-show-save-part"
+# save as archive to test that Emacs does not re-compress .gz
+test_emacs '(let ((standard-input "\"attachment2.gz\""))
+             (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
+             (search-forward "0001-Deal-with")
+             (notmuch-show-save-part))'
+test_expect_equal_file attachment2.gz "$EXPECTED/attachment"
+
+test_begin_subtest "Save 8bit attachment from within emacs using notmuch-show-save-attachments"
+
+add_message '[subject]="Attachment with 8bit chars"' \
+       '[header]="MIME-Version: 1.0"' \
+       '[content-type]="multipart/mixed; boundary=\"abcd\""' \
+       '[body]="--abcd
+Content-Type: text/plain
+
+Attachment follows:
+
+--abcd
+Content-Type: application/octet-stream; name=\"sample\"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename=\"sample\"
+
+“¡ Hey ! It compiles ¡ Ship it !”
+
+--abcd--
+"'
+test_emacs '(notmuch-show "id:'"${gen_msg_id}"'")
+           (delete-file "OUTPUT")
+           (let ((standard-input "\"OUTPUT\""))
+             (notmuch-show-save-attachments))'
+
+test_expect_equal "$(cat OUTPUT)" '“¡ Hey ! It compiles ¡ Ship it !”'
+
+test_begin_subtest "View raw message within emacs"
+test_emacs '(notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
+           (notmuch-show-view-raw-message)
+           (test-output)'
+test_expect_equal_file OUTPUT $EXPECTED/raw-message-cf0c4d-52ad0a
+
+test_begin_subtest "Hiding/showing signature in notmuch-show view"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(notmuch-show \"$maildir_storage_thread\")
+           (search-forward \"Click/Enter to show.\")
+           (button-activate (button-at (point)))
+           (search-backward \"Click/Enter to hide.\")
+           (button-activate (button-at (point)))
+           (test-output)"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
+
+test_begin_subtest "Detection and hiding of top-post quoting of message"
+add_message '[subject]="The problem with top-posting"' \
+           [id]=top-post-target \
+           '[body]="A: Because it messes up the order in which people normally read text.
+Q: Why is top-posting such a bad thing?
+A: Top-posting.
+Q: What is the most annoying thing in e-mail?"'
+add_message '[from]="Top Poster <top@poster.com>"' \
+           [in-reply-to]=top-post-target \
+           [references]=top-post-target \
+           '[subject]="Re: The problem with top-posting"' \
+           '[body]="Thanks for the advice! I will be sure to put it to good use.
+
+-Top Poster
+
+----- Original Message -----
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmai.org>
+Sent: Fri, 05 Jan 2001 15:43:57 +0000
+Subject: The problem with top-posting
+
+Q: Why is top-posting such a bad thing?
+A: Top-posting.
+Q: What is the most annoying thing in e-mail?"'
+test_emacs "(notmuch-show \"top-posting\")
+           (test-visible-output \"OUTPUT.raw\")"
+echo "Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (inbox)
+Subject: The problem with top-posting
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: GENERATED_DATE
+
+A: Because it messes up the order in which people normally read text.
+Q: Why is top-posting such a bad thing?
+A: Top-posting.
+Q: What is the most annoying thing in e-mail?
+Top Poster <top@poster.com> (2001-01-05) (inbox unread)
+Subject: Re: The problem with top-posting
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: GENERATED_DATE
+
+Thanks for the advice! I will be sure to put it to good use.
+
+-Top Poster
+
+[ 9-line hidden original message. Click/Enter to show. ]" > EXPECTED
+notmuch_date_sanitize < OUTPUT.raw > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Hiding message in notmuch-show view"
+test_emacs '(notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+           (notmuch-show-toggle-message)
+           (test-visible-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-with-hidden-messages
+
+test_begin_subtest "Hiding message with visible citation in notmuch-show view"
+test_emacs '(notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+           (search-forward "Click/Enter to show.")
+           (button-activate (button-at (point)))
+           (notmuch-show-toggle-message)
+           (test-visible-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-with-hidden-messages
+
+test_begin_subtest "notmuch-show: show message headers"
+test_emacs \
+       '(let ((notmuch-message-headers '\''("Subject" "To" "Cc" "Date"))
+              (notmuch-message-headers-visible t))
+          (notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+          (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-message-with-headers-visible
+
+test_begin_subtest "notmuch-show: hide message headers"
+test_emacs \
+       '(let ((notmuch-message-headers '\''("Subject" "To" "Cc" "Date"))
+              (notmuch-message-headers-visible nil))
+          (notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+          (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-message-with-headers-hidden
+
+test_begin_subtest "notmuch-show: hide message headers (w/ notmuch-show-toggle-visibility-headers)"
+test_emacs \
+       '(let ((notmuch-message-headers '\''("Subject" "To" "Cc" "Date"))
+              (notmuch-message-headers-visible t))
+          (notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+          (notmuch-show-toggle-visibility-headers)
+          (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-message-with-headers-hidden
+
+test_begin_subtest "notmuch-show: collapse all messages in thread"
+test_emacs '(notmuch-show "id:f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com")
+       (let ((current-prefix-arg t))
+         (notmuch-show-open-or-close-all)
+         (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-with-all-messages-collapsed
+
+test_begin_subtest "notmuch-show: uncollapse all messages in thread"
+test_emacs '(notmuch-show "id:f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com")
+       (notmuch-show-open-or-close-all)
+       (test-visible-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-with-all-messages-uncollapsed
+
+test_begin_subtest "Stashing in notmuch-show"
+add_message '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' \
+    '[from]="Some One <someone@somewhere.org>"' \
+    '[to]="Some One Else <notsomeone@somewhere.org>"' \
+    '[cc]="Notmuch <notmuch@notmuchmail.org>"' \
+    '[subject]="Stash my stashables"' \
+    '[id]="bought"' \
+    '[body]="Unable to stash body. Where did you get it in the first place?!?"'
+notmuch tag +stashtest id:${gen_msg_id}
+test_emacs '(notmuch-show "id:\"bought\"")
+       (notmuch-show-stash-date)
+       (notmuch-show-stash-from)
+       (notmuch-show-stash-to)
+       (notmuch-show-stash-cc)
+       (notmuch-show-stash-subject)
+       (notmuch-show-stash-message-id)
+       (notmuch-show-stash-message-id-stripped)
+       (notmuch-show-stash-tags)
+       (notmuch-show-stash-filename)
+       (notmuch-show-stash-mlarchive-link "Gmane")
+       (notmuch-show-stash-mlarchive-link "MARC")
+       (notmuch-show-stash-mlarchive-link "Mail Archive, The")
+       (switch-to-buffer
+         (generate-new-buffer "*test-stashing*"))
+       (dotimes (i 12)
+         (yank)
+         (insert "\n")
+         (rotate-yank-pointer 1))
+       (reverse-region (point-min) (point-max))
+           (test-output)'
+cat <<EOF >EXPECTED
+Sat, 01 Jan 2000 12:00:00 +0000
+Some One <someone@somewhere.org>
+Some One Else <notsomeone@somewhere.org>
+Notmuch <notmuch@notmuchmail.org>
+Stash my stashables
+id:bought
+bought
+inbox,stashtest
+${gen_msg_filename}
+http://mid.gmane.org/bought
+http://marc.info/?i=bought
+http://mail-archive.com/search?l=mid&q=bought
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Stashing in notmuch-search"
+test_emacs '(notmuch-search "id:\"bought\"")
+       (notmuch-test-wait)
+       (notmuch-search-stash-thread-id)
+       (switch-to-buffer
+         (generate-new-buffer "*test-stashing*"))
+       (yank)
+           (test-output)'
+sed -i -e 's/^thread:.*$/thread:XXX/' OUTPUT
+test_expect_equal "$(cat OUTPUT)" "thread:XXX"
+
+test_begin_subtest 'notmuch-show-advance-and-archive with invisible signature'
+message1='id:20091118010116.GC25380@dottiness.seas.harvard.edu'
+message2='id:1258491078-29658-1-git-send-email-dottedmag@dottedmag.net'
+test_emacs "(notmuch-show \"$message2\")
+           (test-output \"EXPECTED\")"
+test_emacs "(notmuch-search \"$message1 or $message2\")
+           (notmuch-test-wait)
+           (notmuch-search-show-thread)
+           (goto-char (point-max))
+           (redisplay)
+           (notmuch-show-advance-and-archive)
+           (test-output)"
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Refresh show buffer"
+test_emacs '(notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+           (test-visible-output "EXPECTED")
+           (notmuch-show-refresh-view)
+           (test-visible-output)'
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Refresh modified show buffer"
+test_emacs '(notmuch-show "id:f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com")
+           (notmuch-show-toggle-message)
+           (notmuch-show-next-message)
+           (notmuch-show-toggle-message)
+           (test-visible-output "EXPECTED")
+           (notmuch-show-refresh-view)
+           (test-visible-output)'
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Do not call notmuch for non-inlinable application/mpeg parts"
+id='message-with-application/mpeg-attachment@notmuchmail.org'
+emacs_fcc_message \
+    'Message with application/mpeg attachment' \
+    '' \
+    "(message-goto-eoh)
+     (insert \"Message-ID: <$id>\n\")
+     (message-goto-body)
+     (mml-insert-part \"application/mpeg\")
+     (insert \"a fake mp3 file\")"
+notmuch_counter_reset
+test_emacs "(let ((notmuch-command \"$notmuch_counter_command\"))
+             (notmuch-show \"id:$id\"))"
+test_expect_equal $(notmuch_counter_value) 1
+
+test_begin_subtest "Do not call notmuch for non-inlinable audio/mpeg parts"
+id='message-with-audio/mpeg-attachment@notmuchmail.org'
+emacs_fcc_message \
+    'Message with audio/mpeg attachment' \
+    '' \
+    "(message-goto-eoh)
+     (insert \"Message-ID: <$id>\n\")
+     (message-goto-body)
+     (mml-insert-part \"audio/mpeg\")
+     (insert \"a fake mp3 file\")"
+notmuch_counter_reset
+test_emacs "(let ((notmuch-command \"$notmuch_counter_command\"))
+             (notmuch-show \"id:$id\"))"
+test_expect_equal $(notmuch_counter_value) 1
+
+test_begin_subtest "notmuch-hello-mode hook is called"
+counter=$(test_emacs \
+    '(let ((notmuch-hello-mode-hook-counter 0))
+       (kill-buffer "*notmuch-hello*")
+       (notmuch-hello)
+       notmuch-hello-mode-hook-counter)'
+)
+test_expect_equal "$counter" 1
+
+test_begin_subtest "notmuch-hello-mode hook is not called on updates"
+counter=$(test_emacs \
+    '(let ((notmuch-hello-mode-hook-counter 0))
+       (kill-buffer "*notmuch-hello*")
+       (notmuch-hello)
+       (notmuch-hello-update)
+       notmuch-hello-mode-hook-counter)'
+)
+test_expect_equal "$counter" 1
+
+test_begin_subtest "notmuch-hello-refresh hook is called"
+counter=$(test_emacs \
+    '(let ((notmuch-hello-refresh-hook-counter 0))
+       (kill-buffer "*notmuch-hello*")
+       (notmuch-hello)
+       notmuch-hello-refresh-hook-counter)'
+)
+test_expect_equal "$counter" 1
+
+test_begin_subtest "notmuch-hello-refresh hook is called on updates"
+counter=$(test_emacs \
+    '(let ((notmuch-hello-refresh-hook-counter 0))
+       (kill-buffer "*notmuch-hello*")
+       (notmuch-hello)
+       (notmuch-hello-update)
+       notmuch-hello-refresh-hook-counter)'
+)
+test_expect_equal "$counter" 2
+
+
+add_message '[subject]="HTML mail with images"' \
+    '[content-type]="multipart/related; boundary=abcd"' \
+    '[body]="--abcd
+Content-Type: text/html
+
+<img src="cid:330@goomoji.gmail"> smiley
+
+--abcd
+Content-Type: image/gif
+Content-Transfer-Encoding: base64
+Content-ID: <330@goomoji.gmail>
+
+R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMl
+WLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7
+--abcd--"'
+test_emacs "(let ((mm-text-html-renderer
+                  (if (assq 'shr mm-text-html-renderer-alist)
+                      'shr 'html2text)))
+             (notmuch-show \"id:${gen_msg_id}\"))
+           (test-output)" > /dev/null
+# Different Emacs versions and renderers give very different results,
+# so just check that something reasonable showed up.  We first cat the
+# output so the test framework will print it if the test fails.
+test_expect_success "Rendering HTML mail with images" \
+    'cat OUTPUT && grep -q smiley OUTPUT'
+
+
+test_begin_subtest "Search handles subprocess error exit codes"
+cat > notmuch_fail <<EOF
+#!/bin/sh
+echo '()'
+exit 1
+EOF
+chmod a+x notmuch_fail
+test_emacs "(let ((notmuch-command \"$PWD/notmuch_fail\"))
+              (with-current-buffer \"*Messages*\" (erase-buffer))
+              (with-current-buffer (get-buffer-create \"*Notmuch errors*\")
+                 (erase-buffer))
+              (notmuch-search \"tag:inbox\")
+              (notmuch-test-wait)
+              (with-current-buffer \"*Messages*\"
+                 (test-output \"MESSAGES\"))
+              (with-current-buffer \"*Notmuch errors*\"
+                 (test-output \"ERROR\"))
+              (test-output))"
+
+test_expect_equal "$(notmuch_emacs_error_sanitize notmuch_fail OUTPUT MESSAGES ERROR)" "\
+=== OUTPUT ===
+End of search results.
+=== MESSAGES ===
+YYY/notmuch_fail exited with status 1 (see *Notmuch errors* for more details)
+=== ERROR ===
+[XXX]
+YYY/notmuch_fail exited with status 1
+command: YYY/notmuch_fail search --format\=sexp --format-version\=2 --sort\=newest-first tag\:inbox
+exit status: 1"
+
+test_begin_subtest "Search handles subprocess warnings"
+cat > notmuch_fail <<EOF
+#!/bin/sh
+echo '()'
+echo This is a warning >&2
+echo This is another warning >&2
+exit 0
+EOF
+chmod a+x notmuch_fail
+test_emacs "(let ((notmuch-command \"$PWD/notmuch_fail\"))
+              (with-current-buffer \"*Messages*\" (erase-buffer))
+              (with-current-buffer (get-buffer-create \"*Notmuch errors*\")
+                 (erase-buffer))
+              (notmuch-search \"tag:inbox\")
+              (notmuch-test-wait)
+              (with-current-buffer \"*Messages*\"
+                 (test-output \"MESSAGES\"))
+              (with-current-buffer \"*Notmuch errors*\"
+                 (test-output \"ERROR\"))
+              (test-output))"
+sed -i -e 's/^\[.*\]$/[XXX]/' ERROR
+test_expect_equal "$(cat OUTPUT; echo ---; cat MESSAGES; echo ---; cat ERROR)" "\
+End of search results.
+---
+This is a warning (see *Notmuch errors* for more details)
+---
+[XXX]
+This is a warning
+This is another warning"
+
+test_begin_subtest "Search thread tag operations are race-free"
+add_message '[subject]="Search race test"'
+gen_msg_id_1=$gen_msg_id
+generate_message '[in-reply-to]="<'$gen_msg_id_1'>"' \
+           '[references]="<'$gen_msg_id_1'>"' \
+           '[subject]="Search race test two"'
+test_emacs '(notmuch-search "subject:\"search race test\"")
+           (notmuch-test-wait)
+           (notmuch-poll)
+           (execute-kbd-macro "+search-thread-race-tag")'
+output=$(notmuch search --output=messages 'tag:search-thread-race-tag')
+test_expect_equal "$output" "id:$gen_msg_id_1"
+
+test_begin_subtest "Search global tag operations are race-free"
+generate_message '[in-reply-to]="<'$gen_msg_id_1'>"' \
+           '[references]="<'$gen_msg_id_1'>"' \
+           '[subject]="Re: Search race test"'
+test_emacs '(notmuch-search "subject:\"search race test\" -subject:two")
+           (notmuch-test-wait)
+           (notmuch-poll)
+           (execute-kbd-macro "*+search-global-race-tag")'
+output=$(notmuch search --output=messages 'tag:search-global-race-tag')
+test_expect_equal "$output" "id:$gen_msg_id_1"
+
+test_done
diff --git a/test/T320-emacs-large-search-buffer.sh b/test/T320-emacs-large-search-buffer.sh
new file mode 100755 (executable)
index 0000000..8b1251f
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+test_description="Emacs with large search results buffer"
+. ./test-lib.sh
+
+x=xxxxxxxxxx # 10
+x=$x$x$x$x$x$x$x$x$x$x # 100
+x=$x$x$x$x$x$x$x$x$x # 900
+
+# We generate a long subject here (over 900 bytes) so that the emacs
+# search results get large quickly. With 30 such messages we should
+# cross several 4kB page boundaries and see the bug.
+n=30
+for i in $(seq 1 $n); do
+  # Roughly 100B2 KiB per message.  That is, we need two messages in order to
+  # exceed the typical size of the pipe buffer (4 KiB on commodity systems).
+  generate_message '[subject]="$x $i of $n"'
+done
+
+notmuch new > /dev/null
+
+test_begin_subtest "Ensure that emacs doesn't drop results"
+notmuch search '*' > EXPECTED
+sed -i -e 's/^thread:[0-9a-f]*  //' -e 's/;//' -e 's/xx*/[BLOB]/' EXPECTED
+echo 'End of search results.' >> EXPECTED
+
+test_emacs '(notmuch-search "*")
+           (notmuch-test-wait)
+           (test-output)'
+sed -i -e s',  *, ,g' -e 's/xxx*/[BLOB]/g' OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_done
diff --git a/test/T330-emacs-subject-to-filename.sh b/test/T330-emacs-subject-to-filename.sh
new file mode 100755 (executable)
index 0000000..230c324
--- /dev/null
@@ -0,0 +1,138 @@
+#!/usr/bin/env bash
+
+test_description="emacs: mail subject to filename"
+. ./test-lib.sh
+
+# emacs server can't be started in a child process with $(test_emacs ...)
+test_emacs '(ignore)' > /dev/null
+
+# test notmuch-wash-subject-to-patch-sequence-number (subject)
+test_begin_subtest "no patch sequence number"
+output=$(test_emacs '(format "%S" (notmuch-wash-subject-to-patch-sequence-number
+      "[PATCH] A normal patch subject without numbers"))'
+)
+test_expect_equal "$output" '"nil"'
+
+test_begin_subtest "patch sequence number #1"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "[PATCH 2/3] A most regular patch subject")'
+)
+test_expect_equal "$output" 2
+
+test_begin_subtest "patch sequence number #2"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "  [dummy list prefix]  [RFC PATCH v2 13/42]  Special prefixes")'
+)
+test_expect_equal "$output" 13
+
+test_begin_subtest "patch sequence number #3"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "[PATCH 2/3] [PATCH 032/037] use the last prefix")'
+)
+test_expect_equal "$output" 32
+
+test_begin_subtest "patch sequence number #4"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "[dummy list prefix] [PATCH 2/3] PATCH 3/3] do not use a broken prefix")'
+)
+test_expect_equal "$output" 2
+
+test_begin_subtest "patch sequence number #5"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "[RFC][PATCH 3/5][PATCH 4/5][PATCH 5/5] A made up test")'
+)
+test_expect_equal "$output" 5
+
+test_begin_subtest "patch sequence number #6"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "[PATCH 2/3] this -> [PATCH 3/3] is not a prefix anymore [nor this 4/4]")'
+)
+test_expect_equal "$output" 2
+
+test_begin_subtest "patch sequence number #7"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number
+      "[liberally accept crapola right before123/456and after] the numbers")'
+)
+test_expect_equal "$output" 123
+
+# test notmuch-wash-subject-to-filename (subject &optional maxlen)
+test_begin_subtest "filename #1"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "just a subject line")'
+)
+test_expect_equal "$output" '"just-a-subject-line"'
+
+test_begin_subtest "filename #2"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      " [any]  [prefixes are ] [removed!] from the subject")'
+)
+test_expect_equal "$output" '"from-the-subject"'
+
+test_begin_subtest "filename #3"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "  leading and trailing space  ")'
+)
+test_expect_equal "$output" '"leading-and-trailing-space"'
+
+test_begin_subtest "filename #4"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "!#  leading ()// &%, and in between_and_trailing garbage ()(&%%")'
+)
+test_expect_equal "$output" '"-leading-and-in-between_and_trailing-garbage"'
+
+test_begin_subtest "filename #5"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_01234567890")'
+)
+test_expect_equal "$output" '"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_01234567890"'
+
+test_begin_subtest "filename #6"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "sequences of ... are squashed and trailing are removed ...")'
+)
+test_expect_equal "$output" '"sequences-of-.-are-squashed-and-trailing-are-removed"'
+
+test_begin_subtest "filename #7"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "max length test" 1)'
+)
+test_expect_equal "$output" '"m"'
+
+test_begin_subtest "filename #8"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "max length test /&(/%&/%%&¤%¤" 20)'
+)
+test_expect_equal "$output" '"max-length-test"'
+
+test_begin_subtest "filename #9"
+output=$(test_emacs '(notmuch-wash-subject-to-filename
+      "[a prefix] [is only separated] by [spaces], so \"by\" is not okay!")'
+)
+test_expect_equal "$output" '"by-spaces-so-by-is-not-okay"'
+
+# test notmuch-wash-subject-to-patch-filename (subject)
+test_begin_subtest "patch filename #1"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-filename
+      "[RFC][PATCH 099/100] rewrite notmuch")'
+)
+test_expect_equal "$output" '"0099-rewrite-notmuch.patch"'
+
+test_begin_subtest "patch filename #2"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-filename
+      "[RFC PATCH v1] has no patch number, default to 1")'
+)
+test_expect_equal "$output" '"0001-has-no-patch-number-default-to-1.patch"'
+
+test_begin_subtest "patch filename #3"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-filename
+      "[PATCH 4/5] the maximum length of a patch filename is 52 + patch sequence number + .patch extension")'
+)
+test_expect_equal "$output" '"0004-the-maximum-length-of-a-patch-filename-is-52-patch-s.patch"'
+
+test_begin_subtest "patch filename #4"
+output=$(test_emacs '(notmuch-wash-subject-to-patch-filename
+      "[PATCH 4/5] the maximum length of a patch filename is 52 + patchh ! sequence number + .patch extension, *before* trimming trailing - and .")'
+)
+test_expect_equal "$output" '"0004-the-maximum-length-of-a-patch-filename-is-52-patchh.patch"'
+
+test_done
diff --git a/test/T340-maildir-sync.sh b/test/T340-maildir-sync.sh
new file mode 100755 (executable)
index 0000000..3186e70
--- /dev/null
@@ -0,0 +1,189 @@
+#!/usr/bin/env bash
+
+test_description="maildir synchronization"
+
+. ./test-lib.sh
+
+# Create the expected maildir structure
+mkdir $MAIL_DIR/cur
+mkdir $MAIL_DIR/new
+mkdir $MAIL_DIR/tmp
+
+test_begin_subtest "Adding 'S' flag to existing filename removes 'unread' tag"
+add_message [subject]='"Adding S flag"' [filename]='adding-s-flag:2,' [dir]=cur
+output=$(notmuch search subject:"Adding S flag" | notmuch_search_sanitize)
+output+="
+"
+mv "${gen_msg_filename}" "${gen_msg_filename}S"
+output+=$(NOTMUCH_NEW)
+output+="
+"
+output+=$(notmuch search subject:"Adding S flag" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Adding S flag (inbox unread)
+No new mail. Detected 1 file rename.
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Adding S flag (inbox)"
+
+test_begin_subtest "Adding message with 'S' flag prevents 'unread' tag"
+add_message [subject]='"Adding message with S"' [filename]='adding-with-s-flag:2,S' [dir]=cur
+output=$(notmuch search subject:"Adding message with S" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Adding message with S (inbox)"
+
+test_begin_subtest "Adding 'replied' tag adds 'R' flag to filename"
+add_message [subject]='"Adding replied tag"' [filename]='adding-replied-tag:2,S' [dir]=cur
+notmuch tag +replied subject:"Adding replied tag"
+output=$(cd ${MAIL_DIR}/cur; ls -1 adding-replied*)
+test_expect_equal "$output" "adding-replied-tag:2,RS"
+
+test_begin_subtest "notmuch show works with renamed file (without notmuch new)"
+output=$(notmuch show --format=json id:${gen_msg_id} | notmuch_json_show_sanitize)
+test_expect_equal_json "$output" '[[[{"id": "XXXXX",
+"match": true,
+"excluded": false,
+"filename": "YYYYY",
+"timestamp": 42,
+"date_relative": "2001-01-05",
+"tags": ["inbox","replied"],
+"headers": {"Subject": "Adding replied tag",
+"From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+"To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+"Date": "GENERATED_DATE"},
+"body": [{"id": 1,
+"content-type": "text/plain",
+"content": "This is just a test message (#3)\n"}]},
+[]]]]'
+
+test_expect_success 'notmuch reply works with renamed file (without notmuch new)' 'notmuch reply id:${gen_msg_id}'
+
+test_begin_subtest "notmuch new detects no file rename after tag->flag synchronization"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+test_begin_subtest "When read, message moved from new to cur"
+add_message [subject]='"Message to move to cur"' [date]='"Sat, 01 Jan 2000 12:00:00 -0000"' [filename]='message-to-move-to-cur' [dir]=new
+notmuch tag -unread subject:"Message to move to cur"
+output=$(cd "$MAIL_DIR/cur"; ls message-to-move*)
+test_expect_equal "$output" "message-to-move-to-cur:2,S"
+
+test_begin_subtest "No rename should be detected by notmuch new"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+# (*) If notmuch new was not run we've got "Processed 1 file in almost
+# no time" here. The reason is that removing unread tag in a previous
+# test created directory document in the database but this document
+# was not linked as subdirectory of $MAIL_DIR. Therefore notmuch new
+# could not reach the cur/ directory and its files in it during
+# recursive traversal.
+#
+# XXX: The above sounds like a bug that should be fixed. If notmuch is
+# creating new directories in the mail store, then it should be
+# creating all necessary database state for those directories.
+
+test_begin_subtest "Adding non-maildir tags does not move message from new to cur"
+add_message [subject]='"Message to stay in new"' \
+    [date]='"Sat, 01 Jan 2000 12:00:00 -0000"' \
+    [filename]='message-to-stay-in-new' [dir]=new
+notmuch tag +donotmove subject:"Message to stay in new"
+output=$(cd "$MAIL_DIR"; ls */message-to-stay-in-new*)
+test_expect_equal "$output" "new/message-to-stay-in-new"
+
+test_begin_subtest "Message in cur lacking maildir info gets one on any tag change"
+add_message [filename]='message-to-get-maildir-info' [dir]=cur
+notmuch tag +anytag id:$gen_msg_id
+output=$(cd "$MAIL_DIR"; ls */message-to-get-maildir-info*)
+test_expect_equal "$output" "cur/message-to-get-maildir-info:2,"
+
+test_begin_subtest "Message in new with maildir info is moved to cur on any tag change"
+add_message [filename]='message-with-info-to-be-moved-to-cur:2,' [dir]=new
+notmuch tag +anytag id:$gen_msg_id
+output=$(cd "$MAIL_DIR"; ls */message-with-info-to-be-moved-to-cur*)
+test_expect_equal "$output" "cur/message-with-info-to-be-moved-to-cur:2,"
+
+test_begin_subtest "Removing 'S' flag from existing filename adds 'unread' tag"
+add_message [subject]='"Removing S flag"' [filename]='removing-s-flag:2,S' [dir]=cur
+output=$(notmuch search subject:"Removing S flag" | notmuch_search_sanitize)
+output+="
+"
+mv "${gen_msg_filename}" "${gen_msg_filename%S}"
+output+=$(NOTMUCH_NEW)
+output+="
+"
+output+=$(notmuch search subject:"Removing S flag" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Removing S flag (inbox)
+No new mail. Detected 1 file rename.
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Removing S flag (inbox unread)"
+
+test_begin_subtest "Removing info from filename leaves tags unchanged"
+add_message [subject]='"Message to lose maildir info"' [filename]='message-to-lose-maildir-info' [dir]=cur
+notmuch tag -unread subject:"Message to lose maildir info"
+mv "$MAIL_DIR/cur/message-to-lose-maildir-info:2,S" "$MAIL_DIR/cur/message-without-maildir-info"
+output=$(NOTMUCH_NEW)
+output+="
+"
+output+=$(notmuch search subject:"Message to lose maildir info" | notmuch_search_sanitize)
+test_expect_equal "$output" "No new mail. Detected 1 file rename.
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message to lose maildir info (inbox)"
+
+add_message [subject]='"Non-maildir message"' [dir]=notmaildir [filename]='non-maildir-message'
+expected=$(notmuch search --output=files subject:"Non-maildir message")
+test_expect_success "Can remove unread tag from message in non-maildir directory" 'notmuch tag -unread subject:"Non-maildir message"'
+
+test_begin_subtest "Message in non-maildir directory does not get renamed"
+output=$(notmuch search --output=files subject:"Non-maildir message")
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "notmuch dump/restore re-synchronizes maildir tags with flags"
+# Capture current filename state
+expected=$(ls $MAIL_DIR/cur)
+# Add/remove some flags from filenames
+mv $MAIL_DIR/cur/adding-replied-tag:2,RS $MAIL_DIR/cur/adding-replied-tag:2,S
+mv $MAIL_DIR/cur/adding-s-flag:2,S $MAIL_DIR/cur/adding-s-flag:2,
+mv $MAIL_DIR/cur/adding-with-s-flag:2,S $MAIL_DIR/cur/adding-with-s-flag:2,RS
+mv $MAIL_DIR/cur/message-to-move-to-cur:2,S $MAIL_DIR/cur/message-to-move-to-cur:2,DS
+notmuch dump --output=dump.txt
+NOTMUCH_NEW >/dev/null
+notmuch restore --input=dump.txt
+output=$(ls $MAIL_DIR/cur)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest 'Adding flags to duplicate message tags the mail'
+add_message [subject]='"Duplicated message"' [dir]=cur [filename]='duplicated-message:2,'
+cp "$MAIL_DIR/cur/duplicated-message:2," "$MAIL_DIR/cur/duplicated-message-copy:2,RS"
+NOTMUCH_NEW > output
+notmuch search subject:"Duplicated message" | notmuch_search_sanitize >> output
+test_expect_equal "$(< output)" "No new mail.
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Duplicated message (inbox replied)"
+
+test_begin_subtest "Adding duplicate message without flags does not remove tags"
+cp "$MAIL_DIR/cur/duplicated-message-copy:2,RS" "$MAIL_DIR/cur/duplicated-message-another-copy:2,"
+NOTMUCH_NEW > output
+notmuch search subject:"Duplicated message" | notmuch_search_sanitize >> output
+test_expect_equal "$(< output)" "No new mail.
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Duplicated message (inbox replied)"
+
+test_begin_subtest "Tag changes modify flags of multiple files"
+notmuch tag -replied subject:"Duplicated message"
+(cd $MAIL_DIR/cur/; ls duplicated*) > actual
+test_expect_equal "$(< actual)"  "duplicated-message-another-copy:2,S
+duplicated-message-copy:2,S
+duplicated-message:2,S"
+
+test_begin_subtest "Synchronizing tag changes preserves unsupported maildir flags"
+add_message [subject]='"Unsupported maildir flags"' [dir]=cur [filename]='unsupported-maildir-flags:2,FSZxyz'
+notmuch tag +unread +draft -flagged subject:"Unsupported maildir flags"
+test_expect_equal "$(cd $MAIL_DIR/cur/; ls unsupported*)" "unsupported-maildir-flags:2,DZxyz"
+
+test_begin_subtest "A file with non-compliant maildir info will not be renamed"
+add_message [subject]='"Non-compliant maildir info"' [dir]=cur [filename]='non-compliant-maildir-info:2,These-are-not-flags-in-ASCII-order-donottouch'
+notmuch tag +unread +draft -flagged subject:"Non-compliant maildir info"
+test_expect_equal "$(cd $MAIL_DIR/cur/; ls non-compliant*)" "non-compliant-maildir-info:2,These-are-not-flags-in-ASCII-order-donottouch"
+
+test_begin_subtest "Files in new/ get default synchronized tags"
+OLDCONFIG=$(notmuch config get new.tags)
+notmuch config set new.tags test
+add_message [subject]='"File in new/"' [dir]=new [filename]='file-in-new'
+notmuch config set new.tags $OLDCONFIG
+notmuch search 'subject:"File in new"' | notmuch_search_sanitize > output
+test_expect_equal "$(< output)" \
+"thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; File in new/ (test unread)"
+
+test_done
diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh
new file mode 100755 (executable)
index 0000000..477b397
--- /dev/null
@@ -0,0 +1,360 @@
+#!/usr/bin/env bash
+
+# TODO:
+# - decryption/verification with signer key not available
+# - verification of signatures from expired/revoked keys
+
+test_description='PGP/MIME signature verification and decryption'
+. ./test-lib.sh
+
+add_gnupg_home ()
+{
+    local output
+    [ -d ${GNUPGHOME} ] && return
+    mkdir -m 0700 "$GNUPGHOME"
+    gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
+    test_debug "cat $GNUPGHOME/import.log"
+    if (gpg --quick-random --version >/dev/null 2>&1) ; then
+       echo quick-random >> "$GNUPGHOME"/gpg.conf
+    elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
+       echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
+    fi
+    echo no-emit-version >> "$GNUPGHOME"/gpg.conf
+}
+
+##################################################
+
+add_gnupg_home
+# get key fingerprint
+FINGERPRINT=$(gpg --no-tty --list-secret-keys --with-colons --fingerprint | grep '^fpr:' | cut -d: -f10)
+
+test_expect_success 'emacs delivery of signed message' \
+'emacs_fcc_message \
+    "test signed message 001" \
+    "This is a test signed message." \
+    "(mml-secure-message-sign)"'
+
+test_begin_subtest "signature verification"
+output=$(notmuch show --format=json --verify subject:"test signed message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["inbox","signed"],
+ "headers": {"Subject": "test signed message 001",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "sigstatus": [{"status": "good",
+ "fingerprint": "'$FINGERPRINT'",
+ "created": 946728000}],
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "text/plain",
+ "content": "This is a test signed message.\n"},
+ {"id": 3,
+ "content-type": "application/pgp-signature",
+ "content-length": 280}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "signature verification with full owner trust"
+# give the key full owner trust
+echo "${FINGERPRINT}:6:" | gpg --no-tty --import-ownertrust >>"$GNUPGHOME"/trust.log 2>&1
+gpg --no-tty --check-trustdb >>"$GNUPGHOME"/trust.log 2>&1
+output=$(notmuch show --format=json --verify subject:"test signed message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["inbox","signed"],
+ "headers": {"Subject": "test signed message 001",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "sigstatus": [{"status": "good",
+ "fingerprint": "'$FINGERPRINT'",
+ "created": 946728000,
+ "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "text/plain",
+ "content": "This is a test signed message.\n"},
+ {"id": 3,
+ "content-type": "application/pgp-signature",
+ "content-length": 280}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "signature verification with signer key unavailable"
+# move the gnupghome temporarily out of the way
+mv "${GNUPGHOME}"{,.bak}
+output=$(notmuch show --format=json --verify subject:"test signed message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["inbox","signed"],
+ "headers": {"Subject": "test signed message 001",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "sigstatus": [{"status": "error",
+ "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",
+ "errors": 2}],
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "text/plain",
+ "content": "This is a test signed message.\n"},
+ {"id": 3,
+ "content-type": "application/pgp-signature",
+ "content-length": 280}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+mv "${GNUPGHOME}"{.bak,}
+
+# create a test encrypted message with attachment
+cat <<EOF >TESTATTACHMENT
+This is a test file.
+EOF
+test_expect_success 'emacs delivery of encrypted message with attachment' \
+'emacs_fcc_message \
+    "test encrypted message 001" \
+    "This is a test encrypted message.\n" \
+    "(mml-attach-file \"TESTATTACHMENT\") (mml-secure-message-encrypt)"'
+
+test_begin_subtest "decryption, --format=text"
+output=$(notmuch show --format=text --decrypt subject:"test encrypted message 001" \
+    | notmuch_show_sanitize_all \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2000-01-01) (encrypted inbox)
+Subject: test encrypted message 001
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: test_suite@notmuchmail.org
+Date: Sat, 01 Jan 2000 12:00:00 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: multipart/encrypted
+\fpart{ ID: 2, Content-type: application/pgp-encrypted
+Non-text part: application/pgp-encrypted
+\fpart}
+\fpart{ ID: 3, Content-type: multipart/mixed
+\fpart{ ID: 4, Content-type: text/plain
+This is a test encrypted message.
+\fpart}
+\fattachment{ ID: 5, Filename: TESTATTACHMENT, Content-type: application/octet-stream
+Non-text part: application/octet-stream
+\fattachment}
+\fpart}
+\fpart}
+\fbody}
+\fmessage}'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "decryption, --format=json"
+output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["encrypted","inbox"],
+ "headers": {"Subject": "test encrypted message 001",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "encstatus": [{"status": "good"}],
+ "sigstatus": [],
+ "content-type": "multipart/encrypted",
+ "content": [{"id": 2,
+ "content-type": "application/pgp-encrypted",
+ "content-length": 11},
+ {"id": 3,
+ "content-type": "multipart/mixed",
+ "content": [{"id": 4,
+ "content-type": "text/plain",
+ "content": "This is a test encrypted message.\n"},
+ {"id": 5,
+ "content-type": "application/octet-stream",
+ "content-length": 28,
+ "content-transfer-encoding": "base64",
+ "filename": "TESTATTACHMENT"}]}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "decryption, --format=json, --part=4"
+output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='{"id": 4,
+ "content-type": "text/plain",
+ "content": "This is a test encrypted message.\n"}'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "decrypt attachment (--part=5 --format=raw)"
+notmuch show \
+    --format=raw \
+    --part=5 \
+    --decrypt \
+    subject:"test encrypted message 001" >OUTPUT
+test_expect_equal_file OUTPUT TESTATTACHMENT
+
+test_begin_subtest "decryption failure with missing key"
+mv "${GNUPGHOME}"{,.bak}
+# The length of the encrypted attachment varies so must be normalized.
+output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|' \
+    | sed -e 's|"content-length": 6[1234567890]*|"content-length": 652|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["encrypted","inbox"],
+ "headers": {"Subject": "test encrypted message 001",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "encstatus": [{"status": "bad"}],
+ "content-type": "multipart/encrypted",
+ "content": [{"id": 2,
+ "content-type": "application/pgp-encrypted",
+ "content-length": 11},
+ {"id": 3,
+ "content-type": "application/octet-stream",
+ "content-length": 652}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+mv "${GNUPGHOME}"{.bak,}
+
+test_expect_success 'emacs delivery of encrypted + signed message' \
+'emacs_fcc_message \
+    "test encrypted message 002" \
+    "This is another test encrypted message.\n" \
+    "(mml-secure-message-sign-encrypt)"'
+
+test_begin_subtest "decryption + signature verification"
+output=$(notmuch show --format=json --decrypt subject:"test encrypted message 002" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["encrypted","inbox"],
+ "headers": {"Subject": "test encrypted message 002",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "encstatus": [{"status": "good"}],
+ "sigstatus": [{"status": "good",
+ "fingerprint": "'$FINGERPRINT'",
+ "created": 946728000,
+ "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],
+ "content-type": "multipart/encrypted",
+ "content": [{"id": 2,
+ "content-type": "application/pgp-encrypted",
+ "content-length": 11},
+ {"id": 3,
+ "content-type": "text/plain",
+ "content": "This is another test encrypted message.\n"}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "reply to encrypted message"
+output=$(notmuch reply --decrypt subject:"test encrypted message 002" \
+    | grep -v -e '^In-Reply-To:' -e '^References:')
+expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: test encrypted message 002
+
+On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> This is another test encrypted message.'
+test_expect_equal \
+    "$output" \
+    "$expected"
+
+test_begin_subtest "signature verification with revoked key"
+# generate revocation certificate and load it to revoke key
+echo "y
+1
+Notmuch Test Suite key revocation (automated) $(date '+%F_%T%z')
+
+y
+
+" \
+    | gpg --no-tty --quiet --command-fd 0 --armor --gen-revoke "0x${FINGERPRINT}!" 2>/dev/null \
+    | gpg --no-tty --quiet --import
+output=$(notmuch show --format=json --verify subject:"test signed message 001" \
+    | notmuch_json_show_sanitize \
+    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
+expected='[[[{"id": "XXXXX",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 946728000,
+ "date_relative": "2000-01-01",
+ "tags": ["inbox","signed"],
+ "headers": {"Subject": "test signed message 001",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "test_suite@notmuchmail.org",
+ "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
+ "body": [{"id": 1,
+ "sigstatus": [{"status": "error",
+ "keyid": "6D92612D94E46381",
+ "errors": 8}],
+ "content-type": "multipart/signed",
+ "content": [{"id": 2,
+ "content-type": "text/plain",
+ "content": "This is a test signed message.\n"},
+ {"id": 3,
+ "content-type": "application/pgp-signature",
+ "content-length": 280}]}]},
+ []]]]'
+test_expect_equal_json \
+    "$output" \
+    "$expected"
+
+test_done
diff --git a/test/T360-symbol-hiding.sh b/test/T360-symbol-hiding.sh
new file mode 100755 (executable)
index 0000000..636ec91
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2011 David Bremner
+#
+
+# This test tests whether hiding Xapian::Error symbols in libnotmuch
+# also hides them for other users of libxapian. This is motivated by
+# the discussion in http://gcc.gnu.org/wiki/Visibility'
+
+test_description='exception symbol hiding'
+
+. ./test-lib.sh
+
+run_test(){
+    result=$(LD_LIBRARY_PATH="$TEST_DIRECTORY/../lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" $TEST_DIRECTORY/symbol-test 2>&1)
+}
+
+output="A Xapian exception occurred opening database: Couldn't stat 'fakedb/.notmuch/xapian'
+caught No chert database found at path \`./nonexistant'"
+
+mkdir -p fakedb/.notmuch
+
+test_expect_success 'running test' run_test
+
+test_begin_subtest 'checking output'
+test_expect_equal "$result" "$output"
+
+test_begin_subtest 'comparing existing to exported symbols'
+objdump -t $TEST_DIRECTORY/../lib/*.o | awk '$4 == ".text" && $6 ~ "^notmuch" {print $6}' | sort | uniq > ACTUAL
+sed -n 's/[[:blank:]]*\(notmuch_[^;]*\);/\1/p' $TEST_DIRECTORY/../notmuch.sym | sort | uniq > EXPORTED
+test_expect_equal_file EXPORTED ACTUAL
+
+test_done
diff --git a/test/T370-search-folder-coherence.sh b/test/T370-search-folder-coherence.sh
new file mode 100755 (executable)
index 0000000..3f6ec76
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+test_description='folder tags removed and added through file renames remain consistent'
+. ./test-lib.sh
+
+test_begin_subtest "No new messages"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+
+test_begin_subtest "Single new message"
+generate_message
+file_x=$gen_msg_filename
+id_x=$gen_msg_id
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Add second folder for same message"
+dir=$(dirname $file_x)
+mkdir $dir/spam
+cp $file_x $dir/spam
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+
+test_begin_subtest "Multiple files for same message"
+cat <<EOF >EXPECTED
+MAIL_DIR/msg-001
+MAIL_DIR/spam/msg-001
+EOF
+notmuch search --output=files id:$id_x | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Test matches folder:spam"
+output=$(notmuch search folder:spam)
+test_expect_equal "$output" "thread:0000000000000001   2001-01-05 [1/1] Notmuch Test Suite; Single new message (inbox unread)"
+
+test_begin_subtest "Remove folder:spam copy of email"
+rm $dir/spam/$(basename $file_x)
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected 1 file rename."
+
+test_begin_subtest "No mails match the folder:spam search"
+output=$(notmuch search folder:spam)
+test_expect_equal "$output" ""
+
+test_done
diff --git a/test/T380-atomicity.sh b/test/T380-atomicity.sh
new file mode 100755 (executable)
index 0000000..1c786fa
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+test_description='atomicity'
+. ./test-lib.sh
+
+# This script tests the effects of killing and restarting "notmuch
+# new" at arbitrary points.  If notmuch new is properly atomic, the
+# final database contents should be the same regardless of when (or
+# if) it is killed and restarted.
+
+if test_require_external_prereq gdb; then
+
+# Create a maildir structure to also stress flag synchronization
+    mkdir $MAIL_DIR/cur
+    mkdir $MAIL_DIR/new
+    mkdir $MAIL_DIR/tmp
+    mkdir $MAIL_DIR/.remove-dir
+
+    # Prepare the initial database
+    generate_message [subject]='Duplicate' [filename]='duplicate:2,' [dir]=cur
+    generate_message [subject]='Remove' [filename]='remove:2,' [dir]=cur
+    generate_message [subject]='"Remove duplicate"' [filename]='remove-duplicate:2,' [dir]=cur
+    cp $MAIL_DIR/cur/remove-duplicate:2, $MAIL_DIR/cur/remove-duplicate-copy:2,
+    generate_message [subject]='Rename' [filename]='rename:2,' [dir]=cur
+    generate_message [subject]='"Rename duplicate"' [filename]='rename-duplicate:2,' [dir]=cur
+    generate_message [subject]='"Move 1"' [filename]='move1:2,' [dir]=cur
+    generate_message [subject]='"Move 2"' [filename]='move2:2,' [dir]=new
+    generate_message [subject]='Flag' [filename]='flag:2,' [dir]=cur
+    generate_message [subject]='"Flag duplicate"' [filename]='flag-duplicate:2,' [dir]=cur
+    cp $MAIL_DIR/cur/flag-duplicate:2, $MAIL_DIR/cur/flag-duplicate-copy:2,F
+    generate_message [subject]='"Remove directory"' [filename]='remove-directory:2,' [dir]=.remove-dir
+    generate_message [subject]='"Remove directory duplicate"' [filename]='remove-directory-duplicate:2,' [dir]=.remove-dir
+    cp $MAIL_DIR/.remove-dir/remove-directory-duplicate:2, $MAIL_DIR/cur/
+    notmuch new > /dev/null
+
+    # Make all maildir changes, but *don't* update the database
+    generate_message [subject]='Added' [filename]='added:2,' [dir]=cur
+    cp $MAIL_DIR/cur/duplicate:2, $MAIL_DIR/cur/duplicate-copy:2,
+    generate_message [subject]='"Add duplicate"' [filename]='add-duplicate:2,' [dir]=cur
+    generate_message [subject]='"Add duplicate copy"' [filename]='add-duplicate-copy:2,' [dir]=cur
+    rm $MAIL_DIR/cur/remove:2,
+    rm $MAIL_DIR/cur/remove-duplicate-copy:2,
+    mv $MAIL_DIR/cur/rename:2, $MAIL_DIR/cur/renamed:2,
+    mv $MAIL_DIR/cur/rename-duplicate:2, $MAIL_DIR/cur/renamed-duplicate:2,
+    mv $MAIL_DIR/cur/move1:2, $MAIL_DIR/new/move1:2,
+    mv $MAIL_DIR/new/move2:2, $MAIL_DIR/cur/move2:2,
+    mv $MAIL_DIR/cur/flag:2, $MAIL_DIR/cur/flag:2,F
+    rm $MAIL_DIR/cur/flag-duplicate-copy:2,F
+    rm $MAIL_DIR/.remove-dir/remove-directory:2,
+    rm $MAIL_DIR/.remove-dir/remove-directory-duplicate:2,
+    rmdir $MAIL_DIR/.remove-dir
+
+    # Prepare a snapshot of the updated maildir.  The gdb script will
+    # update the database in this snapshot as it goes.
+    cp -a $MAIL_DIR $MAIL_DIR.snap
+    cp ${NOTMUCH_CONFIG} ${NOTMUCH_CONFIG}.snap
+    NOTMUCH_CONFIG=${NOTMUCH_CONFIG}.snap notmuch config set database.path $MAIL_DIR.snap
+
+
+
+    # Execute notmuch new and, at every call to rename, snapshot the
+    # database, run notmuch new again on the snapshot, and capture the
+    # results of search.
+    #
+    # -tty /dev/null works around a conflict between the 'timeout' wrapper
+    # and gdb's attempt to control the TTY.
+    export MAIL_DIR
+    gdb -tty /dev/null -batch -x $TEST_DIRECTORY/atomicity.gdb notmuch >/dev/null 2>/dev/null
+
+    # Get the final, golden output
+    notmuch search '*' > expected
+
+    # Check output against golden output
+    outcount=$(cat outcount)
+    echo -n > searchall
+    echo -n > expectall
+    for ((i = 0; i < $outcount; i++)); do
+       if ! cmp -s search.$i expected; then
+           # Find the range of interruptions that match this output
+           for ((end = $i + 1 ; end < $outcount; end++)); do
+               if ! cmp -s search.$i search.$end; then
+                   break
+               fi
+           done
+           echo "When interrupted after $test/backtrace.$(expr $i - 1) (abort points $i-$(expr $end - 1))" >> searchall
+           cat search.$i >> searchall
+           cat expected >> expectall
+           echo >> searchall
+           echo >> expectall
+
+           i=$(expr $end - 1)
+       fi
+    done
+fi
+
+test_begin_subtest '"notmuch new" is idempotent under arbitrary aborts'
+test_expect_equal_file searchall expectall
+
+test_expect_success "detected $outcount>10 abort points" "test $outcount -gt 10"
+
+test_done
diff --git a/test/T390-python.sh b/test/T390-python.sh
new file mode 100755 (executable)
index 0000000..3f03a2e
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+test_description="python bindings"
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "compare thread ids"
+test_python <<EOF
+import notmuch
+db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY)
+q_new = notmuch.Query(db, 'tag:inbox')
+q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
+for t in q_new.search_threads():
+    print t.get_thread_id()
+EOF
+notmuch search --sort=oldest-first --output=threads tag:inbox | sed s/^thread:// > EXPECTED
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "compare message ids"
+test_python <<EOF
+import notmuch
+db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY)
+q_new = notmuch.Query(db, 'tag:inbox')
+q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
+for m in q_new.search_messages():
+    print m.get_message_id()
+EOF
+notmuch search --sort=oldest-first --output=messages tag:inbox | sed s/^id:// > EXPECTED
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "get non-existent file"
+test_python <<EOF
+import notmuch
+db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY)
+print db.find_message_by_filename("i-dont-exist")
+EOF
+test_expect_equal "$(cat OUTPUT)" "None"
+
+test_done
diff --git a/test/T400-hooks.sh b/test/T400-hooks.sh
new file mode 100755 (executable)
index 0000000..77e8569
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/env bash
+test_description='hooks'
+. ./test-lib.sh
+
+HOOK_DIR=${MAIL_DIR}/.notmuch/hooks
+
+create_echo_hook () {
+    local TOKEN="${RANDOM}"
+    mkdir -p ${HOOK_DIR}
+    cat <<EOF >"${HOOK_DIR}/${1}"
+#!/bin/sh
+echo "${TOKEN}" > ${3}
+EOF
+    chmod +x "${HOOK_DIR}/${1}"
+    echo "${TOKEN}" > ${2}
+}
+
+create_failing_hook () {
+    mkdir -p ${HOOK_DIR}
+    cat <<EOF >"${HOOK_DIR}/${1}"
+#!/bin/sh
+exit 13
+EOF
+    chmod +x "${HOOK_DIR}/${1}"
+}
+
+rm_hooks () {
+    rm -rf ${HOOK_DIR}
+}
+
+# add a message to generate mail dir and database
+add_message
+
+test_begin_subtest "pre-new is run"
+rm_hooks
+generate_message
+create_echo_hook "pre-new" expected output
+notmuch new > /dev/null
+test_expect_equal_file expected output
+
+test_begin_subtest "post-new is run"
+rm_hooks
+generate_message
+create_echo_hook "post-new" expected output
+notmuch new > /dev/null
+test_expect_equal_file expected output
+
+test_begin_subtest "pre-new is run before post-new"
+rm_hooks
+generate_message
+create_echo_hook "pre-new" pre-new.expected pre-new.output
+create_echo_hook "post-new" post-new.expected post-new.output
+notmuch new > /dev/null
+test_expect_equal_file post-new.expected post-new.output
+
+test_begin_subtest "pre-new non-zero exit status (hook status)"
+rm_hooks
+generate_message
+create_failing_hook "pre-new"
+output=`notmuch new 2>&1`
+test_expect_equal "$output" "Error: pre-new hook failed with status 13"
+
+# depends on the previous subtest leaving broken hook behind
+test_expect_code 1 "pre-new non-zero exit status (notmuch status)" "notmuch new"
+
+# depends on the previous subtests leaving 1 new message behind
+test_begin_subtest "pre-new non-zero exit status aborts new"
+rm_hooks
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "post-new non-zero exit status (hook status)"
+rm_hooks
+generate_message
+create_failing_hook "post-new"
+NOTMUCH_NEW 2>output.stderr >output
+cat output.stderr >> output
+echo "Added 1 new message to the database." > expected
+echo "Error: post-new hook failed with status 13" >> expected
+test_expect_equal_file expected output
+
+# depends on the previous subtest leaving broken hook behind
+test_expect_code 1 "post-new non-zero exit status (notmuch status)" "notmuch new"
+
+# test_begin_subtest "hook without executable permissions"
+rm_hooks
+mkdir -p ${HOOK_DIR}
+cat <<EOF >"${HOOK_DIR}/pre-new"
+#!/bin/sh
+echo foo
+EOF
+output=`notmuch new 2>&1`
+test_expect_code 1 "hook without executable permissions" "notmuch new"
+
+# test_begin_subtest "hook execution failure"
+rm_hooks
+mkdir -p ${HOOK_DIR}
+cat <<EOF >"${HOOK_DIR}/pre-new"
+no hashbang, execl fails
+EOF
+chmod +x "${HOOK_DIR}/pre-new"
+test_expect_code 1 "hook execution failure" "notmuch new"
+
+test_done
diff --git a/test/T410-argument-parsing.sh b/test/T410-argument-parsing.sh
new file mode 100755 (executable)
index 0000000..94e9087
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+test_description="argument parsing"
+. ./test-lib.sh
+
+test_begin_subtest "sanity check"
+$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 > OUTPUT
+cat <<EOF > EXPECTED
+keyword 1
+int 7
+string foo
+positional arg 1 pos1
+positional arg 2 pos2
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_done
diff --git a/test/T420-emacs-test-functions.sh b/test/T420-emacs-test-functions.sh
new file mode 100755 (executable)
index 0000000..ca4a798
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+test_description="emacs test function sanity"
+. ./test-lib.sh
+
+test_begin_subtest "emacs test function sanity"
+test_emacs_expect_t 't'
+
+test_done
diff --git a/test/T430-emacs-address-cleaning.sh b/test/T430-emacs-address-cleaning.sh
new file mode 100755 (executable)
index 0000000..0472346
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+test_description="emacs address cleaning"
+. ./test-lib.sh
+
+test_begin_subtest "notmuch-test-address-clean part 1"
+test_emacs_expect_t '(notmuch-test-address-cleaning-1)'
+
+test_begin_subtest "notmuch-test-address-clean part 2"
+test_emacs_expect_t '(notmuch-test-address-cleaning-2)'
+
+test_begin_subtest "notmuch-test-address-clean part 3"
+test_emacs_expect_t '(notmuch-test-address-cleaning-3)'
+
+test_done
diff --git a/test/T440-emacs-hello.sh b/test/T440-emacs-hello.sh
new file mode 100755 (executable)
index 0000000..f729616
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env bash
+
+test_description="emacs notmuch-hello view"
+. ./test-lib.sh
+
+EXPECTED=$TEST_DIRECTORY/emacs.expected-output
+
+add_email_corpus
+
+test_begin_subtest "User-defined section with inbox tag"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test\" '((\"inbox\" . \"tag:inbox\")))))))
+           (notmuch-hello)
+           (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-new-section
+
+test_begin_subtest "User-defined section with empty, hidden entry"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-searches
+                                     \"Test-with-empty\"
+                                     '((\"inbox\" . \"tag:inbox\")
+                                       (\"doesnotexist\" . \"tag:doesnotexist\"))
+                                     :hide-empty-searches t)))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-with-empty
+
+test_begin_subtest "User-defined section, unread tag filtered out"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-filtered\"
+                                     :hide-tags '(\"unread\"))))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-hidden-tag
+
+test_begin_subtest "User-defined section, different query for counts"
+test_emacs "(let ((notmuch-hello-sections
+                   (list (lambda () (notmuch-hello-insert-tags-section
+                                     \"Test-with-counts\"
+                                     :filter-count \"tag:signed\")))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-section-counts
+
+test_begin_subtest "Empty custom tags section"
+test_emacs "(let* ((widget (widget-create 'notmuch-hello-tags-section))
+                   (notmuch-hello-sections (list (widget-value widget))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-empty-custom-tags-section
+
+test_begin_subtest "Empty custom queries section"
+test_emacs "(let* ((widget (widget-create 'notmuch-hello-query-section))
+                   (notmuch-hello-sections (list (widget-value widget))))
+             (notmuch-hello)
+             (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-empty-custom-queries-section
+
+test_begin_subtest "Column alignment for tag/queries with long names"
+tag=a-very-long-tag # length carefully calculated for 80 characters window width
+notmuch tag +$tag '*'
+test_emacs '(notmuch-hello)
+            (test-output)'
+notmuch tag -$tag '*'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-long-names
+
+test_done
diff --git a/test/T450-emacs-show.sh b/test/T450-emacs-show.sh
new file mode 100755 (executable)
index 0000000..2a3a535
--- /dev/null
@@ -0,0 +1,201 @@
+#!/usr/bin/env bash
+
+test_description="emacs notmuch-show view"
+. ./test-lib.sh
+
+EXPECTED=$TEST_DIRECTORY/emacs-show.expected-output
+
+add_email_corpus
+
+test_begin_subtest "Hiding Original Message region at beginning of a message"
+message_id='OriginalMessageHiding.1@notmuchmail.org'
+add_message \
+    [id]="$message_id" \
+    '[subject]="Hiding Original Message region at beginning of a message"' \
+    '[body]="-----Original Message-----
+Text here."'
+
+cat <<EOF >EXPECTED
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (inbox)
+Subject: Hiding Original Message region at beginning of a message
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: GENERATED_DATE
+
+[ 2-line hidden original message. Click/Enter to show. ]
+EOF
+
+test_emacs "(notmuch-show \"id:$message_id\")
+           (test-visible-output \"OUTPUT.raw\")"
+notmuch_date_sanitize < OUTPUT.raw > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "Bare subject #1"
+output=$(test_emacs '(notmuch-show-strip-re "Re: subject")')
+test_expect_equal "$output" '"subject"'
+
+test_begin_subtest "Bare subject #2"
+output=$(test_emacs '(notmuch-show-strip-re "re:Re: re:  Re:  re:subject")')
+test_expect_equal "$output" '"subject"'
+
+test_begin_subtest "Bare subject #3"
+output=$(test_emacs '(notmuch-show-strip-re "the cure: fix the regexp")')
+test_expect_equal "$output" '"the cure: fix the regexp"'
+
+test_begin_subtest "don't process cryptographic MIME parts"
+test_emacs '(let ((notmuch-crypto-process-mime nil))
+       (notmuch-show "id:20091117203301.GV3165@dottiness.seas.harvard.edu")
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-process-crypto-mime-parts-off
+
+test_begin_subtest "process cryptographic MIME parts"
+test_emacs '(let ((notmuch-crypto-process-mime t))
+       (notmuch-show "id:20091117203301.GV3165@dottiness.seas.harvard.edu")
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-process-crypto-mime-parts-on
+
+test_begin_subtest "process cryptographic MIME parts (w/ notmuch-show-toggle-process-crypto)"
+test_emacs '(let ((notmuch-crypto-process-mime nil))
+       (notmuch-show "id:20091117203301.GV3165@dottiness.seas.harvard.edu")
+       (notmuch-show-toggle-process-crypto)
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-process-crypto-mime-parts-on
+
+test_begin_subtest "notmuch-show: don't elide non-matching messages"
+test_emacs '(let ((notmuch-show-only-matching-messages nil))
+       (notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
+       (notmuch-test-wait)
+       (notmuch-search-show-thread)
+       (notmuch-test-wait)
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-elide-non-matching-messages-off
+
+test_begin_subtest "notmuch-show: elide non-matching messages"
+test_emacs '(let ((notmuch-show-only-matching-messages t))
+       (notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
+       (notmuch-test-wait)
+       (notmuch-search-show-thread)
+       (notmuch-test-wait)
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-elide-non-matching-messages-on
+
+test_begin_subtest "notmuch-show: elide non-matching messages (w/ notmuch-show-toggle-elide-non-matching)"
+test_emacs '(let ((notmuch-show-only-matching-messages nil))
+       (notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
+       (notmuch-test-wait)
+       (notmuch-search-show-thread)
+       (notmuch-test-wait)
+       (notmuch-show-toggle-elide-non-matching)
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-elide-non-matching-messages-on
+
+test_begin_subtest "notmuch-show: elide non-matching messages (w/ prefix arg to notmuch-show)"
+test_emacs '(let ((notmuch-show-only-matching-messages nil))
+       (notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
+       (notmuch-test-wait)
+       (notmuch-search-show-thread t)
+       (notmuch-test-wait)
+       (test-visible-output))'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-elide-non-matching-messages-on
+
+test_begin_subtest "notmuch-show: disable indentation of thread content (w/ notmuch-show-toggle-thread-indentation)"
+test_emacs '(notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
+       (notmuch-test-wait)
+       (notmuch-search-show-thread)
+       (notmuch-test-wait)
+       (notmuch-show-toggle-thread-indentation)
+       (test-visible-output)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-indent-thread-content-off
+
+test_begin_subtest "id buttonization"
+add_message '[body]="
+id:abc
+id:abc.def. id:abc,def, id:abc;def; id:abc:def:
+id:foo@bar.?baz? id:foo@bar!.baz!
+(id:foo@bar.baz) [id:foo@bar.baz]
+id:foo@bar.baz...
+id:2+2=5
+id:=_-:/.[]@$%+
+id:abc)def
+id:ab\"c def
+id:\"abc\"def
+id:\"ab\"\"c\"def
+id:\"ab c\"def
+id:\"abc\".def
+id:\"abc
+\"
+id:)
+id:
+cid:xxx
+mid:abc mid:abc/def
+mid:abc%20def
+mid:abc. mid:abc, mid:abc;"'
+test_emacs '(notmuch-show "id:'$gen_msg_id'")
+       (notmuch-test-mark-links)
+       (test-visible-output "OUTPUT.raw")'
+cat <<EOF >EXPECTED
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (inbox)
+Subject: id buttonization
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: GENERATED_DATE
+
+<<id:abc>>
+<<id:abc.def>>. <<id:abc,def>>, <<id:abc;def>>; <<id:abc:def>>:
+<<id:foo@bar.?baz>>? <<id:foo@bar!.baz>>!
+(<<id:foo@bar.baz>>) [<<id:foo@bar.baz>>]
+<<id:foo@bar.baz>>...
+<<id:2+2=5>>
+<<id:=_-:/.[]@$%+>>
+<<id:abc>>)def
+<<id:ab"c>> def
+<<id:"abc">>def
+<<id:"ab""c">>def
+<<id:"ab c">>def
+<<id:"abc">>.def
+id:"abc
+"
+id:)
+id:
+cid:xxx
+<<mid:abc>> <<mid:abc/def>>
+<<mid:abc%20def>>
+<<mid:abc>>. <<mid:abc>>, <<mid:abc>>;
+EOF
+notmuch_date_sanitize < OUTPUT.raw > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+
+test_begin_subtest "Show handles subprocess errors"
+cat > notmuch_fail <<EOF
+#!/bin/sh
+echo This is output
+echo This is an error >&2
+exit 1
+EOF
+chmod a+x notmuch_fail
+test_emacs "(let ((notmuch-command \"$PWD/notmuch_fail\"))
+              (with-current-buffer \"*Messages*\" (erase-buffer))
+              (condition-case err
+                  (notmuch-show \"*\")
+                (error (message \"%s\" (second err))))
+              (notmuch-test-wait)
+              (with-current-buffer \"*Messages*\"
+                 (test-output \"MESSAGES\"))
+              (with-current-buffer \"*Notmuch errors*\"
+                 (test-output \"ERROR\"))
+              (test-output))"
+test_expect_equal "$(notmuch_emacs_error_sanitize notmuch_fail OUTPUT MESSAGES ERROR)" "\
+=== OUTPUT ===
+=== MESSAGES ===
+This is an error (see *Notmuch errors* for more details)
+=== ERROR ===
+[XXX]
+This is an error
+command: YYY/notmuch_fail show --format\\=sexp --format-version\\=1 --exclude\\=false \\' \\* \\'
+exit status: 1
+stderr:
+This is an error
+stdout:
+This is output"
+
+
+test_done
diff --git a/test/T460-emacs-tree.sh b/test/T460-emacs-tree.sh
new file mode 100755 (executable)
index 0000000..8e9f37c
--- /dev/null
@@ -0,0 +1,181 @@
+#!/usr/bin/env bash
+
+test_description="emacs tree view interface"
+. test-lib.sh
+
+EXPECTED=$TEST_DIRECTORY/tree.expected-output
+
+add_email_corpus
+
+test_begin_subtest "Basic notmuch-tree view in emacs"
+test_emacs '(notmuch-tree "tag:inbox")
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox
+
+test_begin_subtest "Refreshed notmuch-tree view in emacs"
+test_emacs '(notmuch-tree "tag:inbox")
+           (notmuch-test-wait)
+           (notmuch-tree-refresh-view)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox
+
+# In the following tag tests we make sure the display is updated
+# correctly and, in a separate test, that the database is updated
+# correctly.
+
+test_begin_subtest "Tag message in notmuch tree view (display)"
+test_emacs '(notmuch-tree "tag:inbox")
+           (notmuch-test-wait)
+           (forward-line)
+           (notmuch-tree-tag (list "+test_tag"))
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox-tagged
+
+test_begin_subtest "Tag message in notmuch tree view (database)"
+output=$(notmuch search --output=messages 'tag:test_tag')
+test_expect_equal "$output" "id:877h1wv7mg.fsf@inf-8657.int-evry.fr"
+
+test_begin_subtest "Untag message in notmuch tree view"
+test_emacs '(notmuch-tree "tag:inbox")
+           (notmuch-test-wait)
+           (forward-line)
+           (notmuch-tree-tag (list "-test_tag"))
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox
+
+test_begin_subtest "Untag message in notmuch tree view (database)"
+output=$(notmuch search --output=messages 'tag:test_tag')
+test_expect_equal "$output" ""
+
+test_begin_subtest "Tag thread in notmuch tree view"
+test_emacs '(notmuch-tree "tag:inbox")
+           (notmuch-test-wait)
+           ;; move to a sizable thread
+           (forward-line 26)
+           (notmuch-tree-tag-thread (list "+test_thread_tag"))
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox-thread-tagged
+
+test_begin_subtest "Tag message in notmuch tree view (database)"
+output=$(notmuch search --output=messages 'tag:test_thread_tag')
+test_expect_equal "$output" \
+"id:87ocn0qh6d.fsf@yoom.home.cworth.org
+id:20091118005040.GA25380@dottiness.seas.harvard.edu
+id:yunaayketfm.fsf@aiko.keithp.com
+id:87fx8can9z.fsf@vertex.dottedmag
+id:20091117203301.GV3165@dottiness.seas.harvard.edu
+id:87iqd9rn3l.fsf@vertex.dottedmag
+id:20091117190054.GU3165@dottiness.seas.harvard.edu"
+
+test_begin_subtest "Untag thread in notmuch tree view"
+test_emacs '(notmuch-tree "tag:inbox")
+           (notmuch-test-wait)
+           ;; move to the same sizable thread as above
+           (forward-line 26)
+           (notmuch-tree-tag-thread (list "-test_thread_tag"))
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox
+
+test_begin_subtest "Untag message in notmuch tree view (database)"
+output=$(notmuch search --output=messages 'tag:test_thread_tag')
+test_expect_equal "$output" ""
+
+test_begin_subtest "Navigation of notmuch-hello to search results"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-tag-inbox
+
+test_begin_subtest "Tree view of a single thread (from search)"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-tree-from-search-thread)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-single-thread
+
+test_begin_subtest "Tree view of a single thread (from show)"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-search-show-thread)
+           (notmuch-tree-from-show-current-query)
+           (notmuch-test-wait)
+           (test-output)
+           (delete-other-windows)'
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-single-thread
+
+test_begin_subtest "Message window of tree view"
+test_emacs '(notmuch-hello)
+           (goto-char (point-min))
+           (re-search-forward "inbox")
+           (widget-button-press (1- (point)))
+           (notmuch-test-wait)
+           (notmuch-search-next-thread)
+           (notmuch-tree-from-search-thread)
+           (notmuch-test-wait)
+           (select-window notmuch-tree-message-window)
+           (test-output)
+           (delete-other-windows)'
+cp OUTPUT /tmp/mjwout
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-tree-show-window
+
+test_begin_subtest "Stash id"
+output=$(test_emacs '(notmuch-tree "id:1258498485-sup-142@elly")
+                    (notmuch-test-wait)
+                    (notmuch-show-stash-message-id)')
+test_expect_equal "$output" "\"Stashed: id:1258498485-sup-142@elly\""
+
+test_begin_subtest "Move to next matching message"
+output=$(test_emacs '(notmuch-tree "from:cworth")
+                    (notmuch-test-wait)
+                    (notmuch-tree-next-matching-message)
+                    (notmuch-show-stash-message-id)')
+test_expect_equal "$output" "\"Stashed: id:878we4qdqf.fsf@yoom.home.cworth.org\""
+
+test_begin_subtest "Move to next thread"
+output=$(test_emacs '(notmuch-tree "tag:inbox")
+                    (notmuch-test-wait)
+                    (forward-line 26)
+                    (notmuch-tree-next-thread)
+                    (notmuch-show-stash-message-id)')
+test_expect_equal "$output" "\"Stashed: id:1258471718-6781-1-git-send-email-dottedmag@dottedmag.net\""
+
+test_begin_subtest "Move to previous thread"
+output=$(test_emacs '(notmuch-tree "tag:inbox")
+                    (notmuch-test-wait)
+                    (forward-line 26)
+                    (notmuch-tree-prev-thread)
+                    (notmuch-show-stash-message-id)')
+test_expect_equal "$output" "\"Stashed: id:20091117190054.GU3165@dottiness.seas.harvard.edu\""
+
+test_begin_subtest "Move to previous previous thread"
+output=$(test_emacs '(notmuch-tree "tag:inbox")
+                    (notmuch-test-wait)
+                    (forward-line 26)
+                    (notmuch-tree-prev-thread)
+                    (notmuch-tree-prev-thread)
+                    (notmuch-show-stash-message-id)')
+test_expect_equal "$output" "\"Stashed: id:1258493565-13508-1-git-send-email-keithp@keithp.com\""
+
+test_done
diff --git a/test/T470-missing-headers.sh b/test/T470-missing-headers.sh
new file mode 100755 (executable)
index 0000000..cb38301
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+test_description='messages with missing headers'
+. ./test-lib.sh
+
+# Notmuch requires at least one of from, subject, or to or it will
+# ignore the file.  Generate two messages so that together they cover
+# all possible missing headers.  We also give one of the messages a
+# date to ensure stable result ordering.
+
+cat <<EOF > "${MAIL_DIR}/msg-2"
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+
+Body
+EOF
+
+cat <<EOF > "${MAIL_DIR}/msg-1"
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+
+Body
+EOF
+
+NOTMUCH_NEW
+
+test_begin_subtest "Search: text"
+output=$(notmuch search '*' | notmuch_search_sanitize)
+test_expect_equal "$output" "\
+thread:XXX   2001-01-05 [1/1] (null);  (inbox unread)
+thread:XXX   1970-01-01 [1/1] Notmuch Test Suite;  (inbox unread)"
+
+test_begin_subtest "Search: json"
+output=$(notmuch search --format=json '*' | notmuch_search_sanitize)
+test_expect_equal_json "$output" '
+[
+    {
+        "authors": "",
+        "date_relative": "2001-01-05",
+        "matched": 1,
+        "subject": "",
+        "tags": [
+            "inbox",
+            "unread"
+        ],
+        "thread": "XXX",
+        "timestamp": 978709437,
+        "total": 1,
+        "query": ["id:notmuch-sha1-7a6e4eac383ef958fcd3ebf2143db71b8ff01161", null]
+    },
+    {
+        "authors": "Notmuch Test Suite",
+        "date_relative": "1970-01-01",
+        "matched": 1,
+        "subject": "",
+        "tags": [
+            "inbox",
+            "unread"
+        ],
+        "thread": "XXX",
+        "timestamp": 0,
+        "total": 1,
+        "query": ["id:notmuch-sha1-ca55943aff7a72baf2ab21fa74fab3d632401334", null]
+    }
+]'
+
+test_begin_subtest "Show: text"
+output=$(notmuch show '*' | notmuch_show_sanitize)
+test_expect_equal "$output" "\
+\fmessage{ id:notmuch-sha1-7a6e4eac383ef958fcd3ebf2143db71b8ff01161 depth:0 match:1 excluded:0 filename:/XXX/mail/msg-2
+\fheader{
+ (2001-01-05) (inbox unread)
+Subject: (null)
+From: (null)
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+Body
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:notmuch-sha1-ca55943aff7a72baf2ab21fa74fab3d632401334 depth:0 match:1 excluded:0 filename:/XXX/mail/msg-1
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (1970-01-01) (inbox unread)
+Subject: (null)
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Thu, 01 Jan 1970 00:00:00 +0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+Body
+\fpart}
+\fbody}
+\fmessage}"
+
+test_begin_subtest "Show: json"
+output=$(notmuch show --format=json '*' | notmuch_json_show_sanitize)
+expected=$(notmuch_json_show_sanitize <<EOF
+[
+    [
+        [
+            {
+                "body": [
+                    {
+                        "content": "Body\n",
+                        "content-type": "text/plain",
+                        "id": 1
+                    }
+                ],
+                "date_relative": "2001-01-05",
+                "excluded": false,
+                "filename": "YYYYY",
+                "headers": {
+                    "Date": "Fri, 05 Jan 2001 15:43:57 +0000",
+                    "From": "",
+                    "Subject": "",
+                    "To": "Notmuch Test Suite <test_suite@notmuchmail.org>"
+                },
+                "id": "XXXXX",
+                "match": true,
+                "tags": [
+                    "inbox",
+                    "unread"
+                ],
+                "timestamp": 978709437
+            },
+            []
+        ]
+    ],
+    [
+        [
+            {
+                "body": [
+                    {
+                        "content": "Body\n",
+                        "content-type": "text/plain",
+                        "id": 1
+                    }
+                ],
+                "date_relative": "1970-01-01",
+                "excluded": false,
+                "filename": "YYYYY",
+                "headers": {
+                    "Date": "Thu, 01 Jan 1970 00:00:00 +0000",
+                    "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+                    "Subject": ""
+                },
+                "id": "XXXXX",
+                "match": true,
+                "tags": [
+                    "inbox",
+                    "unread"
+                ],
+                "timestamp": 0
+            },
+            []
+        ]
+    ]
+]
+EOF
+)
+test_expect_equal_json "$output" "$expected"
+
+test_done
diff --git a/test/T480-hex-escaping.sh b/test/T480-hex-escaping.sh
new file mode 100755 (executable)
index 0000000..ad50e1b
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+test_description="hex encoding and decoding"
+. ./test-lib.sh
+
+test_begin_subtest "round trip"
+find $TEST_DIRECTORY/corpus -type f -print | sort | xargs cat > EXPECTED
+$TEST_DIRECTORY/hex-xcode --direction=encode < EXPECTED | $TEST_DIRECTORY/hex-xcode --direction=decode > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "punctuation"
+tag1='comic_swear=$&^%$^%\\//-+$^%$'
+tag_enc1=$($TEST_DIRECTORY/hex-xcode --direction=encode "$tag1")
+test_expect_equal "$tag_enc1" "comic_swear=%24%26%5e%25%24%5e%25%5c%5c%2f%2f-+%24%5e%25%24"
+
+test_begin_subtest "round trip newlines"
+printf 'this\n tag\t has\n spaces\n' > EXPECTED.$test_count
+$TEST_DIRECTORY/hex-xcode --direction=encode  < EXPECTED.$test_count |\
+       $TEST_DIRECTORY/hex-xcode --direction=decode > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest "round trip 8bit chars"
+echo '%c3%91%c3%a5%c3%b0%c3%a3%c3%a5%c3%a9-%c3%8f%c3%8a' > EXPECTED.$test_count
+$TEST_DIRECTORY/hex-xcode --direction=decode  < EXPECTED.$test_count |\
+    $TEST_DIRECTORY/hex-xcode --direction=encode > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest "round trip (in-place)"
+find $TEST_DIRECTORY/corpus -type f -print | sort | xargs cat > EXPECTED
+$TEST_DIRECTORY/hex-xcode --in-place --direction=encode < EXPECTED |\
+     $TEST_DIRECTORY/hex-xcode --in-place --direction=decode > OUTPUT
+test_expect_equal_file OUTPUT EXPECTED
+
+test_begin_subtest "punctuation (in-place)"
+tag1='comic_swear=$&^%$^%\\//-+$^%$'
+tag_enc1=$($TEST_DIRECTORY/hex-xcode --in-place --direction=encode "$tag1")
+test_expect_equal "$tag_enc1" "comic_swear=%24%26%5e%25%24%5e%25%5c%5c%2f%2f-+%24%5e%25%24"
+
+test_begin_subtest "round trip newlines (in-place)"
+printf 'this\n tag\t has\n spaces\n' > EXPECTED.$test_count
+$TEST_DIRECTORY/hex-xcode --in-place --direction=encode  < EXPECTED.$test_count |\
+    $TEST_DIRECTORY/hex-xcode --in-place --direction=decode > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_begin_subtest "round trip 8bit chars (in-place)"
+echo '%c3%91%c3%a5%c3%b0%c3%a3%c3%a5%c3%a9-%c3%8f%c3%8a' > EXPECTED.$test_count
+$TEST_DIRECTORY/hex-xcode --in-place --direction=decode  < EXPECTED.$test_count |\
+    $TEST_DIRECTORY/hex-xcode --in-place --direction=encode > OUTPUT.$test_count
+test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
+
+test_done
diff --git a/test/T490-parse-time-string.sh b/test/T490-parse-time-string.sh
new file mode 100755 (executable)
index 0000000..8ae0b4c
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+test_description="date/time parser module"
+. ./test-lib.sh
+
+# Sanity/smoke tests for the date/time parser independent of notmuch
+
+_date ()
+{
+    date -d "$*" +%s
+}
+
+_parse_time ()
+{
+    ${TEST_DIRECTORY}/parse-time --format=%s "$*"
+}
+
+test_begin_subtest "date(1) default format without TZ code"
+test_expect_equal "$(_parse_time Fri Aug 3 23:06:06 2012)" "$(_date Fri Aug 3 23:06:06 2012)"
+
+test_begin_subtest "date(1) --rfc-2822 format"
+test_expect_equal "$(_parse_time Fri, 03 Aug 2012 23:07:46 +0100)" "$(_date Fri, 03 Aug 2012 23:07:46 +0100)"
+
+test_begin_subtest "date(1) --rfc=3339=seconds format"
+test_expect_equal "$(_parse_time 2012-08-03 23:09:37+03:00)" "$(_date 2012-08-03 23:09:37+03:00)"
+
+test_begin_subtest "Date parser tests"
+REFERENCE=$(_date Tue Jan 11 11:11:00 +0000 2011)
+cat <<EOF > INPUT
+now          ==> Tue Jan 11 11:11:00 +0000 2011
+2010-1-1     ==> ERROR: DATEFORMAT
+Jan 2        ==> Sun Jan 02 11:11:00 +0000 2011
+Mon          ==> Mon Jan 10 11:11:00 +0000 2011
+last Friday  ==> ERROR: FORMAT
+2 hours ago  ==> Tue Jan 11 09:11:00 +0000 2011
+last month   ==> Sat Dec 11 11:11:00 +0000 2010
+month ago    ==> Sat Dec 11 11:11:00 +0000 2010
+two mo       ==> Thu Nov 11 11:11:00 +0000 2010
+3M           ==> Mon Oct 11 11:11:00 +0000 2010
+4-mont       ==> Sat Sep 11 11:11:00 +0000 2010
+5m           ==> Tue Jan 11 11:06:00 +0000 2011
+dozen mi     ==> Tue Jan 11 10:59:00 +0000 2011
+8am          ==> Tue Jan 11 08:00:00 +0000 2011
+9:15         ==> Tue Jan 11 09:15:00 +0000 2011
+12:34        ==> Tue Jan 11 12:34:00 +0000 2011
+monday       ==> Mon Jan 10 11:11:00 +0000 2011
+yesterday    ==> Mon Jan 10 11:11:00 +0000 2011
+tomorrow     ==> ERROR: KEYWORD
+             ==> Tue Jan 11 11:11:00 +0000 2011 # empty string is reference time
+
+Aug 3 23:06:06 2012             ==> Fri Aug 03 23:06:06 +0000 2012 # date(1) default format without TZ code
+Fri, 03 Aug 2012 23:07:46 +0100 ==> Fri Aug 03 22:07:46 +0000 2012 # rfc-2822
+2012-08-03 23:09:37+03:00       ==> Fri Aug 03 20:09:37 +0000 2012 # rfc-3339 seconds
+
+10s           ==> Tue Jan 11 11:10:50 +0000 2011
+19701223s     ==> Fri May 28 10:37:17 +0000 2010
+19701223      ==> Wed Dec 23 11:11:00 +0000 1970
+
+19701223 +0100 ==> Wed Dec 23 11:11:00 +0000 1970 # Timezone is ignored without an error
+
+today ==^^> Wed Jan 12 00:00:00 +0000 2011
+today ==^> Tue Jan 11 23:59:59 +0000 2011
+today ==_> Tue Jan 11 00:00:00 +0000 2011
+
+this week ==^^> Sun Jan 16 00:00:00 +0000 2011
+this week ==^> Sat Jan 15 23:59:59 +0000 2011
+this week ==_> Sun Jan 09 00:00:00 +0000 2011
+
+two months ago ==> Thu Nov 11 11:11:00 +0000 2010
+two months ==> Thu Nov 11 11:11:00 +0000 2010
+
+@1348569850 ==> Tue Sep 25 10:44:10 +0000 2012
+@10 ==> Thu Jan 01 00:00:10 +0000 1970
+EOF
+
+${TEST_DIRECTORY}/parse-time --ref=${REFERENCE} < INPUT > OUTPUT
+test_expect_equal_file INPUT OUTPUT
+
+test_done
diff --git a/test/T500-search-date.sh b/test/T500-search-date.sh
new file mode 100755 (executable)
index 0000000..70bcf34
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+test_description="date:since..until queries"
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "Absolute date range"
+output=$(notmuch search date:2010-12-16..12/16/2010 | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread)"
+
+test_begin_subtest "Absolute time range with TZ"
+notmuch search date:18-Nov-2009_02:19:26-0800..2009-11-18_04:49:52-06:00 | notmuch_search_sanitize > OUTPUT
+cat <<EOF >EXPECTED
+thread:XXX   2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
+thread:XXX   2009-11-18 [1/2] Carl Worth| Jan Janak; [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+thread:XXX   2009-11-18 [1/3] Carl Worth| Aron Griffis, Keith Packard; [notmuch] archive (inbox unread)
+thread:XXX   2009-11-18 [1/2] Carl Worth| Keith Packard; [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+EOF
+test_expect_equal_file OUTPUT EXPECTED
+
+test_done
diff --git a/test/T510-thread-replies.sh b/test/T510-thread-replies.sh
new file mode 100755 (executable)
index 0000000..eeb70d0
--- /dev/null
@@ -0,0 +1,141 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2013 Aaron Ecay
+#
+
+test_description='test of proper handling of in-reply-to and references headers'
+
+# This test makes sure that the thread structure in the notmuch
+# database is constructed properly, even in the presence of
+# non-RFC-compliant headers'
+
+. ./test-lib.sh
+
+test_begin_subtest "Use References when In-Reply-To is broken"
+add_message '[id]="foo@one.com"' \
+    '[subject]=one'
+add_message '[in-reply-to]="mumble"' \
+    '[references]="<foo@one.com>"' \
+    '[subject]="Re: one"'
+output=$(notmuch show --format=json 'subject:one' | notmuch_json_show_sanitize)
+expected='[[[{"id": "foo@one.com",
+ "match": true,
+ "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437,
+ "date_relative": "2001-01-05",
+ "tags": ["inbox", "unread"],
+ "headers": {"Subject": "one",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
+ "body": [{"id": 1,
+ "content-type": "text/plain",
+ "content": "This is just a test message (#1)\n"}]},
+ [[{"id": "msg-002@notmuch-test-suite",
+ "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05",
+ "tags": ["inbox", "unread"], "headers": {"Subject": "Re: one",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
+ "body": [{"id": 1, "content-type": "text/plain",
+ "content": "This is just a test message (#2)\n"}]}, []]]]]]'
+expected=`echo "$expected" | notmuch_json_show_sanitize`
+test_expect_equal_json "$output" "$expected"
+
+test_begin_subtest "Prefer References to In-Reply-To"
+add_message '[id]="foo@two.com"' \
+    '[subject]=two'
+add_message '[in-reply-to]="<bar@baz.com>"' \
+    '[references]="<foo@two.com>"' \
+    '[subject]="Re: two"'
+output=$(notmuch show --format=json 'subject:two' | notmuch_json_show_sanitize)
+expected='[[[{"id": "foo@two.com",
+ "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "two",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
+ "body": [{"id": 1, "content-type": "text/plain",
+ "content": "This is just a test message (#3)\n"}]},
+ [[{"id": "msg-004@notmuch-test-suite", "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "Re: two",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"},
+ "body": [{"id": 1,
+ "content-type": "text/plain", "content": "This is just a test message (#4)\n"}]},
+ []]]]]]'
+expected=`echo "$expected" | notmuch_json_show_sanitize`
+test_expect_equal_json "$output" "$expected"
+
+test_begin_subtest "Use In-Reply-To when no References"
+add_message '[id]="foo@three.com"' \
+    '[subject]="three"'
+add_message '[in-reply-to]="<foo@three.com>"' \
+    '[subject]="Re: three"'
+output=$(notmuch show --format=json 'subject:three' | notmuch_json_show_sanitize)
+expected='[[[{"id": "foo@three.com", "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "three",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [{"id": 1,
+ "content-type": "text/plain", "content": "This is just a test message (#5)\n"}]},
+ [[{"id": "msg-006@notmuch-test-suite", "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "Re: three",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [{"id": 1,
+ "content-type": "text/plain", "content": "This is just a test message (#6)\n"}]},
+ []]]]]]'
+expected=`echo "$expected" | notmuch_json_show_sanitize`
+test_expect_equal_json "$output" "$expected"
+
+test_begin_subtest "Use last Reference"
+add_message '[id]="foo@four.com"' \
+    '[subject]="four"'
+add_message '[id]="bar@four.com"' \
+    '[subject]="not-four"'
+add_message '[in-reply-to]="<baz@four.com>"' \
+    '[references]="<baz@four.com> <foo@four.com>"' \
+    '[subject]="neither"'
+output=$(notmuch show --format=json 'subject:four' | notmuch_json_show_sanitize)
+expected='[[[{"id": "foo@four.com", "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "four",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [{"id": 1,
+ "content-type": "text/plain", "content": "This is just a test message (#7)\n"}]},
+ [[{"id": "msg-009@notmuch-test-suite", "match": false, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "neither",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [{"id": 1,
+ "content-type": "text/plain", "content": "This is just a test message (#9)\n"}]},
+ []]]]], [[{"id": "bar@four.com", "match": true, "excluded": false,
+ "filename": "YYYYY",
+ "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["inbox", "unread"],
+ "headers": {"Subject": "not-four",
+ "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "To": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+ "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [{"id": 1,
+ "content-type": "text/plain", "content": "This is just a test message (#8)\n"}]}, []]]]'
+expected=`echo "$expected" | notmuch_json_show_sanitize`
+test_expect_equal_json "$output" "$expected"
+
+
+test_done
diff --git a/test/argument-parsing b/test/argument-parsing
deleted file mode 100755 (executable)
index 94e9087..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-test_description="argument parsing"
-. ./test-lib.sh
-
-test_begin_subtest "sanity check"
-$TEST_DIRECTORY/arg-test  pos1  --keyword=one --string=foo pos2 --int=7 > OUTPUT
-cat <<EOF > EXPECTED
-keyword 1
-int 7
-string foo
-positional arg 1 pos1
-positional arg 2 pos2
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_done
diff --git a/test/atomicity b/test/atomicity
deleted file mode 100755 (executable)
index 1c786fa..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env bash
-test_description='atomicity'
-. ./test-lib.sh
-
-# This script tests the effects of killing and restarting "notmuch
-# new" at arbitrary points.  If notmuch new is properly atomic, the
-# final database contents should be the same regardless of when (or
-# if) it is killed and restarted.
-
-if test_require_external_prereq gdb; then
-
-# Create a maildir structure to also stress flag synchronization
-    mkdir $MAIL_DIR/cur
-    mkdir $MAIL_DIR/new
-    mkdir $MAIL_DIR/tmp
-    mkdir $MAIL_DIR/.remove-dir
-
-    # Prepare the initial database
-    generate_message [subject]='Duplicate' [filename]='duplicate:2,' [dir]=cur
-    generate_message [subject]='Remove' [filename]='remove:2,' [dir]=cur
-    generate_message [subject]='"Remove duplicate"' [filename]='remove-duplicate:2,' [dir]=cur
-    cp $MAIL_DIR/cur/remove-duplicate:2, $MAIL_DIR/cur/remove-duplicate-copy:2,
-    generate_message [subject]='Rename' [filename]='rename:2,' [dir]=cur
-    generate_message [subject]='"Rename duplicate"' [filename]='rename-duplicate:2,' [dir]=cur
-    generate_message [subject]='"Move 1"' [filename]='move1:2,' [dir]=cur
-    generate_message [subject]='"Move 2"' [filename]='move2:2,' [dir]=new
-    generate_message [subject]='Flag' [filename]='flag:2,' [dir]=cur
-    generate_message [subject]='"Flag duplicate"' [filename]='flag-duplicate:2,' [dir]=cur
-    cp $MAIL_DIR/cur/flag-duplicate:2, $MAIL_DIR/cur/flag-duplicate-copy:2,F
-    generate_message [subject]='"Remove directory"' [filename]='remove-directory:2,' [dir]=.remove-dir
-    generate_message [subject]='"Remove directory duplicate"' [filename]='remove-directory-duplicate:2,' [dir]=.remove-dir
-    cp $MAIL_DIR/.remove-dir/remove-directory-duplicate:2, $MAIL_DIR/cur/
-    notmuch new > /dev/null
-
-    # Make all maildir changes, but *don't* update the database
-    generate_message [subject]='Added' [filename]='added:2,' [dir]=cur
-    cp $MAIL_DIR/cur/duplicate:2, $MAIL_DIR/cur/duplicate-copy:2,
-    generate_message [subject]='"Add duplicate"' [filename]='add-duplicate:2,' [dir]=cur
-    generate_message [subject]='"Add duplicate copy"' [filename]='add-duplicate-copy:2,' [dir]=cur
-    rm $MAIL_DIR/cur/remove:2,
-    rm $MAIL_DIR/cur/remove-duplicate-copy:2,
-    mv $MAIL_DIR/cur/rename:2, $MAIL_DIR/cur/renamed:2,
-    mv $MAIL_DIR/cur/rename-duplicate:2, $MAIL_DIR/cur/renamed-duplicate:2,
-    mv $MAIL_DIR/cur/move1:2, $MAIL_DIR/new/move1:2,
-    mv $MAIL_DIR/new/move2:2, $MAIL_DIR/cur/move2:2,
-    mv $MAIL_DIR/cur/flag:2, $MAIL_DIR/cur/flag:2,F
-    rm $MAIL_DIR/cur/flag-duplicate-copy:2,F
-    rm $MAIL_DIR/.remove-dir/remove-directory:2,
-    rm $MAIL_DIR/.remove-dir/remove-directory-duplicate:2,
-    rmdir $MAIL_DIR/.remove-dir
-
-    # Prepare a snapshot of the updated maildir.  The gdb script will
-    # update the database in this snapshot as it goes.
-    cp -a $MAIL_DIR $MAIL_DIR.snap
-    cp ${NOTMUCH_CONFIG} ${NOTMUCH_CONFIG}.snap
-    NOTMUCH_CONFIG=${NOTMUCH_CONFIG}.snap notmuch config set database.path $MAIL_DIR.snap
-
-
-
-    # Execute notmuch new and, at every call to rename, snapshot the
-    # database, run notmuch new again on the snapshot, and capture the
-    # results of search.
-    #
-    # -tty /dev/null works around a conflict between the 'timeout' wrapper
-    # and gdb's attempt to control the TTY.
-    export MAIL_DIR
-    gdb -tty /dev/null -batch -x $TEST_DIRECTORY/atomicity.gdb notmuch >/dev/null 2>/dev/null
-
-    # Get the final, golden output
-    notmuch search '*' > expected
-
-    # Check output against golden output
-    outcount=$(cat outcount)
-    echo -n > searchall
-    echo -n > expectall
-    for ((i = 0; i < $outcount; i++)); do
-       if ! cmp -s search.$i expected; then
-           # Find the range of interruptions that match this output
-           for ((end = $i + 1 ; end < $outcount; end++)); do
-               if ! cmp -s search.$i search.$end; then
-                   break
-               fi
-           done
-           echo "When interrupted after $test/backtrace.$(expr $i - 1) (abort points $i-$(expr $end - 1))" >> searchall
-           cat search.$i >> searchall
-           cat expected >> expectall
-           echo >> searchall
-           echo >> expectall
-
-           i=$(expr $end - 1)
-       fi
-    done
-fi
-
-test_begin_subtest '"notmuch new" is idempotent under arbitrary aborts'
-test_expect_equal_file searchall expectall
-
-test_expect_success "detected $outcount>10 abort points" "test $outcount -gt 10"
-
-test_done
diff --git a/test/author-order b/test/author-order
deleted file mode 100755 (executable)
index 6ffeffc..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env bash
-test_description="author reordering;"
-. ./test-lib.sh
-
-test_begin_subtest "Adding parent message"
-generate_message [body]=findme [id]=new-parent-id [subject]=author-reorder-threadtest '[from]="User <user@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
-
-test_begin_subtest "Adding initial child message"
-generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User1 <user1@example.com>"' '[date]="Sat, 01 Jan 2000 12:01:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
-
-test_begin_subtest "Adding second child message"
-generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User2 <user2@example.com>"' '[date]="Sat, 01 Jan 2000 12:02:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
-
-test_begin_subtest "Searching when all three messages match"
-output=$(notmuch search findme | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [3/3] User, User1, User2; author-reorder-threadtest (inbox unread)"
-
-test_begin_subtest "Searching when two messages match"
-output=$(notmuch search User1 or User2 | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [2/3] User1, User2| User; author-reorder-threadtest (inbox unread)"
-
-test_begin_subtest "Searching when only one message matches"
-output=$(notmuch search User2 | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [1/3] User2| User, User1; author-reorder-threadtest (inbox unread)"
-
-test_begin_subtest "Searching when only first message matches"
-output=$(notmuch search User | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [1/3] User| User1, User2; author-reorder-threadtest (inbox unread)"
-
-test_begin_subtest "Adding duplicate author"
-generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User1 <user1@example.com>"' '[date]="Sat, 01 Jan 2000 12:03:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
-
-test_begin_subtest "Searching when all four messages match"
-output=$(notmuch search findme | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [4/4] User, User1, User2; author-reorder-threadtest (inbox unread)"
-
-test_begin_subtest "Adding non-monotonic child message"
-generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User0 <user0@example.com>"' '[date]="Sat, 01 Jan 2000 11:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-test_expect_equal "$output" "Added 1 new message to the database."
-
-test_begin_subtest "Searching non-monotonic messages (oldest-first)"
-output=$(notmuch search --sort=oldest-first findme | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [5/5] User0, User, User1, User2; author-reorder-threadtest (inbox unread)"
-
-test_begin_subtest "Searching non-monotonic messages (newest-first)"
-output=$(notmuch search --sort=newest-first findme | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [5/5] User0, User, User1, User2; author-reorder-threadtest (inbox unread)"
-
-test_done
diff --git a/test/basic b/test/basic
deleted file mode 100755 (executable)
index 9c94b62..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-
-test_description='the test framework itself.'
-
-################################################################
-# It appears that people try to run tests without building...
-
-if ! test -x ../notmuch
-then
-       echo >&2 'You do not seem to have built notmuch yet.'
-       exit 1
-fi
-
-. ./test-lib.sh
-
-################################################################
-# Test harness
-test_expect_success 'success is reported like this' '
-    :
-'
-test_set_prereq HAVEIT
-haveit=no
-test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
-    test_have_prereq HAVEIT &&
-    haveit=yes
-'
-
-clean=no
-test_expect_success 'tests clean up after themselves' '
-    test_when_finished clean=yes
-'
-
-cleaner=no
-test_expect_code 1 'tests clean up even after a failure' '
-    test_when_finished cleaner=yes &&
-    (exit 1)
-'
-
-if test $clean$cleaner != yesyes
-then
-       say "bug in test framework: cleanup commands do not work reliably"
-       exit 1
-fi
-
-test_expect_code 2 'failure to clean up causes the test to fail' '
-    test_when_finished "(exit 2)"
-'
-
-EXPECTED=$TEST_DIRECTORY/test.expected-output
-suppress_diff_date() {
-    sed -e 's/\(.*\-\-\- test-verbose\.4\.\expected\).*/\1/' \
-       -e 's/\(.*\+\+\+ test-verbose\.4\.\output\).*/\1/'
-}
-
-test_begin_subtest "Ensure that test output is suppressed unless the test fails"
-output=$(cd $TEST_DIRECTORY; NOTMUCH_TEST_QUIET= ./test-verbose 2>&1 | suppress_diff_date)
-expected=$(cat $EXPECTED/test-verbose-no | suppress_diff_date)
-test_expect_equal "$output" "$expected"
-
-test_begin_subtest "Ensure that -v does not suppress test output"
-output=$(cd $TEST_DIRECTORY; NOTMUCH_TEST_QUIET= ./test-verbose -v 2>&1 | suppress_diff_date)
-expected=$(cat $EXPECTED/test-verbose-yes | suppress_diff_date)
-# Do not include the results of test-verbose in totals
-rm $TEST_DIRECTORY/test-results/test-verbose
-rm -r $TEST_DIRECTORY/tmp.test-verbose
-test_expect_equal "$output" "$expected"
-
-
-################################################################
-# Test mail store prepared in test-lib.sh
-
-test_expect_success \
-    'test that mail store was created' \
-    'test -d "${MAIL_DIR}"'
-
-
-find "${MAIL_DIR}" -type f -print >should-be-empty
-test_expect_success \
-    'mail store should be empty' \
-    'cmp -s /dev/null should-be-empty'
-
-test_expect_success \
-    'NOTMUCH_CONFIG is set and points to an existing file' \
-    'test -f "${NOTMUCH_CONFIG}"'
-
-test_expect_success \
-    'PATH is set to this repository' \
-    'test "`echo $PATH|cut -f1 -d: | sed -e 's,/test/valgrind/bin$,,'`" = "`dirname ${TEST_DIRECTORY}`"'
-
-test_done
diff --git a/test/compact b/test/compact
deleted file mode 100755 (executable)
index ac174ce..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env bash
-test_description='"notmuch compact"'
-. ./test-lib.sh
-
-add_message '[subject]=One'
-add_message '[subject]=Two'
-add_message '[subject]=Three'
-
-notmuch tag +tag1 \*
-notmuch tag +tag2 subject:Two
-notmuch tag -tag1 +tag3 subject:Three
-
-test_expect_success "Running compact" "notmuch compact --backup=${TEST_DIRECTORY}/xapian.old"
-
-test_begin_subtest "Compact preserves database"
-output=$(notmuch search \* | notmuch_search_sanitize)
-test_expect_equal "$output" "\
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag2 unread)
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Three (inbox tag3 unread)"
-
-test_expect_success 'Restoring Backup' \
-    'rm -Rf ${MAIL_DIR}/.notmuch/xapian &&
-     mv ${TEST_DIRECTORY}/xapian.old ${MAIL_DIR}/.notmuch/xapian'
-
-test_begin_subtest "Checking restored backup"
-output=$(notmuch search \* | notmuch_search_sanitize)
-test_expect_equal "$output" "\
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (inbox tag1 unread)
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag2 unread)
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Three (inbox tag3 unread)"
-
-test_done
diff --git a/test/config b/test/config
deleted file mode 100755 (executable)
index ca4cf33..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env bash
-
-test_description='"notmuch config"'
-. ./test-lib.sh
-
-test_begin_subtest "Get string value"
-test_expect_equal "$(notmuch config get user.name)" "Notmuch Test Suite"
-
-test_begin_subtest "Get list value"
-test_expect_equal "$(notmuch config get new.tags)" "\
-unread
-inbox"
-
-test_begin_subtest "Set string value"
-notmuch config set foo.string "this is a string value"
-test_expect_equal "$(notmuch config get foo.string)" "this is a string value"
-
-test_begin_subtest "Set string value again"
-notmuch config set foo.string "this is another string value"
-test_expect_equal "$(notmuch config get foo.string)" "this is another string value"
-
-test_begin_subtest "Set list value"
-notmuch config set foo.list this "is a" "list value"
-test_expect_equal "$(notmuch config get foo.list)" "\
-this
-is a
-list value"
-
-test_begin_subtest "Set list value again"
-notmuch config set foo.list this "is another" "list value"
-test_expect_equal "$(notmuch config get foo.list)" "\
-this
-is another
-list value"
-
-test_begin_subtest "Remove key"
-notmuch config set foo.remove baz
-notmuch config set foo.remove
-test_expect_equal "$(notmuch config get foo.remove)" ""
-
-test_begin_subtest "Remove non-existent key"
-notmuch config set foo.nonexistent
-test_expect_equal "$(notmuch config get foo.nonexistent)" ""
-
-test_begin_subtest "List all items"
-notmuch config set database.path "/canonical/path"
-output=$(notmuch config list)
-test_expect_equal "$output" "\
-database.path=/canonical/path
-user.name=Notmuch Test Suite
-user.primary_email=test_suite@notmuchmail.org
-user.other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org
-new.tags=unread;inbox;
-new.ignore=
-search.exclude_tags=
-maildir.synchronize_flags=true
-foo.string=this is another string value
-foo.list=this;is another;list value;"
-
-test_begin_subtest "Top level --config=FILE option"
-cp "${NOTMUCH_CONFIG}" alt-config
-notmuch --config=alt-config config set user.name "Another Name"
-test_expect_equal "$(notmuch --config=alt-config config get user.name)" \
-    "Another Name"
-
-test_begin_subtest "Top level --config=FILE option changed the right file"
-test_expect_equal "$(notmuch config get user.name)" \
-    "Notmuch Test Suite"
-
-test_begin_subtest "Read config file through a symlink"
-ln -s alt-config alt-config-link
-test_expect_equal "$(notmuch --config=alt-config-link config get user.name)" \
-    "Another Name"
-
-test_begin_subtest "Write config file through a symlink"
-notmuch --config=alt-config-link config set user.name "Link Name"
-test_expect_equal "$(notmuch --config=alt-config-link config get user.name)" \
-    "Link Name"
-
-test_begin_subtest "Writing config file through symlink follows symlink"
-test_expect_equal "$(readlink alt-config-link)" "alt-config"
-
-test_done
diff --git a/test/count b/test/count
deleted file mode 100755 (executable)
index da86c8c..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env bash
-test_description='"notmuch count" for messages and threads'
-. ./test-lib.sh
-
-add_email_corpus
-
-# Note: The 'wc -l' results below are wrapped in arithmetic evaluation
-# $((...)) to strip whitespace. This is for portability, as 'wc -l'
-# emits whitespace on some BSD variants.
-
-test_begin_subtest "message count is the default for notmuch count"
-test_expect_equal \
-    "$((`notmuch search --output=messages '*' | wc -l`))" \
-    "`notmuch count '*'`"
-
-test_begin_subtest "message count with --output=messages"
-test_expect_equal \
-    "$((`notmuch search --output=messages '*' | wc -l`))" \
-    "`notmuch count --output=messages '*'`"
-
-test_begin_subtest "thread count with --output=threads"
-test_expect_equal \
-    "$((`notmuch search --output=threads '*' | wc -l`))" \
-    "`notmuch count --output=threads '*'`"
-
-test_begin_subtest "thread count is the default for notmuch search"
-test_expect_equal \
-    "$((`notmuch search '*' | wc -l`))" \
-    "`notmuch count --output=threads '*'`"
-
-test_begin_subtest "files count"
-test_expect_equal \
-    "$((`notmuch search --output=files '*' | wc -l`))" \
-    "`notmuch count --output=files '*'`"
-
-test_begin_subtest "files count for a duplicate message-id"
-test_expect_equal \
-    "2" \
-    "`notmuch count --output=files id:20091117232137.GA7669@griffis1.net`"
-
-test_begin_subtest "count with no matching messages"
-test_expect_equal \
-    "0" \
-    "`notmuch count --output=messages from:cworth and not from:cworth`"
-
-test_begin_subtest "count with no matching threads"
-test_expect_equal \
-    "0" \
-    "`notmuch count --output=threads from:cworth and not from:cworth`"
-
-test_begin_subtest "message count is the default for batch count"
-notmuch count --batch >OUTPUT <<EOF
-
-from:cworth
-EOF
-notmuch count --output=messages >EXPECTED
-notmuch count --output=messages from:cworth >>EXPECTED
-test_expect_equal_file EXPECTED OUTPUT
-
-test_begin_subtest "batch message count"
-notmuch count --batch --output=messages >OUTPUT <<EOF
-from:cworth
-
-tag:inbox
-EOF
-notmuch count --output=messages from:cworth >EXPECTED
-notmuch count --output=messages >>EXPECTED
-notmuch count --output=messages tag:inbox >>EXPECTED
-test_expect_equal_file EXPECTED OUTPUT
-
-test_begin_subtest "batch thread count"
-notmuch count --batch --output=threads >OUTPUT <<EOF
-
-from:cworth
-from:cworth and not from:cworth
-foo
-EOF
-notmuch count --output=threads >EXPECTED
-notmuch count --output=threads from:cworth >>EXPECTED
-notmuch count --output=threads from:cworth and not from:cworth >>EXPECTED
-notmuch count --output=threads foo >>EXPECTED
-test_expect_equal_file EXPECTED OUTPUT
-
-test_begin_subtest "batch message count with input file"
-cat >INPUT <<EOF
-from:cworth
-
-tag:inbox
-EOF
-notmuch count --input=INPUT --output=messages >OUTPUT
-notmuch count --output=messages from:cworth >EXPECTED
-notmuch count --output=messages >>EXPECTED
-notmuch count --output=messages tag:inbox >>EXPECTED
-test_expect_equal_file EXPECTED OUTPUT
-
-
-test_done
diff --git a/test/crypto b/test/crypto
deleted file mode 100755 (executable)
index 477b397..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-#!/usr/bin/env bash
-
-# TODO:
-# - decryption/verification with signer key not available
-# - verification of signatures from expired/revoked keys
-
-test_description='PGP/MIME signature verification and decryption'
-. ./test-lib.sh
-
-add_gnupg_home ()
-{
-    local output
-    [ -d ${GNUPGHOME} ] && return
-    mkdir -m 0700 "$GNUPGHOME"
-    gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
-    test_debug "cat $GNUPGHOME/import.log"
-    if (gpg --quick-random --version >/dev/null 2>&1) ; then
-       echo quick-random >> "$GNUPGHOME"/gpg.conf
-    elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
-       echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
-    fi
-    echo no-emit-version >> "$GNUPGHOME"/gpg.conf
-}
-
-##################################################
-
-add_gnupg_home
-# get key fingerprint
-FINGERPRINT=$(gpg --no-tty --list-secret-keys --with-colons --fingerprint | grep '^fpr:' | cut -d: -f10)
-
-test_expect_success 'emacs delivery of signed message' \
-'emacs_fcc_message \
-    "test signed message 001" \
-    "This is a test signed message." \
-    "(mml-secure-message-sign)"'
-
-test_begin_subtest "signature verification"
-output=$(notmuch show --format=json --verify subject:"test signed message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["inbox","signed"],
- "headers": {"Subject": "test signed message 001",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "sigstatus": [{"status": "good",
- "fingerprint": "'$FINGERPRINT'",
- "created": 946728000}],
- "content-type": "multipart/signed",
- "content": [{"id": 2,
- "content-type": "text/plain",
- "content": "This is a test signed message.\n"},
- {"id": 3,
- "content-type": "application/pgp-signature",
- "content-length": 280}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "signature verification with full owner trust"
-# give the key full owner trust
-echo "${FINGERPRINT}:6:" | gpg --no-tty --import-ownertrust >>"$GNUPGHOME"/trust.log 2>&1
-gpg --no-tty --check-trustdb >>"$GNUPGHOME"/trust.log 2>&1
-output=$(notmuch show --format=json --verify subject:"test signed message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["inbox","signed"],
- "headers": {"Subject": "test signed message 001",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "sigstatus": [{"status": "good",
- "fingerprint": "'$FINGERPRINT'",
- "created": 946728000,
- "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],
- "content-type": "multipart/signed",
- "content": [{"id": 2,
- "content-type": "text/plain",
- "content": "This is a test signed message.\n"},
- {"id": 3,
- "content-type": "application/pgp-signature",
- "content-length": 280}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "signature verification with signer key unavailable"
-# move the gnupghome temporarily out of the way
-mv "${GNUPGHOME}"{,.bak}
-output=$(notmuch show --format=json --verify subject:"test signed message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["inbox","signed"],
- "headers": {"Subject": "test signed message 001",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "sigstatus": [{"status": "error",
- "keyid": "'$(echo $FINGERPRINT | cut -c 25-)'",
- "errors": 2}],
- "content-type": "multipart/signed",
- "content": [{"id": 2,
- "content-type": "text/plain",
- "content": "This is a test signed message.\n"},
- {"id": 3,
- "content-type": "application/pgp-signature",
- "content-length": 280}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-mv "${GNUPGHOME}"{.bak,}
-
-# create a test encrypted message with attachment
-cat <<EOF >TESTATTACHMENT
-This is a test file.
-EOF
-test_expect_success 'emacs delivery of encrypted message with attachment' \
-'emacs_fcc_message \
-    "test encrypted message 001" \
-    "This is a test encrypted message.\n" \
-    "(mml-attach-file \"TESTATTACHMENT\") (mml-secure-message-encrypt)"'
-
-test_begin_subtest "decryption, --format=text"
-output=$(notmuch show --format=text --decrypt subject:"test encrypted message 001" \
-    | notmuch_show_sanitize_all \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='\fmessage{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2000-01-01) (encrypted inbox)
-Subject: test encrypted message 001
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: test_suite@notmuchmail.org
-Date: Sat, 01 Jan 2000 12:00:00 +0000
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: multipart/encrypted
-\fpart{ ID: 2, Content-type: application/pgp-encrypted
-Non-text part: application/pgp-encrypted
-\fpart}
-\fpart{ ID: 3, Content-type: multipart/mixed
-\fpart{ ID: 4, Content-type: text/plain
-This is a test encrypted message.
-\fpart}
-\fattachment{ ID: 5, Filename: TESTATTACHMENT, Content-type: application/octet-stream
-Non-text part: application/octet-stream
-\fattachment}
-\fpart}
-\fpart}
-\fbody}
-\fmessage}'
-test_expect_equal \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "decryption, --format=json"
-output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["encrypted","inbox"],
- "headers": {"Subject": "test encrypted message 001",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "encstatus": [{"status": "good"}],
- "sigstatus": [],
- "content-type": "multipart/encrypted",
- "content": [{"id": 2,
- "content-type": "application/pgp-encrypted",
- "content-length": 11},
- {"id": 3,
- "content-type": "multipart/mixed",
- "content": [{"id": 4,
- "content-type": "text/plain",
- "content": "This is a test encrypted message.\n"},
- {"id": 5,
- "content-type": "application/octet-stream",
- "content-length": 28,
- "content-transfer-encoding": "base64",
- "filename": "TESTATTACHMENT"}]}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "decryption, --format=json, --part=4"
-output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='{"id": 4,
- "content-type": "text/plain",
- "content": "This is a test encrypted message.\n"}'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "decrypt attachment (--part=5 --format=raw)"
-notmuch show \
-    --format=raw \
-    --part=5 \
-    --decrypt \
-    subject:"test encrypted message 001" >OUTPUT
-test_expect_equal_file OUTPUT TESTATTACHMENT
-
-test_begin_subtest "decryption failure with missing key"
-mv "${GNUPGHOME}"{,.bak}
-# The length of the encrypted attachment varies so must be normalized.
-output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|' \
-    | sed -e 's|"content-length": 6[1234567890]*|"content-length": 652|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["encrypted","inbox"],
- "headers": {"Subject": "test encrypted message 001",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "encstatus": [{"status": "bad"}],
- "content-type": "multipart/encrypted",
- "content": [{"id": 2,
- "content-type": "application/pgp-encrypted",
- "content-length": 11},
- {"id": 3,
- "content-type": "application/octet-stream",
- "content-length": 652}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-mv "${GNUPGHOME}"{.bak,}
-
-test_expect_success 'emacs delivery of encrypted + signed message' \
-'emacs_fcc_message \
-    "test encrypted message 002" \
-    "This is another test encrypted message.\n" \
-    "(mml-secure-message-sign-encrypt)"'
-
-test_begin_subtest "decryption + signature verification"
-output=$(notmuch show --format=json --decrypt subject:"test encrypted message 002" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["encrypted","inbox"],
- "headers": {"Subject": "test encrypted message 002",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "encstatus": [{"status": "good"}],
- "sigstatus": [{"status": "good",
- "fingerprint": "'$FINGERPRINT'",
- "created": 946728000,
- "userid": " Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"}],
- "content-type": "multipart/encrypted",
- "content": [{"id": 2,
- "content-type": "application/pgp-encrypted",
- "content-length": 11},
- {"id": 3,
- "content-type": "text/plain",
- "content": "This is another test encrypted message.\n"}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "reply to encrypted message"
-output=$(notmuch reply --decrypt subject:"test encrypted message 002" \
-    | grep -v -e '^In-Reply-To:' -e '^References:')
-expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: test encrypted message 002
-
-On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
-> This is another test encrypted message.'
-test_expect_equal \
-    "$output" \
-    "$expected"
-
-test_begin_subtest "signature verification with revoked key"
-# generate revocation certificate and load it to revoke key
-echo "y
-1
-Notmuch Test Suite key revocation (automated) $(date '+%F_%T%z')
-
-y
-
-" \
-    | gpg --no-tty --quiet --command-fd 0 --armor --gen-revoke "0x${FINGERPRINT}!" 2>/dev/null \
-    | gpg --no-tty --quiet --import
-output=$(notmuch show --format=json --verify subject:"test signed message 001" \
-    | notmuch_json_show_sanitize \
-    | sed -e 's|"created": [1234567890]*|"created": 946728000|')
-expected='[[[{"id": "XXXXX",
- "match": true,
- "excluded": false,
- "filename": "YYYYY",
- "timestamp": 946728000,
- "date_relative": "2000-01-01",
- "tags": ["inbox","signed"],
- "headers": {"Subject": "test signed message 001",
- "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
- "To": "test_suite@notmuchmail.org",
- "Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
- "body": [{"id": 1,
- "sigstatus": [{"status": "error",
- "keyid": "6D92612D94E46381",
- "errors": 8}],
- "content-type": "multipart/signed",
- "content": [{"id": 2,
- "content-type": "text/plain",
- "content": "This is a test signed message.\n"},
- {"id": 3,
- "content-type": "application/pgp-signature",
- "content-length": 280}]}]},
- []]]]'
-test_expect_equal_json \
-    "$output" \
-    "$expected"
-
-test_done
diff --git a/test/dump-restore b/test/dump-restore
deleted file mode 100755 (executable)
index 0004438..0000000
+++ /dev/null
@@ -1,293 +0,0 @@
-#!/usr/bin/env bash
-test_description="\"notmuch dump\" and \"notmuch restore\""
-. ./test-lib.sh
-
-add_email_corpus
-
-test_expect_success 'Dumping all tags' \
-  'generate_message &&
-  notmuch new &&
-  notmuch dump > dump.expected'
-
-# The use of from:cworth is rather arbitrary: it matches some of the
-# email corpus' messages, but not all of them.
-
-test_expect_success 'Dumping all tags II' \
-  'notmuch tag +ABC +DEF -- from:cworth &&
-  notmuch dump > dump-ABC_DEF.expected &&
-  ! cmp dump.expected dump-ABC_DEF.expected'
-
-test_expect_success 'Clearing all tags' \
-  'sed -e "s/(\([^(]*\))$/()/" < dump.expected > clear.expected &&
-  notmuch restore --input=clear.expected &&
-  notmuch dump > clear.actual &&
-  test_cmp clear.expected clear.actual'
-
-test_expect_success 'Accumulate original tags' \
-  'notmuch tag +ABC +DEF -- from:cworth &&
-  notmuch restore --accumulate < dump.expected &&
-  notmuch dump > dump.actual &&
-  test_cmp dump-ABC_DEF.expected dump.actual'
-
-test_expect_success 'Restoring original tags' \
-  'notmuch restore --input=dump.expected &&
-  notmuch dump > dump.actual &&
-  test_cmp dump.expected dump.actual'
-
-test_expect_success 'Restore with nothing to do' \
-  'notmuch restore < dump.expected &&
-  notmuch dump > dump.actual &&
-  test_cmp dump.expected dump.actual'
-
-test_expect_success 'Accumulate with existing tags' \
-  'notmuch restore --accumulate --input=dump.expected &&
-  notmuch dump > dump.actual &&
-  test_cmp dump.expected dump.actual'
-
-test_expect_success 'Accumulate with no tags' \
-  'notmuch restore --accumulate < clear.expected &&
-  notmuch dump > dump.actual &&
-  test_cmp dump.expected dump.actual'
-
-test_expect_success 'Accumulate with new tags' \
-  'notmuch restore --input=dump.expected &&
-  notmuch restore --accumulate --input=dump-ABC_DEF.expected &&
-  notmuch dump >  OUTPUT.$test_count &&
-  notmuch restore --input=dump.expected &&
-  test_cmp dump-ABC_DEF.expected OUTPUT.$test_count'
-
-# notmuch restore currently only considers the first argument.
-test_expect_success 'Invalid restore invocation' \
-  'test_must_fail notmuch restore --input=dump.expected another_one'
-
-test_begin_subtest "dump --output=outfile"
-notmuch dump --output=dump-outfile.actual
-test_expect_equal_file dump.expected dump-outfile.actual
-
-test_begin_subtest "dump --output=outfile --"
-notmuch dump --output=dump-1-arg-dash.actual --
-test_expect_equal_file dump.expected dump-1-arg-dash.actual
-
-# Note, we assume all messages from cworth have a message-id
-# containing cworth.org
-
-grep 'cworth[.]org' dump.expected > dump-cworth.expected
-
-test_begin_subtest "dump -- from:cworth"
-notmuch dump -- from:cworth > dump-dash-cworth.actual
-test_expect_equal_file dump-cworth.expected dump-dash-cworth.actual
-
-test_begin_subtest "dump --output=outfile from:cworth"
-notmuch dump --output=dump-outfile-cworth.actual from:cworth
-test_expect_equal_file dump-cworth.expected dump-outfile-cworth.actual
-
-test_begin_subtest "dump --output=outfile -- from:cworth"
-notmuch dump --output=dump-outfile-dash-inbox.actual -- from:cworth
-test_expect_equal_file dump-cworth.expected dump-outfile-dash-inbox.actual
-
-test_begin_subtest "Check for a safe set of message-ids"
-notmuch search --output=messages from:cworth | sed s/^id:// > EXPECTED
-notmuch search --output=messages from:cworth | sed s/^id:// |\
-       $TEST_DIRECTORY/hex-xcode --direction=encode > OUTPUT
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "format=batch-tag, dump sanity check."
-notmuch dump --format=sup from:cworth | cut -f1 -d' ' | \
-    sort > EXPECTED.$test_count
-notmuch dump --format=batch-tag from:cworth | sed 's/^.*-- id://' | \
-    sort > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest "format=batch-tag, # round-trip"
-notmuch dump --format=sup | sort > EXPECTED.$test_count
-notmuch dump --format=batch-tag | notmuch restore --format=batch-tag
-notmuch dump --format=sup | sort > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest "format=batch-tag, # blank lines and comments"
-notmuch dump --format=batch-tag| sort > EXPECTED.$test_count
-notmuch restore <<EOF
-# this line is a comment; the next has only white space
-        
-
-# the previous line is empty
-EOF
-notmuch dump --format=batch-tag | sort > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest "format=batch-tag, # reverse-round-trip empty tag"
-cat <<EOF >EXPECTED.$test_count
-+ -- id:20091117232137.GA7669@griffis1.net
-EOF
-notmuch restore --format=batch-tag < EXPECTED.$test_count
-notmuch dump --format=batch-tag id:20091117232137.GA7669@griffis1.net > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-tag1='comic_swear=$&^%$^%\\//-+$^%$'
-enc1=$($TEST_DIRECTORY/hex-xcode --direction=encode "$tag1")
-
-tag2=$(printf 'this\n tag\t has\n spaces')
-enc2=$($TEST_DIRECTORY/hex-xcode --direction=encode "$tag2")
-
-enc3='%c3%91%c3%a5%c3%b0%c3%a3%c3%a5%c3%a9-%c3%8f%c3%8a'
-tag3=$($TEST_DIRECTORY/hex-xcode --direction=decode $enc3)
-
-notmuch dump --format=batch-tag > BACKUP
-
-notmuch tag +"$tag1" +"$tag2" +"$tag3" -inbox -unread "*"
-
-# initial segment of file used for several tests below.
-cat <<EOF > comments-and-blanks
-# this is a comment
-
-# next line has leading whitespace
-       
-
-EOF
-
-test_begin_subtest 'restoring empty file is not an error'
-notmuch restore < /dev/null 2>OUTPUT.$test_count
-cp /dev/null EXPECTED
-test_expect_equal_file EXPECTED OUTPUT.$test_count
-
-test_begin_subtest 'file of comments and blank lines is not an error'
-notmuch restore --input=comments-and-blanks
-ret_val=$?
-test_expect_equal "$ret_val" "0"
-
-cp comments-and-blanks leading-comments-blanks-batch-tag
-echo "+some_tag -- id:yun1vjwegii.fsf@aiko.keithp.com" \
-    >> leading-comments-blanks-batch-tag
-
-test_begin_subtest 'detect format=batch-tag with leading comments and blanks'
-notmuch restore --input=leading-comments-blanks-batch-tag
-notmuch search --output=tags id:yun1vjwegii.fsf@aiko.keithp.com > OUTPUT.$test_count
-echo "some_tag" > EXPECTED
-test_expect_equal_file EXPECTED OUTPUT.$test_count
-
-cp comments-and-blanks leading-comments-blanks-sup
-echo "yun1vjwegii.fsf@aiko.keithp.com (another_tag)" \
-    >> leading-comments-blanks-sup
-
-test_begin_subtest 'detect format=sup with leading comments and blanks'
-notmuch restore --input=leading-comments-blanks-sup
-notmuch search --output=tags id:yun1vjwegii.fsf@aiko.keithp.com > OUTPUT.$test_count
-echo "another_tag" > EXPECTED
-test_expect_equal_file EXPECTED OUTPUT.$test_count
-
-test_begin_subtest 'format=batch-tag, round trip with strange tags'
-notmuch dump --format=batch-tag > EXPECTED.$test_count
-notmuch dump --format=batch-tag | notmuch restore --format=batch-tag
-notmuch dump --format=batch-tag > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest 'format=batch-tag, checking encoded output'
-notmuch dump --format=batch-tag -- from:cworth |\
-        awk "{ print \"+$enc1 +$enc2 +$enc3 -- \" \$5 }" > EXPECTED.$test_count
-notmuch dump --format=batch-tag -- from:cworth  > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest 'restoring sane tags'
-notmuch restore --format=batch-tag < BACKUP
-notmuch dump --format=batch-tag > OUTPUT.$test_count
-test_expect_equal_file BACKUP OUTPUT.$test_count
-
-test_begin_subtest 'format=batch-tag, restore=auto'
-notmuch dump --format=batch-tag > EXPECTED.$test_count
-notmuch tag -inbox -unread "*"
-notmuch restore --format=auto < EXPECTED.$test_count
-notmuch dump --format=batch-tag > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest 'format=sup, restore=auto'
-notmuch dump --format=sup > EXPECTED.$test_count
-notmuch tag -inbox -unread "*"
-notmuch restore --format=auto < EXPECTED.$test_count
-notmuch dump --format=sup > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest 'format=batch-tag, restore=default'
-notmuch dump --format=batch-tag > EXPECTED.$test_count
-notmuch tag -inbox -unread "*"
-notmuch restore < EXPECTED.$test_count
-notmuch dump --format=batch-tag > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest 'format=sup, restore=default'
-notmuch dump --format=sup > EXPECTED.$test_count
-notmuch tag -inbox -unread "*"
-notmuch restore < EXPECTED.$test_count
-notmuch dump --format=sup > OUTPUT.$test_count
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_begin_subtest 'restore: checking error messages'
-notmuch restore <<EOF 2>OUTPUT
-# the next line has a space
-a
-+0
-+a +b
-# trailing whitespace
-+a +b 
-+c +d --
-# this is a harmless comment, do not yell about it.
-
-# the previous line was blank; also no yelling please
-+%zz -- id:whatever
-+e +f id:"
-+e +f tag:abc
-# the next non-comment line should report an an empty tag error for
-# batch tagging, but not for restore
-+ +e -- id:20091117232137.GA7669@griffis1.net
-# valid id, but warning about missing message
-+e id:missing_message_id
-# exercise parser
-+e -- id:some)stuff
-+e -- id:some stuff
-+e -- id:some"stuff
-+e -- id:"a_message_id_with""_a_quote"
-+e -- id:"a message id with spaces"
-+e --  id:an_id_with_leading_and_trailing_ws \
-
-EOF
-
-cat <<EOF > EXPECTED
-Warning: cannot parse query: a (skipping)
-Warning: no query string [+0]
-Warning: no query string [+a +b]
-Warning: missing query string [+a +b ]
-Warning: no query string after -- [+c +d --]
-Warning: hex decoding of tag %zz failed [+%zz -- id:whatever]
-Warning: cannot parse query: id:" (skipping)
-Warning: not an id query: tag:abc (skipping)
-Warning: cannot apply tags to missing message: missing_message_id
-Warning: cannot parse query: id:some)stuff (skipping)
-Warning: cannot parse query: id:some stuff (skipping)
-Warning: cannot apply tags to missing message: some"stuff
-Warning: cannot apply tags to missing message: a_message_id_with"_a_quote
-Warning: cannot apply tags to missing message: a message id with spaces
-Warning: cannot apply tags to missing message: an_id_with_leading_and_trailing_ws
-EOF
-
-test_expect_equal_file EXPECTED OUTPUT
-
-test_begin_subtest 'roundtripping random message-ids and tags'
-
-    ${TEST_DIRECTORY}/random-corpus --config-path=${NOTMUCH_CONFIG} \
-                       --num-messages=100
-
-     notmuch dump --format=batch-tag| \
-        sort > EXPECTED.$test_count
-
-     notmuch tag +this_tag_is_very_unlikely_to_be_random '*'
-
-     notmuch restore --format=batch-tag < EXPECTED.$test_count
-
-     notmuch dump --format=batch-tag| \
-        sort > OUTPUT.$test_count
-
-test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count
-
-test_done
-
-# Note the database is "poisoned" for sup format at this point.
diff --git a/test/emacs b/test/emacs
deleted file mode 100755 (executable)
index 863219d..0000000
+++ /dev/null
@@ -1,952 +0,0 @@
-#!/usr/bin/env bash
-
-test_description="emacs interface"
-. ./test-lib.sh
-
-EXPECTED=$TEST_DIRECTORY/emacs.expected-output
-
-add_email_corpus
-
-test_begin_subtest "Basic notmuch-hello view in emacs"
-test_emacs '(notmuch-hello)
-           (test-output)'
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello
-
-test_begin_subtest "Saved search with 0 results"
-test_emacs '(let ((notmuch-show-empty-saved-searches t)
-                 (notmuch-saved-searches
-                  '\''(("inbox" . "tag:inbox")
-                       ("unread" . "tag:unread")
-                       ("empty" . "tag:doesnotexist"))))
-             (notmuch-hello)
-             (test-output))'
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-with-empty
-
-test_begin_subtest "No saved searches displayed (all with 0 results)"
-test_emacs '(let ((notmuch-saved-searches
-                  '\''(("empty" . "tag:doesnotexist"))))
-             (notmuch-hello)
-             (test-output))'
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-no-saved-searches
-
-test_begin_subtest "Basic notmuch-search view in emacs"
-test_emacs '(notmuch-search "tag:inbox")
-           (notmuch-test-wait)
-           (test-output)'
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox
-
-test_begin_subtest "Incremental parsing of search results"
-test_emacs "(ad-enable-advice 'notmuch-search-process-filter 'around 'pessimal)
-           (ad-activate 'notmuch-search-process-filter)
-           (notmuch-search \"tag:inbox\")
-           (notmuch-test-wait)
-           (ad-disable-advice 'notmuch-search-process-filter 'around 'pessimal)
-           (ad-activate 'notmuch-search-process-filter)
-           (test-output)"
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-search-tag-inbox
-
-test_begin_subtest "Navigation of notmuch-hello to search results"
-test_emacs '(notmuch-hello)
-           (goto-char (point-min))
-           (re-search-forward "inbox")
-           (widget-button-press (1- (point)))
-           (notmuch-test-wait)
-           (test-output)'
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-hello-view-inbox
-
-test_begin_subtest "Basic notmuch-show view in emacs"
-maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
-test_emacs "(notmuch-show \"$maildir_storage_thread\")
-           (test-output)"
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
-
-test_begin_subtest "Basic notmuch-show view in emacs default indentation"
-maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
-test_emacs "(let ((notmuch-show-indent-messages-width 1))
-             (notmuch-show \"$maildir_storage_thread\")
-             (test-output))"
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
-
-test_begin_subtest "Basic notmuch-show view in emacs without indentation"
-maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
-test_emacs "(let ((notmuch-show-indent-messages-width 0))
-             (notmuch-show \"$maildir_storage_thread\")
-             (test-output))"
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage-without-indentation
-
-test_begin_subtest "Basic notmuch-show view in emacs with fourfold indentation"
-maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
-test_emacs "(let ((notmuch-show-indent-messages-width 4))
-             (notmuch-show \"$maildir_storage_thread\")
-             (test-output))"
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage-with-fourfold-indentation
-
-test_begin_subtest "notmuch-show for message with invalid From"
-add_message "[subject]=\"message-with-invalid-from\"" \
-           "[from]=\"\\\"Invalid \\\" From\\\" <test_suite@notmuchmail.org>\""
-thread=$(notmuch search --output=threads subject:message-with-invalid-from)
-test_emacs "(notmuch-show \"$thread\")
-           (test-output \"OUTPUT.raw\")"
-cat <<EOF >EXPECTED
-"Invalid " (2001-01-05) (inbox)
-Subject: message-with-invalid-from
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: GENERATED_DATE
-
-This is just a test message (#1)
-EOF
-notmuch_date_sanitize < OUTPUT.raw > OUTPUT
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Navigation of notmuch-search to thread view"
-test_emacs '(notmuch-search "tag:inbox")
-           (notmuch-test-wait)
-           (goto-char (point-min))
-           (re-search-forward "Working with Maildir")
-           (notmuch-search-show-thread)
-           (notmuch-test-wait)
-           (test-output)'
-test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
-
-test_begin_subtest "Add tag from search view"
-os_x_darwin_thread=$(notmuch search --output=threads id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com)
-test_emacs "(notmuch-search \"$os_x_darwin_thread\")
-           (notmuch-test-wait)
-           (execute-kbd-macro \"+tag-from-search-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-search-view unread)"
-
-test_begin_subtest "Remove tag from search view"
-test_emacs "(notmuch-search \"$os_x_darwin_thread\")
-           (notmuch-test-wait)
-           (execute-kbd-macro \"-tag-from-search-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
-
-test_begin_subtest "Add tag (large query)"
-# We use a long query to force us into batch mode and use a funny tag
-# that requires escaping for batch tagging.
-test_emacs "(notmuch-tag (concat \"$os_x_darwin_thread\" \" or \" (make-string notmuch-tag-argument-limit ?x)) (list \"+tag-from-%-large-query\"))"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-%-large-query unread)"
-notmuch tag -tag-from-%-large-query $os_x_darwin_thread
-
-test_begin_subtest "notmuch-show: add single tag to single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-           (execute-kbd-macro \"+tag-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-show-view unread)"
-
-test_begin_subtest "notmuch-show: remove single tag from single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-           (execute-kbd-macro \"-tag-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
-
-test_begin_subtest "notmuch-show: add multiple tags to single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-           (execute-kbd-macro \"+tag1-from-show-view +tag2-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag1-from-show-view tag2-from-show-view unread)"
-
-test_begin_subtest "notmuch-show: remove multiple tags from single message"
-test_emacs "(notmuch-show \"$os_x_darwin_thread\")
-           (execute-kbd-macro \"-tag1-from-show-view -tag2-from-show-view\")"
-output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
-
-test_begin_subtest "Message with .. in Message-Id:"
-add_message [id]=123..456@example '[subject]="Message with .. in Message-Id"'
-test_emacs '(notmuch-search "id:\"123..456@example\"")
-           (notmuch-test-wait)
-           (execute-kbd-macro "+search-add")
-           (execute-kbd-macro "+search-remove")
-           (execute-kbd-macro "-search-remove")
-           (notmuch-show "id:\"123..456@example\"")
-           (notmuch-test-wait)
-           (execute-kbd-macro "+show-add")
-           (execute-kbd-macro "+show-remove")
-           (execute-kbd-macro "-show-remove")'
-output=$(notmuch search 'id:"123..456@example"' | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message with .. in Message-Id (inbox search-add show-add)"
-
-test_begin_subtest "Message with quote in Message-Id:"
-add_message '[id]="\"quote\"@example"' '[subject]="Message with quote in Message-Id"'
-test_emacs '(notmuch-search "subject:\"Message with quote\"")
-           (notmuch-test-wait)
-           (execute-kbd-macro "+search-add")
-            (notmuch-search-show-thread)
-           (notmuch-test-wait)
-           (execute-kbd-macro "+show-add")'
-output=$(notmuch search 'id:"""quote""@example"' | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message with quote in Message-Id (inbox search-add show-add)"
-
-test_begin_subtest "Sending a message via (fake) SMTP"
-emacs_deliver_message \
-    'Testing message sent via SMTP' \
-    'This is a test that messages are sent via SMTP' \
-    '(message-goto-to)
-     (kill-whole-line)
-     (insert "To: user@example.com\n")'
-sed \
-    -e s',^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' \
-    -e s',^Message-ID: <.*>$,Message-ID: <XXX>,' \
-    -e s',^\(Content-Type: text/plain\); charset=us-ascii$,\1,' < sent_message >OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: user@example.com
-Subject: Testing message sent via SMTP
-Date: 01 Jan 2000 12:00:00 -0000
-User-Agent: Notmuch/XXX Emacs/XXX
-Message-ID: <XXX>
-MIME-Version: 1.0
-Content-Type: text/plain
-
-This is a test that messages are sent via SMTP
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Verify that sent messages are saved/searchable (via FCC)"
-notmuch new > /dev/null
-output=$(notmuch search 'subject:"testing message sent via SMTP"' | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; Testing message sent via SMTP (inbox)"
-
-test_begin_subtest "notmuch-fcc-dirs set to nil"
-test_emacs "(let ((notmuch-fcc-dirs nil))
-             (notmuch-mua-mail)
-             (test-output))"
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
-Subject: 
---text follows this line--
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-# Make another FCC maildir specific for the next test
-mkdir -p mail/sent-string/cur
-mkdir -p mail/sent-string/new
-mkdir -p mail/sent-string/tmp
-
-test_begin_subtest "notmuch-fcc-dirs set to a string"
-test_emacs "(let ((notmuch-fcc-dirs \"sent-string\"))
-             (notmuch-mua-mail)
-             (test-output))"
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
-Subject: 
-Fcc: ${MAIL_DIR}/sent-string
---text follows this line--
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-# Make more FCC maildirs specific for the next test
-mkdir -p mail/sent-list-match/cur
-mkdir -p mail/sent-list-match/new
-mkdir -p mail/sent-list-match/tmp
-mkdir -p mail/failure/cur
-mkdir -p mail/failure/new
-mkdir -p mail/failure/tmp
-
-test_begin_subtest "notmuch-fcc-dirs set to a list (with match)"
-test_emacs "(let ((notmuch-fcc-dirs
-                  '((\"notmuchmail.org\" . \"sent-list-match\")
-                    (\".*\" . \"failure\"))))
-             (notmuch-mua-mail)
-             (test-output))"
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
-Subject: 
-Fcc: ${MAIL_DIR}/sent-list-match
---text follows this line--
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-# Make another FCC maildir specific for the next test
-mkdir -p mail/sent-list-catch-all/cur
-mkdir -p mail/sent-list-catch-all/new
-mkdir -p mail/sent-list-catch-all/tmp
-
-test_begin_subtest "notmuch-fcc-dirs set to a list (catch-all)"
-test_emacs "(let ((notmuch-fcc-dirs
-                  '((\"example.com\" . \"failure\")
-                    (\".*\" . \"sent-list-catch-all\"))))
-             (notmuch-mua-mail)
-             (test-output))"
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
-Subject: 
-Fcc: ${MAIL_DIR}/sent-list-catch-all
---text follows this line--
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "notmuch-fcc-dirs set to a list (no match)"
-test_emacs "(let ((notmuch-fcc-dirs
-                  '((\"example.com\" . \"failure\")
-                    (\"nomatchhere.net\" . \"failure\"))))
-             (notmuch-mua-mail)
-             (test-output))"
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
-Subject: 
---text follows this line--
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Reply within emacs"
-test_emacs '(let ((message-hidden-headers ''()))
-           (notmuch-search "subject:\"testing message sent via SMTP\"")
-           (notmuch-test-wait)
-           (notmuch-search-reply-to-thread)
-           (test-output))'
-sed -i -e 's/^In-Reply-To: <.*>$/In-Reply-To: <XXX>/' OUTPUT
-sed -i -e 's/^References: <.*>$/References: <XXX>/' OUTPUT
-sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: user@example.com
-Subject: Re: Testing message sent via SMTP
-In-Reply-To: <XXX>
-Fcc: ${MAIL_DIR}/sent
-References: <XXX>
-User-Agent: Notmuch/XXX Emacs/XXX
---text follows this line--
-Notmuch Test Suite <test_suite@notmuchmail.org> writes:
-
-> This is a test that messages are sent via SMTP
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Reply from alternate address within emacs"
-add_message '[from]="Sender <sender@example.com>"' \
-            [to]=test_suite_other@notmuchmail.org
-
-test_emacs "(let ((message-hidden-headers '()))
-           (notmuch-search \"id:\\\"${gen_msg_id}\\\"\")
-           (notmuch-test-wait)
-           (notmuch-search-reply-to-thread)
-           (test-output))"
-sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
-To: Sender <sender@example.com>
-Subject: Re: ${test_subtest_name}
-In-Reply-To: <${gen_msg_id}>
-Fcc: ${MAIL_DIR}/sent
-References: <${gen_msg_id}>
-User-Agent: Notmuch/XXX Emacs/XXX
---text follows this line--
-Sender <sender@example.com> writes:
-
-> This is just a test message (#${gen_msg_cnt})
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Reply from address in named group list within emacs"
-add_message '[from]="Sender <sender@example.com>"' \
-            '[to]=group:test_suite@notmuchmail.org,someone@example.com\;' \
-             [cc]=test_suite_other@notmuchmail.org
-
-test_emacs "(let ((message-hidden-headers '()))
-           (notmuch-search \"id:\\\"${gen_msg_id}\\\"\")
-           (notmuch-test-wait)
-           (notmuch-search-reply-to-thread)
-           (test-output))"
-sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Sender <sender@example.com>, someone@example.com
-Subject: Re: ${test_subtest_name}
-In-Reply-To: <${gen_msg_id}>
-Fcc: ${MAIL_DIR}/sent
-References: <${gen_msg_id}>
-User-Agent: Notmuch/XXX Emacs/XXX
---text follows this line--
-Sender <sender@example.com> writes:
-
-> This is just a test message (#${gen_msg_cnt})
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Reply within emacs to a multipart/mixed message"
-test_emacs '(let ((message-hidden-headers ''()))
-           (notmuch-show "id:20091118002059.067214ed@hikari")
-               (notmuch-show-reply)
-               (test-output))'
-sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Adrian Perez de Castro <aperez@igalia.com>, notmuch@notmuchmail.org
-Subject: Re: [notmuch] Introducing myself
-In-Reply-To: <20091118002059.067214ed@hikari>
-Fcc: ${MAIL_DIR}/sent
-References: <20091118002059.067214ed@hikari>
-User-Agent: Notmuch/XXX Emacs/XXX
---text follows this line--
-Adrian Perez de Castro <aperez@igalia.com> writes:
-
-> Hello to all,
->
-> I have just heard about Not Much today in some random Linux-related news
-> site (LWN?), my name is Adrian Perez and I work as systems administrator
-> (although I can do some code as well :P). I have always thought that the
-> ideas behind Sup were great, but after some time using it, I got tired of
-> the oddities that it has. I also do not like doing things like having to
-> install Ruby just for reading and sorting mails. Some time ago I thought
-> about doing something like Not Much and in fact I played a bit with the
-> Python+Xapian and the Python+Whoosh combinations, because I find relaxing
-> to code things in Python when I am not working and also it is installed
-> by default on most distribution. I got to have some mailboxes indexed and
-> basic searching working a couple of months ago. Lately I have been very
-> busy and had no time for coding, and them... boom! Not Much appears -- and
-> it is almost exactly what I was trying to do, but faster. I have been
-> playing a bit with Not Much today, and I think it has potential.
->
-> Also, I would like to share one idea I had in mind, that you might find
-> interesting: One thing I have found very annoying is having to re-tag my
-> mail when the indexes get b0rked (it happened a couple of times to me while
-> using Sup), so I was planning to mails as read/unread and adding the tags
-> not just to the index, but to the mail text itself, e.g. by adding a
-> "X-Tags" header field or by reusing the "Keywords" one. This way, the index
-> could be totally recreated by re-reading the mail directories, and this
-> would also allow to a tools like OfflineIMAP [1] to get the mails into a
-> local maildir, tagging and indexing the mails with the e-mail reader and
-> then syncing back the messages with the "X-Tags" header to the IMAP server.
-> This would allow to use the mail reader from a different computer and still
-> have everything tagged finely.
->
-> Best regards,
->
->
-> ---
-> [1] http://software.complete.org/software/projects/show/offlineimap
->
-> -- 
-> Adrian Perez de Castro <aperez@igalia.com>
-> Igalia - Free Software Engineering
-> _______________________________________________
-> notmuch mailing list
-> notmuch@notmuchmail.org
-> http://notmuchmail.org/mailman/listinfo/notmuch
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Reply within emacs to a multipart/alternative message"
-test_emacs '(let ((message-hidden-headers ''()))
-           (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
-               (notmuch-show-reply)
-               (test-output))'
-sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Alex Botero-Lowry <alex.boterolowry@gmail.com>, notmuch@notmuchmail.org
-Subject: Re: [notmuch] preliminary FreeBSD support
-In-Reply-To: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
-Fcc: ${MAIL_DIR}/sent
-References: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
-User-Agent: Notmuch/XXX Emacs/XXX
---text follows this line--
-Alex Botero-Lowry <alex.boterolowry@gmail.com> writes:
-
-> I saw the announcement this morning, and was very excited, as I had been
-> hoping sup would be turned into a library,
-> since I like the concept more than the UI (I'd rather an emacs interface).
->
-> I did a preliminary compile which worked out fine, but
-> sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on
-> FreeBSD, so notmuch_config_open segfaulted.
->
-> Attached is a patch that supplies a default buffer size of 64 in cases where
-> -1 is returned.
->
-> http://www.opengroup.org/austin/docs/austin_328.txt - seems to indicate this
-> is acceptable behavior,
-> and http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically
-> uses 64 as the
-> buffer size.
-> _______________________________________________
-> notmuch mailing list
-> notmuch@notmuchmail.org
-> http://notmuchmail.org/mailman/listinfo/notmuch
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Reply within emacs to an html-only message"
-add_message '[content-type]="text/html"' \
-           '[body]="Hi,<br />This is an <b>HTML</b> test message.<br /><br />OK?"'
-test_emacs "(let ((message-hidden-headers '()) (mm-text-html-renderer 'html2text))
-           (notmuch-show \"id:${gen_msg_id}\")
-           (notmuch-show-reply)
-           (test-output))"
-sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT
-cat <<EOF >EXPECTED
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
-Subject: Re: Reply within emacs to an html-only message
-In-Reply-To: <${gen_msg_id}>
-Fcc: ${MAIL_DIR}/sent
-References: <${gen_msg_id}>
-User-Agent: Notmuch/XXX Emacs/XXX
---text follows this line--
-Notmuch Test Suite <test_suite@notmuchmail.org> writes:
-
-> Hi,This is an HTML test message.OK?
-EOF
-test_expect_equal_file OUTPUT EXPECTED
-
-test_begin_subtest "Quote MML tags in reply"
-message_id='test-emacs-mml-quoting@message.id'
-add_message [id]="$message_id" \
-           "[subject]='$test_subtest_name'" \
-           '[body]="<#part disposition=inline>"'
-test_emacs "(let ((message-hidden-headers '()))
-       &nbs