]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - tests/functions.sh
Merge branch 'dmverity_options' of https://github.com/bluca/util-linux
[thirdparty/util-linux.git] / tests / functions.sh
index d1c97e0b6faecec4ca896da93b5ddc333a5f09da..0af8b96b77f9a7527baa95013d5224bbabd1b054 100644 (file)
@@ -75,9 +75,25 @@ function ts_report {
 }
 
 function ts_check_test_command {
-       if [ ! -x "$1" ]; then
-               ts_skip "${1##*/} not found"
-       fi
+       case "$1" in
+       */*)
+               # paths
+               if [ ! -x "$1" ]; then
+                       ts_skip "${1##*/} not found"
+               fi
+               ;;
+       *)
+               # just command names (e.g. --use-system-commands)
+               local cmd=$1
+               type "$cmd" >/dev/null 2>&1
+               if [ $? -ne 0 ]; then
+                       if [ "$TS_NOSKIP_COMMANDS" = "yes" ]; then
+                               ts_failed "missing in PATH: $cmd"
+                       fi
+                       ts_skip "missing in PATH: $cmd"
+               fi
+               ;;
+       esac
 }
 
 function ts_check_prog {
@@ -158,6 +174,17 @@ function ts_log {
        [ "$TS_VERBOSE" == "yes" ] && echo "$1"
 }
 
+function ts_logerr {
+       echo "$1" >> $TS_ERRLOG
+       [ "$TS_VERBOSE" == "yes" ] && echo "$1"
+}
+
+function ts_log_both {
+       echo "$1" >> $TS_OUTPUT
+       echo "$1" >> $TS_ERRLOG
+       [ "$TS_VERBOSE" == "yes" ] && echo "$1"
+}
+
 function ts_has_option {
        NAME="$1"
        ALL="$2"
@@ -201,24 +228,28 @@ function ts_init_core_env {
        TS_SUBNAME=""
        TS_NS="$TS_COMPONENT/$TS_TESTNAME"
        TS_OUTPUT="$TS_OUTDIR/$TS_TESTNAME"
+       TS_ERRLOG="$TS_OUTDIR/$TS_TESTNAME.err"
        TS_VGDUMP="$TS_OUTDIR/$TS_TESTNAME.vgdump"
        TS_DIFF="$TS_DIFFDIR/$TS_TESTNAME"
        TS_EXPECTED="$TS_TOPDIR/expected/$TS_NS"
+       TS_EXPECTED_ERR="$TS_TOPDIR/expected/$TS_NS.err"
        TS_MOUNTPOINT="$TS_OUTDIR/${TS_TESTNAME}-mnt"
 }
 
 function ts_init_core_subtest_env {
        TS_NS="$TS_COMPONENT/$TS_TESTNAME-$TS_SUBNAME"
        TS_OUTPUT="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME"
+       TS_ERRLOG="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME.err"
        TS_VGDUMP="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME.vgdump"
        TS_DIFF="$TS_DIFFDIR/$TS_TESTNAME-$TS_SUBNAME"
        TS_EXPECTED="$TS_TOPDIR/expected/$TS_NS"
+       TS_EXPECTED_ERR="$TS_TOPDIR/expected/$TS_NS.err"
        TS_MOUNTPOINT="$TS_OUTDIR/${TS_TESTNAME}-${TS_SUBNAME}-mnt"
 
-       rm -f $TS_OUTPUT $TS_VGDUMP
+       rm -f $TS_OUTPUT $TS_ERRLOG $TS_VGDUMP
        [ -d "$TS_OUTDIR" ]  || mkdir -p "$TS_OUTDIR"
 
-       touch $TS_OUTPUT
+       touch $TS_OUTPUT $TS_ERRLOG
        [ -n "$TS_VALGRIND_CMD" ] && touch $TS_VGDUMP
 }
 
@@ -254,8 +285,20 @@ function ts_init_env {
        top_srcdir=$(ts_abspath $top_srcdir)
        top_builddir=$(ts_abspath $top_builddir)
 
-       # some ul commands search other ul commands in $PATH
-       export PATH="$top_builddir:$PATH"
+       # We use helpser always from build tree
+       ts_helpersdir="${top_builddir}/"
+
+       TS_USE_SYSTEM_COMMANDS=$(ts_has_option "use-system-commands" "$*")
+       if [ "$TS_USE_SYSTEM_COMMANDS" == "yes" ]; then
+               # Don't define anything, just follow current PATH
+               ts_commandsdir=""
+       else
+               # The default is to use commands from build tree
+               ts_commandsdir="${top_builddir}/"
+
+               # some ul commands search other ul commands in $PATH
+               export PATH="$ts_commandsdir:$PATH"
+       fi
 
        TS_SCRIPT="$mydir/$(basename $0)"
        TS_SUBDIR=$(dirname $TS_SCRIPT)
@@ -274,15 +317,12 @@ function ts_init_env {
        TS_NOLOCKS=$(ts_has_option "nolocks" "$*")
        TS_LOCKDIR="$top_builddir/tests/output"
 
-       if [ ! -d "/proc/self/fd" ]; then
-               TS_NOLOCKS="yes"
-       fi
-
        # Don't lock if flock(1) is missing
        type "flock" >/dev/null 2>&1 || TS_NOLOCKS="yes"
 
        ts_init_core_env
 
+       TS_NOSKIP_COMMANDS=$(ts_has_option "noskip-commands" "$*")
        TS_VERBOSE=$(ts_has_option "verbose" "$*")
        TS_SHOWDIFF=$(ts_has_option "show-diff" "$*")
        TS_PARALLEL=$(ts_has_option "parallel" "$*")
@@ -306,6 +346,7 @@ function ts_init_env {
        declare -a TS_SUID_USER
        declare -a TS_SUID_GROUP
        declare -a TS_LOOP_DEVS
+       declare -a TS_LOCKFILE_FD
 
        if [ -f $TS_TOPDIR/commands.sh ]; then
                . $TS_TOPDIR/commands.sh
@@ -313,15 +354,17 @@ function ts_init_env {
 
        export BLKID_FILE
 
-       rm -f $TS_OUTPUT $TS_VGDUMP
+       rm -f $TS_OUTPUT $TS_ERRLOG $TS_VGDUMP
        [ -d "$TS_OUTDIR" ]  || mkdir -p "$TS_OUTDIR"
 
-       touch $TS_OUTPUT
+       touch $TS_OUTPUT $TS_ERRLOG
        [ -n "$TS_VALGRIND_CMD" ] && touch $TS_VGDUMP
 
        if [ "$TS_VERBOSE" == "yes" ]; then
                echo
                echo "     script: $TS_SCRIPT"
+               echo "   commands: $ts_commandsdir"
+               echo "    helpers: $ts_helpersdir"
                echo "    sub dir: $TS_SUBDIR"
                echo "    top dir: $TS_TOPDIR"
                echo "       self: $TS_SELF"
@@ -331,8 +374,9 @@ function ts_init_env {
                echo "  namespace: $TS_NS"
                echo "    verbose: $TS_VERBOSE"
                echo "     output: $TS_OUTPUT"
+               echo "  error log: $TS_ERRLOG"
                echo "   valgrind: $TS_VGDUMP"
-               echo "   expected: $TS_EXPECTED"
+               echo "   expected: $TS_EXPECTED{.err}"
                echo " mountpoint: $TS_MOUNTPOINT"
                echo
        fi
@@ -392,55 +436,79 @@ function ts_init_py {
 }
 
 function ts_run {
-       #
-       # valgrind mode
-       #
-       if [ -n "$TS_VALGRIND_CMD" ]; then
-               $TS_VALGRIND_CMD --tool=memcheck --leak-check=full \
-                                --leak-resolution=high --num-callers=20 \
-                                --log-file="$TS_VGDUMP" "$@"
+       declare -a args
+
        #
        # ASAN mode
        #
-       elif [ "$TS_ENABLE_ASAN" == "yes" ]; then
-               ASAN_OPTIONS='detect_leaks=1' "$@"
+       if [ "$TS_ENABLE_ASAN" == "yes" ]; then
+               args+=(env ASAN_OPTIONS=detect_leaks=1)
+       fi
 
        #
-       # Default mode
+       # valgrind mode
        #
-       else
-               "$@"
+       if [ -n "$TS_VALGRIND_CMD" ]; then
+               args+=(libtool --mode=execute "$TS_VALGRIND_CMD" --tool=memcheck --leak-check=full)
+               args+=(--leak-resolution=high --num-callers=20 --log-file="$TS_VGDUMP")
        fi
+
+       "${args[@]}" "$@"
 }
 
-function ts_gen_diff {
+function ts_gen_diff_from {
        local res=0
+       local expected="$1"
+       local output="$2"
+       local difffile="$3"
 
-       [ -f "$TS_OUTPUT" ] || return 1
-       [ -f "$TS_EXPECTED" ] || TS_EXPECTED=/dev/null
-
-       # remove libtool lt- prefixes
-       sed --in-place 's/^lt\-\(.*\: \)/\1/g' $TS_OUTPUT
-
-       [ -d "$TS_DIFFDIR" ] || mkdir -p "$TS_DIFFDIR"
-       diff -u $TS_EXPECTED $TS_OUTPUT > $TS_DIFF
+       diff -u $expected $output > $difffile
 
-       if [ $? -ne 0 ] || [ -s $TS_DIFF ]; then
+       if [ $? -ne 0 ] || [ -s $difffile ]; then
                res=1
-               if [ "$TS_SHOWDIFF" == "yes" ]; then
+               if [ "$TS_SHOWDIFF" == "yes" -a "$TS_KNOWN_FAIL" != "yes" ]; then
                        echo
                        echo "diff-{{{"
-                       cat $TS_DIFF
+                       cat $difffile
                        echo "}}}-diff"
                        echo
                fi
        else
-               rm -f $TS_DIFF;
+               rm -f $difffile;
        fi
 
        return $res
 }
 
+function ts_gen_diff {
+       local status_out=0
+       local status_err=0
+
+       [ -f "$TS_OUTPUT" ] || return 1
+       [ -f "$TS_EXPECTED" ] || TS_EXPECTED=/dev/null
+
+       # remove libtool lt- prefixes
+       sed --in-place 's/^lt\-\(.*\: \)/\1/g' $TS_OUTPUT
+       sed --in-place 's/^lt\-\(.*\: \)/\1/g' $TS_ERRLOG
+
+       [ -d "$TS_DIFFDIR" ] || mkdir -p "$TS_DIFFDIR"
+
+       ts_gen_diff_from $TS_EXPECTED $TS_OUTPUT $TS_DIFF
+       status_out=$?
+
+       # error log is fully optional
+       [ -f "$TS_EXPECTED_ERR" ] || TS_EXPECTED_ERR=/dev/null
+       [ -f "$TS_ERRLOG" ] || TS_ERRLOG=/dev/null
+
+       ts_gen_diff_from $TS_EXPECTED_ERR $TS_ERRLOG $TS_DIFF.err
+       status_err=$?
+
+       if [ $status_out -ne 0 -o $status_err -ne 0 ]; then
+               return 1
+       fi
+       return 0
+}
+
 function tt_gen_mem_report {
        if [ -n "$TS_VALGRIND_CMD" ]; then
                grep -q -E 'ERROR SUMMARY: [1-9]' $TS_VGDUMP &> /dev/null
@@ -558,16 +626,34 @@ function ts_device_deinit {
        fi
 }
 
+function ts_blkidtag_by_devname()
+{
+       local tag=$1
+       local dev=$2
+       local out
+       local rval
+
+       out=$($TS_CMD_BLKID -p -s "$tag" -o value "$dev")
+       rval=$?
+       printf "%s\n" "$out"
+
+       test -n "$out" -a "$rval" = "0"
+       return $?
+}
+
 function ts_uuid_by_devname {
-       echo $($TS_CMD_BLKID -p -s UUID -o value $1)
+       ts_blkidtag_by_devname "UUID" "$1"
+       return $?
 }
 
 function ts_label_by_devname {
-       echo $($TS_CMD_BLKID -p -s LABEL -o value $1)
+       ts_blkidtag_by_devname "LABEL" "$1"
+       return $?
 }
 
 function ts_fstype_by_devname {
-       echo $($TS_CMD_BLKID -p -s TYPE -o value $1)
+       ts_blkidtag_by_devname "TYPE" "$1"
+       return $?
 }
 
 function ts_device_has {
@@ -576,24 +662,24 @@ function ts_device_has {
        local DEV="$3"
        local vl=""
 
-       case $TAG in
-               "TYPE") vl=$(ts_fstype_by_devname $DEV);;
-               "LABEL") vl=$(ts_label_by_devname $DEV);;
-               "UUID") vl=$(ts_uuid_by_devname $DEV);;
-               *) return 1;;
-       esac
-
-       if [ "$vl" == "$VAL" ]; then
-               return 0
-       fi
-       return 1
+       vl=$(ts_blkidtag_by_devname "$TAG" "$DEV")
+       test $? = 0 -a "$vl" = "$VAL"
+       return $?
 }
 
-function ts_device_has_uuid {
-       ts_uuid_by_devname "$1" | egrep -q '^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$'
+function ts_is_uuid()
+{
+       printf "%s\n" "$1" | egrep -q '^[0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12}$'
        return $?
 }
 
+function ts_udevadm_settle()
+{
+       local dev=$1 # optional, might be empty
+       shift        # all other args are tags, LABEL, UUID, ...
+       udevadm settle
+}
+
 function ts_mount {
        local out
        local result
@@ -621,10 +707,10 @@ function ts_mount {
 function ts_is_mounted {
        local DEV=$(ts_canonicalize "$1")
 
-       grep -q $DEV /proc/mounts && return 0
+       grep -q "\(^\| \)$DEV " /proc/mounts && return 0
 
        if [ "${DEV#/dev/loop/}" != "$DEV" ]; then
-               grep -q "/dev/loop${DEV#/dev/loop/}" /proc/mounts && return 0
+               grep -q "^/dev/loop${DEV#/dev/loop/} " /proc/mounts && return 0
        fi
        return 1
 }
@@ -659,6 +745,7 @@ function ts_fstab_add {
 }
 
 function ts_fstab_clean {
+       ts_have_lock "fstab" || return 0
        sed --in-place "
 /# <!-- util-linux/!b
 :a
@@ -678,7 +765,7 @@ function ts_fdisk_clean {
 
        # remove non comparable parts of fdisk output
        if [ -n "${DEVNAME}" ]; then
-               sed -i -e "s@${DEVNAME}@<removed>@;" $TS_OUTPUT
+               sed -i -e "s@${DEVNAME}@<removed>@;" $TS_OUTPUT $TS_ERRLOG
        fi
 
        sed -i \
@@ -689,22 +776,45 @@ function ts_fdisk_clean {
                -e 's/Welcome to fdisk.*/Welcome to fdisk <removed>./' \
                -e 's/typescript file.*/typescript file <removed>./' \
                -e 's@^\(I/O size (minimum/op.* bytes /\) [1-9][0-9]* @\1 <removed> @' \
-               $TS_OUTPUT
+               $TS_OUTPUT $TS_ERRLOG
 }
 
 
+# https://stackoverflow.com/questions/41603787/how-to-find-next-available-file-descriptor-in-bash
+function ts_find_free_fd()
+{
+       local rco
+       local rci
+       for fd in {3..200}; do
+               rco="$(true 2>/dev/null >&${fd}; echo $?)"
+               rci="$(true 2>/dev/null <&${fd}; echo $?)"
+               if [[ "${rco}${rci}" = "11" ]]; then
+                       echo "$fd"
+                       return 0
+               fi
+       done
+       return 1
+}
+
 function ts_get_lock_fd {
-        local proc=$1
-        local lockfile=$2
+       local resource=$1
+       local fd
+
+       for fd in "${!TS_LOCKFILE_FD[@]}"; do
+               if [ "${TS_LOCKFILE_FD["$fd"]}" = "$resource" ]; then
+                       echo "$fd"
+                       return 0
+               fi
+       done
+       return 1
+}
+
+function ts_have_lock {
+       local resource=$1
 
-        for fd in $(ls /proc/$proc/fd); do
-                file=$(readlink "/proc/$proc/fd/$fd")
-                if [ x"$file" = x"$lockfile" ]; then
-                        echo "$fd"
-                        return 0
-                fi
-        done
-        return 1
+       test "$TS_NOLOCKS" = "yes" && return 0
+       ts_get_lock_fd "$resource" >/dev/null && return 0
+       return 1
 }
 
 function ts_lock {
@@ -717,17 +827,18 @@ function ts_lock {
        fi
 
        # Don't lock again
-       fd=$(ts_get_lock_fd $$ $lockfile)
+       fd=$(ts_get_lock_fd "$resource")
        if [ -n "$fd" ]; then
                echo "[$$ $TS_TESTNAME] ${resource} already locked!"
                return 0
        fi
 
-       fd=$(( $(ls /proc/$$/fd/ | sort | tail -1) + 1))
+       fd=$(ts_find_free_fd) || ts_skip "failed to find lock fd"
 
        eval "exec $fd>$lockfile"
-       flock --exclusive --timeout 30 $fd || ts_skip "failed to lock $resource"
+       flock --exclusive "$fd" || ts_skip "failed to lock $resource"
 
+       TS_LOCKFILE_FD["$fd"]="$resource"
        ###echo "[$$ $TS_TESTNAME] Locked   $resource"
 }
 
@@ -740,10 +851,13 @@ function ts_unlock {
                return 0
        fi
 
-       fd=$(ts_get_lock_fd $$ $lockfile)
+       fd=$(ts_get_lock_fd "$resource")
        if [ -n "$fd" ]; then
-               ###echo "[$$ $TS_TESTNAME] Unlocked $resource"
                eval "exec $fd<&-"
+               TS_LOCKFILE_FD["$fd"]=""
+               ###echo "[$$ $TS_TESTNAME] Unlocked $resource"
+       else
+               echo "[$$ $TS_TESTNAME] unlocking unlocked $resource!?"
        fi
 }
 
@@ -795,6 +909,9 @@ function ts_scsi_debug_rmmod {
        local t
        local lastmsg
 
+       # We must not run if we don't have the lock
+       ts_have_lock "scsi_debug" || return 0
+
        # Return early most importantly in case we are not root or the module does
        # not exist at all.
        [ $UID -eq 0 ] || return 0
@@ -899,4 +1016,3 @@ function ts_has_ncurses_support {
                echo "no"
        fi
 }
-