| #!/bin/sh |
| # SPDX-License-Identifier: GPL-2.0-or-later |
| # Copyright (c) Linux Test Project, 2014-2022 |
| # Author: Cyril Hrubis <chrubis@suse.cz> |
| # |
| # LTP test library for shell. |
| |
| [ -n "$TST_LIB_LOADED" ] && return 0 |
| |
| export TST_PASS=0 |
| export TST_FAIL=0 |
| export TST_BROK=0 |
| export TST_WARN=0 |
| export TST_CONF=0 |
| export TST_COUNT=1 |
| export TST_ITERATIONS=1 |
| export TST_TMPDIR_RHOST=0 |
| export TST_LIB_LOADED=1 |
| |
| if [ -z "$TST_FS_TYPE" ]; then |
| export TST_FS_TYPE="${LTP_DEV_FS_TYPE:-ext2}" |
| fi |
| |
| . tst_ansi_color.sh |
| . tst_security.sh |
| |
| # default trap function |
| trap "tst_brk TBROK 'test interrupted'" INT |
| trap "unset _tst_setup_timer_pid; tst_brk TBROK 'test terminated'" TERM |
| |
| _tst_do_cleanup() |
| { |
| if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$LTP_NO_CLEANUP" ]; then |
| if command -v $TST_CLEANUP >/dev/null 2>/dev/null; then |
| TST_DO_CLEANUP= |
| $TST_CLEANUP |
| else |
| tst_res TWARN "TST_CLEANUP=$TST_CLEANUP declared, but function not defined (or cmd not found)" |
| fi |
| fi |
| TST_DO_CLEANUP= |
| } |
| |
| _tst_do_exit() |
| { |
| local ret=0 |
| TST_DO_EXIT=1 |
| |
| _tst_do_cleanup |
| |
| cd "$LTPROOT" |
| [ "$TST_MOUNT_FLAG" = 1 ] && tst_umount |
| |
| if [ "$TST_NEEDS_DEVICE" = 1 -a "$TST_DEVICE_FLAG" = 1 ]; then |
| if ! tst_device release "$TST_DEVICE"; then |
| tst_res TWARN "Failed to release device '$TST_DEVICE'" |
| fi |
| fi |
| |
| if [ "$TST_NEEDS_TMPDIR" = 1 -a -n "$TST_TMPDIR" ]; then |
| rm -r "$TST_TMPDIR" |
| [ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost |
| fi |
| |
| if [ -n "$TST_NEEDS_CHECKPOINTS" -a -f "$LTP_IPC_PATH" ]; then |
| rm $LTP_IPC_PATH |
| fi |
| |
| _tst_cleanup_timer |
| |
| if [ $TST_FAIL -gt 0 ]; then |
| ret=$((ret|1)) |
| fi |
| |
| if [ $TST_BROK -gt 0 ]; then |
| ret=$((ret|2)) |
| fi |
| |
| if [ $TST_WARN -gt 0 ]; then |
| ret=$((ret|4)) |
| fi |
| |
| if [ $TST_CONF -gt 0 -a $TST_PASS -eq 0 ]; then |
| ret=$((ret|32)) |
| fi |
| |
| if [ $TST_BROK -gt 0 -o $TST_FAIL -gt 0 -o $TST_WARN -gt 0 ]; then |
| _tst_check_security_modules |
| fi |
| |
| cat >&2 << EOF |
| |
| Summary: |
| passed $TST_PASS |
| failed $TST_FAIL |
| broken $TST_BROK |
| skipped $TST_CONF |
| warnings $TST_WARN |
| EOF |
| |
| exit $ret |
| } |
| |
| _tst_inc_res() |
| { |
| case "$1" in |
| TPASS) TST_PASS=$((TST_PASS+1));; |
| TFAIL) TST_FAIL=$((TST_FAIL+1));; |
| TBROK) TST_BROK=$((TST_BROK+1));; |
| TWARN) TST_WARN=$((TST_WARN+1));; |
| TCONF) TST_CONF=$((TST_CONF+1));; |
| TINFO) ;; |
| *) tst_brk TBROK "Invalid res type '$1'";; |
| esac |
| } |
| |
| tst_res() |
| { |
| local res=$1 |
| shift |
| |
| _tst_inc_res "$res" |
| |
| printf "$TST_ID $TST_COUNT " >&2 |
| tst_print_colored $res "$res: " >&2 |
| echo "$@" >&2 |
| } |
| |
| tst_brk() |
| { |
| local res=$1 |
| shift |
| |
| # TBROK => TWARN on cleanup or exit |
| if [ "$res" = TBROK ] && [ "$TST_DO_EXIT" = 1 -o -z "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" ]; then |
| tst_res TWARN "$@" |
| TST_DO_CLEANUP= |
| return |
| fi |
| |
| if [ "$res" != TBROK -a "$res" != TCONF ]; then |
| tst_res TBROK "tst_brk can be called only with TBROK or TCONF ($res)" |
| else |
| tst_res "$res" "$@" |
| fi |
| |
| _tst_do_exit |
| } |
| |
| ROD_SILENT() |
| { |
| local tst_out |
| |
| tst_out="$(tst_rod $@ 2>&1)" |
| if [ $? -ne 0 ]; then |
| echo "$tst_out" |
| tst_brk TBROK "$@ failed" |
| fi |
| } |
| |
| ROD() |
| { |
| tst_rod "$@" |
| if [ $? -ne 0 ]; then |
| tst_brk TBROK "$@ failed" |
| fi |
| } |
| |
| _tst_expect_pass() |
| { |
| local fnc="$1" |
| shift |
| |
| tst_rod "$@" |
| if [ $? -eq 0 ]; then |
| tst_res TPASS "$@ passed as expected" |
| return 0 |
| else |
| $fnc TFAIL "$@ failed unexpectedly" |
| return 1 |
| fi |
| } |
| |
| _tst_expect_fail() |
| { |
| local fnc="$1" |
| shift |
| |
| # redirect stderr since we expect the command to fail |
| tst_rod "$@" 2> /dev/null |
| if [ $? -ne 0 ]; then |
| tst_res TPASS "$@ failed as expected" |
| return 0 |
| else |
| $fnc TFAIL "$@ passed unexpectedly" |
| return 1 |
| fi |
| } |
| |
| EXPECT_PASS() |
| { |
| _tst_expect_pass tst_res "$@" |
| } |
| |
| EXPECT_PASS_BRK() |
| { |
| _tst_expect_pass tst_brk "$@" |
| } |
| |
| EXPECT_FAIL() |
| { |
| _tst_expect_fail tst_res "$@" |
| } |
| |
| EXPECT_FAIL_BRK() |
| { |
| _tst_expect_fail tst_brk "$@" |
| } |
| |
| TST_RETRY_FN_EXP_BACKOFF() |
| { |
| local tst_fun="$1" |
| local tst_exp=$2 |
| local tst_sec=$(($3 * 1000000)) |
| local tst_delay=1 |
| |
| _tst_multiply_timeout tst_sec |
| |
| if [ $# -ne 3 ]; then |
| tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF expects 3 parameters" |
| fi |
| |
| if ! tst_is_int "$tst_sec"; then |
| tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF: tst_sec must be integer ('$tst_sec')" |
| fi |
| |
| while true; do |
| eval "$tst_fun" |
| if [ "$?" = "$tst_exp" ]; then |
| break |
| fi |
| |
| if [ $tst_delay -lt $tst_sec ]; then |
| tst_sleep ${tst_delay}us |
| tst_delay=$((tst_delay*2)) |
| else |
| tst_brk TBROK "\"$tst_fun\" timed out" |
| fi |
| done |
| |
| return $tst_exp |
| } |
| |
| TST_RETRY_FUNC() |
| { |
| if [ $# -ne 2 ]; then |
| tst_brk TBROK "TST_RETRY_FUNC expects 2 parameters" |
| fi |
| |
| TST_RETRY_FN_EXP_BACKOFF "$1" "$2" 1 |
| return $2 |
| } |
| |
| TST_RTNL_CHK() |
| { |
| local msg1="RTNETLINK answers: Function not implemented" |
| local msg2="RTNETLINK answers: Operation not supported" |
| local msg3="RTNETLINK answers: Protocol not supported" |
| local output="$($@ 2>&1 || echo 'LTP_ERR')" |
| local msg |
| |
| echo "$output" | grep -q "LTP_ERR" || return 0 |
| |
| for msg in "$msg1" "$msg2" "$msg3"; do |
| echo "$output" | grep -q "$msg" && tst_brk TCONF "'$@': $msg" |
| done |
| |
| tst_brk TBROK "$@ failed: $output" |
| } |
| |
| TST_CHECKPOINT_WAIT() |
| { |
| ROD tst_checkpoint wait 10000 "$1" |
| } |
| |
| TST_CHECKPOINT_WAKE() |
| { |
| ROD tst_checkpoint wake 10000 "$1" 1 |
| } |
| |
| TST_CHECKPOINT_WAKE2() |
| { |
| ROD tst_checkpoint wake 10000 "$1" "$2" |
| } |
| |
| TST_CHECKPOINT_WAKE_AND_WAIT() |
| { |
| TST_CHECKPOINT_WAKE "$1" |
| TST_CHECKPOINT_WAIT "$1" |
| } |
| |
| tst_mount() |
| { |
| local mnt_opt mnt_err mnt_real |
| |
| if [ -n "$TST_FS_TYPE" ]; then |
| mnt_opt="-t $TST_FS_TYPE" |
| mnt_err=" $TST_FS_TYPE type" |
| fi |
| local cmd="mount $mnt_opt $TST_DEVICE $TST_MNTPOINT $TST_MNT_PARAMS" |
| |
| ROD_SILENT mkdir -p $TST_MNTPOINT |
| tst_res TINFO "Mounting device: $cmd" |
| $cmd |
| local ret=$? |
| |
| if [ $ret -eq 32 ]; then |
| tst_brk TCONF "Cannot mount${mnt_err}, missing driver?" |
| fi |
| |
| if [ $ret -ne 0 ]; then |
| tst_brk TBROK "Failed to mount device${mnt_err}: mount exit = $ret" |
| fi |
| } |
| |
| tst_umount() |
| { |
| local mntpoint="${1:-$TST_MNTPOINT}" |
| local i=0 |
| |
| [ -z "$mntpoint" ] && return |
| |
| if ! echo "$mntpoint" | grep -q ^/; then |
| tst_brk TCONF "The '$mntpoint' is not an absolute path" |
| fi |
| |
| if ! grep -q "${mntpoint%/}" /proc/mounts; then |
| tst_res TINFO "The '$mntpoint' is not mounted, skipping umount" |
| return |
| fi |
| |
| while [ "$i" -lt 50 ]; do |
| if umount "$mntpoint" > /dev/null; then |
| return |
| fi |
| |
| i=$((i+1)) |
| |
| tst_res TINFO "umount($mntpoint) failed, try $i ..." |
| tst_res TINFO "Likely gvfsd-trash is probing newly mounted "\ |
| "fs, kill it to speed up tests." |
| |
| tst_sleep 100ms |
| done |
| |
| tst_res TWARN "Failed to umount($mntpoint) after 50 retries" |
| } |
| |
| tst_mkfs() |
| { |
| local opts |
| local fs_type=${1:-$TST_FS_TYPE} |
| [ $# -ge 1 ] && shift |
| |
| opts="$@" |
| |
| if [ "$fs_type" = tmpfs ]; then |
| tst_res TINFO "Skipping mkfs for TMPFS filesystem" |
| return |
| fi |
| |
| if [ -z "$opts" ]; then |
| if [ "$TST_NEEDS_DEVICE" != 1 ]; then |
| tst_brk "Using default parameters in tst_mkfs requires TST_NEEDS_DEVICE=1" |
| fi |
| opts="$TST_DEVICE" |
| fi |
| |
| tst_require_cmds mkfs.$fs_type |
| |
| tst_res TINFO "Formatting $fs_type with opts='$opts'" |
| ROD_SILENT mkfs.$fs_type $opts |
| } |
| |
| # Detect whether running under hypervisor: Microsoft Hyper-V |
| # Return 0: running under Hyper-V |
| # Return 1: not running under Hyper-V (bare metal, other hypervisor or |
| # failure of detection) |
| tst_virt_hyperv() |
| { |
| local v |
| |
| tst_cmd_available systemd-detect-virt || return 1 |
| |
| v="$(systemd-detect-virt)" |
| |
| [ $? -eq 0 ] || return 1 |
| [ "$v" = "microsoft" ] || return 1 |
| |
| return 0 |
| } |
| |
| tst_cmd_available() |
| { |
| command -v $1 >/dev/null 2>&1 |
| } |
| |
| tst_require_cmds() |
| { |
| local cmd |
| for cmd in $*; do |
| tst_cmd_available $cmd || tst_brk TCONF "'$cmd' not found" |
| done |
| } |
| |
| tst_check_cmds() |
| { |
| local cmd |
| for cmd in $*; do |
| if ! tst_cmd_available $cmd; then |
| tst_res TCONF "'$cmd' not found" |
| return 1 |
| fi |
| done |
| return 0 |
| } |
| |
| tst_require_drivers() |
| { |
| [ $# -eq 0 ] && return 0 |
| |
| local drv |
| |
| drv="$(tst_check_drivers $@ 2>&1)" |
| |
| [ $? -ne 0 ] && tst_brk TCONF "$drv driver not available" |
| return 0 |
| } |
| |
| tst_require_kconfigs() |
| { |
| local delim |
| |
| if [ $# -gt 2 ]; then |
| return 0 |
| elif [ $# -eq 1 ]; then |
| delim="$TST_NEEDS_KCONFIGS_IFS" |
| else |
| delim="$2" |
| fi |
| |
| [ -z "$1" ] && return 0 |
| |
| tst_check_kconfigs "$1" "$delim" > /dev/null |
| |
| [ $? -ne 0 ] && tst_brk TCONF "Aborting due to unsuitable kernel config, see above!" |
| return 0 |
| } |
| |
| tst_is_int() |
| { |
| [ "$1" -eq "$1" ] 2>/dev/null |
| return $? |
| } |
| |
| tst_is_num() |
| { |
| echo "$1" | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$' |
| } |
| |
| tst_usage() |
| { |
| if [ -n "$TST_USAGE" ]; then |
| $TST_USAGE |
| else |
| echo "usage: $0" |
| echo |
| echo "Options" |
| echo "-------" |
| fi |
| |
| echo "-h Prints this help" |
| echo "-i n Execute test n times" |
| |
| cat << EOF |
| |
| Environment Variables |
| --------------------- |
| KCONFIG_PATH Specify kernel config file |
| KCONFIG_SKIP_CHECK Skip kernel config check if variable set (not set by default) |
| LTPROOT Prefix for installed LTP (default: /opt/ltp) |
| LTP_COLORIZE_OUTPUT Force colorized output behaviour (y/1 always, n/0: never) |
| LTP_DEV Path to the block device to be used (for .needs_device) |
| LTP_DEV_FS_TYPE Filesystem used for testing (default: ext2) |
| LTP_SINGLE_FS_TYPE Testing only - specifies filesystem instead all supported (for TST_ALL_FILESYSTEMS=1) |
| LTP_TIMEOUT_MUL Timeout multiplier (must be a number >=1, ceiled to int) |
| TMPDIR Base directory for template directory (for .needs_tmpdir, default: /tmp) |
| EOF |
| } |
| |
| _tst_resstr() |
| { |
| echo "$TST_PASS$TST_FAIL$TST_CONF" |
| } |
| |
| _tst_rescmp() |
| { |
| local res=$(_tst_resstr) |
| |
| if [ "$1" = "$res" ]; then |
| tst_brk TBROK "Test didn't report any results" |
| fi |
| } |
| |
| _tst_multiply_timeout() |
| { |
| [ $# -ne 1 ] && tst_brk TBROK "_tst_multiply_timeout expect 1 parameter" |
| eval "local timeout=\$$1" |
| |
| LTP_TIMEOUT_MUL=${LTP_TIMEOUT_MUL:-1} |
| |
| local err="LTP_TIMEOUT_MUL must be number >= 1!" |
| |
| tst_is_num "$LTP_TIMEOUT_MUL" || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)" |
| |
| if ! tst_is_int "$LTP_TIMEOUT_MUL"; then |
| LTP_TIMEOUT_MUL=$(echo "$LTP_TIMEOUT_MUL" | cut -d. -f1) |
| LTP_TIMEOUT_MUL=$((LTP_TIMEOUT_MUL+1)) |
| tst_res TINFO "ceiling LTP_TIMEOUT_MUL to $LTP_TIMEOUT_MUL" |
| fi |
| |
| [ "$LTP_TIMEOUT_MUL" -ge 1 ] || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)" |
| [ "$timeout" -ge 1 ] || tst_brk TBROK "timeout need to be >= 1 ($timeout)" |
| |
| eval "$1='$((timeout * LTP_TIMEOUT_MUL))'" |
| return 0 |
| } |
| |
| _tst_cleanup_timer() |
| { |
| if [ -n "$_tst_setup_timer_pid" ]; then |
| kill -TERM $_tst_setup_timer_pid 2>/dev/null |
| # kill is successful only on test timeout |
| wait $_tst_setup_timer_pid 2>/dev/null || true |
| fi |
| } |
| |
| _tst_setup_timer() |
| { |
| TST_TIMEOUT=${TST_TIMEOUT:-300} |
| |
| if [ "$TST_TIMEOUT" = -1 ]; then |
| tst_res TINFO "Timeout per run is disabled" |
| return |
| fi |
| |
| if ! tst_is_int "$TST_TIMEOUT" || [ "$TST_TIMEOUT" -lt 1 ]; then |
| tst_brk TBROK "TST_TIMEOUT must be int >= 1! ($TST_TIMEOUT)" |
| fi |
| |
| local sec=$TST_TIMEOUT |
| _tst_multiply_timeout sec |
| local h=$((sec / 3600)) |
| local m=$((sec / 60 % 60)) |
| local s=$((sec % 60)) |
| local pid=$$ |
| |
| tst_res TINFO "timeout per run is ${h}h ${m}m ${s}s" |
| |
| _tst_cleanup_timer |
| |
| tst_timeout_kill $sec $pid & |
| |
| _tst_setup_timer_pid=$! |
| |
| while true; do |
| local state |
| |
| state=$(cut -d' ' -f3 "/proc/$_tst_setup_timer_pid/stat") |
| |
| if [ "$state" = "S" ]; then |
| break; |
| fi |
| |
| tst_sleep 1ms |
| done |
| } |
| |
| tst_require_root() |
| { |
| if [ "$(id -ru)" != 0 ]; then |
| tst_brk TCONF "Must be super/root for this test!" |
| fi |
| } |
| |
| tst_require_module() |
| { |
| local _tst_module=$1 |
| |
| for tst_module in "$_tst_module" \ |
| "$LTPROOT/testcases/bin/$_tst_module" \ |
| "$TST_STARTWD/$_tst_module"; do |
| |
| if [ -f "$tst_module" ]; then |
| TST_MODPATH="$tst_module" |
| break |
| fi |
| done |
| |
| if [ -z "$TST_MODPATH" ]; then |
| tst_brk TCONF "Failed to find module '$_tst_module'" |
| fi |
| |
| tst_res TINFO "Found module at '$TST_MODPATH'" |
| } |
| |
| tst_set_timeout() |
| { |
| TST_TIMEOUT="$1" |
| _tst_setup_timer |
| } |
| |
| _tst_init_checkpoints() |
| { |
| local pagesize |
| |
| LTP_IPC_PATH="/dev/shm/ltp_${TST_ID}_$$" |
| pagesize=$(tst_getconf PAGESIZE) |
| if [ $? -ne 0 ]; then |
| tst_brk TBROK "tst_getconf PAGESIZE failed" |
| fi |
| ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$pagesize" count=1 |
| ROD_SILENT chmod 600 "$LTP_IPC_PATH" |
| export LTP_IPC_PATH |
| } |
| |
| _prepare_device() |
| { |
| if [ "$TST_FORMAT_DEVICE" = 1 ]; then |
| tst_device clear "$TST_DEVICE" |
| tst_mkfs $TST_FS_TYPE $TST_DEV_FS_OPTS $TST_DEVICE $TST_DEV_EXTRA_OPTS |
| fi |
| |
| if [ "$TST_MOUNT_DEVICE" = 1 ]; then |
| tst_mount |
| TST_MOUNT_FLAG=1 |
| fi |
| } |
| |
| _tst_run_tcases_per_fs() |
| { |
| local fs |
| local filesystems |
| |
| filesystems="$(tst_supported_fs -s "$TST_SKIP_FILESYSTEMS")" |
| if [ $? -ne 0 ]; then |
| tst_brk TCONF "There are no supported filesystems or all skipped" |
| fi |
| |
| for fs in $filesystems; do |
| tst_res TINFO "=== Testing on $fs ===" |
| TST_FS_TYPE="$fs" |
| _tst_run_iterations |
| done |
| } |
| |
| tst_run() |
| { |
| local _tst_i |
| local _tst_data |
| local _tst_max |
| local _tst_name |
| local _tst_pattern='[='\''"} \t\/:`$\;|].*' |
| local ret |
| |
| if [ -n "$TST_TEST_PATH" ]; then |
| for _tst_i in $(grep '^[^#]*\bTST_' "$TST_TEST_PATH" | sed "s/.*TST_//; s/$_tst_pattern//"); do |
| case "$_tst_i" in |
| ALL_FILESYSTEMS|DISABLE_APPARMOR|DISABLE_SELINUX);; |
| SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);; |
| OPTS|USAGE|PARSE_ARGS|POS_ARGS);; |
| NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);; |
| NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);; |
| NEEDS_DRIVERS|FS_TYPE|MNTPOINT|MNT_PARAMS);; |
| NEEDS_KCONFIGS|NEEDS_KCONFIGS_IFS);; |
| IPV6|IPV6_FLAG|IPVER|TEST_DATA|TEST_DATA_IFS);; |
| RETRY_FUNC|RETRY_FN_EXP_BACKOFF|TIMEOUT);; |
| NET_DATAROOT|NET_MAX_PKT|NET_RHOST_RUN_DEBUG|NETLOAD_CLN_NUMBER);; |
| NET_SKIP_VARIABLE_INIT|NEEDS_CHECKPOINTS);; |
| CHECKPOINT_WAIT|CHECKPOINT_WAKE);; |
| CHECKPOINT_WAKE2|CHECKPOINT_WAKE_AND_WAIT);; |
| DEV_EXTRA_OPTS|DEV_FS_OPTS|FORMAT_DEVICE|MOUNT_DEVICE);; |
| SKIP_FILESYSTEMS|SKIP_IN_LOCKDOWN|SKIP_IN_SECUREBOOT);; |
| DEVICE_SIZE);; |
| *) tst_res TWARN "Reserved variable TST_$_tst_i used!";; |
| esac |
| done |
| |
| for _tst_i in $(grep '^[^#]*\b_tst_' "$TST_TEST_PATH" | sed "s/.*_tst_//; s/$_tst_pattern//"); do |
| tst_res TWARN "Private variable or function _tst_$_tst_i used!" |
| done |
| fi |
| |
| if ! tst_is_int "$TST_ITERATIONS"; then |
| tst_brk TBROK "Expected number (-i) not '$TST_ITERATIONS'" |
| fi |
| |
| if [ "$TST_ITERATIONS" -lt 0 ]; then |
| tst_brk TBROK "Number of iterations (-i) must be >= 0" |
| fi |
| |
| [ "$TST_NEEDS_ROOT" = 1 ] && tst_require_root |
| |
| if [ "$TST_SKIP_IN_SECUREBOOT" = 1 ] && tst_secureboot_enabled; then |
| tst_brk TCONF "SecureBoot enabled, skipping test" |
| fi |
| |
| if [ "$TST_SKIP_IN_LOCKDOWN" = 1 ] && tst_lockdown_enabled; then |
| tst_brk TCONF "Kernel is locked down, skipping test" |
| fi |
| |
| [ "$TST_DISABLE_APPARMOR" = 1 ] && tst_disable_apparmor |
| [ "$TST_DISABLE_SELINUX" = 1 ] && tst_disable_selinux |
| |
| tst_require_cmds $TST_NEEDS_CMDS |
| tst_require_kconfigs "$TST_NEEDS_KCONFIGS" |
| tst_require_drivers $TST_NEEDS_DRIVERS |
| |
| if [ -n "$TST_MIN_KVER" ]; then |
| tst_kvcmp -lt "$TST_MIN_KVER" && \ |
| tst_brk TCONF "test requires kernel $TST_MIN_KVER+" |
| fi |
| |
| [ -n "$TST_NEEDS_MODULE" ] && tst_require_module "$TST_NEEDS_MODULE" |
| |
| [ "$TST_MOUNT_DEVICE" = 1 ] && TST_FORMAT_DEVICE=1 |
| [ "$TST_FORMAT_DEVICE" = 1 -o "$TST_ALL_FILESYSTEMS" = 1 ] && TST_NEEDS_DEVICE=1 |
| [ "$TST_NEEDS_DEVICE" = 1 ] && TST_NEEDS_TMPDIR=1 |
| |
| if [ "$TST_NEEDS_TMPDIR" = 1 ]; then |
| if [ -z "$TMPDIR" ]; then |
| export TMPDIR="/tmp" |
| fi |
| |
| TST_TMPDIR=$(mktemp -d "$TMPDIR/LTP_$TST_ID.XXXXXXXXXX") |
| # remove possible trailing slash or double slashes from TMPDIR |
| TST_TMPDIR=$(echo "$TST_TMPDIR" | sed 's~/\+~/~g') |
| |
| chmod 777 "$TST_TMPDIR" |
| |
| TST_STARTWD=$(pwd) |
| cd "$TST_TMPDIR" |
| tst_res TINFO "Using $TST_TMPDIR as tmpdir ($(stat -f -c '%T' $TST_TMPDIR) filesystem)" |
| fi |
| |
| # needs to be after cd $TST_TMPDIR to keep test_dev.img under $TST_TMPDIR |
| if [ "$TST_NEEDS_DEVICE" = 1 ]; then |
| TST_DEVICE=$(tst_device acquire $TST_DEVICE_SIZE) |
| |
| if [ ! -b "$TST_DEVICE" -o $? -ne 0 ]; then |
| unset TST_DEVICE |
| tst_brk TBROK "Failed to acquire device" |
| fi |
| TST_DEVICE_FLAG=1 |
| |
| if [ -z "$TST_FS_TYPE" ]; then |
| export TST_FS_TYPE="${LTP_DEV_FS_TYPE:-ext2}" |
| fi |
| fi |
| |
| if [ "$TST_ALL_FILESYSTEMS" != 1 -a "$TST_SKIP_FILESYSTEMS" ]; then |
| if ! tst_supported_fs -s "$TST_SKIP_FILESYSTEMS" -d . > /dev/null; then |
| tst_brk TCONF "filesystem is not supported by the test" |
| fi |
| |
| tst_res TINFO "filesystem is supported by the test" |
| fi |
| |
| [ -n "$TST_NEEDS_CHECKPOINTS" ] && _tst_init_checkpoints |
| |
| TST_MNTPOINT="${TST_MNTPOINT:-$PWD/mntpoint}" |
| |
| if [ "$TST_ALL_FILESYSTEMS" = 1 ]; then |
| _tst_run_tcases_per_fs |
| else |
| _tst_run_iterations |
| fi |
| |
| _tst_do_exit |
| } |
| |
| _tst_run_iterations() |
| { |
| local _tst_i=$TST_ITERATIONS |
| local _tst_j |
| |
| [ "$TST_NEEDS_TMPDIR" = 1 ] && cd "$TST_TMPDIR" |
| |
| _prepare_device |
| |
| _tst_setup_timer |
| |
| if [ -n "$TST_SETUP" ]; then |
| if command -v $TST_SETUP >/dev/null 2>/dev/null; then |
| TST_DO_CLEANUP=1 |
| $TST_SETUP |
| else |
| tst_brk TBROK "TST_SETUP=$TST_SETUP declared, but function not defined (or cmd not found)" |
| fi |
| fi |
| |
| #TODO check that test reports some results for each test function call |
| while [ $_tst_i -gt 0 ]; do |
| if [ -n "$TST_TEST_DATA" ]; then |
| tst_require_cmds cut tr wc |
| _tst_max=$(( $(echo $TST_TEST_DATA | tr -cd "$TST_TEST_DATA_IFS" | wc -c) +1)) |
| for _tst_j in $(seq $_tst_max); do |
| _tst_data="$(echo "$TST_TEST_DATA" | cut -d"$TST_TEST_DATA_IFS" -f$_tst_j)" |
| _tst_run_tests "$_tst_data" |
| done |
| else |
| _tst_run_tests |
| fi |
| _tst_i=$((_tst_i-1)) |
| done |
| |
| _tst_do_cleanup |
| |
| if [ "$TST_MOUNT_FLAG" = 1 ]; then |
| cd "$LTPROOT" |
| tst_umount |
| TST_MOUNT_FLAG= |
| fi |
| } |
| |
| _tst_run_tests() |
| { |
| local _tst_data="$1" |
| local _tst_i |
| |
| TST_DO_CLEANUP=1 |
| for _tst_i in $(seq ${TST_CNT:-1}); do |
| if command -v ${TST_TESTFUNC}1 > /dev/null 2>&1; then |
| _tst_run_test "$TST_TESTFUNC$_tst_i" $_tst_i "$_tst_data" |
| else |
| _tst_run_test "$TST_TESTFUNC" $_tst_i "$_tst_data" |
| fi |
| done |
| } |
| |
| _tst_run_test() |
| { |
| local _tst_res=$(_tst_resstr) |
| local _tst_fnc="$1" |
| shift |
| |
| $_tst_fnc "$@" |
| _tst_rescmp "$_tst_res" |
| TST_COUNT=$((TST_COUNT+1)) |
| } |
| |
| export LC_ALL=C |
| |
| if [ -z "$TST_ID" ]; then |
| _tst_filename=$(basename $0) || \ |
| tst_brk TCONF "Failed to set TST_ID from \$0 ('$0'), fix it with setting TST_ID before sourcing tst_test.sh" |
| TST_ID=${_tst_filename%%.*} |
| fi |
| export TST_ID="$TST_ID" |
| |
| if [ -z "$LTPROOT" ]; then |
| export LTPROOT="$PWD" |
| export TST_DATAROOT="$LTPROOT/datafiles" |
| else |
| export TST_DATAROOT="$LTPROOT/testcases/data/$TST_ID" |
| fi |
| |
| if [ -z "$TST_NO_DEFAULT_RUN" ]; then |
| if TST_TEST_PATH=$(command -v $0) 2>/dev/null; then |
| if ! grep -q tst_run "$TST_TEST_PATH"; then |
| tst_brk TBROK "Test $0 must call tst_run!" |
| fi |
| fi |
| |
| if [ -z "$TST_TESTFUNC" ]; then |
| tst_brk TBROK "TST_TESTFUNC is not defined" |
| fi |
| |
| TST_TEST_DATA_IFS="${TST_TEST_DATA_IFS:- }" |
| |
| TST_NEEDS_KCONFIGS_IFS="${TST_NEEDS_KCONFIGS_IFS:-,}" |
| |
| if [ -n "$TST_CNT" ]; then |
| if ! tst_is_int "$TST_CNT"; then |
| tst_brk TBROK "TST_CNT must be integer" |
| fi |
| |
| if [ "$TST_CNT" -le 0 ]; then |
| tst_brk TBROK "TST_CNT must be > 0" |
| fi |
| fi |
| |
| if [ -n "$TST_POS_ARGS" ]; then |
| if ! tst_is_int "$TST_POS_ARGS"; then |
| tst_brk TBROK "TST_POS_ARGS must be integer" |
| fi |
| |
| if [ "$TST_POS_ARGS" -le 0 ]; then |
| tst_brk TBROK "TST_POS_ARGS must be > 0" |
| fi |
| fi |
| |
| TST_ARGS="$@" |
| |
| tst_res TINFO "Running: $(basename $0) $TST_ARGS" |
| tst_res TINFO "Tested kernel: $(uname -a)" |
| |
| OPTIND=1 |
| |
| while getopts ":hi:$TST_OPTS" _tst_name $TST_ARGS; do |
| case $_tst_name in |
| 'h') tst_usage; exit 0;; |
| 'i') TST_ITERATIONS=$OPTARG;; |
| '?') tst_usage; exit 2;; |
| *) $TST_PARSE_ARGS "$_tst_name" "$OPTARG";; |
| esac |
| done |
| |
| shift $((OPTIND - 1)) |
| |
| if [ -n "$TST_POS_ARGS" ]; then |
| if [ $# -ne "$TST_POS_ARGS" ]; then |
| tst_brk TBROK "Invalid number of positional parameters:"\ |
| "have ($@) $#, expected ${TST_POS_ARGS}" |
| fi |
| else |
| if [ $# -ne 0 ]; then |
| tst_brk TBROK "Unexpected positional arguments '$@'" |
| fi |
| fi |
| fi |