summaryrefslogtreecommitdiffstats
path: root/Tools/Scripts/webkitpy/common/system
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/Scripts/webkitpy/common/system')
-rw-r--r--Tools/Scripts/webkitpy/common/system/directoryfileset.py14
-rw-r--r--Tools/Scripts/webkitpy/common/system/fileset.py6
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem.py16
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_mock.py71
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_unittest.py13
-rw-r--r--Tools/Scripts/webkitpy/common/system/logutils.py2
-rw-r--r--Tools/Scripts/webkitpy/common/system/logutils_unittest.py2
-rw-r--r--Tools/Scripts/webkitpy/common/system/ospath.py8
-rw-r--r--Tools/Scripts/webkitpy/common/system/stack_utils.py67
-rw-r--r--Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py76
-rw-r--r--Tools/Scripts/webkitpy/common/system/urlfetcher.py55
-rw-r--r--Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py46
-rw-r--r--Tools/Scripts/webkitpy/common/system/zip_mock.py55
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset.py10
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset_mock.py51
-rw-r--r--Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py6
16 files changed, 448 insertions, 50 deletions
diff --git a/Tools/Scripts/webkitpy/common/system/directoryfileset.py b/Tools/Scripts/webkitpy/common/system/directoryfileset.py
index 11acaf4..a5cab0e 100644
--- a/Tools/Scripts/webkitpy/common/system/directoryfileset.py
+++ b/Tools/Scripts/webkitpy/common/system/directoryfileset.py
@@ -21,14 +21,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-from __future__ import with_statement
-
-import os
-import shutil
-
from webkitpy.common.system.fileset import FileSetFileHandle
from webkitpy.common.system.filesystem import FileSystem
-import webkitpy.common.system.ospath as ospath
class DirectoryFileSet(object):
@@ -36,8 +30,8 @@ class DirectoryFileSet(object):
def __init__(self, path, filesystem=None):
self._path = path
self._filesystem = filesystem or FileSystem()
- if not self._path.endswith(os.path.sep):
- self._path += os.path.sep
+ if not self._path.endswith(self._filesystem.sep):
+ self._path += self._filesystem.sep
def _full_path(self, filename):
assert self._is_under(self._path, filename)
@@ -52,7 +46,7 @@ class DirectoryFileSet(object):
return self._filesystem.files_under(self._path)
def _is_under(self, dir, filename):
- return bool(ospath.relpath(self._filesystem.join(dir, filename), dir))
+ return bool(self._filesystem.relpath(self._filesystem.join(dir, filename), dir))
def open(self, filename):
return FileSetFileHandle(self, filename, self._filesystem)
@@ -69,7 +63,7 @@ class DirectoryFileSet(object):
dest = self._filesystem.join(path, filename)
# As filename may have slashes in it, we must ensure that the same
# directory hierarchy exists at the output path.
- self._filesystem.maybe_make_directory(os.path.split(dest)[0])
+ self._filesystem.maybe_make_directory(self._filesystem.dirname(dest))
self._filesystem.copyfile(src, dest)
def delete(self, filename):
diff --git a/Tools/Scripts/webkitpy/common/system/fileset.py b/Tools/Scripts/webkitpy/common/system/fileset.py
index 22f7c4d..598c1c5 100644
--- a/Tools/Scripts/webkitpy/common/system/fileset.py
+++ b/Tools/Scripts/webkitpy/common/system/fileset.py
@@ -22,7 +22,6 @@
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
from __future__ import with_statement
-import os
from webkitpy.common.system.filesystem import FileSystem
@@ -38,6 +37,9 @@ class FileSetFileHandle(object):
def __str__(self):
return "%s:%s" % (self._fileset, self._filename)
+ def close(self):
+ pass
+
def contents(self):
if self._contents is None:
self._contents = self._fileset.read(self._filename)
@@ -61,4 +63,4 @@ class FileSetFileHandle(object):
return self._filename
def splitext(self):
- return os.path.splitext(self.name())
+ return self._filesystem.splitext(self.name())
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem.py b/Tools/Scripts/webkitpy/common/system/filesystem.py
index 05513a9..b876807 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem.py
@@ -39,13 +39,20 @@ import shutil
import tempfile
import time
+from webkitpy.common.system import ospath
+
class FileSystem(object):
"""FileSystem interface for webkitpy.
Unless otherwise noted, all paths are allowed to be either absolute
or relative."""
def __init__(self):
- self.sep = os.sep
+ self._sep = os.sep
+
+ def _get_sep(self):
+ return self._sep
+
+ sep = property(_get_sep, doc="pathname separator")
def abspath(self, path):
return os.path.abspath(path)
@@ -165,8 +172,8 @@ class FileSystem(object):
if e.errno != errno.EEXIST:
raise
- def move(self, src, dest):
- shutil.move(src, dest)
+ def move(self, source, destination):
+ shutil.move(source, destination)
def mtime(self, path):
return os.stat(path).st_mtime
@@ -200,6 +207,9 @@ class FileSystem(object):
with codecs.open(path, 'r', 'utf8') as f:
return f.read()
+ def relpath(self, path, start='.'):
+ return ospath.relpath(path, start)
+
class _WindowsError(exceptions.OSError):
"""Fake exception for Linux and Mac."""
pass
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
index 0004944..aa79a8c 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py
@@ -28,9 +28,11 @@
import errno
import os
-import path
import re
+from webkitpy.common.system import path
+from webkitpy.common.system import ospath
+
class MockFileSystem(object):
def __init__(self, files=None):
@@ -44,17 +46,23 @@ class MockFileSystem(object):
"""
self.files = files or {}
self.written_files = {}
- self.sep = '/'
+ self._sep = '/'
self.current_tmpno = 0
+ def _get_sep(self):
+ return self._sep
+
+ sep = property(_get_sep, doc="pathname separator")
+
def _raise_not_found(self, path):
raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT))
def _split(self, path):
- idx = path.rfind('/')
- return (path[0:idx], path[idx + 1:])
+ return path.rsplit(self.sep, 1)
def abspath(self, path):
+ if path.endswith(self.sep):
+ return path[:-1]
return path
def basename(self, path):
@@ -69,6 +77,7 @@ class MockFileSystem(object):
raise IOError(errno.EISDIR, destination, os.strerror(errno.ISDIR))
self.files[destination] = self.files[source]
+ self.written_files[destination] = self.files[source]
def dirname(self, path):
return self._split(path)[0]
@@ -90,10 +99,10 @@ class MockFileSystem(object):
if self.basename(path) in dirs_to_skip:
return []
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
- dir_substrings = ['/' + d + '/' for d in dirs_to_skip]
+ dir_substrings = [self.sep + d + self.sep for d in dirs_to_skip]
for filename in self.files:
if not filename.startswith(path):
continue
@@ -117,7 +126,7 @@ class MockFileSystem(object):
return [f for f in self.files if f == path]
def isabs(self, path):
- return path.startswith('/')
+ return path.startswith(self.sep)
def isfile(self, path):
return path in self.files and self.files[path] is not None
@@ -125,8 +134,8 @@ class MockFileSystem(object):
def isdir(self, path):
if path in self.files:
return False
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
# We need to use a copy of the keys here in order to avoid switching
# to a different thread and potentially modifying the dict in
@@ -135,22 +144,24 @@ class MockFileSystem(object):
return any(f.startswith(path) for f in files)
def join(self, *comps):
- return re.sub(re.escape(os.path.sep), '/', os.path.join(*comps))
+ # FIXME: might want tests for this and/or a better comment about how
+ # it works.
+ return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps))
def listdir(self, path):
if not self.isdir(path):
raise OSError("%s is not a directory" % path)
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
dirs = []
files = []
for f in self.files:
if self.exists(f) and f.startswith(path):
remaining = f[len(path):]
- if '/' in remaining:
- dir = remaining[:remaining.index('/')]
+ if self.sep in remaining:
+ dir = remaining[:remaining.index(self.sep)]
if not dir in dirs:
dirs.append(dir)
else:
@@ -164,7 +175,7 @@ class MockFileSystem(object):
def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs):
if dir is None:
- dir = '/__im_tmp'
+ dir = self.sep + '__im_tmp'
curno = self.current_tmpno
self.current_tmpno += 1
return self.join(dir, "%s_%u_%s" % (prefix, curno, suffix))
@@ -196,24 +207,26 @@ class MockFileSystem(object):
# FIXME: Implement such that subsequent calls to isdir() work?
pass
- def move(self, src, dst):
- if self.files[src] is None:
- self._raise_not_found(src)
- self.files[dst] = self.files[src]
- self.files[src] = None
+ def move(self, source, destination):
+ if self.files[source] is None:
+ self._raise_not_found(source)
+ self.files[destination] = self.files[source]
+ self.written_files[destination] = self.files[destination]
+ self.files[source] = None
+ self.written_files[source] = None
def normpath(self, path):
return path
- def open_binary_tempfile(self, suffix):
+ def open_binary_tempfile(self, suffix=''):
path = self._mktemp(suffix)
- return WritableFileObject(self, path), path
+ return (WritableFileObject(self, path), path)
def open_text_file_for_writing(self, path, append=False):
return WritableFileObject(self, path, append)
def read_text_file(self, path):
- return self.read_binary_file(path)
+ return self.read_binary_file(path).decode('utf-8')
def read_binary_file(self, path):
# Intentionally raises KeyError if we don't recognize the path.
@@ -221,14 +234,18 @@ class MockFileSystem(object):
self._raise_not_found(path)
return self.files[path]
+ def relpath(self, path, start='.'):
+ return ospath.relpath(path, start, self.abspath, self.sep)
+
def remove(self, path):
if self.files[path] is None:
self._raise_not_found(path)
self.files[path] = None
+ self.written_files[path] = None
def rmtree(self, path):
- if not path.endswith('/'):
- path += '/'
+ if not path.endswith(self.sep):
+ path += self.sep
for f in self.files:
if f.startswith(path):
@@ -241,7 +258,7 @@ class MockFileSystem(object):
return (path[0:idx], path[idx:])
def write_text_file(self, path, contents):
- return self.write_binary_file(path, contents)
+ return self.write_binary_file(path, contents.encode('utf-8'))
def write_binary_file(self, path, contents):
self.files[path] = contents
diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py b/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
index 267ca13..8455d72 100644
--- a/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/filesystem_unittest.py
@@ -167,6 +167,19 @@ class FileSystemTest(unittest.TestCase):
self.assertTrue(fs.remove('filename', remove_with_exception))
self.assertEquals(-1, FileSystemTest._remove_failures)
+ def test_sep(self):
+ fs = FileSystem()
+
+ self.assertEquals(fs.sep, os.sep)
+ self.assertEquals(fs.join("foo", "bar"),
+ os.path.join("foo", "bar"))
+
+ def test_sep__is_readonly(self):
+ def assign_sep():
+ fs.sep = ' '
+ fs = FileSystem()
+ self.assertRaises(AttributeError, assign_sep)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/system/logutils.py b/Tools/Scripts/webkitpy/common/system/logutils.py
index cd4e60f..eef4636 100644
--- a/Tools/Scripts/webkitpy/common/system/logutils.py
+++ b/Tools/Scripts/webkitpy/common/system/logutils.py
@@ -83,7 +83,7 @@ def get_logger(path):
Sample usage:
- import webkitpy.common.system.logutils as logutils
+ from webkitpy.common.system import logutils
_log = logutils.get_logger(__file__)
diff --git a/Tools/Scripts/webkitpy/common/system/logutils_unittest.py b/Tools/Scripts/webkitpy/common/system/logutils_unittest.py
index b77c284..f1b494d 100644
--- a/Tools/Scripts/webkitpy/common/system/logutils_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/logutils_unittest.py
@@ -28,7 +28,7 @@ import unittest
from webkitpy.common.system.logtesting import LogTesting
from webkitpy.common.system.logtesting import TestLogStream
-import webkitpy.common.system.logutils as logutils
+from webkitpy.common.system import logutils
class GetLoggerTest(unittest.TestCase):
diff --git a/Tools/Scripts/webkitpy/common/system/ospath.py b/Tools/Scripts/webkitpy/common/system/ospath.py
index aed7a3d..2504645 100644
--- a/Tools/Scripts/webkitpy/common/system/ospath.py
+++ b/Tools/Scripts/webkitpy/common/system/ospath.py
@@ -32,7 +32,7 @@ import os
#
# It should behave essentially the same as os.path.relpath(), except for
# returning None on paths not contained in abs_start_path.
-def relpath(path, start_path, os_path_abspath=None):
+def relpath(path, start_path, os_path_abspath=None, sep=None):
"""Return a path relative to the given start path, or None.
Returns None if the path is not contained in the directory start_path.
@@ -44,10 +44,12 @@ def relpath(path, start_path, os_path_abspath=None):
os_path_abspath: A replacement function for unit testing. This
function should strip trailing slashes just like
os.path.abspath(). Defaults to os.path.abspath.
+ sep: Path separator. Defaults to os.path.sep
"""
if os_path_abspath is None:
os_path_abspath = os.path.abspath
+ sep = sep or os.sep
# Since os_path_abspath() calls os.path.normpath()--
#
@@ -67,11 +69,11 @@ def relpath(path, start_path, os_path_abspath=None):
if not rel_path:
# Then the paths are the same.
pass
- elif rel_path[0] == os.sep:
+ elif rel_path[0] == sep:
# It is probably sufficient to remove just the first character
# since os.path.normpath() collapses separators, but we use
# lstrip() just to be sure.
- rel_path = rel_path.lstrip(os.sep)
+ rel_path = rel_path.lstrip(sep)
else:
# We are in the case typified by the following example:
#
diff --git a/Tools/Scripts/webkitpy/common/system/stack_utils.py b/Tools/Scripts/webkitpy/common/system/stack_utils.py
new file mode 100644
index 0000000..a343807
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/stack_utils.py
@@ -0,0 +1,67 @@
+# Copyright (C) 2011 Google Inc. 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 Google Inc. nor the names of its
+# contributors 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 COPYRIGHT
+# OWNER 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.
+
+"""Simple routines for logging, obtaining thread stack information."""
+
+import sys
+import traceback
+
+
+def log_thread_state(logger, name, thread_id, msg=''):
+ """Log information about the given thread state."""
+ stack = _find_thread_stack(thread_id)
+ assert(stack is not None)
+ logger("")
+ logger("%s (tid %d) %s" % (name, thread_id, msg))
+ _log_stack(logger, stack)
+ logger("")
+
+
+def _find_thread_stack(thread_id):
+ """Returns a stack object that can be used to dump a stack trace for
+ the given thread id (or None if the id is not found)."""
+ for tid, stack in sys._current_frames().items():
+ if tid == thread_id:
+ return stack
+ return None
+
+
+def _log_stack(logger, stack):
+ """Log a stack trace to the logger callback."""
+ for filename, lineno, name, line in traceback.extract_stack(stack):
+ logger('File: "%s", line %d, in %s' % (filename, lineno, name))
+ if line:
+ logger(' %s' % line.strip())
+
+
+def log_traceback(logger, tb):
+ stack = traceback.extract_tb(tb)
+ for frame_str in traceback.format_list(stack):
+ for line in frame_str.split('\n'):
+ if line:
+ logger(" %s" % line)
diff --git a/Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py b/Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py
new file mode 100644
index 0000000..b21319f
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/stack_utils_unittest.py
@@ -0,0 +1,76 @@
+# Copyright (C) 2011 Google Inc. 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 Google Inc. nor the names of its
+# contributors 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 COPYRIGHT
+# OWNER 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.
+
+import sys
+import unittest
+
+from webkitpy.common.system import outputcapture
+from webkitpy.common.system import stack_utils
+
+
+def current_thread_id():
+ thread_id, _ = sys._current_frames().items()[0]
+ return thread_id
+
+
+class Test(unittest.TestCase):
+ def test_find_thread_stack_found(self):
+ thread_id = current_thread_id()
+ found_stack = stack_utils._find_thread_stack(thread_id)
+ self.assertNotEqual(found_stack, None)
+
+ def test_find_thread_stack_not_found(self):
+ found_stack = stack_utils._find_thread_stack(0)
+ self.assertEqual(found_stack, None)
+
+ def test_log_thread_state(self):
+ msgs = []
+
+ def logger(msg):
+ msgs.append(msg)
+
+ thread_id = current_thread_id()
+ stack_utils.log_thread_state(logger, "test-thread", thread_id,
+ "is tested")
+ self.assertTrue(msgs)
+
+ def test_log_traceback(self):
+ msgs = []
+
+ def logger(msg):
+ msgs.append(msg)
+
+ try:
+ raise ValueError
+ except:
+ stack_utils.log_traceback(logger, sys.exc_info()[2])
+ self.assertTrue(msgs)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/Tools/Scripts/webkitpy/common/system/urlfetcher.py b/Tools/Scripts/webkitpy/common/system/urlfetcher.py
new file mode 100644
index 0000000..2d9e5ec
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/urlfetcher.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2011 Google Inc. 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 Google Inc. nor the names of its
+# contributors 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 COPYRIGHT
+# OWNER 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.
+
+"""Wrapper module for fetching URLs."""
+
+import urllib
+
+
+class UrlFetcher(object):
+ """Class with restricted interface to fetch URLs (makes testing easier)"""
+ def __init__(self, filesystem):
+ self._filesystem = filesystem
+
+ def fetch(self, url):
+ """Fetches the contents of the URL as a string."""
+ file_object = urllib.urlopen(url)
+ content = file_object.read()
+ file_object.close()
+ return content
+
+ def fetch_into_file(self, url):
+ """Fetches the contents of the URL into a temporary file and return the filename.
+
+ This is the equivalent of urllib.retrieve() except that we don't return any headers.
+ """
+ file_object, filename = self._filesystem.open_binary_tempfile('-fetched')
+ contents = self.fetch(url)
+ file_object.write(contents)
+ file_object.close()
+ return filename
diff --git a/Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py b/Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py
new file mode 100644
index 0000000..e8a7532
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/urlfetcher_mock.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2011 Google Inc. 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 Google Inc. nor the names of its
+# contributors 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 COPYRIGHT
+# OWNER 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.
+
+
+def make_fetcher_cls(urls):
+ """UrlFetcher factory routine that simulates network access
+ using a dict of URLs -> contents."""
+ class MockFetcher(object):
+ def __init__(self, filesystem):
+ self._filesystem = filesystem
+
+ def fetch(self, url):
+ return urls[url]
+
+ def fetch_into_file(self, url):
+ f, fn = self._filesystem.open_binary_tempfile('mockfetcher')
+ f.write(self.fetch(url))
+ f.close()
+ return fn
+
+ return MockFetcher
diff --git a/Tools/Scripts/webkitpy/common/system/zip_mock.py b/Tools/Scripts/webkitpy/common/system/zip_mock.py
new file mode 100644
index 0000000..dcfaba7
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/zip_mock.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2010 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+
+from webkitpy.common.system.fileset import FileSetFileHandle
+from webkitpy.common.system.filesystem_mock import MockFileSystem
+
+
+class MockZip(object):
+ """A mock zip file that can have new files inserted into it."""
+ def __init__(self, filesystem=None):
+ self._filesystem = filesystem or MockFileSystem()
+ self._files = {}
+
+ def __str__(self):
+ return "MockZip"
+
+ def insert(self, filename, content):
+ self._files[filename] = content
+
+ def namelist(self):
+ return self._files.keys()
+
+ def open(self, filename):
+ return FileSetFileHandle(self, filename)
+
+ def read(self, filename):
+ return self._files[filename]
+
+ def extract(self, filename, path):
+ full_path = self._filesystem.join(path, filename)
+ contents = self.open(filename).contents()
+ self._filesystem.write_text_file(full_path, contents)
+
+ def delete(self, filename):
+ self._files[filename] = None
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset.py b/Tools/Scripts/webkitpy/common/system/zipfileset.py
index fa2b762..5cf3616 100644
--- a/Tools/Scripts/webkitpy/common/system/zipfileset.py
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset.py
@@ -33,22 +33,28 @@ class ZipFileSet(object):
"""The set of files in a zip file that resides at a URL (local or remote)"""
def __init__(self, zip_url, filesystem=None, zip_factory=None):
self._zip_url = zip_url
+ self._temp_file = None
self._zip_file = None
self._filesystem = filesystem or FileSystem()
self._zip_factory = zip_factory or self._retrieve_zip_file
def _retrieve_zip_file(self, zip_url):
temp_file = NetworkTransaction().run(lambda: urllib.urlretrieve(zip_url)[0])
- return zipfile.ZipFile(temp_file)
+ return (temp_file, zipfile.ZipFile(temp_file))
def _load(self):
if self._zip_file is None:
- self._zip_file = self._zip_factory(self._zip_url)
+ self._temp_file, self._zip_file = self._zip_factory(self._zip_url)
def open(self, filename):
self._load()
return FileSetFileHandle(self, filename, self._filesystem)
+ def close(self):
+ if self._temp_file:
+ self._filesystem.remove(self._temp_file)
+ self._temp_file = None
+
def namelist(self):
self._load()
return self._zip_file.namelist()
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset_mock.py b/Tools/Scripts/webkitpy/common/system/zipfileset_mock.py
new file mode 100644
index 0000000..24ac8cb
--- /dev/null
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset_mock.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2011 Google Inc. 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 Google Inc. nor the names of its
+# contributors 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 COPYRIGHT
+# OWNER 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.
+
+
+def make_factory(ziphashes):
+ """ZipFileSet factory routine that looks up zipfiles in a dict;
+ each zipfile should also be a dict of member names -> contents."""
+ class MockZipFileSet(object):
+ def __init__(self, url):
+ self._url = url
+ self._ziphash = ziphashes[url]
+
+ def namelist(self):
+ return self._ziphash.keys()
+
+ def read(self, member):
+ return self._ziphash[member]
+
+ def close(self):
+ pass
+
+ def maker(url):
+ # We return None because there's no tempfile to delete.
+ return (None, MockZipFileSet(url))
+
+ return maker
diff --git a/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py b/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
index a9ba5ad..6801406 100644
--- a/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
+++ b/Tools/Scripts/webkitpy/common/system/zipfileset_unittest.py
@@ -64,13 +64,17 @@ class ZipFileSetTest(unittest.TestCase):
result = FakeZip(self._filesystem)
result.add_file('some-file', 'contents')
result.add_file('a/b/some-other-file', 'other contents')
- return result
+ return (None, result)
def test_open(self):
file = self._zip.open('a/b/some-other-file')
self.assertEquals('a/b/some-other-file', file.name())
self.assertEquals('other contents', file.contents())
+ def test_close(self):
+ zipfileset = ZipFileSet('blah', self._filesystem, self.make_fake_zip)
+ zipfileset.close()
+
def test_read(self):
self.assertEquals('contents', self._zip.read('some-file'))