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