Add testing framework and tests
This commit is contained in:
parent
49181b0666
commit
eeab507d15
17 changed files with 398 additions and 0 deletions
1
test/.gitignore
vendored
Normal file
1
test/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.vagrant/
|
24
test/README.md
Normal file
24
test/README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
Testing
|
||||||
|
=======
|
||||||
|
|
||||||
|
Dotbot testing code uses [Vagrant][vagrant] to run all tests inside a virtual
|
||||||
|
machine to have tests be completely isolated from the host machine. The test
|
||||||
|
driver relies on the [Sahara][sahara] plugin to snapshot and roll back virtual
|
||||||
|
machine state. The tests are deterministic, and each test is run in a virtual
|
||||||
|
machine with fresh state, ensuring that tests that modify system state are
|
||||||
|
easily repeatable.
|
||||||
|
|
||||||
|
Running the Tests
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Before running the tests, the virtual machine must be running. It can be
|
||||||
|
started by running `vagrant up`.
|
||||||
|
|
||||||
|
The test suite can be run by running `./test`. Selected tests can be run by
|
||||||
|
passing paths to the tests as arguments to `./test`.
|
||||||
|
|
||||||
|
When finished with testing, it is good to shut down the virtual machine by
|
||||||
|
running `vagrant halt`.
|
||||||
|
|
||||||
|
[vagrant]: https://www.vagrantup.com/
|
||||||
|
[sahara]: https://github.com/jedi4ever/sahara
|
10
test/Vagrantfile
vendored
Normal file
10
test/Vagrantfile
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Vagrant.configure(2) do |config|
|
||||||
|
config.vm.box = 'ubuntu/trusty64'
|
||||||
|
|
||||||
|
# sync by copying for isolation
|
||||||
|
config.vm.synced_folder "..", "/dotbot", type: "rsync",
|
||||||
|
rsync__exclude: ".git/"
|
||||||
|
|
||||||
|
# disable default synced folder
|
||||||
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
|
end
|
117
test/driver-lib.bash
Normal file
117
test/driver-lib.bash
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
MAXRETRY=5
|
||||||
|
TIMEOUT=1
|
||||||
|
|
||||||
|
red() {
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
printf "\033[31m%s\033[0m\n" "$*"
|
||||||
|
else
|
||||||
|
printf "%s\n" "$*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
green() {
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
printf "\033[32m%s\033[0m\n" "$*"
|
||||||
|
else
|
||||||
|
printf "%s\n" "$*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
yellow() {
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
printf "\033[33m%s\033[0m\n" "$*"
|
||||||
|
else
|
||||||
|
printf "%s\n" "$*"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
check_prereqs() {
|
||||||
|
if ! (vagrant ssh -c 'exit') >/dev/null 2>&1; then
|
||||||
|
>&2 echo "vagrant vm must be running."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if ! (vagrant plugin list | grep '^sahara\s\+') >/dev/null 2>&1; then
|
||||||
|
>&2 echo "vagrant plugin 'sahara' is not installed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
until_success() {
|
||||||
|
local timeout=${TIMEOUT}
|
||||||
|
local attempt=0
|
||||||
|
local success=0
|
||||||
|
while [ $attempt -lt $MAXRETRY ]; do
|
||||||
|
if ($@) >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep $timeout
|
||||||
|
timeout=$((timeout * 2))
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_vagrant() {
|
||||||
|
until_success vagrant ssh -c 'exit'
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback() {
|
||||||
|
vagrant sandbox rollback >/dev/null 2>&1 &&
|
||||||
|
wait_for_vagrant &&
|
||||||
|
vagrant rsync >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
echo "initializing."
|
||||||
|
vagrant sandbox on >/dev/null 2>&1
|
||||||
|
tests_run=0
|
||||||
|
tests_passed=0
|
||||||
|
tests_failed=0
|
||||||
|
tests_total="${1}"
|
||||||
|
local plural="" && [ ${tests_total} -gt 1 ] && plural="s"
|
||||||
|
printf -- "running %d test%s...\n\n" ${tests_total} $plural
|
||||||
|
}
|
||||||
|
|
||||||
|
pass() {
|
||||||
|
tests_passed=$((tests_passed + 1))
|
||||||
|
green "-> ok."
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
tests_failed=$((tests_failed + 1))
|
||||||
|
yellow "-> fail!"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test() {
|
||||||
|
tests_run=$((tests_run + 1))
|
||||||
|
printf '[%d/%d]\n' ${tests_run} ${tests_total}
|
||||||
|
rollback || die "unable to rollback vm." # start with a clean slate
|
||||||
|
vagrant ssh -c "cd /dotbot/test/tests && bash ${1}" 2>/dev/null && pass || fail
|
||||||
|
}
|
||||||
|
|
||||||
|
report() {
|
||||||
|
printf -- "test report\n"
|
||||||
|
printf -- "-----------\n"
|
||||||
|
printf -- "- %3d run\n" ${tests_run}
|
||||||
|
printf -- "- %3d passed\n" ${tests_passed}
|
||||||
|
if [ ${tests_failed} -gt 0 ]; then
|
||||||
|
printf -- "- %3d failed\n" ${tests_failed}
|
||||||
|
echo
|
||||||
|
red "==> not ok!"
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo
|
||||||
|
green "==> all ok."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
>&2 echo $@
|
||||||
|
>&2 echo "terminating..."
|
||||||
|
exit 1
|
||||||
|
}
|
35
test/test
Executable file
35
test/test
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "${BASEDIR}"
|
||||||
|
. "./driver-lib.bash"
|
||||||
|
|
||||||
|
start="$(date +%s)"
|
||||||
|
|
||||||
|
check_prereqs || die "prerequisites unsatsfied."
|
||||||
|
|
||||||
|
declare -a tests=()
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
while read file; do
|
||||||
|
tests+=("${file}")
|
||||||
|
done < <(find tests -type f -name '*.bash')
|
||||||
|
else
|
||||||
|
tests=("$@")
|
||||||
|
fi
|
||||||
|
|
||||||
|
initialize "${#tests[@]}"
|
||||||
|
|
||||||
|
for file in "${tests[@]}"; do
|
||||||
|
run_test "$(basename ${file})"
|
||||||
|
done
|
||||||
|
|
||||||
|
if report; then
|
||||||
|
ret=0
|
||||||
|
else
|
||||||
|
ret=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "(tests run in $(($(date +%s) - start)) seconds)"
|
||||||
|
exit ${ret}
|
51
test/test-lib.bash
Normal file
51
test/test-lib.bash
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
DEBUG=false
|
||||||
|
DOTFILES='/home/vagrant/dotfiles'
|
||||||
|
INSTALL_CONF='install.conf.yaml'
|
||||||
|
|
||||||
|
test_run_() {
|
||||||
|
if ! ${DEBUG}; then
|
||||||
|
(eval "$*") >/dev/null 2>&1
|
||||||
|
else
|
||||||
|
(eval "$*")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_success() {
|
||||||
|
local tag=${1} && shift
|
||||||
|
if ! test_run_ "$@"; then
|
||||||
|
>&2 echo "- ${tag} failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_expect_failure() {
|
||||||
|
local tag=${1} && shift
|
||||||
|
if test_run_ "$@"; then
|
||||||
|
>&2 echo "- ${tag} failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_vm() {
|
||||||
|
if [ "$(whoami)" != "vagrant" ]; then
|
||||||
|
>&2 echo "test can't run outside vm!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
check_vm
|
||||||
|
echo "${test_description}"
|
||||||
|
mkdir -p "${DOTFILES}"
|
||||||
|
cd
|
||||||
|
}
|
||||||
|
|
||||||
|
run_dotbot() {
|
||||||
|
(
|
||||||
|
cd "${DOTFILES}"
|
||||||
|
cat > "${INSTALL_CONF}"
|
||||||
|
/dotbot/bin/dotbot -d . -c "${INSTALL_CONF}" "${@}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize
|
19
test/tests/clean-missing.bash
Normal file
19
test/tests/clean-missing.bash
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
test_description='clean deletes links to missing files'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
touch ${DOTFILES}/f &&
|
||||||
|
ln -s ${DOTFILES}/f ~/.f &&
|
||||||
|
ln -s ${DOTFILES}/g ~/.g
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- clean: ["~"]
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test' '
|
||||||
|
test -f ~/.f &&
|
||||||
|
! test -h ~/.g
|
||||||
|
'
|
8
test/tests/clean-nonexistent.bash
Normal file
8
test/tests/clean-nonexistent.bash
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
test_description='clean ignores nonexistent directories'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- clean: ["~", "~/fake"]
|
||||||
|
EOF
|
||||||
|
'
|
18
test/tests/clean-outside.bash
Normal file
18
test/tests/clean-outside.bash
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
test_description='clean ignores files linking outside dotfiles directory'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
ln -s ${DOTFILES}/f ~/.f &&
|
||||||
|
ln -s ~/g ~/.g
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- clean: ["~"]
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test' '
|
||||||
|
! test -h ~/.f &&
|
||||||
|
test -h ~/.g
|
||||||
|
'
|
8
test/tests/config-blank.bash
Normal file
8
test/tests/config-blank.bash
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
test_description='blank config allowed'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
[]
|
||||||
|
EOF
|
||||||
|
'
|
7
test/tests/config-empty.bash
Normal file
7
test/tests/config-empty.bash
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
test_description='empty config disallowed'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_failure 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
EOF
|
||||||
|
'
|
21
test/tests/link-force-overwrite-symlink.bash
Normal file
21
test/tests/link-force-overwrite-symlink.bash
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
test_description='force overwrites symlinked directory'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
mkdir ${DOTFILES}/dir ~/dir &&
|
||||||
|
touch ${DOTFILES}/dir/f &&
|
||||||
|
ln -s ~/ ~/.dir
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- link:
|
||||||
|
~/.dir:
|
||||||
|
path: dir
|
||||||
|
force: true
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test' '
|
||||||
|
test -f ~/.dir/f
|
||||||
|
'
|
18
test/tests/link-leaves-file.bash
Normal file
18
test/tests/link-leaves-file.bash
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
test_description='relink does not overwrite file'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
echo "apple" > ${DOTFILES}/f &&
|
||||||
|
echo "grape" > ~/.f
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- link:
|
||||||
|
~/.f: f
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test' '
|
||||||
|
grep "grape" ~/.f
|
||||||
|
'
|
20
test/tests/link-relink-leaves-file.bash
Normal file
20
test/tests/link-relink-leaves-file.bash
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
test_description='relink does not overwrite file'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
echo "apple" > ${DOTFILES}/f &&
|
||||||
|
echo "grape" > ~/.f
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_failure 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- link:
|
||||||
|
~/.f:
|
||||||
|
path: f
|
||||||
|
relink: true
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test' '
|
||||||
|
grep "grape" ~/.f
|
||||||
|
'
|
21
test/tests/link-relink-overwrite-symlink.bash
Normal file
21
test/tests/link-relink-overwrite-symlink.bash
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
test_description='relink overwrites symlink'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
echo "apple" > ${DOTFILES}/f &&
|
||||||
|
echo "grape" > ~/f &&
|
||||||
|
ln -s ~/f ~/.f
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
run_dotbot <<EOF
|
||||||
|
- link:
|
||||||
|
~/.f:
|
||||||
|
path: f
|
||||||
|
relink: true
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'test' '
|
||||||
|
grep "apple" ~/.f
|
||||||
|
'
|
11
test/tests/shell-allow-stdout.bash
Normal file
11
test/tests/shell-allow-stdout.bash
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
test_description='shell command stdout works'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
(run_dotbot | grep "^apple") <<EOF
|
||||||
|
- shell:
|
||||||
|
-
|
||||||
|
command: echo apple
|
||||||
|
stdout: true
|
||||||
|
EOF
|
||||||
|
'
|
9
test/tests/shell-disables-stdout.bash
Normal file
9
test/tests/shell-disables-stdout.bash
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
test_description='shell command stdout disabled by default'
|
||||||
|
. '../test-lib.bash'
|
||||||
|
|
||||||
|
test_expect_success 'run' '
|
||||||
|
(run_dotbot | (! grep "^banana")) <<EOF
|
||||||
|
- shell:
|
||||||
|
- echo banana
|
||||||
|
EOF
|
||||||
|
'
|
Loading…
Reference in a new issue