• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2#
3# Copyright (c) Linux Test Project, 2014-2018
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 <chrubis@suse.cz>
20#
21# This is a LTP test library for shell.
22#
23
24[ -n "$TST_LIB_LOADED" ] && return 0
25
26export TST_PASS=0
27export TST_FAIL=0
28export TST_BROK=0
29export TST_WARN=0
30export TST_CONF=0
31export TST_COUNT=1
32export TST_ITERATIONS=1
33export TST_TMPDIR_RHOST=0
34export TST_LIB_LOADED=1
35
36. tst_ansi_color.sh
37
38# default trap function
39trap "tst_brk TBROK 'test interrupted'" INT
40
41_tst_do_exit()
42{
43	local ret=0
44	TST_DO_EXIT=1
45
46	if [ -n "$TST_SETUP_STARTED" -a -n "$TST_CLEANUP" -a \
47	     -z "$TST_NO_CLEANUP" ]; then
48		$TST_CLEANUP
49	fi
50
51	if [ "$TST_NEEDS_DEVICE" = 1 -a "$TST_DEVICE_FLAG" = 1 ]; then
52		if ! tst_device release "$TST_DEVICE"; then
53			tst_res TWARN "Failed to release device '$TST_DEVICE'"
54		fi
55	fi
56
57	if [ "$TST_NEEDS_TMPDIR" = 1 -a -n "$TST_TMPDIR" ]; then
58		cd "$LTPROOT"
59		rm -r "$TST_TMPDIR"
60		[ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost
61	fi
62
63	if [ -n "$_tst_setup_timer_pid" ]; then
64		kill $_tst_setup_timer_pid 2>/dev/null
65		wait $_tst_setup_timer_pid 2>/dev/null
66	fi
67
68	if [ $TST_FAIL -gt 0 ]; then
69		ret=$((ret|1))
70	fi
71
72	if [ $TST_BROK -gt 0 ]; then
73		ret=$((ret|2))
74	fi
75
76	if [ $TST_WARN -gt 0 ]; then
77		ret=$((ret|4))
78	fi
79
80	if [ $TST_CONF -gt 0 ]; then
81		ret=$((ret|32))
82	fi
83
84	echo
85	echo "Summary:"
86	echo "passed   $TST_PASS"
87	echo "failed   $TST_FAIL"
88	echo "skipped  $TST_CONF"
89	echo "warnings $TST_WARN"
90
91	exit $ret
92}
93
94_tst_inc_res()
95{
96	case "$1" in
97	TPASS) TST_PASS=$((TST_PASS+1));;
98	TFAIL) TST_FAIL=$((TST_FAIL+1));;
99	TBROK) TST_BROK=$((TST_BROK+1));;
100	TWARN) TST_WARN=$((TST_WARN+1));;
101	TCONF) TST_CONF=$((TST_CONF+1));;
102	TINFO) ;;
103	*) tst_brk TBROK "Invalid res type '$1'";;
104	esac
105}
106
107tst_res()
108{
109	local res=$1
110	shift
111
112	tst_color_enabled
113	local color=$?
114
115	_tst_inc_res "$res"
116
117	printf "$TST_ID $TST_COUNT "
118	tst_print_colored $res "$res: "
119	echo "$@"
120}
121
122tst_brk()
123{
124	local res=$1
125	shift
126
127	if [ "$TST_DO_EXIT" = 1 ]; then
128		tst_res TWARN "$@"
129		return
130	fi
131
132	tst_res "$res" "$@"
133	_tst_do_exit
134}
135
136ROD_SILENT()
137{
138	local tst_out="$(tst_rod $@ 2>&1)"
139	if [ $? -ne 0 ]; then
140		echo "$tst_out"
141		tst_brk TBROK "$@ failed"
142	fi
143}
144
145ROD()
146{
147	tst_rod "$@"
148	if [ $? -ne 0 ]; then
149		tst_brk TBROK "$@ failed"
150	fi
151}
152
153EXPECT_PASS()
154{
155	tst_rod "$@"
156	if [ $? -eq 0 ]; then
157		tst_res TPASS "$@ passed as expected"
158	else
159		tst_res TFAIL "$@ failed unexpectedly"
160	fi
161}
162
163EXPECT_FAIL()
164{
165	# redirect stderr since we expect the command to fail
166	tst_rod "$@" 2> /dev/null
167	if [ $? -ne 0 ]; then
168		tst_res TPASS "$@ failed as expected"
169	else
170		tst_res TFAIL "$@ passed unexpectedly"
171	fi
172}
173
174TST_RETRY_FN_EXP_BACKOFF()
175{
176	local tst_fun="$1"
177	local tst_exp=$2
178	local tst_sec=$(expr $3 \* 1000000)
179	local tst_delay=1
180
181	if [ $# -ne 3 ]; then
182		tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF expects 3 parameters"
183	fi
184
185	if ! tst_is_int "$tst_sec"; then
186		tst_brk TBROK "TST_RETRY_FN_EXP_BACKOFF: tst_sec must be integer ('$tst_sec')"
187	fi
188
189	while true; do
190		$tst_fun
191		if [ "$?" = "$tst_exp" ]; then
192			break
193		fi
194
195		if [ $tst_delay -lt $tst_sec ]; then
196			tst_sleep ${tst_delay}us
197			tst_delay=$((tst_delay*2))
198		else
199			tst_brk TBROK "\"$tst_fun\" timed out"
200		fi
201	done
202
203	return $tst_exp
204}
205
206TST_RETRY_FUNC()
207{
208	if [ $# -ne 2 ]; then
209		tst_brk TBROK "TST_RETRY_FUNC expects 2 parameters"
210	fi
211
212	TST_RETRY_FN_EXP_BACKOFF "$1" "$2" 1
213	return $2
214}
215
216TST_RTNL_CHK()
217{
218	local msg1="RTNETLINK answers: Function not implemented"
219	local msg2="RTNETLINK answers: Operation not supported"
220	local msg3="RTNETLINK answers: Protocol not supported"
221	local output="$($@ 2>&1 || echo 'LTP_ERR')"
222	local msg
223
224	echo "$output" | grep -q "LTP_ERR" || return 0
225
226	for msg in "$msg1" "$msg2" "$msg3"; do
227		echo "$output" | grep -q "$msg" && tst_brk TCONF "'$@': $msg"
228	done
229
230	tst_brk TBROK "$@ failed: $output"
231}
232
233tst_umount()
234{
235	local device="$1"
236	local i=0
237
238	if ! grep -q "$device" /proc/mounts; then
239		tst_res TINFO "The $device is not mounted, skipping umount"
240		return
241	fi
242
243	while [ "$i" -lt 50 ]; do
244		if umount "$device" > /dev/null; then
245			return
246		fi
247
248		i=$((i+1))
249
250		tst_res TINFO "umount($device) failed, try $i ..."
251		tst_res TINFO "Likely gvfsd-trash is probing newly mounted "\
252		              "fs, kill it to speed up tests."
253
254		tst_sleep 100ms
255	done
256
257	tst_res TWARN "Failed to umount($device) after 50 retries"
258}
259
260tst_mkfs()
261{
262	local fs_type=$1
263	local device=$2
264	shift 2
265	local fs_opts="$@"
266
267	if [ -z "$fs_type" ]; then
268		tst_brk TBROK "No fs_type specified"
269	fi
270
271	if [ -z "$device" ]; then
272		tst_brk TBROK "No device specified"
273	fi
274
275	tst_res TINFO "Formatting $device with $fs_type extra opts='$fs_opts'"
276
277	ROD_SILENT mkfs.$fs_type $fs_opts $device
278}
279
280tst_cmd_available()
281{
282	if type command > /dev/null 2>&1; then
283		command -v $1 > /dev/null 2>&1 || return 1
284	else
285		which $1 > /dev/null 2>&1
286		if [ $? -eq 0 ]; then
287			return 0
288		elif [ $? -eq 127 ]; then
289			tst_brk TCONF "missing which command"
290		else
291			return 1
292		fi
293	fi
294}
295
296tst_test_cmds()
297{
298	local cmd
299	for cmd in $*; do
300		tst_cmd_available $cmd || tst_brk TCONF "'$cmd' not found"
301	done
302}
303
304tst_check_cmds()
305{
306	local cmd
307	for cmd; do
308		if ! tst_cmd_available $cmd; then
309			tst_res TCONF "'$cmd' not found"
310			return 1
311		fi
312	done
313	return 0
314}
315
316tst_test_drivers()
317{
318	[ $# -eq 0 ] && return 0
319
320	local drv
321
322	drv="$(tst_check_drivers $@ 2>&1)"
323
324	[ $? -ne 0 ] && tst_brk TCONF "$drv driver not available"
325	return 0
326}
327
328tst_is_int()
329{
330	[ "$1" -eq "$1" ] 2>/dev/null
331	return $?
332}
333
334tst_usage()
335{
336	if [ -n "$TST_USAGE" ]; then
337		$TST_USAGE
338	else
339		echo "usage: $0"
340		echo "OPTIONS"
341	fi
342
343	echo "-h      Prints this help"
344	echo "-i n    Execute test n times"
345}
346
347_tst_resstr()
348{
349	echo "$TST_PASS$TST_FAIL$TST_CONF"
350}
351
352_tst_rescmp()
353{
354	local res=$(_tst_resstr)
355
356	if [ "$1" = "$res" ]; then
357		tst_brk TBROK "Test didn't report any results"
358	fi
359}
360
361
362_tst_setup_timer()
363{
364	LTP_TIMEOUT_MUL=${LTP_TIMEOUT_MUL:-1}
365
366	local sec=$((300 * LTP_TIMEOUT_MUL))
367	local h=$((sec / 3600))
368	local m=$((sec / 60 % 60))
369	local s=$((sec % 60))
370	local pid=$$
371
372	tst_res TINFO "timeout per run is ${h}h ${m}m ${s}s"
373
374	sleep $sec && tst_res TBROK "test killed, timeout! If you are running on slow machine, try exporting LTP_TIMEOUT_MUL > 1" && kill -9 -$pid &
375
376	_tst_setup_timer_pid=$!
377}
378
379tst_run()
380{
381	local _tst_i
382	local _tst_data
383	local _tst_max
384	local _tst_name
385
386	if [ -n "$TST_TEST_PATH" ]; then
387		for _tst_i in $(grep TST_ "$TST_TEST_PATH" | sed 's/.*TST_//; s/[="} \t\/:`].*//'); do
388			case "$_tst_i" in
389			SETUP|CLEANUP|TESTFUNC|ID|CNT|MIN_KVER);;
390			OPTS|USAGE|PARSE_ARGS|POS_ARGS);;
391			NEEDS_ROOT|NEEDS_TMPDIR|TMPDIR|NEEDS_DEVICE|DEVICE);;
392			NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);;
393			NEEDS_DRIVERS);;
394			IPV6|IPVER|TEST_DATA|TEST_DATA_IFS);;
395			RETRY_FUNC|RETRY_FN_EXP_BACKOFF);;
396			*) tst_res TWARN "Reserved variable TST_$_tst_i used!";;
397			esac
398		done
399
400		for _tst_i in $(grep _tst_ "$TST_TEST_PATH" | sed 's/.*_tst_//; s/[="} \t\/:`].*//'); do
401			tst_res TWARN "Private variable or function _tst_$_tst_i used!"
402		done
403	fi
404
405	OPTIND=1
406
407	while getopts ":hi:$TST_OPTS" _tst_name $TST_ARGS; do
408		case $_tst_name in
409		'h') tst_usage; exit 0;;
410		'i') TST_ITERATIONS=$OPTARG;;
411		'?') tst_usage; exit 2;;
412		*) $TST_PARSE_ARGS "$_tst_name" "$OPTARG";;
413		esac
414	done
415
416	if ! tst_is_int "$TST_ITERATIONS"; then
417		tst_brk TBROK "Expected number (-i) not '$TST_ITERATIONS'"
418	fi
419
420	if [ "$TST_ITERATIONS" -le 0 ]; then
421		tst_brk TBROK "Number of iterations (-i) must be > 0"
422	fi
423
424	if [ "$TST_NEEDS_ROOT" = 1 ]; then
425		if [ "$(id -ru)" != 0 ]; then
426			tst_brk TCONF "Must be super/root for this test!"
427		fi
428	fi
429
430	tst_test_cmds $TST_NEEDS_CMDS
431	tst_test_drivers $TST_NEEDS_DRIVERS
432
433	if [ -n "$TST_MIN_KVER" ]; then
434		tst_kvcmp -lt "$TST_MIN_KVER" && \
435			tst_brk TCONF "test requires kernel $TST_MIN_KVER+"
436	fi
437
438	_tst_setup_timer
439
440	if [ "$TST_NEEDS_TMPDIR" = 1 ]; then
441		if [ -z "$TMPDIR" ]; then
442			export TMPDIR="/tmp"
443		fi
444
445		TST_TMPDIR=$(mktemp -d "$TMPDIR/LTP_$TST_ID.XXXXXXXXXX")
446
447		chmod 777 "$TST_TMPDIR"
448
449		TST_STARTWD=$(pwd)
450
451		cd "$TST_TMPDIR"
452	fi
453
454	if [ "$TST_NEEDS_DEVICE" = 1 ]; then
455		if [ -z ${TST_TMPDIR} ]; then
456			tst_brk TBROK "Use TST_NEEDS_TMPDIR must be set for TST_NEEDS_DEVICE"
457		fi
458
459		TST_DEVICE=$(tst_device acquire)
460
461		if [ ! -b "$TST_DEVICE" -o $? -ne 0 ]; then
462			tst_brk TBROK "Failed to acquire device"
463		fi
464
465		TST_DEVICE_FLAG=1
466	fi
467
468	if [ -n "$TST_NEEDS_MODULE" ]; then
469		for tst_module in "$TST_NEEDS_MODULE" \
470		                  "$LTPROOT/testcases/bin/$TST_NEEDS_MODULE" \
471		                  "$TST_STARTWD/$TST_NEEDS_MODULE"; do
472
473				if [ -f "$tst_module" ]; then
474					TST_MODPATH="$tst_module"
475					break
476				fi
477		done
478
479		if [ -z "$TST_MODPATH" ]; then
480			tst_brk TCONF "Failed to find module '$TST_NEEDS_MODULE'"
481		else
482			tst_res TINFO "Found module at '$TST_MODPATH'"
483		fi
484	fi
485
486	if [ -n "$TST_SETUP" ]; then
487		TST_SETUP_STARTED=1
488		$TST_SETUP
489	fi
490
491	#TODO check that test reports some results for each test function call
492	while [ $TST_ITERATIONS -gt 0 ]; do
493		if [ -n "$TST_TEST_DATA" ]; then
494			tst_test_cmds cut tr wc
495			_tst_max=$(( $(echo $TST_TEST_DATA | tr -cd "$TST_TEST_DATA_IFS" | wc -c) +1))
496			for _tst_i in $(seq $_tst_max); do
497				_tst_data="$(echo "$TST_TEST_DATA" | cut -d"$TST_TEST_DATA_IFS" -f$_tst_i)"
498				_tst_run_tests "$_tst_data"
499			done
500		else
501			_tst_run_tests
502		fi
503		TST_ITERATIONS=$((TST_ITERATIONS-1))
504	done
505
506	_tst_do_exit
507}
508
509_tst_run_tests()
510{
511	local _tst_data="$1"
512	local _tst_i
513
514	for _tst_i in $(seq ${TST_CNT:-1}); do
515		if type ${TST_TESTFUNC}1 > /dev/null 2>&1; then
516			_tst_run_test "$TST_TESTFUNC$_tst_i" $_tst_i "$_tst_data"
517		else
518			_tst_run_test "$TST_TESTFUNC" $_tst_i "$_tst_data"
519		fi
520	done
521}
522
523_tst_run_test()
524{
525	local _tst_res=$(_tst_resstr)
526	local _tst_fnc="$1"
527	shift
528
529	$_tst_fnc "$@"
530	_tst_rescmp "$_tst_res"
531	TST_COUNT=$((TST_COUNT+1))
532}
533
534if [ -z "$TST_ID" ]; then
535	_tst_filename=$(basename $0) || \
536		tst_brk TCONF "Failed to set TST_ID from \$0 ('$0'), fix it with setting TST_ID before sourcing tst_test.sh"
537	TST_ID=${_tst_filename%%.*}
538fi
539export TST_ID="$TST_ID"
540
541if [ -z "$LTPROOT" ]; then
542	export LTPROOT="$PWD"
543	export TST_DATAROOT="$LTPROOT/datafiles"
544else
545	export TST_DATAROOT="$LTPROOT/testcases/data/$TST_ID"
546fi
547
548if [ -z "$TST_NO_DEFAULT_RUN" ]; then
549	if TST_TEST_PATH=$(which $0) 2>/dev/null; then
550		if ! grep -q tst_run "$TST_TEST_PATH"; then
551			tst_brk TBROK "Test $0 must call tst_run!"
552		fi
553	fi
554
555	if [ -z "$TST_TESTFUNC" ]; then
556		tst_brk TBROK "TST_TESTFUNC is not defined"
557	fi
558
559	TST_TEST_DATA_IFS="${TST_TEST_DATA_IFS:- }"
560
561	if [ -n "$TST_CNT" ]; then
562		if ! tst_is_int "$TST_CNT"; then
563			tst_brk TBROK "TST_CNT must be integer"
564		fi
565
566		if [ "$TST_CNT" -le 0 ]; then
567			tst_brk TBROK "TST_CNT must be > 0"
568		fi
569	fi
570
571	if [ -n "$TST_POS_ARGS" ]; then
572		if ! tst_is_int "$TST_POS_ARGS"; then
573			tst_brk TBROK "TST_POS_ARGS must be integer"
574		fi
575
576		if [ "$TST_POS_ARGS" -le 0 ]; then
577			tst_brk TBROK "TST_POS_ARGS must be > 0"
578		fi
579	fi
580
581	TST_ARGS="$@"
582
583	while getopts ":hi:$TST_OPTS" tst_name; do
584		case $tst_name in
585		'h') TST_PRINT_HELP=1;;
586		*);;
587		esac
588	done
589
590	shift $((OPTIND - 1))
591
592	if [ -n "$TST_POS_ARGS" ]; then
593		if [ -z "$TST_PRINT_HELP" -a $# -ne "$TST_POS_ARGS" ]; then
594			tst_brk TBROK "Invalid number of positional parameters:"\
595					  "have ($@) $#, expected ${TST_POS_ARGS}"
596		fi
597	else
598		if [ -z "$TST_PRINT_HELP" -a $# -ne 0 ]; then
599			tst_brk TBROK "Unexpected positional arguments '$@'"
600		fi
601	fi
602fi
603