Module Name:    src
Committed By:   kre
Date:           Thu Jul 21 09:52:49 UTC 2022

Modified Files:
        src/distrib/sets/lists/tests: mi
        src/etc/mtree: NetBSD.dist.tests
        src/tests/usr.bin: Makefile
Added Files:
        src/tests/usr.bin/realpath: Makefile t_realpath.sh

Log Message:
Add ATF tests for realpath(1)

Note that realpath can act differently for root than for other users
(where an ordinary user will see EACCESS root just barrels right through).

The tests adapt themselves, when run as root, less error cases can be
tested than when run as some other user.


To generate a diff of this commit:
cvs rdiff -u -r1.1216 -r1.1217 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.192 -r1.193 src/etc/mtree/NetBSD.dist.tests
cvs rdiff -u -r1.36 -r1.37 src/tests/usr.bin/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/usr.bin/realpath/Makefile \
    src/tests/usr.bin/realpath/t_realpath.sh

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1216 src/distrib/sets/lists/tests/mi:1.1217
--- src/distrib/sets/lists/tests/mi:1.1216	Tue Jul  5 22:50:41 2022
+++ src/distrib/sets/lists/tests/mi	Thu Jul 21 09:52:48 2022
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1216 2022/07/05 22:50:41 rillig Exp $
+# $NetBSD: mi,v 1.1217 2022/07/21 09:52:48 kre Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -6254,6 +6254,10 @@
 ./usr/tests/usr.bin/pwhash/Atffile			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/pwhash/Kyuafile			tests-usr.bin-tests	compattestfile,atf,kyua
 ./usr/tests/usr.bin/pwhash/t_pwhash			tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/realpath				tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/realpath/Atffile			tests-usr.bin-tests	compattestfile,atf
+./usr/tests/usr.bin/realpath/Kyuafile			tests-usr.bin-tests	compattestfile,atf,kyua
+./usr/tests/usr.bin/realpath/t_realpath			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/rump_server				tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/rump_server/Atffile			tests-usr.bin-tests	compattestfile,atf
 ./usr/tests/usr.bin/rump_server/Kyuafile		tests-usr.bin-tests	compattestfile,atf,kyua

Index: src/etc/mtree/NetBSD.dist.tests
diff -u src/etc/mtree/NetBSD.dist.tests:1.192 src/etc/mtree/NetBSD.dist.tests:1.193
--- src/etc/mtree/NetBSD.dist.tests:1.192	Sun May 22 17:55:08 2022
+++ src/etc/mtree/NetBSD.dist.tests	Thu Jul 21 09:52:48 2022
@@ -1,4 +1,4 @@
-#	$NetBSD: NetBSD.dist.tests,v 1.192 2022/05/22 17:55:08 rillig Exp $
+#	$NetBSD: NetBSD.dist.tests,v 1.193 2022/07/21 09:52:48 kre Exp $
 
 ./usr/libdata/debug/usr/tests
 ./usr/libdata/debug/usr/tests/atf
@@ -456,6 +456,7 @@
 ./usr/tests/usr.bin/pr
 ./usr/tests/usr.bin/printf
 ./usr/tests/usr.bin/pwhash
+./usr/tests/usr.bin/realpath
 ./usr/tests/usr.bin/rump_server
 ./usr/tests/usr.bin/sdiff
 ./usr/tests/usr.bin/sed

Index: src/tests/usr.bin/Makefile
diff -u src/tests/usr.bin/Makefile:1.36 src/tests/usr.bin/Makefile:1.37
--- src/tests/usr.bin/Makefile:1.36	Sun May 22 17:55:08 2022
+++ src/tests/usr.bin/Makefile	Thu Jul 21 09:52:48 2022
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.36 2022/05/22 17:55:08 rillig Exp $
+#	$NetBSD: Makefile,v 1.37 2022/07/21 09:52:48 kre Exp $
 #
 
 .include <bsd.own.mk>
