aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dotfiles/dotfile.py43
-rw-r--r--tests/test_dotfile.py112
2 files changed, 155 insertions, 0 deletions
diff --git a/dotfiles/dotfile.py b/dotfiles/dotfile.py
new file mode 100644
index 0000000..ed0b2cb
--- /dev/null
+++ b/dotfiles/dotfile.py
@@ -0,0 +1,43 @@
+import errno
+
+
+class Dotfile:
+ """
+ This class implements the 'dotfile' abstraction.
+
+ A dotfile has two primary attributes:
+
+ name -- name of dotfile in the home directory (~/.vimrc)
+ target -- target the dotfile should point to (~/Dotfiles/vimrc)
+
+ The above attributes are both py.path.local objects.
+
+ The goal is for there to be no special logic or stored global state. Only
+ the implementation of three operations made available to the caller:
+
+ add -- moves a dotfile into the repository and replaces it with a symlink
+ remove -- the opposite of add
+ sync -- ensure that each repository file has a corresponding symlink
+
+ This is where most filesystem operaitons (link, delete, etc) should be
+ called, and not in the layers above.
+ """
+
+ def __init__(self, name, target):
+ self.name = name
+ self.target = target
+
+ def add(self):
+ if self.target.check(exists=1):
+ raise OSError(errno.EEXIST, self.target)
+ self.name.move(self.target)
+ self.sync()
+
+ def remove(self):
+ if self.target.check(exists=0):
+ raise OSError(errno.ENOENT, self.target)
+ self.name.remove()
+ self.target.move(self.name)
+
+ def sync(self):
+ self.name.mksymlinkto(self.target)
diff --git a/tests/test_dotfile.py b/tests/test_dotfile.py
new file mode 100644
index 0000000..b854859
--- /dev/null
+++ b/tests/test_dotfile.py
@@ -0,0 +1,112 @@
+import pytest
+import py.error
+from dotfiles.dotfile import Dotfile
+
+
+class TestAdd:
+
+ def test_add(self, tmpdir):
+
+ repo = tmpdir.ensure("Dotfiles", dir=1)
+ name = tmpdir.ensure(".vimrc")
+ target = repo.join("vimrc")
+
+ dotfile = Dotfile(name, target)
+ dotfile.add()
+
+ assert target.check(file=1, link=0)
+ assert name.check(file=1, link=1)
+ assert name.samefile(target)
+
+ def test_add_twice(self, tmpdir):
+
+ repo = tmpdir.ensure("Dotfiles", dir=1)
+ name = tmpdir.ensure(".vimrc")
+ target = repo.join("vimrc")
+
+ dotfile = Dotfile(name, target)
+ dotfile.add()
+
+ assert target.check(file=1, link=0)
+ assert name.check(file=1, link=1)
+ assert name.samefile(target)
+
+ with pytest.raises(OSError):
+ dotfile.add()
+
+ assert target.check(file=1, link=0)
+ assert name.check(file=1, link=1)
+ assert name.samefile(target)
+
+
+class TestRemove:
+
+ def test_remove(self, tmpdir):
+
+ repo = tmpdir.ensure("Dotfiles", dir=1)
+ name = tmpdir.join(".vimrc")
+ target = repo.ensure("vimrc")
+
+ name.mksymlinkto(target)
+
+ dotfile = Dotfile(name, target)
+ dotfile.remove()
+
+ assert False == target.check()
+ assert name.check(file=1, link=0)
+
+ def test_remove_twice(self, tmpdir):
+
+ repo = tmpdir.ensure("Dotfiles", dir=1)
+ name = tmpdir.join(".vimrc")
+ target = repo.ensure("vimrc")
+
+ name.mksymlinkto(target)
+
+ dotfile = Dotfile(name, target)
+ dotfile.remove()
+
+ assert False == target.check()
+ assert name.check(file=1, link=0)
+
+ with pytest.raises(OSError):
+ dotfile.remove()
+
+ assert False == target.check()
+ assert name.check(file=1, link=0)
+
+
+class TestSync:
+
+ def test_sync(self, tmpdir):
+
+ repo = tmpdir.ensure("Dotfiles", dir=1)
+ name = tmpdir.join(".vimrc")
+ target = repo.ensure("vimrc")
+
+ dotfile = Dotfile(name, target)
+ dotfile.sync()
+
+ assert target.check(file=1, link=0)
+ assert name.check(file=1, link=1)
+ assert name.samefile(target)
+
+ def test_sync_twice(self, tmpdir):
+
+ repo = tmpdir.ensure("Dotfiles", dir=1)
+ name = tmpdir.join(".vimrc")
+ target = repo.ensure("vimrc")
+
+ dotfile = Dotfile(name, target)
+ dotfile.sync()
+
+ assert target.check(file=1, link=0)
+ assert name.check(file=1, link=1)
+ assert name.samefile(target)
+
+ with pytest.raises(py.error.EEXIST):
+ dotfile.sync()
+
+ assert target.check(file=1, link=0)
+ assert name.check(file=1, link=1)
+ assert name.samefile(target)