~sircmpwn/ctools

eeb67c08093705187889bfa09d4bbff9c61ad365 — Drew DeVault 1 year, 9 months ago bc6c5b9
Add initial test suite
8 files changed, 339 insertions(+), 1 deletions(-)

M README.md
M meson.build
M src/chgrp.c
A test/basename
A test/cat
A test/chgrp
A test/harness.sh
A test/meson.build
M README.md => README.md +5 -0
@@ 35,6 35,11 @@ Alternatively, you can compile all of the tools with meson:
This is recommended for distro packagers. Various options are available when
building with meson via meson_options.txt.

## Conformance tests

Build with meson and run `ninja -C build test` to run POSIX conformance tests
against ctools.

## Scope

ctools includes POSIX.1-2017 base utilities, but does not support any

M meson.build => meson.build +2 -0
@@ 61,3 61,5 @@ add_project_arguments(cc.get_supported_arguments([
foreach prog : oneshots
	executable(prog, ['src/' + prog + '.c'])
endforeach

subdir('test')

M src/chgrp.c => src/chgrp.c +1 -1
@@ 55,7 55,7 @@ static int
process(const char *path, struct context *ctx, int depth)
{
	struct stat s;
	if (stat(path, &s) < 0) {
	if (lstat(path, &s) < 0) {
		perror(path);
		return 1;
	}

A test/basename => test/basename +37 -0
@@ 0,0 1,37 @@
#!/bin/sh
. "$HARNESS" basename

should_handle_null_string() (
	bn="$(basename '')"
	[ "$bn" = "." ] || [ "$bn" = "" ]
)

should_handle_slashes() (
	bn="$(basename '////')"
	[ "$bn" = "/" ]
)

should_handle_trailing_slashes() (
	bn="$(basename 'bar/')"
	[ "$bn" = "bar" ]
)

should_handle_basename() (
	bn="$(basename '/foo/bar/baz')"
	[ "$bn" = "baz" ]
)

should_handle_suffix() (
	bn="$(basename '/foo/bar/baz.txt' '.txt')"
	[ "$bn" = "baz" ]
)

should_handle_ddash basename foo/bar.txt .txt

runtests \
	should_handle_ddash \
	should_handle_null_string \
	should_handle_slashes \
	should_handle_trailing_slashes \
	should_handle_basename \
	should_handle_suffix

A test/cat => test/cat +35 -0
@@ 0,0 1,35 @@
#!/bin/sh
. "$HARNESS" cat

echo "this is a test file" >"$TMPDIR"/test-file-1
echo "this is another test file" >"$TMPDIR"/test-file-2

should_handle_one_file() (
	ct="$(cat "$TMPDIR"/test-file-1)"
	[ "$ct" = "this is a test file" ]
)

should_handle_two_files() (
	ct="$(cat "$TMPDIR"/test-file-1 "$TMPDIR"/test-file-2)"
	ref=$(echo "this is a test file" && echo "this is another test file")
	[ "$ct" = "$ref" ]
)

should_handle_stdin() (
	ct="$(echo "this is from stdin" | cat -)"
	[ "$ct" = "this is from stdin" ] || return 1
	ct="$(echo "this is from stdin" | cat)"
	[ "$ct" = "this is from stdin" ]
)

should_handle_u_flag() (
	# actual behavior is not especially important/testable
	ct="$(echo "this is from stdin" | cat -u)"
	[ "$ct" = "this is from stdin" ]
)

runtests \
	should_handle_one_file \
	should_handle_two_files \
	should_handle_stdin \
	should_handle_u_flag

A test/chgrp => test/chgrp +182 -0
@@ 0,0 1,182 @@
#!/bin/sh
. "$HARNESS" chgrp

auxgroup=${AUXGROUP:-}
maingroup="$(id -gn)"
for grp in $(id -Gn)
do
	if [ $grp != "$maingroup" ]
	then
		auxgroup=$grp
		break
	fi
done
[ -z "$auxgroup" ] && ! echo "Unable to select auxillary group for testing" >&2

assert_grp() (
	grp="$1"
	shift
	for path in $*
	do
		dir="$(dirname "$path")"
		base="$(basename "$path")"
		if [ "$(find "$dir" -name "$base" -group "$grp")" != "$path" ]
		then
			printf "(%s:group != %s) " "$path" "$grp"
			return 1
		fi
	done
)

should_handle_simple() (
	path="$TMPDIR"/simple
	touch "$path"
	assert_grp "$maingroup" "$path" || return 1
	chgrp "$auxgroup" "$path" || return 1
	assert_grp "$auxgroup" "$path" || return 1
)

should_handle_mutex_options() (
	! chgrp -hR "$auxgroup" "$TMPDIR" 2>/dev/null
)

should_handle_several() (
	path_1="$TMPDIR"/several-1
	path_2="$TMPDIR"/several-2
	touch "$path_1" "$path_2"
	assert_grp "$maingroup" "$path_1" "$path_2" || return 1
	chgrp "$auxgroup" "$path_1" "$path_2" || return 1
	assert_grp "$auxgroup" "$path_1" "$path_2" || return 1
)

should_handle_recursive() (
	mkdir -p "$TMPDIR"/recursive/subdir
	path_1="$TMPDIR"/recursive/recursive-1
	path_2="$TMPDIR"/recursive/subdir/recursive-2
	touch "$path_1" "$path_2"

	assert_grp "$maingroup" "$path_1" "$path_2" || return 1
	chgrp -R "$auxgroup" "$TMPDIR"/recursive || return 1
	assert_grp "$auxgroup" "$path_1" "$path_2" || return 1
)

should_handle_recursive_Hflag() (
	# -H: If the -R option is specified and a symbolic link referencing a
	# file of type directory is specified on the command line, chgrp shall
	# change the group of the directory referenced by the symbolic link and
	# all files in the file hierarchy below it.
	dir_1="$TMPDIR"/recursive_hflag
	dir_2="$TMPDIR"/recursive_hflag2
	dir_3="$TMPDIR"/recursive_hflag3
	path_1="$dir_1"/file1
	path_2="$dir_2"/file2
	path_3="$dir_3"/file3
	explicit_link="$dir_1"/explicit
	implicit_link="$dir_1"/implicit
	mkdir -p "$dir_1" "$dir_2" "$dir_3"
	ln -s "$dir_2" "$explicit_link"
	ln -s "$dir_3" "$implicit_link"
	touch "$path_1" "$path_2" "$path_3"

	# Assert that initial state matches expectations
	assert_grp "$maingroup" "$path_1" "$path_2" "$path_3" \
		"$dir_1" "$dir_2" "$dir_3" \
		"$explicit_link" "$implicit_link" || return 1

	# If repeated, the last -HLP option is effective
	chgrp -RHLPH "$auxgroup" "$dir_1" "$explicit_link" || return 1

	# Explicit link target and children updated
	assert_grp "$auxgroup" "$path_1" "$path_2" "$dir_2" || return 1
	# Explicit link unchanged
	assert_grp "$maingroup" "$explicit_link" || return 1
	# Implicit link target updated
	assert_grp "$auxgroup" "$dir_3" || return 1
	# Implicit link children unchanged
	assert_grp "$maingroup" "$path_3" || return 1
	# Implicit link unchanged
	assert_grp "$maingroup" "$implicit_link" || return 1
)

should_handle_recursive_Lflag() (
	# -L: If the -R option is specified and a symbolic link referencing a
	# file of type directory is specified on the command line or
	# encountered during the traversal of a file hierarchy, chgrp shall
	# change the group of the directory referenced by the symbolic link and
	# all files in the file hierarchy below it.
	dir_1="$TMPDIR"/recursive_lflag
	dir_2="$TMPDIR"/recursive_lflag2
	dir_3="$TMPDIR"/recursive_lflag3
	path_1="$dir_1"/file1
	path_2="$dir_2"/file2
	path_3="$dir_3"/file3
	explicit_link="$dir_1"/explicit
	implicit_link="$dir_1"/implicit
	mkdir -p "$dir_1" "$dir_2" "$dir_3"
	ln -s "$dir_2" "$explicit_link"
	ln -s "$dir_3" "$implicit_link"
	touch "$path_1" "$path_2" "$path_3"

	# Assert that initial state matches expectations
	assert_grp "$maingroup" "$path_1" "$path_2" "$path_3" \
		"$dir_1" "$dir_2" "$dir_3" \
		"$explicit_link" "$implicit_link" || return 1

	# If repeated, the last -HLP option is effective
	chgrp -RHLPL "$auxgroup" "$dir_1" "$explicit_link" || return 1

	# Explicit link target and children updated
	assert_grp "$auxgroup" "$path_1" "$path_2" "$dir_2" || return 1
	# Explicit link unchanged
	assert_grp "$maingroup" "$explicit_link" || return 1
	# Implicit link target and children updated
	assert_grp "$auxgroup" "$path_3" "$dir_3" || return 1
	# Implicit link unchanged
	assert_grp "$maingroup" "$implicit_link" || return 1
)

should_handle_recursive_Pflag() (
	# -P: If the -R option is specified and a symbolic link is specified on
	# the command line or encountered during the traversal of a file
	# hierarchy, chgrp shall change the group ID of the symbolic link. The
	# chgrp utility shall not follow the symbolic link to any other part of
	# the file hierarchy.
	dir_1="$TMPDIR"/recursive_pflag
	dir_2="$TMPDIR"/recursive_pflag2
	dir_3="$TMPDIR"/recursive_pflag3
	path_1="$dir_1"/file1
	path_2="$dir_2"/file2
	path_3="$dir_3"/file3
	explicit_link="$dir_1"/explicit
	implicit_link="$dir_1"/implicit
	mkdir -p "$dir_1" "$dir_2" "$dir_3"
	ln -s "$dir_2" "$explicit_link"
	ln -s "$dir_3" "$implicit_link"
	touch "$path_1" "$path_2" "$path_3"

	# Assert that initial state matches expectations
	assert_grp "$maingroup" "$path_1" "$path_2" "$path_3" \
		"$dir_1" "$dir_2" "$dir_3" \
		"$explicit_link" "$implicit_link" || return 1

	# If repeated, the last -HLP option is effective
	chgrp -RHLPP "$auxgroup" "$dir_1" "$explicit_link" || return 1

	# Explicit link target and children updated
	assert_grp "$auxgroup" "$path_1" || return 1
	# Explicit link updated
	assert_grp "$auxgroup" "$explicit_link" || return 1
	# Implicit link target and children unchanged
	assert_grp "$maingroup" "$dir_2" "$path_2" "$path_3" "$dir_3" || return 1
	# Implicit link updated
	assert_grp "$auxgroup" "$implicit_link" || return 1
)

runtests \
	should_handle_simple \
	should_handle_several \
	should_handle_mutex_options \
	should_handle_recursive \
	should_handle_recursive_Hflag \
	should_handle_recursive_Lflag \
	should_handle_recursive_Pflag

A test/harness.sh => test/harness.sh +59 -0
@@ 0,0 1,59 @@
set -eu
tool="$1"

export PATH="$BUILDDIR/..:$PATH"
TMPDIR=${TMPDIR:-/tmp}
TMPDIR=$TMPDIR/ctools-$tool-testfiles
mkdir -p "$TMPDIR"

cleanup() {
	rm -rf "$TMPDIR"
}

trap cleanup EXIT

npass=0
nfail=0

runtests() {
	max=0
	for t in $@
	do
		if [ ${#t} -gt $max ]
		then
			max=${#t}
		fi
	done
	for t in $@
	do
		printf "%-${max}s " "$t"
		if "$t"
		then
			printf 'OK\n'
			npass=$((npass+1))
		else
			printf 'FAIL\n'
			nfail=$((nfail+1))
		fi
	done
	printf 'Passed: %d tests\n' $npass
	printf 'Failed: %d tests\n' $nfail
	if [ $nfail -ne 0 ]
	then
		exit 1
	fi
}

_ddash_tool=
_ddash_args=
# Pass the reference command line and this tests it with and without --
should_handle_ddash() {
	if [ $# -ne 0 ]
	then
		_ddash_tool="$1"
		shift
		_ddash_args="$@"
	else
		$_ddash_tool $_ddash_args && $_ddash_tool -- $_ddash_args
	fi
}

A test/meson.build => test/meson.build +18 -0
@@ 0,0 1,18 @@
harness=join_paths(meson.current_source_dir(), 'harness.sh')

test_files = [
	'basename',
	'cat',
	'chgrp',
]

foreach test_file : test_files
	test(
		test_file,
		find_program(test_file),
		env: [
			'HARNESS=@0@'.format(harness),
			'BUILDDIR=@0@'.format(meson.current_build_dir()),
		],
	)
endforeach