Merge 2faa26a3b7
into 30dc7d5788
This commit is contained in:
commit
cecede341b
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
|
.coverage
|
||||||
|
.tox
|
||||||
|
|
14
.travis.yml
Normal file
14
.travis.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
language: python
|
||||||
|
python: "2.7"
|
||||||
|
env:
|
||||||
|
- TOXENV=py26
|
||||||
|
- TOXENV=py27
|
||||||
|
- TOXENV=py32
|
||||||
|
- TOXENV=py33
|
||||||
|
- TOXENV=py34
|
||||||
|
install:
|
||||||
|
- pip install tox python-coveralls
|
||||||
|
script:
|
||||||
|
- tox
|
||||||
|
after_success:
|
||||||
|
- coveralls
|
|
@ -1,6 +1,10 @@
|
||||||
Dotbot
|
Dotbot
|
||||||
======
|
======
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/PacketPerception/dotbot.svg?branch=feature%2Fnew_tests)](https://travis-ci.org/PacketPerception/dotbot) [![Coverage Status](https://coveralls.io/repos/PacketPerception/dotbot/badge.svg?branch=feature%2Fnew_tests)](https://coveralls.io/r/PacketPerception/dotbot?branch=feature%2Fnew_tests)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dotbot is a tool that bootstraps your dotfiles (it's a [Dot]files
|
Dotbot is a tool that bootstraps your dotfiles (it's a [Dot]files
|
||||||
[bo]o[t]strapper, get it?). It does *less* than you think, because version
|
[bo]o[t]strapper, get it?). It does *less* than you think, because version
|
||||||
control systems do more than you think.
|
control systems do more than you think.
|
||||||
|
|
|
@ -1,24 +1,41 @@
|
||||||
Testing
|
Testing
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Dotbot testing code uses [Vagrant][vagrant] to run all tests inside a virtual
|
Testing is run against multiple Python using ``tox``. It is recommended to use ``pyenv`` to manage
|
||||||
machine to have tests be completely isolated from the host machine. The test
|
your Python version.
|
||||||
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
|
Setup
|
||||||
started by running `vagrant up`.
|
=====
|
||||||
|
|
||||||
The test suite can be run by running `./test`. Selected tests can be run by
|
* Ensure git submodules are up to date
|
||||||
passing paths to the tests as arguments to `./test`.
|
* Install pyenv
|
||||||
|
* Install Python versions
|
||||||
|
|
||||||
When finished with testing, it is good to shut down the virtual machine by
|
```
|
||||||
running `vagrant halt`.
|
pyenv install 3.4.3
|
||||||
|
pyenv install 3.3.6
|
||||||
|
pyenv install 3.2.6
|
||||||
|
pyenv install 2.7.10
|
||||||
|
pyenv install 2.6.9
|
||||||
|
```
|
||||||
|
|
||||||
|
* *cd* into the *dotbot* repository and set the local Python versions for pyenv
|
||||||
|
|
||||||
|
```
|
||||||
|
pyenv local 3.4.3 3.3.6 3.2.6 2.7.10 2.6.9
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install test requirements
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install tox
|
||||||
|
pyenv rehash
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Running the Test Suite
|
||||||
|
======================
|
||||||
|
|
||||||
|
Once the environment has been setup, simply run the ``tox`` command in the ``dotbot`` directory
|
||||||
|
|
||||||
[vagrant]: https://www.vagrantup.com/
|
|
||||||
[sahara]: https://github.com/jedi4ever/sahara
|
|
||||||
|
|
10
test/Vagrantfile
vendored
10
test/Vagrantfile
vendored
|
@ -1,10 +0,0 @@
|
||||||
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
|
|
|
@ -1,120 +0,0 @@
|
||||||
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
|
|
||||||
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] (%s)\n' "${tests_run}" "${tests_total}" "${1}"
|
|
||||||
rollback || die "unable to rollback vm." # start with a clean slate
|
|
||||||
if vagrant ssh -c "cd /dotbot/test/tests && bash ${1}" 2>/dev/null; then
|
|
||||||
pass
|
|
||||||
else
|
|
||||||
fail
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
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
35
test/test
|
@ -1,35 +0,0 @@
|
||||||
#!/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}
|
|
|
@ -1,51 +0,0 @@
|
||||||
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
|
|
31
test/test_clean.py
Normal file
31
test/test_clean.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import os
|
||||||
|
from utils import DotbotTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class MissingTestCase(DotbotTestCase):
|
||||||
|
def test_clean(self):
|
||||||
|
""" clean deletes links to missing files """
|
||||||
|
self.add_file('f')
|
||||||
|
self.add_symlink('f')
|
||||||
|
self.add_symlink('g')
|
||||||
|
|
||||||
|
self.run_dotbot(config='- clean: ["~"]')
|
||||||
|
|
||||||
|
self.assertIsLinked('f')
|
||||||
|
self.assertDoesNotExist('g')
|
||||||
|
|
||||||
|
def test_ignores_nonexistant(self):
|
||||||
|
""" clean ignores nonexistant directories """
|
||||||
|
self.run_dotbot(config='- clean: ["~", "~/fake"]')
|
||||||
|
|
||||||
|
def test_ignores_outside_linking(self):
|
||||||
|
""" clean ignores files linking outside dotfiles directory """
|
||||||
|
self.add_symlink('f')
|
||||||
|
self.add_home_file('g')
|
||||||
|
os.symlink(os.path.join(self.home_dir, 'g'), os.path.join(self.home_dir, '.g'))
|
||||||
|
|
||||||
|
self.run_dotbot(config='- clean: ["~"]')
|
||||||
|
|
||||||
|
self.assertDoesNotExist('f')
|
||||||
|
self.assertEqual(os.stat(os.path.join(self.home_dir, 'g')),
|
||||||
|
os.stat(os.path.join(self.home_dir, '.g')))
|
12
test/test_config.py
Normal file
12
test/test_config.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import os
|
||||||
|
from utils import DotbotTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigTestCase(DotbotTestCase):
|
||||||
|
def test_blank_config_allowed(self):
|
||||||
|
""" blank config allowed """
|
||||||
|
self.run_dotbot(config='[]')
|
||||||
|
|
||||||
|
def test_empty_config_not_allowed(self):
|
||||||
|
""" empty config disallowed """
|
||||||
|
self.assertRaises(SystemExit, self.run_dotbot, skip_config=True)
|
92
test/test_link.py
Normal file
92
test/test_link.py
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import os
|
||||||
|
from utils import DotbotTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class LinkTestCase(DotbotTestCase):
|
||||||
|
def test_var_expansion_source(self):
|
||||||
|
""" link expands environment variables in source """
|
||||||
|
self.add_file('h')
|
||||||
|
os.environ['DB_TEST_ENV'] = 'h'
|
||||||
|
|
||||||
|
self.run_dotbot([{'link':
|
||||||
|
{'~/.i': '$DB_TEST_ENV'}
|
||||||
|
}])
|
||||||
|
|
||||||
|
self.assertIsLinked('.i', dotbot_path='h')
|
||||||
|
|
||||||
|
|
||||||
|
def test_var_expansion_target(self):
|
||||||
|
""" link expands environment variables in target """
|
||||||
|
self.add_file('f')
|
||||||
|
self.add_file('h')
|
||||||
|
os.environ['DB_TEST_DIR'] = '.config'
|
||||||
|
os.environ['DB_TEST_FILE'] = 'g'
|
||||||
|
if 'DB_UNSET_VAR' in os.environ:
|
||||||
|
del os.environ['DB_UNSET_VAR']
|
||||||
|
|
||||||
|
self.run_dotbot([{'link': {
|
||||||
|
'~/${DB_TEST_DIR}/$DB_TEST_FILE': {'path': 'f',
|
||||||
|
'create': True},
|
||||||
|
'~/$DB_UNSET_VAR': 'h'
|
||||||
|
}}])
|
||||||
|
|
||||||
|
self.assertIsLinked('.config/g', 'f')
|
||||||
|
self.assertIsLinked('$DB_UNSET_VAR', 'h')
|
||||||
|
|
||||||
|
|
||||||
|
def test_leaves_unset_vars(self):
|
||||||
|
""" link leaves unset environment variables """
|
||||||
|
if 'DB_UNSET_VAR' in os.environ:
|
||||||
|
del os.environ['DB_UNSET_VAR']
|
||||||
|
self.add_file('$DB_UNSET_VAR')
|
||||||
|
|
||||||
|
self.run_dotbot([{'link': {'~/.f': '$DB_UNSET_VAR'}}])
|
||||||
|
|
||||||
|
self.assertIsLinked('.f', '$DB_UNSET_VAR')
|
||||||
|
|
||||||
|
|
||||||
|
def test_force_overwrite_symlinked_directory(self):
|
||||||
|
""" force overwrites symlinked directory """
|
||||||
|
self.add_dirs('dir')
|
||||||
|
os.makedirs(os.path.join(self.home_dir, 'dir'))
|
||||||
|
os.symlink(os.path.join(self.home_dir, 'dir'),
|
||||||
|
os.path.join(self.home_dir, '.dir'))
|
||||||
|
|
||||||
|
self.run_dotbot([{'link': {'~/.dir': {'path': 'dir', 'force': True}}}])
|
||||||
|
|
||||||
|
self.assertIsLinked(path='.dir', dotbot_path='dir')
|
||||||
|
|
||||||
|
|
||||||
|
def test_leaves_file(self):
|
||||||
|
""" relink does not overwrite file """
|
||||||
|
self.add_file('f')
|
||||||
|
self.add_home_file('.f')
|
||||||
|
|
||||||
|
self.assertRaises(SystemExit, self.run_dotbot,
|
||||||
|
[{'link': {'~/.f': 'f'}}])
|
||||||
|
|
||||||
|
self.assertNotLinked(path='.f', dotbot_path='f')
|
||||||
|
|
||||||
|
|
||||||
|
def test_relink_no_overwrite(self):
|
||||||
|
""" relink does not overwrite file """
|
||||||
|
self.add_file('f')
|
||||||
|
self.add_home_file('.f')
|
||||||
|
|
||||||
|
self.assertRaises(SystemExit, self.run_dotbot,
|
||||||
|
[{'link': {'~/.f': {'path': 'f', 'relink': True}}}])
|
||||||
|
|
||||||
|
self.assertNotLinked(path='.f', dotbot_path='f')
|
||||||
|
|
||||||
|
|
||||||
|
def test_relink_overwrites_symlink(self):
|
||||||
|
""" relink overwrites symlink """
|
||||||
|
self.add_file('f')
|
||||||
|
self.add_home_file('f')
|
||||||
|
os.symlink(os.path.join(self.home_dir, 'f'),
|
||||||
|
os.path.join(self.home_dir, '.f'))
|
||||||
|
|
||||||
|
|
||||||
|
self.run_dotbot([{'link':{'~/.f': {'path': 'f', 'relink': True}}}])
|
||||||
|
|
||||||
|
self.assertIsLinked('.f', dotbot_path='f')
|
29
test/test_shell.py
Normal file
29
test/test_shell.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import os
|
||||||
|
from utils import DotbotTestCase, mock
|
||||||
|
|
||||||
|
|
||||||
|
class ShellTestCase(DotbotTestCase):
|
||||||
|
def test_stdout_disabled_default(self):
|
||||||
|
""" shell command stdout disabled by default """
|
||||||
|
with mock.patch('dotbot.executor.commandrunner.subprocess.call',
|
||||||
|
return_value=0) as mock_call:
|
||||||
|
self.run_dotbot([{'shell': [
|
||||||
|
{'command': 'echo test'}
|
||||||
|
]}])
|
||||||
|
assert mock_call.called
|
||||||
|
|
||||||
|
args, kwargs = mock_call.call_args
|
||||||
|
self.assertTrue('stdout' in kwargs and kwargs['stdout'] is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_stdout_works(self):
|
||||||
|
""" shell command stdout works """
|
||||||
|
with mock.patch('dotbot.executor.commandrunner.subprocess.call',
|
||||||
|
return_value=0) as mock_call:
|
||||||
|
self.run_dotbot([{'shell': [
|
||||||
|
{'command': 'echo test', 'stdout': True}
|
||||||
|
]}])
|
||||||
|
assert mock_call.called
|
||||||
|
|
||||||
|
args, kwargs = mock_call.call_args
|
||||||
|
self.assertTrue('stdout' in kwargs and kwargs['stdout'] is None)
|
|
@ -1,19 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,8 +0,0 @@
|
||||||
test_description='clean ignores nonexistent directories'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_success 'run' '
|
|
||||||
run_dotbot <<EOF
|
|
||||||
- clean: ["~", "~/fake"]
|
|
||||||
EOF
|
|
||||||
'
|
|
|
@ -1,18 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,8 +0,0 @@
|
||||||
test_description='blank config allowed'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_success 'run' '
|
|
||||||
run_dotbot <<EOF
|
|
||||||
[]
|
|
||||||
EOF
|
|
||||||
'
|
|
|
@ -1,7 +0,0 @@
|
||||||
test_description='empty config disallowed'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_failure 'run' '
|
|
||||||
run_dotbot <<EOF
|
|
||||||
EOF
|
|
||||||
'
|
|
|
@ -1,18 +0,0 @@
|
||||||
test_description='link expands environment variables in source'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_success 'setup' '
|
|
||||||
echo "grape" > ${DOTFILES}/h
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'run' '
|
|
||||||
export APPLE="h" &&
|
|
||||||
run_dotbot <<EOF
|
|
||||||
- link:
|
|
||||||
~/.i: \$APPLE
|
|
||||||
EOF
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'test' '
|
|
||||||
grep "grape" ~/.i
|
|
||||||
'
|
|
|
@ -1,25 +0,0 @@
|
||||||
test_description='link expands environment variables in target'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_success 'setup' '
|
|
||||||
echo "apple" > ${DOTFILES}/f &&
|
|
||||||
echo "grape" > ${DOTFILES}/h
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'run' '
|
|
||||||
export ORANGE=".config" &&
|
|
||||||
export BANANA="g" &&
|
|
||||||
unset PEAR &&
|
|
||||||
run_dotbot <<EOF
|
|
||||||
- link:
|
|
||||||
~/\${ORANGE}/\$BANANA:
|
|
||||||
path: f
|
|
||||||
create: true
|
|
||||||
~/\$PEAR: h
|
|
||||||
EOF
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'test' '
|
|
||||||
grep "apple" ~/.config/g &&
|
|
||||||
grep "grape" ~/\$PEAR
|
|
||||||
'
|
|
|
@ -1,18 +0,0 @@
|
||||||
test_description='link leaves unset environment variables'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_success 'setup' '
|
|
||||||
echo "apple" > ${DOTFILES}/\$ORANGE
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'run' '
|
|
||||||
unset ORANGE &&
|
|
||||||
run_dotbot <<EOF
|
|
||||||
- link:
|
|
||||||
~/.f: \$ORANGE
|
|
||||||
EOF
|
|
||||||
'
|
|
||||||
|
|
||||||
test_expect_success 'test' '
|
|
||||||
grep "apple" ~/.f
|
|
||||||
'
|
|
|
@ -1,21 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,18 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,20 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,21 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,11 +0,0 @@
|
||||||
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
|
|
||||||
'
|
|
|
@ -1,9 +0,0 @@
|
||||||
test_description='shell command stdout disabled by default'
|
|
||||||
. '../test-lib.bash'
|
|
||||||
|
|
||||||
test_expect_success 'run' '
|
|
||||||
(run_dotbot | (! grep "^banana")) <<EOF
|
|
||||||
- shell:
|
|
||||||
- echo banana
|
|
||||||
EOF
|
|
||||||
'
|
|
158
test/utils.py
Normal file
158
test/utils.py
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
PROJECT_ROOT_DIRECTORY = os.path.dirname(
|
||||||
|
os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
def inject(lib_path):
|
||||||
|
path = os.path.join(PROJECT_ROOT_DIRECTORY, 'lib', lib_path)
|
||||||
|
sys.path.insert(0, path)
|
||||||
|
|
||||||
|
# version dependent libraries
|
||||||
|
if sys.version_info[0] >= 3:
|
||||||
|
inject('pyyaml/lib3')
|
||||||
|
else:
|
||||||
|
inject('pyyaml/lib')
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'dotbot')):
|
||||||
|
if PROJECT_ROOT_DIRECTORY not in sys.path:
|
||||||
|
sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
|
||||||
|
os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY)
|
||||||
|
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import dotbot
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
import shlex
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
try:
|
||||||
|
from StringIO import StringIO
|
||||||
|
except ImportError:
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
# mock is built-in after py3.3, otherwise import the 3rd party mock
|
||||||
|
try:
|
||||||
|
from unittest import mock
|
||||||
|
except ImportError:
|
||||||
|
import mock
|
||||||
|
|
||||||
|
|
||||||
|
class DotbotTestCase(unittest.TestCase):
|
||||||
|
""" Dotbot specific TestCase that will take care of setting up temporary directories and a convenience
|
||||||
|
function for simulating running Dotbot from the CLI """
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
""" Creates a temporary directory to run in for every test """
|
||||||
|
self.tempdir = tempfile.mkdtemp()
|
||||||
|
self.dotbot_dir = os.path.join(self.tempdir, 'dotbot')
|
||||||
|
self.home_dir = os.path.join(self.tempdir, 'home')
|
||||||
|
self.config_file = os.path.join(self.dotbot_dir, 'dotbot.config')
|
||||||
|
|
||||||
|
if int(os.environ.get('NO_CLEAN', '0')):
|
||||||
|
print(self.tempdir) # If we're not going to clean up, print out where we're runnin
|
||||||
|
|
||||||
|
os.mkdir(self.dotbot_dir)
|
||||||
|
os.mkdir(self.home_dir)
|
||||||
|
|
||||||
|
def add_file(self, filename, contents=""):
|
||||||
|
""" Create a file in temporary dotbot_dir. Optionally with content """
|
||||||
|
with open(os.path.join(self.dotbot_dir, filename), 'w') as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
def add_home_file(self, filename, contents=""):
|
||||||
|
""" Create a file in the temporary home_dir. Optionally with content """
|
||||||
|
with open(os.path.join(self.home_dir, filename), 'w') as f:
|
||||||
|
f.write(contents)
|
||||||
|
|
||||||
|
def add_dirs(self, path):
|
||||||
|
""" Create directories within the temporary dotbot_dir. Acts like ``mkdir -p``. Path is relative to the
|
||||||
|
dotbot dir """
|
||||||
|
os.makedirs(os.path.join(self.dotbot_dir, path))
|
||||||
|
|
||||||
|
def add_home_dirs(self, path):
|
||||||
|
""" Create directories within the temporary home_dir. Acts like ``mkdir -p``. Path is relative to the
|
||||||
|
home dir """
|
||||||
|
os.makedirs(os.path.join(self.home_dir, path))
|
||||||
|
|
||||||
|
def add_symlink(self, path, dotbot_path=None):
|
||||||
|
""" Creates a symlink from ``self.home_dir``/path to ``self.dotbot_dir``/dotbot_path.
|
||||||
|
If dotbot_path is None, path will be used for home_dir and dotbot_dir """
|
||||||
|
dotbot_path = path if dotbot_path is None else dotbot_path
|
||||||
|
os.symlink(os.path.join(self.dotbot_dir, dotbot_path), os.path.join(self.home_dir, path))
|
||||||
|
|
||||||
|
def assertIsLinked(self, path, dotbot_path=None):
|
||||||
|
""" Asserts that the given ``path`` in self.home_dir is symlinked to the corresponding ``path``
|
||||||
|
in self.dotbot_dir """
|
||||||
|
dotbot_path = path if dotbot_path is None else dotbot_path
|
||||||
|
self.assertTrue(os.path.islink(os.path.join(self.home_dir, path)))
|
||||||
|
self.assertEqual(os.stat(os.path.join(self.dotbot_dir, dotbot_path)),
|
||||||
|
os.stat(os.path.join(self.home_dir, path)))
|
||||||
|
|
||||||
|
def assertNotLinked(self, path, dotbot_path=None):
|
||||||
|
""" Asserts that the given ``path`` in self.home_dir is symlinked to the corresponding ``path``
|
||||||
|
in self.dotbot_dir """
|
||||||
|
dotbot_path = path if dotbot_path is None else dotbot_path
|
||||||
|
self.assertNotEqual(os.stat(os.path.join(self.dotbot_dir, dotbot_path)),
|
||||||
|
os.stat(os.path.join(self.home_dir, path)))
|
||||||
|
|
||||||
|
def assertDoesNotExist(self, path):
|
||||||
|
""" Asserts the given ``path`` in self.home_dir does not exist """
|
||||||
|
self.assertFalse(os.path.exists(os.path.join(self.home_dir, path)) or
|
||||||
|
os.path.lexists(os.path.join(self.home_dir, path)))
|
||||||
|
|
||||||
|
def run_dotbot(self, config="", args="", skip_config=False):
|
||||||
|
""" Runs dotbot in a simulated way temporarily intercepting stdout, stderr, setting the HOME
|
||||||
|
environment variable to ``self.home_dir``, and setting sys.argv to the simulated command
|
||||||
|
line options. ``run_dotbot`` will automatically set the ``--base-directory`` and
|
||||||
|
``--config-file`` command line arguments appropriately.
|
||||||
|
|
||||||
|
The ``config`` argument is a string that is written out as the configuration file for dotbot
|
||||||
|
to use. The ``args`` argument is a string of extra command line arguments to pass to dotbot
|
||||||
|
just like they would be passed on the command line.
|
||||||
|
|
||||||
|
If ``skip_config`` is True, a config file will not be written.
|
||||||
|
|
||||||
|
Returns a tuple (out, err) of the stdout and stderr from dotbot.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not skip_config:
|
||||||
|
with open(self.config_file, 'w') as f:
|
||||||
|
if not isinstance(config, str):
|
||||||
|
f.write(yaml.dump(config, default_flow_style=False))
|
||||||
|
else:
|
||||||
|
f.write(config)
|
||||||
|
|
||||||
|
base_args = [
|
||||||
|
'dotbot',
|
||||||
|
'--base-directory', self.dotbot_dir,
|
||||||
|
'--config-file', self.config_file,
|
||||||
|
]
|
||||||
|
|
||||||
|
old_home, os.environ['HOME'] = os.path.expanduser('~'), self.home_dir
|
||||||
|
old_stdout, sys.stdout = sys.stdout, StringIO()
|
||||||
|
old_stderr, sys.stderr = sys.stderr, StringIO()
|
||||||
|
old_argv, sys.argv = sys.argv, base_args + shlex.split(args)
|
||||||
|
old_environ = os.environ.copy()
|
||||||
|
|
||||||
|
try:
|
||||||
|
dotbot.cli.main()
|
||||||
|
finally:
|
||||||
|
os.environ['HOME'] = old_home
|
||||||
|
out, sys.stdout = sys.stdout.getvalue(), old_stdout
|
||||||
|
err, sys.stderr = sys.stderr.getvalue(), old_stderr
|
||||||
|
sys.argv = old_argv
|
||||||
|
os.environ.clear()
|
||||||
|
os.environ.update(old_environ)
|
||||||
|
|
||||||
|
if int(os.environ.get('DEBUG', '0')):
|
||||||
|
print("\nDotbot Output:")
|
||||||
|
print('out:\n', out)
|
||||||
|
print('err:\n', err)
|
||||||
|
|
||||||
|
return out, err
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
""" Clean up the temporary directory that was created. Set NO_CLEAN=1 to disable cleanup """
|
||||||
|
if not int(os.environ.get('NO_CLEAN', '0')):
|
||||||
|
shutil.rmtree(self.tempdir)
|
Loading…
Reference in a new issue