def test_partial_run_cleanup(tmpdir): from homely._engine2 import Engine from homely.files import MakeDir cfgpath = gettmpfilepath(tmpdir, '.json') d1 = os.path.join(tmpdir, 'dir1') d2 = os.path.join(tmpdir, 'dir2') # simulate adding our first repo which just creates d1 e = Engine(cfgpath) e.run(MakeDir(d1)) e.cleanup(e.RAISE) del e assert os.path.isdir(d1) and not os.path.isdir(d2) # simulate adding a 2nd repo that would just created 2 ... this shouldn't # trigger a cleanup though e = Engine(cfgpath) e.run(MakeDir(d2)) del e assert os.path.isdir(d1) and os.path.isdir(d2) # pretend that the first repo is removed, and do a full update e = Engine(cfgpath) e.run(MakeDir(d2)) e.cleanup(e.RAISE) assert not os.path.isdir(d1) and os.path.isdir(d2)
def test_cleanup_everything(tmpdir): ''' test that recreating the engine, running nothing on it, then calling cleanup() will remove all of things that might be lying around ''' from homely._engine2 import Engine from homely.files import MakeDir, MakeSymlink, LineInFile cfgpath = gettmpfilepath(tmpdir, '.json') d1 = gettmpfilepath(tmpdir, '.d') d1f1 = os.path.join(d1, 'sub-file.txt') l1 = gettmpfilepath(tmpdir, '.lnk') e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(MakeSymlink(d1, l1)) e.run(LineInFile(d1f1, "AAA")) e.cleanup(e.RAISE) del e assert os.path.isdir(d1) assert contents(d1f1, "AAA\n") assert os.path.islink(l1) and os.readlink(l1) == d1 # if we recreate the engine with nothing on it, it should clean everything # up e = Engine(cfgpath) e.cleanup(e.RAISE) assert not os.path.exists(d1) assert not os.path.islink(l1)
def test_engine_folder_cleanup(tmpdir): from homely._engine2 import Engine from homely.files import MakeDir # first thing ... test mkdir cleanup # a temporary file where the engine can store its config cfgpath = gettmpfilepath(tmpdir, '.json') d1 = os.path.join(tmpdir, 'dir1') d2 = os.path.join(tmpdir, 'dir2') d2a = os.path.join(d2, 'sub-a') d2a1 = os.path.join(d2a, 'supersub-1') d2a2 = os.path.join(d2a, 'supersub-2') # create the first directory e = Engine(cfgpath) e.run(MakeDir(d1)) e.cleanup(e.RAISE) del e e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(MakeDir(d2)) e.run(MakeDir(d2a)) e.run(MakeDir(d2a1)) e.run(MakeDir(d2a2)) e.cleanup(e.RAISE) del e # all dirs should exist now check = [d1, d2, d2a, d2a1, d2a2] for d in check: assert os.path.exists(d) assert all(map(os.path.exists, check)) # make a new engine and make ONLY d2a1 ... this should # be enough to keep everything but d1 and d2a2 alive e = Engine(cfgpath) e.run(MakeDir(d2a2)) e.cleanup(e.POSTPONE) del e # all dirs should exist except for d1 and d2a1 assert all(map(os.path.exists, [d2, d2a, d2a2])) assert not any(map(os.path.exists, [d1, d2a1])) # make a new engine, add nothing to it, this should be enough to blast away # all directories e = Engine(cfgpath) e.cleanup(e.RAISE) del e # all dirs are gone, EXCEPT FOR our original tmpdir, which our cleaners # were smart enough to leave behind assert not any(map(os.path.exists, [d1, d2, d2a, d2a1, d2a2])) assert os.path.exists(tmpdir)
def test_symlink_cleanup_interaction(tmpdir): from homely._engine2 import Engine from homely._errors import CleanupConflict from homely.files import MakeDir, MakeSymlink cfgpath = gettmpfilepath(tmpdir) d1 = os.path.join(tmpdir, 'dir1') l1 = os.path.join(tmpdir, 'link1') d1da = os.path.join(d1, 'dir-a') l1da = os.path.join(l1, 'dir-a') # basic symlink e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(MakeSymlink(d1, l1)) e.cleanup(e.RAISE) del e # make sure they both exist, and the symlink has the correct target assert os.path.isdir(d1) assert os.path.islink(l1) and os.readlink(l1) == d1 # mkdir() inside a symlink's target e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(MakeSymlink(d1, l1)) e.run(MakeDir(l1da)) e.cleanup(e.POSTPONE) del e # everything should exist, the symlink should have the correct target assert os.path.isdir(d1) assert os.path.islink(l1) and os.readlink(l1) == d1 assert os.path.isdir(d1da) and os.path.isdir(l1da) # if we try and get rid of the symlink now, the cleaner will hang around # because it can't garbage collect the thing right now e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(MakeDir(l1da)) try: e.cleanup(e.RAISE) except CleanupConflict as err: # conflict should be over the symlink's path assert err.conflictpath == l1 # the path should still be needed by `l1da` assert err.pathwanter == l1da else: raise Exception("Expected a cleanup error!") # noqa del e # clean everything up now e = Engine(cfgpath) e.cleanup(e.RAISE) del e assert not any(map(os.path.exists, [d1, d1da, l1])) os.unlink(cfgpath) # now we try the opposite - symlink to a directory and try to clean up the # directory e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(MakeSymlink(d1, l1)) e.cleanup(e.RAISE) del e assert os.path.isdir(d1) assert os.path.islink(l1) and os.readlink(l1) == d1 # the engine will happily clean up the directory, even though there is a # symlink pointing to it ... symlinks don't care whether the target exists # or not e = Engine(cfgpath) e.run(MakeSymlink(d1, l1)) e.cleanup(e.RAISE) del e # clean up everything now e = Engine(cfgpath) e.cleanup(e.RAISE) assert not any(map(os.path.exists, [d1, d1da, l1])) os.unlink(cfgpath)
def test_lineinfile_cleanup_interaction(tmpdir): from homely._engine2 import Engine from homely.files import MakeDir, LineInFile # a temporary file where the engine can store its config cfgpath = gettmpfilepath(tmpdir, '.json') f1 = os.path.join(tmpdir, 'f1.txt') d1 = os.path.join(tmpdir, 'f1.txt.dir') d1f1 = os.path.join(d1, 'f-1.txt') d2 = os.path.join(tmpdir, 'dir2') d2f1 = os.path.join(d2, 'f-1.txt') d3 = os.path.join(tmpdir, 'dir3') d3d1 = os.path.join(d3, 'sub-1') d3d2 = os.path.join(d3, 'sub-2') d3d1f1 = os.path.join(d3d1, 'somefile.txt') d3d2f1 = os.path.join(d3d2, 'somefile.txt') # make the dir and the file, make sure they both exist e = Engine(cfgpath) e.run(LineInFile(f1, "AAA")) e.run(MakeDir(d1)) e.cleanup(e.RAISE) del e assert os.path.isdir(d1) assert contents(f1) == "AAA\n" # remake the engine without the dir, make sure the dir isn't kept around by # the file e = Engine(cfgpath) e.run(LineInFile(f1, "AAA")) e.cleanup(e.POSTPONE) del e assert not os.path.exists(d1) assert contents(f1) == "AAA\n" # make a new file in the directory, make sure they take ownership of the # directory e = Engine(cfgpath) e.run(MakeDir(d1)) e.run(LineInFile(d1f1, "AAA")) e.run(LineInFile(d1f1, "BBB")) e.cleanup(e.POSTPONE) del e # the directory should still exist, the old file should not, and the new # file should contain both lines assert os.path.isdir(d1) assert not os.path.exists(f1) assert contents(d1f1) == "AAA\nBBB\n" # if we do the same thing again but without the MakeDir, we should get # exactly the same result e = Engine(cfgpath) e.run(LineInFile(d1f1, "AAA")) e.run(LineInFile(d1f1, "BBB")) e.cleanup(e.POSTPONE) del e # the directory should still exist, the old file should not, and the new # file should contain both lines assert os.path.isdir(d1) assert not os.path.exists(f1) assert contents(d1f1) == "AAA\nBBB\n" # the important thing to note is that the engine still knows that it needs # to clean up the directory later e = Engine(cfgpath) assert e.pathstoclean()[d1] == e.TYPE_FOLDER_ONLY del e # if we get rid of one of the LineInFile() items, the file and dir still # hang around e = Engine(cfgpath) e.run(LineInFile(d1f1, "AAA")) e.cleanup(e.POSTPONE) del e assert contents(d1f1) == "AAA\n" # now, when we get rid of the last LineInFile() items, everything will be # cleaned up e = Engine(cfgpath) e.cleanup(e.RAISE) del e assert not os.path.exists(d1) os.unlink(cfgpath) # if we make a dir ourselves, a LineInFile() will not clean it up assert not os.path.exists(d2) os.mkdir(d2) e = Engine(cfgpath) e.run(LineInFile(d2f1, "AAA")) del e assert contents(d2f1) == "AAA\n" e = Engine(cfgpath) e.cleanup(e.RAISE) del e assert os.path.isdir(d2) and not os.path.exists(d2f1) os.rmdir(d2) # if we make a file ourselves, LineInFile() will not try to clean it up assert not os.path.exists(f1) contents(f1, "") e = Engine(cfgpath) e.run(LineInFile(f1, "AAA")) del e assert contents(f1) == "AAA\n" e = Engine(cfgpath) e.cleanup(e.RAISE) del e assert contents(f1) == "" # if two LineInFile() items share a parent dir (even the parent dir's # parent, and so on), and that parent dir was made by a MakeDir(), then it # gets cleaned up eventually when both of the LineInFile() items go away # - create the folder structure assert not os.path.exists(d3) os.unlink(cfgpath) e = Engine(cfgpath) e.run(MakeDir(d3)) e.run(MakeDir(d3d1)) e.run(MakeDir(d3d2)) e.cleanup(e.RAISE) del e assert all(map(os.path.isdir, [d3, d3d1, d3d2])) assert not any(map(os.path.exists, [d3d1f1, d3d2f1])) # - scrap the folder structure, but make files that depend on those folders e = Engine(cfgpath) e.run(LineInFile(d3d1f1, "AAA")) e.run(LineInFile(d3d2f1, "BBB")) e.cleanup(e.POSTPONE) del e assert contents(d3d1f1) == "AAA\n" and contents(d3d2f1) == "BBB\n" # - scrap the first file, and check that only things needed for the 2nd # file remain e = Engine(cfgpath) e.run(LineInFile(d3d2f1, "BBB")) e.cleanup(e.POSTPONE) del e assert not os.path.exists(d3d1) assert contents(d3d2f1) == "BBB\n" # - scrap the 2nd file and make sure everything else disappears e = Engine(cfgpath) e.cleanup(e.RAISE) del e assert not os.path.exists(d3)