Exemple #1
0
    def test_openDirectoryMemory(self):
        """openDirectory works on MemoryTransport."""
        transport = MemoryTransport()
        transport.put_bytes('hello', 'hello')
        sftp_server = TransportSFTPServer(AsyncTransport(transport))
        deferred = sftp_server.openDirectory('.')

        def check_directory(directory):
            with closing(directory):
                names = [entry[0] for entry in directory]
            self.assertEqual(['hello'], names)

        return deferred.addCallback(check_directory)
    def test_openDirectoryMemory(self):
        """openDirectory works on MemoryTransport."""
        transport = MemoryTransport()
        transport.put_bytes('hello', 'hello')
        sftp_server = TransportSFTPServer(AsyncTransport(transport))
        deferred = sftp_server.openDirectory('.')

        def check_directory(directory):
            with closing(directory):
                names = [entry[0] for entry in directory]
            self.assertEqual(['hello'], names)

        return deferred.addCallback(check_directory)
Exemple #3
0
 def setUp(self):
     TestCaseInTempDir.setUp(self)
     SFTPTestMixin.setUp(self)
     transport = AsyncTransport(
         FatLocalTransport(urlutils.local_path_to_url('.')))
     self.sftp_server = TransportSFTPServer(transport)
Exemple #4
0
class TestSFTPServer(TestCaseInTempDir, SFTPTestMixin):
    """Tests for `TransportSFTPServer` and `TransportSFTPFile`."""

    run_tests_with = AsynchronousDeferredRunTest

    def setUp(self):
        TestCaseInTempDir.setUp(self)
        SFTPTestMixin.setUp(self)
        transport = AsyncTransport(
            FatLocalTransport(urlutils.local_path_to_url('.')))
        self.sftp_server = TransportSFTPServer(transport)

    def test_serverSetAttrs(self):
        # setAttrs on the TransportSFTPServer doesn't do anything either.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        self.sftp_server.setAttrs(filename, {})

    def test_serverGetAttrs(self):
        # getAttrs on the TransportSFTPServer also returns a dictionary
        # consistent with the results of os.stat.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        stat_value = os.stat(filename)
        deferred = self.sftp_server.getAttrs(filename, False)
        return deferred.addCallback(self.checkAttrs, stat_value)

    def test_serverGetAttrsError(self):
        # Errors in getAttrs on the TransportSFTPServer are translated into
        # SFTPErrors.
        nonexistent_file = self.getPathSegment()
        deferred = self.sftp_server.getAttrs(nonexistent_file, False)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_removeFile(self):
        # removeFile removes the file.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.sftp_server.removeFile(filename)

        def assertFileRemoved(ignored):
            self.assertFalse(file_exists(filename))

        return deferred.addCallback(assertFileRemoved)

    def test_removeFileError(self):
        # Errors in removeFile are translated into SFTPErrors.
        filename = self.getPathSegment()
        deferred = self.sftp_server.removeFile(filename)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_removeFile_directory(self):
        # Errors in removeFile are translated into SFTPErrors.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename + '/',)])
        deferred = self.sftp_server.removeFile(filename)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_renameFile(self):
        # renameFile renames the file.
        orig_filename = self.getPathSegment()
        new_filename = self.getPathSegment()
        self.build_tree_contents([(orig_filename, 'bar')])
        deferred = self.sftp_server.renameFile(orig_filename, new_filename)

        def assertFileRenamed(ignored):
            self.assertFalse(file_exists(orig_filename))
            self.assertTrue(file_exists(new_filename))

        return deferred.addCallback(assertFileRenamed)

    def test_renameFileError(self):
        # Errors in renameFile are translated into SFTPErrors.
        orig_filename = self.getPathSegment()
        new_filename = self.getPathSegment()
        deferred = self.sftp_server.renameFile(orig_filename, new_filename)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_makeDirectory(self):
        # makeDirectory makes the directory.
        directory = self.getPathSegment()
        deferred = self.sftp_server.makeDirectory(
            directory, {'permissions': 0o777})

        def assertDirectoryExists(ignored):
            self.assertTrue(
                os.path.isdir(directory), '%r is not a directory' % directory)
            self.assertEqual(0o40777, os.stat(directory).st_mode)

        return deferred.addCallback(assertDirectoryExists)

    def test_makeDirectoryError(self):
        # Errors in makeDirectory are translated into SFTPErrors.
        nonexistent = self.getPathSegment()
        nonexistent_child = '%s/%s' % (nonexistent, self.getPathSegment())
        deferred = self.sftp_server.makeDirectory(
            nonexistent_child, {'permissions': 0o777})
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_removeDirectory(self):
        # removeDirectory removes the directory.
        directory = self.getPathSegment()
        os.mkdir(directory)
        deferred = self.sftp_server.removeDirectory(directory)

        def assertDirectoryRemoved(ignored):
            self.assertFalse(file_exists(directory))

        return deferred.addCallback(assertDirectoryRemoved)

    def test_removeDirectoryError(self):
        # Errors in removeDirectory are translated into SFTPErrors.
        directory = self.getPathSegment()
        deferred = self.sftp_server.removeDirectory(directory)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_gotVersion(self):
        # gotVersion returns an empty dictionary.
        extended = self.sftp_server.gotVersion('version', {})
        self.assertEqual({}, extended)

    def test_extendedRequest(self):
        # We don't support any extensions.
        self.assertRaises(
            NotImplementedError, self.sftp_server.extendedRequest,
            'foo', 'bar')

    def test_realPath(self):
        # realPath returns the absolute path of the file.
        src, dst = self.getPathSegment(), self.getPathSegment()
        os.symlink(src, dst)
        deferred = self.sftp_server.realPath(dst)
        return deferred.addCallback(self.assertEqual, os.path.abspath(src))

    def test_makeLink(self):
        # makeLink is not supported.
        self.assertRaises(
            NotImplementedError, self.sftp_server.makeLink,
            self.getPathSegment(), self.getPathSegment())

    def test_readLink(self):
        # readLink is not supported.
        self.assertRaises(
            NotImplementedError, self.sftp_server.readLink,
            self.getPathSegment())

    def test_openDirectory(self):
        # openDirectory returns an iterator that iterates over the contents of
        # the directory.
        parent_dir = self.getPathSegment()
        child_dir = self.getPathSegment()
        child_file = self.getPathSegment()
        self.build_tree([
            parent_dir + '/',
            '%s/%s/' % (parent_dir, child_dir),
            '%s/%s' % (parent_dir, child_file)])
        deferred = self.sftp_server.openDirectory(parent_dir)

        def check_entry(entries, filename):
            t = get_transport('.')
            stat = t.stat(urlutils.escape('%s/%s' % (parent_dir, filename)))
            named_entries = [
                entry for entry in entries if entry[0] == filename]
            self.assertEqual(1, len(named_entries))
            name, longname, attrs = named_entries[0]
            self.assertEqual(lsLine(name, stat), longname)
            self.assertEqual(self.sftp_server._translate_stat(stat), attrs)

        def check_open_directory(directory):
            entries = list(directory)
            directory.close()
            names = [entry[0] for entry in entries]
            self.assertEqual(set(names), set([child_dir, child_file]))
            check_entry(entries, child_dir)
            check_entry(entries, child_file)

        return deferred.addCallback(check_open_directory)

    def test_openDirectoryError(self):
        # Errors in openDirectory are translated into SFTPErrors.
        nonexistent = self.getPathSegment()
        deferred = self.sftp_server.openDirectory(nonexistent)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_openDirectoryMemory(self):
        """openDirectory works on MemoryTransport."""
        transport = MemoryTransport()
        transport.put_bytes('hello', 'hello')
        sftp_server = TransportSFTPServer(AsyncTransport(transport))
        deferred = sftp_server.openDirectory('.')

        def check_directory(directory):
            with closing(directory):
                names = [entry[0] for entry in directory]
            self.assertEqual(['hello'], names)

        return deferred.addCallback(check_directory)

    def test__format_directory_entries_with_MemoryStat(self):
        """format_directory_entries works with MemoryStat.

        MemoryStat lacks many fields, but format_directory_entries works
        around that.
        """
        t = MemoryTransport()
        stat_result = t.stat('.')
        entries = self.sftp_server._format_directory_entries(
            [stat_result], ['filename'])
        self.assertEqual(list(entries), [
            ('filename', 'drwxr-xr-x    0 0        0               0 '
             'Jan 01  1970 filename',
             {'atime': 0,
              'gid': 0,
              'mtime': 0,
              'permissions': 16877,
              'size': 0,
              'uid': 0})])
        self.assertIs(None, getattr(stat_result, 'st_mtime', None))

    def do_translation_test(self, exception, sftp_code, method_name=None):
        """Test that `exception` is translated into the correct SFTPError."""
        result = self.assertRaises(filetransfer.SFTPError,
            self.sftp_server.translateError,
            failure.Failure(exception), method_name)
        self.assertEqual(sftp_code, result.code)
        self.assertEqual(str(exception), result.message)

    def test_translatePermissionDenied(self):
        exception = bzr_errors.PermissionDenied(self.getPathSegment())
        self.do_translation_test(exception, filetransfer.FX_PERMISSION_DENIED)

    def test_translateTransportNotPossible(self):
        exception = bzr_errors.TransportNotPossible(self.getPathSegment())
        self.do_translation_test(exception, filetransfer.FX_PERMISSION_DENIED)

    def test_translateNoSuchFile(self):
        exception = bzr_errors.NoSuchFile(self.getPathSegment())
        self.do_translation_test(exception, filetransfer.FX_NO_SUCH_FILE)

    def test_translateFileExists(self):
        exception = bzr_errors.FileExists(self.getPathSegment())
        self.do_translation_test(
            exception, filetransfer.FX_FILE_ALREADY_EXISTS)

    def test_translateFileIsADirectory(self):
        exception = FileIsADirectory(self.getPathSegment())
        self.do_translation_test(
            exception, filetransfer.FX_FILE_IS_A_DIRECTORY)

    def test_translateDirectoryNotEmpty(self):
        exception = bzr_errors.DirectoryNotEmpty(self.getPathSegment())
        self.do_translation_test(
            exception, filetransfer.FX_FAILURE)

    def test_translateRandomError(self):
        # translateError re-raises unrecognized errors.
        exception = KeyboardInterrupt()
        result = self.assertRaises(KeyboardInterrupt,
            self.sftp_server.translateError,
            failure.Failure(exception), 'methodName')
        self.assertIs(result, exception)
