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
-echo ./attachment1.gz |
-test_emacs '(notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
-           (notmuch-show-save-attachments)' > /dev/null 2>&1
+test_emacs '(let ((standard-input "\"attachment1.gz\""))
+             (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
+             (notmuch-show-save-attachments))' > /dev/null 2>&1
 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
-echo ./attachment2.gz |
-test_emacs '(notmuch-show-save-part "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com" 5)' > /dev/null 2>&1
+test_emacs '(let ((standard-input "\"attachment2.gz\""))
+             (notmuch-show-save-part "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com" 5))' > /dev/null 2>&1
 test_expect_equal_file attachment2.gz "$EXPECTED/attachment"
 
 test_begin_subtest "View raw message within emacs"
 
 ;; avoid crazy 10-column default of --batch
 (set-frame-width (window-frame (get-buffer-window)) 80)
 
+;; `read-file-name' by default uses `completing-read' function to read
+;; user input.  It does not respect `standard-input' variable which we
+;; use in tests to provide user input.  So replace it with a plain
+;; `read' call.
+(setq read-file-name-function (lambda (&rest _) (read)))
+
 (defun notmuch-test-wait ()
   "Wait for process completion."
   (while (get-buffer-process (current-buffer))
          (setq str (concat str (buffer-substring start next-pos))))
        (setq start next-pos)))
     str))
+
+(defun orphan-watchdog (pid)
+  "Periodically check that the process with id PID is still
+running, quit if it terminated."
+  (if (not (process-attributes pid))
+      (kill-emacs)
+    (run-at-time "1 min" nil 'orphan-watchdog pid)))
 
 
        echo
 
+       [ -n "$EMACS_SERVER" ] && test_emacs '(kill-emacs)'
+
        if [ "$test_failure" = "0" ]; then
            if [ "$test_broken" = "0" ]; then       
                rm -rf "$remove_tmp"
 export PATH=$PATH
 export NOTMUCH_CONFIG=$NOTMUCH_CONFIG
 
-# We assume that the user will give a command-line argument only if
-# wanting to run in batch mode.
-if [ \$# -gt 0 ]; then
-       BATCH=--batch
-fi
-
 # Here's what we are using here:
 #
-# --batch:             Quit after given commands and print all (messages)
-#
 # --no-init-file       Don't load users ~/.emacs
 #
 # --no-site-file       Don't load the site-wide startup stuff
 #
 # --load               Force loading of notmuch.el and test-lib.el
 
-emacs \$BATCH --no-init-file --no-site-file \
+emacs --no-init-file --no-site-file \
        --directory "$TEST_DIRECTORY/../emacs" --load notmuch.el \
        --directory "$TEST_DIRECTORY" --load test-lib.el \
-       --eval "(progn \$@)"
+       "\$@"
 EOF
        chmod a+x "$TMP_DIRECTORY/run_emacs"
 }
 
 test_emacs () {
-       "$TMP_DIRECTORY/run_emacs" "$@"
+       if [ -z "$EMACS_SERVER" ]; then
+               EMACS_SERVER="notmuch-test-suite-$$"
+               "$TMP_DIRECTORY/run_emacs" \
+                       --daemon \
+                       --eval "(setq server-name \"$EMACS_SERVER\")" \
+                       --eval "(orphan-watchdog $$)" || return
+       fi
+
+       emacsclient --socket-name="$EMACS_SERVER" --eval "(progn $@)"
 }