summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAnish Athalye <me@anishathalye.com>2015-05-05 19:58:25 -0400
committerAnish Athalye <me@anishathalye.com>2015-05-05 20:11:46 -0400
commiteeab507d151351b3643b6608cd3a68863234f3e6 (patch)
tree205824cd0640a6121bdf93d3ee31527c62dbf757 /test
parent49181b06660c8fd66873676001500db52b1f8b68 (diff)
Add testing framework and tests
Diffstat (limited to 'test')
-rw-r--r--test/.gitignore1
-rw-r--r--test/README.md24
-rw-r--r--test/Vagrantfile10
-rw-r--r--test/driver-lib.bash117
-rwxr-xr-xtest/test35
-rw-r--r--test/test-lib.bash51
-rw-r--r--test/tests/clean-missing.bash19
-rw-r--r--test/tests/clean-nonexistent.bash8
-rw-r--r--test/tests/clean-outside.bash18
-rw-r--r--test/tests/config-blank.bash8
-rw-r--r--test/tests/config-empty.bash7
-rw-r--r--test/tests/link-force-overwrite-symlink.bash21
-rw-r--r--test/tests/link-leaves-file.bash18
-rw-r--r--test/tests/link-relink-leaves-file.bash20
-rw-r--r--test/tests/link-relink-overwrite-symlink.bash21
-rw-r--r--test/tests/shell-allow-stdout.bash11
-rw-r--r--test/tests/shell-disables-stdout.bash9
17 files changed, 398 insertions, 0 deletions
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..a977916
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1 @@
+.vagrant/
diff --git a/test/README.md b/test/README.md
new file mode 100644
index 0000000..824dbca
--- /dev/null
+++ b/test/README.md
@@ -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
diff --git a/test/Vagrantfile b/test/Vagrantfile
new file mode 100644
index 0000000..4463f99
--- /dev/null
+++ b/test/Vagrantfile
@@ -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
diff --git a/test/driver-lib.bash b/test/driver-lib.bash
new file mode 100644
index 0000000..82766b9
--- /dev/null
+++ b/test/driver-lib.bash
@@ -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
+}
diff --git a/test/test b/test/test
new file mode 100755
index 0000000..2d1e3a8
--- /dev/null
+++ b/test/test
@@ -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}
diff --git a/test/test-lib.bash b/test/test-lib.bash
new file mode 100644
index 0000000..425ae56
--- /dev/null
+++ b/test/test-lib.bash
@@ -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
diff --git a/test/tests/clean-missing.bash b/test/tests/clean-missing.bash
new file mode 100644
index 0000000..dc6c84c
--- /dev/null
+++ b/test/tests/clean-missing.bash
@@ -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
+'
diff --git a/test/tests/clean-nonexistent.bash b/test/tests/clean-nonexistent.bash
new file mode 100644
index 0000000..39d33ef
--- /dev/null
+++ b/test/tests/clean-nonexistent.bash
@@ -0,0 +1,8 @@
+test_description='clean ignores nonexistent directories'
+. '../test-lib.bash'
+
+test_expect_success 'run' '
+run_dotbot <<EOF
+- clean: ["~", "~/fake"]
+EOF
+'
diff --git a/test/tests/clean-outside.bash b/test/tests/clean-outside.bash
new file mode 100644
index 0000000..9d9a055
--- /dev/null
+++ b/test/tests/clean-outside.bash
@@ -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
+'
diff --git a/test/tests/config-blank.bash b/test/tests/config-blank.bash
new file mode 100644
index 0000000..3eb389c
--- /dev/null
+++ b/test/tests/config-blank.bash
@@ -0,0 +1,8 @@
+test_description='blank config allowed'
+. '../test-lib.bash'
+
+test_expect_success 'run' '
+run_dotbot <<EOF
+[]
+EOF
+'
diff --git a/test/tests/config-empty.bash b/test/tests/config-empty.bash
new file mode 100644
index 0000000..146d89e
--- /dev/null
+++ b/test/tests/config-empty.bash
@@ -0,0 +1,7 @@
+test_description='empty config disallowed'
+. '../test-lib.bash'
+
+test_expect_failure 'run' '
+run_dotbot <<EOF
+EOF
+'
diff --git a/test/tests/link-force-overwrite-symlink.bash b/test/tests/link-force-overwrite-symlink.bash
new file mode 100644
index 0000000..e4feedb
--- /dev/null
+++ b/test/tests/link-force-overwrite-symlink.bash
@@ -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
+'
diff --git a/test/tests/link-leaves-file.bash b/test/tests/link-leaves-file.bash
new file mode 100644
index 0000000..e4c6cf9
--- /dev/null
+++ b/test/tests/link-leaves-file.bash
@@ -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
+'
diff --git a/test/tests/link-relink-leaves-file.bash b/test/tests/link-relink-leaves-file.bash
new file mode 100644
index 0000000..74c6d29
--- /dev/null
+++ b/test/tests/link-relink-leaves-file.bash
@@ -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
+'
diff --git a/test/tests/link-relink-overwrite-symlink.bash b/test/tests/link-relink-overwrite-symlink.bash
new file mode 100644
index 0000000..5057e27
--- /dev/null
+++ b/test/tests/link-relink-overwrite-symlink.bash
@@ -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
+'
diff --git a/test/tests/shell-allow-stdout.bash b/test/tests/shell-allow-stdout.bash
new file mode 100644
index 0000000..298e685
--- /dev/null
+++ b/test/tests/shell-allow-stdout.bash
@@ -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
+'
diff --git a/test/tests/shell-disables-stdout.bash b/test/tests/shell-disables-stdout.bash
new file mode 100644
index 0000000..8f4408f
--- /dev/null
+++ b/test/tests/shell-disables-stdout.bash
@@ -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
+'