|
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2012 Eric Radman <ericshane@eradman.com>
|
|
#
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
# test runner
|
|
|
|
trap 'printf "$0: exit code $? on line $LINENO\nFAIL: $this\n"; exit 1' ERR \
|
|
2> /dev/null || exec bash $0 "$@"
|
|
typeset -i tests=0
|
|
function try { let tests+=1; this="$1"; }
|
|
|
|
function assert {
|
|
[[ "$1" == "$2" ]] && { printf "."; return; }
|
|
printf "\nFAIL: $this\n'$1' != '$2'\n"; exit 1
|
|
}
|
|
function skip { printf "s"; }
|
|
|
|
function zz { sleep 0.25; }
|
|
function setup { rm -f $tmp/*; touch $tmp/file{1,2}; zz; }
|
|
tmp=$(cd $(mktemp -d ${TMPDIR:-/tmp}/entr-system-test-XXXXXX); pwd -P)
|
|
tsession=$(basename $tmp)
|
|
|
|
clear_tty='test -t 0 && stty echo icanon'
|
|
clear_tmux='tmux kill-session -t $tsession 2>/dev/null || true'
|
|
clear_tmp='rm -rf $tmp'
|
|
trap "$clear_tty; $clear_tmux; $clear_tmp" EXIT
|
|
|
|
# required utilities
|
|
|
|
utils="git vim tmux"
|
|
for util in $utils; do
|
|
p=$(which $util 2> /dev/null) || {
|
|
echo "ERROR: could not locate the '$util' utility" >&2
|
|
echo "System tests depend on the following: $utils" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
# fast tests
|
|
|
|
try "no arguments"
|
|
./entr 2> /dev/null || code=$?
|
|
assert $code 1
|
|
|
|
try "no input"
|
|
./entr echo "vroom" 2> /dev/null || code=$?
|
|
assert $code 1
|
|
|
|
try "reload and clear options with no utility to run"
|
|
./entr -r -c 2> /dev/null || code=$?
|
|
assert $code 1
|
|
|
|
try "empty input"
|
|
echo "" | ./entr echo 2> /dev/null || code=$?
|
|
assert $code 1
|
|
|
|
try "no regular files provided as input"
|
|
mkdir $tmp/dir1
|
|
ls $tmp | ./entr echo 2> /dev/null || code=$?
|
|
rmdir $tmp/dir1
|
|
assert $code 1
|
|
|
|
# terminal tests
|
|
|
|
unset TMUX
|
|
|
|
try "spacebar triggers utility"
|
|
setup
|
|
tmux new-session -s $tsession -d
|
|
echo "waiting" > $tmp/file1
|
|
echo "finished" > $tmp/file2
|
|
tmux send-keys -t $tsession:0 \
|
|
"ls $tmp/file2 | ./entr -p cp $tmp/file2 $tmp/file1" C-m ; zz
|
|
assert "$(cat $tmp/file1)" "waiting"
|
|
tmux send-keys -t $tsession:0 "xyz" C-m ; zz
|
|
assert "$(cat $tmp/file1)" "waiting"
|
|
tmux send-keys -t $tsession:0 " " ; zz
|
|
assert "$(cat $tmp/file1)" "finished"
|
|
tmux send-keys -t $tsession:0 "q" ; zz
|
|
tmux kill-session -t $tsession
|
|
|
|
# file system tests
|
|
|
|
try "exec a command using one-shot option"
|
|
setup
|
|
ls $tmp/file* | ./entr -zp cat $tmp/file2 >$tmp/exec.out 2>$tmp/exec.err &
|
|
bgpid=$! ; zz
|
|
echo 456 >> $tmp/file2 ; zz
|
|
wait $bgpid || assert "$?" "0"
|
|
assert "$(cat $tmp/exec.err)" ""
|
|
assert "$(head -n1 $tmp/exec.out)" "$(printf '456\n')"
|
|
|
|
try "fail to exec a command using one-shot option"
|
|
setup
|
|
ls $tmp/file* | ./entr -z /usr/bin/false_X >$tmp/exec.out 2>$tmp/exec.err &
|
|
bgpid=$! ; zz
|
|
wait $bgpid || assert "$?" "1"
|
|
|
|
try "exec a command using one-shot option exit code from child"
|
|
setup
|
|
ls $tmp/file* | ./entr -z sh -c 'exit 4' &
|
|
bgpid=$! ; zz
|
|
wait $bgpid || assert "$?" "4"
|
|
|
|
try "restart a server when a file is modified using one-shot option"
|
|
setup
|
|
if [ $(uname) == 'Linux' ]; then
|
|
skip "GNU nc spins while retrying SELECT(2); busybox does not support domain sockets"
|
|
else
|
|
ls $tmp/file2 | ./entr -rz nc -l -U $tmp/nc.s >> $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo "123" | nc -NU $tmp/nc.s 2> /dev/null || {
|
|
echo "123" | nc -U $tmp/nc.s
|
|
} ; zz
|
|
echo 456 >> $tmp/file2 ; zz
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "123"
|
|
fi
|
|
|
|
try "exec a command in non-intertive mode"
|
|
setup
|
|
ls $tmp/file* | ./entr -n tty >$tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "not a tty"
|
|
|
|
try "exec a command as a background task and ensure stdin is closed"
|
|
setup
|
|
ls $tmp/file* | ./entr -r sh -c 'test -t 0; echo $?; kill $$' >$tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "1"
|
|
|
|
try "exec a command as a background task, and verify that read from stdin doesn't complain"
|
|
setup
|
|
ls $tmp/file* | ./entr -r sh -c 'read X' 2>$tmp/exec.err &
|
|
bgpid=$! ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.err)" ""
|
|
|
|
try "exec single shell utility and exit when a file is added to an implicit watch path"
|
|
setup
|
|
ls $tmp/file* | ./entr -dp sh -c 'echo ping' >$tmp/exec.out 2>$tmp/exec.err \
|
|
|| true &
|
|
bgpid=$! ; zz
|
|
touch $tmp/newfile
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "ping"
|
|
assert "$(cat $tmp/exec.err)" "entr: directory altered"
|
|
|
|
try "exec single shell utility and exit when a subdirectory is added"
|
|
setup
|
|
ls -d $tmp | ./entr -dp sh -c 'echo ping' >$tmp/exec.out 2>$tmp/exec.err \
|
|
|| true &
|
|
bgpid=$! ; zz
|
|
mkdir $tmp/newdir
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "ping"
|
|
assert "$(cat $tmp/exec.err)" "entr: directory altered"
|
|
rmdir $tmp/newdir
|
|
|
|
try "exec single shell utility and exit when a file is added to a specific path"
|
|
setup
|
|
ls -d $tmp | ./entr -dp sh -c 'echo ping' >$tmp/exec.out 2>$tmp/exec.err \
|
|
|| true &
|
|
bgpid=$! ; zz
|
|
touch $tmp/newfile
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "ping"
|
|
assert "$(cat $tmp/exec.err)" "entr: directory altered"
|
|
|
|
try "do nothing when a file not monitored is changed in directory watch mode"
|
|
setup
|
|
ls $tmp/file2 | ./entr -dp echo "changed" >$tmp/exec.out 2>$tmp/exec.err &
|
|
bgpid=$! ; zz
|
|
echo "123" > $tmp/file1
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" ""
|
|
assert "$(cat $tmp/exec.err)" ""
|
|
|
|
try "exec utility when a file is written by Vim in directory watch mode"
|
|
setup
|
|
ls $tmp/file* | ./entr -dp echo "changed" >$tmp/exec.out 2>$tmp/exec.err &
|
|
bgpid=$! ; zz
|
|
vim -e -s -u NONE -N \
|
|
-c ":r!date" \
|
|
-c ":wq" $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "changed"
|
|
assert "$(cat $tmp/exec.err)" ""
|
|
|
|
try "exec utility when a file is opened for write and then closed"
|
|
setup
|
|
echo "---" > $tmp/file1
|
|
ls $tmp/file* | ./entr -p echo "changed" > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
: > $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
if [ $(uname | egrep 'Darwin|FreeBSD|DragonFly') ]; then
|
|
skip "NOTE_TRUNCATE not supported"
|
|
else
|
|
assert "$(cat $tmp/exec.out)" "changed"
|
|
fi
|
|
|
|
try "exec single utility when an entire stash of files is reverted"
|
|
setup
|
|
cp /usr/include/*.h $tmp/
|
|
cd $tmp
|
|
git init -q
|
|
git add *.h
|
|
git commit -m "initial checkin" -q
|
|
for f in `ls *.h | head`; do
|
|
chmod 644 $f
|
|
echo "" >> $f
|
|
done
|
|
cd - > /dev/null ; zz
|
|
ls $tmp/*.h | ./entr -p echo "changed" > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
cd $tmp
|
|
git checkout *.h -q
|
|
cd - > /dev/null ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "changed"
|
|
|
|
try "exec utility when a file is written by Vim"
|
|
setup
|
|
ls $tmp/file* | ./entr -p echo "changed" > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
vim -e -s -u NONE -N \
|
|
-c ":r!date" \
|
|
-c ":wq" $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "changed"
|
|
|
|
try "exec shell utility when a file is written by Vim with 'backup'"
|
|
setup
|
|
ls $tmp/file* | ./entr -p echo "changed" > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
vim -e -s -u NONE -N \
|
|
-c ":set backup" \
|
|
-c ":r!date" \
|
|
-c ":wq" $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "changed"
|
|
|
|
try "exec shell utility when a file is written by Vim with 'nowritebackup'"
|
|
setup
|
|
ls $tmp/file* | ./entr -p echo "changed" > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
vim -e -s -u NONE -N \
|
|
-c ":set nowritebackup" \
|
|
-c ":r!date" \
|
|
-c ":wq" $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "changed"
|
|
|
|
try "restart a server when a file is modified"
|
|
setup
|
|
echo "started." > $tmp/file1
|
|
ls $tmp/file2 | ./entr -r tail -f $tmp/file1 2> /dev/null > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
assert "$(cat $tmp/exec.out)" "started."
|
|
echo 456 >> $tmp/file2 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'started.\nstarted.')"
|
|
|
|
try "ensure that all shell subprocesses are terminated in restart mode"
|
|
setup
|
|
cat <<-SCRIPT > $tmp/go.sh
|
|
#!/bin/sh
|
|
trap 'echo "caught signal"; exit' TERM
|
|
echo "running"; sleep 10
|
|
SCRIPT
|
|
chmod +x $tmp/go.sh
|
|
ls $tmp/file2 | ./entr -r sh -c "$tmp/go.sh" 2> /dev/null > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
kill -INT $bgpid ; zz
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'running\ncaught signal')"
|
|
|
|
try "ensure that all shell subprocesses are terminated when terminal is closed"
|
|
setup
|
|
cat <<-SCRIPT > $tmp/go.sh
|
|
#!/bin/sh
|
|
trap 'echo "caught signal"; exit' TERM
|
|
echo "running"; sleep 10
|
|
SCRIPT
|
|
chmod +x $tmp/go.sh
|
|
ls $tmp/file2 | ./entr -r sh -c "$tmp/go.sh" 2> /dev/null > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
kill -HUP $bgpid ; zz
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'running\ncaught signal')"
|
|
|
|
try "exit with no action when restart and dirwatch flags are combined"
|
|
setup
|
|
echo "started." > $tmp/file1
|
|
ls $tmp/file* | ./entr -rd tail -f $tmp/file1 2> /dev/null > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
assert "$(cat $tmp/exec.out)" "started."
|
|
touch $tmp/newfile
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'started.')"
|
|
|
|
try "exec single shell utility when two files change simultaneously"
|
|
setup
|
|
ln $tmp/file1 $tmp/file3
|
|
ls $tmp/file* | ./entr -p sh -c 'echo ping' > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo 456 >> $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "ping"
|
|
|
|
try "exec single shell utility on startup and when a file is changed"
|
|
setup
|
|
ls $tmp/file* | ./entr sh -c 'printf ping' > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo 456 >> $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "pingping"
|
|
|
|
try "exec a command if a file is made executable"
|
|
setup
|
|
ls $tmp/file* | ./entr -p echo /_ > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
chmod +x $tmp/file2 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "$tmp/file2"
|
|
|
|
try "ensure watches operate on a running executable"
|
|
setup
|
|
cp /bin/sleep $tmp/
|
|
ls $tmp/sleep | ./entr -rs "echo 'vroom'; $tmp/sleep 30" \
|
|
> $tmp/exec.out 2> /dev/null &
|
|
bgpid=$! ; zz
|
|
cp -f /bin/sleep $tmp/ ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
rm -f $tmp/sleep
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'vroom\nvroom\n')"
|
|
|
|
try "exec a command using the first file to change"
|
|
setup
|
|
ls $tmp/file* | ./entr -p cat /_ > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo 456 > $tmp/file1 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "456"
|
|
|
|
try "exec single shell utility using utility substitution"
|
|
setup
|
|
ls $tmp/file1 $tmp/file2 | ./entr -p file /_ > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo 456 >> $tmp/file2; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "$tmp/file2: ASCII text"
|
|
|
|
try "watch and exec a program that is overwritten"
|
|
setup
|
|
touch $tmp/script; chmod 755 $tmp/script
|
|
echo $tmp/script | ./entr -p $tmp/script $tmp/file1 > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
cat > $tmp/script <<-EOF
|
|
#!/bin/sh
|
|
echo vroom
|
|
EOF
|
|
zz ; kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "vroom"
|
|
|
|
try "exec an interactive utility when a file changes"
|
|
setup
|
|
ls $tmp/file* | ./entr -p sh -c 'tty | cut -c1-8' 2> /dev/null > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo 456 >> $tmp/file2 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
if ! test -t 0 ; then
|
|
skip "A TTY is not available"
|
|
else
|
|
assert "$(cat $tmp/exec.out | tr '/pts' '/tty')" "/dev/tty"
|
|
fi
|
|
|
|
try "exec a command using shell option"
|
|
setup
|
|
ls $tmp/file* | ./entr -ps 'file $0; exit 2' >$tmp/exec.out 2>$tmp/exec.err &
|
|
bgpid=$! ; zz
|
|
echo 456 >> $tmp/file2 ; zz
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.err)" ""
|
|
assert "$(head -n1 $tmp/exec.out)" "$(printf ${tmp}'/file2: ASCII text')"
|
|
|
|
try "exec a command as a background task"
|
|
setup
|
|
(ls $tmp/file* | ./entr -ps 'echo terminating; kill $$' >$tmp/exec.out 2>$tmp/exec.err &)
|
|
zz
|
|
echo 456 >> $tmp/file2 ; zz
|
|
assert "$(cat $tmp/exec.err)" ""
|
|
assert "$(head -n1 $tmp/exec.out)" "terminating"
|
|
|
|
# extra slow tests that rely on timeouts
|
|
|
|
try "respond to events that occur while the utility is running"
|
|
setup
|
|
ls $tmp/file* | ./entr -a sh -c 'echo "vroom"; sleep 0.5' > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
echo "123" > $tmp/file1
|
|
sleep 1
|
|
kill -INT $bgpid
|
|
wait $bgpid || assert "$?" "130"
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'vroom\nvroom\n')"
|
|
|
|
try "ensure that all subprocesses are terminated in restart mode when a file is removed"
|
|
setup
|
|
cat <<-SCRIPT > $tmp/go.sh
|
|
#!/bin/sh
|
|
trap 'echo "caught signal"; exit' TERM
|
|
echo "running"; sleep 10
|
|
SCRIPT
|
|
chmod +x $tmp/go.sh
|
|
ls $tmp/file2 | ./entr -r sh -c "$tmp/go.sh" 2> /dev/null > $tmp/exec.out &
|
|
bgpid=$! ; zz
|
|
rm $tmp/file2; sleep 2
|
|
pgrep -P $bgpid > /dev/null || assert "$?" "1"
|
|
assert "$(cat $tmp/exec.out)" "$(printf 'running\ncaught signal')"
|
|
|
|
this="exit 0"
|
|
echo; echo "$tests tests PASSED"
|