27 Commits

Author SHA1 Message Date
Thomas Levine
61315d377e remote 2016-04-08 07:32:01 +00:00
Thomas Levine
7daa85cd32 remote host names 2016-04-08 07:08:05 +00:00
Thomas Levine
90b2f93de6 output format to support remotes 2016-04-08 06:56:22 +00:00
Thomas Levine
fa432b5c09 remote testing proposal 2016-04-07 12:38:23 +00:00
Thomas Levine
236b8f86c2 dep proposal 2016-04-07 12:32:19 +00:00
Thomas Levine
74e9d95f77 fixtures idea 2016-04-07 12:20:31 +00:00
Thomas Levine
0395ebb5b2 comment the line in tap 2016-04-07 05:19:47 +00:00
Thomas Levine
cebf0d3add update format tests 2016-04-07 05:18:56 +00:00
Thomas Levine
123f04270b md5 format 2016-04-07 03:55:43 +00:00
Thomas Levine
82c81822d3 mktemp templates 2016-04-07 03:51:01 +00:00
Thomas Levine
9fe6058fbf Automatic commit with j 2016-04-07 03:46:38 +00:00
Thomas Levine
e58ae17704 supporting no rsync 2016-04-07 03:41:33 +00:00
Thomas Levine
367ae3f2b1 tests to write 2016-04-07 03:25:45 +00:00
Thomas Levine
5ffd065317 solaris 2016-04-07 03:23:19 +00:00
Thomas Levine
c0239915a2 error 2016-04-07 03:20:39 +00:00
Thomas Levine
b339c5f98e wrong flag 2016-04-07 03:17:08 +00:00
Thomas Levine
92d40c9ff1 DISABLE_CYCLING 2016-04-07 03:13:42 +00:00
Thomas Levine
24ecd302cd document environment variables 2016-04-07 03:12:10 +00:00
Thomas Levine
135c24fd72 hpux exit codes 2016-04-07 03:03:07 +00:00
Thomas Levine
c219f0a0e6 shorter output 2016-04-07 02:54:30 +00:00
Thomas Levine
adf8dc3562 epoch 2016-04-07 02:52:40 +00:00
Thomas Levine
969340bfd6 bugs 2016-04-07 02:52:38 +00:00
Thomas Levine
a484300263 oops 2016-04-07 02:48:58 +00:00
Thomas Levine
e8d946cc5c switch debug to set -x 2016-04-07 02:47:55 +00:00
Thomas Levine
81c4cdeac0 oops 2016-04-07 02:44:22 +00:00
Thomas Levine
b79045c6ee refactor mktemp 2016-04-07 02:42:29 +00:00
Thomas Levine
c6061b377b alternative ways of getting a seconds counter 2016-04-07 02:39:28 +00:00
7 changed files with 293 additions and 70 deletions

164
TODO
View File

