diff options
Diffstat (limited to 'Tools/Scripts/webkitpy/common')
-rw-r--r-- | Tools/Scripts/webkitpy/common/config/committers.py | 3 | ||||
-rw-r--r-- | Tools/Scripts/webkitpy/common/host.py | 80 | ||||
-rw-r--r-- | Tools/Scripts/webkitpy/common/system/executive_mock.py | 10 | ||||
-rw-r--r-- | Tools/Scripts/webkitpy/common/system/filesystem.py | 128 | ||||
-rw-r--r-- | Tools/Scripts/webkitpy/common/system/filesystem_mock.py | 191 | ||||
-rw-r--r-- | Tools/Scripts/webkitpy/common/system/workspace.py | 2 | ||||
-rw-r--r-- | Tools/Scripts/webkitpy/common/system/workspace_unittest.py | 5 |
7 files changed, 378 insertions, 41 deletions
diff --git a/Tools/Scripts/webkitpy/common/config/committers.py b/Tools/Scripts/webkitpy/common/config/committers.py index 6a235f5..f7d59fe 100644 --- a/Tools/Scripts/webkitpy/common/config/committers.py +++ b/Tools/Scripts/webkitpy/common/config/committers.py @@ -150,7 +150,7 @@ committers_unable_to_review = [ Committer("Krzysztof Kowalczyk", "kkowalczyk@gmail.com"), Committer("Kwang Yul Seo", ["kwangyul.seo@gmail.com", "skyul@company100.net", "kseo@webkit.org"], "kwangseo"), Committer("Leandro Pereira", ["leandro@profusion.mobi", "leandro@webkit.org"], "acidx"), - Committer("Levi Weintraub", "lweintraub@apple.com"), + Committer("Levi Weintraub", ["leviw@chromium.org", "leviw@google.com", "lweintraub@apple.com"], "leviw"), Committer("Lucas De Marchi", ["lucas.demarchi@profusion.mobi", "demarchi@webkit.org"], "demarchi"), Committer("Luiz Agostini", ["luiz@webkit.org", "luiz.agostini@openbossa.org"], "lca"), Committer("Mads Ager", "ager@chromium.org"), @@ -198,6 +198,7 @@ committers_unable_to_review = [ Committer("Yaar Schnitman", ["yaar@chromium.org", "yaar@google.com"]), Committer("Yong Li", ["yong.li.webkit@gmail.com", "yong.li@torchmobile.com"], "yong"), Committer("Yongjun Zhang", "yongjun.zhang@nokia.com"), + Committer("Yi Shen", ["yi.4.shen@nokia.com", "shenyi2006@gmail.com"]), Committer("Yuta Kitamura", "yutak@chromium.org", "yutak"), Committer("Yuzo Fujishima", "yuzo@google.com", "yuzo"), Committer("Zhenyao Mo", "zmo@google.com", "zhenyao"), diff --git a/Tools/Scripts/webkitpy/common/host.py b/Tools/Scripts/webkitpy/common/host.py new file mode 100644 index 0000000..8ec271e --- /dev/null +++ b/Tools/Scripts/webkitpy/common/host.py @@ -0,0 +1,80 @@ +# Copyright (c) 2010 Google Inc. All rights reserved. +# Copyright (c) 2009 Apple 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. + + +from webkitpy.common.checkout.api import Checkout +from webkitpy.common.checkout.scm import default_scm +from webkitpy.common.config.ports import WebKitPort +from webkitpy.common.net import bugzilla, buildbot, irc, statusserver +from webkitpy.common.system import executive, filesystem, platforminfo, user, workspace +from webkitpy.layout_tests import port + + +class Host(object): + def __init__(self): + self.bugs = bugzilla.Bugzilla() + self.buildbot = buildbot.BuildBot() + self.executive = executive.Executive() + self._irc = None + self.filesystem = filesystem.FileSystem() + self.workspace = workspace.Workspace(self.filesystem, self.executive) + self._port = None + self.user = user.User() + self._scm = None + self._checkout = None + self.status_server = statusserver.StatusServer() + self.port_factory = port.factory + self.platform = platforminfo.PlatformInfo() + + def _initialize_scm(self, patch_directories=None): + self._scm = default_scm(patch_directories) + self._checkout = Checkout(self.scm()) + + def scm(self): + return self._scm + + def checkout(self): + return self._checkout + + def port(self): + return self._port + + def ensure_irc_connected(self, irc_delegate): + if not self._irc: + self._irc = irc.ircproxy.IRCProxy(irc_delegate) + + def irc(self): + # We don't automatically construct IRCProxy here because constructing + # IRCProxy actually connects to IRC. We want clients to explicitly + # connect to IRC. + return self._irc + + def command_completed(self): + if self._irc: + self._irc.disconnect() diff --git a/Tools/Scripts/webkitpy/common/system/executive_mock.py b/Tools/Scripts/webkitpy/common/system/executive_mock.py index c1cf999..943b70c 100644 --- a/Tools/Scripts/webkitpy/common/system/executive_mock.py +++ b/Tools/Scripts/webkitpy/common/system/executive_mock.py @@ -30,6 +30,8 @@ # FIXME: Unify with tool/mocktool.MockExecutive. +from webkitpy.common.system import executive + class MockExecutive2(object): def __init__(self, output='', exit_code=0, exception=None, @@ -48,7 +50,7 @@ class MockExecutive2(object): def kill_process(self, pid): pass - def run_command(self, arg_list, return_exit_code=False, + def run_command(self, arg_list, error_handler=None, return_exit_code=False, decode_output=False): if self._exception: raise self._exception @@ -56,4 +58,10 @@ class MockExecutive2(object): return self._exit_code if self._run_command_fn: return self._run_command_fn(arg_list) + if self._exit_code and error_handler: + script_error = executive.ScriptError(script_args=arg_list, + exit_code=self._exit_code, + output=self._output) + error_handler(script_error) + return self._output diff --git a/Tools/Scripts/webkitpy/common/system/filesystem.py b/Tools/Scripts/webkitpy/common/system/filesystem.py index 527b6bd..05513a9 100644 --- a/Tools/Scripts/webkitpy/common/system/filesystem.py +++ b/Tools/Scripts/webkitpy/common/system/filesystem.py @@ -33,6 +33,7 @@ from __future__ import with_statement import codecs import errno import exceptions +import glob import os import shutil import tempfile @@ -43,11 +44,71 @@ class FileSystem(object): Unless otherwise noted, all paths are allowed to be either absolute or relative.""" + def __init__(self): + self.sep = os.sep + + def abspath(self, path): + return os.path.abspath(path) + + def basename(self, path): + """Wraps os.path.basename().""" + return os.path.basename(path) + + def copyfile(self, source, destination): + """Copies the contents of the file at the given path to the destination + path.""" + shutil.copyfile(source, destination) + + def dirname(self, path): + """Wraps os.path.dirname().""" + return os.path.dirname(path) def exists(self, path): """Return whether the path exists in the filesystem.""" return os.path.exists(path) + def files_under(self, path, dirs_to_skip=[], file_filter=None): + """Return the list of all files under the given path in topdown order. + + Args: + dirs_to_skip: a list of directories to skip over during the + traversal (e.g., .svn, resources, etc.) + file_filter: if not None, the filter will be invoked + with the filesystem object and the dirname and basename of + each file found. The file is included in the result if the + callback returns True. + """ + def filter_all(fs, dirpath, basename): + return True + + file_filter = file_filter or filter_all + files = [] + if self.isfile(path): + if file_filter(self, self.dirname(path), self.basename(path)): + files.append(path) + return files + + if self.basename(path) in dirs_to_skip: + return [] + + for (dirpath, dirnames, filenames) in os.walk(path): + for d in dirs_to_skip: + if d in dirnames: + dirnames.remove(d) + + for filename in filenames: + if file_filter(self, dirpath, filename): + files.append(self.join(dirpath, filename)) + return files + + def glob(self, path): + """Wraps glob.glob().""" + return glob.glob(path) + + def isabs(self, path): + """Return whether the path is an absolute path.""" + return os.path.isabs(path) + def isfile(self, path): """Return whether the path refers to a file.""" return os.path.isfile(path) @@ -71,14 +132,20 @@ class FileSystem(object): the directory will self-delete at the end of the block (if the directory is empty; non-empty directories raise errors). The directory can be safely deleted inside the block as well, if so - desired.""" + desired. + + Note that the object returned is not a string and does not support all of the string + methods. If you need a string, coerce the object to a string and go from there. + """ class TemporaryDirectory(object): def __init__(self, **kwargs): self._kwargs = kwargs - self._directory_path = None + self._directory_path = tempfile.mkdtemp(**self._kwargs) + + def __str__(self): + return self._directory_path def __enter__(self): - self._directory_path = tempfile.mkdtemp(**self._kwargs) return self._directory_path def __exit__(self, type, value, traceback): @@ -98,6 +165,41 @@ class FileSystem(object): if e.errno != errno.EEXIST: raise + def move(self, src, dest): + shutil.move(src, dest) + + def mtime(self, path): + return os.stat(path).st_mtime + + def normpath(self, path): + """Wraps os.path.normpath().""" + return os.path.normpath(path) + + def open_binary_tempfile(self, suffix): + """Create, open, and return a binary temp file. Returns a tuple of the file and the name.""" + temp_fd, temp_name = tempfile.mkstemp(suffix) + f = os.fdopen(temp_fd, 'wb') + return f, temp_name + + def open_text_file_for_writing(self, path, append=False): + """Returns a file handle suitable for writing to.""" + mode = 'w' + if append: + mode = 'a' + return codecs.open(path, mode, 'utf8') + + def read_binary_file(self, path): + """Return the contents of the file at the given path as a byte string.""" + with file(path, 'rb') as f: + return f.read() + + def read_text_file(self, path): + """Return the contents of the file at the given path as a Unicode string. + + The file is read assuming it is a UTF-8 encoded file with no BOM.""" + with codecs.open(path, 'r', 'utf8') as f: + return f.read() + class _WindowsError(exceptions.OSError): """Fake exception for Linux and Mac.""" pass @@ -124,8 +226,9 @@ class FileSystem(object): if retry_timeout_sec < 0: raise e - def remove_tree(self, path, ignore_errors=False): - shutil.rmtree(path, ignore_errors) + def rmtree(self, path): + """Delete the directory rooted at path, empty or no.""" + shutil.rmtree(path, ignore_errors=True) def read_binary_file(self, path): """Return the contents of the file at the given path as a byte string.""" @@ -139,6 +242,10 @@ class FileSystem(object): with codecs.open(path, 'r', 'utf8') as f: return f.read() + def splitext(self, path): + """Return (dirname + os.sep + basename, '.' + ext)""" + return os.path.splitext(path) + def write_binary_file(self, path, contents): """Write the contents to the file at the given location.""" with file(path, 'wb') as f: @@ -150,14 +257,3 @@ class FileSystem(object): The file is written encoded as UTF-8 with no BOM.""" with codecs.open(path, 'w', 'utf8') as f: f.write(contents) - - def copyfile(self, source, destination): - """Copies the contents of the file at the given path to the destination - path.""" - shutil.copyfile(source, destination) - - def files_under(self, path): - """Return the list of all files under the given path.""" - return [self.join(path_to_file, filename) - for (path_to_file, _, filenames) in os.walk(path) - for filename in filenames] diff --git a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py index 809c4c6..0004944 100644 --- a/Tools/Scripts/webkitpy/common/system/filesystem_mock.py +++ b/Tools/Scripts/webkitpy/common/system/filesystem_mock.py @@ -43,10 +43,82 @@ class MockFileSystem(object): not exist. """ self.files = files or {} + self.written_files = {} + self.sep = '/' + self.current_tmpno = 0 + + 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:]) + + def abspath(self, path): + return path + + def basename(self, path): + return self._split(path)[1] + + def copyfile(self, source, destination): + if not self.exists(source): + self._raise_not_found(source) + if self.isdir(source): + raise IOError(errno.EISDIR, source, os.strerror(errno.ISDIR)) + if self.isdir(destination): + raise IOError(errno.EISDIR, destination, os.strerror(errno.ISDIR)) + + self.files[destination] = self.files[source] + + def dirname(self, path): + return self._split(path)[0] def exists(self, path): return self.isfile(path) or self.isdir(path) + def files_under(self, path, dirs_to_skip=[], file_filter=None): + def filter_all(fs, dirpath, basename): + return True + + file_filter = file_filter or filter_all + files = [] + if self.isfile(path): + if file_filter(self, self.dirname(path), self.basename(path)): + files.append(path) + return files + + if self.basename(path) in dirs_to_skip: + return [] + + if not path.endswith('/'): + path += '/' + + dir_substrings = ['/' + d + '/' for d in dirs_to_skip] + for filename in self.files: + if not filename.startswith(path): + continue + + suffix = filename[len(path) - 1:] + if any(dir_substring in suffix for dir_substring in dir_substrings): + continue + + dirpath, basename = self._split(filename) + if file_filter(self, dirpath, basename): + files.append(filename) + + return files + + def glob(self, path): + # FIXME: This only handles a wildcard '*' at the end of the path. + # Maybe it should handle more? + if path[-1] == '*': + return [f for f in self.files if f.startswith(path[:-1])] + else: + return [f for f in self.files if f == path] + + def isabs(self, path): + return path.startswith('/') + def isfile(self, path): return path in self.files and self.files[path] is not None @@ -55,7 +127,12 @@ class MockFileSystem(object): return False if not path.endswith('/'): path += '/' - return any(f.startswith(path) for f in self.files) + + # 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 + # mid-iteration. + files = self.files.keys()[:] + 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)) @@ -80,42 +157,114 @@ class MockFileSystem(object): files.append(remaining) return dirs + files + def mtime(self, path): + if self.exists(path): + return 0 + self._raise_not_found(path) + + def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs): + if dir is None: + dir = '/__im_tmp' + curno = self.current_tmpno + self.current_tmpno += 1 + return self.join(dir, "%s_%u_%s" % (prefix, curno, suffix)) + + def mkdtemp(self, **kwargs): + class TemporaryDirectory(object): + def __init__(self, fs, **kwargs): + self._kwargs = kwargs + self._filesystem = fs + self._directory_path = fs._mktemp(**kwargs) + fs.maybe_make_directory(self._directory_path) + + def __str__(self): + return self._directory_path + + def __enter__(self): + return self._directory_path + + def __exit__(self, type, value, traceback): + # Only self-delete if necessary. + + # FIXME: Should we delete non-empty directories? + if self._filesystem.exists(self._directory_path): + self._filesystem.rmtree(self._directory_path) + + return TemporaryDirectory(fs=self, **kwargs) + def maybe_make_directory(self, *path): # 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 normpath(self, path): + return path + + def open_binary_tempfile(self, suffix): + path = self._mktemp(suffix) + 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) def read_binary_file(self, path): - if path in self.files: - if self.files[path] is None: - raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT)) - return self.files[path] + # Intentionally raises KeyError if we don't recognize the path. + if self.files[path] is None: + self._raise_not_found(path) + return self.files[path] + + def remove(self, path): + if self.files[path] is None: + self._raise_not_found(path) + self.files[path] = None + + def rmtree(self, path): + if not path.endswith('/'): + path += '/' + + for f in self.files: + if f.startswith(path): + self.files[f] = None + + def splitext(self, path): + idx = path.rfind('.') + if idx == -1: + idx = 0 + return (path[0:idx], path[idx:]) def write_text_file(self, path, contents): return self.write_binary_file(path, contents) def write_binary_file(self, path, contents): self.files[path] = contents + self.written_files[path] = contents - def copyfile(self, source, destination): - if not self.exists(source): - raise IOError(errno.ENOENT, source, os.strerror(errno.ENOENT)) - if self.isdir(source): - raise IOError(errno.EISDIR, source, os.strerror(errno.ISDIR)) - if self.isdir(destination): - raise IOError(errno.EISDIR, destination, os.strerror(errno.ISDIR)) - self.files[destination] = self.files[source] +class WritableFileObject(object): + def __init__(self, fs, path, append=False, encoding=None): + self.fs = fs + self.path = path + self.closed = False + if path not in self.fs.files or not append: + self.fs.files[path] = "" - def files_under(self, path): - if not path.endswith('/'): - path += '/' - return [file for file in self.files if file.startswith(path)] + def __enter__(self): + return self - def remove(self, path): - del self.files[path] + def __exit__(self, type, value, traceback): + self.close() + + def close(self): + self.closed = True - def remove_tree(self, path, ignore_errors=False): - self.files = [file for file in self.files if not file.startswith(path)] + def write(self, str): + self.fs.files[self.path] += str + self.fs.written_files[self.path] = self.fs.files[self.path] diff --git a/Tools/Scripts/webkitpy/common/system/workspace.py b/Tools/Scripts/webkitpy/common/system/workspace.py index 3b755ad..afb0009 100644 --- a/Tools/Scripts/webkitpy/common/system/workspace.py +++ b/Tools/Scripts/webkitpy/common/system/workspace.py @@ -36,7 +36,7 @@ class Workspace(object): self._filesystem = filesystem self._executive = executive # FIXME: Remove if create_zip is moved to python. - def find_unused_filename(self, directory, name, extension, search_limit=10): + def find_unused_filename(self, directory, name, extension, search_limit=100): for count in range(search_limit): if count: target_name = "%s-%s.%s" % (name, count, extension) diff --git a/Tools/Scripts/webkitpy/common/system/workspace_unittest.py b/Tools/Scripts/webkitpy/common/system/workspace_unittest.py index e5fbb26..6be7664 100644 --- a/Tools/Scripts/webkitpy/common/system/workspace_unittest.py +++ b/Tools/Scripts/webkitpy/common/system/workspace_unittest.py @@ -40,10 +40,13 @@ class WorkspaceTest(unittest.TestCase): filesystem = MockFileSystem({ "dir/foo.jpg": "", "dir/foo-1.jpg": "", + "dir/foo-2.jpg": "", }) workspace = Workspace(filesystem, None) self.assertEqual(workspace.find_unused_filename("bar", "bar", "bar"), "bar/bar.bar") - self.assertEqual(workspace.find_unused_filename("dir", "foo", "jpg"), "dir/foo-2.jpg") + self.assertEqual(workspace.find_unused_filename("dir", "foo", "jpg", search_limit=1), None) + self.assertEqual(workspace.find_unused_filename("dir", "foo", "jpg", search_limit=2), None) + self.assertEqual(workspace.find_unused_filename("dir", "foo", "jpg"), "dir/foo-3.jpg") def test_create_zip(self): workspace = Workspace(None, MockExecutive(should_log=True)) |