def test_rename_and_undo(): """All renamings succeed, then are successfully undone.""" renamer = Renamer(context, testing=True) base = Path("test") / "happy_path" rm_tree(base) base.mkdir() arcs = [ Arc(base / "source_0", base / "target_0"), Arc(base / "source_1", base / "target_1"), Arc(base / "source_2", base / "target_2"), ] for arc in arcs: arc.source.touch() renamer.perform_renamings(arcs) arcs_for_undoing = renamer.get_arcs_for_undoing(logger.get_contents()) renamer.perform_renamings(arcs_for_undoing) assert set(base.iterdir()) == set(arc.source for arc in arcs) print(logger.get_contents()) assert logger.get_contents() == "\n".join([ "INFO:root:3 items to rename.", "INFO:root:SOURCE:test/happy_path/source_0\tTARGET:test/happy_path/target_0", "INFO:root:SOURCE:test/happy_path/source_1\tTARGET:test/happy_path/target_1", "INFO:root:SOURCE:test/happy_path/source_2\tTARGET:test/happy_path/target_2", "INFO:root:3 items renamed.", "INFO:root:3 items to rename.", "INFO:root:SOURCE:test/happy_path/target_2\tTARGET:test/happy_path/source_2", "INFO:root:SOURCE:test/happy_path/target_1\tTARGET:test/happy_path/source_1", "INFO:root:SOURCE:test/happy_path/target_0\tTARGET:test/happy_path/source_0", "INFO:root:3 items renamed.", ]) rm_tree(base)
def test_rename_when_git_is_not_installed(): """All renamings succeed.""" renamer = Renamer(context, testing=True) base = Path("test") / "happy_path" rm_tree(base) base.mkdir() arcs = [ Arc(base / "source_0", base / "target_0"), Arc(base / "source_1", base / "target_1"), Arc(base / "source_2", base / "target_2"), ] for arc in arcs: arc.source.touch() path_backup = os.environ["PATH"] os.environ["PATH"] = "" # make git unavailable renamer.perform_renamings(arcs) os.environ["PATH"] = path_backup assert set(base.iterdir()) == set(arc.target for arc in arcs) print(logger.get_contents()) assert logger.get_contents() == "\n".join([ "INFO:root:3 items to rename.", "WARNING:root:Git is not installed. Falling back to a non-git strategy.", "INFO:root:SOURCE:test/happy_path/source_0\tTARGET:test/happy_path/target_0", "INFO:root:SOURCE:test/happy_path/source_1\tTARGET:test/happy_path/target_1", "INFO:root:SOURCE:test/happy_path/source_2\tTARGET:test/happy_path/target_2", "INFO:root:3 items renamed.", ]) rm_tree(base)
def test_rename_fail_and_rollback_fail(): """One renaming fails, and the previous ones fail to be rolled back.""" renamer = Renamer(context, testing=True) base = Path("test") / "rollback_path" rm_tree(base) base.mkdir() arcs = [ Arc(base / "source_0", base / "target_0"), Arc(base / "source_1", base / "target_1"), Arc(base / "source_2", base / "target_2"), Arc(base / "source_3", base / "target_3"), ] for arc in arcs: arc.source.touch() arcs[2].source.unlink() # delete a source with pytest.raises(RecoverableRenamingError): renamer.perform_renamings(arcs) # the renaming fails arcs[1].target.unlink() # delete a file already renamed with pytest.raises(FileNotFoundError): renamer.rollback_renamings() # the rollback fails print(logger.get_contents()) assert logger.get_contents() == "\n".join([ "INFO:root:4 items to rename.", "INFO:root:SOURCE:test/rollback_path/source_0\tTARGET:test/rollback_path/target_0", "INFO:root:SOURCE:test/rollback_path/source_1\tTARGET:test/rollback_path/target_1", "WARNING:root:perform_renamings: [Errno 2] No such file or directory: 'test/rollback_path/source_2' -> 'test/rollback_path/target_2'", "INFO:root:2 renamings to roll back.", "ERROR:root:rollback_renamings: [Errno 2] No such file or directory: 'test/rollback_path/target_1' -> 'test/rollback_path/source_1'", ]) with pytest.raises(Exception): renamer.undo_renamings() # The previous rollback failed. Undoing is not possible. rm_tree(base)
def test_rename_fail_and_rollback(): """One renaming fails, but the previous ones are successfully rolled back.""" renamer = Renamer(context, testing=True) base = Path("test") / "rollback_path" rm_tree(base) base.mkdir() arcs = [ Arc(base / "source_0", base / "target_0"), Arc(base / "source_1", base / "target_1"), Arc(base / "source_2", base / "target_2"), Arc(base / "source_3", base / "target_3"), ] for arc in arcs: arc.source.touch() arcs[2].source.unlink() # delete a source with pytest.raises(RecoverableRenamingError): renamer.perform_renamings(arcs) # the renaming fails renamer.rollback_renamings() # but we can roll back the previous ones assert set(base.iterdir()) == { base / "source_0", # rolled back base / "source_1", # rolled back base / "source_3", # untouched } print(logger.get_contents()) assert logger.get_contents() == "\n".join([ "INFO:root:4 items to rename.", "INFO:root:SOURCE:test/rollback_path/source_0\tTARGET:test/rollback_path/target_0", "INFO:root:SOURCE:test/rollback_path/source_1\tTARGET:test/rollback_path/target_1", "WARNING:root:perform_renamings: [Errno 2] No such file or directory: 'test/rollback_path/source_2' -> 'test/rollback_path/target_2'", "INFO:root:2 renamings to roll back.", "INFO:root:SOURCE:test/rollback_path/target_1\tTARGET:test/rollback_path/source_1", "INFO:root:SOURCE:test/rollback_path/target_0\tTARGET:test/rollback_path/source_0", "INFO:root:2 renamings rolled back.", ]) rm_tree(base)
def test_rename_and_undo_fail_after_target_deletion(): """All renamings succeed, but undo them fails.""" renamer = Renamer(context, testing=True) base = Path("test") / "happy_path" rm_tree(base) base.mkdir() arcs = [ Arc(base / "source_0", base / "target_0"), Arc(base / "source_1", base / "target_1"), Arc(base / "source_2", base / "target_2"), ] for arc in arcs: arc.source.touch() renamer.perform_renamings(arcs) assert set(base.iterdir()) == set(arc.target for arc in arcs) arcs[1].target.unlink() # delete a renamed file arcs_for_undoing = renamer.get_arcs_for_undoing(logger.get_contents()) with pytest.raises(RecoverableRenamingError): renamer.perform_renamings(arcs_for_undoing) assert set(base.iterdir()) == { arcs[2].source, # correctly undone arcs[0].target, # failed to be undone } renamer.rollback_renamings() print(set(base.iterdir())) assert set(base.iterdir()) == { arcs[2].target, # correctly undone arcs[0].target, # failed to be undone } print(logger.get_contents()) assert logger.get_contents() == "\n".join([ "INFO:root:3 items to rename.", "INFO:root:SOURCE:test/happy_path/source_0\tTARGET:test/happy_path/target_0", "INFO:root:SOURCE:test/happy_path/source_1\tTARGET:test/happy_path/target_1", "INFO:root:SOURCE:test/happy_path/source_2\tTARGET:test/happy_path/target_2", "INFO:root:3 items renamed.", "INFO:root:3 items to rename.", "INFO:root:SOURCE:test/happy_path/target_2\tTARGET:test/happy_path/source_2", "WARNING:root:perform_renamings: [Errno 2] No such file or directory: 'test/happy_path/target_1' -> 'test/happy_path/source_1'", "INFO:root:1 renaming to roll back.", "INFO:root:SOURCE:test/happy_path/source_2\tTARGET:test/happy_path/target_2", "INFO:root:1 renaming rolled back.", ]) rm_tree(base)
def test_rename_and_undo_fail_after_log_file_deletion(): """All renamings succeed, but undo them fails.""" renamer = Renamer(context, testing=True) base = Path("test") / "happy_path" rm_tree(base) base.mkdir() arcs = [ Arc(base / "source_0", base / "target_0"), Arc(base / "source_1", base / "target_1"), Arc(base / "source_2", base / "target_2"), ] for arc in arcs: arc.source.touch() renamer.perform_renamings(arcs) assert set(base.iterdir()) == set(arc.target for arc in arcs) logger.path.unlink() # delete the log file arcs_for_undoing = renamer.get_arcs_for_undoing(logger.get_contents()) assert arcs_for_undoing == [] assert set(base.iterdir()) == set(arc.target for arc in arcs) rm_tree(base)
playground / "a/ac/aca", playground / "a/ac/acb", playground / "a/ac/acc", playground / "b/ba/bab", playground / "b/ba/bac", playground / "b/bb/bba", playground / "b/bb/bbb", playground / "b/bb/bbc", playground / "a/aa", playground / "a/ab", playground / "a", playground / "b/ba", ] rm_tree(playground) for path in paths: path.parent.mkdir(parents=True, exist_ok=True) if not path.exists(): path.touch() print("Playground recreated. Now, have fun renaming some files and folders...") context = Context() context.logger.create_new_log_file() do_renamings(context, paths=paths) input("Press Enter to show the log file...") print(context.logger.get_contents())