@@ -72,6 +72,114 @@ Setup for other environments includes the following.
* `touch .zshrc`
* Copy urchin and tests
Fixtures
------------
I want to change the way that fixtures are done.
Instead of using setup, teardown, &c., use ordinary programs from within
your tests. For example.
# tests/.fixtures/tmp-dir
tmp=$(mktemp -d)
cd $tmp
@$
code=$?
cd /
rm -Rf $tmp
exit $code
# tests/blah
../.fixtures/tmp-dir 'blah blah blah'
It's best if I can wrap a bunch of commands in braces or paratheses
rather than just one command. Is there a nice way to do that?
Once I have this new way, I guess I might as well keep the old way.
I think the setup, teardown thing can be easier if you only have simple
fixtures. And since I'm going to keep it, I'm going to add another one.
* setup_dir runs once for the present directory.
* setup_children runs once for each child.
* setup_file runs once for each file descendent.
The present `setup` is renamed to `setup_children`, and the new
`setup_file` runs on each file (not directory) that is a child,
grandchild, great-grandchild, and so on.
Dependency checking
----------------------
You might want to skip tests based on dependencies. Currently you can
conditionally skip tests one at a time by exiting with code 3. I want to
be able to skip an entire directory.
So we add a new magic file called `dep`. If it exists, it is run before
everything else in the directory.
* If it exits with code 0, tests continue as if dep did not exist.
* If it exits with code 3, all tests in the directory are marked as
skipped.
* If it exits with code 1, all tests in the directory are marked as
failed. To make the implementation easier, I'll probably treat the
directory as a single test in this case.
A note on magic files
-------------------------
It is nice to have access to things like setup and dep (magic files)
once in a while, but you need to be doing rather substantial testing
before they make your test suite simpler; the documentation should
strongly recommend writing your tests without magic files and then
refactoring and only then considering moving things to magic files.
Remote testing
----------------
In order to test Urchin across multiple operating systems, I have
already added tests in Urchin's test suite that run Urchin tests in
remote servers. I would like to move this to Urchin itself so that
Urchin can test other things on remote servers.
Urchin's output presently looks like this.
Cycling with the following shells: sh bash dash mksh zsh
Running tests at 2016-04-07T12:33:49
Flags/
> --timeout output
. bash (0 seconds)
. dash (0 seconds)
. mksh (0 seconds)
. sh (0 seconds)
. zsh (0 seconds)
Done, took 1 second.
5 tests passed.
0 tests skipped.
0 tests failed.
After the change, the output should look like this.
Cycling with the following shells: sh dash mksh
Running tests at 2016-04-07T12:33:49
Flags/
> --timeout output
. dash on localhost (0 seconds)
. dash on localhost:8080 (0 seconds)
. dash on tlevine@hpux.polarhome.com (0 seconds)
. mksh on localhost (0 seconds)
. mksh on tlevine@hpux.polarhome.com (0 seconds)
. sh on localhost (0 seconds)
. sh on localhost:8080 (0 seconds)
. sh on tlevine@hpux.polarhome.com (0 seconds)
Done, took 1 second.
8 tests passed.
0 tests skipped.
0 tests failed.
This is just how the output should look; the tests run in whatever order
makes sense.
Bugs
-------
@@ -114,3 +222,59 @@ date
tlevine@hpux64$ ./urchin tests/ -n -vv
date: bad format character - s
So I need a portable seconds-from epoch
I also need to handle when no arguments are passed to urchin.
Exit code is wrong for which on HP-UX
## `$(...)`
Solaris doesn't support `$(...)`; you need `\`...\`` instead.
tlevine@solaris$ ./urchin --run-in-series tests/Errors/
./urchin: syntax error at line 84: `tmp=$' unexpected
I use this a lot.
$ grep -c '\$(' urchin
52
Darn
Update tests to support
* md5
* rsync
* mktemp
* epoch
* Report cycling by default
* New format for reporting cycling
Support systems without rsync
BSD mktemp
| NetBSD 6.1.3
| Welcome to NetBSD ...member of polarhome.com realm
|
| Usage: mktemp [-dqu] [-p <tmpdir>] {-t prefix | template ...}
| mkdir: : No such file or directory
| ./urchin: cannot create /log: permission denied
NetBSD
md5: unknown option -- q
usage: cksum [-n] [-a algorithm [-ptx] [-s string]] [-o 1|2]
[file ... | -c [-w] [sumfile]]

View File

@@ -8,6 +8,7 @@ fi
urchin_dir=.urchin-cross-shell-test
rsync --archive -e "ssh ${flags}" $RSYNC_FLAGS \
../urchin ../tests "${hostname}":"${urchin_dir}"
../urchin ../tests "${hostname}":"${urchin_dir}" ||
scp -r ${flags} ../urchin ../tests "${hostname}":"${urchin_dir}"
ssh "${hostname}" ${flags} \
"cd ${urchin_dir} && ./urchin --run-in-series tests"

View File

@@ -1,3 +1,3 @@
$TEST_SHELL ../../urchin -v -s sh -t .testsuite/ |
sed -e 1d -e /second/d > $tmp
sed -e 1,2\ d -e /second/d > $tmp
diff $tmp .tap-output-expectation

View File

@@ -0,0 +1,5 @@
lines=$(
$TEST_SHELL ../../urchin -v -s sh -t .testsuite/ |
tee $tmp | grep -v '^#' | wc -l)
cat $tmp
test $lines -eq 4

View File

@@ -1,3 +1,3 @@
$TEST_SHELL ../../urchin -vv -s sh .testsuite/ |
sed -e 1d -e 's/. seconds\?/1 second/' > $tmp
sed -e 1,2\ d -e 's/. seconds\?/1 second/' > $tmp
diff $tmp .urchin-output-expectation

View File

@@ -1,3 +1,3 @@
$TEST_SHELL ../../urchin --pretty -vv --shell sh .testsuite/ |
sed -e 1d -e 's/. seconds\?/1 second/' > $tmp
sed -e 1,2\ d -e 's/. seconds\?/1 second/' > $tmp
diff $tmp .urchin-output-expectation-color

185
urchin
View File

@@ -68,6 +68,35 @@ if [ -n "${ZSH_VERSION}" ]; then
emulate sh
fi
epoch_date() {
date +%s
}
epoch_pax() {
# Based on http://stackoverflow.com/a/7262588/407226
tmp="$(mktemp_file)"
echo "ibase=8;$({ pax -wx cpio "${tmp}"; echo; } | cut -c 48-59)" | bc
rm "${tmp}"
}
mktemp_dir() {
# Support HP-UX mktemp that has wrong exit codes and
# can't make directories.
tmp=$(mktemp /tmp/urchin.XXXXXXXX)
if test -f "${tmp}"; then
rm "${tmp}"
fi
mkdir "${tmp}"
echo "${tmp}"
}
mktemp_file() {
tmp=$(mktemp /tmp/urchin.XXXXXXXX)
if ! test -f "${tmp}"; then
> "${tmp}"
fi
echo "${tmp}"
}
validate_test_arg() {
# Must be a file or directory
if [ ! -e "${1}" ]; then
@@ -91,15 +120,7 @@ validate_test_arg() {
}
# All temporary files go here
urchin_tmp=$(mktemp)
# Support HP-UX mktemp that has wrong exit codes and
# can't make directories.
if test -f "${urchin_tmp}"; then
rm "${urchin_tmp}"
fi
mkdir "${urchin_tmp}"
urchin_tmp=$(mktemp_dir)
> "${urchin_tmp}/log"
urchin_exit() {
@@ -116,18 +137,35 @@ else
urchin_exit 1
fi
if epoch_date 2>&1 > /dev/null; then
epoch=epoch_date
elif epoch_pax 2>&1 > /dev/null; then
epoch=epoch_pax
else
echo I could not find a seconds counter. >&2
urchin_exit 1
fi
stdout_file() {
the_test="${1}"
the_shell="${2}"
host="${3}"
if test -n "${host}"; then
# This assumes the tests ran on a remote and have been copied.
x="${urchin_tmp}/remote/${host}/stdout${the_test}"
else
# This can be run during the tests.
x="${urchin_tmp}/stdout$(fullpath "$the_test")"
mkdir -p "${x}"
fi
x="${urchin_tmp}/stdout$(fullpath "$the_test")"
mkdir -p "${x}"
case "${urchin_md5}" in
md5sum) y=$(echo "${the_shell}" | md5sum | cut -d\ -f1) ;;
md5) y=$(echo "${the_shell}" | md5 -q) ;;
md5) y=$(echo "${the_shell}" | md5 | sed 's/.* //') ;;
*) echo md5 command is not configured >&2; urchin_exit 1;;
esac
echo "${x}/${y}"
}
@@ -186,10 +224,6 @@ recurse() {
cycle_shell="${3}"
TEST_SHELL="${4}"
if $print_debug; then
echo Entered directory "${PWD}"
fi
for ignore in setup_dir teardown_dir setup teardown; do
if test "$(basename "${potential_test}")" = "${ignore}"; then
return
@@ -220,9 +254,6 @@ recurse() {
fi
if test -f setup_dir; then
if $print_debug; then
echo Sourcing "${PWD}/setup_dir"
fi
. ./setup_dir
fi
@@ -239,9 +270,6 @@ recurse() {
if wait "${!}"; then exit_code=0; else exit_code="${?}"; fi
if "${exit_on_not_ok}" && test "${exit_code}" -ne 0; then
if test -f teardown_dir; then
if $print_debug; then
echo Sourcing "${PWD}/teardown_dir"
fi
. ./teardown_dir
fi
return 1
@@ -250,32 +278,22 @@ recurse() {
done
wait
if test -f teardown_dir; then
if $print_debug; then
echo Sourcing "${PWD}/teardown_dir"
fi
. ./teardown_dir
fi
)
elif [ -f "${potential_test}" ]; then
cd -- "$(dirname -- "${potential_test}")"
if $print_debug; then
echo Running "${potential_test}"
fi
# Determine the environment variable to define for test scripts
# that reflects the specified or implied shell to use for shell-code tests.
while read the_test_shell; do
(
if test -f setup; then
if $print_debug; then
echo Sourcing "${PWD}/setup"
fi
. ./setup
fi
# Run the test
start=$(date +%s)
start=$("${epoch}")
set +e
{
if "${cycle_shell}"; then
@@ -297,12 +315,9 @@ recurse() {
} > "$(stdout_file "${potential_test}" "${the_test_shell}")" 2>&1
exit_code="${?}"
set -e
finish=$(date +%s)
finish=$("${epoch}")
if test -f teardown; then
if $print_debug; then
echo Sourcing "${PWD}/teardown"
fi
. ./teardown
fi
@@ -324,9 +339,6 @@ recurse() {
if wait "${!}"; then exit_code=0; else exit_code="${?}"; fi
if "${exit_on_not_ok}" && test "${exit_code}" -ne 0; then
if test -f teardown_dir; then
if $print_debug; then
echo Sourcing "${PWD}/teardown_dir"
fi
. ./teardown_dir
fi
return 1
@@ -350,23 +362,30 @@ report_outcome() {
start="${4}"
finish="${5}"
host="${6}"
if test -n "${host}"; then
onhost=" on ${host}"
fi
escaped_root="$(fullpath "${root}" | sed 's/\//\\\//g')"
elapsed=$(($finish - $start))
if "${tap_format}"; then
printf \#\
fi
if "${print_margins}" || "${tap_format}"; then
echo Running tests at $(date +%Y-%m-%dT%H:%M:%S)
if $tap_format; then printf \#\ ; fi
echo Ran tests at $(date +%Y-%m-%dT%H:%M:%S) with the following shells:
if $tap_format; then printf \#\ ; fi
cat "${shell_list}" | tr '\n' \
echo
fi
for number in n oks skips not_oks; do
eval "${number}=0"
done
# Use a temporary file rather than a pipe because a pipe starts a sub-shell
# and thus makes the above variables local.
sorted_log_file=$(mktemp)
sorted_log_file=$(mktemp_file)
cat "${log_file}" | LC_COLLATE=C sort > "${sorted_log_file}"
while read line; do
@@ -399,13 +418,13 @@ report_outcome() {
fi
if test -z "${the_shell}"; then
the_shell='File is not executable.'
the_shell='File is not executable'
fi
echo "${not}ok $n - ${path} (${the_shell}) ${skip}"
echo "${not}ok $n - ${path} (${the_shell}${onhost}) ${skip}"
if { test "${result}" = not_ok && "${print_not_ok_stdout}"; } ||
{ test "${result}" = ok && "${print_ok_stdout}"; }; then
echo '# ------------ Begin output ------------'
sed 's/^/# /' "$(stdout_file "${abspath}" "${the_shell}")"
sed 's/^/# /' "$(stdout_file "${abspath}" "${the_shell}" "${host}")"
echo '# ------------ End output ------------'
fi
echo "# Previous test took ${file_elapsed} seconds."
@@ -428,7 +447,7 @@ report_outcome() {
else
printf "${success_mark} "
fi
echo "${the_shell} ("${file_elapsed}" $(plural second $file_elapsed))"
echo "${the_shell}${onhost} (${file_elapsed} $(plural second $file_elapsed))"
fi
;;
not_ok)
@@ -439,23 +458,23 @@ report_outcome() {
else
printf "${fail_mark} "
fi
echo "${the_shell} ("${file_elapsed}" $(plural second $file_elapsed))"
echo "${the_shell}${onhost} (${file_elapsed} $(plural second $file_elapsed))"
fi
;;
skip)
if "${print_ok}"; then
header
if test -z "${the_shell}"; then
echo ' (File is not executable.)'
echo " (File is not executable${onhost}.)"
else
echo " ${the_shell} ("${file_elapsed}" $(plural second $file_elapsed))"
echo " ${the_shell}${onhost} (${file_elapsed} $(plural second $file_elapsed))"
fi
fi
;;
esac
if { test "${result}" = not_ok && "${print_not_ok_stdout}"; } ||
{ test "${result}" = ok && "${print_ok_stdout}"; }; then
sed 's/^/ | /' "$(stdout_file "${abspath}" "${the_shell}")"
sed 's/^/ | /' "$(stdout_file "${abspath}" "${the_shell}" "${host}")"
fi
fi
@@ -545,13 +564,20 @@ And these options affect how much is printed.
-v Print stdout from failing tests.
-vv Print names of passed tests.
-vvv, --verbose Print stdout from all tests.
-vvvv, --debug Print debugging messages.
-vvvv, --debug Run with set -x.
The remaining flags provide information about urchin.
-h, --help Display this help.
--version Display the version number.
Urchin recognizes certain environment variables.
TEST_SHELL This is sometimes over-ridden; see -s.
RUN_IN_SERIES Set this to true to have the same effect as
-b/--run-in-series. This is helpful if you are
calling urchin inside an urchin test suite.
Go to https://thomaslevine.com/!/urchin/ for documentation on writing tests.
EOF
@@ -578,9 +604,13 @@ validate_strings() {
}
main() {
argv="$@"
cycle_shell=true
shell_list="${urchin_tmp}"/shell_list
test_arg_list="${urchin_tmp}"/test_list
> "${test_arg_list}"
remotes_list="${urchin_tmp}"/remotes
run_in_series=false
force=false
exit_on_not_ok=false
@@ -592,7 +622,6 @@ main() {
print_not_ok=true
print_ok_stdout=false
print_not_ok_stdout=false
print_debug=false
while [ "${#}" -gt 0 ]
do
case "${1}" in
@@ -642,7 +671,7 @@ main() {
-vvvv|--debug) print_not_ok_stdout=true
print_ok=true;
print_ok_stdout=true
print_debug=true;;
set -x;;
-h|--help) urchin_help
urchin_exit 0;;
@@ -656,6 +685,9 @@ main() {
esac
shift
done
if "${RUN_IN_SERIES}" 2> /dev/null; then
run_in_series=true
fi
if $print_in_color; then
success_mark=✓
@@ -684,11 +716,6 @@ main() {
fi
fi
if $print_debug; then
echo Cycling with the following shells:
cat "${shell_list}"
fi
if test -n "${urchin_timeout}"; then
# Choose the timeout command
if timeout -t 0 true 2> /dev/null; then
@@ -707,7 +734,7 @@ main() {
fi
# -------------------- REALLY RUN -------------------- #
start=$(date +%s)
start=$("${epoch}")
# 1 test file or folder to run
# 2 urchin root
@@ -717,16 +744,42 @@ main() {
recurse "$(fullpath "${seed}")" "${root}" "${cycle_shell}" \
"${TEST_SHELL}" || break
done < "${test_arg_list}"
finish=$(date +%s)
finish=$("${epoch}")
if test $(cat "${urchin_tmp}"/log | wc -l) -eq 0; then
echo 'No tests found' >&2
urchin_exit 1
fi
report_outcome "${root}" "${tap_format}" "${urchin_tmp}"/log "${start}" \
"${finish}"
urchin_exit "${?}"
if test -n "${RUNNING_ON_REMOTE}"; then
echo "${urchin_tmp}"
elif test -f "${remotes_list}"; then
while read remote; do
remote_main $remote "${argv}"
done < "${remotes_list}"
else
report_outcome "${root}" "${tap_format}" "${urchin_tmp}"/log \
"${start}" "${finish}"
urchin_exit "${?}"
fi
}
remote_main() {
hostname="${1}"; shift
flags="${1}"; shift
urchin_dir=.urchin-cross-shell-test
rsync --archive -e "ssh ${flags}"
../urchin ../tests "${hostname}":"${urchin_dir}" ||
scp -r ${flags} ../urchin ../tests "${hostname}":"${urchin_dir}"
remote_tmp="$(ssh "${hostname}" ${flags} "cd ${urchin_dir} && ./urchin $@")"
remotedir="${hostname}":"${remote_tmp}"
localdir="${urchin_tmp}/remote/${host}"
rsync --archive -e "ssh ${flags}" "${remotedir}" "${localdir}" ||
scp -r ${flags} "${remotedir}"
}
test -n "${TESTING_URCHIN_INTERNALS}" || main "$@"