xref: /aosp_15_r20/external/ltp/testcases/lib/test.sh (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1#!/bin/sh
2#
3# Copyright (c) Linux Test Project, 2014-2017
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18#
19# Written by Cyril Hrubis <[email protected]>
20#
21# This is a LTP test library for shell.
22#
23
24export LTP_RET_VAL=0
25export TST_COUNT=1
26export TST_PASS_COUNT=0
27export TST_LIB_LOADED=1
28export TST_TMPDIR_RHOST=0
29
30. tst_ansi_color.sh
31
32# Exit values map
33tst_flag2mask()
34{
35	case "$1" in
36	TPASS) return 0;;
37	TFAIL) return 1;;
38	TBROK) return 2;;
39	TWARN) return 4;;
40	TINFO) return 16;;
41	TCONF) return 32;;
42	*) tst_brkm TBROK "Invalid resm type '$1'";;
43	esac
44}
45
46tst_resm()
47{
48	local ttype="$1"
49
50	tst_flag2mask "$ttype"
51	local mask=$?
52	LTP_RET_VAL=$((LTP_RET_VAL|mask))
53
54	local ret=$1
55	shift
56
57	printf "$TCID $TST_COUNT "
58	tst_print_colored $ret "$ret:"
59	echo " $@"
60
61	case "$ret" in
62	TPASS|TFAIL|TCONF) TST_COUNT=$((TST_COUNT+1));;
63	esac
64
65	if [ "$ret" = TPASS ]; then
66		TST_PASS_COUNT=$((TST_PASS_COUNT+1))
67	fi
68}
69
70tst_brkm()
71{
72	case "$1" in
73	TFAIL) ;;
74	TBROK) ;;
75	TCONF) ;;
76	*) tst_brkm TBROK "Invalid tst_brkm type '$1'";;
77	esac
78
79	local ret=$1
80	shift
81	tst_resm "$ret" "$@"
82	tst_exit
83}
84
85tst_record_childstatus()
86{
87	if [ $# -ne 1 ]; then
88		tst_brkm TBROK "Requires child pid as parameter"
89	fi
90
91	local child_pid=$1
92	local ret=0
93
94	wait $child_pid
95	ret=$?
96	if [ $ret -eq 127 ]; then
97		tst_brkm TBROK "Child process pid='$child_pid' does not exist"
98	fi
99	LTP_RET_VAL=$((LTP_RET_VAL|ret))
100}
101
102tst_require_root()
103{
104	if [ "$(id -ru)" != 0 ]; then
105		tst_brkm TCONF "Must be super/root for this test!"
106	fi
107}
108
109tst_exit()
110{
111	if [ -n "${TST_CLEANUP:-}" -a -z "${LTP_NO_CLEANUP:-}" ]; then
112		$TST_CLEANUP
113	fi
114
115	if [ -n "${LTP_IPC_PATH:-}" -a -f "${LTP_IPC_PATH:-}" ]; then
116		rm -f "$LTP_IPC_PATH"
117	fi
118
119	# Mask out TCONF if no TFAIL/TBROK/TWARN but has TPASS
120	if [ $((LTP_RET_VAL & 7)) -eq 0 -a $TST_PASS_COUNT -gt 0 ]; then
121		LTP_RET_VAL=$((LTP_RET_VAL & ~32))
122	fi
123	# Mask out TINFO
124	exit $((LTP_RET_VAL & ~16))
125}
126
127tst_tmpdir()
128{
129	if [ -z "$TMPDIR" ]; then
130		export TMPDIR="/tmp"
131	fi
132
133	TST_TMPDIR=$(mktemp -d "$TMPDIR/$TCID.XXXXXXXXXX")
134
135	chmod 777 "$TST_TMPDIR"
136
137	TST_STARTWD=$(pwd)
138
139	cd "$TST_TMPDIR"
140}
141
142tst_rmdir()
143{
144	if [ -n "$TST_TMPDIR" ]; then
145		cd "$LTPROOT"
146		rm -r "$TST_TMPDIR"
147		[ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost
148	fi
149}
150
151#
152# Checks if commands passed as arguments exists
153#
154tst_require_cmds()
155{
156	local cmd
157	for cmd in $*; do
158		if ! command -v $cmd > /dev/null 2>&1; then
159			tst_brkm TCONF "'$cmd' not found"
160		fi
161	done
162}
163
164# tst_retry "command" [times]
165# try run command for specified times, default is 3.
166# Function returns 0 if succeed in RETRIES times or the last retcode the cmd
167# returned
168tst_retry()
169{
170	local cmd="$1"
171	local RETRIES=${2:-"3"}
172	local i=$RETRIES
173
174	while [ $i -gt 0 ]; do
175		eval "$cmd"
176		ret=$?
177		if [ $ret -eq 0 ]; then
178			break
179		fi
180		i=$((i-1))
181		sleep 1
182	done
183
184	if [ $ret -ne 0 ]; then
185		tst_resm TINFO "Failed to execute '$cmd' after $RETRIES retries"
186	fi
187
188	return $ret
189}
190
191# tst_timeout "command arg1 arg2 ..." timeout
192# Runs command for specified timeout (in seconds).
193# Function returns retcode of command or 1 if arguments are invalid.
194tst_timeout()
195{
196	local command=$1
197	local timeout=$(echo $2 | grep -o "^[0-9]\+$")
198
199	# command must be non-empty string with command to run
200	if [ -z "$command" ]; then
201		echo "first argument must be non-empty string"
202		return 1
203	fi
204
205	# accept only numbers as timeout
206	if [ -z "$timeout" ]; then
207		echo "only numbers as second argument"
208		return 1
209	fi
210
211	setsid sh -c "eval $command" 2>&1 &
212	local pid=$!
213	while [ $timeout -gt 0 ]; do
214		kill -s 0 $pid 2>/dev/null
215		if [ $? -ne 0 ]; then
216			break
217		fi
218		timeout=$((timeout - 1))
219		sleep 1
220	done
221
222	local ret=0
223	if [ $timeout -le 0 ]; then
224		ret=128
225		kill -TERM -- -$pid
226	fi
227
228	wait $pid
229	ret=$((ret | $?))
230
231	return $ret
232}
233
234ROD_SILENT()
235{
236	local tst_out
237
238	tst_out="$($@ 2>&1)"
239	if [ $? -ne 0 ]; then
240		echo "$tst_out"
241		tst_brkm TBROK "$@ failed"
242	fi
243}
244
245ROD_BASE()
246{
247	local cmd
248	local arg
249	local file
250	local flag
251
252	for arg; do
253		file="${arg#\>}"
254		if [ "$file" != "$arg" ]; then
255			flag=1
256			if [ -n "$file" ]; then
257				break
258			fi
259			continue
260		fi
261
262		if [ -n "$flag" ]; then
263			file="$arg"
264			break
265		fi
266
267		cmd="$cmd $arg"
268	done
269
270	if [ -n "$flag" ]; then
271		$cmd > $file
272	else
273		$@
274	fi
275}
276
277ROD()
278{
279	ROD_BASE "$@"
280	if [ $? -ne 0 ]; then
281		tst_brkm TBROK "$@ failed"
282	fi
283}
284
285EXPECT_PASS()
286{
287	ROD_BASE "$@"
288	if [ $? -eq 0 ]; then
289		tst_resm TPASS "$@ passed as expected"
290	else
291		tst_resm TFAIL "$@ failed unexpectedly"
292	fi
293}
294
295EXPECT_FAIL()
296{
297	# redirect stderr since we expect the command to fail
298	ROD_BASE "$@" 2> /dev/null
299	if [ $? -ne 0 ]; then
300		tst_resm TPASS "$@ failed as expected"
301	else
302		tst_resm TFAIL "$@ passed unexpectedly"
303	fi
304}
305
306tst_mkfs()
307{
308	local fs_type=$1
309	local device=$2
310	shift 2
311	local fs_opts="$@"
312
313	if [ -z "$fs_type" ]; then
314		tst_brkm TBROK "No fs_type specified"
315	fi
316
317	if [ -z "$device" ]; then
318		tst_brkm TBROK "No device specified"
319	fi
320
321	tst_resm TINFO "Formatting $device with $fs_type extra opts='$fs_opts'"
322
323	ROD_SILENT mkfs.$fs_type $fs_opts $device
324}
325
326# Detect whether running under hypervisor: Microsoft Hyper-V
327# Return 0: running under Hyper-V
328# Return 1: not running under Hyper-V (bare metal, other hypervisor or
329#           failure of detection)
330tst_virt_hyperv()
331{
332	local v
333
334	v="$(systemd-detect-virt)"
335
336	[ $? -eq 0 ] || return 1
337	[ "$v" = "microsoft" ] || return 1
338
339	return 0
340}
341
342tst_umount()
343{
344	local device="$1"
345	local i=0
346
347	if ! grep -q "$device" /proc/mounts; then
348		tst_resm TINFO "The $device is not mounted, skipping umount"
349		return
350	fi
351
352	while [ "$i" -lt 50 ]; do
353		if umount "$device" > /dev/null; then
354			return
355		fi
356
357		i=$((i+1))
358
359		tst_resm TINFO "umount($device) failed, try $i ..."
360		tst_resm TINFO "Likely gvfsd-trash is probing newly mounted "\
361			       "fs, kill it to speed up tests."
362
363		tst_sleep 100ms
364	done
365
366	tst_resm TWARN "Failed to umount($device) after 50 retries"
367}
368
369# Check a module file existence
370# Should be called after tst_tmpdir()
371tst_module_exists()
372{
373	local mod_name="$1"
374
375	if [ -f "$mod_name" ]; then
376		TST_MODPATH="$mod_name"
377		return
378	fi
379
380	local mod_path="$LTPROOT/testcases/bin/$mod_name"
381	if [ -f "$mod_path" ]; then
382		TST_MODPATH="$mod_path"
383		return
384	fi
385
386	if [ -n "$TST_TMPDIR" ]; then
387		mod_path="$TST_STARTWD/$mod_name"
388		if [ -f "$mod_path" ]; then
389			TST_MODPATH="$mod_path"
390			return
391		fi
392	fi
393
394	tst_brkm TCONF "Failed to find module '$mod_name'"
395}
396
397TST_CHECKPOINT_WAIT()
398{
399	ROD tst_checkpoint wait 10000 "$1"
400}
401
402TST_CHECKPOINT_WAKE()
403{
404	ROD tst_checkpoint wake 10000 "$1" 1
405}
406
407TST_CHECKPOINT_WAKE2()
408{
409	ROD tst_checkpoint wake 10000 "$1" "$2"
410}
411
412TST_CHECKPOINT_WAKE_AND_WAIT()
413{
414	TST_CHECKPOINT_WAKE "$1"
415	TST_CHECKPOINT_WAIT "$1"
416}
417
418# Check that test name is set
419if [ -z "$TCID" ]; then
420	tst_brkm TBROK "TCID is not defined"
421fi
422
423if [ -z "$TST_TOTAL" ]; then
424	tst_brkm TBROK "TST_TOTAL is not defined"
425fi
426
427export TCID="$TCID"
428export TST_TOTAL="$TST_TOTAL"
429
430# Setup LTPROOT, default to current directory if not set
431if [ -z "$LTPROOT" ]; then
432	export LTPROOT="$PWD"
433	export LTP_DATAROOT="$LTPROOT/datafiles"
434else
435	export LTP_DATAROOT="$LTPROOT/testcases/data/$TCID"
436fi
437
438if [ "$TST_NEEDS_CHECKPOINTS" = "1" ]; then
439	LTP_IPC_PATH="/dev/shm/ltp_${TCID}_$$"
440
441	LTP_IPC_SIZE=$(tst_getconf PAGESIZE)
442	if [ $? -ne 0 ]; then
443		tst_brkm TBROK "tst_getconf PAGESIZE failed"
444	fi
445
446	ROD_SILENT dd if=/dev/zero of="$LTP_IPC_PATH" bs="$LTP_IPC_SIZE" count=1
447	ROD_SILENT chmod 600 "$LTP_IPC_PATH"
448	export LTP_IPC_PATH
449fi
450