Exemple #5
0
class TestSFTPFile(TestCaseInTempDir, SFTPTestMixin):
    """Tests for `TransportSFTPServer` and `TransportSFTPFile`."""

    run_tests_with = AsynchronousDeferredRunTest

    # This works around a clash between the TrialTestCase and the BzrTestCase.
    skip = None

    def setUp(self):
        TestCaseInTempDir.setUp(self)
        SFTPTestMixin.setUp(self)
        transport = AsyncTransport(
            FatLocalTransport(urlutils.local_path_to_url('.')))
        self._sftp_server = TransportSFTPServer(transport)

    def assertSFTPError(self, sftp_code, function, *args, **kwargs):
        """Assert that calling functions fails with `sftp_code`."""
        deferred = defer.maybeDeferred(function, *args, **kwargs)
        deferred = assert_fails_with(deferred, filetransfer.SFTPError)

        def check_sftp_code(exception):
            self.assertEqual(sftp_code, exception.code)
            return exception

        return deferred.addCallback(check_sftp_code)

    def openFile(self, path, flags, attrs):
        return self._sftp_server.openFile(path, flags, attrs)

    def test_openFileInNonexistingDirectory(self):
        # openFile fails with a no such file error if we try to open a file in
        # a directory that doesn't exist. The flags passed to openFile() do
        # not have any effect.
        return self.assertSFTPError(
            filetransfer.FX_NO_SUCH_FILE,
            self.openFile,
            '%s/%s' % (self.getPathSegment(), self.getPathSegment()), 0, {})

    def test_openFileInNonDirectory(self):
        # openFile fails with a no such file error if we try to open a file
        # that has another file as one of its "parents". The flags passed to
        # openFile() do not have any effect.
        nondirectory = self.getPathSegment()
        self.build_tree_contents([(nondirectory, 'content')])
        return self.assertSFTPError(
            filetransfer.FX_NO_SUCH_FILE,
            self.openFile,
            '%s/%s' % (nondirectory, self.getPathSegment()), 0, {})

    def test_createEmptyFile(self):
        # Opening a file with create flags and then closing it will create a
        # new, empty file.
        filename = self.getPathSegment()
        deferred = self.openFile(filename, filetransfer.FXF_CREAT, {})
        return deferred.addCallback(
            self._test_createEmptyFile_callback, filename)

    def _test_createEmptyFile_callback(self, handle, filename):
        deferred = handle.close()
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('', filename))

    def test_createFileWithData(self):
        # writeChunk writes data to the file.
        filename = self.getPathSegment()
        deferred = self.openFile(
            filename, filetransfer.FXF_CREAT | filetransfer.FXF_WRITE, {})
        return deferred.addCallback(
            self._test_createFileWithData_callback, filename)

    def _test_createFileWithData_callback(self, handle, filename):
        deferred = handle.writeChunk(0, 'bar')
        deferred.addCallback(lambda ignored: handle.close())
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('bar', filename))

    def test_writeChunkToFile(self):
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'contents')])
        deferred = self.openFile(filename, filetransfer.FXF_WRITE, {})
        return deferred.addCallback(
            self._test_writeChunkToFile_callback, filename)

    def _test_writeChunkToFile_callback(self, handle, filename):
        deferred = handle.writeChunk(1, 'qux')
        deferred.addCallback(lambda ignored: handle.close())
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('cquxents', filename))

    def test_writeTwoChunks(self):
        # We can write one chunk after another.
        filename = self.getPathSegment()
        deferred = self.openFile(
            filename, filetransfer.FXF_WRITE | filetransfer.FXF_TRUNC, {})

        def write_chunks(handle):
            deferred = handle.writeChunk(1, 'a')
            deferred.addCallback(lambda ignored: handle.writeChunk(2, 'a'))
            deferred.addCallback(lambda ignored: handle.close())

        deferred.addCallback(write_chunks)
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual(chr(0) + 'aa', filename))

    def test_writeChunkToNonexistentFile(self):
        # Writing a chunk of data to a non-existent file creates the file even
        # if the create flag is not set. NOTE: This behaviour is unspecified
        # in the SFTP drafts at
        # http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/
        filename = self.getPathSegment()
        deferred = self.openFile(filename, filetransfer.FXF_WRITE, {})
        return deferred.addCallback(
            self._test_writeChunkToNonexistentFile_callback, filename)

    def _test_writeChunkToNonexistentFile_callback(self, handle, filename):
        deferred = handle.writeChunk(1, 'qux')
        deferred.addCallback(lambda ignored: handle.close())
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual(chr(0) + 'qux', filename))

    def test_writeToReadOpenedFile(self):
        # writeChunk raises an error if we try to write to a file that has
        # been opened only for reading.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, filetransfer.FXF_READ, {})
        return deferred.addCallback(
            self._test_writeToReadOpenedFile_callback)

    def _test_writeToReadOpenedFile_callback(self, handle):
        return self.assertSFTPError(
            filetransfer.FX_PERMISSION_DENIED,
            handle.writeChunk, 0, 'new content')

    def test_overwriteFile(self):
        # writeChunk overwrites a file if write, create and trunk flags are
        # set.
        self.build_tree_contents([('foo', 'contents')])
        deferred = self.openFile(
            'foo', filetransfer.FXF_CREAT | filetransfer.FXF_TRUNC |
            filetransfer.FXF_WRITE, {})
        return deferred.addCallback(self._test_overwriteFile_callback)

    def _test_overwriteFile_callback(self, handle):
        deferred = handle.writeChunk(0, 'bar')
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('bar', 'foo'))

    def test_writeToAppendingFileIgnoresOffset(self):
        # If a file is opened with the 'append' flag, writeChunk ignores its
        # offset parameter.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, filetransfer.FXF_APPEND, {})
        return deferred.addCallback(
            self._test_writeToAppendingFileIgnoresOffset_cb, filename)

    def _test_writeToAppendingFileIgnoresOffset_cb(self, handle, filename):
        deferred = handle.writeChunk(0, 'baz')
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('barbaz', filename))

    def test_openAndCloseExistingFileLeavesUnchanged(self):
        # If we open a file with the 'create' flag and without the 'truncate'
        # flag, the file remains unchanged.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, filetransfer.FXF_CREAT, {})
        return deferred.addCallback(
            self._test_openAndCloseExistingFileUnchanged_cb, filename)

    def _test_openAndCloseExistingFileUnchanged_cb(self, handle, filename):
        deferred = handle.close()
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('bar', filename))

    def test_openAndCloseExistingFileTruncation(self):
        # If we open a file with the 'create' flag and the 'truncate' flag,
        # the file is reset to empty.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(
            filename, filetransfer.FXF_TRUNC | filetransfer.FXF_CREAT, {})
        return deferred.addCallback(
            self._test_openAndCloseExistingFileTruncation_cb, filename)

    def _test_openAndCloseExistingFileTruncation_cb(self, handle, filename):
        deferred = handle.close()
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('', filename))

    def test_writeChunkOnDirectory(self):
        # Errors in writeChunk are translated to SFTPErrors.
        directory = self.getPathSegment()
        os.mkdir(directory)
        deferred = self.openFile(directory, filetransfer.FXF_WRITE, {})
        deferred.addCallback(lambda handle: handle.writeChunk(0, 'bar'))
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_readChunk(self):
        # readChunk reads a chunk of data from the file.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(1, 2))
        return deferred.addCallback(self.assertEqual, 'ar')

    def test_readChunkPastEndOfFile(self):
        # readChunk returns the rest of the file if it is asked to read past
        # the end of the file.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(2, 10))
        return deferred.addCallback(self.assertEqual, 'r')

    def test_readChunkEOF(self):
        # readChunk returns the empty string if it encounters end-of-file
        # before reading any data.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(3, 10))
        return deferred.addCallback(self.assertEqual, '')

    def test_readChunkError(self):
        # Errors in readChunk are translated to SFTPErrors.
        filename = self.getPathSegment()
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(1, 2))
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_setAttrs(self):
        # setAttrs on TransportSFTPFile does nothing.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        return deferred.addCallback(lambda handle: handle.setAttrs({}))

    def test_getAttrs(self):
        # getAttrs on TransportSFTPFile returns a dictionary consistent
        # with the results of os.stat.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        stat_value = os.stat(filename)
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.getAttrs())
        return deferred.addCallback(self.checkAttrs, stat_value)

    def test_getAttrsError(self):
        # Errors in getAttrs on TransportSFTPFile are translated into
        # SFTPErrors.
        filename = self.getPathSegment()
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.getAttrs())
        return assert_fails_with(deferred, filetransfer.SFTPError)
 def setUp(self):
     TestCaseInTempDir.setUp(self)
     SFTPTestMixin.setUp(self)
     transport = AsyncTransport(
         FatLocalTransport(urlutils.local_path_to_url('.')))
     self.sftp_server = TransportSFTPServer(transport)
class TestSFTPServer(TestCaseInTempDir, SFTPTestMixin):
    """Tests for `TransportSFTPServer` and `TransportSFTPFile`."""

    run_tests_with = AsynchronousDeferredRunTest

    def setUp(self):
        TestCaseInTempDir.setUp(self)
        SFTPTestMixin.setUp(self)
        transport = AsyncTransport(
            FatLocalTransport(urlutils.local_path_to_url('.')))
        self.sftp_server = TransportSFTPServer(transport)

    def test_serverSetAttrs(self):
        # setAttrs on the TransportSFTPServer doesn't do anything either.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        self.sftp_server.setAttrs(filename, {})

    def test_serverGetAttrs(self):
        # getAttrs on the TransportSFTPServer also returns a dictionary
        # consistent with the results of os.stat.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        stat_value = os.stat(filename)
        deferred = self.sftp_server.getAttrs(filename, False)
        return deferred.addCallback(self.checkAttrs, stat_value)

    def test_serverGetAttrsError(self):
        # Errors in getAttrs on the TransportSFTPServer are translated into
        # SFTPErrors.
        nonexistent_file = self.getPathSegment()
        deferred = self.sftp_server.getAttrs(nonexistent_file, False)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_removeFile(self):
        # removeFile removes the file.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.sftp_server.removeFile(filename)

        def assertFileRemoved(ignored):
            self.assertFalse(file_exists(filename))

        return deferred.addCallback(assertFileRemoved)

    def test_removeFileError(self):
        # Errors in removeFile are translated into SFTPErrors.
        filename = self.getPathSegment()
        deferred = self.sftp_server.removeFile(filename)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_removeFile_directory(self):
        # Errors in removeFile are translated into SFTPErrors.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename + '/',)])
        deferred = self.sftp_server.removeFile(filename)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_renameFile(self):
        # renameFile renames the file.
        orig_filename = self.getPathSegment()
        new_filename = self.getPathSegment()
        self.build_tree_contents([(orig_filename, 'bar')])
        deferred = self.sftp_server.renameFile(orig_filename, new_filename)

        def assertFileRenamed(ignored):
            self.assertFalse(file_exists(orig_filename))
            self.assertTrue(file_exists(new_filename))

        return deferred.addCallback(assertFileRenamed)

    def test_renameFileError(self):
        # Errors in renameFile are translated into SFTPErrors.
        orig_filename = self.getPathSegment()
        new_filename = self.getPathSegment()
        deferred = self.sftp_server.renameFile(orig_filename, new_filename)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_makeDirectory(self):
        # makeDirectory makes the directory.
        directory = self.getPathSegment()
        deferred = self.sftp_server.makeDirectory(
            directory, {'permissions': 0777})

        def assertDirectoryExists(ignored):
            self.assertTrue(
                os.path.isdir(directory), '%r is not a directory' % directory)
            self.assertEqual(040777, os.stat(directory).st_mode)

        return deferred.addCallback(assertDirectoryExists)

    def test_makeDirectoryError(self):
        # Errors in makeDirectory are translated into SFTPErrors.
        nonexistent = self.getPathSegment()
        nonexistent_child = '%s/%s' % (nonexistent, self.getPathSegment())
        deferred = self.sftp_server.makeDirectory(
            nonexistent_child, {'permissions': 0777})
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_removeDirectory(self):
        # removeDirectory removes the directory.
        directory = self.getPathSegment()
        os.mkdir(directory)
        deferred = self.sftp_server.removeDirectory(directory)

        def assertDirectoryRemoved(ignored):
            self.assertFalse(file_exists(directory))

        return deferred.addCallback(assertDirectoryRemoved)

    def test_removeDirectoryError(self):
        # Errors in removeDirectory are translated into SFTPErrors.
        directory = self.getPathSegment()
        deferred = self.sftp_server.removeDirectory(directory)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_gotVersion(self):
        # gotVersion returns an empty dictionary.
        extended = self.sftp_server.gotVersion('version', {})
        self.assertEqual({}, extended)

    def test_extendedRequest(self):
        # We don't support any extensions.
        self.assertRaises(
            NotImplementedError, self.sftp_server.extendedRequest,
            'foo', 'bar')

    def test_realPath(self):
        # realPath returns the absolute path of the file.
        src, dst = self.getPathSegment(), self.getPathSegment()
        os.symlink(src, dst)
        deferred = self.sftp_server.realPath(dst)
        return deferred.addCallback(self.assertEqual, os.path.abspath(src))

    def test_makeLink(self):
        # makeLink is not supported.
        self.assertRaises(
            NotImplementedError, self.sftp_server.makeLink,
            self.getPathSegment(), self.getPathSegment())

    def test_readLink(self):
        # readLink is not supported.
        self.assertRaises(
            NotImplementedError, self.sftp_server.readLink,
            self.getPathSegment())

    def test_openDirectory(self):
        # openDirectory returns an iterator that iterates over the contents of
        # the directory.
        parent_dir = self.getPathSegment()
        child_dir = self.getPathSegment()
        child_file = self.getPathSegment()
        self.build_tree([
            parent_dir + '/',
            '%s/%s/' % (parent_dir, child_dir),
            '%s/%s' % (parent_dir, child_file)])
        deferred = self.sftp_server.openDirectory(parent_dir)

        def check_entry(entries, filename):
            t = get_transport('.')
            stat = t.stat(urlutils.escape('%s/%s' % (parent_dir, filename)))
            named_entries = [
                entry for entry in entries if entry[0] == filename]
            self.assertEqual(1, len(named_entries))
            name, longname, attrs = named_entries[0]
            self.assertEqual(lsLine(name, stat), longname)
            self.assertEqual(self.sftp_server._translate_stat(stat), attrs)

        def check_open_directory(directory):
            entries = list(directory)
            directory.close()
            names = [entry[0] for entry in entries]
            self.assertEqual(set(names), set([child_dir, child_file]))
            check_entry(entries, child_dir)
            check_entry(entries, child_file)

        return deferred.addCallback(check_open_directory)

    def test_openDirectoryError(self):
        # Errors in openDirectory are translated into SFTPErrors.
        nonexistent = self.getPathSegment()
        deferred = self.sftp_server.openDirectory(nonexistent)
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_openDirectoryMemory(self):
        """openDirectory works on MemoryTransport."""
        transport = MemoryTransport()
        transport.put_bytes('hello', 'hello')
        sftp_server = TransportSFTPServer(AsyncTransport(transport))
        deferred = sftp_server.openDirectory('.')

        def check_directory(directory):
            with closing(directory):
                names = [entry[0] for entry in directory]
            self.assertEqual(['hello'], names)

        return deferred.addCallback(check_directory)

    def test__format_directory_entries_with_MemoryStat(self):
        """format_directory_entries works with MemoryStat.

        MemoryStat lacks many fields, but format_directory_entries works
        around that.
        """
        t = MemoryTransport()
        stat_result = t.stat('.')
        entries = self.sftp_server._format_directory_entries(
            [stat_result], ['filename'])
        self.assertEqual(list(entries), [
            ('filename', 'drwxr-xr-x    0 0        0               0 '
             'Jan 01  1970 filename',
             {'atime': 0,
              'gid': 0,
              'mtime': 0,
              'permissions': 16877,
              'size': 0,
              'uid': 0})])
        self.assertIs(None, getattr(stat_result, 'st_mtime', None))

    def do_translation_test(self, exception, sftp_code, method_name=None):
        """Test that `exception` is translated into the correct SFTPError."""
        result = self.assertRaises(filetransfer.SFTPError,
            self.sftp_server.translateError,
            failure.Failure(exception), method_name)
        self.assertEqual(sftp_code, result.code)
        self.assertEqual(str(exception), result.message)

    def test_translatePermissionDenied(self):
        exception = bzr_errors.PermissionDenied(self.getPathSegment())
        self.do_translation_test(exception, filetransfer.FX_PERMISSION_DENIED)

    def test_translateTransportNotPossible(self):
        exception = bzr_errors.TransportNotPossible(self.getPathSegment())
        self.do_translation_test(exception, filetransfer.FX_PERMISSION_DENIED)

    def test_translateNoSuchFile(self):
        exception = bzr_errors.NoSuchFile(self.getPathSegment())
        self.do_translation_test(exception, filetransfer.FX_NO_SUCH_FILE)

    def test_translateFileExists(self):
        exception = bzr_errors.FileExists(self.getPathSegment())
        self.do_translation_test(
            exception, filetransfer.FX_FILE_ALREADY_EXISTS)

    def test_translateFileIsADirectory(self):
        exception = FileIsADirectory(self.getPathSegment())
        self.do_translation_test(
            exception, filetransfer.FX_FILE_IS_A_DIRECTORY)

    def test_translateDirectoryNotEmpty(self):
        exception = bzr_errors.DirectoryNotEmpty(self.getPathSegment())
        self.do_translation_test(
            exception, filetransfer.FX_FAILURE)

    def test_translateRandomError(self):
        # translateError re-raises unrecognized errors.
        exception = KeyboardInterrupt()
        result = self.assertRaises(KeyboardInterrupt,
            self.sftp_server.translateError,
            failure.Failure(exception), 'methodName')
        self.assertIs(result, exception)
class TestSFTPFile(TestCaseInTempDir, SFTPTestMixin):
    """Tests for `TransportSFTPServer` and `TransportSFTPFile`."""

    run_tests_with = AsynchronousDeferredRunTest

    # This works around a clash between the TrialTestCase and the BzrTestCase.
    skip = None

    def setUp(self):
        TestCaseInTempDir.setUp(self)
        SFTPTestMixin.setUp(self)
        transport = AsyncTransport(
            FatLocalTransport(urlutils.local_path_to_url('.')))
        self._sftp_server = TransportSFTPServer(transport)

    def assertSFTPError(self, sftp_code, function, *args, **kwargs):
        """Assert that calling functions fails with `sftp_code`."""
        deferred = defer.maybeDeferred(function, *args, **kwargs)
        deferred = assert_fails_with(deferred, filetransfer.SFTPError)

        def check_sftp_code(exception):
            self.assertEqual(sftp_code, exception.code)
            return exception

        return deferred.addCallback(check_sftp_code)

    def openFile(self, path, flags, attrs):
        return self._sftp_server.openFile(path, flags, attrs)

    def test_openFileInNonexistingDirectory(self):
        # openFile fails with a no such file error if we try to open a file in
        # a directory that doesn't exist. The flags passed to openFile() do
        # not have any effect.
        return self.assertSFTPError(
            filetransfer.FX_NO_SUCH_FILE,
            self.openFile,
            '%s/%s' % (self.getPathSegment(), self.getPathSegment()), 0, {})

    def test_openFileInNonDirectory(self):
        # openFile fails with a no such file error if we try to open a file
        # that has another file as one of its "parents". The flags passed to
        # openFile() do not have any effect.
        nondirectory = self.getPathSegment()
        self.build_tree_contents([(nondirectory, 'content')])
        return self.assertSFTPError(
            filetransfer.FX_NO_SUCH_FILE,
            self.openFile,
            '%s/%s' % (nondirectory, self.getPathSegment()), 0, {})

    def test_createEmptyFile(self):
        # Opening a file with create flags and then closing it will create a
        # new, empty file.
        filename = self.getPathSegment()
        deferred = self.openFile(filename, filetransfer.FXF_CREAT, {})
        return deferred.addCallback(
            self._test_createEmptyFile_callback, filename)

    def _test_createEmptyFile_callback(self, handle, filename):
        deferred = handle.close()
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('', filename))

    def test_createFileWithData(self):
        # writeChunk writes data to the file.
        filename = self.getPathSegment()
        deferred = self.openFile(
            filename, filetransfer.FXF_CREAT | filetransfer.FXF_WRITE, {})
        return deferred.addCallback(
            self._test_createFileWithData_callback, filename)

    def _test_createFileWithData_callback(self, handle, filename):
        deferred = handle.writeChunk(0, 'bar')
        deferred.addCallback(lambda ignored: handle.close())
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('bar', filename))

    def test_writeChunkToFile(self):
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'contents')])
        deferred = self.openFile(filename, filetransfer.FXF_WRITE, {})
        return deferred.addCallback(
            self._test_writeChunkToFile_callback, filename)

    def _test_writeChunkToFile_callback(self, handle, filename):
        deferred = handle.writeChunk(1, 'qux')
        deferred.addCallback(lambda ignored: handle.close())
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('cquxents', filename))

    def test_writeTwoChunks(self):
        # We can write one chunk after another.
        filename = self.getPathSegment()
        deferred = self.openFile(
            filename, filetransfer.FXF_WRITE | filetransfer.FXF_TRUNC, {})

        def write_chunks(handle):
            deferred = handle.writeChunk(1, 'a')
            deferred.addCallback(lambda ignored: handle.writeChunk(2, 'a'))
            deferred.addCallback(lambda ignored: handle.close())

        deferred.addCallback(write_chunks)
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual(chr(0) + 'aa', filename))

    def test_writeChunkToNonexistentFile(self):
        # Writing a chunk of data to a non-existent file creates the file even
        # if the create flag is not set. NOTE: This behaviour is unspecified
        # in the SFTP drafts at
        # http://tools.ietf.org/wg/secsh/draft-ietf-secsh-filexfer/
        filename = self.getPathSegment()
        deferred = self.openFile(filename, filetransfer.FXF_WRITE, {})
        return deferred.addCallback(
            self._test_writeChunkToNonexistentFile_callback, filename)

    def _test_writeChunkToNonexistentFile_callback(self, handle, filename):
        deferred = handle.writeChunk(1, 'qux')
        deferred.addCallback(lambda ignored: handle.close())
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual(chr(0) + 'qux', filename))

    def test_writeToReadOpenedFile(self):
        # writeChunk raises an error if we try to write to a file that has
        # been opened only for reading.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, filetransfer.FXF_READ, {})
        return deferred.addCallback(
            self._test_writeToReadOpenedFile_callback)

    def _test_writeToReadOpenedFile_callback(self, handle):
        return self.assertSFTPError(
            filetransfer.FX_PERMISSION_DENIED,
            handle.writeChunk, 0, 'new content')

    def test_overwriteFile(self):
        # writeChunk overwrites a file if write, create and trunk flags are
        # set.
        self.build_tree_contents([('foo', 'contents')])
        deferred = self.openFile(
            'foo', filetransfer.FXF_CREAT | filetransfer.FXF_TRUNC |
            filetransfer.FXF_WRITE, {})
        return deferred.addCallback(self._test_overwriteFile_callback)

    def _test_overwriteFile_callback(self, handle):
        deferred = handle.writeChunk(0, 'bar')
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('bar', 'foo'))

    def test_writeToAppendingFileIgnoresOffset(self):
        # If a file is opened with the 'append' flag, writeChunk ignores its
        # offset parameter.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, filetransfer.FXF_APPEND, {})
        return deferred.addCallback(
            self._test_writeToAppendingFileIgnoresOffset_cb, filename)

    def _test_writeToAppendingFileIgnoresOffset_cb(self, handle, filename):
        deferred = handle.writeChunk(0, 'baz')
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('barbaz', filename))

    def test_openAndCloseExistingFileLeavesUnchanged(self):
        # If we open a file with the 'create' flag and without the 'truncate'
        # flag, the file remains unchanged.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, filetransfer.FXF_CREAT, {})
        return deferred.addCallback(
            self._test_openAndCloseExistingFileUnchanged_cb, filename)

    def _test_openAndCloseExistingFileUnchanged_cb(self, handle, filename):
        deferred = handle.close()
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('bar', filename))

    def test_openAndCloseExistingFileTruncation(self):
        # If we open a file with the 'create' flag and the 'truncate' flag,
        # the file is reset to empty.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(
            filename, filetransfer.FXF_TRUNC | filetransfer.FXF_CREAT, {})
        return deferred.addCallback(
            self._test_openAndCloseExistingFileTruncation_cb, filename)

    def _test_openAndCloseExistingFileTruncation_cb(self, handle, filename):
        deferred = handle.close()
        return deferred.addCallback(
            lambda ignored: self.assertFileEqual('', filename))

    def test_writeChunkOnDirectory(self):
        # Errors in writeChunk are translated to SFTPErrors.
        directory = self.getPathSegment()
        os.mkdir(directory)
        deferred = self.openFile(directory, filetransfer.FXF_WRITE, {})
        deferred.addCallback(lambda handle: handle.writeChunk(0, 'bar'))
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_readChunk(self):
        # readChunk reads a chunk of data from the file.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(1, 2))
        return deferred.addCallback(self.assertEqual, 'ar')

    def test_readChunkPastEndOfFile(self):
        # readChunk returns the rest of the file if it is asked to read past
        # the end of the file.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(2, 10))
        return deferred.addCallback(self.assertEqual, 'r')

    def test_readChunkEOF(self):
        # readChunk returns the empty string if it encounters end-of-file
        # before reading any data.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(3, 10))
        return deferred.addCallback(self.assertEqual, '')

    def test_readChunkError(self):
        # Errors in readChunk are translated to SFTPErrors.
        filename = self.getPathSegment()
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.readChunk(1, 2))
        return assert_fails_with(deferred, filetransfer.SFTPError)

    def test_setAttrs(self):
        # setAttrs on TransportSFTPFile does nothing.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        deferred = self.openFile(filename, 0, {})
        return deferred.addCallback(lambda handle: handle.setAttrs({}))

    def test_getAttrs(self):
        # getAttrs on TransportSFTPFile returns a dictionary consistent
        # with the results of os.stat.
        filename = self.getPathSegment()
        self.build_tree_contents([(filename, 'bar')])
        stat_value = os.stat(filename)
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.getAttrs())
        return deferred.addCallback(self.checkAttrs, stat_value)

    def test_getAttrsError(self):
        # Errors in getAttrs on TransportSFTPFile are translated into
        # SFTPErrors.
        filename = self.getPathSegment()
        deferred = self.openFile(filename, 0, {})
        deferred.addCallback(lambda handle: handle.getAttrs())
        return assert_fails_with(deferred, filetransfer.SFTPError)