NBash

Форк
0
476 строк · 11.9 Кб
1
#!/usr/bin/env bash
2
# -*- tab-width: 4; encoding: utf-8 -*-
3
#
4
#
5
#
6

7
PS4='+ ${FUNCNAME:-main}${LINENO:+:$LINENO}>'
8

9
# If it works with failglob and nounset, it will work without.
10
shopt -s failglob
11
set -o nounset
12

13
. ./argsparse.sh
14

15
shopt -s extdebug
16
errors=0
17
disabled=0
18

19
rm -f unittest.log unittest.env.log
20

21
err_trap() {
22
	: $((errors+=1))
23
	return 1
24
}
25

26
trap err_trap ERR
27

28
print_exit_status() {
29
    # Prints [OK] at the end of the screen of first argument is 0,
30
    # else [FAILURE].
31
    # 1st Parameter: a number, usually the exit status of your
32
    # previous command. If omitted, will use $?.
33
    # returns the first parameter value
34
    local ret="${1:-$?}"
35
    # If you want you can override the FAILED and OK messages by
36
    # pre-defining those variables.
37
    local FAILED=${FAILED:-FAILED}
38
    local OK=${OK:-OK}
39
    # Move to column default is 70
40
    local COL=${COL:=70}
41
    [[ -t 1 ]] && echo -en "\033[${COL}G"
42
    # Colors
43
    if [[ -t 1 ]]
44
    then
45
        local COLOR_SUCCESS=${COLOR_SUCCESS:-'\033[1;32m'}
46
        local COLOR_FAILURE=${COLOR_FAILURE:-'\033[1;31m'}
47
        local COLOR_WARNING='\033[1;33m' COLOR_NORMAL='\033[0;39m'
48
    else
49
        local COLOR_SUCCESS= COLOR_FAILURE= COLOR_WARNING= COLOR_NORMAL=
50
    fi
51
    [[ "$ret" -eq 0 ]] && echo -e "[$COLOR_SUCCESS$OK$COLOR_NORMAL]" || \
52
		echo -e "[$COLOR_FAILURE$FAILED$COLOR_NORMAL]"
53
    return $ret
54
}
55

56
echo_for_print_status() {
57
    local m=$1
58
    if tty >/dev/null 2>&1 && [[ -n "${PRINT_STATUS_COLOR:-}" ]]
59
    then
60
        m="$(tput setaf $PRINT_STATUS_COLOR)$m$(tput sgr0)"
61
    fi
62
    printf "%s: " "$m"
63
}
64

