1#
2# This file and its contents are supplied under the terms of the
3# Common Development and Distribution License ("CDDL"), version 1.0.
4# You may only use this file in accordance with the terms of version
5# 1.0 of the CDDL.
6#
7# A full copy of the text of the CDDL should have accompanied this
8# source.  A copy of the CDDL is also available via the Internet at
9# http://www.illumos.org/license/CDDL.
10#
11
12#
13# Copyright (c) 2016, 2017 by Delphix. All rights reserved.
14#
15
16. $STF_SUITE/include/libtest.shlib
17
18ZCP_ROOT=$STF_SUITE/tests/functional/channel_program
19
20#
21# Note: In case of failure (log_fail) in this function
22# we delete the file passed as <input file> so the
23# test suite doesn't leak temp files on failures. So it
24# is expected that <input file> is a temp file and not
25# an installed file.
26#
27# <exitcode> <expected error string> <input file> <zfs program args>
28# e.g. log_program 0 "" tmp.7a12V $POOL foo.zcp arg1 arg2
29function log_program
30{
31	typeset expectexit=$1
32	shift
33	typeset expecterror=$1
34	shift
35	typeset tmpin=$1
36	shift
37	typeset cmdargs=$@ tmpout=$(mktemp) tmperr=$(mktemp)
38
39	# Expected output/error filename is the same as the .zcp name
40	typeset basename
41	if [[ $2 != "-" ]]; then
42		basename=${2%.*}
43	fi
44
45	log_note "running: zfs program $cmdargs:"
46
47	zfs program $cmdargs >$tmpout 2>$tmperr
48	typeset ret=$?
49
50	log_note $'input:\n'"$(<$tmpin)"
51	log_note $'output:\n'"$(<$tmpout)"
52	log_note $'error:\n'"$(<$tmperr)"
53	log_note "ret: $ret"
54
55	#
56	# Verify correct return value
57	#
58	if [[ $ret -ne $expectexit ]]; then
59		rm $tmpout $tmperr $tmpin
60		log_fail "return mismatch: expected $expectexit, got $ret"
61	fi
62
63	#
64	# Check the output or reported error for successful or error returns,
65	# respectively.
66	#
67	if [[ -f "$basename.out" ]] && [[ $expectexit -eq 0 ]]; then
68		if ! outdiff=$(diff "$basename.out" "$tmpout"); then
69			output=$(<$tmpout)
70			rm $tmpout $tmperr $tmpin
71			log_fail $'Output mismatch. Expected:\n' \
72				"$(<$basename.out)"$'\nBut got:\n'"$output"$'\n' \
73				$'Diff:\n'"$outdiff"
74		fi
75
76	elif [[ -f "$basename.err" ]] && [[ $expectexit -ne 0 ]]; then
77		if ! outdiff=$(diff "$basename.err" "$tmperr"); then
78			outputerror=$(<$tmperr)
79			rm $tmpout $tmperr $tmpin
80			log_fail $'Error mismatch. Expected:\n' \
81				"$(<$basename.err)"$'\nBut got:\n'"$outputerror"$'\n' \
82				$'Diff:\n'"$outdiff"
83		fi
84
85	elif [[ -n $expecterror ]] && [[ $expectexit -ne 0 ]]; then
86		if ! grep -q "$expecterror" $tmperr; then
87			outputerror=$(<$tmperr)
88			rm $tmpout $tmperr $tmpin
89			log_fail $'Error mismatch. Expected to contain:\n' \
90				"$expecterror"$'\nBut got:\n'"$outputerror"$'\n'
91		fi
92
93	elif [[ $expectexit -ne 0 ]]; then
94		#
95		# If there's no expected output, error reporting is allowed to
96		# vary, but ensure that we didn't fail silently.
97		#
98		if [[ -z "$(<$tmperr)" ]]; then
99			rm $tmpout $tmperr $tmpin
100			log_fail "error with no stderr output"
101		fi
102	fi
103
104	#
105	# Clean up all temp files except $tmpin which is
106	# reused for the second invocation of log_program.
107	#
108	rm $tmpout $tmperr
109}
110
111#
112# Even though the command's arguments are passed correctly
113# to the log_must_program family of wrappers the majority
114# of the time, zcp scripts passed as HERE documents can
115# make things trickier (see comment within the function
116# below) in the ordering of the commands arguments and how
117# they are passed. Thus, with this function we reconstruct
118# them to ensure that they are passed properly.
119#
120function log_program_construct_args
121{
122	typeset tmpin=$1
123	shift
124
125	args=""
126	i=0
127	while getopts "nt:m:" opt; do
128		case $opt in
129			t) args="$args -t $OPTARG"; i=$(($i + 2)) ;;
130			m) args="$args -m $OPTARG"; i=$(($i + 2)) ;;
131			n) args="$args -n"; i=$(($i + 1)) ;;
132		esac
133	done
134	shift $i
135
136	pool=$1
137	shift
138
139	infile=$1
140	shift
141
142	#
143	# Copy the contents of the original channel program to $tmpin.
144	#
145	# If $infile currently holds "-" (a dash) it means that we consume a
146	# HERE doc from stdin, otherwise $infile is a file path.
147	#
148	cat $infile > $tmpin
149
150	lua_args=$@
151
152	echo "$args $pool $tmpin $lua_args"
153}
154
155#
156# Program should complete successfully
157# when run in either context.
158#
159function log_must_program
160{
161	typeset tmpin=$(mktemp)
162
163	program_args=$(log_program_construct_args $tmpin $@)
164
165	log_program 0 "" $tmpin "-n $program_args"
166	log_program 0 "" $tmpin "$program_args"
167
168	rm $tmpin
169}
170#
171# Program should error as expected in
172# the same way in both contexts.
173#
174function log_mustnot_checkerror_program
175{
176	typeset expecterror=$1
177	shift
178	typeset tmpin=$(mktemp)
179
180	program_args=$(log_program_construct_args $tmpin $@)
181
182	log_program 1 "$expecterror" $tmpin "-n $program_args"
183	log_program 1 "$expecterror" $tmpin "$program_args"
184
185	rm $tmpin
186}
187
188#
189# Program should fail when run in either
190# context.
191#
192function log_mustnot_program
193{
194	log_mustnot_checkerror_program "" $@
195}
196
197
198#
199# Program should error as expected in
200# open context but complete successfully
201# in syncing context.
202#
203function log_mustnot_checkerror_program_open
204{
205	typeset expecterror=$1
206	shift
207	typeset tmpin=$(mktemp)
208
209	program_args=$(log_program_construct_args $tmpin $@)
210
211	log_program 1 "$expecterror" $tmpin "-n $program_args"
212	log_program 0 "" $tmpin "$program_args"
213
214	rm $tmpin
215}
216
217#
218# Program should complete successfully
219# when run in syncing context but fail
220# when attempted to run in open context.
221#
222function log_must_program_sync
223{
224	log_mustnot_checkerror_program_open "requires passing sync=TRUE" $@
225}
226