]> git.notmuchmail.org Git - notmuch/blobdiff - test/test-lib.sh
emacs: Add new option notmuch-search-hide-excluded
[notmuch] / test / test-lib.sh
index c379d140ca1cb170770b44e7966c0d364e065acf..059e110c87d540bb5bdd8baca0ef6e2140a1bfc4 100644 (file)
@@ -29,8 +29,8 @@ shopt -u xpg_echo
 # Ensure NOTMUCH_SRCDIR and NOTMUCH_BUILDDIR are set.
 . $(dirname "$0")/export-dirs.sh || exit 1
 
-# It appears that people try to run tests without building...
-if [[ ! -x "$NOTMUCH_BUILDDIR/notmuch" ]]; then
+# We need either a built tree, or a promise of an installed notmuch
+if [ -z "${NOTMUCH_TEST_INSTALLED-}" -a ! -x "$NOTMUCH_BUILDDIR/notmuch" ]; then
        echo >&2 'You do not seem to have built notmuch yet.'
        exit 1
 fi
@@ -64,62 +64,14 @@ exec 6>&1 7>&2
 BASH_XTRACEFD=7
 export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
 
-# Keep the original TERM for say_color and test_emacs
-ORIGINAL_TERM=$TERM
-
-# Set SMART_TERM to vt100 for known dumb/unknown terminal.
-# Otherwise use whatever TERM is currently used so that
-# users' actual TERM environments are being used in tests.
-case ${TERM-} in
-       '' | dumb | unknown )
-               SMART_TERM=vt100 ;;
-       *)
-               SMART_TERM=$TERM ;;
-esac
-
-# For repeatability, reset the environment to known value.
-LANG=C
-LC_ALL=C
-PAGER=cat
-TZ=UTC
-TERM=dumb
-export LANG LC_ALL PAGER TERM TZ
-GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
-if [[ ( -n "$TEST_EMACS" && -z "$TEST_EMACSCLIENT" ) || \
-      ( -z "$TEST_EMACS" && -n "$TEST_EMACSCLIENT" ) ]]; then
-    echo "error: must specify both or neither of TEST_EMACS and TEST_EMACSCLIENT" >&2
-    exit 1
-fi
-TEST_EMACS=${TEST_EMACS:-${EMACS:-emacs}}
-TEST_EMACSCLIENT=${TEST_EMACSCLIENT:-emacsclient}
-TEST_GDB=${TEST_GDB:-gdb}
-TEST_CC=${TEST_CC:-cc}
-TEST_CFLAGS=${TEST_CFLAGS:-"-g -O0"}
-TEST_SHIM_CFLAGS=${TEST_SHIM_CFLAGS:-"-fpic -shared"}
-TEST_SHIM_LDFLAGS=${TEST_SHIM_LDFLAGS:-"-ldl"}
-
-# Protect ourselves from common misconfiguration to export
-# CDPATH into the environment
-unset CDPATH
-
-unset GREP_OPTIONS
-
-# For lib/open.cc:_load_key_file
-unset XDG_CONFIG_HOME
-
-# For emacsclient
-unset ALTERNATE_EDITOR
-
-# for reproducibility
-unset EMAIL
-unset NAME
+. "$NOTMUCH_SRCDIR/test/test-vars.sh" || exit 1
 
 add_gnupg_home () {
     [ -e "${GNUPGHOME}/gpg.conf" ] && return
     _gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
     at_exit_function _gnupg_exit
     mkdir -p -m 0700 "$GNUPGHOME"
-    gpg --no-tty --import <$NOTMUCH_SRCDIR/test/gnupg-secret-key.asc >"$GNUPGHOME"/import.log 2>&1
+    gpg --no-tty --import <$NOTMUCH_SRCDIR/test/openpgp4-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
@@ -129,22 +81,23 @@ add_gnupg_home () {
     echo no-emit-version >> "$GNUPGHOME"/gpg.conf
 
     # Change this if we ship a new test key
-    FINGERPRINT="5AEAB11F5E33DCE875DDB75B6D92612D94E46381"
-    SELF_USERID="Notmuch Test Suite <test_suite@notmuchmail.org> (INSECURE!)"
+    FINGERPRINT="9A3AFE6C60065A148FD4B58A7E6ABE924645CC60"
+    SELF_USERID="Notmuch Test Suite (INSECURE!) <test_suite@notmuchmail.org>"
+    SELF_EMAIL="test_suite@notmuchmail.org"
     printf '%s:6:\n' "$FINGERPRINT" | gpg --quiet --batch --no-tty --import-ownertrust
 }
 
 add_gpgsm_home () {
+    test_require_external_prereq openssl
+
     local fpr
     [ -e "$GNUPGHOME/gpgsm.conf" ] && return
     _gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
     at_exit_function _gnupg_exit
     mkdir -p -m 0700 "$GNUPGHOME"
-    openssl pkcs12 -export -passout pass: -inkey "$NOTMUCH_SRCDIR/test/smime/key+cert.pem" \
-       < "$NOTMUCH_SRCDIR/test/smime/test.crt" | \
-       gpgsm --batch --no-tty --no-common-certs-import --pinentry-mode=loopback --passphrase-fd 3 \
-             --disable-dirmngr --import  >"$GNUPGHOME"/import.log 2>&1 3<<<''
-    fpr=$(gpgsm --batch --list-key test_suite@notmuchmail.org | sed -n 's/.*fingerprint: //p')
+    gpgsm --batch --no-tty --no-common-certs-import --pinentry-mode=loopback --passphrase-fd 3 \
+         --disable-dirmngr --import  >"$GNUPGHOME"/import.log 2>&1 3<<<'' <$NOTMUCH_SRCDIR/test/smime/0xE0972A47.p12
+    fpr=$(gpgsm --batch --with-colons --list-key test_suite@notmuchmail.org | awk -F: '/^fpr/ {print $10}')
     echo "$fpr S relax" >> "$GNUPGHOME/trustlist.txt"
     gpgsm --quiet --batch --no-tty --no-common-certs-import --disable-dirmngr --import < $NOTMUCH_SRCDIR/test/smime/ca.crt
     echo "4D:E0:FF:63:C0:E9:EC:01:29:11:C8:7A:EE:DA:3A:9A:7F:6E:C1:0D S" >> "$GNUPGHOME/trustlist.txt"
@@ -193,56 +146,53 @@ do
 done
 
 if test -n "$debug"; then
-    print_subtest () {
-       printf " %-4s" "[$((test_count - 1))]"
-    }
+       fmt_subtest () {
+               printf -v $1 " %-4s" "[$((test_count - 1))]"
+       }
 else
-    print_subtest () {
-       true
-    }
+       fmt_subtest () {
+               printf -v $1 ''
+       }
 fi
 
 test -n "$COLORS_WITHOUT_TTY" || [ -t 1 ] || color=
 
-if [ -n "$color" ] && [ "$ORIGINAL_TERM" != 'dumb' ] && (
-               TERM=$ORIGINAL_TERM &&
-               export TERM &&
-               tput bold
-               tput setaf
-               tput sgr0
-       ) >/dev/null 2>&1
+if [ -n "$color" ] && [ "$ORIGINAL_TERM" != 'dumb' ] &&
+       tput -T "$ORIGINAL_TERM" -S <<<$'bold\nsetaf\nsgr0\n' >/dev/null 2>&1
 then
        color=t
 else
        color=
 fi
 
-if test -n "$color"; then
+if test -n "$color"
+then
+       # _tput run in subshell (``) only
+       _tput () { exec tput -T "$ORIGINAL_TERM" "$@"; }
+       unset BOLD RED GREEN BROWN SGR0
        say_color () {
-               (
-               TERM=$ORIGINAL_TERM
-               export TERM
                case "$1" in
-                       error) tput bold; tput setaf 1;; # bold red
-                       skip)  tput bold; tput setaf 2;; # bold green
-                       pass)  tput setaf 2;;            # green
-                       info)  tput setaf 3;;            # brown
-                       *) test -n "$quiet" && return;;
+                       error)  b=${BOLD=`_tput bold`}
+                               c=${RED=`_tput setaf 1`}   ;; # bold red
+                       skip)   b=${BOLD=`_tput bold`}
+                               c=${GREEN=`_tput setaf 2`} ;; # bold green
+                       pass)   b= c=${GREEN=`_tput setaf 2`} ;; # green
+                       info)   b= c=${BROWN=`_tput setaf 3`} ;; # brown
+                       *) b= c=; test -n "$quiet" && return ;;
                esac
