From 5b7db08e8a1ef5d3f4703eb3e0503d0f42c3e2c1 Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Tue, 26 Apr 2022 07:03:44 -0500 Subject: [PATCH] Migrate clean-* tests to Python --- tests/test_clean.py | 149 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/test_clean.py diff --git a/tests/test_clean.py b/tests/test_clean.py new file mode 100644 index 0000000..e67d4de --- /dev/null +++ b/tests/test_clean.py @@ -0,0 +1,149 @@ +import os +import sys + +import pytest + + +# Python 2.7 on Windows does not have an `os.symlink()` function. +# PyPy on Windows raises NotImplementedError when `os.symlink()` is called. +# Older Python 3 versions on Windows require admin rights to create symlinks. +# +# In addition, functions like `os.path.realpath()` on Windows Pythons < 3.8 +# do not resolve symlinks and directory junctions correctly, +# and `shutil.rmtree()` will fail to delete directory junctions. +# +# For these reasons, if the tests are running on Windows with Python < 3.8 +# or with PyPy, the entire link test suite must be skipped. +# +if ( + sys.platform[:5] == "win32" + and (sys.version_info < (3, 8) or "pypy" in sys.version.lower()) +): + reason = "It is impossible to perform link tests on this platform" + pytestmark = pytest.mark.skip(reason=reason) + + +def test_clean_default(root, home, dotfiles, run_dotbot): + """Verify clean uses default unless overridden.""" + + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g")) + dotfiles.write_config([{ + "clean": { + "~/nonexistent": {"force": True}, + "~/": None, + }, + }]) + run_dotbot() + + assert not os.path.isdir(os.path.join(home, "nonexistent")) + assert os.path.islink(os.path.join(home, ".g")) + + +def test_clean_environment_variable_expansion(home, dotfiles, run_dotbot): + """Verify clean expands environment variables.""" + + os.symlink(os.path.join(dotfiles.directory, "f"), os.path.join(home, ".f")) + variable = "$HOME" + if sys.platform[:5] == "win32": + variable = "$USERPROFILE" + dotfiles.write_config([{"clean": [variable]}]) + run_dotbot() + + assert not os.path.islink(os.path.join(home, ".f")) + + +def test_clean_missing(home, dotfiles, run_dotbot): + """Verify clean deletes links to missing files.""" + + dotfiles.write("f") + os.symlink(os.path.join(dotfiles.directory, "f"), os.path.join(home, ".f")) + os.symlink(os.path.join(dotfiles.directory, "g"), os.path.join(home, ".g")) + dotfiles.write_config([{"clean": ["~"]}]) + run_dotbot() + + assert os.path.islink(os.path.join(home, ".f")) + assert not os.path.islink(os.path.join(home, ".g")) + + +def test_clean_nonexistent(home, dotfiles, run_dotbot): + """Verify clean ignores nonexistent directories.""" + + dotfiles.write_config([{"clean": ["~", "~/fake"]}]) + run_dotbot() # Nonexistent directories should not raise exceptions. + + assert not os.path.isdir(os.path.join(home, "fake")) + + +def test_clean_outside_force(root, home, dotfiles, run_dotbot): + """Verify clean forced to remove files linking outside dotfiles directory.""" + + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g")) + dotfiles.write_config([{"clean": {"~/": {"force": True}}}]) + run_dotbot() + + assert not os.path.islink(os.path.join(home, ".g")) + + +def test_clean_outside(root, home, dotfiles, run_dotbot): + """Verify clean ignores files linking outside dotfiles directory.""" + + os.symlink(os.path.join(dotfiles.directory, "f"), os.path.join(home, ".f")) + os.symlink(os.path.join(home, "g"), os.path.join(home, ".g")) + dotfiles.write_config([{"clean": ["~"]}]) + run_dotbot() + + assert not os.path.islink(os.path.join(home, ".f")) + assert os.path.islink(os.path.join(home, ".g")) + + +def test_clean_recursive_1(root, home, dotfiles, run_dotbot): + """Verify clean respects when the recursive directive is off (default).""" + + os.makedirs(os.path.join(home, "a", "b")) + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, "c")) + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, "a", "d")) + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, "a", "b", "e")) + dotfiles.write_config([{"clean": {"~": {"force": True}}}]) + run_dotbot() + + assert not os.path.islink(os.path.join(home, "c")) + assert os.path.islink(os.path.join(home, "a", "d")) + assert os.path.islink(os.path.join(home, "a", "b", "e")) + + +def test_clean_recursive_2(root, home, dotfiles, run_dotbot): + """Verify clean respects when the recursive directive is on.""" + + os.makedirs(os.path.join(home, "a", "b")) + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, "c")) + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, "a", "d")) + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, "a", "b", "e")) + dotfiles.write_config([{"clean": {"~": {"force": True, "recursive": True}}}]) + run_dotbot() + + assert not os.path.islink(os.path.join(home, "c")) + assert not os.path.islink(os.path.join(home, "a", "d")) + assert not os.path.islink(os.path.join(home, "a", "b", "e")) + + +def test_clean_defaults_1(root, home, dotfiles, run_dotbot): + """Verify that clean doesn't erase non-dotfiles links by default.""" + + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g")) + dotfiles.write_config([{"clean": ["~"]}]) + run_dotbot() + + assert os.path.islink(os.path.join(home, ".g")) + + +def test_clean_defaults_2(root, home, dotfiles, run_dotbot): + """Verify that explicit clean defaults override the implicit default.""" + + os.symlink(os.path.join(root, "nowhere"), os.path.join(home, ".g")) + dotfiles.write_config([ + {"defaults": {"clean": {"force": True}}}, + {"clean": ["~"]}, + ]) + run_dotbot() + + assert not os.path.islink(os.path.join(home, ".g"))