54 Commits

Author SHA1 Message Date
Thomas Levine
87bb9f4660 link to COPYING file on web 2016-02-08 16:09:00 +00:00
Thomas Levine
caabcd25ff update HISTORY 2016-02-08 16:06:22 +00:00
Thomas Levine
8f82cf5aeb make long lines shorter 2016-02-08 16:05:56 +00:00
Thomas Levine
385ec3fe81 add a copying notice to the urchin excutable 2016-02-08 16:02:03 +00:00
Thomas Levine
d4eb99ffb0 rename LICENSE -> COPYING 2016-02-08 15:59:56 +00:00
Thomas Levine
ed939ff39a update history 2016-02-08 15:59:47 +00:00
Thomas Levine
d24a71d0d7 copyright notice
add Thomas Levine and Michael Klement
2016-02-08 15:59:16 +00:00
Thomas Levine
ceae1811e2 nagios todo 2016-02-08 15:51:53 +00:00
Thomas Levine
2ac3a52707 fix printf to work in mksh 2016-02-08 15:50:12 +00:00
Thomas Levine
2d4de62729 msys64 test results 2016-02-08 15:48:57 +00:00
Thomas Levine
3b3ba0e1fa windows bash implementations list 2016-02-08 15:48:57 +00:00
Thomas Levine
1bc9bb2671 explain license thing 2016-02-08 15:48:57 +00:00
Thomas Levine
fafd0c1801 note desire to test on windows 2016-02-08 15:48:57 +00:00
Thomas Levine
d85b0ee352 packaging ideas 2016-02-08 15:48:57 +00:00
Thomas Levine
cf793387e9 licensing 2016-02-08 15:48:57 +00:00
Thomas Levine
1f79db874b environment variables 2016-02-08 15:48:57 +00:00
Thomas Levine
c770479884 TODO list 2016-02-08 15:48:57 +00:00
Thomas Levine
96ab43c233 explain why urchin is called urchin 2016-01-29 17:28:26 +00:00
Thomas Levine
2be0151213 document the --help flag 2016-01-29 17:20:18 +00:00
Thomas Levine
655daf4b74 show all counts in cross-shell-tests runner
It previously just showed the last three lines. It needs to show the
last four lines now because urchin now displays skipped tests.
2016-01-29 17:19:15 +00:00
Thomas Levine
31023bce21 update HISTORY 2016-01-29 17:17:35 +00:00
Thomas Levine
d96f3707e9 implement -v 2016-01-29 17:17:31 +00:00
Thomas Levine
3f05794d7e add -v to usage 2016-01-29 17:16:01 +00:00
Thomas Levine
d7c8549206 urchin -v test 2016-01-29 17:14:34 +00:00
Thomas Levine
bc74b20c5c add authors 2016-01-28 15:33:43 +00:00
Thomas Levine
8bdd00c3e9 explain the branching 2016-01-28 15:30:59 +00:00
Thomas Levine
42dd0947e8 more changes 2016-01-28 15:28:36 +00:00
Thomas Levine
da6d89aa56 add to change log 2016-01-28 15:27:32 +00:00
Thomas Levine
3ea035d188 rename test to be more clear 2016-01-28 15:24:24 +00:00
Thomas Levine
8e669c40a6 add + indent symbol so directories line up 2016-01-28 15:23:14 +00:00
Thomas Levine
66c010b694 fix fixture to not expect stderr 2016-01-28 15:23:14 +00:00
Thomas Levine
7339e84d7c test stdout stderr for TAP 2016-01-28 15:23:14 +00:00
Thomas Levine
e364d40f3a TAP test 2016-01-28 15:23:14 +00:00
Thomas Levine
13e748d684 remove awk dependency in test suite
I'm on NixOS, so awk isn't in /usr/bin. I can search for it with
/usr/bin/env, but then I can't use -f in the shebang line.
2016-01-28 15:23:14 +00:00
Thomas Levine
bb42d27917 test plan 2016-01-28 15:23:14 +00:00
Thomas Levine
c1ec06c6fe logfile 2016-01-28 15:23:14 +00:00
Thomas Levine
5382946639 error on fail 2016-01-28 15:23:14 +00:00
Thomas Levine
144013c71c print skip count at end 2016-01-28 15:23:13 +00:00
Thomas Levine
009fd7ea05 print stdout for tap and not-tap 2016-01-28 15:23:13 +00:00
Thomas Levine
c3901fe175 tap indentation comments for directories 2016-01-28 15:23:13 +00:00
Thomas Levine
538c8437a7 assorted tap stuff 2016-01-28 15:23:13 +00:00
Thomas Levine
cd124e09ee more tap 2016-01-28 15:23:13 +00:00
Thomas Levine
d963f10be1 more tap 2016-01-28 15:23:13 +00:00
Thomas Levine
0ca55d155b oops 2016-01-28 15:23:13 +00:00
Thomas Levine
8d55550248 test test successes better 2016-01-28 15:23:13 +00:00
Thomas Levine
0f86778d79 convert indents to comments 2016-01-28 15:23:13 +00:00
Thomas Levine
92cb735bb7 start writing non-tap cases 2016-01-28 15:23:13 +00:00
Thomas Levine
c817355e94 not working 2016-01-28 15:23:13 +00:00
Thomas Levine
d4bedbbfcf first pass implementation of exit on fail 2016-01-28 15:23:13 +00:00
Thomas Levine
f6b57772f2 document unimplemented -e flag 2016-01-28 15:23:13 +00:00
Thomas Levine
72fa30e787 Test die on fail. 2016-01-28 15:23:13 +00:00
Thomas Levine
eda4a6e42c call urchin -s in cross-shell tests 2016-01-28 15:23:12 +00:00
Thomas Levine
df80586d79 remove "urchin -x" test 2016-01-28 15:23:12 +00:00
Thomas Levine
d525c1793b remove "urchin -x"
unnecessary now that shall exists
2016-01-28 15:23:12 +00:00
23 changed files with 353 additions and 74 deletions

