71 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
Thomas Levine
80ebf28da9 test skipping idea 2016-02-26 16:39:53 +00:00
Thomas Levine
3f0e925975 move molly-guard change from todo to history 2016-02-26 16:26:22 +00:00
Thomas Levine
43674f12e2 more accepting molly-guard 2016-02-26 16:23:41 +00:00
Thomas Levine
9bb1366549 simpler truth test 2016-02-26 16:19:32 +00:00
Thomas Levine
643a1bf576 test a more accepting molly-guard 2016-02-26 16:18:49 +00:00
Thomas Levine
08ea7bb88f specify version in readme 2016-02-26 16:14:54 +00:00
Thomas Levine
e5ada4899a task runner ideas 2016-02-26 15:57:21 +00:00
Thomas Levine
c818033faa list alternatives 2016-02-14 16:38:02 +00:00
Thomas Levine
88572b6c90 to support single file runs 2016-02-14 07:44:07 +00:00
Thomas Levine
3e5e7334e8 sourcing idea 2016-02-11 20:18:35 +00:00
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
Thomas Levine
56a4e5d8af introduction paragraph 2016-01-25 14:06:33 +00:00
Thomas Levine
d81b7efdc3 asciibetical 2016-01-25 13:56:33 +00:00
Thomas Levine
7f5455739d update readme
* move shall reference to the right place
* further explain cross-shell testing methods
2016-01-25 13:49:36 +00:00
29 changed files with 474 additions and 124 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 Copyright (c) 2012, ScraperWiki Limited
All rights reserved. All rights reserved.

30
HISTORY
View File

@@ -1,5 +1,33 @@
HISTORY HISTORY
------- =======
Version 0.0.7
---------------------
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
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 Version 0.0.5
--------------------- ---------------------

164
TODO Normal file
View File

@@ -0,0 +1,164 @@
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
-------------
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
Source setup and teardown
--------------------
If setup and teardown are sourced instead of executed, maybe we can more
cleanly create and teardown temporary files.
(
. ./setup
./$thetestfile
. ./teardown
)
On the other hand, this could just be sourced explicitly in the test file,
without the special setup and teardown feature.
Run on a file
----------------
Presently you can run urchin only on a directory.
It would be neat if you could run it on a file as well.
This occurred to me when I wanted to run
urchin test/fast/Unit\ tests/nvm_ls_current
on the nvm tests. I wound up running this instead.
urchin test/fast/Unit\ tests/ | grep nvm_ls_current
The Molly guard would be assessed, and the corresponding setup, setup_dir,
teardown, and teardown_dir files would be run in the appropriate order.
Running automated tasks
-------------------------
Urchin might be appropriate for if you have lots of tasks that you want to run
periodically; add an urchin call to your crontab, and call all of your other
tasks with urchin. Here are some features that might make urchin better for
this sort of thing.
* Time how long each test/job takes
* Optionally kill tests/jobs after a specific timeout threshold
* Send output of different tests/jobs to different files for each file
descriptor (STDOUT, STDERR)

View File

@@ -1,2 +1,9 @@
Totally different syntax and similar features, plus TAP output Totally different syntax and similar features, plus TAP output
https://github.com/sstephenson/bats https://github.com/sstephenson/bats
Relatively similar interface
https://github.com/mlafeldt/sharness
Lists of alternatives
https://thomaslevine.com/!/shell-testing/
https://github.com/mlafeldt/sharness#alternatives

View File

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

View File

