From b622c5ea20a46143e345e9b6c3f8954425115070 Mon Sep 17 00:00:00 2001 From: Brian Knobbs Date: Sat, 27 Jun 2015 01:53:57 -0400 Subject: [PATCH 1/4] Switched to unittest/tox tests for possible better python2/3 testing. anishathalye/dotbot#40 --- .gitignore | 2 + test/README.md | 49 ++++--- test/Vagrantfile | 10 -- test/driver-lib.bash | 120 ---------------- test/test | 35 ----- test/test-lib.bash | 51 ------- test/test_clean.py | 33 +++++ test/test_config.py | 10 ++ test/tests/clean-missing.bash | 19 --- test/tests/clean-nonexistent.bash | 8 -- test/tests/clean-outside.bash | 18 --- test/tests/config-blank.bash | 8 -- test/tests/config-empty.bash | 7 - ...environment-variable-expansion-source.bash | 18 --- ...environment-variable-expansion-target.bash | 25 ---- .../link-environment-variable-unset.bash | 18 --- test/tests/link-force-overwrite-symlink.bash | 21 --- test/tests/link-leaves-file.bash | 18 --- test/tests/link-relink-leaves-file.bash | 20 --- test/tests/link-relink-overwrite-symlink.bash | 21 --- test/tests/shell-allow-stdout.bash | 11 -- test/tests/shell-disables-stdout.bash | 9 -- test/utils.py | 129 ++++++++++++++++++ tox.ini | 15 ++ 24 files changed, 222 insertions(+), 453 deletions(-) delete mode 100644 test/Vagrantfile delete mode 100644 test/driver-lib.bash delete mode 100755 test/test delete mode 100644 test/test-lib.bash create mode 100644 test/test_clean.py create mode 100644 test/test_config.py delete mode 100644 test/tests/clean-missing.bash delete mode 100644 test/tests/clean-nonexistent.bash delete mode 100644 test/tests/clean-outside.bash delete mode 100644 test/tests/config-blank.bash delete mode 100644 test/tests/config-empty.bash delete mode 100644 test/tests/link-environment-variable-expansion-source.bash delete mode 100644 test/tests/link-environment-variable-expansion-target.bash delete mode 100644 test/tests/link-environment-variable-unset.bash delete mode 100644 test/tests/link-force-overwrite-symlink.bash delete mode 100644 test/tests/link-leaves-file.bash delete mode 100644 test/tests/link-relink-leaves-file.bash delete mode 100644 test/tests/link-relink-overwrite-symlink.bash delete mode 100644 test/tests/shell-allow-stdout.bash delete mode 100644 test/tests/shell-disables-stdout.bash create mode 100644 test/utils.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 0d20b64..670777e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.pyc +.coverage +.tox diff --git a/test/README.md b/test/README.md index 824dbca..2cd4290 100644 --- a/test/README.md +++ b/test/README.md @@ -1,24 +1,41 @@ 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. +Testing is run against multiple Python using ``tox``. It is recommended to use ``pyenv`` to manage +your Python version. -Running the Tests ------------------ -Before running the tests, the virtual machine must be running. It can be -started by running `vagrant up`. +Setup +===== -The test suite can be run by running `./test`. Selected tests can be run by -passing paths to the tests as arguments to `./test`. +* Ensure git submodules are up to date +* 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 diff --git a/test/Vagrantfile b/test/Vagrantfile deleted file mode 100644 index 4463f99..0000000 --- a/test/Vagrantfile +++ /dev/null @@ -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 diff --git a/test/driver-lib.bash b/test/driver-lib.bash deleted file mode 100644 index f7b25ba..0000000 --- a/test/driver-lib.bash +++ /dev/null @@ -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 -} diff --git a/test/test b/test/test deleted file mode 100755 index 7f4b8f1..0000000 --- a/test/test +++ /dev/null @@ -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} diff --git a/test/test-lib.bash b/test/test-lib.bash deleted file mode 100644 index 425ae56..0000000 --- a/test/test-lib.bash +++ /dev/null @@ -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 diff --git a/test/test_clean.py b/test/test_clean.py new file mode 100644 index 0000000..02060c5 --- /dev/null +++ b/test/test_clean.py @@ -0,0 +1,33 @@ +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') + + with open(os.path.join(self.home_dir, 'g'), 'w') as g: + g.write('') + 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'))) diff --git a/test/test_config.py b/test/test_config.py new file mode 100644 index 0000000..7a25e97 --- /dev/null +++ b/test/test_config.py @@ -0,0 +1,10 @@ +import os +from utils import DotbotTestCase + + +class ConfigTestCase(DotbotTestCase): + def test_blank_config_allowed(self): + self.run_dotbot(config='[]') + + def test_empty_config_not_allowed(self): + self.assertRaises(SystemExit, self.run_dotbot, skip_config=True) diff --git a/test/tests/clean-missing.bash b/test/tests/clean-missing.bash deleted file mode 100644 index dc6c84c..0000000 --- a/test/tests/clean-missing.bash +++ /dev/null @@ -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 < ${DOTFILES}/h -' - -test_expect_success 'run' ' -export APPLE="h" && -run_dotbot < ${DOTFILES}/f && -echo "grape" > ${DOTFILES}/h -' - -test_expect_success 'run' ' -export ORANGE=".config" && -export BANANA="g" && -unset PEAR && -run_dotbot < ${DOTFILES}/\$ORANGE -' - -test_expect_success 'run' ' -unset ORANGE && -run_dotbot < ${DOTFILES}/f && -echo "grape" > ~/.f -' - -test_expect_failure 'run' ' -run_dotbot < ${DOTFILES}/f && -echo "grape" > ~/.f -' - -test_expect_failure 'run' ' -run_dotbot < ${DOTFILES}/f && -echo "grape" > ~/f && -ln -s ~/f ~/.f -' - -test_expect_success 'run' ' -run_dotbot <= 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 + +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_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_symlink(self, path): + """ Creates a symlink from ``self.home_dir``/path to ``self.dotbot_dir``/path """ + os.symlink(os.path.join(self.dotbot_dir, path), os.path.join(self.home_dir, path)) + + def assertIsLinked(self, path): + """ Asserts that the given ``path`` in self.home_dir is symlinked to the corresponding ``path`` + in self.dotbot_dir """ + self.assertTrue(os.path.islink(os.path.join(self.home_dir, path))) + self.assertEqual(os.stat(os.path.join(self.dotbot_dir, 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: + f.write(config) + + base_args = [ + 'dotbot', + '--base-directory', self.tempdir, + '--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) + + 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 + 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) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..877c01b --- /dev/null +++ b/tox.ini @@ -0,0 +1,15 @@ +[tox] +skipsdist=True +envlist = + {py26,py27,py32,py33,py34} + +[testenv] +deps= + {py26,py27,py32}: mock + nose + coverage + python-coveralls +commands= + nosetests {posargs:--with-coverage --cover-package=dotbot} + + From ee3ddb02c530942b00d50bdbd9942d4944c49786 Mon Sep 17 00:00:00 2001 From: Brian Knobbs Date: Sat, 27 Jun 2015 02:10:22 -0400 Subject: [PATCH 2/4] Adding travis-ci config. anishathalye/dotbot#40 --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0b1ee98 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: python +python: "2.7" +env: + - TOXENV=py26 + - TOXENV=py27 + - TOXENV=py32 + - TOXENV=py33 + - TOXENV=py34 +install: + - pip install tox +script: + - tox +after_success: + - coveralls From 640b454ef91d95adf0743463addf8cdeaae17716 Mon Sep 17 00:00:00 2001 From: Brian Knobbs Date: Sat, 27 Jun 2015 19:36:23 -0400 Subject: [PATCH 3/4] Reimplemented all old tests into new test framework. anishathalye/dotbot#40 --- test/test_clean.py | 4 +- test/test_config.py | 2 + test/test_link.py | 92 +++++++++++++++++++++++++++++++++++++++++++++ test/test_shell.py | 29 ++++++++++++++ test/utils.py | 51 +++++++++++++++++++------ 5 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 test/test_link.py create mode 100644 test/test_shell.py diff --git a/test/test_clean.py b/test/test_clean.py index 02060c5..aa21c70 100644 --- a/test/test_clean.py +++ b/test/test_clean.py @@ -21,9 +21,7 @@ class MissingTestCase(DotbotTestCase): def test_ignores_outside_linking(self): """ clean ignores files linking outside dotfiles directory """ self.add_symlink('f') - - with open(os.path.join(self.home_dir, 'g'), 'w') as g: - g.write('') + 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: ["~"]') diff --git a/test/test_config.py b/test/test_config.py index 7a25e97..0ad61a9 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -4,7 +4,9 @@ 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) diff --git a/test/test_link.py b/test/test_link.py new file mode 100644 index 0000000..ca7be73 --- /dev/null +++ b/test/test_link.py @@ -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') diff --git a/test/test_shell.py b/test/test_shell.py new file mode 100644 index 0000000..9df44f6 --- /dev/null +++ b/test/test_shell.py @@ -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) diff --git a/test/utils.py b/test/utils.py index c90d55f..0473fac 100644 --- a/test/utils.py +++ b/test/utils.py @@ -24,6 +24,7 @@ import dotbot import tempfile import unittest import shlex +import yaml try: from StringIO import StringIO @@ -59,22 +60,42 @@ class DotbotTestCase(unittest.TestCase): 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_symlink(self, path): - """ Creates a symlink from ``self.home_dir``/path to ``self.dotbot_dir``/path """ - os.symlink(os.path.join(self.dotbot_dir, path), os.path.join(self.home_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 assertIsLinked(self, 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, 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 @@ -97,11 +118,14 @@ class DotbotTestCase(unittest.TestCase): if not skip_config: with open(self.config_file, 'w') as f: - f.write(config) + if not isinstance(config, str): + f.write(yaml.dump(config, default_flow_style=False)) + else: + f.write(config) base_args = [ 'dotbot', - '--base-directory', self.tempdir, + '--base-directory', self.dotbot_dir, '--config-file', self.config_file, ] @@ -109,17 +133,22 @@ class DotbotTestCase(unittest.TestCase): 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() + 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 - print("\nDotbot Output:") - print('out:\n', out) - print('err:\n', err) + 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 From 2faa26a3b7fbbd7f3817063d3eeaca8887aa66ce Mon Sep 17 00:00:00 2001 From: Brian Knobbs Date: Sat, 27 Jun 2015 19:40:01 -0400 Subject: [PATCH 4/4] Added sample badges to README.md --- .travis.yml | 2 +- README.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0b1ee98..05ffcf4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ env: - TOXENV=py33 - TOXENV=py34 install: - - pip install tox + - pip install tox python-coveralls script: - tox after_success: diff --git a/README.md b/README.md index 10ad29c..fb48f62 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ 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 [bo]o[t]strapper, get it?). It does *less* than you think, because version control systems do more than you think.