@@ -8,7 +8,7 @@ TESTSDIR=       ${TESTSBASE}/usr.bin
 TESTS_SUBDIRS=	awk basename bzip2 cc cmp compress config cpio col cut \
 		diff dirname find fstat gdb grep gzip id indent \
 		infocmp jot ld locale m4 make mixerctl mkdep nbperf \
-		netpgpverify patch pkill pr printf pwhash rump_server \
+		netpgpverify patch pkill pr printf pwhash realpath rump_server \
 		shmif_dumpbus sdiff sed sort tar tmux tr unifdef uniq \
 		vmstat xlint ztest
 

Added files:

Index: src/tests/usr.bin/realpath/Makefile
diff -u /dev/null src/tests/usr.bin/realpath/Makefile:1.1
--- /dev/null	Thu Jul 21 09:52:49 2022
+++ src/tests/usr.bin/realpath/Makefile	Thu Jul 21 09:52:49 2022
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.1 2022/07/21 09:52:49 kre Exp $
+
+.include <bsd.own.mk>
+
+TESTSDIR=       ${TESTSBASE}/usr.bin/realpath
+
+TESTS_SH=	t_realpath
+
+.include <bsd.test.mk>
Index: src/tests/usr.bin/realpath/t_realpath.sh
diff -u /dev/null src/tests/usr.bin/realpath/t_realpath.sh:1.1
--- /dev/null	Thu Jul 21 09:52:49 2022
+++ src/tests/usr.bin/realpath/t_realpath.sh	Thu Jul 21 09:52:49 2022
@@ -0,0 +1,670 @@
+# $NetBSD: t_realpath.sh,v 1.1 2022/07/21 09:52:49 kre Exp $
+#
+# Copyright (c) 2022 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+# ===========================================================
+#
+# Test data and expected results
+
+# Note that the empty line calls realpath with no file arg
+existing='.
+
+../Dir/StdOut
+./S1
+./S1/../S4
+./Snr/../S4
+S1/S2/File
+S1/S3/Link
+Snr/HoHo
+Snr/Link
+L
+/
+/bin
+Self
+Self/
+S4/S1/'
+
+exist_results='Dir
+Dir
+Dir/StdOut
+Dir/S1
+Dir/S4
+Dir/S4
+Dir/S1/S2/File
+Dir/S1/S2/File
+Dir/Snr/HoHo
+Dir/S1
+Dir/StdOut
+/
+/bin
+Dir
+Dir
+Dir/S1'
+
+exist_root_only='Snx/HaHa
+Snx/Link'
+
+exist_root_results='Dir/Snx/HaHa
+Dir/S1/S2/File'
+
+nofile='-
+trash
+Snr/Haha
+T1
+T2
+T3
+T4
+T5
+../Dir/T2
+../Dir/T3
+/nonsense
+/bin/../nonsense
+./Self/Self/Self/Self/S1/../Self/../Dir/Self/T1
+Self/nonsense'
+
+nofile_results='Dir/-
+Dir/trash
+Dir/Snr/Haha
+Dir/NoSuchFile
+Dir/S1/NoSuchFile
+Dir/S1/NoSuchFile
+Dir/S1/S2/NoSuchFile
+Dir/S1/S2/NoSuchFile
+Dir/S1/NoSuchFile
+Dir/S1/NoSuchFile
+/nonsense
+/nonsense
+Dir/NoSuchFile
+Dir/nonsense'
+
+always_fail='StdOut/
+StdOut/../StdErr
+Loop
+S1/S5/Link
+Loop/../StdOut
+BigLoop
+U1
+U2
+U3
+U4
+U5
+U6
+U7
+U8
+U9
+T1/NoSuchFile
+T1/../NoSuchFile
+U9/../NoSuchFile
+U9/../StdOut'
+
+
+# ===========================================================
+# Helper functions
+#
+
+# Create the test environment
+setup()
+{
+	atf_require_prog /usr/bin/mktemp
+	atf_require_prog /bin/ln
+	atf_require_prog /bin/cp
+	atf_require_prog /bin/mkdir
+	atf_require_prog /bin/chmod
+
+	DIR=${PWD}/$(mktemp -d Dir.XXXXX) ||
+		atf_fail "Did not make test directory"
+	cd "${DIR}" || atf_fail "Unable to cd $DIR"
+
+	ID=$( set -- $( type id ) && test "$1" = id && test "$2" = is &&
+		test $# -eq 3 && printf %s "$3"  || printf no-id-program)
+
+	mkdir Dir && cd Dir			|| atf_fail "enter Dir"
+
+	>StdOut					|| atf_fail "setup StdOut"
+	>StdErr					|| atf_fail "setup StdErr"
+	ln -s ../Dir Dir			|| atf_fail "setup Dir"
+	ln -s Loop Loop				|| atf_fail "setup Loop"
+	ln -s . Self				|| atf_fail "setup Self"
+	mkdir S1 S1/S2 S1/S3 S4 S4/S5		|| atf_fail "setup subdirs"
+	echo S1/S2/File > S1/S2/File		|| atf_fail "setup File"
+	ln -s ../S2/File S1/S3/Link		|| atf_fail "setup S3/Link"
+	ln -s ../S1 S4/S1			|| atf_fail "setup S4/S1"
+	ln -s StdOut L1				|| atf_fail "setup L1"
+	ln -s L1 L2				|| atf_fail "setup L2"
+	ln -s ../L2 S1/L3			|| atf_fail "setup L3"
+	ln -s ../L3 S1/S2/L4			|| atf_fail "setup L4"
+	ln -s ../S2/L4 S1/S3/L5			|| atf_fail "setup L5"
+	ln -s S1/S3/L5 L			|| atf_fail "setup L"
+	ln -s ${PWD}/S1 S4/PWDS1		|| atf_fail "setup PWDS1"
+	ln -s ${PWD}/S9 S4/PWDS9		|| atf_fail "setup PWDS9"
+	ln -s ${PWD}/S9/File S4/PWDS9F		|| atf_fail "setup PWDS9F"
+	ln -s ../S4/BigLoop S1/BigLoop		|| atf_fail "setup S1/BigLoop"
+	ln -s ../BigLoop S4/BigLoop		|| atf_fail "setup S4/BigLoop"
+	ln -s "${DIR}"/Dir/S1/BigLoop BigLoop	|| atf_fail "setup BigLoop"
+	mkdir Snx				|| atf_fail "setup Snx"
+	cp /dev/null Snx/HaHa			|| atf_fail "setup Snx/HaHa"
+	ln -s "${DIR}"/Dir/S1/S2/File Snx/Link	|| atf_fail "setup Snx/Link"
+	mkdir Snr				|| atf_fail "setup Snr"
+	cp /dev/null Snr/HoHo			|| atf_fail "setup Snr/HoHo"
+	ln -s "${DIR}"/Dir/S4/PWDS1 Snr/Link	|| atf_fail "setup Snr/Link"
+	ln -s ../Snx/HaHa Snr/HaHa		|| atf_fail "setup HaHa"
+	ln -s "${DIR}"/Dir/NoSuchFile T1	|| atf_fail "setup T1"
+	ln -s "${DIR}"/Dir/S1/NoSuchFile T2	|| atf_fail "setup T2"
+	ln -s S1/NoSuchFile T3			|| atf_fail "setup T3"
+	ln -s "${DIR}"/Dir/S1/S2/NoSuchFile T4	|| atf_fail "setup T4"
+	ln -s S1/S2/NoSuchFile T5		|| atf_fail "setup T5"
+	ln -s "${DIR}"/Dir/StdOut/CannotExist T6 || atf_fail "setup T6"
+	ln -s "${DIR}"/Dir/NoDir/WhoKnows U1	|| atf_fail "setup U1"
+	ln -s "${DIR}"/Dir/S1/NoDir/WhoKnows U2	|| atf_fail "setup U2"
+	ln -s "${DIR}"/Dir/S1/S2/NoDir/WhoKnows U3 || atf_fail "setup U3"
+	ln -s "${DIR}"/Dir/S1/../NoDir/WhoKnows U4 || atf_fail "setup U4"
+	ln -s "${DIR}"/Dir/NoDir/../StdOut U5	|| atf_fail "setup U5"
+	ln -s NoDir/../StdOut U6		|| atf_fail "setup U6"
+	ln -s S1/NoDir/../../StdOut U7		|| atf_fail "setup U7"
+	ln -s "${DIR}"/Dir/Missing/NoDir/WhoKnows U8 || atf_fail "setup U8"
+	ln -s "${DIR}"/Dir/Missing/NoDir/../../StdOut U9 || atf_fail "setup U9"
+	chmod a+r,a-x Snx			|| atf_fail "setup a-x "
+	chmod a+x,a-r Snr			|| atf_fail "setup a-r"
+}
+
+# ATF will remove all the files we made, just ensure perms are OK
+cleanup()
+{
+	chmod -R u+rwx .
+	return 0
+}
+
+run_tests_pass()
+{
+	opt=$1
+	tests=$2
+	results=$3
+
+	FAILS=
+	FAILURES=0
+	T=0
+
+	while [ "${#tests}" -gt 0 ]
+	do
+		FILE=${tests%%$'\n'*}
+		EXP=${results%%$'\n'*}
+
+		tests=${tests#"${FILE}"};	tests=${tests#$'\n'}
+		results=${results#"${EXP}"};	results=${results#$'\n'}
+
+		test -z "${EXP}" && atf_fail "Too few results (test botch)"
+
+		T=$(( $T + 1 ))
+
+		GOT=$(realpath $opt -- ${FILE:+"${FILE}"})
+		STATUS=$?
+
+		case "${GOT}" in
+		'')	;;		# nothing printed, deal with that below
+
+		/*)			# Full Path (what we want)
+			# Remove the unpredictable ATF dir prefix (if present)
+			GOT=${GOT#"${DIR}/"}
+			# Now it might be a relative path, that's OK
+			# at least it can be compared (its prefix is known)
+			;;
+
+		*)			# a relative path was printed
+			FAILURES=$(($FAILURES + 1))
+			FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+			FAILS="${FAILS}${opt:+ $opt} '${FILE}'"
+			FAILS="${FAILS}: output relative path '${GOT}'"
+			FAILS="${FAILS}, and exit($STATUS)"
+			continue
+			;;
+		esac
+
+
+		if [ $STATUS -ne 0 ] || [ "${EXP}" != "${GOT}" ]
+		then
+			FAILURES=$(($FAILURES + 1))
+			if [ $STATUS -ne 0 ]
+			then
+			    FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+			    FAILS="${FAILS}${opt:+ $opt} '${FILE}'"
+			    FAILS="${FAILS} failed: status ${STATUS}"
+			else
+			    FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+			    FAILS="${FAILS}${opt:+ $opt} '${FILE}'"
+			    FAILS="${FAILS} expected '${EXP}' received '${GOT}'"
+			fi
+		fi
+	done
+
+	if test  -n "${results}"
+	then
+		FAILURES=$(( $FAILURES + 1 ))
+
+		N=$(( $(printf '%s\n' "${results}" | wc -l) ))
+		s=s; if [ $N -eq 1 ]; then s=; fi
+		FAILS=${FAILS:+"${FAILS}"$'\n'}"After $T tests"
+		FAILS="still $N more result$s (test botch)"
+	fi
+
+	if [ $FAILURES -gt 0 ]
+	then
+		s=s
+		if [ $FAILURES -eq 1 ]; then s=; fi
+		printf >&2 '%d path%s resolved incorrectly:\n%s\n' \
+			"$FAILURES" "$s" "${FAILS}"
+		atf_fail "$FAILURES path$s resolved incorrectly; see stderr"
+	fi
+	return 0
+}
+
+run_tests_fail()
+{
+	opt=$1
+	tests=$2
+
+	FAILS=
+	FAILURES=0
+	T=0
+
+	while [ "${#tests}" -gt 0 ]
+	do
+		FILE=${tests%%$'\n'*}
+
+		tests=${tests#"${FILE}"};	tests=${tests#$'\n'}
+
+		test -z "${FILE}" && continue
+
+		T=$(( $T + 1 ))
+
+		GOT=$(realpath $opt -- "${FILE}" 2>StdErr)
+		STATUS=$?
+
+		ERR=$(cat StdErr)
+
+		if [ $STATUS -eq 0 ] || [ "${GOT}" ] || ! [ "${ERR}" ]
+		then
+			FAILURES=$(($FAILURES + 1))
+			if [ "${STATUS}" -eq 0 ]
+			then
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T: "
+				FAILS="${FAILS}${opt:+ $opt} '${FILE}' worked;"
+				FAILS="${FAILS} received: '${GOT}'}"
+
+				if [ "${ERR}" ]; then
+					FAILS="${FAILS} and on stderr '${ERR}'"
+				fi
+			elif [ "${GOT}" ]
+			then
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+				FAILS="${FAILS}${opt:+ $opt} '${FILE}' failed,"
+				FAILS="${FAILS} but with '${GOT}' on stdout"
+
+				if [ "${ERR}" ]; then
+					FAILS="${FAILS}, and on stderr '${ERR}'"
+				else
+					FAILS="${FAILS}, and empty stderr"
+				fi
+			else
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+				FAILS="${FAILS}${opt:+ $opt} '${FILE}' failed,"
+				FAILS="${FAILS} but with no error message"
+			fi
+		fi
+	done
+	if [ $FAILURES -gt 0 ]
+	then
+		S=s
+		if [ $FAILURES -eq 1 ]; then s=; fi
+		printf >&2 '%d path%s resolved incorrectly:\n%s\n' \
+			"$FAILURES" "$s" "${FAILS}"
+		atf_fail "$FAILURES path$s resolved incorrectly; see stderr"
+	fi
+	return 0
+}
+
+# ===================================================================
+# Test cases setup follows (but almost all the work is earlier)
+
+atf_test_case a__e_ok cleanup
+realpath_e_ok_head()
+{
+	atf_set descr "Test realpath (with -e) cases which should work"
+}
+a__e_ok_body()
+{
+	setup
+	run_tests_pass -e "${existing}" "${exist_results}"
+
+	if [ -x "${ID}" ] && [ "$("$ID" -u)" = 0 ]
+	then
+		run_tests_pass -e "${exist_root_only}" "${exist_root_results}"
+	fi
+}
+a__e_ok_cleanup()
+{
+	cleanup
+}
+
+atf_test_case b__E_ok cleanup
+b__E_ok_head()
+{
+	atf_set descr "Test realpath (with -E) cases which should work"
+}
+b__E_ok_body() {
+	setup
+	# everything which works with -e should also work with -E
+	run_tests_pass -E "${existing}" "${exist_results}"
+	run_tests_pass -E "${nofile}" "${nofile_results}"
+
+	if [ -x "${ID}" ] && [ "$("${ID}" -u)" = 0 ]
+	then
+		run_tests_pass -E "${exist_root_only}" "${exist_root_results}"
+	fi
+}
+b__E_ok_cleanup()
+{
+	cleanup
+}
+
+atf_test_case c__ok cleanup
+c__ok_head()
+{
+	atf_set descr "Test realpath (without -e or -E) cases which should work"
+}
+c__ok_body() {
+	setup
+	# Our default for realpath is -E, so the -E tests should work
+	run_tests_pass '' "${existing}" "${exist_results}"
+	# but more should work as well
+	run_tests_pass '' "${nofile}" "${nofile_results}"
+
+	if [ -x "${ID}" ] && [ "$("${ID}" -u)" = 0 ]
+	then
+		run_tests_pass '' "${exist_root_only}" "${exist_root_results}"
+	fi
+}
+c__ok_cleanup()
+{
+	cleanup
+}
+
+atf_test_case d__E_fail
+d__E_fail_head()
+{
+	atf_set descr "Test realpath -e cases which should not work"
+}
+d__E_fail_body()
+{
+	setup
+	run_tests_fail -E "${always_fail}"
+	if [ -x "${ID}" ] && [ "$("${ID}" -u)" != 0 ]
+	then
+		run_tests_fail -E "${exist_root_only}"
+	fi
+}
+d__E_fail_cleanup()
+{
+	cleanup
+}
+
+atf_test_case e__e_fail
+e__e_fail_head()
+{
+	atf_set descr "Test realpath -e cases which should not work"
+}
+e__e_fail_body()
+{
+	setup
+	# Some -E tests that work should fail with -e
+	run_tests_fail -e "${nofile}"
+	run_tests_fail -e "${always_fail}"
+	if [ -x "${ID}" ] && [ "$("${ID}" -u)" != 0 ]
+	then
+		run_tests_fail -e "${exist_root_only}"
+	fi
+}
+e__e_fail_cleanup()
+{
+	cleanup
+}
+
+atf_test_case f__fail
+f__fail_head()
+{
+	atf_set descr "Test realpath cases which should not work (w/o -[eE])"
+}
+f__fail_body()
+{
+	setup
+	run_tests_fail '' "${always_fail}"
+	if [ -x "${ID}" ] && [ "$("${ID}" -u)" != 0 ]
+	then
+		run_tests_fail '' "${exist_root_only}"
+	fi
+}
+f__fail_cleanup()
+{
+	cleanup
+}
+
+atf_test_case g__q cleanup
+g__q_head()
+{
+	atf_set descr "Test realpath's -q option; also test usage message"
+}
+g__q_body()
+{
+	setup
+
+	# Just run these tests here, the paths have been tested
+	# already, all we care about is that -q suppresses err messages
+	# about the ones that fail, so just test those.  Since those
+	# always fail, it is irrlevant which of -e or -E we would use,
+	# so simply use neither.
+
+	# This is adapted from run_tests_fail
+
+	FAILURES=0
+	FAILS=
+
+	opt=-q
+
+	T=0
+	for FILE in ${always_fail}
+	do
+
+		test -z "${FILE}" && continue
+
+		T=$(( $T + 1 ))
+
+		GOT=$(realpath $opt -- "${FILE}" 2>StdErr)
+		STATUS=$?
+
+		ERR=$(cat StdErr)
+
+		if [ $STATUS -eq 0 ] || [ "${GOT}" ] || [ "${ERR}" ]
+		then
+			FAILURES=$(($FAILURES + 1))
+			if [ "${STATUS}" -eq 0 ]
+			then
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T: "
+				FAILS="${FAILS}${opt:+ $opt} '${FILE}' worked;"
+				FAILS="${FAILS} received: '${GOT}'}"
+
+				if [ "${ERR}" ]; then
+					FAILS="${FAILS} and on stderr '${ERR}'"
+				fi
+			elif [ "${GOT}" ]
+			then
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+				FAILS="${FAILS}${opt:+ $opt} '${FILE}' failed,"
+				FAILS="${FAILS} but with '${GOT}' on stdout"
+
+				if [ "${ERR}" ]; then
+					FAILS="${FAILS}, and on stderr '${ERR}'"
+				else
+					FAILS="${FAILS}, and empty stderr"
+				fi
+			else
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+				FAILS="${FAILS}${opt:+ $opt} '${FILE}' failed,"
+				FAILS="${FAILS} stderr: '${ERR}'"
+			fi
+		fi
+	done
+
+	# There are a couple of cases where -q does not suppress stderr
+
+	for FILE in '' -wObBl@ --
+	do
+
+		T=$(( $T + 1 ))
+
+		unset XTRA
+		case "${FILE}" in
+		'')	;;
+		--)	XTRA=;;
+		-*)	XTRA=/junk;;
+		esac
+
+		# Note lack of -- in the following, so $FILE can be either
+		# a file name (well, kind of...), or options.
+
+		GOT=$(realpath $opt "${FILE}" ${XTRA+"${XTRA}"} 2>StdErr)
+		STATUS=$?
+
+		ERR=$(cat StdErr)
+
+		if [ $STATUS -eq 0 ] || [ "${GOT}" ] || ! [ "${ERR}" ]
+		then
+			FAILURES=$(($FAILURES + 1))
+			if [ "${STATUS}" -eq 0 ]
+			then
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T: "
+				FAILS="${FAILS}${opt:+ $opt} ${FILE:-''}"
+				FAILS="${FAILS}${XTRA:+ $XTRA} worked;"
+				FAILS="${FAILS} received: '${GOT}'}"
+
+				if [ "${ERR}" ]; then
+					FAILS="${FAILS} and on stderr '${ERR}'"
+				fi
+			elif [ "${GOT}" ]
+			then
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+				FAILS="${FAILS}${opt:+ $opt} ${FILE:-''}"
+				FAILS="${FAILS}${XTRA:+ ${XTRA}} failed,"
+				FAILS="${FAILS} but with '${GOT}' on stdout"
+
+				if [ "${ERR}" ]; then
+					FAILS="${FAILS}, and on stderr '${ERR}'"
+				else
+					FAILS="${FAILS}, and empty stderr"
+				fi
+			else
+				FAILS=${FAILS:+"${FAILS}"$'\n'}"Path $T:"
+				FAILS="${FAILS}${opt:+ $opt} ${FILE:-''}"
+				FAILS="${FAILS}${XTRA:+ ${XTRA}} failed,"
+				FAILS="${FAILS} with stderr empty"
+			fi
+		fi
+	done
+
+	if [ $FAILURES -gt 0 ]
+	then
+		s=s
+		if [ $FAILURES -eq 1 ]; then s=; fi
+		printf >&2 '%d path%s resolved incorrectly:\n%s\n' \
+			"$FAILURES" "$s" "${FAILS}"
+		atf_fail "$FAILURES path$s resolved incorrectly; see stderr"
+	fi
+	return 0
+}
+g__q_cleanup()
+{
+	cleanup
+}
+
+atf_test_case h__n_args
+h__n_args_head()
+{
+	atf_set descr "Test realpath with multiple file args"
+}
+h__n_args_body()
+{
+	setup
+
+	# Since these paths have already (hopefully) tested and work
+	# (if a__e_ok had any failures, fix those before even looking
+	# at any failure here)
+
+	# Since we are assuming that the test cases all work, simply
+	# Count how many there are, and then expect the same number
+	# of answers
+
+	unset IFS
+	set -- ${existing}
+	# Note that any empty line (no args) case just vanished...
+	# That would be meaningless here, removing it is correct.
+
+ 	GOT=$(realpath -e -- "$@" 2>StdErr)
+	STATUS=$?
+
+	ERR=$(cat StdErr; printf X)
+	ERR=${ERR%X}
+
+	NR=$(( $(printf '%s\n' "${GOT}" | wc -l) ))
+
+	if [ $NR -ne $# ] || [ $STATUS -ne 0 ] || [ -s StdErr ]
+	then
+		printf >&2 'Stderr from test:\n%s\n' "${ERR}"
+		if [ $STATUS -eq 0 ]; then S="OK"; else S="FAIL($STATUS)"; fi
+		if [ ${#ERR} -ne 0 ]
+		then
+			E="${#ERR} bytes on stderr"
+		else
+			E="nothing on stderr"
+		fi
+		atf_fail 'Given %d args, got %d results; Status:%s; %s\n' \
+			"$#" "${NR}" "${S}" "${E}"
+	fi
+	return 0
+}
+h__n_args_cleanup()
+{
+	cleanup
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case a__e_ok
+	atf_add_test_case b__E_ok
+	atf_add_test_case c__ok
+	atf_add_test_case d__E_fail
+	atf_add_test_case e__e_fail
+	atf_add_test_case f__fail
+	atf_add_test_case g__q
+	atf_add_test_case h__n_args
+}

Reply via email to