65
exec_and_print_status() {
66
    # prints a message, execute a command and print its exit status
67
    # using print_exit_status function.
68
    # 1st Parameter: a message
69
    # all other parameter: the command to execute
70
    # returns the exit status code of the command
71
    [[ $# -lt 2 ]] && return 1
72
    local m=$1 ; shift
73
    PRINT_STATUS_COLOR="${PRINT_STATUS_COLOR-}" \
74
		echo_for_print_status "$m"
75
    "$@"
76
    print_exit_status $?
77
}
78

79
default_test() {
80
	[[ $? -eq 0 ]]
81
}
82

83
failure() {
84
	[[ $? -ne 0 ]]
85
}
86

87
shell_env() {
88
	# Greps are:
89
	# * variables automatically altered by bash itself.
90
	# * local environment variables, used by the environment checking routines.
91
	# * argsparse private variables, subjects to modifications/deletion.
92
	# * argsparse public variables.
93
	# * argsparse results, which are expected to change.
94
	set | \
95
		grep -vE '^(FIRST|SECOND|SHELLOPTS|FUNCNAME|_|LINENO|BASH_(ARG[CV]|LINENO|SOURCE|REMATCH)|PIPESTATUS)=' | \
96
		grep -vE '^before=' | \
97
		grep -vE '^__argsparse_(options_descriptions|short_options|tmp_identifiers)=' | \
98
		grep -vE '^argsparse_usage_description=' | \
99
		grep -vE '^(program_(params|options))|cumulated_values_[0-9a-zA-Z_]+='
100
	shopt
101
	set -o | grep -v xtrace
102
}
103

104
check_env() {
105
	{
106
		printf "For test: %s\n" "$message"
107
		diff -u <(echo "$before") <(shell_env)
108
	} >> unittest.env.log
109
}
110

111
parse_option_wrapper() {
112
	local message=$1
113
	local TEST=${TEST:-default_test}
114
	shift
115
	local before=$(shell_env)
116
	echo_for_print_status "Checking $message"
117
	(
118
		printf "Test is: %s\n" "$message"
119
		printf "Validation is: %s\n" "$TEST"
120
		set -x
121
		(
122
			trap check_env EXIT
123
			argsparse_parse_options "$@"
124
			${INNERTEST:-exit $?}
125
		)
126
	) >>unittest.log 2>&1
127
	$TEST
128
	print_exit_status || exit
129
}
130

131
(
132
	argsparse_usage_description="Usage has been triggered"
133
	echo_for_print_status "Checking no parameter triggers usage"
134
	( 
135
		argsparse_parse_options
136
	) | grep -q "$argsparse_usage_description"
137
	[ ${PIPESTATUS[0]} -ne 0 -a ${PIPESTATUS[1]} -eq 0 ]
138
	print_exit_status 
139
)
140

141
(
142
	argsparse_allow_no_argument yes
143
	argsparse_use_option option ""
144
	parse_option_wrapper "argsparse_allow_no_argument yes"
145
)
146

147
(
148
	argsparse_usage_description="Usage has been triggered"
149
	echo_for_print_status "Checking dummy option triggers usage"
150
	( 
151
		argsparse_parse_options --dumb 2>/dev/null
152
	) | grep -q "$argsparse_usage_description"
153
	[ ${PIPESTATUS[0]} -ne 0 -a ${PIPESTATUS[1]} -eq 0 ]
154
	print_exit_status 
155
)
156

157
(
158
	argsparse_use_option first-option "first option description"
159
	echo_for_print_status "Checking if option description appear in usage"
160
	(
161
		argsparse_parse_options -h
162
	) | grep -q "first option description"
163
	print_exit_status
164
)
165

166
(
167
	argsparse_use_option shortcut ""
168
	parse_option_wrapper "option detection" --shortcut
169

170
	argsparse_set_option_property short:S shortcut
171
	parse_option_wrapper "short property" -S
172
)
173

174
(
175
	argsparse_use_option =shortcut ""
176
	parse_option_wrapper "= in optstring" -s
177
)
178

179
(
180
	argsparse_use_option option1 "" type:weird:value
181
	echo_for_print_status "Checking 'weird:value' property value"
182
	[[ $(argsparse_has_option_property option1 type) = 'weird:value' ]]
183
	print_exit_status
184
)
185

186
(
187
	argsparse_use_option option1 ""
188
	for weirdo in '?' , '*' '!'
189
	do
190
		(
191
			echo_for_print_status "Checking forbidden '$weirdo' property value"
192
			argsparse_set_option_property type:"foo${weirdo}bar" option1 2>/dev/null
193
			failure
194
			print_exit_status
195
		)
196
	done
197
)
198

199
value_check_test() {
200
	[[ ${program_options[$value_check_option]} = $value_check_value ]]
201
}
202

203
value_check() {
204
	local value_check_option=$1
205
	local value_check_value=$2
206
	shift 2
207
	INNERTEST=value_check_test parse_option_wrapper "$@"
208
}
209

210
(
211
	argsparse_use_option value ""
212
	argsparse_set_option_property value value
213
	TEST=failure parse_option_wrapper "if value property expects value" --value
214
	value_check value 1 "if value is correctly detected" --value 1
215
)
216

217
(
218
	argsparse_use_option value: ""
219
	value_check value 1 "if value is correctly detected with optstring" --value 1
220
	TEST=failure parse_option_wrapper "if value property expects value if set with optstring" --value
221
)	
222

223
(
224
	argsparse_use_option option1 ""
225
	argsparse_use_option option2 "" mandatory
226
	TEST=failure parse_option_wrapper "if missing mandatory option triggers error" --option1
227
	parse_option_wrapper "if mandatory option makes return code 0" --option1 --option2
228
	argsparse_use_option option3 "" mandatory
229
	TEST=failure parse_option_wrapper "if missing mandatory options trigger error (1)" --option1
230
	TEST=failure parse_option_wrapper "if missing mandatory options trigger error (2)" --option1 --option2
231
	TEST=failure parse_option_wrapper "if missing mandatory options trigger error (3)" --option1 --option3
232
	parse_option_wrapper "if missing mandatory options trigger error (4)" --option3 --option2
233
)
234

235
cumul_test() {
236
	[[ $? -ne 0 ]] && return 1
237
	local cumul="cumulated_values_$option[@]"
238
	# Shoud never trigger nounset
239
	local -a array=( "${!cumul}" )
240
	local i
241
	[[ ${#array[@]} -eq "${#cumul_array[@]}" ]] || return 1
242
	for ((i=0 ; i < ${#array[@]} ; i++))
243
	do
244
		[[ ${array[$i]} = "${cumul_array[$i]}" ]] || return 1
245
	done
246
	return 0
247
}
248

249
(
250
	argsparse_use_option option1 "" cumulative
251
	option=option1
252
	cumul_array=(1 2 1 2)
253
	INNERTEST=cumul_test parse_option_wrapper "cumulative property behaviour" "${cumul_array[@]/#/--option1=}"
254
)
255

256
(
257
	option=option2
258
	cumul_array=(1 2)
259
	argsparse_use_option option2 "" cumulativeset
260
	INNERTEST=cumul_test parse_option_wrapper "cumulativeset property behaviour" "${cumul_array[@]/#/--option2=}" "${cumul_array[@]/#/--option2=}"
261
)
262

263
(
264
	argsparse_use_option option1 ""
265
	argsparse_use_option option2 "" alias:option1
266
	value_check option1 1 "simple alias" --option1
267
	argsparse_use_option option3 "" alias:option2
268
	value_check option1 1 "recursive alias" --option1
269
)
270

271
(
272
	argsparse_use_option option1 ""
273
	argsparse_use_option option2 "" require:option1
274
	parse_option_wrapper "simple require (1)" --option2 --option1
275
	TEST=failure parse_option_wrapper "simple require (2)" --option2
276
	argsparse_use_option option3 "" require:option2
277
	TEST=failure parse_option_wrapper "recursive require (1)" --option3
278
	TEST=failure parse_option_wrapper "recursive require (2)" --option3 --option2
279
	parse_option_wrapper "recursive require (3)" --option3 --option2 --option1
280
)
281

282
(
283
	argsparse_use_option option1 ""
284
	argsparse_use_option option2 "" exclude:option1
285
	TEST=failure parse_option_wrapper "exclusion" --option2 --option1
286
)
287

288
(
289
	argsparse_use_option option1 ""
290
	argsparse_use_option option2 "" default:1 exclude:option1
291
	parse_option_wrapper "exclusion does not prevent default values" --option1
292
)
293

294
(
295
	argsparse_use_option option1 ""
296
	argsparse_use_option option2 ""
297
	argsparse_use_option option3 "" require:"option1 option2"
298
	TEST=failure parse_option_wrapper "multiple require (1)" --option3
299
	TEST=failure parse_option_wrapper "multiple require (2)" --option3 --option2
300
	TEST=failure parse_option_wrapper "multiple require (3)" --option3 --option1
301
	parse_option_wrapper "multiple require (4)" --option3 --option2 --option1
302
)
303

304

305
(
306
	declare -a option_option1_values=(accept)
307
	argsparse_use_option option1: ""
308
	TEST=failure parse_option_wrapper "accepted values (bad value)" \
309
		--option1 foo
310
	parse_option_wrapper "accepted values (good value)" --option1 accept
311
)
312

313
dir=$(mktemp -d)
314
file=$(mktemp --tmpdir="$dir")
315
declare -A types=(
316
	[link]="$dir/link1 $dir/link2"
317
	[file]="$file $dir/link1"
318
	[directory]=". .. $dir $dir/linkdir"
319
	[char]="c . 0 @"
320
	[unsignedint]=123
321
	[int]="1 123 0 -1 -938945349"
322
	[hexa]="0x123abc 71234 abc"
323
	[ipv4]="192.168.40.254 127.0.0.1 1.2.3.4"
324
	[ipv6]="2001:7a8:b018::1 ::1 2001:7a8:b018:0:21f:c6ff:fe59:71fd"
325
	[username]=$(whoami)
326
	[group]=$(id -gn)
327
	[port]="ssh 80"
328
	[portnumber]=1234
329
)
330

331
fifo="$dir/fifo"
332
if mkfifo "$fifo"
333
then
334
	types[pipe]=$fifo
335
else
336
	printf "Could not create persistent fifo. Disabling pipe type test.\n"
337
	: $((disabled++))
338
fi
339

340
for socket in /dev/log
341
do
342
	[[ -S $socket ]] || continue
343
	types[socket]=$socket
344
done
345

346
# Look for a terminal
347
for stdfd in 0
348
do
349
	[[ -t $stdfd ]] || continue
350
	types[terminal]=$stdfd
351
done
352

353
for t in terminal socket
354
do
355
	if [[ ${types[$t]-unset} = unset ]]
356
	then
357
		: $((disabled++))
358
		printf "No %s found: won't test.\n" "$t"
359
	fi
360
done
361

362
# Default 'host' type test case:
363
types[host]="${types[ipv4]} ${types[ipv6]}"
364
# Test hostnames only if we can actually resolv.
365
if host localhost >/dev/null 2>&1
366
then
367
	types[hostname]="livna.org localhost"
368
	types[host]+=" ${types[hostname]}"
369
else
370
	printf "Cannot resolv localhost. Not testing hostname type.\n"
371
	: $((disabled++))
372
fi
373

374
(cd "$dir" && ln -s "$file" link1 && ln -s asdf link2 && ln -s . linkdir)
375

376
for type in "${!types[@]}"
377
do
378
	(
379
		argsparse_use_option option: "" "type:$type"
380
		# Left unquoted on purpose. There's no funny chars in this array.
381
		i=1
382
		for value in ${types[$type]}
383
		do
384
			parse_option_wrapper "type '$type' ($i)" --option "$value"
385
			: $((i++))
386
		done
387
	)
388
done
389

390
declare -A bad_types=(
391
	[file]=". .. $dir $fifo"
392
	[directory]="${types[file]} $fifo $dir/link1"
393
	[pipe]="${types[file]} ${types[dir]-}"
394
	[socket]="${types[file]} $fifo $dir/link1"
395
	[link]="$file $fifo"
396
	[char]="12 abc"
397
	[unsignedint]="-1 -2234958 abc"
398
	[int]="a b casdf"
399
	[ipv4]="${types[ipv6]}"
400
	[ipv6]="${types[ipv4]}"
401
	[host]="livna.org1"
402
	[username]="asdkfjasdkfanfk1234"
403
	[group]="asdkfjasdkfanfk1234"
404
	[port]="12345678 foobar"
405
	[portnumber]=12345678
406
	[hostname]="livna.org1 google.com1"
407
)
408

409
for type in "${!bad_types[@]}"
410
do
411
	(
412
		argsparse_use_option option: "" "type:$type"
413
		# Left unquoted on purpose. (again)
414
		i=1
415
		for value in ${bad_types[$type]}
416
		do
417
			TEST=failure parse_option_wrapper "failure with type '$type' ($i)" --option "$value"
418
			: $((i++))
419
		done
420
	)
421
done
422

423
(
424
	argsparse_use_option option: "" "type:terminal"
425
	TEST=failure parse_option_wrapper "failure with type 'terminal' (1)" --option 0 </dev/null
426
)
427
rm -Rf "$dir"
428

429
(
430
	check_option_type_unittest() {
431
		local value=$1
432
		[[ $value = 1 ]]
433
	}
434
	argsparse_use_option option: "" type:unittest
435
	parse_option_wrapper "custom type (1)" --option 1
436
	TEST=failure parse_option_wrapper "custom type (2)" --option asdf
437
)
438

439
printf "Tests report:\n"
440
if [[ $disabled -ne 0 ]]
441
then
442
	printf "* %d test(s) disabled.\n" "$disabled"
443
fi
444

445
if [[ $errors -ne 0 ]]
446
then
447
	printf "* %d error(s) encountered. (See above)\n" "$errors"
448
	exit_code=1
449
else
450
	printf "All tests passed.\n"
451
	exit_code=0
452
fi
453

454
printf "Environment alteration detected:\n"
455
if grep -v -B 1 '^For test:' unittest.env.log
456
then
457
	: exit_code $((exit_code))
458
else
459
	printf "None.\n"
460
fi
461

462
if [[ $exit_code -ne 0 ]]
463
then
464
	printf "Runtime environment was:\n"
465
	set -x
466
	{
467
		command -v bash
468
		bash --version
469
		shopt
470
		set -o
471
	} 2>&1
472
fi
473

474
printf "Completed in %d seconds.\n" "$SECONDS"
475

476
exit "$exit_code"
477

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.