9
AUTHORS Normal file
View File

@@ -0,0 +1,9 @@
Authors
-------
David Jones
Michael Klement
Thomas Levine
Maintainer
-------
Thomas Levine <_@thomaslevine.com>

View File

@@ -1,3 +1,5 @@
Copyright (c) 2013, 2014, 2015, 2016 Thomas Levine
Copyright (c) 2014, Michael Klement
Copyright (c) 2012, ScraperWiki Limited
All rights reserved.

23
HISTORY
View File

@@ -1,5 +1,26 @@
HISTORY
-------
=======
Version 0.0.6
---------------------
* Produce TAP output with the -t flag.
* Add a + sign in front of directories in the normal output so that they
line up with non-directories.
* Display skipped tests in the normal output and in the TAP output.
* Correct some things in the documentation.
* Rearrange things in the documentation to be more clear.
* Pass the -e flag to exit urchin if any single test fails.
* Remove the undocumented, experimental -x flag now that shall exists.
* Display version number with the -v flag.
* Document why Urchin is called "Urchin"
* Update TODO
* Support mksh (Change a printf command.)
* Make long lines shorter.
These changes are made somewhat separately in the branches "exit-on-fail",
"remove-urchin-x", "tap", and "update-readme". They are rebased into one
branch, "tlevine-2016-02", for merging into "master".
Version 0.0.5
---------------------

117
TODO Normal file
View File

