summaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/kselftest/runner.sh
blob: 74954f6a8f94b3709ecfe965b3f3f6c95bad6a2f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Runs a set of tests in a given subdirectory.
export skip_rc=4
export timeout_rc=124
export logfile=/dev/stdout
export per_test_logging=
export RUN_IN_NETNS=

# Defaults for "settings" file fields:
# "timeout" how many seconds to let each test run before running
# over our soft timeout limit.
export kselftest_default_timeout=45

# There isn't a shell-agnostic way to find the path of a sourced file,
# so we must rely on BASE_DIR being set to find other tools.
if [ -z "$BASE_DIR" ]; then
	echo "Error: BASE_DIR must be set before sourcing." >&2
	exit 1
fi

TR_CMD=$(command -v tr)

# If Perl is unavailable, we must fall back to line-at-a-time prefixing
# with sed instead of unbuffered output.
tap_prefix()
{
	if [ ! -x /usr/bin/perl ]; then
		sed -e 's/^/# /'
	else
		"$BASE_DIR"/kselftest/prefix.pl
	fi
}

tap_timeout()
{
	# Make sure tests will time out if utility is available.
	if [ -x /usr/bin/timeout ] ; then
		/usr/bin/timeout --foreground "$kselftest_timeout" \
			/usr/bin/timeout "$kselftest_timeout" $1
	else
		$1
	fi
}

run_one()
{
	DIR="$1"
	TEST="$2"
	local test_num="$3"

	BASENAME_TEST=$(basename $TEST)

	# Reset any "settings"-file variables.
	export kselftest_timeout="$kselftest_default_timeout"

	# Safe default if tr not available
	kselftest_cmd_args_ref="KSELFTEST_ARGS"

	# Optional arguments for this command, possibly defined as an
	# environment variable built using the test executable in all
	# uppercase and sanitized substituting non acceptable shell
	# variable name characters with "_" as in:
	#
	# 	KSELFTEST_<UPPERCASE_SANITIZED_TESTNAME>_ARGS="<options>"
	#
	# e.g.
	#
	# 	rtctest --> KSELFTEST_RTCTEST_ARGS="/dev/rtc1"
	#
	# 	cpu-on-off-test.sh --> KSELFTEST_CPU_ON_OFF_TEST_SH_ARGS="-a -p 10"
	#
	if [ -n "$TR_CMD" ]; then
		BASENAME_SANITIZED=$(echo "$BASENAME_TEST" | \
					$TR_CMD -d "[:blank:][:cntrl:]" | \
					$TR_CMD -c "[:alnum:]_" "_" | \
					$TR_CMD [:lower:] [:upper:])
		kselftest_cmd_args_ref="KSELFTEST_${BASENAME_SANITIZED}_ARGS"
	fi

	# Load per-test-directory kselftest "settings" file.
	settings="$BASE_DIR/$DIR/settings"
	if [ -r "$settings" ] ; then
		while read line ; do
			# Skip comments.
			if echo "$line" | grep -q '^#'; then
				continue
			fi
			field=$(echo "$line" | cut -d= -f1)
			value=$(echo "$line" | cut -d= -f2-)
			eval "kselftest_$field"="$value"
		done < "$settings"
	fi

	# Command line timeout overrides the settings file
	if [ -n "$kselftest_override_timeout" ]; then
		kselftest_timeout="$kselftest_override_timeout"
		echo "# overriding timeout to $kselftest_timeout" >> "$logfile"
	else
		echo "# timeout set to $kselftest_timeout" >> "$logfile"
	fi

	TEST_HDR_MSG="selftests: $DIR: $BASENAME_TEST"
	echo "# $TEST_HDR_MSG"
	if [ ! -e "$TEST" ]; then
		echo "# Warning: file $TEST is missing!"
		echo "not ok $test_num $TEST_HDR_MSG"
	else
		if [ -x /usr/bin/stdbuf ]; then
			stdbuf="/usr/bin/stdbuf --output=L "
		fi
		eval kselftest_cmd_args="\$${kselftest_cmd_args_ref:-}"
		cmd="$stdbuf ./$BASENAME_TEST $kselftest_cmd_args"
		if [ ! -x "$TEST" ]; then
			echo "# Warning: file $TEST is not executable"

			if [ $(head -n 1 "$TEST" | cut -c -2) = "#!" ]
			then
				interpreter=$(head -n 1 "$TEST" | cut -c 3-)
				cmd="$stdbuf $interpreter ./$BASENAME_TEST"
			else
				echo "not ok $test_num $TEST_HDR_MSG"
				return
			fi
		fi
		cd `dirname $TEST` > /dev/null
		((((( tap_timeout "$cmd" 2>&1; echo $? >&3) |
			tap_prefix >&4) 3>&1) |
			(read xs; exit $xs)) 4>>"$logfile" &&
		echo "ok $test_num $TEST_HDR_MSG") ||
		(rc=$?;	\
		if [ $rc -eq $skip_rc ]; then	\
			echo "ok $test_num $TEST_HDR_MSG # SKIP"
		elif [ $rc -eq $timeout_rc ]; then \
			echo "#"
			echo "not ok $test_num $TEST_HDR_MSG # TIMEOUT $kselftest_timeout seconds"
		else
			echo "not ok $test_num $TEST_HDR_MSG # exit=$rc"
		fi)
		cd - >/dev/null
	fi
}

in_netns()
{
	local name=$1
	ip netns exec $name bash <<-EOF
		BASE_DIR=$BASE_DIR
		source $BASE_DIR/kselftest/runner.sh
		logfile=$logfile
		run_one $DIR $TEST $test_num
	EOF
}

run_in_netns()
{
	local netns=$(mktemp -u ${BASENAME_TEST}-XXXXXX)
	local tmplog="/tmp/$(mktemp -u ${BASENAME_TEST}-XXXXXX)"
	ip netns add $netns
	if [ $? -ne 0 ]; then
		echo "# Warning: Create namespace failed for $BASENAME_TEST"
		echo "not ok $test_num selftests: $DIR: $BASENAME_TEST # Create NS failed"
	fi
	ip -n $netns link set lo up
	in_netns $netns &> $tmplog
	ip netns del $netns &> /dev/null
	cat $tmplog
	rm -f $tmplog
}

run_many()
{
	echo "TAP version 13"
	DIR="${PWD#${BASE_DIR}/}"
	test_num=0
	total=$(echo "$@" | wc -w)
	echo "1..$total"
	for TEST in "$@"; do
		BASENAME_TEST=$(basename $TEST)
		test_num=$(( test_num + 1 ))
		if [ -n "$per_test_logging" ]; then
			logfile="/tmp/$BASENAME_TEST"
			cat /dev/null > "$logfile"
		fi
		if [ -n "$RUN_IN_NETNS" ]; then
			run_in_netns &
		else
			run_one "$DIR" "$TEST" "$test_num"
		fi
	done

	wait
}