util-linux/tests/functions.sh

1107 lines
25 KiB
Bash
Raw Permalink Normal View History

#
# Copyright (C) 2007 Karel Zak <kzak@redhat.com>
#
# This file is part of util-linux.
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
function ts_abspath {
cd $1
pwd
}
function ts_canonicalize {
P="$1"
C=$(readlink -f $P)
if [ -n "$C" ]; then
echo "$C"
else
echo "$P"
fi
}
function ts_cd {
if [ $# -eq 0 ]; then
ts_failed "ul_cd: not enough arguments"
fi
DEST=$(readlink -f "$1" 2>/dev/null)
if [ "x$DEST" = "x" ] || [ ! -d "$DEST" ]; then
ts_failed "ul_cd: $1: no such directory"
fi
cd "$DEST" 2>/dev/null || ts_failed "ul_cd: $1: cannot change directory"
if [ "$PWD" != "$DEST" ]; then
ts_failed "ul_cd: $PWD is not $DEST"
fi
}
function ts_separator {
local header="$1"
echo >> $TS_OUTPUT
if [ -z "$header" ]; then
echo "============================================" >> $TS_OUTPUT
else
echo "=====$header================================" >> $TS_OUTPUT
fi
}
function ts_report {
local desc=
if [ "$TS_PARSABLE" != "yes" ]; then
if [ $TS_NSUBTESTS -ne 0 ] && [ -z "$TS_SUBNAME" ]; then
desc=$(printf "%11s...")
fi
echo "$desc$1"
return
fi
if [ -n "$TS_SUBNAME" ]; then
desc=$(printf "%s: [%02d] %s" "$TS_DESC" "$TS_NSUBTESTS" "$TS_SUBNAME")
else
desc=$TS_DESC
fi
printf "%13s: %-45s ...%s\n" "$TS_COMPONENT" "$desc" "$1"
}
function ts_check_test_command {
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 {
local cmd=$1
type "$cmd" >/dev/null 2>&1 || ts_skip "missing in PATH: $cmd"
}
function ts_check_losetup {
local tmp
ts_check_test_command "$TS_CMD_LOSETUP"
if [ "$TS_SKIP_LOOPDEVS" = "yes" ]; then
ts_skip "loop-device tests disabled"
fi
# assuming that losetup -f works ... to be checked somewhere else
tmp=$($TS_CMD_LOSETUP -f 2>/dev/null)
if test -b "$tmp"; then
return 0
fi
ts_skip "no loop-device support"
}
function ts_report_skip {
ts_report " SKIPPED ($1)"
}
function ts_skip {
ts_report_skip "$1"
ts_cleanup_on_exit
exit 0
}
function ts_skip_nonroot {
if [ $UID -ne 0 ]; then
ts_skip "no root permissions"
fi
}
function ts_failed_subtest {
local msg="FAILED"
local ret=1
if [ "$TS_KNOWN_FAIL" = "yes" ]; then
msg="KNOWN FAILED"
ret=0
fi
if [ x"$1" == x"" ]; then
ts_report " $msg ($TS_NS)"
else
ts_report " $msg ($1)"
fi
return $ret
}
function ts_failed {
ts_failed_subtest "$1"
exit $?
}
function ts_report_ok {
if [ x"$1" == x"" ]; then
ts_report " OK"
else
ts_report " OK ($1)"
fi
}
function ts_ok {
ts_report_ok "$1"
exit 0
}
function ts_log {
echo "$1" >> $TS_OUTPUT
[ "$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"
# user may set options by env for a single test or whole component
# e.g. TS_OPT_ipcs_limits2_fake="yes" or TS_OPT_ipcs_fake="yes"
local v_test=${TS_TESTNAME//[-.]/_}
local v_comp=${TS_COMPONENT//[-.]/_}
local v_name=${NAME//[-.]/_}
eval local env_opt_test=\$TS_OPT_${v_comp}_${v_test}_${v_name}
eval local env_opt_comp=\$TS_OPT_${v_comp}_${v_name}
if [ "$env_opt_test" = "yes" \
-o "$env_opt_comp" = "yes" -a "$env_opt_test" != "no" ]; then
echo "yes"
return
elif [ "$env_opt_test" = "no" \
-o "$env_opt_comp" = "no" -a "$env_opt_test" != "yes" ]; then
return
fi
# or just check the global command line options
if [[ $ALL =~ ([$' \t\n']|^)--$NAME([$'= \t\n']|$) ]]; then
echo yes
return
fi
# or the _global_ env, e.g TS_OPT_parsable="yes"
eval local env_opt=\$TS_OPT_${v_name}
if [ "$env_opt" = "yes" ]; then echo "yes"; fi
}
function ts_option_argument {
NAME="$1"
ALL="$2"
# last option wins!
echo "$ALL" | sed -n "s/.*[ \t\n]--$NAME=\([^ \t\n]*\).*/\1/p" | tail -n 1
}
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_EXIT_CODE="$TS_OUTDIR/$TS_TESTNAME.exit_code"
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_EXIT_CODE="$TS_OUTDIR/$TS_TESTNAME-$TS_SUBNAME.exit_code"
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_ERRLOG $TS_VGDUMP $TS_EXIT_CODE
[ -d "$TS_OUTDIR" ] || mkdir -p "$TS_OUTDIR"
touch $TS_OUTPUT $TS_ERRLOG $TS_EXIT_CODE
[ -n "$TS_VALGRIND_CMD" ] && touch $TS_VGDUMP
}
function ts_init_env {
local mydir=$(ts_abspath ${0%/*})
local tmp
LANG="POSIX"
LANGUAGE="POSIX"
LC_ALL="POSIX"
CHARSET="UTF-8"
ASAN_OPTIONS="detect_leaks=0"
UBSAN_OPTIONS="print_stacktrace=1:print_summary=1:halt_on_error=1"
export LANG LANGUAGE LC_ALL CHARSET ASAN_OPTIONS UBSAN_OPTIONS
mydir=$(ts_canonicalize "$mydir")
# automake directories
top_srcdir=$(ts_option_argument "srcdir" "$*")
top_builddir=$(ts_option_argument "builddir" "$*")
# where is this script
TS_TOPDIR=$(ts_abspath $mydir/../../)
# default
if [ -z "$top_srcdir" ]; then
top_srcdir="$TS_TOPDIR/.."
fi
if [ -z "$top_builddir" ]; then
top_builddir="$TS_TOPDIR/.."
fi
top_srcdir=$(ts_abspath $top_srcdir)
top_builddir=$(ts_abspath $top_builddir)
meson: add second build system To build: meson build && ninja -C build To run tests: ninja -C build check To install for packaging: DESTDIR=/var/tmp/inst ninja -C build install To install for realz: sudo ninja -C build install v2: - Optional items are now based on the 'feature' feature in meson. Built libraries which are disabled turn into disabler() objects and also poison any executables which link to them. What is there: - building of the binaries and libs and the python module - installation of binaries, libs, python module, localization files, man pages, pkgconfig files - running of tests - most options to configure build equivalently to the ./configure settings Partially implemented: - disabling of stuff when things missing. In the C code, the defines are all used, so that should be fine. In the build system, some files should be skipped, but that is probably not always done properly. Getting this right might require some testing of various build option combinations to get the details right. Not implemented: - static builds of fdisk and other binaries - things marked with XXX or FIXME - ??? Differences: - .la files are not created. They are useless and everybody hates them. - Requires.private in pkgconfig files are not present in the autogenerated .pc file. Not sure if they should be there or not. If necessary, they can be added by hand. - man pages and systemd units are installed by the install target. Not sure why 'make install' doesn't do that. - the split between / and /usr is probably wrong. But it's all pointless anyway, so maybe we could simplify things but not implementing it at all under meson?
2020-02-23 12:42:55 -06:00
if [ -e "$top_builddir/meson.conf" ]; then
. "$top_builddir/meson.conf"
fi
# 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)
TS_TESTNAME=$(basename $TS_SCRIPT)
TS_COMPONENT=$(basename $TS_SUBDIR)
TS_DESC=${TS_DESC:-$TS_TESTNAME}
TS_NSUBTESTS=0
TS_NSUBFAILED=0
TS_SELF="$TS_SUBDIR"
TS_OUTDIR="$top_builddir/tests/output/$TS_COMPONENT"
TS_DIFFDIR="$top_builddir/tests/diff/$TS_COMPONENT"
TS_NOLOCKS=$(ts_has_option "nolocks" "$*")
TS_LOCKDIR="$top_builddir/tests/output"
# 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" "$*")
TS_KNOWN_FAIL=$(ts_has_option "known-fail" "$*")
TS_SKIP_LOOPDEVS=$(ts_has_option "skip-loopdevs" "$*")
TS_PARSABLE=$(ts_has_option "parsable" "$*")
[ "$TS_PARSABLE" = "yes" ] || TS_PARSABLE="$TS_PARALLEL"
tmp=$( ts_has_option "memcheck-valgrind" "$*")
if [ "$tmp" == "yes" -a -f /usr/bin/valgrind ]; then
TS_VALGRIND_CMD="/usr/bin/valgrind"
fi
tmp=$( ts_has_option "memcheck-asan" "$*")
if [ "$tmp" == "yes" ]; then
TS_ENABLE_ASAN="yes"
fi
tmp=$( ts_has_option "memcheck-ubsan" "$*")
if [ "$tmp" == "yes" ]; then
TS_ENABLE_UBSAN="yes"
fi
BLKID_FILE="$TS_OUTDIR/${TS_TESTNAME}.blkidtab"
declare -a TS_SUID_PROGS
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
fi
export BLKID_FILE
rm -f $TS_OUTPUT $TS_ERRLOG $TS_VGDUMP $TS_EXIT_CODE
[ -d "$TS_OUTDIR" ] || mkdir -p "$TS_OUTDIR"
touch $TS_OUTPUT $TS_ERRLOG $TS_EXIT_CODE
[ -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"
echo " test name: $TS_TESTNAME"
echo " test desc: $TS_DESC"
echo " component: $TS_COMPONENT"
echo " namespace: $TS_NS"
echo " verbose: $TS_VERBOSE"
echo " output: $TS_OUTPUT"
echo " error log: $TS_ERRLOG"
echo " exit code: $TS_EXIT_CODE"
echo " valgrind: $TS_VGDUMP"
echo " expected: $TS_EXPECTED{.err}"
echo " mountpoint: $TS_MOUNTPOINT"
echo
fi
}
function ts_init_subtest {
TS_SUBNAME="$1"
ts_init_core_subtest_env
TS_NSUBTESTS=$(( $TS_NSUBTESTS + 1 ))
if [ "$TS_PARSABLE" != "yes" ]; then
[ $TS_NSUBTESTS -eq 1 ] && echo
printf "%16s: %-27s ..." "" "$TS_SUBNAME"
fi
}
function ts_init {
ts_init_env "$*"
local is_fake=$( ts_has_option "fake" "$*")
local is_force=$( ts_has_option "force" "$*")
if [ "$TS_PARSABLE" != "yes" ]; then
printf "%13s: %-30s ..." "$TS_COMPONENT" "$TS_DESC"
fi
[ "$is_fake" == "yes" ] && ts_skip "fake mode"
[ "$TS_OPTIONAL" == "yes" -a "$is_force" != "yes" ] && ts_skip "optional"
}
function ts_init_suid {
PROG="$1"
ct=${#TS_SUID_PROGS[*]}
# Save info about original setting
TS_SUID_PROGS[$ct]=$PROG
TS_SUID_USER[$ct]=$(stat --printf="%U" $PROG)
TS_SUID_GROUP[$ct]=$(stat --printf="%G" $PROG)
chown root.root $PROG &> /dev/null
chmod u+s $PROG &> /dev/null
}
function ts_init_py {
LIBNAME="$1"
meson: add second build system To build: meson build && ninja -C build To run tests: ninja -C build check To install for packaging: DESTDIR=/var/tmp/inst ninja -C build install To install for realz: sudo ninja -C build install v2: - Optional items are now based on the 'feature' feature in meson. Built libraries which are disabled turn into disabler() objects and also poison any executables which link to them. What is there: - building of the binaries and libs and the python module - installation of binaries, libs, python module, localization files, man pages, pkgconfig files - running of tests - most options to configure build equivalently to the ./configure settings Partially implemented: - disabling of stuff when things missing. In the C code, the defines are all used, so that should be fine. In the build system, some files should be skipped, but that is probably not always done properly. Getting this right might require some testing of various build option combinations to get the details right. Not implemented: - static builds of fdisk and other binaries - things marked with XXX or FIXME - ??? Differences: - .la files are not created. They are useless and everybody hates them. - Requires.private in pkgconfig files are not present in the autogenerated .pc file. Not sure if they should be there or not. If necessary, they can be added by hand. - man pages and systemd units are installed by the install target. Not sure why 'make install' doesn't do that. - the split between / and /usr is probably wrong. But it's all pointless anyway, so maybe we could simplify things but not implementing it at all under meson?
2020-02-23 12:42:55 -06:00
if [ -f "$top_builddir/py${LIBNAME}.la" ]; then
# autotoolz build
export LD_LIBRARY_PATH="$top_builddir/.libs:$LD_LIBRARY_PATH"
export PYTHONPATH="$top_builddir/$LIBNAME/python:$top_builddir/.libs:$PYTHONPATH"
PYTHON_VERSION=$(awk '/^PYTHON_VERSION/ { print $3 }' $top_builddir/Makefile)
PYTHON_MAJOR_VERSION=$(echo $PYTHON_VERSION | sed 's/\..*//')
meson: add second build system To build: meson build && ninja -C build To run tests: ninja -C build check To install for packaging: DESTDIR=/var/tmp/inst ninja -C build install To install for realz: sudo ninja -C build install v2: - Optional items are now based on the 'feature' feature in meson. Built libraries which are disabled turn into disabler() objects and also poison any executables which link to them. What is there: - building of the binaries and libs and the python module - installation of binaries, libs, python module, localization files, man pages, pkgconfig files - running of tests - most options to configure build equivalently to the ./configure settings Partially implemented: - disabling of stuff when things missing. In the C code, the defines are all used, so that should be fine. In the build system, some files should be skipped, but that is probably not always done properly. Getting this right might require some testing of various build option combinations to get the details right. Not implemented: - static builds of fdisk and other binaries - things marked with XXX or FIXME - ??? Differences: - .la files are not created. They are useless and everybody hates them. - Requires.private in pkgconfig files are not present in the autogenerated .pc file. Not sure if they should be there or not. If necessary, they can be added by hand. - man pages and systemd units are installed by the install target. Not sure why 'make install' doesn't do that. - the split between / and /usr is probably wrong. But it's all pointless anyway, so maybe we could simplify things but not implementing it at all under meson?
2020-02-23 12:42:55 -06:00
export PYTHON="python${PYTHON_MAJOR_VERSION}"
meson: add second build system To build: meson build && ninja -C build To run tests: ninja -C build check To install for packaging: DESTDIR=/var/tmp/inst ninja -C build install To install for realz: sudo ninja -C build install v2: - Optional items are now based on the 'feature' feature in meson. Built libraries which are disabled turn into disabler() objects and also poison any executables which link to them. What is there: - building of the binaries and libs and the python module - installation of binaries, libs, python module, localization files, man pages, pkgconfig files - running of tests - most options to configure build equivalently to the ./configure settings Partially implemented: - disabling of stuff when things missing. In the C code, the defines are all used, so that should be fine. In the build system, some files should be skipped, but that is probably not always done properly. Getting this right might require some testing of various build option combinations to get the details right. Not implemented: - static builds of fdisk and other binaries - things marked with XXX or FIXME - ??? Differences: - .la files are not created. They are useless and everybody hates them. - Requires.private in pkgconfig files are not present in the autogenerated .pc file. Not sure if they should be there or not. If necessary, they can be added by hand. - man pages and systemd units are installed by the install target. Not sure why 'make install' doesn't do that. - the split between / and /usr is probably wrong. But it's all pointless anyway, so maybe we could simplify things but not implementing it at all under meson?
2020-02-23 12:42:55 -06:00
elif compgen -G "$top_builddir/$LIBNAME/python/py$LIBNAME*.so" >/dev/null; then
# mezon!
export PYTHONPATH="$top_builddir/$LIBNAME/python:$PYTHONPATH"
meson: add second build system To build: meson build && ninja -C build To run tests: ninja -C build check To install for packaging: DESTDIR=/var/tmp/inst ninja -C build install To install for realz: sudo ninja -C build install v2: - Optional items are now based on the 'feature' feature in meson. Built libraries which are disabled turn into disabler() objects and also poison any executables which link to them. What is there: - building of the binaries and libs and the python module - installation of binaries, libs, python module, localization files, man pages, pkgconfig files - running of tests - most options to configure build equivalently to the ./configure settings Partially implemented: - disabling of stuff when things missing. In the C code, the defines are all used, so that should be fine. In the build system, some files should be skipped, but that is probably not always done properly. Getting this right might require some testing of various build option combinations to get the details right. Not implemented: - static builds of fdisk and other binaries - things marked with XXX or FIXME - ??? Differences: - .la files are not created. They are useless and everybody hates them. - Requires.private in pkgconfig files are not present in the autogenerated .pc file. Not sure if they should be there or not. If necessary, they can be added by hand. - man pages and systemd units are installed by the install target. Not sure why 'make install' doesn't do that. - the split between / and /usr is probably wrong. But it's all pointless anyway, so maybe we could simplify things but not implementing it at all under meson?
2020-02-23 12:42:55 -06:00
else
ts_skip "py${LIBNAME} not compiled"
fi
}
function ts_run {
declare -a args
local asan_options="strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1"
#
# ASAN mode
#
if [ "$TS_ENABLE_ASAN" == "yes" -o "$TS_ENABLE_UBSAN" == "yes" ]; then
args+=(env)
if [ "$TS_ENABLE_ASAN" == "yes" ]; then
# detect_leaks isn't supported on s390x: https://github.com/llvm/llvm-project/blob/master/compiler-rt/lib/lsan/lsan_common.h
if [ "$(uname -m)" != "s390x" ]; then
asan_options="$asan_options:detect_leaks=1"
fi
args+=(ASAN_OPTIONS=$asan_options)
fi
if [ "$TS_ENABLE_UBSAN" == "yes" ]; then
args+=(UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1)
fi
fi
#
# valgrind mode
#
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[@]}" "$@"
echo $? >$TS_EXIT_CODE
}
function ts_gen_diff_from {
local res=0
local expected="$1"
local output="$2"
local difffile="$3"
diff -u $expected $output > $difffile
if [ $? -ne 0 ] || [ -s $difffile ]; then
res=1
if [ "$TS_SHOWDIFF" == "yes" -a "$TS_KNOWN_FAIL" != "yes" ]; then
echo
echo "diff-{{{"
cat $difffile
echo "}}}-diff"
echo
fi
else
rm -f $difffile;
fi
return $res
}
function ts_gen_diff {
local status_out=0
local status_err=0
local exit_code=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"
# error log is fully optional
[ -f "$TS_EXPECTED_ERR" ] || TS_EXPECTED_ERR=/dev/null
[ -f "$TS_ERRLOG" ] || TS_ERRLOG=/dev/null
if [ "$TS_COMPONENT" != "fuzzers" ]; then
ts_gen_diff_from $TS_EXPECTED $TS_OUTPUT $TS_DIFF
status_out=$?
ts_gen_diff_from $TS_EXPECTED_ERR $TS_ERRLOG $TS_DIFF.err
status_err=$?
else
# TS_EXIT_CODE is empty when tests aren't run with ts_run: https://github.com/karelzak/util-linux/issues/1072
# or when ts_finalize is called right after ts_finalize_subtest.
exit_code="$(cat $TS_EXIT_CODE)"
if [ -z "$exit_code" ]; then
exit_code=0
fi
if [ $exit_code -ne 0 ]; then
ts_gen_diff_from $TS_EXPECTED $TS_OUTPUT $TS_DIFF
ts_gen_diff_from $TS_EXPECTED_ERR $TS_ERRLOG $TS_DIFF.err
fi
fi
if [ $status_out -ne 0 -o $status_err -ne 0 -o $exit_code -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
if [ $? -eq 0 ]; then
echo "mem-error detected!"
fi
else
echo "$1"
fi
}
function ts_finalize_subtest {
local res=0
ts_gen_diff
if [ $? -eq 1 ]; then
ts_failed_subtest "$1"
res=1
else
ts_report_ok "$(tt_gen_mem_report "$1")"
fi
[ $res -ne 0 ] && TS_NSUBFAILED=$(( $TS_NSUBFAILED + 1 ))
# reset environment back to parental test
ts_init_core_env
return $res
}
function ts_skip_subtest {
ts_report_skip "$1"
# reset environment back to parental test
ts_init_core_env
}
function ts_finalize {
ts_cleanup_on_exit
if [ $TS_NSUBTESTS -ne 0 ]; then
if ! ts_gen_diff || [ $TS_NSUBFAILED -ne 0 ]; then
ts_failed "$TS_NSUBFAILED from $TS_NSUBTESTS sub-tests"
else
ts_ok "all $TS_NSUBTESTS sub-tests PASSED"
fi
fi
ts_gen_diff || ts_failed "$1"
ts_ok "$1"
}
function ts_die {
ts_log "$1"
ts_finalize
}
function ts_cleanup_on_exit {
for idx in $(seq 0 $((${#TS_SUID_PROGS[*]} - 1))); do
PROG=${TS_SUID_PROGS[$idx]}
chmod a-s $PROG &> /dev/null
chown ${TS_SUID_USER[$idx]}.${TS_SUID_GROUP[$idx]} $PROG &> /dev/null
done
for dev in "${TS_LOOP_DEVS[@]}"; do
ts_device_deinit "$dev"
done
unset TS_LOOP_DEVS
ts_scsi_debug_rmmod
}
function ts_image_md5sum {
local img=${1:-"$TS_OUTDIR/${TS_TESTNAME}.img"}
echo $("$TS_HELPER_MD5" < "$img") $(basename "$img")
}
function ts_image_init {
local mib=${1:-"5"} # size in MiBs
local img=${2:-"$TS_OUTDIR/${TS_TESTNAME}.img"}
rm -f $img
truncate -s "${mib}M" "$img"
echo "$img"
return 0
}
function ts_register_loop_device {
local ct=${#TS_LOOP_DEVS[*]}
TS_LOOP_DEVS[$ct]=$1
}
function ts_device_init {
local img
local dev
img=$(ts_image_init $1 $2)
dev=$($TS_CMD_LOSETUP --show -f "$img")
if [ "$?" != "0" -o "$dev" = "" ]; then
ts_die "Cannot init device"
fi
ts_register_loop_device "$dev"
TS_LODEV=$dev
}
# call from ts_cleanup_on_exit() only because of TS_LOOP_DEVS maintenance
function ts_device_deinit {
local DEV="$1"
if [ -b "$DEV" ]; then
$TS_CMD_UMOUNT "$DEV" &> /dev/null
$TS_CMD_LOSETUP -d "$DEV" &> /dev/null
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 {
ts_blkidtag_by_devname "UUID" "$1"
return $?
}
function ts_label_by_devname {
ts_blkidtag_by_devname "LABEL" "$1"
return $?
}
function ts_fstype_by_devname {
ts_blkidtag_by_devname "TYPE" "$1"
return $?
}
function ts_vfs_dump {
if [ "$TS_SHOWDIFF" == "yes" -a "$TS_KNOWN_FAIL" != "yes" ]; then
echo
echo "{{{{ VFS dump:"
findmnt
echo "}}}}"
fi
}
function ts_blk_dump {
if [ "$TS_SHOWDIFF" == "yes" -a "$TS_KNOWN_FAIL" != "yes" ]; then
echo
echo "{{{{ blkdevs dump:"
lsblk -o+FSTYPE
echo "}}}}"
fi
}
function ts_device_has {
local TAG="$1"
local VAL="$2"
local DEV="$3"
local vl=""
local res=""
vl=$(ts_blkidtag_by_devname "$TAG" "$DEV")
test $? = 0 -a "$vl" = "$VAL"
res=$?
if [ "$res" != 0 ]; then
ts_vfs_dump
ts_blk_dump
fi
return $res
}
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
local msg
local fs
local fs_exp=$1
shift
out=$($TS_CMD_MOUNT "$@" 2>&1)
result=$?
echo -n "$out" >> $TS_OUTPUT
if [ $result != 0 ] \
&& msg=$(echo "$out" | grep -m1 "unknown filesystem type")
then
# skip only if reported fs correctly and if it's not available
fs=$(echo "$msg" | sed -n "s/.*type '\(.*\)'$/\1/p")
[ "$fs" = "fs_exp" ] \
&& grep -qe "[[:space:]]${fs}$" /proc/filesystems &>/dev/null \
|| ts_skip "$msg"
fi
return $result
}
function ts_is_mounted {
local DEV=$(ts_canonicalize "$1")
grep -q "\(^\| \)$DEV " /proc/mounts && return 0
if [ "${DEV#/dev/loop/}" != "$DEV" ]; then
grep -q "^/dev/loop${DEV#/dev/loop/} " /proc/mounts && return 0
fi
return 1
}
function ts_fstab_open {
echo "# <!-- util-linux test entry" >> /etc/fstab
}
function ts_fstab_close {
echo "# -->" >> /etc/fstab
sync /etc/fstab 2>/dev/null
}
function ts_fstab_addline {
local SPEC="$1"
local MNT=${2:-"$TS_MOUNTPOINT"}
local FS=${3:-"auto"}
local OPT=${4:-"defaults"}
echo "$SPEC $MNT $FS $OPT 0 0" >> /etc/fstab
}
function ts_fstab_lock {
ts_lock "fstab"
}
function ts_fstab_add {
ts_fstab_lock
ts_fstab_open
ts_fstab_addline $*
ts_fstab_close
}
function ts_fstab_clean {
ts_have_lock "fstab" || return 0
sed --in-place "
/# <!-- util-linux/!b
:a
/# -->/!{
N
ba
}
s/# <!-- util-linux.*-->//;
/^$/d" /etc/fstab
sync /etc/fstab 2>/dev/null
ts_unlock "fstab"
}
function ts_fdisk_clean {
2013-09-18 08:38:33 -05:00
local DEVNAME=$1
# remove non comparable parts of fdisk output
if [ -n "${DEVNAME}" ]; then
sed -i -e "s@${DEVNAME}@<removed>@;" $TS_OUTPUT $TS_ERRLOG
fi
sed -i \
-e 's/Disk identifier:.*/Disk identifier: <removed>/' \
-e 's/Created a new partition.*/Created a new partition <removed>./' \
-e 's/Created a new .* disklabel .*/Created a new disklabel./' \
-e 's/^Device[[:blank:]]*Start/Device Start/' \
-e 's/^Device[[:blank:]]*Boot/Device Boot/' \
-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_ERRLOG
}
tests: don't lock fd 1 (stdout), don't use /proc/$$/fd On debian-kfreebsd we've locked stdout which messed up our test logs. Using /proc/*/fd/ is not portable. Even ts_init's test for "/proc/self/fd" does not help because /proc/*/fd behaves strange here: $ ls -l /proc/$$/fd lr--r--r-- 1 rudi user 0 Mar 6 23:11 /proc/2194/fd -> unknown $ file /proc/$$/fd /proc/2194/fd: broken symbolic link to `unknown' ## wtf? $ test -d /proc/$$/fd; echo $? 0 $ ls -l /proc/$$/fd/ ls: cannot access /proc/2194/fd/: No such file or directory ## but $ ls -l /proc/self/fd/ total 0 cr-xr-xr-x 1 root root 0, 3 Mar 6 19:39 0 cr-xr-xr-x 1 root root 0, 4 Mar 6 19:39 1 cr-xr-xr-x 1 root root 0, 5 Mar 6 19:39 2 cr-xr-xr-x 1 root root 0, 6 Mar 6 19:39 3 This is how this patch changes the test output: [...] blkid: partitions probing: [06] sgi ... OK blkid: partitions probing: [07] sun ... OK blkid: partitions probing ... OK (all 7 sub-tests PASSED) -ls: cannot access /proc/66215/fd/: No such file or directory + blkid: mbr-wholedisk ... SKIPPED (missing scsi_debug module (dry-run)) blkid: MD raid0 (whole-disks) ... SKIPPED (losetup not found) blkid: MD raid1 (last partition) ... SKIPPED (missing in PATH: mdadm) blkid: MD raid1 (whole-disks) ... SKIPPED (losetup not found) @@ -343,11 +343,11 @@ dmesg: facilities ... SKIPPED (test_dmesg not found) dmesg: indentation ... SKIPPED (test_dmesg not found) eject: umount ... SKIPPED (eject not found) -ls: cannot access /proc/69561/fd/: No such file or directory -ls: cannot access /proc/69609/fd/: No such file or directory + fdisk: align 512/4K ... SKIPPED (missing scsi_debug module (dry-run)) + fdisk: align 512/4K +alignment_offset ... SKIPPED (missing scsi_debug module (dry-run)) fdisk: align 512/4K +MD ... SKIPPED (missing in PATH: mdadm) fdisk: align 512/512 ... SKIPPED (losetup not found) [...] Signed-off-by: Ruediger Meier <ruediger.meier@ga-group.nl>
2018-03-06 17:29:59 -06:00
# 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 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
test "$TS_NOLOCKS" = "yes" && return 0
ts_get_lock_fd "$resource" >/dev/null && return 0
return 1
}
function ts_lock {
local resource="$1"
local lockfile="${TS_LOCKDIR}/${resource}.lock"
local fd
if [ "$TS_NOLOCKS" == "yes" ]; then
return 0
fi
# Don't lock again
fd=$(ts_get_lock_fd "$resource")
if [ -n "$fd" ]; then
echo "[$$ $TS_TESTNAME] ${resource} already locked!"
return 0
fi
tests: don't lock fd 1 (stdout), don't use /proc/$$/fd On debian-kfreebsd we've locked stdout which messed up our test logs. Using /proc/*/fd/ is not portable. Even ts_init's test for "/proc/self/fd" does not help because /proc/*/fd behaves strange here: $ ls -l /proc/$$/fd lr--r--r-- 1 rudi user 0 Mar 6 23:11 /proc/2194/fd -> unknown $ file /proc/$$/fd /proc/2194/fd: broken symbolic link to `unknown' ## wtf? $ test -d /proc/$$/fd; echo $? 0 $ ls -l /proc/$$/fd/ ls: cannot access /proc/2194/fd/: No such file or directory ## but $ ls -l /proc/self/fd/ total 0 cr-xr-xr-x 1 root root 0, 3 Mar 6 19:39 0 cr-xr-xr-x 1 root root 0, 4 Mar 6 19:39 1 cr-xr-xr-x 1 root root 0, 5 Mar 6 19:39 2 cr-xr-xr-x 1 root root 0, 6 Mar 6 19:39 3 This is how this patch changes the test output: [...] blkid: partitions probing: [06] sgi ... OK blkid: partitions probing: [07] sun ... OK blkid: partitions probing ... OK (all 7 sub-tests PASSED) -ls: cannot access /proc/66215/fd/: No such file or directory + blkid: mbr-wholedisk ... SKIPPED (missing scsi_debug module (dry-run)) blkid: MD raid0 (whole-disks) ... SKIPPED (losetup not found) blkid: MD raid1 (last partition) ... SKIPPED (missing in PATH: mdadm) blkid: MD raid1 (whole-disks) ... SKIPPED (losetup not found) @@ -343,11 +343,11 @@ dmesg: facilities ... SKIPPED (test_dmesg not found) dmesg: indentation ... SKIPPED (test_dmesg not found) eject: umount ... SKIPPED (eject not found) -ls: cannot access /proc/69561/fd/: No such file or directory -ls: cannot access /proc/69609/fd/: No such file or directory + fdisk: align 512/4K ... SKIPPED (missing scsi_debug module (dry-run)) + fdisk: align 512/4K +alignment_offset ... SKIPPED (missing scsi_debug module (dry-run)) fdisk: align 512/4K +MD ... SKIPPED (missing in PATH: mdadm) fdisk: align 512/512 ... SKIPPED (losetup not found) [...] Signed-off-by: Ruediger Meier <ruediger.meier@ga-group.nl>
2018-03-06 17:29:59 -06:00
fd=$(ts_find_free_fd) || ts_skip "failed to find lock fd"
eval "exec $fd>$lockfile"
flock --exclusive "$fd" || ts_skip "failed to lock $resource"
TS_LOCKFILE_FD["$fd"]="$resource"
###echo "[$$ $TS_TESTNAME] Locked $resource"
}
function ts_unlock {
local resource="$1"
local lockfile="${TS_LOCKDIR}/${resource}.lock"
local fd
if [ "$TS_NOLOCKS" == "yes" ]; then
return 0
fi
fd=$(ts_get_lock_fd "$resource")
if [ -n "$fd" ]; then
eval "exec $fd<&-"
TS_LOCKFILE_FD["$fd"]=""
tests: don't lock fd 1 (stdout), don't use /proc/$$/fd On debian-kfreebsd we've locked stdout which messed up our test logs. Using /proc/*/fd/ is not portable. Even ts_init's test for "/proc/self/fd" does not help because /proc/*/fd behaves strange here: $ ls -l /proc/$$/fd lr--r--r-- 1 rudi user 0 Mar 6 23:11 /proc/2194/fd -> unknown $ file /proc/$$/fd /proc/2194/fd: broken symbolic link to `unknown' ## wtf? $ test -d /proc/$$/fd; echo $? 0 $ ls -l /proc/$$/fd/ ls: cannot access /proc/2194/fd/: No such file or directory ## but $ ls -l /proc/self/fd/ total 0 cr-xr-xr-x 1 root root 0, 3 Mar 6 19:39 0 cr-xr-xr-x 1 root root 0, 4 Mar 6 19:39 1 cr-xr-xr-x 1 root root 0, 5 Mar 6 19:39 2 cr-xr-xr-x 1 root root 0, 6 Mar 6 19:39 3 This is how this patch changes the test output: [...] blkid: partitions probing: [06] sgi ... OK blkid: partitions probing: [07] sun ... OK blkid: partitions probing ... OK (all 7 sub-tests PASSED) -ls: cannot access /proc/66215/fd/: No such file or directory + blkid: mbr-wholedisk ... SKIPPED (missing scsi_debug module (dry-run)) blkid: MD raid0 (whole-disks) ... SKIPPED (losetup not found) blkid: MD raid1 (last partition) ... SKIPPED (missing in PATH: mdadm) blkid: MD raid1 (whole-disks) ... SKIPPED (losetup not found) @@ -343,11 +343,11 @@ dmesg: facilities ... SKIPPED (test_dmesg not found) dmesg: indentation ... SKIPPED (test_dmesg not found) eject: umount ... SKIPPED (eject not found) -ls: cannot access /proc/69561/fd/: No such file or directory -ls: cannot access /proc/69609/fd/: No such file or directory + fdisk: align 512/4K ... SKIPPED (missing scsi_debug module (dry-run)) + fdisk: align 512/4K +alignment_offset ... SKIPPED (missing scsi_debug module (dry-run)) fdisk: align 512/4K +MD ... SKIPPED (missing in PATH: mdadm) fdisk: align 512/512 ... SKIPPED (losetup not found) [...] Signed-off-by: Ruediger Meier <ruediger.meier@ga-group.nl>
2018-03-06 17:29:59 -06:00
###echo "[$$ $TS_TESTNAME] Unlocked $resource"
else
echo "[$$ $TS_TESTNAME] unlocking unlocked $resource!?"
fi
}
function ts_scsi_debug_init {
local devname
local t
TS_DEVICE="none"
ts_lock "scsi_debug"
# dry run is not really reliable, real modprobe may still fail
modprobe --dry-run --quiet scsi_debug &>/dev/null \
|| ts_skip "missing scsi_debug module (dry-run)"
# skip if still in use or removal of modules not supported at all
# We don't want a slow timeout here so we don't use ts_scsi_debug_rmmod!
modprobe -r scsi_debug &>/dev/null
if [ "$?" -eq 1 ]; then
ts_unlock "scsi_debug"
ts_skip "cannot remove scsi_debug module (rmmod)"
fi
modprobe -b scsi_debug "$@" &>/dev/null \
|| ts_skip "cannot load scsi_debug module (modprobe)"
# it might be still not loaded, modprobe.conf or whatever
lsmod 2>/dev/null | grep -q "^scsi_debug " \
|| ts_skip "scsi_debug module not loaded (lsmod)"
udevadm settle
# wait for device if udevadm settle does not work
for t in 0 0.02 0.05 0.1 1; do
sleep $t
devname=$(grep --no-messages --with-filename scsi_debug /sys/block/*/device/model) && break
done
[ -n "${devname}" ] || ts_skip "timeout waiting for scsi_debug device"
devname=$(echo $devname | awk -F '/' '{print $4}')
TS_DEVICE="/dev/${devname}"
# TODO validate that device is really up, for now just a warning on stderr
test -b $TS_DEVICE || echo "warning: scsi_debug device is still down" >&2
}
# automatically called once in ts_cleanup_on_exit()
function ts_scsi_debug_rmmod {
local err=1
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
[ -n "$TS_DEVICE" ] || return 0
lsmod 2>/dev/null | grep -q "^scsi_debug " || return 0
udevadm settle
# wait for successful rmmod if udevadm settle does not work
for t in 0 0.02 0.05 0.1 1; do
sleep $t
lastmsg="$(modprobe -r scsi_debug 2>&1)" && err=0 && break
done
if [ "$err" = "1" ]; then
ts_log "rmmod failed: '$lastmsg'"
ts_log "timeout removing scsi_debug module (rmmod)"
return 1
fi
if lsmod | grep -q "^scsi_debug "; then
ts_log "BUG! scsi_debug still loaded"
return 1
fi
# TODO Do we need to validate that all devices are gone?
udevadm settle
test -b "$TS_DEVICE" && echo "warning: scsi_debug device is still up" >&2
# TODO unset TS_DEVICE, check that nobody uses it later, e.g. ts_fdisk_clean
ts_unlock "scsi_debug"
return 0
}
function ts_resolve_host {
local host="$1"
local tmp
# currently we just resolve default records (might be "A", ipv4 only)
if type "dig" >/dev/null 2>&1; then
tmp=$(dig "$host" +short 2>/dev/null) || return 1
elif type "nslookup" >/dev/null 2>&1; then
tmp=$(nslookup "$host" 2>/dev/null) || return 1
tmp=$(echo "$tmp"| grep -A1 "^Name:"| grep "^Address:"| cut -d" " -f2)
elif type "host" >/dev/null 2>&1; then
tmp=$(host "$host" 2>/dev/null) || return 1
tmp=$(echo "$tmp" | grep " has address " | cut -d " " -f4)
elif type "getent" >/dev/null 2>&1; then
tmp=$(getent ahosts "$host" 2>/dev/null) || return 1
tmp=$(echo "$tmp" | cut -d " " -f 1 | sort -u)
fi
# we return 1 if tmp is empty
test -n "$tmp" || return 1
echo "$tmp" | sort -R | head -n 1
}
# listen to unix socket (background socat)
function ts_init_socket_to_file {
local socket=$1
local outfile=$2
local pid="0"
ts_check_prog "socat"
rm -f "$socket" "$outfile"
# if socat is too old for these options we'll skip it below
socat -u UNIX-LISTEN:$socket,fork,max-children=1,backlog=128 \
STDOUT > "$outfile" 2>/dev/null &
pid=$!
# check for running background process
if [ "$pid" -le "0" ] || ! kill -s 0 "$pid" &>/dev/null; then
ts_skip "unable to run socat"
fi
# wait for the socket listener
if ! socat -u /dev/null UNIX-CONNECT:$socket,retry=30,interval=0.1 &>/dev/null; then
kill -9 "$pid" &>/dev/null
ts_skip "timeout waiting for socat socket"
fi
# check socket again
if ! socat -u /dev/null UNIX-CONNECT:$socket &>/dev/null; then
kill -9 "$pid" &>/dev/null
ts_skip "socat socket stopped listening"
fi
}
function ts_has_mtab_support {
grep -q '#define USE_LIBMOUNT_SUPPORT_MTAB' ${top_builddir}/config.h
if [ $? == 0 ]; then
echo "yes"
else
echo "no"
fi
}
function ts_has_ncurses_support {
grep -q '#define HAVE_LIBNCURSES' ${top_builddir}/config.h
if [ $? == 0 ]; then
echo "yes"
else
echo "no"
fi
}
# Get path to the ASan runtime DSO the given binary was compiled with
function ts_get_asan_rt_path {
local binary="${1?}"
local rt_path
ts_check_prog "ldd"
ts_check_prog "awk"
rt_path="$(ldd "$binary" | awk '/lib.+asan.*.so/ {print $3; exit}')"
if [ -n "$rt_path" -a -f "$rt_path" ]; then
echo "$rt_path"
fi
}