-               shift
-               printf " "
-               printf "$@"
-               tput sgr0
-               print_subtest
-               )
+               f=$2
+               shift 2
+               sgr0=${SGR0=`_tput sgr0`}
+               fmt_subtest st
+               printf " ${b}${c}${f}${sgr0}${st}" "$@"
        }
 else
        say_color() {
                test -z "$1" && test -n "$quiet" && return
-               shift
-               printf " "
-               printf "$@"
-               print_subtest
+               f=$2
+               shift 2
+               fmt_subtest st
+               printf " ${f}${st}" "$@"
        }
 fi
 
@@ -332,11 +282,6 @@ die () {
        exit 1
 }
 
-GIT_EXIT_OK=
-# Note: TEST_TMPDIR *NOT* exported!
-TEST_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-test-$$.XXXXXX")
-# Put GNUPGHOME in TMPDIR to avoid problems with long paths.
-export GNUPGHOME="${TEST_TMPDIR}/gnupg"
 trap 'trap_exit' EXIT
 trap 'trap_signal' HUP INT TERM
 
@@ -402,32 +347,78 @@ test_expect_equal () {
     fi
 }
 
+test_diff_file_ () {
+    local file1 file2 testname basename1 basename2
+    file1="$1"
+    file2="$2"
+    if ! test_skip "$test_subtest_name"
+    then
+       if diff -q "$file1" "$file2" >/dev/null ; then
+           test_ok_
+       else
+           testname=$this_test.$test_count
+           basename1=`basename "$file1"`
+           basename2=`basename "$file2"`
+           cp "$file1" "$testname.$basename1"
+           cp "$file2" "$testname.$basename2"
+           test_failure_ "$(diff -u "$testname.$basename1" "$testname.$basename2")"
+       fi
+    fi
+}
+
 # Like test_expect_equal, but takes two filenames.
 test_expect_equal_file () {
-       local file1 file2 testname basename1 basename2
-       exec 1>&6 2>&7          # Restore stdout and stderr
-       if [ -z "$inside_subtest" ]; then
-               error "bug in the test script: test_expect_equal_file without test_begin_subtest"
-       fi
-       inside_subtest=
-       test "$#" = 2 ||
+    exec 1>&6 2>&7             # Restore stdout and stderr
+    if [ -z "$inside_subtest" ]; then
+       error "bug in the test script: test_expect_equal_file without test_begin_subtest"
+    fi
+    inside_subtest=
+    test "$#" = 2 ||
        error "bug in the test script: not 2 parameters to test_expect_equal_file"
 
-       file1="$1"
-       file2="$2"
-       if ! test_skip "$test_subtest_name"
-       then
-               if diff -q "$file1" "$file2" >/dev/null ; then
-                       test_ok_
-               else
-                       testname=$this_test.$test_count
-                       basename1=`basename "$file1"`
-                       basename2=`basename "$file2"`
-                       cp "$file1" "$testname.$basename1"
-                       cp "$file2" "$testname.$basename2"
-                       test_failure_ "$(diff -u "$testname.$basename1" "$testname.$basename2")"
-               fi
+    test_diff_file_ "$1" "$2"
+}
+
+# Like test_expect_equal_file, but compare the part of the two files after the first blank line
+test_expect_equal_message_body () {
+    exec 1>&6 2>&7             # Restore stdout and stderr
+    if [ -z "$inside_subtest" ]; then
+       error "bug in the test script: test_expect_equal_file without test_begin_subtest"
+    fi
+    test "$#" = 2 ||
+       error "bug in the test script: not 2 parameters to test_expect_equal_file"
+
+    for file in "$1" "$2"; do
+       if [ ! -s "$file" ]; then
+           test_failure_ "Missing or zero length file: $file"
+           inside_subtest=
+           return 1
+       fi
+    done
+
+    expected=$(sed '1,/^$/d' "$1")
+    output=$(sed '1,/^$/d' "$2")
+    test_expect_equal "$expected" "$output"
+}
+
+# Like test_expect_equal, but takes two filenames. Fails if either is empty
+test_expect_equal_file_nonempty () {
+    exec 1>&6 2>&7             # Restore stdout and stderr
+    if [ -z "$inside_subtest" ]; then
+       error "bug in the test script: test_expect_equal_file_nonempty without test_begin_subtest"
     fi
+    inside_subtest=
+    test "$#" = 2 ||
+       error "bug in the test script: not 2 parameters to test_expect_equal_file_nonempty"
+
+    for file in "$1" "$2"; do
+       if [ ! -s "$file" ]; then
+           test_failure_ "Missing or zero length file: $file"
+           return $?
+       fi
+    done
+
+    test_diff_file_ "$1" "$2"
 }
 
 # Like test_expect_equal, but arguments are JSON expressions to be