@@ -0,0 +1,117 @@
Things I want
=============
Molly guard
-------------
The Molly-guard should be more accepting so that people don't have to use it
all the time and thus get used to using it. For example, you shouldn't need to
pass -f in this case.
https://github.com/creationix/nvm/issues/357
Test speed
-------------
Make tests run faster.
https://github.com/bike-barn/hermit/issues/62
First, easier thing is probably to run tests in parallel.
Second, also easier thing is to tell people to save things to RAM rather than
disk whenever they can.
Third, harder thing is to put the test suite in RAM automatically. Maybe the
whole test directory, which includes fixtures, gets copied to a tmpfs if one
exists.
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
the temporary file place, in case they want a different tmpfs location.
Options
-------------
I want long options. For example, there's presently -f and -e.
I want to make them -f|--force and -e|--exit.
Environment variables
-------------
Do something to make it easier to debug environment variables, because that is
often confusing.
https://github.com/creationix/nvm/issues/719
https://github.com/creationix/nvm/issues/589
Documenting that people should run "env" when their tests fail might be good
enough.
Licensing and copyright
------------------------
* Reference all owners and years in the Copyright file
* Consider copyleft licenses
* Add license notices to other files if necessary
Packaging
------------
Package for package managers.
* I want NixOS, of course.
* Debian is probably the big one.
Other interesting package managers
* Update the npm package
* Homebrew (for Mac)
Windows
----------
Try running Urchin in Windows somehow. Interpreters include
* CygWin (https://www.cygwin.com/)
* MSYS (http://mingw.org/wiki/msys)
* GNU on Windows (https://github.com/bmatzelle/gow/wiki)
* Git for Windows (https://git-scm.com/download/win)
* win-bash (http://win-bash.sourceforge.net/)
Consider copyleft licenses
----------
ScraperWiki owns the original version of Urchin (Thomas Levine did the early
work as part of his work for ScraperWiki.) and originally licensed it under an
MIT-style license. Other people made changes after this original ScraperWiki
version. As of January 2016, they are just Thomas Levine (when he wasn't
working for ScraperWiki) and Michael Klement.
The original license was MIT just because that's what ScraperWiki put on
everything. Should we change the license?
The MIT-style license grants pretty much all rights. It says that you need
to attribute when you redistribute source code, but you don't
necessarily have to redistribute source code.
A copyleft license adds the restriction that modified versions of the
code need to be licensed under the same license. GNU licenses in
particular require that source code be released if non-source versions are
released, and the different GNU licenses differ in what how the
non-source version is defined. (The original, GPL, discusses compiled
binaries.) Copyleft doesn't mean anything specific for commercial use.
MIT-licensed code can be modified and then licensed as GPL, because MIT
license allows that, but GPL code can't be modified as MIT, because MIT
doesn't allow that. And if we get all of the authors to agree on it, we
can always add whatever crazy license we want, regardless of what we
have already.
The distinction between MIT-style and GNU-something might matter quite little
in the case of Urchin.
1. Urchin is written in an interpreted language (shell), so it might be
hard to distribute usefully without providing the source code.
2. Urchin just runs tests; it doesn't get compiled with the rest of the
code (also because it's in shell). Thus, I think a GPL license on
Urchin wouldn't infect the code being tested.
This is as far as I have gotten with contemplating license changes. For now
we're sticking with the original MIT-style license, but it's easy to change
licenses later.
Nagios plugins
-----------------
It would be cool to run Nagios plugins with Urchin. This is already possible,
actually, but it might be worth giving some special thought to it.
https://nagios-plugins.org/doc/guidelines.html

View File

@@ -2,11 +2,11 @@
# Run urchin in a bunch of different shells,
# including a shell that isn't quite POSIX-compatible (zsh)
for shell in dash bash ksh zsh; do
for shell in dash bash mksh ksh zsh; do
if which $shell > /dev/null 2> /dev/null; then
echo
echo Running urchin tests in $shell
$shell urchin tests | tail -n 3
$shell urchin -s $shell tests | tail -n 4
else
echo
echo Skipping $shell because it is not in the PATH

View File

@@ -8,6 +8,10 @@ Urchin is a file-based test harness, normally used for testing shell programs.
It is written in portable shell and should thus work on GNU/Linux, BSD
(including Mac OS X), and other Unix-like platforms.
Urchin is called "Urchin" because
[sea urchins](https://en.wikipedia.org/wiki/Sea_urchin)
have shells called "tests".
## Try it out
Urchin's tests are written in Urchin, so you can run them to see what Urchin
is like. Clone the repository
@@ -165,9 +169,3 @@ It does something similar, but the interface may be more intuitive.
## Alternatives to Urchin
Alternatives to Urchin are discussed in
[this blog post](https://blog.scraperwiki.com/2012/12/how-to-test-shell-scripts/).
## Ideas for new features
* Support [Nagios plugins](https://nagios-plugins.org/doc/guidelines.html)
* Stop running if a test fails so one can use Urchin as a
[setup framework](https://github.com/tlevine/urchin/issues/16).

View File

View File

@@ -0,0 +1 @@
false

View File

@@ -0,0 +1 @@
false

View File

@@ -0,0 +1 @@
false

View File

@@ -1,5 +1,3 @@
#!/usr/bin/awk -f
# This script will only succeed if it is indeed processed by awk.
BEGIN { print "ok" }
#!/usr/bin/env true
true will processed the contents of this script, but that
means that nothing will happen and the script will exit 0

View File

@@ -3,6 +3,5 @@
# Tests the `-s <shell> option, which invokes shebang-less and sh-shebang-line test scripts with the specified shell (for testing *sourced* shell code).
which bash >/dev/null || { echo "Cannot test -s option: bash cannot be located." >&2; exit 2; }
which /usr/bin/awk >/dev/null || { echo "Cannot test -s option: /usr/bin/awk not found." >&2; exit 2; }
../../urchin -s bash ./.test-run-by-specified-shell

View File

@@ -0,0 +1,11 @@
tmp=$(mktemp)
../urchin -e -f ./.die-on-fail > $tmp
result=$?
grep '1 should run.' $tmp
grep '2 should run.' $tmp
grep -v '3 should not run.' $tmp
grep -v '4 should not run.' $tmp
rm $tmp
exit $result

2
tests/Print version on -v. Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/sh
../urchin -v | grep '[0-9.]\{3,\}'

View File

@@ -0,0 +1,10 @@
# Begin - .testsuite/
not ok 1 - a
# ------------ Begin output ------------
# This is stdout from a.
# ------------ End output ------------
ok 2 - b
ok 3 - # SKIP c
# End - .testsuite/
# Took 0 seconds.
1..3

4
tests/TAP/.testsuite/a Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
echo This is stderr from a. > /dev/stderr
echo This is stdout from a. > /dev/stdout
false

4
tests/TAP/.testsuite/b Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
echo This is stderr from b. > /dev/stderr
echo This is stdout from b. > /dev/stdout
true

1
tests/TAP/.testsuite/c Normal file
View File

@@ -0,0 +1 @@
This should not be run.

View File

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

View File

@@ -1,3 +0,0 @@
#!/bin/sh
test c = $(../urchin -x .print-arg-3 a 'b b b b' c d e)

211
urchin
View File

@@ -1,9 +1,21 @@
#!/bin/sh
# Make sure that CDPATH isn't set, as it causes `cd` to behave unpredictably - notably, it can produce output,
# which breaks fullpath().
# This file is part of urchin. It is subject to the license terms in the
# COPYING file found in the top-level directory of this distribution or at
# https://raw.githubusercontent.com/tlevine/urchin/master/COPYING
# No part of urchin, including this file, may be copied, modified, propagated,
# or distributed except according to the terms contained in the COPYING file.
# Make sure that CDPATH isn't set, as it causes `cd` to behave unpredictably -
# notably, it can produce output, which breaks fullpath().
unset CDPATH
# Urchin version number
VERSION=0.0.6
fullpath() {
(
cd -- "$1"
@@ -13,7 +25,9 @@ fullpath() {
indent() {
level="$1"
printf "%$((2 * ${level}))s"
if test "$level" -gt 0; then
printf "%$((2 * ${level}))s"
fi
}
recurse() {
@@ -30,9 +44,16 @@ recurse() {
if [ -d "$potential_test" ]
then
(
if $tap_format; then
indent $indent_level | sed 's/ /#/g'
echo "# Begin - ${potential_test}"
else
indent $indent_level
echo " ${potential_test}"
echo "+ ${potential_test}"
fi
(
cd -- "$potential_test"
[ -f setup_dir ] && [ -x setup_dir ] && ./setup_dir >> "$stdout_file"
@@ -47,52 +68,108 @@ recurse() {
# $2 instead of $indent_level so it doesn't clash
recurse "${test}" $(( $2 + 1 )) "$shell_for_sh_tests"
exit_code=$?
if $exit_on_fail && test $exit_code -ne 0; then
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file"
return 1
fi
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
done
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file"
echo
)
elif [ -x "$potential_test" ]
then
[ -f setup ] && [ -x setup ] && ./setup >> "$stdout_file"
# Run the test
if [ -n "$shell_for_sh_tests" ] && has_sh_or_no_shebang_line ./"$potential_test"
then
TEST_SHELL="$TEST_SHELL" "$shell_for_sh_tests" ./"$potential_test" > "$stdout_file" 2>&1
if $tap_format; then
indent $indent_level | sed 's/ /#/g'
echo "# End - ${potential_test}"
else
TEST_SHELL="$TEST_SHELL" ./"$potential_test" > "$stdout_file" 2>&1
echo
fi
exit_code="$?"
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
indent $indent_level
if [ $exit_code -eq 0 ]
else
if [ -x "$potential_test" ]
then
# On success, print a green '✓'
printf '\033[32m✓ \033[0m'
printf '%s\n' "${potential_test}"
printf '%s\n' "${potential_test} passed" >> "$logfile"
[ -f setup ] && [ -x setup ] && ./setup >> "$stdout_file"
# Run the test
if [ -n "$shell_for_sh_tests" ] && has_sh_or_no_shebang_line ./"$potential_test"
then
TEST_SHELL="$TEST_SHELL" "$shell_for_sh_tests" ./"$potential_test" > "$stdout_file" 2>&1
else
TEST_SHELL="$TEST_SHELL" ./"$potential_test" > "$stdout_file" 2>&1
fi
exit_code="$?"
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
if [ $exit_code -eq 0 ]; then
result=success
else
result=fail
fi
else
# On fail, print a red '✗'
printf '\033[31m✗ \033[0m'
printf '%s\n' "${potential_test}"
printf '%s\n' "${potential_test} failed" >> "$logfile"
printf '\033[31m' # Print output captured from failed test in red.
cat "$stdout_file"
printf '\033[0m'
result=skip
fi
echo "${result}" >> "$logfile"
if $tap_format; then
n=$(grep -ce '^\(success\|fail\|skip\)' "$logfile")
if [ "$result" == fail ]; then
not='not '
else
not=''
fi
if [ "$result" == skip ]; then
skip='# SKIP '
else
skip=''
fi
echo "${not}ok $n - ${skip}${potential_test}"
if [ "$result" == fail ]; then
echo '# ------------ Begin output ------------'
sed 's/^/# /' "$stdout_file"
echo '# ------------ End output ------------'
fi
else
indent $indent_level
case "$result" in
success)
# On success, print a green '✓'
printf '\033[32m✓ \033[0m'
printf '%s\n' "${potential_test}"
;;
fail)
# On fail, print a red '✗'
printf '\033[31m✗ \033[0m'
printf '%s\n' "${potential_test}"
# Print output captured from failed test in red.
printf '\033[31m'
cat "$stdout_file"
printf '\033[0m'
;;
skip)
printf ' %s\n' "${potential_test}"
;;
esac
fi
if $exit_on_fail && test 0 -ne $exit_code; then
return 1
fi
fi
[ $indent_level -eq 0 ] && rm "$stdout_file"
}
has_sh_or_no_shebang_line() {
head -n 1 "$1" | grep -vqE '^#!' && return 0 # no shebang line at all
head -n 1 "$1" | grep -qE '^#![[:blank:]]*/bin/sh($|[[:blank:]])' && return 0 # shebang line is '#!/bin/sh' or legal variations thereof
# no shebang line at all
head -n 1 "$1" | grep -vqE '^#!' && return 0
# shebang line is '#!/bin/sh' or legal variations thereof
head -n 1 "$1" | grep -qE '^#![[:blank:]]*/bin/sh($|[[:blank:]])' &&
return 0
return 1
}
@@ -105,17 +182,18 @@ $USAGE
-s <shell> Invoke test scripts that either have no shebang line at all or
have shebang line "#!/bin/sh" with the specified shell.
-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
configuration scripts.
-f Force running even if the test directory's name does not
contain the word "test".
-h This help.
-t Format output in Test Anything Protocol (TAP)
-h, --help This help.
-v Display the version number.
Go to https://github.com/tlevine/urchin for documentation on writing tests.
EOF
# [Experimental -x option left undocumented for now.]
# -x [Experimental; not meant for direct invocation, but for use in
# the shebang line of test scripts]
# Run with "\$TEST_SHELL", falling back on /bin/sh.
}
plural () {
@@ -131,7 +209,11 @@ plural () {
}
urchin_go() {
echo Running tests at $(date +%Y-%m-%dT%H:%M:%S) | tee "$logfile"
rm -f "$logfile"
if "$tap_format"; then
printf \#\
fi
echo Running tests at $(date +%Y-%m-%dT%H:%M:%S)
start=$(date +%s)
# Determine the environment variable to define for test scripts
@@ -151,13 +233,26 @@ urchin_go() {
finish=$(date +%s)
elapsed=$(($finish - $start))
echo "Done, took $elapsed $(plural second $elapsed)."
set -- $(grep -e 'passed$' "$logfile"|wc -l) $(grep -e 'failed$' "$logfile"|wc -l)
printf '%s\n' "$1 $(plural test "$1") passed."
[ $2 -gt 0 ] && printf '\033[31m' || printf '\033[32m' # If tests failed, print the message in red, otherwise in green.
printf '%s\n' "$2 $(plural test "$2") failed."
printf '\033[m'
return "$2"
passed=$(grep -c '^success' "$logfile")
failed=$(grep -c '^fail' "$logfile")
skipped=$(grep -c '^skip' "$logfile")
if $tap_format; then
echo "# Took $elapsed $(plural second $elapsed)."
echo 1..$(($passed + $failed + $skipped))
else
echo "Done, took $elapsed $(plural second $elapsed)."
printf '%s\n' "$passed $(plural test "$passed") passed."
printf '%s\n' "$skipped $(plural test "$skipped") skipped."
# If tests failed, print the message in red, otherwise in green.
[ $failed -gt 0 ] && printf '\033[31m' || printf '\033[32m'
printf '%s\n' "$failed $(plural test "$failed") failed."
printf '\033[m'
fi
rm -f "$logfile"
test -z "$failed" || test "$failed" -eq '0'
}
urchin_molly_guard() {
@@ -174,22 +269,26 @@ urchin_molly_guard() {
shell_for_sh_tests=
force=false
exit_on_fail=false
tap_format=false
while [ $# -gt 0 ]
do
case "$1" in
-e) exit_on_fail=true;;
-f) force=true;;
-s)
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 2; }
;;
-x) # [EXPERIMENTAL; UNDOCUMENTED FOR NOW] `urchin -x <test-script>` in a test script's shebang line is equivalent to invoking that script with `"$TEST_SHELL" <test-script>`
shift
urchinsh=${TEST_SHELL:-/bin/sh}
"$urchinsh" "$@"
exit $?;;
which "$shell_for_sh_tests" > /dev/null || {
echo "Cannot find specified shell: '$shell_for_sh_tests'" >&2
urchin_help >&2
exit 2
} ;;
-t) tap_format=true;;
-h|--help) urchin_help
exit 0;;
-v) echo "$VERSION"
exit;;
-*) urchin_help >&2
exit 1;;
*) break;;

BIN
urchin_test_on_msys64_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
urchin_test_on_msys64_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB