def test_renamed_destination(self): dest_path = as_human_readable(join(self._dest, 'a.txt')) sel_start = dest_path.rindex(os.sep) + 1 self._expect_prompt( ('Move "a.txt" to', dest_path, sel_start, sel_start + 1), (as_human_readable(join(self._dest, 'z.txt')), True)) self._check([self._a_txt], (self._dest, 'z.txt'))
def test_empty_directory(self): empty_dir = join(self.src, 'test') self._mkdir(empty_dir) self._perform_on(empty_dir) self._expect_files({'test'}) self._expect_files(set(), in_dir=join(self.dest, 'test')) return empty_dir
def test_relative_path_parent_dir(self): src_file = join(self.src, 'test.txt') self._touch(src_file, '1234') self._perform_on(src_file, dest_dir='..') dest_dir_abs = dirname(self.src) self._expect_files({'src', 'test.txt'}, dest_dir_abs) self._assert_file_contents_equal(join(dest_dir_abs, 'test.txt'), '1234')
def test_overwrite_locked_file(self): # Would also like to have this as a test case in MoveFilesTest but the # call to chmod(0o444) which we use to lock the file doesn't prevent the # file from being overwritten by a move. Another solution would be to # chown the file as a different user, but then the test would require # root privileges. So keep it here only for now. dir_ = join(self.src, 'dir') self._fs.makedirs(dir_) src_file = join(dir_, 'foo.txt') self._touch(src_file, 'dstn') dest_dir = join(self.dest, 'dir') self._fs.makedirs(dest_dir) locked_dest_file = join(dest_dir, 'foo.txt') self._touch(locked_dest_file) self._chmod(locked_dest_file, 0o444) try: self._expect_alert( ('foo.txt exists. Do you want to overwrite it?', YES | NO | YES_TO_ALL | NO_TO_ALL | ABORT, YES), answer=YES) self._expect_alert( ('Error copying foo.txt (permission denied).', OK, OK), answer=OK) self._perform_on(dir_) finally: # Make the file writeable again because on Windows, the temp dir # containing it can't be cleaned up otherwise. self._chmod(locked_dest_file, 0o777)
def test_error(self, answer_1=YES, answer_2=YES): nonexistent_file_1 = join(self.src, 'foo1.txt') nonexistent_file_2 = join(self.src, 'foo2.txt') existent_file = join(self.src, 'bar.txt') self._touch(existent_file) self._expect_alert( ('Could not %s %s (%s). ' 'Do you want to continue?' % (self.operation_descr_verb, as_human_readable(nonexistent_file_1), self._NO_SUCH_FILE_MSG), YES | YES_TO_ALL | ABORT, YES), answer=answer_1) if not answer_1 & ABORT and not answer_1 & YES_TO_ALL: self._expect_alert( ('Could not %s %s (%s). ' 'Do you want to continue?' % (self.operation_descr_verb, as_human_readable(nonexistent_file_2), self._NO_SUCH_FILE_MSG), YES | YES_TO_ALL | ABORT, YES), answer=answer_2) self._perform_on(nonexistent_file_1, nonexistent_file_2, existent_file) if not answer_1 & ABORT and not answer_2 & ABORT: expected_files = {'bar.txt'} else: expected_files = set() self._expect_files(expected_files)
def setUp(self): super().setUp() self._ui = StubUI(self) self._root = as_url('C:\\' if PLATFORM == 'Windows' else '/') self._src = join(self._root, 'src') self._dest = join(self._root, 'dest') self._a = join(self._root, 'src/a') self._a_txt = join(self._root, 'src/a.txt') self._b_txt = join(self._root, 'src/b.txt') self._fs = self.FileSystem({ self._src: { 'is_dir': True }, self._dest: { 'is_dir': True }, self._a: { 'is_dir': True }, self._a_txt: { 'is_dir': False }, self._b_txt: { 'is_dir': False }, })
def test_relative_path_subdir(self): src_file = join(self.src, 'test.txt') self._touch(src_file, '1234') subdir = join(self.src, 'subdir') self._makedirs(subdir, exist_ok=True) self._perform_on(src_file, dest_dir='subdir') self._expect_files({'test.txt'}, subdir) self._assert_file_contents_equal(join(subdir, 'test.txt'), '1234')
def test_rename_directory_case(self): container = join(self.dest, 'container') directory = join(container, 'a') self._makedirs(directory) self._perform_on( directory, src_dir=container, dest_dir=container, dest_name='A' ) self._expect_files({'A'}, in_dir=container)
def test_move_to_own_subdir(self): dir_ = join(self.src, 'dir') subdir = join(dir_, 'subdir') self._makedirs(subdir) self._expect_alert( ('You cannot %s a file to itself.' % self.operation_descr_verb, ), answer=OK) self._perform_on(dir_, dest_dir=subdir)
def test_single_file(self, dest_dir=None): if dest_dir is None: dest_dir = self.dest src_file = join(self.src, 'test.txt') self._touch(src_file, '1234') self._perform_on(src_file, dest_dir=dest_dir) self._expect_files({'test.txt'}, dest_dir) self._assert_file_contents_equal(join(dest_dir, 'test.txt'), '1234') return src_file
def test_dest_name(self, src_equals_dest=False, preserves_files=True): src_dir = self.dest if src_equals_dest else self.src foo = join(src_dir, 'foo') self._touch(foo, '1234') self._perform_on(foo, dest_name='bar') expected_files = {'bar'} if preserves_files and src_equals_dest: expected_files.add('foo') self._expect_files(expected_files) self._assert_file_contents_equal(join(self.dest, 'bar'), '1234') return foo
def test_symlink(self): symlink_source = join(self.src, 'symlink_source') self._touch(symlink_source) symlink = join(self.src, 'symlink') self._symlink(symlink_source, symlink) self._perform_on(symlink) self._expect_files({'symlink'}) symlink_dest = join(self.dest, 'symlink') self.assertTrue(self._islink(symlink_dest)) symlink_dest_source = self._readlink(symlink_dest) self.assertTrue(self._fs.samefile(symlink_source, symlink_dest_source)) return symlink
def test_nested_dir(self): parent_dir = join(self.src, 'parent_dir') nested_dir = join(parent_dir, 'nested_dir') text_file = join(nested_dir, 'file.txt') self._makedirs(nested_dir) self._touch(text_file) self._perform_on(parent_dir) self._expect_files({'parent_dir'}) self._expect_files({'nested_dir'}, join(self.dest, 'parent_dir')) self._expect_files({'file.txt'}, join(self.dest, 'parent_dir', 'nested_dir')) return parent_dir
def _prepare_move(self, src_url, dst_url, measure_size=False, use_rename=True, expected_st_dev=None): if expected_st_dev is None: expected_st_dev = {} src_path, dst_path = self._check_transfer_precnds(src_url, dst_url) if use_rename: src_stat = self.stat(src_path) dst_par_path = splitscheme(dirname(dst_url))[1] try: dst_par_dev = expected_st_dev[dst_par_path] except KeyError: dst_par_dev = self.stat(dst_par_path).st_dev if src_stat.st_dev == dst_par_dev: yield Task('Moving ' + basename(src_url), size=1, fn=self._rename, args=(src_url, dst_url)) return src_is_dir = self.is_dir(src_path) if src_is_dir: yield Task('Creating ' + basename(dst_url), fn=self.mkdir, args=(dst_path, )) dst_par_path = splitscheme(dirname(dst_url))[1] # Expect `dst_path` to inherit .st_dev from its parent: try: expected_st_dev[dst_path] = expected_st_dev[dst_par_path] except KeyError: expected_st_dev[dst_path] = self.stat(dst_par_path).st_dev for name in self.iterdir(src_path): try: yield from self._prepare_move(join(src_url, name), join(dst_url, name), measure_size, use_rename, expected_st_dev) except FileNotFoundError: pass yield DeleteIfEmpty(self, src_url) else: size = self.size_bytes(src_path) if measure_size else 0 # It's tempting to "yield copy" then "yield delete" here. But this # can lead to data loss: fman / the user may choose to ignore errors # in a particular subtask. (Eg. moving file1.txt failed but the user # wants to go on to move file2.txt.) If we ignored a failure in # "yield copy" but then execute the "yield delete", we would delete # a file that has not been copied! # To avoid this, we "copy and delete" as a single, atomic task: yield MoveByCopying(self, src_url, dst_url, size)
def test_move_to_self(self): a, b = join(self.dest, 'a'), join(self.dest, 'b') c = join(self.external_dir, 'c') dir_ = join(self.dest, 'dir') self._makedirs(dir_) files = [a, b, c] for file_ in files: self._touch(file_) # Expect alert only once: self._expect_alert( ('You cannot %s a file to itself.' % self.operation_descr_verb, ), answer=OK) self._perform_on(dir_, *files)
def setUp(self): super().setUp() self._fs = StubFS() self._progress_dialog = MockProgressDialog(self) self._tmp_dir = TemporaryDirectory() self._root = as_url(self._tmp_dir.name) # We need intermediate 'src-parent' for test_relative_path_parent_dir: self.src = join(self._root, 'src-parent', 'src') self._makedirs(self.src) self.dest = join(self._root, 'dest') self._makedirs(self.dest) self.external_dir = join(self._root, 'external-dir') self._makedirs(self.external_dir) # Create a dummy file to test that not _all_ files are copied from src: self._touch(join(self.src, 'dummy'))
def _merge_directory(self, src): for file_name in self._fs.iterdir(src): file_url = join(src, file_name) try: src_is_dir = self._fs.is_dir(file_url) except OSError: src_is_dir = False dst = self._get_dest_url(file_url) if src_is_dir: try: dst_is_dir = self._fs.is_dir(dst) except OSError: dst_is_dir = False if dst_is_dir: if not self._merge_directory(file_url): return False else: self._enqueue(self._prepare_transfer(file_url, dst)) else: if self._fs.exists(dst): should_overwrite = self._should_overwrite(dst) if should_overwrite == NO: continue elif should_overwrite == ABORT: return False else: assert should_overwrite == YES, \ should_overwrite self._enqueue(self._prepare_transfer(file_url, dst)) if self._does_postprocess_directory(): # Post-process the parent directories bottom-up. For Move, this # ensures that each directory is empty when post-processing. self._enqueue([self._postprocess_directory(src)]) return True
def test_error_only_one_file(self): nonexistent_file = join(self.src, 'foo.txt') file_path = as_human_readable(nonexistent_file) message = 'Could not %s %s (%s).' % \ (self.operation_descr_verb, file_path, self._NO_SUCH_FILE_MSG) self._expect_alert((message, OK, OK), answer=OK) self._perform_on(nonexistent_file)
def test_move_file_between_archives(self, operation=None, get_contents=None): if operation is None: operation = self._fs.move if get_contents is None: get_contents = self._pop_from_dir_dict src_path = 'ZipFileTest/Directory/Subdirectory/file 3.txt' expected_contents = self._get_zip_contents() src_contents = get_contents(expected_contents, src_path) with TemporaryDirectory() as dst_dir: dst_zip = os.path.join(dst_dir, 'dest.zip') # Give the Zip file some contents: dummy_txt = os.path.join(dst_dir, 'dummy.txt') dummy_contents = 'some contents' with open(dummy_txt, 'w') as f: f.write(dummy_contents) with ZipFile(dst_zip, 'w') as zip_file: zip_file.write(dummy_txt, 'dummy.txt') dst_url = join(as_url(dst_zip, 'zip://'), 'dest.txt') operation(self._url(src_path), dst_url) self.assertEqual(expected_contents, self._get_zip_contents()) self.assertEqual( { 'dummy.txt': dummy_contents, 'dest.txt': src_contents }, self._get_zip_contents(dst_zip))
def test_file_system_root(self): dest_path = as_human_readable(join(self._root, 'a.txt')) sel_start = dest_path.rindex(os.sep) + 1 self._expect_prompt( ('Move "a.txt" to', dest_path, sel_start, sel_start + 1), (dest_path, True)) self._check([self._a_txt], (self._root, 'a.txt'), dest_dir=self._root)
def test_into_subfolder(self): dest_path = as_human_readable(join(self._dest, 'a.txt')) sel_start = dest_path.rindex(os.sep) + 1 self._expect_prompt( ('Move "a.txt" to', dest_path, sel_start, sel_start + 1), ('a', True)) self._check([self._a_txt], (self._a, None))
def _prepare_copy(self, src_url, dst_url, measure_size=False): src_path, dst_path = self._check_transfer_precnds(src_url, dst_url) src_is_dir = self.is_dir(src_path) if src_is_dir: yield Task('Creating ' + basename(dst_url), fn=self.mkdir, args=(dst_path, )) for name in self.iterdir(src_path): try: yield from self._prepare_copy(join(src_url, name), join(dst_url, name), measure_size) except FileNotFoundError: pass else: size = self.size_bytes(src_path) if measure_size else 0 yield CopyFile(self, src_url, dst_url, size)
def getURL(url): if not "drives://" in url: files = list(iterdir(url)) if len(files) == 1: newUrl = join(url, files[0]) if is_dir(newUrl): url = getURL(newUrl) return url
def _get_dest_dir_url(self): try: splitscheme(self._dest_dir) except ValueError as not_a_url: is_url = False else: is_url = True return self._dest_dir if is_url else join(self._src_dir, self._dest_dir)
def test_overwrite_dir_skip_file(self): src_dir = join(self.src, 'dir') self._makedirs(src_dir) src_file = join(src_dir, 'test.txt') self._touch(src_file, 'src contents') dest_dir = join(self.dest, 'dir') self._makedirs(dest_dir) dest_file = join(dest_dir, 'test.txt') self._touch(dest_file, 'dest contents') self._expect_alert(('test.txt exists. Do you want to overwrite it?', YES | NO | YES_TO_ALL | NO_TO_ALL | ABORT, YES), answer=NO) self._perform_on(src_dir) self.assertTrue( self._fs.exists(src_file), "Source file was skipped and should not have been deleted.") self._assert_file_contents_equal(src_file, 'src contents') self._assert_file_contents_equal(dest_file, 'dest contents')
def test_different_scheme(self): dest_path = as_human_readable(join(self._dest, 'a.txt')) sel_start = dest_path.rindex(os.sep) + 1 self._expect_prompt( ('Move "a.txt" to', dest_path, sel_start, sel_start + 1), (dest_path, True)) src_url = 'zip:///dest.zip/a.txt' src_dir = dirname(src_url) self._check([src_url], (self._dest, 'a.txt'), src_dir=src_dir)
def test_overwrite_single_file(self): dest_url = join(self._dest, 'a.txt') self._fs._files[dest_url] = {'is_dir': False} dest_path = as_human_readable(dest_url) sel_start = dest_path.rindex(os.sep) + 1 self._expect_prompt( ('Move "a.txt" to', dest_path, sel_start, sel_start + 1), (dest_path, True)) self._check([self._a_txt], (self._dest, 'a.txt'))
def _get_dest_url(self, src_file): dest_name = self._dest_name or basename(src_file) if self._src_dir: try: rel_path = \ relpath(join(dirname(src_file), dest_name), self._src_dir) except ValueError as e: raise ValueError('Could not construct path. ' 'src_file: %r, dest_name: %r, src_dir: %r' % (src_file, dest_name, self._src_dir)) from e is_in_src_dir = not rel_path.startswith(pardir) if is_in_src_dir: try: splitscheme(self._dest_dir) except ValueError as no_scheme: return join(self._src_dir, self._dest_dir, rel_path) else: return join(self._dest_dir, rel_path) return join(self._dest_dir, dest_name)
def test_multiple_files_over_one(self): dest_url = join(self._dest, 'a.txt') self._fs._files[dest_url] = {'is_dir': False} dest_path = as_human_readable(dest_url) self._expect_prompt( ('Move 2 files to', as_human_readable(self._dest), 0, None), (dest_path, True)) self._expect_alert( ('You cannot move multiple files to a single file!', ), answer=OK) self._check([self._a_txt, self._b_txt], None)
def __call__(self): # get_chosen_files() returns the files selected by the user # (="red" files). If no files are selected, the file under the cursor # is returned: chosen_files = self.get_chosen_files() if chosen_files: # equivalent to `len(chosen_files) > 0` ndnam, okay = show_prompt("Directory Name?") # Check ndnam as well because it could be empty if ndnam and okay: numf = 0 newdirp = join(self.pane.get_path(), ndnam) if not self.isADir(newdirp): # Create directory and its parent directories: makedirs(newdirp) for filep in chosen_files: move(filep, join(newdirp, basename(filep))) numf += 1 show_alert('%d items were moved!' % numf) else: show_alert("No files or directories selected")