]> git.notmuchmail.org Git - notmuch/blob - test/test-lib.sh
tests: prepare for more crypto tests (using add_gnupg_home)
[notmuch] / test / test-lib.sh
1 #
2 # Copyright (c) 2005 Junio C Hamano
3 # Copyright (c) 2010 Notmuch Developers
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see https://www.gnu.org/licenses/ .
17
18 if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
19     echo "Error: The notmuch test suite requires a bash version >= 4.0"
20     echo "due to use of associative arrays within the test suite."
21     echo "Please try again with a newer bash (or help us fix the"
22     echo "test suite to be more portable). Thanks."
23     exit 1
24 fi
25
26 # Make sure echo builtin does not expand backslash-escape sequences by default.
27 shopt -u xpg_echo
28
29 this_test=${0##*/}
30 this_test=${this_test%.sh}
31 this_test_bare=${this_test#T[0-9][0-9][0-9]-}
32
33 # if --tee was passed, write the output not only to the terminal, but
34 # additionally to the file test-results/$BASENAME.out, too.
35 case "$GIT_TEST_TEE_STARTED, $* " in
36 done,*)
37         # do not redirect again
38         ;;
39 *' --tee '*|*' --va'*)
40         mkdir -p test-results
41         BASE=test-results/$this_test
42         (GIT_TEST_TEE_STARTED=done "$BASH" "$0" "$@" 2>&1;
43          echo $? > $BASE.exit) | tee $BASE.out
44         test "$(cat $BASE.exit)" = 0
45         exit
46         ;;
47 esac
48
49 # Save STDOUT to fd 6 and STDERR to fd 7.
50 exec 6>&1 7>&2
51 # Make xtrace debugging (when used) use redirected STDERR, with verbose lead:
52 BASH_XTRACEFD=7
53 export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
54
55 # Keep the original TERM for say_color and test_emacs
56 ORIGINAL_TERM=$TERM
57
58 # Set SMART_TERM to vt100 for known dumb/unknown terminal.
59 # Otherwise use whatever TERM is currently used so that
60 # users' actual TERM environments are being used in tests.
61 case ${TERM-} in
62         '' | dumb | unknown )
63                 SMART_TERM=vt100 ;;
64         *)
65                 SMART_TERM=$TERM ;;
66 esac
67
68 # For repeatability, reset the environment to known value.
69 LANG=C
70 LC_ALL=C
71 PAGER=cat
72 TZ=UTC
73 TERM=dumb
74 export LANG LC_ALL PAGER TERM TZ
75 GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
76 if [[ ( -n "$TEST_EMACS" && -z "$TEST_EMACSCLIENT" ) || \
77       ( -z "$TEST_EMACS" && -n "$TEST_EMACSCLIENT" ) ]]; then
78     echo "error: must specify both or neither of TEST_EMACS and TEST_EMACSCLIENT" >&2
79     exit 1
80 fi
81 TEST_EMACS=${TEST_EMACS:-${EMACS:-emacs}}
82 TEST_EMACSCLIENT=${TEST_EMACSCLIENT:-emacsclient}
83 TEST_GDB=${TEST_GDB:-gdb}
84 TEST_CC=${TEST_CC:-cc}
85 TEST_CFLAGS=${TEST_CFLAGS:-"-g -O0"}
86
87 # Protect ourselves from common misconfiguration to export
88 # CDPATH into the environment
89 unset CDPATH
90
91 unset GREP_OPTIONS
92
93 # For emacsclient
94 unset ALTERNATE_EDITOR
95
96 add_gnupg_home ()
97 {
98     local output
99     [ -d ${GNUPGHOME} ] && return
100     _gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
101     at_exit_function _gnupg_exit
102     mkdir -m 0700 "$GNUPGHOME"
103     gpg --no-tty --import <$TEST_DIRECTORY/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
104     test_debug "cat $GNUPGHOME/import.log"
105     if (gpg --quick-random --version >/dev/null 2>&1) ; then
106         echo quick-random >> "$GNUPGHOME"/gpg.conf
107     elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then
108         echo debug-quick-random >> "$GNUPGHOME"/gpg.conf
109     fi
110     echo no-emit-version >> "$GNUPGHOME"/gpg.conf
111 }
112
113 # Each test should start with something like this, after copyright notices:
114 #
115 # test_description='Description of this test...
116 # This test checks if command xyzzy does the right thing...
117 # '
118 # . ./test-lib.sh || exit 1
119
120 [ "x$ORIGINAL_TERM" != "xdumb" ] && (
121                 TERM=$ORIGINAL_TERM &&
122                 export TERM &&
123                 [ -t 1 ] &&
124                 tput bold >/dev/null 2>&1 &&
125                 tput setaf 1 >/dev/null 2>&1 &&
126                 tput sgr0 >/dev/null 2>&1
127         ) &&
128         color=t
129
130 while test "$#" -ne 0
131 do
132         case "$1" in
133         -d|--debug)
134                 debug=t; shift ;;
135         -i|--immediate)
136                 immediate=t; shift ;;
137         -h|--help)
138                 help=t; shift ;;
139         -v|--verbose)
140                 verbose=t; shift ;;
141         -q|--quiet)
142                 quiet=t; shift ;;
143         --with-dashes)
144                 with_dashes=t; shift ;;
145         --no-color)
146                 color=; shift ;;
147         --no-python)
148                 # noop now...
149                 shift ;;
150         --valgrind)
151                 valgrind=t; verbose=t; shift ;;
152         --tee)
153                 shift ;; # was handled already
154         --root=*)
155                 root=$(expr "z$1" : 'z[^=]*=\(.*\)')
156                 shift ;;
157         *)
158                 echo "error: unknown test option '$1'" >&2; exit 1 ;;
159         esac
160 done
161
162 if test -n "$debug"; then
163     print_subtest () {
164         printf " %-4s" "[$((test_count - 1))]"
165     }
166 else
167     print_subtest () {
168         true
169     }
170 fi
171
172 if test -n "$color"; then
173         say_color () {
174                 (
175                 TERM=$ORIGINAL_TERM
176                 export TERM
177                 case "$1" in
178                         error) tput bold; tput setaf 1;; # bold red
179                         skip)  tput bold; tput setaf 2;; # bold green
180                         pass)  tput setaf 2;;            # green
181                         info)  tput setaf 3;;            # brown
182                         *) test -n "$quiet" && return;;
183                 esac
184                 shift
185                 printf " "
186                 printf "$@"
187                 tput sgr0
188                 print_subtest
189                 )
190         }
191 else
192         say_color() {
193                 test -z "$1" && test -n "$quiet" && return
194                 shift
195                 printf " "
196                 printf "$@"
197                 print_subtest
198         }
199 fi
200
201 error () {
202         say_color error "error: $*\n"
203         GIT_EXIT_OK=t
204         exit 1
205 }
206
207 say () {
208         say_color info "$*"
209 }
210
211 test "${test_description}" != "" ||
212 error "Test script did not set test_description."
213
214 if test "$help" = "t"
215 then
216         echo "Tests ${test_description}"
217         exit 0
218 fi
219
220 test_description_printed=
221 print_test_description ()
222 {
223         test -z "$test_description_printed" || return 0
224         echo
225         echo $this_test: "Testing ${test_description}"
226         test_description_printed=1
227 }
228 if [ -z "$NOTMUCH_TEST_QUIET" ]
229 then
230         print_test_description
231 fi
232
233 test_failure=0
234 test_count=0
235 test_fixed=0
236 test_broken=0
237 test_success=0
238
239 declare -a _exit_functions=()
240
241 at_exit_function () {
242         _exit_functions=($1 ${_exit_functions[@]/$1})
243 }
244
245 rm_exit_function () {
246         _exit_functions=(${_exit_functions[@]/$1})
247 }
248
249 _exit_common () {
250         code=$?
251         trap - EXIT
252         set +ex
253         for _fn in ${_exit_functions[@]}; do $_fn; done
254         rm -rf "$TEST_TMPDIR"
255 }
256
257 trap_exit () {
258         _exit_common
259         if test -n "$GIT_EXIT_OK"
260         then
261                 exit $code
262         else
263                 exec >&6
264                 say_color error '%-6s' FATAL
265                 echo " $test_subtest_name"
266                 echo
267                 echo "Unexpected exit while executing $0. Exit code $code."
268                 exit 1
269         fi
270 }
271
272 trap_signal () {
273         _exit_common
274         echo >&6 "FATAL: $0: interrupted by signal" $((code - 128))
275         exit $code
276 }
277
278 die () {
279         _exit_common
280         exec >&6
281         say_color error '%-6s' FATAL
282         echo " $*"
283         echo
284         echo "Unexpected exit while executing $0."
285         exit 1
286 }
287
288 GIT_EXIT_OK=
289 # Note: TEST_TMPDIR *NOT* exported!
290 TEST_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-test-$$.XXXXXX")
291 # Put GNUPGHOME in TMPDIR to avoid problems with long paths.
292 export GNUPGHOME="${TEST_TMPDIR}/gnupg"
293 trap 'trap_exit' EXIT
294 trap 'trap_signal' HUP INT TERM
295
296 # Deliver a message with emacs and add it to the database
297 #
298 # Uses emacs to generate and deliver a message to the mail store.
299 # Accepts arbitrary extra emacs/elisp functions to modify the message
300 # before sending, which is useful to doing things like attaching files
301 # to the message and encrypting/signing.
302 emacs_deliver_message ()
303 {
304     local subject="$1"
305     local body="$2"
306     shift 2
307     # before we can send a message, we have to prepare the FCC maildir
308     mkdir -p "$MAIL_DIR"/sent/{cur,new,tmp}
309     # eval'ing smtp-dummy --background will set smtp_dummy_pid
310     smtp_dummy_pid=
311     eval `$TEST_DIRECTORY/smtp-dummy --background sent_message`
312     test -n "$smtp_dummy_pid" || return 1
313
314     test_emacs \
315         "(let ((message-send-mail-function 'message-smtpmail-send-it)
316                (mail-host-address \"example.com\")
317                (smtpmail-smtp-server \"localhost\")
318                (smtpmail-smtp-service \"25025\"))
319            (notmuch-mua-mail)
320            (message-goto-to)
321            (insert \"test_suite@notmuchmail.org\nDate: 01 Jan 2000 12:00:00 -0000\")
322            (message-goto-subject)
323            (insert \"${subject}\")
324            (message-goto-body)
325            (insert \"${body}\")
326            $@
327            (notmuch-mua-send-and-exit))"
328
329     # In case message was sent properly, client waits for confirmation
330     # before exiting and resuming control here; therefore making sure
331     # that server exits by sending (KILL) signal to it is safe.
332     kill -9 $smtp_dummy_pid
333     notmuch new >/dev/null
334 }
335
336 # Pretend to deliver a message with emacs. Really save it to a file
337 # and add it to the database
338 #
339 # Uses emacs to generate and deliver a message to the mail store.
340 # Accepts arbitrary extra emacs/elisp functions to modify the message
341 # before sending, which is useful to doing things like attaching files
342 # to the message and encrypting/signing.
343 emacs_fcc_message ()
344 {
345     local subject="$1"
346     local body="$2"
347     shift 2
348     # before we can send a message, we have to prepare the FCC maildir
349     mkdir -p "$MAIL_DIR"/sent/{cur,new,tmp}
350
351     test_emacs \
352         "(let ((message-send-mail-function (lambda () t))
353                (mail-host-address \"example.com\"))
354            (notmuch-mua-mail)
355            (message-goto-to)
356            (insert \"test_suite@notmuchmail.org\nDate: 01 Jan 2000 12:00:00 -0000\")
357            (message-goto-subject)
358            (insert \"${subject}\")
359            (message-goto-body)
360            (insert \"${body}\")
361            $@
362            (notmuch-mua-send-and-exit))" || return 1
363     notmuch new >/dev/null
364 }
365
366 # Add an existing, fixed corpus of email to the database.
367 #
368 # $1 is the corpus dir under corpora to add, using "default" if unset.
369 #
370 # The default corpus is based on about 50 messages from early in the
371 # history of the notmuch mailing list, which allows for reliably
372 # testing commands that need to operate on a not-totally-trivial
373 # number of messages.
374 add_email_corpus ()
375 {
376     corpus=${1:-default}
377
378     rm -rf ${MAIL_DIR}
379     if [ -d $TEST_DIRECTORY/corpora.mail/$corpus ]; then
380         cp -a $TEST_DIRECTORY/corpora.mail/$corpus ${MAIL_DIR}
381     else
382         cp -a $TEST_DIRECTORY/corpora/$corpus ${MAIL_DIR}
383         notmuch new >/dev/null || die "'notmuch new' failed while adding email corpus"
384         mkdir -p $TEST_DIRECTORY/corpora.mail
385         cp -a ${MAIL_DIR} $TEST_DIRECTORY/corpora.mail/$corpus
386     fi
387 }
388
389 test_begin_subtest ()
390 {
391     if [ -n "$inside_subtest" ]; then
392         exec 1>&6 2>&7          # Restore stdout and stderr
393         error "bug in test script: Missing test_expect_equal in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}"
394     fi
395     test_subtest_name="$1"
396     test_reset_state_
397     # Redirect test output to the previously prepared file descriptors
398     # 3 and 4 (see below)
399     if test "$verbose" != "t"; then exec 4>test.output 3>&4; fi
400     exec >&3 2>&4
401     inside_subtest=t
402 }
403
404 # Pass test if two arguments match
405 #
406 # Note: Unlike all other test_expect_* functions, this function does
407 # not accept a test name. Instead, the caller should call
408 # test_begin_subtest before calling this function in order to set the
409 # name.
410 test_expect_equal ()
411 {
412         exec 1>&6 2>&7          # Restore stdout and stderr
413         if [ -z "$inside_subtest" ]; then
414                 error "bug in the test script: test_expect_equal without test_begin_subtest"
415         fi
416         inside_subtest=
417         test "$#" = 2 ||
418         error "bug in the test script: not 2 parameters to test_expect_equal"
419
420         output="$1"
421         expected="$2"
422         if ! test_skip "$test_subtest_name"
423         then
424                 if [ "$output" = "$expected" ]; then
425                         test_ok_
426                 else
427                         testname=$this_test.$test_count
428                         echo "$expected" > $testname.expected
429                         echo "$output" > $testname.output
430                         test_failure_ "$(diff -u $testname.expected $testname.output)"
431                 fi
432     fi
433 }
434
435 # Like test_expect_equal, but takes two filenames.
436 test_expect_equal_file ()
437 {
438         exec 1>&6 2>&7          # Restore stdout and stderr
439         if [ -z "$inside_subtest" ]; then
440                 error "bug in the test script: test_expect_equal_file without test_begin_subtest"
441         fi
442         inside_subtest=
443         test "$#" = 2 ||
444         error "bug in the test script: not 2 parameters to test_expect_equal_file"
445
446         file1="$1"
447         file2="$2"
448         if ! test_skip "$test_subtest_name"
449         then
450                 if diff -q "$file1" "$file2" >/dev/null ; then
451                         test_ok_
452                 else
453                         testname=$this_test.$test_count
454                         basename1=`basename "$file1"`
455                         basename2=`basename "$file2"`
456                         cp "$file1" "$testname.$basename1"
457                         cp "$file2" "$testname.$basename2"
458                         test_failure_ "$(diff -u "$testname.$basename1" "$testname.$basename2")"
459                 fi
460     fi
461 }
462
463 # Like test_expect_equal, but arguments are JSON expressions to be
464 # canonicalized before diff'ing.  If an argument cannot be parsed, it
465 # is used unchanged so that there's something to diff against.
466 test_expect_equal_json () {
467     # The test suite forces LC_ALL=C, but this causes Python 3 to
468     # decode stdin as ASCII.  We need to read JSON in UTF-8, so
469     # override Python's stdio encoding defaults.
470     local script='import json, sys; json.dump(json.load(sys.stdin), sys.stdout, sort_keys=True, indent=4)'
471     output=$(echo "$1" | PYTHONIOENCODING=utf-8 $NOTMUCH_PYTHON -c "$script" \
472         || echo "$1")
473     expected=$(echo "$2" | PYTHONIOENCODING=utf-8 $NOTMUCH_PYTHON -c "$script" \
474         || echo "$2")
475     shift 2
476     test_expect_equal "$output" "$expected" "$@"
477 }
478
479 # Sort the top-level list of JSON data from stdin.
480 test_sort_json () {
481     PYTHONIOENCODING=utf-8 $NOTMUCH_PYTHON -c \
482         "import sys, json; json.dump(sorted(json.load(sys.stdin)),sys.stdout)"
483 }
484
485 test_emacs_expect_t () {
486         test "$#" = 1 ||
487         error "bug in the test script: not 1 parameter to test_emacs_expect_t"
488         if [ -z "$inside_subtest" ]; then
489                 error "bug in the test script: test_emacs_expect_t without test_begin_subtest"
490         fi
491
492         # Run the test.
493         if ! test_skip "$test_subtest_name"
494         then
495                 test_emacs "(notmuch-test-run $1)" >/dev/null
496
497                 # Restore state after the test.
498                 exec 1>&6 2>&7          # Restore stdout and stderr
499                 inside_subtest=
500
501                 # Report success/failure.
502                 result=$(cat OUTPUT)
503                 if [ "$result" = t ]
504                 then
505                         test_ok_
506                 else
507                         test_failure_ "${result}"
508                 fi
509         else
510                 # Restore state after the (non) test.
511                 exec 1>&6 2>&7          # Restore stdout and stderr
512                 inside_subtest=
513         fi
514 }
515
516 NOTMUCH_NEW ()
517 {
518     notmuch new "${@}" | grep -v -E -e '^Processed [0-9]*( total)? file|Found [0-9]* total file'
519 }
520
521 NOTMUCH_DUMP_TAGS ()
522 {
523     # this relies on the default format being batch-tag, otherwise some tests will break
524     notmuch dump --include=tags "${@}" | sed '/^#/d' | sort
525 }
526
527 notmuch_drop_mail_headers ()
528 {
529     $NOTMUCH_PYTHON -c '
530 import email, sys
531 msg = email.message_from_file(sys.stdin)
532 for hdr in sys.argv[1:]: del msg[hdr]
533 print(msg.as_string(False))
534 ' "$@"
535 }
536
537 notmuch_search_sanitize ()
538 {
539     perl -pe 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
540 }
541
542 notmuch_search_files_sanitize ()
543 {
544     notmuch_dir_sanitize
545 }
546
547 notmuch_dir_sanitize ()
548 {
549     sed -e "s,$MAIL_DIR,MAIL_DIR," -e "s,${PWD},CWD,g" "$@"
550 }
551
552 NOTMUCH_SHOW_FILENAME_SQUELCH='s,filename:.*/mail,filename:/XXX/mail,'
553 notmuch_show_sanitize ()
554 {
555     sed -e "$NOTMUCH_SHOW_FILENAME_SQUELCH"
556 }
557 notmuch_show_sanitize_all ()
558 {
559     sed \
560         -e 's| filename:.*| filename:XXXXX|' \
561         -e 's| id:[^ ]* | id:XXXXX |' | \
562         notmuch_date_sanitize
563 }
564
565 notmuch_json_show_sanitize ()
566 {
567     sed \
568         -e 's|"id": "[^"]*",|"id": "XXXXX",|g' \
569         -e 's|"Date": "Fri, 05 Jan 2001 [^"]*0000"|"Date": "GENERATED_DATE"|g' \
570         -e 's|"filename": "signature.asc",||g' \
571         -e 's|"filename": \["/[^"]*"\],|"filename": \["YYYYY"\],|g' \
572         -e 's|"timestamp": 97.......|"timestamp": 42|g' \
573         -e 's|"content-length": [1-9][0-9]*|"content-length": "NONZERO"|g'
574 }
575
576 notmuch_emacs_error_sanitize ()
577 {
578     local command=$1
579     shift
580     for file in "$@"; do
581         echo "=== $file ==="
582         cat "$file"
583     done | sed  \
584         -e 's/^\[.*\]$/[XXX]/' \
585         -e "s|^\(command: \)\{0,1\}/.*/$command|\1YYY/$command|"
586 }
587
588 notmuch_date_sanitize ()
589 {
590     sed \
591         -e 's/^Date: Fri, 05 Jan 2001 .*0000/Date: GENERATED_DATE/'
592 }
593
594 notmuch_uuid_sanitize ()
595 {
596     sed 's/[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}/UUID/g'
597 }
598
599 notmuch_built_with_sanitize ()
600 {
601     sed 's/^built_with[.]\(.*\)=.*$/built_with.\1=something/'
602 }
603
604 notmuch_config_sanitize ()
605 {
606     notmuch_dir_sanitize | notmuch_built_with_sanitize
607 }
608
609 # End of notmuch helper functions
610
611 # Use test_set_prereq to tell that a particular prerequisite is available.
612 #
613 # The prerequisite can later be checked for by using test_have_prereq.
614 #
615 # The single parameter is the prerequisite tag (a simple word, in all
616 # capital letters by convention).
617
618 test_set_prereq () {
619         satisfied="$satisfied$1 "
620 }
621 satisfied=" "
622
623 test_have_prereq () {
624         case $satisfied in
625         *" $1 "*)
626                 : yes, have it ;;
627         *)
628                 ! : nope ;;
629         esac
630 }
631
632 declare -A test_missing_external_prereq_
633 declare -A test_subtest_missing_external_prereq_
634
635 # declare prerequisite for the given external binary
636 test_declare_external_prereq () {
637         binary="$1"
638         test "$#" = 2 && name=$2 || name="$binary(1)"
639
640         if ! hash $binary 2>/dev/null; then
641                 test_missing_external_prereq_["${binary}"]=t
642                 eval "
643 $binary () {
644         test_subtest_missing_external_prereq_[\"${name}\"]=t
645         false
646 }"
647         fi
648 }
649
650 # Explicitly require external prerequisite.  Useful when binary is
651 # called indirectly (e.g. from emacs).
652 # Returns success if dependency is available, failure otherwise.
653 test_require_external_prereq () {
654         binary="$1"
655         if [[ ${test_missing_external_prereq_["${binary}"]} == t ]]; then
656                 # dependency is missing, call the replacement function to note it
657                 eval "$binary"
658         else
659                 true
660         fi
661 }
662
663 # You are not expected to call test_ok_ and test_failure_ directly, use
664 # the text_expect_* functions instead.
665
666 test_ok_ () {
667         if test "$test_subtest_known_broken_" = "t"; then
668                 test_known_broken_ok_
669                 return
670         fi
671         test_success=$(($test_success + 1))
672         if test -n "$NOTMUCH_TEST_QUIET"; then
673                 return 0
674         fi
675         say_color pass "%-6s" "PASS"
676         echo " $test_subtest_name"
677 }
678
679 test_failure_ () {
680         print_test_description
681         if test "$test_subtest_known_broken_" = "t"; then
682                 test_known_broken_failure_ "$@"
683                 return
684         fi
685         test_failure=$(($test_failure + 1))
686         test_failure_message_ "FAIL" "$test_subtest_name" "$@"
687         test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
688         return 1
689 }
690
691 test_failure_message_ () {
692         say_color error "%-6s" "$1"
693         echo " $2"
694         shift 2
695         if [ "$#" != "0" ]; then
696                 echo "$@" | sed -e 's/^/        /'
697         fi
698         if test "$verbose" != "t"; then cat test.output; fi
699 }
700
701 test_known_broken_ok_ () {
702         test_reset_state_
703         test_fixed=$(($test_fixed+1))
704         say_color pass "%-6s" "FIXED"
705         echo " $test_subtest_name"
706 }
707
708 test_known_broken_failure_ () {
709         test_reset_state_
710         test_broken=$(($test_broken+1))
711         if [ -z "$NOTMUCH_TEST_QUIET" ]; then
712                 test_failure_message_ "BROKEN" "$test_subtest_name" "$@"
713         else
714                 test_failure_message_ "BROKEN" "$test_subtest_name"
715         fi
716         return 1
717 }
718
719 test_debug () {
720         test "$debug" = "" || eval "$1"
721 }
722
723 test_run_ () {
724         test_cleanup=:
725         if test "$verbose" != "t"; then exec 4>test.output 3>&4; fi
726         eval >&3 2>&4 "$1"
727         eval_ret=$?
728         eval >&3 2>&4 "$test_cleanup"
729         return 0
730 }
731
732 test_skip () {
733         test_count=$(($test_count+1))
734         to_skip=
735         for skp in $NOTMUCH_SKIP_TESTS
736         do
737                 case $this_test.$test_count in
738                 $skp)
739                         to_skip=t
740                         break
741                 esac
742                 case $this_test_bare.$test_count in
743                 $skp)
744                         to_skip=t
745                         break
746                 esac
747         done
748         case "$to_skip" in
749         t)
750                 test_report_skip_ "$@"
751                 ;;
752         *)
753                 test_check_missing_external_prereqs_ "$@"
754                 ;;
755         esac
756 }
757
758 test_check_missing_external_prereqs_ () {
759         if [[ ${#test_subtest_missing_external_prereq_[@]} != 0 ]]; then
760                 say_color skip >&1 "missing prerequisites: "
761                 echo ${!test_subtest_missing_external_prereq_[@]} >&1
762                 test_report_skip_ "$@"
763         else
764                 false
765         fi
766 }
767
768 test_report_skip_ () {
769         test_reset_state_
770         say_color skip >&3 "skipping test:"
771         echo " $@" >&3
772         say_color skip "%-6s" "SKIP"
773         echo " $1"
774 }
775
776 test_subtest_known_broken () {
777         test_subtest_known_broken_=t
778 }
779
780 test_expect_success () {
781         exec 1>&6 2>&7          # Restore stdout and stderr
782         if [ -z "$inside_subtest" ]; then
783                 error "bug in the test script: test_expect_success without test_begin_subtest"
784         fi
785         inside_subtest=
786         test "$#" = 1 ||
787         error "bug in the test script: not 1 parameters to test_expect_success"
788
789         if ! test_skip "$test_subtest_name"
790         then
791                 test_run_ "$1"
792                 run_ret="$?"
793                 # test_run_ may update missing external prerequisites
794                 test_check_missing_external_prereqs_ "$@" ||
795                 if [ "$run_ret" = 0 -a "$eval_ret" = 0 ]
796                 then
797                         test_ok_
798                 else
799                         test_failure_ "$1"
800                 fi
801         fi
802 }
803
804 test_expect_code () {
805         exec 1>&6 2>&7          # Restore stdout and stderr
806         if [ -z "$inside_subtest" ]; then
807                 error "bug in the test script: test_expect_code without test_begin_subtest"
808         fi
809         inside_subtest=
810         test "$#" = 2 ||
811         error "bug in the test script: not 2 parameters to test_expect_code"
812
813         if ! test_skip "$test_subtest_name"
814         then
815                 test_run_ "$2"
816                 run_ret="$?"
817                 # test_run_ may update missing external prerequisites,
818                 test_check_missing_external_prereqs_ "$@" ||
819                 if [ "$run_ret" = 0 -a "$eval_ret" = "$1" ]
820                 then
821                         test_ok_
822                 else
823                         test_failure_ "exit code $eval_ret, expected $1" "$2"
824                 fi
825         fi
826 }
827
828 # This is not among top-level (test_expect_success)
829 # but is a prefix that can be used in the test script, like:
830 #
831 #       test_expect_success 'complain and die' '
832 #           do something &&
833 #           do something else &&
834 #           test_must_fail git checkout ../outerspace
835 #       '
836 #
837 # Writing this as "! git checkout ../outerspace" is wrong, because
838 # the failure could be due to a segv.  We want a controlled failure.
839
840 test_must_fail () {
841         "$@"
842         test $? -gt 0 -a $? -le 129 -o $? -gt 192
843 }
844
845 # test_cmp is a helper function to compare actual and expected output.
846 # You can use it like:
847 #
848 #       test_expect_success 'foo works' '
849 #               echo expected >expected &&
850 #               foo >actual &&
851 #               test_cmp expected actual
852 #       '
853 #
854 # This could be written as either "cmp" or "diff -u", but:
855 # - cmp's output is not nearly as easy to read as diff -u
856 # - not all diff versions understand "-u"
857
858 test_cmp() {
859         $GIT_TEST_CMP "$@"
860 }
861
862 # This function can be used to schedule some commands to be run
863 # unconditionally at the end of the test to restore sanity:
864 #
865 #       test_expect_success 'test core.capslock' '
866 #               git config core.capslock true &&
867 #               test_when_finished "git config --unset core.capslock" &&
868 #               hello world
869 #       '
870 #
871 # That would be roughly equivalent to
872 #
873 #       test_expect_success 'test core.capslock' '
874 #               git config core.capslock true &&
875 #               hello world
876 #               git config --unset core.capslock
877 #       '
878 #
879 # except that the greeting and config --unset must both succeed for
880 # the test to pass.
881
882 test_when_finished () {
883         test_cleanup="{ $*
884                 } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
885 }
886
887 test_done () {
888         GIT_EXIT_OK=t
889         test_results_dir="$TEST_DIRECTORY/test-results"
890         mkdir -p "$test_results_dir"
891         test_results_path="$test_results_dir/$this_test"
892
893         echo "total $test_count" >> $test_results_path
894         echo "success $test_success" >> $test_results_path
895         echo "fixed $test_fixed" >> $test_results_path
896         echo "broken $test_broken" >> $test_results_path
897         echo "failed $test_failure" >> $test_results_path
898         echo "" >> $test_results_path
899
900         [ -n "$EMACS_SERVER" ] && test_emacs '(kill-emacs)'
901
902         if [ "$test_failure" = "0" ]; then
903             if [ "$test_broken" = "0" ]; then
904                 rm -rf "$remove_tmp"
905             fi
906             exit 0
907         else
908             exit 1
909         fi
910 }
911
912 emacs_generate_script () {
913         # Construct a little test script here for the benefit of the user,
914         # (who can easily run "run_emacs" to get the same emacs environment
915         # for investigating any failures).
916         cat <<EOF >"$TMP_DIRECTORY/run_emacs"
917 #!/bin/sh
918 export PATH=$PATH
919 export NOTMUCH_CONFIG=$NOTMUCH_CONFIG
920
921 # Here's what we are using here:
922 #
923 # --quick              Use minimal customization. This implies --no-init-file,
924 #                      --no-site-file and (emacs 24) --no-site-lisp
925 #
926 # --directory           Ensure that the local elisp sources are found
927 #
928 # --load                Force loading of notmuch.el and test-lib.el
929
930 exec ${TEST_EMACS} --quick \
931         --directory "$TEST_DIRECTORY/../emacs" --load notmuch.el \
932         --directory "$TEST_DIRECTORY" --load test-lib.el \
933         "\$@"
934 EOF
935         chmod a+x "$TMP_DIRECTORY/run_emacs"
936 }
937
938 test_emacs () {
939         # test dependencies beforehand to avoid the waiting loop below
940         missing_dependencies=
941         test_require_external_prereq dtach || missing_dependencies=1
942         test_require_external_prereq emacs || missing_dependencies=1
943         test_require_external_prereq ${TEST_EMACSCLIENT} || missing_dependencies=1
944         test -z "$missing_dependencies" || return
945
946         if [ -z "$EMACS_SERVER" ]; then
947                 emacs_tests="${this_test_bare}.el"
948                 if [ -f "$TEST_DIRECTORY/$emacs_tests" ]; then
949                         load_emacs_tests="--eval '(load \"$emacs_tests\")'"
950                 else
951                         load_emacs_tests=
952                 fi
953                 server_name="notmuch-test-suite-$$"
954                 # start a detached session with an emacs server
955                 # user's TERM (or 'vt100' in case user's TERM is known dumb
956                 # or unknown) is given to dtach which assumes a minimally
957                 # VT100-compatible terminal -- and emacs inherits that
958                 TERM=$SMART_TERM dtach -n "$TEST_TMPDIR/emacs-dtach-socket.$$" \
959                         sh -c "stty rows 24 cols 80; exec '$TMP_DIRECTORY/run_emacs' \
960                                 --no-window-system \
961                                 $load_emacs_tests \
962                                 --eval '(setq server-name \"$server_name\")' \
963                                 --eval '(server-start)' \
964                                 --eval '(orphan-watchdog $$)'" || return
965                 EMACS_SERVER="$server_name"
966                 # wait until the emacs server is up
967                 until test_emacs '()' >/dev/null 2>/dev/null; do
968                         sleep 1
969                 done
970         fi
971
972         # Clear test-output output file.  Most Emacs tests end with a
973         # call to (test-output).  If the test code fails with an
974         # exception before this call, the output file won't get
975         # updated.  Since we don't want to compare against an output
976         # file from another test, so start out with an empty file.
977         rm -f OUTPUT
978         touch OUTPUT
979
980         ${TEST_EMACSCLIENT} --socket-name="$EMACS_SERVER" --eval "(notmuch-test-progn $@)"
981 }
982
983 test_python() {
984     # Note: if there is need to print debug information from python program,
985     # use stdout = os.fdopen(6, 'w') or stderr = os.fdopen(7, 'w')
986     PYTHONPATH="$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \
987         $NOTMUCH_PYTHON -B - > OUTPUT
988 }
989
990 test_ruby() {
991     MAIL_DIR=$MAIL_DIR ruby -I $TEST_DIRECTORY/../bindings/ruby> OUTPUT
992 }
993
994 test_C () {
995     exec_file="test${test_count}"
996     test_file="${exec_file}.c"
997     cat > ${test_file}
998     ${TEST_CC} ${TEST_CFLAGS} -I${TEST_DIRECTORY} -I${NOTMUCH_SRCDIR}/lib -o ${exec_file} ${test_file} -L${TEST_DIRECTORY}/../lib/ -lnotmuch -ltalloc
999     echo "== stdout ==" > OUTPUT.stdout
1000     echo "== stderr ==" > OUTPUT.stderr
1001     ./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr
1002     notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr > OUTPUT
1003 }
1004
1005
1006 # Creates a script that counts how much time it is executed and calls
1007 # notmuch.  $notmuch_counter_command is set to the path to the
1008 # generated script.  Use notmuch_counter_value() function to get the
1009 # current counter value.
1010 notmuch_counter_reset () {
1011         notmuch_counter_command="$TMP_DIRECTORY/notmuch_counter"
1012         if [ ! -x "$notmuch_counter_command" ]; then
1013                 notmuch_counter_state_path="$TMP_DIRECTORY/notmuch_counter.state"
1014                 cat >"$notmuch_counter_command" <<EOF || return
1015 #!/bin/sh
1016
1017 read count < "$notmuch_counter_state_path"
1018 echo \$((count + 1)) > "$notmuch_counter_state_path"
1019
1020 exec notmuch "\$@"
1021 EOF
1022                 chmod +x "$notmuch_counter_command" || return
1023         fi
1024
1025         echo 0 > "$notmuch_counter_state_path"
1026 }
1027
1028 # Returns the current notmuch counter value.
1029 notmuch_counter_value () {
1030         if [ -r "$notmuch_counter_state_path" ]; then
1031                 read count < "$notmuch_counter_state_path"
1032         else
1033                 count=0
1034         fi
1035         echo $count
1036 }
1037
1038 test_reset_state_ () {
1039         test -z "$test_init_done_" && test_init_
1040
1041         test_subtest_known_broken_=
1042         test_subtest_missing_external_prereq_=()
1043 }
1044
1045 # called once before the first subtest
1046 test_init_ () {
1047         test_init_done_=t
1048
1049         # skip all tests if there were external prerequisites missing during init
1050         test_check_missing_external_prereqs_ "all tests in $this_test" && test_done
1051 }
1052
1053
1054 . ./test-lib-common.sh || exit 1
1055
1056 if [ "${NOTMUCH_GMIME_MAJOR}" = 3 ]; then
1057     test_subtest_broken_gmime_3 () {
1058         test_subtest_known_broken
1059     }
1060     test_subtest_broken_gmime_2 () {
1061         true
1062     }
1063 else
1064     test_subtest_broken_gmime_3 () {
1065         true
1066     }
1067     test_subtest_broken_gmime_2 () {
1068         test_subtest_known_broken
1069     }
1070 fi
1071
1072 emacs_generate_script
1073
1074
1075 # Use -P to resolve symlinks in our working directory so that the cwd
1076 # in subprocesses like git equals our $PWD (for pathname comparisons).
1077 cd -P "$test" || error "Cannot set up test environment"
1078
1079 if test "$verbose" = "t"
1080 then
1081         exec 4>&2 3>&1
1082 else
1083         exec 4>test.output 3>&4
1084 fi
1085
1086 for skp in $NOTMUCH_SKIP_TESTS
1087 do
1088         to_skip=
1089         for skp in $NOTMUCH_SKIP_TESTS
1090         do
1091                 case "$this_test" in
1092                 $skp)
1093                         to_skip=t
1094                         break
1095                 esac
1096                 case "$this_test_bare" in
1097                 $skp)
1098                         to_skip=t
1099                         break
1100                 esac
1101         done
1102         case "$to_skip" in
1103         t)
1104                 say_color skip >&3 "skipping test $this_test altogether"
1105                 say_color skip "skip all tests in $this_test"
1106                 test_done
1107         esac
1108 done
1109
1110 # Provide an implementation of the 'yes' utility
1111 yes () {
1112         if test $# = 0
1113         then
1114                 y=y
1115         else
1116                 y="$*"
1117         fi
1118
1119         while echo "$y"
1120         do
1121                 :
1122         done
1123 }
1124
1125 # Fix some commands on Windows
1126 case $(uname -s) in
1127 *MINGW*)
1128         # Windows has its own (incompatible) sort and find
1129         sort () {
1130                 /usr/bin/sort "$@"
1131         }
1132         find () {
1133                 /usr/bin/find "$@"
1134         }
1135         sum () {
1136                 md5sum "$@"
1137         }
1138         # git sees Windows-style pwd
1139         pwd () {
1140                 builtin pwd -W
1141         }
1142         # no POSIX permissions
1143         # backslashes in pathspec are converted to '/'
1144         # exec does not inherit the PID
1145         ;;
1146 *)
1147         test_set_prereq POSIXPERM
1148         test_set_prereq BSLASHPSPEC
1149         test_set_prereq EXECKEEPSPID
1150         ;;
1151 esac
1152
1153 test -z "$NO_PERL" && test_set_prereq PERL
1154 test -z "$NO_PYTHON" && test_set_prereq PYTHON
1155
1156 # test whether the filesystem supports symbolic links
1157 ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
1158 rm -f y
1159
1160 # convert variable from configure to more convenient form
1161 case "$NOTMUCH_DEFAULT_XAPIAN_BACKEND" in
1162     glass)
1163         db_ending=glass
1164     ;;
1165     chert)
1166         db_ending=DB
1167     ;;
1168     *)
1169         error "Unknown Xapian backend $NOTMUCH_DEFAULT_XAPIAN_BACKEND"
1170 esac
1171 # declare prerequisites for external binaries used in tests
1172 test_declare_external_prereq dtach
1173 test_declare_external_prereq emacs
1174 test_declare_external_prereq ${TEST_EMACSCLIENT}
1175 test_declare_external_prereq ${TEST_GDB}
1176 test_declare_external_prereq gpg
1177 test_declare_external_prereq openssl
1178 test_declare_external_prereq gpgsm
1179 test_declare_external_prereq ${NOTMUCH_PYTHON}