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 <chrubis@suse.cz>
20#
21# This is a LTP test library for shell.
22#
23
24export TST_PASS=0
25export TST_FAIL=0
26export TST_BROK=0
27export TST_WARN=0
28export TST_CONF=0
29export TST_COUNT=1
30export TST_ITERATIONS=1
31export TST_TMPDIR_RHOST=0
32
33. tst_ansi_color.sh
34
35tst_do_exit()
36{
37	local ret=0
38
39	if [ -n "$TST_SETUP_STARTED" -a -n "$TST_CLEANUP" -a \
40	     -z "$TST_NO_CLEANUP" ]; then
41		$TST_CLEANUP
42	fi
43
44	if [ "$TST_NEEDS_DEVICE" = 1 -a "$TST_DEVICE_FLAG" = 1 ]; then
45		if ! tst_device release "$TST_DEVICE"; then
46			tst_res TWARN "Failed to release device '$TST_DEVICE'"
47		fi
48	fi
49
50	if [ "$TST_NEEDS_TMPDIR" = 1 -a -n "$TST_TMPDIR" ]; then
51		cd "$LTPROOT"
52		rm -r "$TST_TMPDIR"
53		[ "$TST_TMPDIR_RHOST" = 1 ] && tst_cleanup_rhost
54	fi
55
56	if [ $TST_FAIL -gt 0 ]; then
57		ret=$((ret|1))
58	fi
59
60	if [ $TST_BROK -gt 0 ]; then
61		ret=$((ret|2))
62	fi
63
64	if [ $TST_WARN -gt 0 ]; then
65		ret=$((ret|4))
66	fi
67
68	if [ $TST_CONF -gt 0 ]; then
69		ret=$((ret|32))
70	fi
71
72	echo
73	echo "Summary:"
74	echo "passed   $TST_PASS"
75	echo "failed   $TST_FAIL"
76	echo "skipped  $TST_CONF"
77	echo "warnings $TST_WARN"
78
79	exit $ret
80}
81
82tst_inc_res()
83{
84	case "$1" in
85	TPASS) TST_PASS=$((TST_PASS+1));;
86	TFAIL) TST_FAIL=$((TST_FAIL+1));;
87	TBROK) TST_BROK=$((TST_BROK+1));;
88	TWARN) TST_WARN=$((TST_WARN+1));;
89	TCONF) TST_CONF=$((TST_CONF+1));;
90	TINFO) ;;
91	*) tst_brk TBROK "Invalid resm type '$1'";;
92	esac
93}
94
95tst_res()
96{
97	local res=$1
98	shift
99
100	tst_color_enabled
101	local color=$?
102
103	tst_inc_res "$res"
104
105	printf "$TCID $TST_COUNT "
106	tst_print_colored $res "$res: "
107	echo "$@"
108}
109
110tst_brk()
111{
112	local res=$1
113	shift
114
115	tst_res "$res" "$@"
116	tst_do_exit
117}
118
119ROD_SILENT()
120{
121	tst_rod $@ > /dev/null 2>&1
122	if [ $? -ne 0 ]; then
123		tst_brk TBROK "$@ failed"
124	fi
125}
126
127ROD()
128{
129	tst_rod "$@"
130	if [ $? -ne 0 ]; then
131		tst_brk TBROK "$@ failed"
132	fi
133}
134
135EXPECT_PASS()
136{
137	tst_rod "$@"
138	if [ $? -eq 0 ]; then
139		tst_res TPASS "$@ passed as expected"
140	else
141		tst_res TFAIL "$@ failed unexpectedly"
142	fi
143}
144
145EXPECT_FAIL()
146{
147	# redirect stderr since we expect the command to fail
148	tst_rod "$@" 2> /dev/null
149	if [ $? -ne 0 ]; then
150		tst_res TPASS "$@ failed as expected"
151	else
152		tst_res TFAIL "$@ passed unexpectedly"
153	fi
154}
155
156tst_umount()
157{
158	local device="$1"
159	local i=0
160
161	if ! grep -q "$device" /proc/mounts; then
162		tst_res TINFO "The $device is not mounted, skipping umount"
163		return
164	fi
165
166	while [ "$i" -lt 50 ]; do
167		if umount "$device" > /dev/null; then
168			return
169		fi
170
171		i=$((i+1))
172
173		tst_res TINFO "umount($device) failed, try $i ..."
174		tst_res TINFO "Likely gvfsd-trash is probing newly mounted "\
175		              "fs, kill it to speed up tests."
176
177		tst_sleep 100ms
178	done
179
180	tst_res TWARN "Failed to umount($device) after 50 retries"
181}
182
183tst_mkfs()
184{
185	local fs_type=$1
186	local device=$2
187	shift 2
188	local fs_opts="$@"
189
190	if [ -z "$fs_type" ]; then
191		tst_brk TBROK "No fs_type specified"
192	fi
193
194	if [ -z "$device" ]; then
195		tst_brk TBROK "No device specified"
196	fi
197
198	tst_res TINFO "Formatting $device with $fs_type extra opts='$fs_opts'"
199
200	ROD_SILENT mkfs.$fs_type $fs_opts $device
201}
202
203tst_check_cmds()
204{
205	local cmd
206	for cmd in $*; do
207		if ! command -v $cmd > /dev/null 2>&1; then
208			tst_brk TCONF "'$cmd' not found"
209		fi
210	done
211}
212
213tst_is_int()
214{
215	[ "$1" -eq "$1" ] 2>/dev/null
216	return $?
217}
218
219tst_usage()
220{
221	if [ -n "$TST_USAGE" ]; then
222		$TST_USAGE
223	else
224		echo "usage: $0"
225		echo "OPTIONS"
226	fi
227
228	echo "-h      Prints this help"
229	echo "-i n    Execute test n times"
230}
231
232tst_resstr()
233{
234	echo "$TST_PASS$TST_FAIL$TST_CONF"
235}
236
237tst_rescmp()
238{
239	local res=$(tst_resstr)
240
241	if [ "$1" = "$res" ]; then
242		tst_brk TBROK "Test didn't report any results"
243	fi
244}
245
246tst_run()
247{
248	local tst_i
249
250	if [ -n "$TST_TEST_PATH" ]; then
251		for tst_i in $(grep TST_ "$TST_TEST_PATH" | sed 's/.*TST_//; s/[="} \t\/:`].*//'); do
252			case "$tst_i" in
253			SETUP|CLEANUP|TESTFUNC|ID|CNT);;
254			OPTS|USAGE|PARSE_ARGS|POS_ARGS);;
255			NEEDS_ROOT|NEEDS_TMPDIR|NEEDS_DEVICE|DEVICE);;
256			NEEDS_CMDS|NEEDS_MODULE|MODPATH|DATAROOT);;
257			*) tst_res TWARN "Reserved variable TST_$tst_i used!";;
258			esac
259		done
260	fi
261
262	local name
263
264	OPTIND=1
265
266	while getopts "hi:$TST_OPTS" name $TST_ARGS; do
267		case $name in
268		'h') tst_usage; exit 0;;
269		'i') TST_ITERATIONS=$OPTARG;;
270		'?') tst_usage; exit 2;;
271		*) $TST_PARSE_ARGS "$name" "$OPTARG";;
272		esac
273	done
274
275	if ! tst_is_int "$TST_ITERATIONS"; then
276		tst_brk TBROK "Expected number (-i) not '$TST_ITERATIONS'"
277	fi
278
279	if [ "$TST_ITERATIONS" -le 0 ]; then
280		tst_brk TBROK "Number of iterations (-i) must be > 0"
281	fi
282
283	if [ "$TST_NEEDS_ROOT" = 1 ]; then
284		if [ "$(id -ru)" != 0 ]; then
285			tst_brk TCONF "Must be super/root for this test!"
286		fi
287	fi
288
289	tst_check_cmds $TST_NEEDS_CMDS
290
291	if [ "$TST_NEEDS_TMPDIR" = 1 ]; then
292		if [ -z "$TMPDIR" ]; then
293			export TMPDIR="/tmp"
294		fi
295
296		TST_TMPDIR=$(mktemp -d "$TMPDIR/LTP_$TST_ID.XXXXXXXXXX")
297
298		chmod 777 "$TST_TMPDIR"
299
300		TST_STARTWD=$(pwd)
301
302		cd "$TST_TMPDIR"
303	fi
304
305	if [ "$TST_NEEDS_DEVICE" = 1 ]; then
306		if [ -z ${TST_TMPDIR} ]; then
307			tst_brk "Use TST_NEEDS_TMPDIR must be set for TST_NEEDS_DEVICE"
308		fi
309
310		TST_DEVICE=$(tst_device acquire)
311
312		if [ -z "$TST_DEVICE" ]; then
313			tst_brk "Failed to acquire device"
314		fi
315
316		TST_DEVICE_FLAG=1
317	fi
318
319	if [ -n "$TST_NEEDS_MODULE" ]; then
320		for tst_module in "$TST_NEEDS_MODULE" \
321		                  "$LTPROOT/testcases/bin/$TST_NEEDS_MODULE" \
322		                  "$TST_STARTWD/$TST_NEEDS_MODULE"; do
323
324				if [ -f "$tst_module" ]; then
325					TST_MODPATH="$tst_module"
326					break
327				fi
328		done
329
330		if [ -z "$TST_MODPATH" ]; then
331			tst_brk TCONF "Failed to find module '$TST_NEEDS_MODULE'"
332		else
333			tst_res TINFO "Found module at '$TST_MODPATH'"
334		fi
335	fi
336
337	if [ -n "$TST_SETUP" ]; then
338		TST_SETUP_STARTED=1
339		$TST_SETUP
340	fi
341
342	#TODO check that test reports some results for each test function call
343	while [ $TST_ITERATIONS -gt 0 ]; do
344		if [ -n "$TST_CNT" ]; then
345			if type test1 > /dev/null 2>&1; then
346				for tst_i in $(seq $TST_CNT); do
347					local res=$(tst_resstr)
348					$TST_TESTFUNC$tst_i
349					tst_rescmp "$res"
350					TST_COUNT=$((TST_COUNT+1))
351				done
352			else
353				for tst_i in $(seq $TST_CNT); do
354					local res=$(tst_resstr)
355					$TST_TESTFUNC $tst_i
356					tst_rescmp "$res"
357					TST_COUNT=$((TST_COUNT+1))
358				done
359			fi
360		else
361			local res=$(tst_resstr)
362			$TST_TESTFUNC
363			tst_rescmp "$res"
364			TST_COUNT=$((TST_COUNT+1))
365		fi
366		TST_ITERATIONS=$((TST_ITERATIONS-1))
367	done
368
369	tst_do_exit
370}
371
372if TST_TEST_PATH=$(which $0) 2>/dev/null; then
373	if ! grep -q tst_run "$TST_TEST_PATH"; then
374		tst_brk TBROK "Test $0 must call tst_run!"
375	fi
376fi
377
378if [ -z "$TST_ID" ]; then
379	filename=$(basename $0)
380	TST_ID=${filename%%.*}
381fi
382export TST_ID="$TST_ID"
383
384if [ -z "$TST_TESTFUNC" ]; then
385	tst_brk TBROK "TST_TESTFUNC is not defined"
386fi
387
388if [ -n "$TST_CNT" ]; then
389	if ! tst_is_int "$TST_CNT"; then
390		tst_brk TBROK "TST_CNT must be integer"
391	fi
392
393	if [ "$TST_CNT" -le 0 ]; then
394		tst_brk TBROK "TST_CNT must be > 0"
395	fi
396fi
397
398if [ -n "$TST_POS_ARGS" ]; then
399	if ! tst_is_int "$TST_POS_ARGS"; then
400		tst_brk TBROK "TST_POS_ARGS must be integer"
401	fi
402
403	if [ "$TST_POS_ARGS" -le 0 ]; then
404		tst_brk TBROK "TST_POS_ARGS must be > 0"
405	fi
406fi
407
408if [ -z "$LTPROOT" ]; then
409	export LTPROOT="$PWD"
410	export TST_DATAROOT="$LTPROOT/datafiles"
411else
412	export TST_DATAROOT="$LTPROOT/testcases/data/$TST_ID"
413fi
414
415TST_ARGS="$@"
416
417while getopts ":hi:$TST_OPTS" tst_name; do
418	case $tst_name in
419	'h') TST_PRINT_HELP=1;;
420	*);;
421	esac
422done
423
424shift $((OPTIND - 1))
425
426if [ -n "$TST_POS_ARGS" ]; then
427	if [ -z "$TST_PRINT_HELP" -a $# -ne "$TST_POS_ARGS" ]; then
428		tst_brk TBROK "Invalid number of positional paramters:"\
429			      "have ($@) $#, expected ${TST_POS_ARGS}"
430	fi
431else
432	if [ -z "$TST_PRINT_HELP" -a $# -ne 0 ]; then
433		tst_brk TBROK "Unexpected positional arguments '$@'"
434	fi
435fi
436