Skip to content

Commit

Permalink
Merge pull request #40 from molovo/version-0.5.x
Browse files Browse the repository at this point in the history
v0.5.0
  • Loading branch information
James Dinsdale authored Feb 15, 2017
2 parents fbdceff + cdb9ee1 commit 58180e4
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 14 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ addons:
apt:
packages:
zsh
dist: trusty
before_script:
- mkdir .bin
- curl -L https://raw.githubusercontent.com/molovo/revolver/master/revolver > .bin/revolver
Expand Down
1 change: 1 addition & 0 deletions .zunit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ directories:
tests: tests
output: tests/_output
support: tests/_support
time_limit: 15
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,23 @@ directories:
support: tests/_support
```
To set up ZUnit for a new project, just run `zunit init` in the project's root directory. This will create the `.zunit.yml` config file and relevant directories, including an example test.
### Bootstrap script
ZUnit will look in the support directory (`tests/_support` by default) for a file named `bootstrap`. If found, this is sourced prior to any tests being run. This bootstrap script can be used to install software, set environment variables and source programs required for your tests to run.

### Test time limits

ZUnit can enforce a time limit for tests, and will terminate them with an error if they run for longer than this. Just add the `time_limit` key to your `.zunit.yml`.

```yaml
time_limit: 5 # Will terminate tests after they have run for 5 seconds
```

> **NOTE:** Due to the way child processes are handled in earlier versions of ZSH, the `time_limit` setting is **ignored** for ZSH versions below **5.1.0**. This is necessary because in versions below 5.1.0, the exit state is never returned from the asynchronous process, which would cause tests to hang indefinitely.

### Setting up a new project

To set up ZUnit for a new project, just run `zunit init` in the project's root directory. This will create the `.zunit.yml` config file and relevant directories, including a bootstrap script and example test.

### [Travis CI](https://travis-ci.org) config

Expand Down
87 changes: 74 additions & 13 deletions zunit
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ function _zunit_assert_in() {
[[ $i = $value ]] && found=1
done


[[ $found -eq 1 ]] && return 0

echo "'$value' is not in (${(@f)array})"
Expand Down Expand Up @@ -155,9 +156,11 @@ function _zunit_assert_exists() {
# If filepath is relative, prepend the test directory
if [[ "${pathname:0:1}" != "/" ]]; then
filepath="$testdir/${pathname}"
else
filepath="$pathname"
fi

[[ -e $filepath ]] && return 0
[[ -e "$filepath" ]] && return 0

echo "'$pathname' does not exist"
exit 1
Expand All @@ -172,9 +175,11 @@ function _zunit_assert_is_file() {
# If filepath is relative, prepend the test directory
if [[ "${pathname:0:1}" != "/" ]]; then
filepath="$testdir/${pathname}"
else
filepath="$pathname"
fi

[[ -f $filepath ]] && return 0
[[ -f "$filepath" ]] && return 0

echo "'$pathname' does not exist or is not a file"
exit 1
Expand All @@ -188,10 +193,12 @@ function _zunit_assert_is_dir() {

# If filepath is relative, prepend the test directory
if [[ "${pathname:0:1}" != "/" ]]; then
filepath="$testdir/${pathname}"
filepath="$testdir/$pathname"
else
filepath="$pathname"
fi

[[ -d $filepath ]] && return 0
[[ -d "$filepath" ]] && return 0

echo "'$pathname' does not exist or is not a directory"
exit 1
Expand All @@ -206,9 +213,11 @@ function _zunit_assert_is_link() {
# If filepath is relative, prepend the test directory
if [[ "${pathname:0:1}" != "/" ]]; then
filepath="$testdir/${pathname}"
else
filepath="$pathname"
fi

[[ -h $filepath ]] && return 0
[[ -h "$filepath" ]] && return 0

echo "'$pathname' does not exist or is not a symbolic link"
exit 1
Expand All @@ -223,9 +232,11 @@ function _zunit_assert_is_readable() {
# If filepath is relative, prepend the test directory
if [[ "${pathname:0:1}" != "/" ]]; then
filepath="$testdir/${pathname}"
else
filepath="$pathname"
fi

[[ -r $filepath ]] && return 0
[[ -r "$filepath" ]] && return 0

echo "'$pathname' does not exist or is not readable"
exit 1
Expand Down Expand Up @@ -291,14 +302,15 @@ function run() {
cmd[1]="$testdir/${name}"
fi

# Store lines of output in an array
IFS=$'\n' lines=($("${cmd[@]}" 2>&1))
# Store full output in a variable
output=$("${cmd[@]}" 2>&1)

# Get the process exit state
state="$?"

# Store the full output in a variable
output=${lines[@]}
# Store individual lines of output in an array
IFS=$'\n'
lines=($output)

# Restore $IFS
IFS=$oldIFS
Expand All @@ -313,6 +325,9 @@ function run() {
function assert() {
local value=$1 assertion=$2
local -a comparisons

IFS=$'\n'

comparisons=(${(@)@:3})

if [[ -z $assertion ]]; then
Expand All @@ -334,6 +349,8 @@ function assert() {
if [[ $state -ne 0 ]]; then
exit $state
fi

IFS=$oldIFS
}

###
Expand Down Expand Up @@ -745,14 +762,58 @@ function _zunit_execute_test() {
return 126
fi

# Execute the test body, and capture its output
output="$(__zunit_tmp_test_function 2>&1)"
autoload is-at-least

# Check if a time limit has been specified
if is-at-least 5.1.0 && [[ -n $zunit_config_time_limit ]]; then
# Create a wrapper function around the test
__zunit_async_test_wrapper() {
local pid

# Get the current timestamp, and the time limit, and use those to
# work out the kill time for the sub process
integer time_limit=$(( ${zunit_config_time_limit:-30} * 1000 ))
integer time=$(( EPOCHREALTIME * 1000 ))
integer kill_time=$(( $time + $time_limit ))

# Launch the test function asynchronously and store its PID
__zunit_tmp_test_function &
pid=$!

# While the child process is still running
while kill -0 $pid >/dev/null 2>&1; do
# Check that the kill time has not yet been reached
time=$(( EPOCHREALTIME * 1000 ))
if [[ $time -gt $kill_time ]]; then
# The kill time has been reached, kill the child process,
# and exit the wrapper function
kill -9 $pid >/dev/null 2>&1
exit 78
fi
done

# Use wait to get the exit code from the background process,
# and return that so that the test result can be deduced
wait $pid
return $?
}

# Launch the async wrapper, and capture the output in a variable
output="$(__zunit_async_test_wrapper 2>&1)"
else
# Launch the test, and capture the output in a variable
output="$(__zunit_tmp_test_function 2>&1)"
fi

# Output the result to the user
state=$?
if [[ $state -eq 48 ]]; then
_zunit_skip $output

return
elif [[ $state -eq 78 ]]; then
_zunit_error "Test took too long to run. Terminated after ${zunit_config_time_limit:-30} seconds" $output

return
elif [[ -z $allow_risky && $state -eq 248 ]]; then
_zunit_warn 'No assertions were run, test is risky'
Expand Down Expand Up @@ -1185,7 +1246,7 @@ function _zunit() {
# If the version option is passed,
# output version information and exit
if [[ -n $version ]]; then
echo '0.4.3'
echo '0.5.0'
exit 0
fi

Expand Down

0 comments on commit 58180e4

Please sign in to comment.