# Copyright (C) 2003-2007, Stefan Schwarzer
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# - Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# - Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# - Neither the name of the above author nor the names of the
# contributors to the software may be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# $Id: _mock_ftplib.py 689 2007-04-16 01:07:10Z schwa $
"""
This module implements a mock version of the standard library's
`ftplib.py` module. Some code is taken from there.
Not all functionality is implemented, only that what is used to
run the unit tests.
"""
import ftplib
import StringIO
DEBUG = 0
# Use a global dictionary of the form `{path: mock_file, ...}` to
# make "volatile" mock files accessible. This is used for testing
# the contents of a file after an `FTPHost.upload` call.
mock_files = {}
def content_of(path):
return mock_files[path].getvalue()
class MockFile(StringIO.StringIO):
"""
Mock class for the file objects _contained in_ `_FTPFile` objects
(not `_FTPFile` objects themselves!).
Unless `StringIO.StringIO` instances, `MockFile` objects can be
queried for their contents after they have been closed.
"""
def __init__(self, path, content=''):
global mock_files
mock_files[path] = self
StringIO.StringIO.__init__(self, content)
def getvalue(self):
if not self.closed:
return StringIO.StringIO.getvalue(self)
else:
return self._value_after_close
def close(self):
if not self.closed:
self._value_after_close = StringIO.StringIO.getvalue(self)
StringIO.StringIO.close(self)
class MockSocket(object):
"""
Mock class which is used to return something from
`MockSession.transfercmd`.
"""
def __init__(self, path, mock_file_content=''):
if DEBUG:
print 'File content: *%s*' % mock_file_content
self.file_path = path
self.mock_file_content = mock_file_content
def makefile(self, mode):
return MockFile(self.file_path, self.mock_file_content)
def close(self):
pass
class MockSession(object):
"""
Mock class which works like `ftplib.FTP` for the purpose of the
unit tests.
"""
# used by MockSession.cwd and MockSession.pwd
current_dir = '/home/sschwarzer'
# used by MockSession.dir
dir_contents = {
'/': """\
drwxr-xr-x 2 45854 200 512 May 4 2000 home""",
'/home': """\
drwxr-sr-x 2 45854 200 512 May 4 2000 sschwarzer
-rw-r--r-- 1 45854 200 4605 Jan 19 1970 older
-rw-r--r-- 1 45854 200 4605 Jan 19 2020 newer
lrwxrwxrwx 1 45854 200 21 Jan 19 2002 link -> sschwarzer/index.html
lrwxrwxrwx 1 45854 200 15 Jan 19 2002 bad_link -> python/bad_link""",
'/home/python': """\
lrwxrwxrwx 1 45854 200 7 Jan 19 2002 link_link -> ../link
lrwxrwxrwx 1 45854 200 14 Jan 19 2002 bad_link -> /home/bad_link""",
'/home/sschwarzer': """\
total 14
drwxr-sr-x 2 45854 200 512 May 4 2000 chemeng
drwxr-sr-x 2 45854 200 512 Jan 3 17:17 download
drwxr-sr-x 2 45854 200 512 Jul 30 17:14 image
-rw-r--r-- 1 45854 200 4604 Jan 19 23:11 index.html
drwxr-sr-x 2 45854 200 512 May 29 2000 os2
lrwxrwxrwx 2 45854 200 6 May 29 2000 osup -> ../os2
drwxr-sr-x 2 45854 200 512 May 25 2000 publications
drwxr-sr-x 2 45854 200 512 Jan 20 16:12 python
drwxr-sr-x 6 45854 200 512 Sep 20 1999 scios2""",
# = /home/dir with spaces
'.': """\
total 1
-rw-r--r-- 1 45854 200 4604 Jan 19 23:11 file with spaces""",
# fail when trying to write to this directory (the content isn't
# relevant)
'sschwarzer': "",
'/home/msformat': """\
10-23-01 03:25PM <DIR> WindowsXP
12-07-01 02:05PM <DIR> XPLaunch
07-17-00 02:08PM 12266720 abcd.exe
07-17-00 02:08PM 89264 O2KKeys.exe""",
'/home/msformat/XPLaunch': """\
10-23-01 03:25PM <DIR> WindowsXP
12-07-01 02:05PM <DIR> XPLaunch
12-07-01 02:05PM <DIR> empty
07-17-00 02:08PM 12266720 abcd.exe
07-17-00 02:08PM 89264 O2KKeys.exe""",
'/home/msformat/XPLaunch/empty': "total 0",
}
# file content to be used (indirectly) with transfercmd
mock_file_content = ''
def __init__(self, host='', user='', password=''):
self.closed = 0
# count successful `transfercmd` invocations to ensure that
# each has a corresponding `voidresp`
self._transfercmds = 0
def _remove_trailing_slash(self, path):
if path != '/' and path.endswith('/'):
path = path[:-1]
return path
def voidcmd(self, cmd):
if DEBUG:
print cmd
if cmd == 'STAT':
return 'MockSession server awaiting your commands ;-)'
elif cmd.startswith('TYPE '):
return
else:
raise ftplib.error_perm
def pwd(self):
return self.current_dir
def cwd(self, path):
path = self._remove_trailing_slash(path)
self.current_dir = path
def dir(self, path, callback=None):
"Provide a callback function with each line of a directory listing."
if DEBUG:
print 'dir: %s' % path
path = self._remove_trailing_slash(path)
if not self.dir_contents.has_key(path):
raise ftplib.error_perm
dir_lines = self.dir_contents[path].split('\n')
for line in dir_lines:
if callback is None:
print line
else:
callback(line)
def voidresp(self):
assert self._transfercmds == 1
self._transfercmds = self._transfercmds - 1
return '2xx'
def transfercmd(self, cmd):
"""
Return a `MockSocket` object whose `makefile` method will
return a mock file object.
"""
if DEBUG:
print cmd
# fail if attempting to read from/write to a directory
cmd, path = cmd.split()
path = self._remove_trailing_slash(path)
if self.dir_contents.has_key(path):
raise ftplib.error_perm
# fail if path isn't available (this name is hard-coded here
# and has to be used for the corresponding tests)
if (cmd, path) == ('RETR', 'notthere'):
raise ftplib.error_perm
assert self._transfercmds == 0
self._transfercmds = self._transfercmds + 1
return MockSocket(path, self.mock_file_content)
def close(self):
if not self.closed:
self.closed = 1
assert self._transfercmds == 0
syntax highlighted by Code2HTML, v. 0.9.1