@@ -4,9 +4,13 @@
/ /_/ / / / /__/ / / / / / / / / /_/ / / / /__/ / / / / / / /
\__,_/_/ \___/_/ /_/_/_/ /_/ \__,_/_/ \___/_/ /_/_/_/ /_/
Urchin is a test framework for shell. It is implemented in Urchin is a file-based test harness, normally used for testing shell programs.
portable /bin/sh and should work on GNU/Linux, Mac OS X, and It is written in portable shell and should thus work on GNU/Linux, BSD
other Unix platforms. (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 ## Try it out
Urchin's tests are written in Urchin, so you can run them to see what Urchin Urchin's tests are written in Urchin, so you can run them to see what Urchin
@@ -27,14 +31,15 @@ run this:
cd urchin cd urchin
./cross-shell-tests ./cross-shell-tests
## Globally ## Install
Download Urchin like so (as root) (or use npm, below): Urchin is contained in a single file, so you can install it by copying it to a
directory in your `PATH`. For example, you can run the following as root.
cd /usr/local/bin cd /usr/local/bin
wget https://raw.github.com/tlevine/urchin/master/urchin wget https://raw.githubusercontent.com/tlevine/urchin/v0.0.6/urchin
chmod +x urchin chmod +x urchin
Can be installed with npm too: Urchin can be installed with npm too.
npm install -g urchin npm install -g urchin
@@ -86,15 +91,14 @@ Files are only run if they are executable, and files beginning with `.` are
ignored. Thus, fixtures and libraries can be included sloppily within the test ignored. Thus, fixtures and libraries can be included sloppily within the test
directory tree. The test passes if the file exits 0; otherwise, it fails. directory tree. The test passes if the file exits 0; otherwise, it fails.
In case you care about the order in which your tests execute, consider that Tests files and subdirectories are run in ASCIIbetical order within each
directory; that is,
urchin looks for files within a directory in the following manner. urchin looks for files within a directory in the following manner.
for file in *; do for file in *; do
do_something_with_test_file $file do_something_with_test_file $file
done done
Tests within a directory are executed in whatever order `*` returns.
### Writing cross-shell compatibility tests for testing shell code ### Writing cross-shell compatibility tests for testing shell code
While you could write your test scripts to explicitly invoke the functionality While you could write your test scripts to explicitly invoke the functionality
@@ -106,24 +110,20 @@ The specific approach depends on your test scenario:
* (b) Your scripts _source_ scripts containing portable shell code. * (b) Your scripts _source_ scripts containing portable shell code.
#### (a) Cross-shell tests with test scripts that _invoke_ shell scripts #### (a) Cross-shell tests with test scripts that _invoke_ shell scripts
Urchin sets the `TEST_SHELL` environment variable so that you may change the
shell with which your tests call other shell programs. To run your test
scripts in multiple shells you must call `$TEST_SHELL` in your tests and then
run urchin with the appropriate option.
First, consider using [shall](https://github.com/mklement0/shall).
#!/usr/bin/env shall
echo This is a test file.
Alternatively, you can use urchin's built-in recognition of the
`TEST_SHELL` environment variable.
In your test scripts, invoke the shell scripts to test via the shell In your test scripts, invoke the shell scripts to test via the shell
specified in environment variable `TEST_SHELL` rather than directly; specified in environment variable `TEST_SHELL` rather than directly;
e.g.: `$TEST_SHELL ../foo bar` (rather than just `../foo bar`). e.g.: `$TEST_SHELL ../foo bar` (rather than just `../foo bar`).
Note that if you alsow want your test scripts to work when run directly,
outside of Urchin, be sure to target scripts that happen to be in the
current directory with prefix `./`; e.g., `$TEST_SHELL ./baz`
(rather than `$TEST_SHELL baz`).
Then, on invocation of Urchin, prepend a definition of environment variable On invocation of Urchin, prepend a definition of environment variable
`TEST_SHELL` specifying the shell to test with, e.g.: `TEST_SHELL=zsh urchin ./tests`. `TEST_SHELL` specifying the shell to test with, e.g.,
TEST_SHELL=zsh urchin ./tests
To test with multiple shells in sequence, use something like: To test with multiple shells in sequence, use something like:
for shell in sh bash ksh zsh; do for shell in sh bash ksh zsh; do
@@ -131,14 +131,20 @@ To test with multiple shells in sequence, use something like:
done done
If `TEST_SHELL` has no value, Urchin defines it as `/bin/sh`, so the test If `TEST_SHELL` has no value, Urchin defines it as `/bin/sh`, so the test
scripts can rely on `$TEST_SHELL` always containing a value. scripts can rely on `$TEST_SHELL` always containing a value when Urchin runs
them.
That said, we still recommand that you account for the possibility that
`$TEST_SHELL` does not contain a value so that you may run your test scripts
without Urchin. Supporting this case is very simple; when you invoke scripts
that happen to be in the current directory, be sure to use the prefix `./`,
e.g., `$TEST_SHELL ./baz` rather than `$TEST_SHELL baz`.
#### (b) Cross-shell tests with test scripts that _source_ shell scripts #### (b) Cross-shell tests with test scripts that _source_ shell scripts
If you _source_ shell code in your test scripts, it is the test scripts If you _source_ shell code in your test scripts, it is the test scripts
themselves that must be run with the shell specified. themselves that must be run with the shell specified.
To that end, Urchin supports the `-s <shell>` option, which instructs Urchin supports the `-s <shell>` option, which instructs
Urchin to invoke the test scripts with the specified shell; e.g., `-s bash`. Urchin to invoke the test scripts with the specified shell; e.g., `-s bash`.
(In addition, Urchin sets environment variable `TEST_SHELL` to the specified (In addition, Urchin sets environment variable `TEST_SHELL` to the specified
shell.) shell.)
@@ -154,27 +160,12 @@ To test with multiple shells in sequence, use something like:
urchin -s $shell ./tests urchin -s $shell ./tests
done done
<!-- Also consider using [shall](https://github.com/mklement0/shall).
#### (c) Cross shell tests with `urchin -x` (experimental) It does something similar, but the interface may be more intuitive.
If you run urchin with the `-x` flag, it will be as if you ran
`$TEST_SHELL`. Unless `$TEST_SHELL` isn't set, in which case it'll
be as if you ran `/bin/sh`. Putting this in she shebang line might
eventually work out to be a cleaner way of doing cross-shell testing.
#!/usr/bin/env urchin -x #!/usr/bin/env shall
test a = a echo This is a test file.
It might make sense if you do this.
export TEST_SHELL=zsh && urchin -x
export TEST_SHELL=bash && urchin -x
-->
## Alternatives to Urchin ## Alternatives to Urchin
Alternatives to Urchin are discussed in Alternatives to Urchin are discussed in
[this blog post](https://blog.scraperwiki.com/2012/12/how-to-test-shell-scripts/). [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 #!/usr/bin/env true
true will processed the contents of this script, but that
# This script will only succeed if it is indeed processed by awk. means that nothing will happen and the script will exit 0
BEGIN { print "ok" }

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). # 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 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 ../../urchin -s bash ./.test-run-by-specified-shell

View File

@@ -0,0 +1,11 @@
tmp=$(mktemp)
../urchin -e -b -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 -b -t .testsuite/ | sed 1d > $tmp
diff $tmp .expected-output

View File

@@ -1,3 +1,5 @@
#!/bin/sh #!/bin/sh
! ../../urchin ./.chainsaw tmp=$(mktemp -d)/blah
echo '#!/usr/bin/env true' > $tmp
! ../../urchin $tmp

View File

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

195
urchin
View File

@@ -1,9 +1,21 @@
#!/bin/sh #!/bin/sh
# Make sure that CDPATH isn't set, as it causes `cd` to behave unpredictably - notably, it can produce output, # This file is part of urchin. It is subject to the license terms in the
# which breaks fullpath(). # 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 unset CDPATH
# Urchin version number
VERSION=0.0.6
fullpath() { fullpath() {
( (
cd -- "$1" cd -- "$1"
@@ -13,7 +25,9 @@ fullpath() {
indent() { indent() {
level="$1" level="$1"
if test "$level" -gt 0; then
printf "%$((2 * ${level}))s" printf "%$((2 * ${level}))s"
fi
} }
recurse() { recurse() {
@@ -30,9 +44,16 @@ recurse() {
if [ -d "$potential_test" ] if [ -d "$potential_test" ]
then then
(
if $tap_format; then
indent $indent_level | sed 's/ /#/g'
echo "# Begin - ${potential_test}"
else
indent $indent_level indent $indent_level
echo " ${potential_test}" echo "+ ${potential_test}"
fi
(
cd -- "$potential_test" cd -- "$potential_test"
[ -f setup_dir ] && [ -x setup_dir ] && ./setup_dir >> "$stdout_file" [ -f setup_dir ] && [ -x setup_dir ] && ./setup_dir >> "$stdout_file"
@@ -46,16 +67,34 @@ 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
(
recurse "${test}" $(( $2 + 1 )) "$shell_for_sh_tests" recurse "${test}" $(( $2 + 1 )) "$shell_for_sh_tests"
exit_code=$?
[ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file" [ -f teardown ] && [ -x teardown ] && ./teardown >> "$stdout_file"
done exit $exit_code
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file" ) &
echo
)
elif [ -x "$potential_test" ]
then
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
wait
[ -f teardown_dir ] && [ -x teardown_dir ] && ./teardown_dir >> "$stdout_file"
)
if $tap_format; then
indent $indent_level | sed 's/ /#/g'
echo "# End - ${potential_test}"
else
echo
fi
else
if [ -x "$potential_test" ]
then
[ -f setup ] && [ -x setup ] && ./setup >> "$stdout_file" [ -f setup ] && [ -x setup ] && ./setup >> "$stdout_file"
# Run the test # Run the test
@@ -65,34 +104,79 @@ 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
result=success
else
result=fail
fi
else
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 indent $indent_level
if [ $exit_code -eq 0 ] case "$result" in
then success)
# On success, print a green '✓' # On success, print a green '✓'
printf '\033[32m✓ \033[0m' printf '\033[32m✓ \033[0m'
printf '%s\n' "${potential_test}" printf '%s\n' "${potential_test}"
printf '%s\n' "${potential_test} passed" >> "$logfile" ;;
else fail)
# On fail, print a red '✗' # On fail, print a red '✗'
printf '\033[31m✗ \033[0m' printf '\033[31m✗ \033[0m'
printf '%s\n' "${potential_test}" printf '%s\n' "${potential_test}"
printf '%s\n' "${potential_test} failed" >> "$logfile"
printf '\033[31m' # Print output captured from failed test in red. # Print output captured from failed test in red.
printf '\033[31m'
cat "$stdout_file" cat "$stdout_file"
printf '\033[0m' 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
fi fi
[ $indent_level -eq 0 ] && rm "$stdout_file" [ $indent_level -eq 0 ] && rm "$stdout_file"
} }
has_sh_or_no_shebang_line() { has_sh_or_no_shebang_line() {
head -n 1 "$1" | grep -vqE '^#!' && return 0 # no shebang line at all # 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 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 return 1
} }
@@ -105,17 +189,20 @@ $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
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 -f Force running even if the test directory's name does not
contain the word "test". 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. Go to https://github.com/tlevine/urchin for documentation on writing tests.
EOF 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 () { plural () {
@@ -131,7 +218,11 @@ plural () {
} }
urchin_go() { 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) start=$(date +%s)
# Determine the environment variable to define for test scripts # Determine the environment variable to define for test scripts
@@ -151,13 +242,26 @@ urchin_go() {
finish=$(date +%s) finish=$(date +%s)
elapsed=$(($finish - $start)) elapsed=$(($finish - $start))
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)." echo "Done, took $elapsed $(plural second $elapsed)."
set -- $(grep -e 'passed$' "$logfile"|wc -l) $(grep -e 'failed$' "$logfile"|wc -l) printf '%s\n' "$passed $(plural test "$passed") passed."
printf '%s\n' "$1 $(plural test "$1") passed." printf '%s\n' "$skipped $(plural test "$skipped") skipped."
[ $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." # 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' printf '\033[m'
return "$2" fi
rm -f "$logfile"
test -z "$failed" || test "$failed" -eq '0'
} }
urchin_molly_guard() { urchin_molly_guard() {
@@ -172,24 +276,30 @@ urchin_molly_guard() {
exit 1 exit 1
} }
single_process=false
shell_for_sh_tests= shell_for_sh_tests=
force=false force=false
exit_on_fail=false
tap_format=false
while [ $# -gt 0 ] while [ $# -gt 0 ]
do do
case "$1" in case "$1" in
-b|--series) single_process=true;;
-e) exit_on_fail=true;;
-f) force=true;; -f) force=true;;
-s) -s)
shift shift
shell_for_sh_tests=$1 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; } which "$shell_for_sh_tests" > /dev/null || {
;; echo "Cannot find specified shell: '$shell_for_sh_tests'" >&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>` urchin_help >&2
shift exit 3
urchinsh=${TEST_SHELL:-/bin/sh} } ;;
"$urchinsh" "$@" -t) tap_format=true;;
exit $?;;
-h|--help) urchin_help -h|--help) urchin_help
exit 0;; exit 0;;
-v) echo "$VERSION"
exit;;
-*) urchin_help >&2 -*) urchin_help >&2
exit 1;; exit 1;;
*) break;; *) break;;
@@ -202,7 +312,12 @@ 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 2 exit 3
fi
if $exit_on_fail && ! $single_process; then
echo 'You must specify -b/--series in order to use -e.' >&2
exit 3
fi fi
# Constants # Constants
@@ -210,7 +325,7 @@ logfile=$(fullpath "$1")/.urchin.log
stdout_file=$(fullpath "$1")/.urchin_stdout stdout_file=$(fullpath "$1")/.urchin_stdout
# Run or present the Molly guard. # Run or present the Molly guard.
if basename "$(fullpath "$1")" | grep -Fi 'test' > /dev/null || $force if fullpath "$1" | grep -Fi 'test' > /dev/null || $force
then then
urchin_go "$1" "$shell_for_sh_tests" urchin_go "$1" "$shell_for_sh_tests"
else else

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