diff options
Diffstat (limited to 'Tools/Scripts/webkitpy/common/system')
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')) |