4 Commits

Author SHA1 Message Date
Thomas Levine
dc0a030f71 change tests that need series flag to be series 2016-02-26 17:28:04 +00:00
Thomas Levine
41a63f003c better waiting 2016-02-26 17:27:14 +00:00
Thomas Levine
aa05b95715 add wait and stuff still works 2016-02-26 17:17:00 +00:00
Thomas Levine
c611667152 add --b, --series parser 2016-02-26 16:54:25 +00:00
19 changed files with 94 additions and 190 deletions

16
HISTORY
View File

@@ -7,22 +7,6 @@ Version 0.0.7
The Molly-guard is now more accepting. For example, you no longer need to The Molly-guard is now more accepting. For example, you no longer need to
pass -f in this case: https://github.com/creationix/nvm/issues/357 pass -f in this case: https://github.com/creationix/nvm/issues/357
Previously, tests were run if they were executable and were otherwise marked
as skipped. Now, an executable script can indicate that it is skipped by
exiting with code 3. For example, if a test requires some dependancy, it
might look for the dependency and then skip if it does not see the dependency.
It might look like this.
#!/bin/sh
if which inkscape; then
exit 3 # status code 3 for skip
fi
inkscape blah blah ...
I chose status code 3 sort of arbitrarily at first, but it turns out that it
would the appropriate status code if these tests were Nagios plugins, as the
concept of skipping a test is similar to the Nagios concept of unknown service
status (https://nagios-plugins.org/doc/guidelines.html#AEN78).
Version 0.0.6 Version 0.0.6
--------------------- ---------------------

24
TODO
View File

@@ -1,6 +1,18 @@
Things I want Things I want
============= =============
Skip tests
-------------
I want tests to be able to decide to skip themselves. For example, if a test
requires some dependancy, it might look for the dependency and then skip if it
does not see the dependency. It might look like this.
#!/bin/sh
if which parallel > /dev/null; then
exit 3 # status code 3 for skip
fi
parallel blah blah ...
Test speed Test speed
------------- -------------
Make tests run faster. Make tests run faster.
@@ -19,11 +31,6 @@ Hmm or maybe there's a compromise: Tell people to mount /tmp as a tmpfs so
that temp files are fast. Maybe allow people to set some other directory as that temp files are fast. Maybe allow people to set some other directory as
the temporary file place, in case they want a different tmpfs location. the temporary file place, in case they want a different tmpfs location.
In order to run things in parallel, we have to change how we do the
stdout_file. I think it's easiest to create separate files for each test and
to save them in testroot/.urchin/stdout/$filename. The test root would be
defined as the closest ancestor containing a .urchin directory.
Options Options
------------- -------------
I want long options. For example, there's presently -f and -e. I want long options. For example, there's presently -f and -e.
@@ -144,13 +151,6 @@ on the nvm tests. I wound up running this instead.
The Molly guard would be assessed, and the corresponding setup, setup_dir, The Molly guard would be assessed, and the corresponding setup, setup_dir,
teardown, and teardown_dir files would be run in the appropriate order. teardown, and teardown_dir files would be run in the appropriate order.
In order to know how far up the tree to evaluate the setup, &c. files,
I think it would make sense to require that a ".urchin" file be placed in the
root of the tests. Urchin would keep going up until it sees this file, and it
would evaluate the appropriate setup, &c. files from there down to the
particular test file of interest. We would also use this for testing
directtories more correctly.
Running automated tasks Running automated tasks
------------------------- -------------------------
Urchin might be appropriate for if you have lots of tasks that you want to run Urchin might be appropriate for if you have lots of tasks that you want to run

View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -1,5 +1,5 @@
tmp=$(mktemp) tmp=$(mktemp)
../urchin -e -f ./.die-on-fail > $tmp ../urchin -e -b -f ./.die-on-fail > $tmp
result=$? result=$?
grep '1 should run.' $tmp grep '1 should run.' $tmp

View File

@@ -1,4 +1,4 @@
tmp=$(mktemp) tmp=$(mktemp)
../../urchin -t .testsuite/ | sed 1d > $tmp ../../urchin -b -t .testsuite/ | sed 1d > $tmp
diff $tmp .expected-output diff $tmp .expected-output

View File

@@ -1,6 +0,0 @@
#!/bin/sh
observed=$(../../urchin "$1" .testsuite/a/b)
expected=.testsuite/a/b/../..
test "$observed" = "$expected"

View File

@@ -1,2 +0,0 @@
*
!.gitignore

View File

@@ -1 +0,0 @@
#!/usr/bin/env true

View File

@@ -1,2 +0,0 @@
#!/bin/sh
../../urchin --root .testsuite/a/b/c/testcase

View File

@@ -1,2 +0,0 @@
#!/bin/sh
./.meta-assess --root

View File

@@ -1,2 +0,0 @@
#!/bin/sh
./.meta-assess -r

View File

@@ -1,2 +0,0 @@
#!/bin/sh
../../urchin --root /bin 2>&1 | grep "'/bin/.urchin'"

View File

@@ -1,2 +0,0 @@
#!/bin/sh
! ../../urchin --root .testsuite/a/not-a-file

View File

@@ -1,4 +0,0 @@
#!/bin/sh
I_AM_SOURCING_URCHIN_IN_A_TEST=true . ../../urchin
test $(get_stdout_file t) = aoeu

View File

@@ -1 +0,0 @@
exit 1

View File

@@ -1 +0,0 @@
exit 3

View File

@@ -1 +0,0 @@
exit 0

View File

@@ -1 +0,0 @@
test $(../../urchin -t .test/ | grep -c SKIP) -eq 1

211
urchin
View File

@@ -7,7 +7,7 @@
# No part of urchin, including this file, may be copied, modified, propagated, # No part of urchin, including this file, may be copied, modified, propagated,
# or distributed except according to the terms contained in the COPYING file. # or distributed except according to the terms contained in the COPYING file.
set +e
# Make sure that CDPATH isn't set, as it causes `cd` to behave unpredictably - # Make sure that CDPATH isn't set, as it causes `cd` to behave unpredictably -
# notably, it can produce output, which breaks fullpath(). # notably, it can produce output, which breaks fullpath().
@@ -16,42 +16,11 @@ unset CDPATH
# Urchin version number # Urchin version number
VERSION=0.0.6 VERSION=0.0.6
urchin_root() { fullpath() {
# Call recursively but remember the original argument. (
current="$(remove_trailing_slash "$1")" cd -- "$1"
if test -n "$2"; then pwd
orig="$2" )
else
orig="$1"
fi
if test "$(readlink -f -- "$1")" = /; then
# Stop traversing upwards at /
if test -d "$orig"; then
origdir="$orig"
else
origdir=$(dirname "$orig")
fi
echo "You need to create the .urchin directory in the root of your tests,
maybe like this:
mkdir '$(readlink -f -- "$(remove_trailing_slash "$origdir")")/.urchin'
" >&2
return 1
elif ! test -e "$current"; then
echo "$current: No such file or directory">&2
return 1
elif test -f "$current"; then
urchin_root "$(dirname "$current")" "$orig"
elif test -d "$current"/.urchin; then
remove_trailing_slash "$current"
elif test "$current" != . && test "$current" != .. &&
echo "$current" | grep '^\.' && > /dev/null; then
# Stop traversing upwards at hidden directories.
urchin_root / "$orig"
else
urchin_root "$current"/.. "$orig"
fi
} }
indent() { indent() {
@@ -61,40 +30,16 @@ indent() {
fi fi
} }
remove_trailing_slash() {
echo "$1" | sed s/\\/$//
}
escape_slashes() {
echo "$1" | sed s+/+\\\\/+g
}
get_stdout_file() {
root="$(readlink -f -- "$(urchin_root "$1")")"
test_file="$(readlink -f -- "$1")"
eroot="$(escape_slashes "$root")"
base="$(echo "$test_file" | sed "s/^$eroot/$eroot\/.urchin/")"
if test -d "$1"; then
echo "$base"/.index
else
echo "$base"
fi
}
recurse() { recurse() {
potential_test="$1" potential_test="$1"
indent_level="$2" indent_level="$2"
shell_for_sh_tests="$3" shell_for_sh_tests="$3"
[ "$potential_test" = '.urchin' ] && return
[ "$potential_test" = 'setup_dir' ] && return [ "$potential_test" = 'setup_dir' ] && return
[ "$potential_test" = 'teardown_dir' ] && return [ "$potential_test" = 'teardown_dir' ] && return
[ "$potential_test" = 'setup' ] && return [ "$potential_test" = 'setup' ] && return
[ "$potential_test" = 'teardown' ] && return [ "$potential_test" = 'teardown' ] && return
stdout_file="$(get_stdout_file "$potential_test")"
mkdir -p "$(dirname "$stdout_file")"
[ $indent_level -eq 0 ] && : > "$stdout_file" [ $indent_level -eq 0 ] && : > "$stdout_file"
if [ -d "$potential_test" ] if [ -d "$potential_test" ]
@@ -122,19 +67,23 @@ recurse() {
[ -f setup ] && [ -x setup ] && ./setup >> "$stdout_file" [ -f setup ] && [ -x setup ] && ./setup >> "$stdout_file"
# $2 instead of $indent_level so it doesn't clash # $2 instead of $indent_level so it doesn't clash
set +e (
recurse "${test}" "$(( $2 + 1 ))" "$shell_for_sh_tests" recurse "${test}" $(( $2 + 1 )) "$shell_for_sh_tests"
exit_code=$? exit_code=$?
set -e
if $exit_on_fail && test $exit_code -ne 0; then
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file" [ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file" exit $exit_code
return 1 ) &
fi
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file" if $single_process; then
wait $!
exit_code=$?
if $exit_on_fail && test $exit_code -ne 0; then
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file"
return 1
fi
fi
done done
wait
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file" [ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file"
) )
if $tap_format; then if $tap_format; then
@@ -155,13 +104,12 @@ recurse() {
else else
TEST_SHELL="$TEST_SHELL" ./"$potential_test" > "$stdout_file" 2>&1 TEST_SHELL="$TEST_SHELL" ./"$potential_test" > "$stdout_file" 2>&1
fi fi
exit_code="$?" exit_code="$?"
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file" [ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
if [ $exit_code -eq 0 ]; then if [ $exit_code -eq 0 ]; then
result=success result=success
elif [ $exit_code -eq 3 ]; then
result=skip
else else
result=fail result=fail
fi fi
@@ -218,6 +166,7 @@ recurse() {
return 1 return 1
fi fi
fi fi
[ $indent_level -eq 0 ] && rm "$stdout_file"
} }
has_sh_or_no_shebang_line() { has_sh_or_no_shebang_line() {
@@ -240,6 +189,8 @@ $USAGE
-s <shell> Invoke test scripts that either have no shebang line at all or -s <shell> Invoke test scripts that either have no shebang line at all or
have shebang line "#!/bin/sh" with the specified shell. have shebang line "#!/bin/sh" with the specified shell.
-b, --series Run all tests in series in a single process. The default
is to run tests from the same directory in parallel.
-e Stop running if any single test fails. This is helpful if you want -e Stop running if any single test fails. This is helpful if you want
to use Urchin to run things other than tests, such as a set of to use Urchin to run things other than tests, such as a set of
configuration scripts. configuration scripts.
@@ -247,7 +198,6 @@ $USAGE
contain the word "test". contain the word "test".
-t Format output in Test Anything Protocol (TAP) -t Format output in Test Anything Protocol (TAP)
-h, --help This help. -h, --help This help.
-r, --root Print the Urchin root for a particular file or directory.
-v Display the version number. -v Display the version number.
Go to https://github.com/tlevine/urchin for documentation on writing tests. Go to https://github.com/tlevine/urchin for documentation on writing tests.
@@ -268,11 +218,12 @@ plural () {
} }
urchin_go() { urchin_go() {
rm -f "$logfile"
if "$tap_format"; then if "$tap_format"; then
printf \#\ printf \#\
fi fi
echo Running tests at $(date +%Y-%m-%dT%H:%M:%S) echo Running tests at $(date +%Y-%m-%dT%H:%M:%S)
start="$(date +%s)" start=$(date +%s)
# Determine the environment variable to define for test scripts # Determine the environment variable to define for test scripts
# that reflects the specified or implied shell to use for shell-code tests. # that reflects the specified or implied shell to use for shell-code tests.
@@ -287,10 +238,7 @@ urchin_go() {
TEST_SHELL='/bin/sh' TEST_SHELL='/bin/sh'
fi fi
# 1 test folder recurse "$1" 0 "$2" # test folder -- indentation level -- [shell to invoke test scripts with]
# 2 indentation level
# 3 shell to invoke test scripts with
recurse "$1" 0 "$2"
finish=$(date +%s) finish=$(date +%s)
elapsed=$(($finish - $start)) elapsed=$(($finish - $start))
@@ -312,7 +260,7 @@ urchin_go() {
printf '%s\n' "$failed $(plural test "$failed") failed." printf '%s\n' "$failed $(plural test "$failed") failed."
printf '\033[m' printf '\033[m'
fi fi
# rm -f "$logfile" rm -f "$logfile"
test -z "$failed" || test "$failed" -eq '0' test -z "$failed" || test "$failed" -eq '0'
} }
@@ -328,57 +276,58 @@ urchin_molly_guard() {
exit 1 exit 1
} }
if test -z "$I_AM_SOURCING_URCHIN_IN_A_TEST"; then single_process=false
shell_for_sh_tests= shell_for_sh_tests=
force=false force=false
exit_on_fail=false exit_on_fail=false
tap_format=false tap_format=false
while [ $# -gt 0 ] while [ $# -gt 0 ]
do do
case "$1" in case "$1" in
-e) exit_on_fail=true;; -b|--series) single_process=true;;
-f) force=true;; -e) exit_on_fail=true;;
-r|--root) -f) force=true;;
shift -s)
urchin_root "$1" shift
shell_for_sh_tests=$1
which "$shell_for_sh_tests" > /dev/null || {
echo "Cannot find specified shell: '$shell_for_sh_tests'" >&2
urchin_help >&2
exit 3
} ;;
-t) tap_format=true;;
-h|--help) urchin_help
exit 0;;
-v) echo "$VERSION"
exit;; exit;;
-s) -*) urchin_help >&2
shift exit 1;;
shell_for_sh_tests=$1 *) break;;
which "$shell_for_sh_tests" > /dev/null || { esac
echo "Cannot find specified shell: '$shell_for_sh_tests'" >&2 shift
urchin_help >&2 done
exit 11
} ;;
-t) tap_format=true;;
-h|--help) urchin_help
exit 0;;
-v) echo "$VERSION"
exit;;
-*) urchin_help >&2
exit 1;;
*) break;;
esac
shift
done
# Verify argument for main stuff # Verify argument for main stuff
if [ "$#" != '1' ] || [ ! -d "$1" ] if [ "$#" != '1' ] || [ ! -d "$1" ]
then then
[ -n "$1" ] && [ ! -d "$1" ] && echo "Not a directory: '$1'" >&2 [ -n "$1" ] && [ ! -d "$1" ] && echo "Not a directory: '$1'" >&2
echo "$USAGE" >&2 echo "$USAGE" >&2
exit 11 exit 3
fi fi
# Run or present the Molly guard. if $exit_on_fail && ! $single_process; then
if ! urchin_root "$1" > /dev/null; then echo 'You must specify -b/--series in order to use -e.' >&2
exit 1 exit 3
elif basename "$(readlink -f -- "$(urchin_root "$1")")" | fi
grep -Fi 'test' > /dev/null || $force; then
logfile="$(readlink -f -- "$(urchin_root "$1")/.urchin/.log")" # Constants
printf '' > "$logfile" logfile=$(fullpath "$1")/.urchin.log
urchin_go "$1" "$shell_for_sh_tests" stdout_file=$(fullpath "$1")/.urchin_stdout
else
urchin_molly_guard # Run or present the Molly guard.
fi if fullpath "$1" | grep -Fi 'test' > /dev/null || $force
then
urchin_go "$1" "$shell_for_sh_tests"
else
urchin_molly_guard
fi fi