@@ -502,16 +493,20 @@ print(msg.as_string(False))
 ' "$@"
 }
 
+notmuch_debug_sanitize () {
+    grep -v '^D.:'
+}
+
 notmuch_exception_sanitize () {
-    perl -pe 's/(A Xapian exception occurred at .*[.]cc?):([0-9]*)/\1:XXX/'
+    perl -pe 's,(A Xapian exception occurred at) .*?([^/]*[.]cc?):([0-9]*),\1 \2:XXX,'
 }
 
 notmuch_search_sanitize () {
-    perl -pe 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
+    notmuch_debug_sanitize | perl -pe 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
 }
 
 notmuch_search_files_sanitize () {
-    notmuch_dir_sanitize
+    notmuch_dir_sanitize |  sed 's/msg-[0-9][0-9][0-9]/msg-XXX/'
 }
 
 notmuch_dir_sanitize () {
@@ -523,6 +518,7 @@ notmuch_show_sanitize () {
     sed -e "$NOTMUCH_SHOW_FILENAME_SQUELCH"
 }
 notmuch_show_sanitize_all () {
+    notmuch_debug_sanitize | \
     sed \
        -e 's| filename:.*| filename:XXXXX|' \
        -e 's| id:[^ ]* | id:XXXXX |' | \
@@ -534,20 +530,37 @@ notmuch_json_show_sanitize () {
        -e 's|"id": "[^"]*",|"id": "XXXXX",|g' \
        -e 's|"Date": "Fri, 05 Jan 2001 [^"]*0000"|"Date": "GENERATED_DATE"|g' \
        -e 's|"filename": "signature.asc",||g' \
+       -e 's|"duplicate": 1,||g' \
        -e 's|"filename": \["/[^"]*"\],|"filename": \["YYYYY"\],|g' \
        -e 's|"timestamp": 97.......|"timestamp": 42|g' \
        -e 's|"content-length": [1-9][0-9]*|"content-length": "NONZERO"|g'
 }
 
+notmuch_sexp_show_sanitize () {
+    sed \
+       -e 's|:id "[^"]*"|:id "XXXXX"|g' \
+       -e 's|:Date "Sat, 01 Jan 2000 [^"]*0000"|:Date "GENERATED_DATE"|g' \
+       -e 's|:filename "signature.asc"||g' \
+       -e 's|:duplicate 1 ||g' \
+       -e 's|:filename ("/[^"]*")|:filename ("YYYYY")|g' \
+       -e 's|:timestamp 9........|:timestamp 42|g' \
+       -e 's|:content-length [1-9][0-9]*|:content-length "NONZERO"|g'
+}
+
+notmuch_sexp_search_sanitize () {
+    sed -e 's|:thread "[^"]*"|:thread "XXX"|'
+}
+
 notmuch_emacs_error_sanitize () {
     local command
     command=$1
     shift
     for file in "$@"; do
        echo "=== $file ==="
-       cat "$file"
+       notmuch_debug_sanitize < "$file"
     done | sed \
-       -e 's/^\[.*\]$/[XXX]/' \
+       -e '/^$/d' \
+       -e '/^\[.*\]$/d' \
        -e "s|^\(command: \)\{0,1\}/.*/$command|\1YYY/$command|"
 }
 
@@ -556,6 +569,10 @@ notmuch_date_sanitize () {
        -e 's/^Date: Fri, 05 Jan 2001 .*0000/Date: GENERATED_DATE/'
 }
 
+# remove redundant parts of notmuch-git internal paths
+notmuch_git_sanitize () {
+    sed  -e 's,tags/\([0-9a-f]\{2\}/\)\{2\},,' -e '/FORMAT/d'
+}
 notmuch_uuid_sanitize () {
     sed 's/[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}/UUID/g'
 }
@@ -614,20 +631,6 @@ $binary () {
        fi
 }
 
-# Explicitly require external prerequisite.  Useful when binary is
-# called indirectly (e.g. from emacs).
-# Returns success if dependency is available, failure otherwise.
-test_require_external_prereq () {
-       local binary
-       binary="$1"
-       if [[ ${test_missing_external_prereq_["${binary}"]} == t ]]; then
-               # dependency is missing, call the replacement function to note it
-               eval "$binary"
-       else
-               true
-       fi
-}
-
 # You are not expected to call test_ok_ and test_failure_ directly, use
 # the text_expect_* functions instead.
 
@@ -745,6 +748,18 @@ test_subtest_known_broken () {
        test_subtest_known_broken_=t
 }
 
+test_subtest_broken_for_installed () {
+    if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       test_subtest_known_broken_=t
+    fi
+}
+
+test_subtest_broken_for_root () {
+   if [ "$EUID" = "0" ]; then
+       test_subtest_known_broken_=t
+    fi
+}
+
 test_expect_success () {
        exec 1>&6 2>&7          # Restore stdout and stderr
        if [ -z "$inside_subtest" ]; then
@@ -858,12 +873,13 @@ test_done () {
        mkdir -p "$test_results_dir"
        test_results_path="$test_results_dir/$this_test"
 
-       echo "total $test_count" >> $test_results_path
-       echo "success $test_success" >> $test_results_path
-       echo "fixed $test_fixed" >> $test_results_path
-       echo "broken $test_broken" >> $test_results_path
-       echo "failed $test_failure" >> $test_results_path
-       echo "" >> $test_results_path
+       printf %s\\n \
+               "success $test_success" \
+               "fixed $test_fixed" \
+               "broken $test_broken" \
+               "failed $test_failure" \
+               "total $test_count" \
+           > $test_results_path
 
        [ -n "$EMACS_SERVER" ] && test_emacs '(kill-emacs)'
 
@@ -880,7 +896,7 @@ test_done () {
 test_python () {
     # Note: if there is need to print debug information from python program,
     # use stdout = os.fdopen(6, 'w') or stderr = os.fdopen(7, 'w')
-    PYTHONPATH="$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \
+    PYTHONPATH="$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage:$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \
        $NOTMUCH_PYTHON -B - > OUTPUT
 }
 
@@ -893,7 +909,20 @@ test_C () {
     echo "== stdout ==" > OUTPUT.stdout
     echo "== stderr ==" > OUTPUT.stderr
     ./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr
-    notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr | notmuch_exception_sanitize > OUTPUT
+    notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr | notmuch_exception_sanitize | notmuch_debug_sanitize > OUTPUT
+}
+
+test_private_C () {
+    local exec_file test_file
+    exec_file="test${test_count}"
+    test_file="${exec_file}.c"
+    echo '#include <notmuch-private.h>' > ${test_file}
+    cat >> ${test_file}
+    ${TEST_CC} ${TEST_CFLAGS} -I${NOTMUCH_SRCDIR}/test -I${NOTMUCH_SRCDIR}/lib -I${NOTMUCH_SRCDIR}/util -I${NOTMUCH_SRCDIR}/compat ${NOTMUCH_GMIME_CFLAGS} -o ${exec_file} ${test_file} ${NOTMUCH_BUILDDIR}/lib/libnotmuch.a ${NOTMUCH_GMIME_LDFLAGS} ${NOTMUCH_XAPIAN_LDFLAGS} ${NOTMUCH_BUILDDIR}/util/libnotmuch_util.a ${NOTMUCH_SFSEXP_LDFLAGS} ${NOTMUCH_BUILDDIR}/parse-time-string/libparse-time-string.a -ltalloc -lstdc++
+    echo "== stdout ==" > OUTPUT.stdout
+    echo "== stderr ==" > OUTPUT.stderr
+    ./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr
+    notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr | notmuch_exception_sanitize | notmuch_debug_sanitize > OUTPUT
 }
 
 make_shim () {
@@ -906,11 +935,16 @@ make_shim () {
 }
 
 notmuch_with_shim () {
-    local base_name shim_file
-    base_name="$1"
+    local base_name shim_file notmuch_cmd
+    if [ -n "${NOTMUCH_TEST_INSTALLED-}" ]; then
+       notmuch_cmd="notmuch"
+    else
+       notmuch_cmd="notmuch-shared"
+    fi
+    base_name=$1
     shift
     shim_file="${base_name}.so"
-    LD_PRELOAD=${LD_PRELOAD:+:$LD_PRELOAD}:./${shim_file} notmuch-shared "$@"
+    LD_PRELOAD=${LD_PRELOAD:+:$LD_PRELOAD}:./${shim_file} $notmuch_cmd "$@"
 }
 
 # Creates a script that counts how much time it is executed and calls
@@ -962,7 +996,11 @@ test_init_ () {
 
 
 # Where to run the tests
-TEST_DIRECTORY=$NOTMUCH_BUILDDIR/test
+if [[ -n "${NOTMUCH_BUILDDIR}" ]]; then
+    TEST_DIRECTORY=$NOTMUCH_BUILDDIR/test
+else
+    TEST_DIRECTORY=$NOTMUCH_SRCDIR/test
+fi
 
 . "$NOTMUCH_SRCDIR/test/test-lib-common.sh" || exit 1