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/executive_mock.py10
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem.py128
-rw-r--r--Tools/Scripts/webkitpy/common/system/filesystem_mock.py191
-rw-r--r--Tools/Scripts/webkitpy/common/system/workspace.py2
-rw-r--r--Tools/Scripts/webkitpy/common/system/workspace_unittest.py5
5 files changed, 296 insertions, 40 deletions
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))