xref: /aosp_15_r20/external/ltp/testcases/lib/tst_test.sh (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-or-later
3# Copyright (c) Linux Test Project, 2014-2022
4# Author: Cyril Hrubis <[email protected]>
5#
6# LTP test library for shell.
7
8[ -n "$TST_LIB_LOADED" ] && return 0
9
10export TST_PASS=0
11export TST_FAIL=0
12export TST_BROK=0
13export TST_WARN=0
14export TST_CONF=0
15export TST_COUNT=1
16export TST_ITERATIONS=1
17export TST_TMPDIR_RHOST=0
18export TST_LIB_LOADED=1
19
20if [ -z "$TST_FS_TYPE" ]; then
21	export TST_FS_TYPE="${LTP_DEV_FS_TYPE:-ext2}"
22fi
23
24. tst_ansi_color.sh
25. tst_security.sh
26
27# default trap function
28trap "tst_brk TBROK 'test interrupted'" INT
29trap "unset _tst_setup_timer_pid; tst_brk TBROK 'test terminated'" TERM
30
31_tst_do_cleanup()
32{
33	if [ -n "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" -a -z "$LTP_NO_CLEANUP" ]; then
34		if command -v $TST_CLEANUP >/dev/null 2>/dev/null; then
35			TST_DO_CLEANUP=
36			$TST_CLEANUP
37		else
38			tst_res TWARN "TST_CLEANUP=$TST_CLEANUP declared, but function not defined (or cmd not found)"
39		fi
40	fi
41	TST_DO_CLEANUP=
42}
43
44_tst_do_exit()
45{
46	local ret=0
47	TST_DO_EXIT=1
48
49	_tst_do_cleanup
50
51	cd "$LTPROOT"
52	[ "$TST_MOUNT_FLAG" = 1 ] && tst_umount
53
54	if [ "$TST_NEEDS_DEVICE" = 1 -a "$TST_DEVICE_FLAG" = 1 ]; then
55		if ! tst_device release "$TST_DEVICE"; then
56			tst_res TWARN "Failed to release device '$TST_DEVICE'"
57		fi
58	fi
59
60	if [ "$TST_NEEDS_TMPDIR" = 1 -a -n "$TST_TMPDIR" ]; then
61		rm -r "$TST_TMPDIR"
62		[ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost
63	fi
64
65	if [ -n "$TST_NEEDS_CHECKPOINTS" -a -f "$LTP_IPC_PATH" ]; then
66		rm $LTP_IPC_PATH
67	fi
68
69	_tst_cleanup_timer
70
71	if [ $TST_FAIL -gt 0 ]; then
72		ret=$((ret|1))
73	fi
74
75	if [ $TST_BROK -gt 0 ]; then
76		ret=$((ret|2))
77	fi
78
79	if [ $TST_WARN -gt 0 ]; then
80		ret=$((ret|4))
81	fi
82
83	if [ $TST_CONF -gt 0 -a $TST_PASS -eq 0 ]; then
84		ret=$((ret|32))
85	fi
86
87	if [ $TST_BROK -gt 0 -o $TST_FAIL -gt 0 -o $TST_WARN -gt 0 ]; then
88		_tst_check_security_modules
89	fi
90
91	cat >&2 << EOF
92
93Summary:
94passed   $TST_PASS
95failed   $TST_FAIL
96broken   $TST_BROK
97skipped  $TST_CONF
98warnings $TST_WARN
99EOF
100
101	exit $ret
102}
103
104_tst_inc_res()
105{
106	case "$1" in
107	TPASS) TST_PASS=$((TST_PASS+1));;
108	TFAIL) TST_FAIL=$((TST_FAIL+1));;
109	TBROK) TST_BROK=$((TST_BROK+1));;
110	TWARN) TST_WARN=$((TST_WARN+1));;
111	TCONF) TST_CONF=$((TST_CONF+1));;
112	TINFO) ;;
113	*) tst_brk TBROK "Invalid res type '$1'";;
114	esac
115}
116
117tst_res()
118{
119	local res=$1
120	shift
121
122	_tst_inc_res "$res"
123
124	printf "$TST_ID $TST_COUNT " >&2
125	tst_print_colored $res "$res: " >&2
126	echo "$@" >&2
127}
128
129tst_brk()
130{
131	local res=$1
132	shift
133
134	# TBROK => TWARN on cleanup or exit
135	if [ "$res" = TBROK ] && [ "$TST_DO_EXIT" = 1 -o -z "$TST_DO_CLEANUP" -a -n "$TST_CLEANUP" ]; then
136		tst_res TWARN "$@"
137		TST_DO_CLEANUP=
138		return
139	fi
140
141	if [ "$res" != TBROK -a "$res" != TCONF ]; then
142		tst_res TBROK "tst_brk can be called only with TBROK or TCONF ($res)"
143	else
144		tst_res "$res" "$@"
145	fi
146
147	_tst_do_exit
148}
149
150ROD_SILENT()
151{
152	local tst_out
153
154	tst_out="$(tst_rod $@ 2>&1)"
155	if [ $? -ne 0 ]; then
156		echo "$tst_out"
157		tst_brk TBROK "$@ failed"
158	fi
159}
160
161ROD()
162{
163	tst_rod "$@"
164	if [ $? -ne 0 ]; then
165		tst_brk TBROK "$@ failed"
166	fi
167}
168
169_tst_expect_pass()
170{
171	local fnc="$1"
172	shift
173
174	tst_rod "$@"
175	if [ $? -eq 0 ]; then
176		tst_res TPASS "$@ passed as expected"
177		return 0
178	else
179		$fnc TFAIL "$@ failed unexpectedly"
180		return 1
181	fi
182}
183
184_tst_expect_fail()
185{
186	local fnc="$1"
187	shift
188
189	# redirect stderr since we expect the command to fail
190	tst_rod "$@" 2> /dev/null
191	if [ $? -ne 0 ]; then
192		tst_res TPASS "$@ failed as expected"
193		return 0
194	else
195		$fnc TFAIL "$@ passed unexpectedly"
196		return 1
197	fi
198}
199
200EXPECT_PASS()
201{
202	_tst_expect_pass tst_res "$@"
203}
204
205EXPECT_PASS_BRK()
206{
207	_tst_expect_pass tst_brk "$@"
208}
209
210EXPECT_FAIL()
211{
212	_tst_expect_fail tst_res "$@"
213}
214
215EXPECT_FAIL_BRK()
216{
217	_tst_expect_fail tst_brk "$@"
218}
219
220TST_RETRY_FN_EXP_BACKOFF()
221{
222	local tst_fun="$1"
223	local tst_exp=$2
224	local tst_sec=$(($3 * 1000000))
225	local tst_delay=1
226
227	_tst_multiply_timeout tst_sec
228
229	if [ $# -ne 3 ]; then
230		tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF expects 3 parameters"
231	fi
232
233	if ! tst_is_int "$tst_sec"; then
234		tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF: tst_sec must be integer ('$tst_sec')"
235	fi
236
237	while true; do
238		eval "$tst_fun"
239		if [ "$?" = "$tst_exp" ]; then
240			break
241		fi
242
243		if [ $tst_delay -lt $tst_sec ]; then
244			tst_sleep ${tst_delay}us
245			tst_delay=$((tst_delay*2))
246		else
247			tst_brk TBROK "\"$tst_fun\" timed out"
248		fi
249	done
250
251	return $tst_exp
252}
253
254TST_RETRY_FUNC()
255{
256	if [ $# -ne 2 ]; then
257		tst_brk TBROK "TST_RETRY_FUNC expects 2 parameters"
258	fi
259
260	TST_RETRY_FN_EXP_BACKOFF "$1" "$2" 1
261	return $2
262}
263
264TST_RTNL_CHK()
265{
266	local msg1="RTNETLINK answers: Function not implemented"
267	local msg2="RTNETLINK answers: Operation not supported"
268	local msg3="RTNETLINK answers: Protocol not supported"
269	local output="$($@ 2>&1 || echo 'LTP_ERR')"
270	local msg
271
272	echo "$output" | grep -q "LTP_ERR" || return 0
273
274	for msg in "$msg1" "$msg2" "$msg3"; do
275		echo "$output" | grep -q "$msg" && tst_brk TCONF "'$@': $msg"
276	done
277
278	tst_brk TBROK "$@ failed: $output"
279}
280
281TST_CHECKPOINT_WAIT()
282{
283	ROD tst_checkpoint wait 10000 "$1"
284}
285
286TST_CHECKPOINT_WAKE()
287{
288	ROD tst_checkpoint wake 10000 "$1" 1
289}
290
291TST_CHECKPOINT_WAKE2()
292{
293	ROD tst_checkpoint wake 10000 "$1" "$2"
294}
295
296TST_CHECKPOINT_WAKE_AND_WAIT()
297{
298	TST_CHECKPOINT_WAKE "$1"
299	TST_CHECKPOINT_WAIT "$1"
300}
301
302tst_mount()
303{
304	local mnt_opt mnt_err mnt_real
305
306	if [ -n "$TST_FS_TYPE" ]; then
307		mnt_opt="-t $TST_FS_TYPE"
308		mnt_err=" $TST_FS_TYPE type"
309	fi
310	local cmd="mount $mnt_opt $TST_DEVICE $TST_MNTPOINT $TST_MNT_PARAMS"
311
312	ROD_SILENT mkdir -p $TST_MNTPOINT
313	tst_res TINFO "Mounting device: $cmd"
314	$cmd
315	local ret=$?
316
317	if [ $ret -eq 32 ]; then
318		tst_brk TCONF "Cannot mount${mnt_err}, missing driver?"
319	fi
320
321	if [ $ret -ne 0 ]; then
322		tst_brk TBROK "Failed to mount device${mnt_err}: mount exit = $ret"
323	fi
324}
325
326tst_umount()
327{
328	local mntpoint="${1:-$TST_MNTPOINT}"
329	local i=0
330
331	[ -z "$mntpoint" ] && return
332
333	if ! echo "$mntpoint" | grep -q ^/; then
334		tst_brk TCONF "The '$mntpoint' is not an absolute path"
335	fi
336
337	if ! grep -q "${mntpoint%/}" /proc/mounts; then
338		tst_res TINFO "The '$mntpoint' is not mounted, skipping umount"
339		return
340	fi
341
342	while [ "$i" -lt 50 ]; do
343		if umount "$mntpoint" > /dev/null; then
344			return
345		fi
346
347		i=$((i+1))
348
349		tst_res TINFO "umount($mntpoint) failed, try $i ..."
350		tst_res TINFO "Likely gvfsd-trash is probing newly mounted "\
351		              "fs, kill it to speed up tests."
352
353		tst_sleep 100ms
354	done
355
356	tst_res TWARN "Failed to umount($mntpoint) after 50 retries"
357}
358
359tst_mkfs()
360{
361	local opts
362	local fs_type=${1:-$TST_FS_TYPE}
363	[ $# -ge 1 ] && shift
364
365	opts="$@"
366
367	if [ "$fs_type" = tmpfs ]; then
368		tst_res TINFO "Skipping mkfs for TMPFS filesystem"
369		return
370	fi
371
372	if [ -z "$opts" ]; then
373		if [ "$TST_NEEDS_DEVICE" != 1 ]; then
374			tst_brk "Using default parameters in tst_mkfs requires TST_NEEDS_DEVICE=1"
375		fi
376		opts="$TST_DEVICE"
377	fi
378
379	tst_require_cmds mkfs.$fs_type
380
381	tst_res TINFO "Formatting $fs_type with opts='$opts'"
382	ROD_SILENT mkfs.$fs_type $opts
383}
384
385# Detect whether running under hypervisor: Microsoft Hyper-V
386# Return 0: running under Hyper-V
387# Return 1: not running under Hyper-V (bare metal, other hypervisor or
388#           failure of detection)
389tst_virt_hyperv()
390{
391	local v
392
393	tst_cmd_available systemd-detect-virt || return 1
394
395	v="$(systemd-detect-virt)"
396
397	[ $? -eq 0 ] || return 1
398	[ "$v" = "microsoft" ] || return 1
399
400	return 0
401}
402
403tst_cmd_available()
404{
405	command -v $1 >/dev/null 2>&1
406}
407
408tst_require_cmds()
409{
410	local cmd
411	for cmd in $*; do
412		tst_cmd_available $cmd || tst_brk TCONF "'$cmd' not found"
413	done
414}
415
416tst_check_cmds()
417{
418	local cmd
419	for cmd in $*; do
420		if ! tst_cmd_available $cmd; then
421			tst_res TCONF "'$cmd' not found"
422			return 1
423		fi
424	done
425	return 0
426}
427
428tst_require_drivers()
429{
430	[ $# -eq 0 ] && return 0
431
432	local drv
433
434	drv="$(tst_check_drivers $@ 2>&1)"
435
436	[ $? -ne 0 ] && tst_brk TCONF "$drv driver not available"
437	return 0
438}
439
440tst_require_kconfigs()
441{
442	local delim
443
444	if [ $# -gt 2 ]; then
445		return 0
446	elif [ $# -eq 1 ]; then
447		delim="$TST_NEEDS_KCONFIGS_IFS"
448	else
449		delim="$2"
450	fi
451
452	[ -z "$1" ] && return 0
453
454	tst_check_kconfigs "$1" "$delim" > /dev/null
455
456	[ $? -ne 0 ] && tst_brk TCONF "Aborting due to unsuitable kernel config, see above!"
457	return 0
458}
459
460tst_is_int()
461{
462	[ "$1" -eq "$1" ] 2>/dev/null
463	return $?
464}
465
466tst_is_num()
467{
468	echo "$1" | grep -Eq '^[-+]?[0-9]+\.?[0-9]*$'
469}
470
471tst_usage()
472{
473	if [ -n "$TST_USAGE" ]; then
474		$TST_USAGE
475	else
476		echo "usage: $0"
477		echo
478		echo "Options"
479		echo "-------"
480	fi
481
482	echo "-h      Prints this help"
483	echo "-i n    Execute test n times"
484
485	cat << EOF
486
487Environment Variables
488---------------------
489KCONFIG_PATH         Specify kernel config file
490KCONFIG_SKIP_CHECK   Skip kernel config check if variable set (not set by default)
491LTPROOT              Prefix for installed LTP (default: /opt/ltp)
492LTP_COLORIZE_OUTPUT  Force colorized output behaviour (y/1 always, n/0: never)
493LTP_DEV              Path to the block device to be used (for .needs_device)
494LTP_DEV_FS_TYPE      Filesystem used for testing (default: ext2)
495LTP_SINGLE_FS_TYPE   Testing only - specifies filesystem instead all supported (for TST_ALL_FILESYSTEMS=1)
496LTP_TIMEOUT_MUL      Timeout multiplier (must be a number >=1, ceiled to int)
497TMPDIR               Base directory for template directory (for .needs_tmpdir, default: /tmp)
498EOF
499}
500
501_tst_resstr()
502{
503	echo "$TST_PASS$TST_FAIL$TST_CONF"
504}
505
506_tst_rescmp()
507{
508	local res=$(_tst_resstr)
509
510	if [ "$1" = "$res" ]; then
511		tst_brk TBROK "Test didn't report any results"
512	fi
513}
514
515_tst_multiply_timeout()
516{
517	[ $# -ne 1 ] && tst_brk TBROK "_tst_multiply_timeout expect 1 parameter"
518	eval "local timeout=\$$1"
519
520	LTP_TIMEOUT_MUL=${LTP_TIMEOUT_MUL:-1}
521
522	local err="LTP_TIMEOUT_MUL must be number >= 1!"
523
524	tst_is_num "$LTP_TIMEOUT_MUL" || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)"
525
526	if ! tst_is_int "$LTP_TIMEOUT_MUL"; then
527		LTP_TIMEOUT_MUL=$(echo "$LTP_TIMEOUT_MUL" | cut -d. -f1)
528		LTP_TIMEOUT_MUL=$((LTP_TIMEOUT_MUL+1))
529		tst_res TINFO "ceiling LTP_TIMEOUT_MUL to $LTP_TIMEOUT_MUL"
530	fi
531
532	[ "$LTP_TIMEOUT_MUL" -ge 1 ] || tst_brk TBROK "$err ($LTP_TIMEOUT_MUL)"
533	[ "$timeout" -ge 1 ] || tst_brk TBROK "timeout need to be >= 1 ($timeout)"
534
535	eval "$1='$((timeout * LTP_TIMEOUT_MUL))'"
536	return 0
537}
538
539_tst_cleanup_timer()
540{
541	if [ -n "$_tst_setup_timer_pid" ]; then
542		kill -TERM $_tst_setup_timer_pid 2>/dev/null
543		# kill is successful only on test timeout
544		wait $_tst_setup_timer_pid 2>/dev/null || true
545	fi
546}
547
548_tst_setup_timer()
549{
550	TST_TIMEOUT=${TST_TIMEOUT:-300}
551
552	if [ "$TST_TIMEOUT" = -1 ]; then
553		tst_res TINFO "Timeout per run is disabled"
554		return
555	fi
556
557	if ! tst_is_int "$TST_TIMEOUT" || [ "$TST_TIMEOUT" -lt 1 ]; then
558		tst_brk TBROK "TST_TIMEOUT must be int >= 1! ($TST_TIMEOUT)"
559	fi
560
561	local sec=$TST_TIMEOUT
562	_tst_multiply_timeout sec
563	local h=$((sec / 3600))
564	local m=$((sec / 60 % 60))
565	local s=$((sec % 60))
566	local pid=$$
567
568	tst_res TINFO "timeout per run is ${h}h ${m}m ${s}s"
569
570	_tst_cleanup_timer
571
572	tst_timeout_kill $sec $pid &
573
574	_tst_setup_timer_pid=$!
575
576	while true; do
577		local state
578
579		state=$(cut -d' ' -f3 "/proc/$_tst_setup_timer_pid/stat")
580
581		if [ "$state" = "S" ]; then
582			break;
583		fi
584
585		tst_sleep 1ms
586	done
587}
588
589tst_require_root()
590{
591	if [ "$(id -ru)" != 0 ]; then
592		tst_brk TCONF "Must be super/root for this test!"
593	fi
594}
595
596tst_require_module()
597{
598	local _tst_module=$1
599
600	for tst_module in "$_tst_module" \
601	                  "$LTPROOT/testcases/bin/$_tst_module" \
602	                  "$TST_STARTWD/$_tst_module"; do
603
604			if [ -f "$tst_module" ]; then
605				TST_MODPATH="$tst_module"
606				break
607			fi
608	done
609
610	if [ -z "$TST_MODPATH" ]; then
611		tst_brk TCONF "Failed to find module '$_tst_module'"
612	fi
613
614	tst_res TINFO "Found module at '$TST_MODPATH'"
615}
616
617tst_set_timeout()
618{
619	TST_TIMEOUT="$1"
620	_tst_setup_timer
621}
622
623_tst_init_checkpoints()
624{
625	local pagesize
626
627	LTP_IPC_PATH="/dev/shm/ltp_${TST_ID}_$$"
628	pagesize=$(tst_getconf PAGESIZE)
629	if [ $? -ne 0 ]; then
630		tst_brk TBROK "tst_getconf PAGESIZE failed"
631	fi
632	ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$pagesize" count=1
633	ROD_SILENT chmod 600 "$LTP_IPC_PATH"
634	export LTP_IPC_PATH
635}
636
637_prepare_device()
638{
639	if [ "$TST_FORMAT_DEVICE" = 1 ]; then
640		tst_device clear "$TST_DEVICE"
641		tst_mkfs $TST_FS_TYPE $TST_DEV_FS_OPTS $TST_DEVICE $TST_DEV_EXTRA_OPTS
642	fi
643
644	if [ "$TST_MOUNT_DEVICE" = 1 ]; then
645		tst_mount
646		TST_MOUNT_FLAG=1
647	fi
648}
649
650_tst_run_tcases_per_fs()
651{
652	local fs
653	local filesystems
654
655	filesystems="$(tst_supported_fs -s "$TST_SKIP_FILESYSTEMS")"
656	if [ $? -ne 0 ]; then
657		tst_brk TCONF "There are no supported filesystems or all skipped"
658	fi
659
660	for fs in $filesystems; do
661		tst_res TINFO "=== Testing on $fs ==="
662		TST_FS_TYPE="$fs"
663		_tst_run_iterations
664	done
665}
666
667tst_run()
668{
669	local _tst_i
670	local _tst_data
671	local _tst_max
672	local _tst_name
673	local _tst_pattern='[='\''"} \t\/:`$\;|].*'
674	local ret
675
676	if [ -n "$TST_TEST_PATH" ]; then
677		for _tst_i in $(grep '^[^#]*\bTST_' "$TST_TEST_PATH" | sed "s/.*TST_//; s/$_tst_pattern//"); do
678			case "$_tst_i" in
679			ALL_FILESYSTEMS|DISABLE_APPARMOR|DISABLE_SELINUX);;
680			SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);;
681			OPTS|USAGE|PARSE_ARGS|POS_ARGS);;
682			NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);;
683			NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);;
684			NEEDS_DRIVERS|FS_TYPE|MNTPOINT|MNT_PARAMS);;
685			NEEDS_KCONFIGS|NEEDS_KCONFIGS_IFS);;
686			IPV6|IPV6_FLAG|IPVER|TEST_DATA|TEST_DATA_IFS);;
687			RETRY_FUNC|RETRY_FN_EXP_BACKOFF|TIMEOUT);;
688			NET_DATAROOT|NET_MAX_PKT|NET_RHOST_RUN_DEBUG|NETLOAD_CLN_NUMBER);;
689			NET_SKIP_VARIABLE_INIT|NEEDS_CHECKPOINTS);;
690			CHECKPOINT_WAIT|CHECKPOINT_WAKE);;
691			CHECKPOINT_WAKE2|CHECKPOINT_WAKE_AND_WAIT);;
692			DEV_EXTRA_OPTS|DEV_FS_OPTS|FORMAT_DEVICE|MOUNT_DEVICE);;
693			SKIP_FILESYSTEMS|SKIP_IN_LOCKDOWN|SKIP_IN_SECUREBOOT);;
694			*) tst_res TWARN "Reserved variable TST_$_tst_i used!";;
695			esac
696		done
697
698		for _tst_i in $(grep '^[^#]*\b_tst_' "$TST_TEST_PATH" | sed "s/.*_tst_//; s/$_tst_pattern//"); do
699			tst_res TWARN "Private variable or function _tst_$_tst_i used!"
700		done
701	fi
702
703	if ! tst_is_int "$TST_ITERATIONS"; then
704		tst_brk TBROK "Expected number (-i) not '$TST_ITERATIONS'"
705	fi
706
707	if [ "$TST_ITERATIONS" -lt 0 ]; then
708		tst_brk TBROK "Number of iterations (-i) must be >= 0"
709	fi
710
711	[ "$TST_NEEDS_ROOT" = 1 ] && tst_require_root
712
713	if [ "$TST_SKIP_IN_SECUREBOOT" = 1 ] && tst_secureboot_enabled; then
714		tst_brk TCONF "SecureBoot enabled, skipping test"
715	fi
716
717	if [ "$TST_SKIP_IN_LOCKDOWN" = 1 ] && tst_lockdown_enabled; then
718		tst_brk TCONF "Kernel is locked down, skipping test"
719	fi
720
721	[ "$TST_DISABLE_APPARMOR" = 1 ] && tst_disable_apparmor
722	[ "$TST_DISABLE_SELINUX" = 1 ] && tst_disable_selinux
723
724	tst_require_cmds $TST_NEEDS_CMDS
725	tst_require_kconfigs "$TST_NEEDS_KCONFIGS"
726	tst_require_drivers $TST_NEEDS_DRIVERS
727
728	if [ -n "$TST_MIN_KVER" ]; then
729		tst_kvcmp -lt "$TST_MIN_KVER" && \
730			tst_brk TCONF "test requires kernel $TST_MIN_KVER+"
731	fi
732
733	[ -n "$TST_NEEDS_MODULE" ] && tst_require_module "$TST_NEEDS_MODULE"
734
735	[ "$TST_MOUNT_DEVICE" = 1 ] && TST_FORMAT_DEVICE=1
736	[ "$TST_FORMAT_DEVICE" = 1 -o "$TST_ALL_FILESYSTEMS" = 1 ] && TST_NEEDS_DEVICE=1
737	[ "$TST_NEEDS_DEVICE" = 1 ] && TST_NEEDS_TMPDIR=1
738
739	if [ "$TST_NEEDS_TMPDIR" = 1 ]; then
740		if [ -z "$TMPDIR" ]; then
741			export TMPDIR="/tmp"
742		fi
743
744		TST_TMPDIR=$(mktemp -d "$TMPDIR/LTP_$TST_ID.XXXXXXXXXX")
745		# remove possible trailing slash or double slashes from TMPDIR
746		TST_TMPDIR=$(echo "$TST_TMPDIR" | sed 's~/\+~/~g')
747
748		chmod 777 "$TST_TMPDIR"
749
750		TST_STARTWD=$(pwd)
751		cd "$TST_TMPDIR"
752	fi
753
754	# needs to be after cd $TST_TMPDIR to keep test_dev.img under $TST_TMPDIR
755	if [ "$TST_NEEDS_DEVICE" = 1 ]; then
756		TST_DEVICE=$(tst_device acquire)
757
758		if [ ! -b "$TST_DEVICE" -o $? -ne 0 ]; then
759			unset TST_DEVICE
760			tst_brk TBROK "Failed to acquire device"
761		fi
762		TST_DEVICE_FLAG=1
763
764		if [ -z "$TST_FS_TYPE" ]; then
765			export TST_FS_TYPE="${LTP_DEV_FS_TYPE:-ext2}"
766		fi
767	fi
768
769	if [ "$TST_ALL_FILESYSTEMS" != 1 -a "$TST_SKIP_FILESYSTEMS" ]; then
770		if ! tst_supported_fs -s "$TST_SKIP_FILESYSTEMS" -d . > /dev/null; then
771			tst_brk TCONF "filesystem is not supported by the test"
772		fi
773
774		tst_res TINFO "filesystem is supported by the test"
775	fi
776
777	[ -n "$TST_NEEDS_CHECKPOINTS" ] && _tst_init_checkpoints
778
779	TST_MNTPOINT="${TST_MNTPOINT:-$PWD/mntpoint}"
780
781	if [ "$TST_ALL_FILESYSTEMS" = 1 ]; then
782		_tst_run_tcases_per_fs
783	else
784		_tst_run_iterations
785	fi
786
787	_tst_do_exit
788}
789
790_tst_run_iterations()
791{
792	local _tst_i=$TST_ITERATIONS
793	local _tst_j
794
795	[ "$TST_NEEDS_TMPDIR" = 1 ] && cd "$TST_TMPDIR"
796
797	_prepare_device
798
799	_tst_setup_timer
800
801	if [ -n "$TST_SETUP" ]; then
802		if command -v $TST_SETUP >/dev/null 2>/dev/null; then
803			TST_DO_CLEANUP=1
804			$TST_SETUP
805		else
806			tst_brk TBROK "TST_SETUP=$TST_SETUP declared, but function not defined (or cmd not found)"
807		fi
808	fi
809
810	#TODO check that test reports some results for each test function call
811	while [ $_tst_i -gt 0 ]; do
812		if [ -n "$TST_TEST_DATA" ]; then
813			tst_require_cmds cut tr wc
814			_tst_max=$(( $(echo $TST_TEST_DATA | tr -cd "$TST_TEST_DATA_IFS" | wc -c) +1))
815			for _tst_j in $(seq $_tst_max); do
816				_tst_data="$(echo "$TST_TEST_DATA" | cut -d"$TST_TEST_DATA_IFS" -f$_tst_j)"
817				_tst_run_tests "$_tst_data"
818			done
819		else
820			_tst_run_tests
821		fi
822		_tst_i=$((_tst_i-1))
823	done
824
825	_tst_do_cleanup
826
827	if [ "$TST_MOUNT_FLAG" = 1 ]; then
828		cd "$LTPROOT"
829		tst_umount
830		TST_MOUNT_FLAG=
831	fi
832}
833
834_tst_run_tests()
835{
836	local _tst_data="$1"
837	local _tst_i
838
839	TST_DO_CLEANUP=1
840	for _tst_i in $(seq ${TST_CNT:-1}); do
841		if command -v ${TST_TESTFUNC}1 > /dev/null 2>&1; then
842			_tst_run_test "$TST_TESTFUNC$_tst_i" $_tst_i "$_tst_data"
843		else
844			_tst_run_test "$TST_TESTFUNC" $_tst_i "$_tst_data"
845		fi
846	done
847}
848
849_tst_run_test()
850{
851	local _tst_res=$(_tst_resstr)
852	local _tst_fnc="$1"
853	shift
854
855	$_tst_fnc "$@"
856	_tst_rescmp "$_tst_res"
857	TST_COUNT=$((TST_COUNT+1))
858}
859
860export LC_ALL=C
861
862if [ -z "$TST_ID" ]; then
863	_tst_filename=$(basename $0) || \
864		tst_brk TCONF "Failed to set TST_ID from \$0 ('$0'), fix it with setting TST_ID before sourcing tst_test.sh"
865	TST_ID=${_tst_filename%%.*}
866fi
867export TST_ID="$TST_ID"
868
869if [ -z "$LTPROOT" ]; then
870	export LTPROOT="$PWD"
871	export TST_DATAROOT="$LTPROOT/datafiles"
872else
873	export TST_DATAROOT="$LTPROOT/testcases/data/$TST_ID"
874fi
875
876if [ -z "$TST_NO_DEFAULT_RUN" ]; then
877	if TST_TEST_PATH=$(command -v $0) 2>/dev/null; then
878		if ! grep -q tst_run "$TST_TEST_PATH"; then
879			tst_brk TBROK "Test $0 must call tst_run!"
880		fi
881	fi
882
883	if [ -z "$TST_TESTFUNC" ]; then
884		tst_brk TBROK "TST_TESTFUNC is not defined"
885	fi
886
887	TST_TEST_DATA_IFS="${TST_TEST_DATA_IFS:- }"
888
889	TST_NEEDS_KCONFIGS_IFS="${TST_NEEDS_KCONFIGS_IFS:-,}"
890
891	if [ -n "$TST_CNT" ]; then
892		if ! tst_is_int "$TST_CNT"; then
893			tst_brk TBROK "TST_CNT must be integer"
894		fi
895
896		if [ "$TST_CNT" -le 0 ]; then
897			tst_brk TBROK "TST_CNT must be > 0"
898		fi
899	fi
900
901	if [ -n "$TST_POS_ARGS" ]; then
902		if ! tst_is_int "$TST_POS_ARGS"; then
903			tst_brk TBROK "TST_POS_ARGS must be integer"
904		fi
905
906		if [ "$TST_POS_ARGS" -le 0 ]; then
907			tst_brk TBROK "TST_POS_ARGS must be > 0"
908		fi
909	fi
910
911	TST_ARGS="$@"
912
913	tst_res TINFO "Running: $(basename $0) $TST_ARGS"
914
915	OPTIND=1
916
917	while getopts ":hi:$TST_OPTS" _tst_name $TST_ARGS; do
918		case $_tst_name in
919		'h') tst_usage; exit 0;;
920		'i') TST_ITERATIONS=$OPTARG;;
921		'?') tst_usage; exit 2;;
922		*) $TST_PARSE_ARGS "$_tst_name" "$OPTARG";;
923		esac
924	done
925
926	shift $((OPTIND - 1))
927
928	if [ -n "$TST_POS_ARGS" ]; then
929		if [ $# -ne "$TST_POS_ARGS" ]; then
930			tst_brk TBROK "Invalid number of positional parameters:"\
931					  "have ($@) $#, expected ${TST_POS_ARGS}"
932		fi
933	else
934		if [ $# -ne 0 ]; then
935			tst_brk TBROK "Unexpected positional arguments '$@'"
936		fi
937	fi
938fi
939