| OLD | NEW |
| 1 # Copyright (C) 2009 Google Inc. All rights reserved. | 1 # Copyright (C) 2009 Google Inc. All rights reserved. |
| 2 # | 2 # |
| 3 # Redistribution and use in source and binary forms, with or without | 3 # Redistribution and use in source and binary forms, with or without |
| 4 # modification, are permitted provided that the following conditions are | 4 # modification, are permitted provided that the following conditions are |
| 5 # met: | 5 # met: |
| 6 # | 6 # |
| 7 # * Redistributions of source code must retain the above copyright | 7 # * Redistributions of source code must retain the above copyright |
| 8 # notice, this list of conditions and the following disclaimer. | 8 # notice, this list of conditions and the following disclaimer. |
| 9 # * Redistributions in binary form must reproduce the above | 9 # * Redistributions in binary form must reproduce the above |
| 10 # copyright notice, this list of conditions and the following disclaimer | 10 # copyright notice, this list of conditions and the following disclaimer |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 28 |
| 29 import StringIO | 29 import StringIO |
| 30 import errno | 30 import errno |
| 31 import hashlib | 31 import hashlib |
| 32 import os | 32 import os |
| 33 import re | 33 import re |
| 34 | 34 |
| 35 from webkitpy.common.system import path | |
| 36 | |
| 37 | 35 |
| 38 class MockFileSystem(object): | 36 class MockFileSystem(object): |
| 39 sep = '/' | 37 sep = '/' |
| 40 pardir = '..' | 38 pardir = '..' |
| 41 | 39 |
| 42 def __init__(self, files=None, dirs=None, cwd='/'): | 40 def __init__(self, files=None, dirs=None, cwd='/'): |
| 43 """Initializes a "mock" filesystem that can be used to completely | 41 """Initializes a "mock" filesystem that can be used to replace the |
| 44 stub out a filesystem. | 42 FileSystem class in tests. |
| 45 | 43 |
| 46 Args: | 44 Args: |
| 47 files: a dict of filenames -> file contents. A file contents | 45 files: A dictionary of filenames to file contents. A file contents |
| 48 value of None is used to indicate that the file should | 46 value of None indicates that the file does not exist. |
| 49 not exist. | |
| 50 """ | 47 """ |
| 51 self.files = files or {} | 48 self.files = files or {} |
| 52 self.executable_files = set() | 49 self.executable_files = set() |
| 53 self.written_files = {} | 50 self.written_files = {} |
| 54 self.last_tmpdir = None | 51 self.last_tmpdir = None |
| 55 self.current_tmpno = 0 | 52 self.current_tmpno = 0 |
| 56 self.cwd = cwd | 53 self.cwd = cwd |
| 57 self.dirs = set(dirs or []) | 54 self.dirs = set(dirs or []) |
| 58 self.dirs.add(cwd) | 55 self.dirs.add(cwd) |
| 59 for f in self.files: | 56 for file_path in self.files: |
| 60 d = self.dirname(f) | 57 directory = self.dirname(file_path) |
| 61 while not d in self.dirs: | 58 while directory not in self.dirs: |
| 62 self.dirs.add(d) | 59 self.dirs.add(directory) |
| 63 d = self.dirname(d) | 60 directory = self.dirname(directory) |
| 64 | 61 |
| 65 def clear_written_files(self): | 62 def clear_written_files(self): |
| 66 # This function can be used to track what is written between steps in a
test. | 63 # This function can be used to track what is written between steps in a
test. |
| 67 self.written_files = {} | 64 self.written_files = {} |
| 68 | 65 |
| 69 def _raise_not_found(self, path): | 66 def _raise_not_found(self, path): |
| 70 raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT)) | 67 raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT)) |
| 71 | 68 |
| 72 def _split(self, path): | 69 def _split(self, path): |
| 73 # This is not quite a full implementation of os.path.split | 70 # This is not quite a full implementation of os.path.split; see: |
| 74 # http://docs.python.org/library/os.path.html#os.path.split | 71 # http://docs.python.org/library/os.path.html#os.path.split |
| 75 if self.sep in path: | 72 if self.sep in path: |
| 76 return path.rsplit(self.sep, 1) | 73 return path.rsplit(self.sep, 1) |
| 77 return ('', path) | 74 return ('', path) |
| 78 | 75 |
| 79 def make_executable(self, file_path): | 76 def make_executable(self, file_path): |
| 80 self.executable_files.add(file_path) | 77 self.executable_files.add(file_path) |
| 81 | 78 |
| 82 def abspath(self, path): | 79 def abspath(self, path): |
| 83 if os.path.isabs(path): | 80 if os.path.isabs(path): |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 | 117 |
| 121 self.files[destination] = self.files[source] | 118 self.files[destination] = self.files[source] |
| 122 self.written_files[destination] = self.files[source] | 119 self.written_files[destination] = self.files[source] |
| 123 | 120 |
| 124 def dirname(self, path): | 121 def dirname(self, path): |
| 125 return self._split(path)[0] | 122 return self._split(path)[0] |
| 126 | 123 |
| 127 def exists(self, path): | 124 def exists(self, path): |
| 128 return self.isfile(path) or self.isdir(path) | 125 return self.isfile(path) or self.isdir(path) |
| 129 | 126 |
| 130 def files_under(self, path, dirs_to_skip=[], file_filter=None): | 127 def files_under(self, path, dirs_to_skip=None, file_filter=None): |
| 131 def filter_all(fs, dirpath, basename): | 128 dirs_to_skip = dirs_to_skip or [] |
| 132 return True | 129 |
| 130 filter_all = lambda fs, dirpath, basename: True |
| 133 | 131 |
| 134 file_filter = file_filter or filter_all | 132 file_filter = file_filter or filter_all |
| 135 files = [] | 133 files = [] |
| 136 if self.isfile(path): | 134 if self.isfile(path): |
| 137 if file_filter(self, self.dirname(path), self.basename(path)) and se
lf.files[path] is not None: | 135 if file_filter(self, self.dirname(path), self.basename(path)) and se
lf.files[path] is not None: |
| 138 files.append(path) | 136 files.append(path) |
| 139 return files | 137 return files |
| 140 | 138 |
| 141 if self.basename(path) in dirs_to_skip: | 139 if self.basename(path) in dirs_to_skip: |
| 142 return [] | 140 return [] |
| (...skipping 19 matching lines...) Expand all Loading... |
| 162 def getcwd(self): | 160 def getcwd(self): |
| 163 return self.cwd | 161 return self.cwd |
| 164 | 162 |
| 165 def glob(self, glob_string): | 163 def glob(self, glob_string): |
| 166 # FIXME: This handles '*', but not '?', '[', or ']'. | 164 # FIXME: This handles '*', but not '?', '[', or ']'. |
| 167 glob_string = re.escape(glob_string) | 165 glob_string = re.escape(glob_string) |
| 168 glob_string = glob_string.replace('\\*', '[^\\/]*') + '$' | 166 glob_string = glob_string.replace('\\*', '[^\\/]*') + '$' |
| 169 glob_string = glob_string.replace('\\/', '/') | 167 glob_string = glob_string.replace('\\/', '/') |
| 170 path_filter = lambda path: re.match(glob_string, path) | 168 path_filter = lambda path: re.match(glob_string, path) |
| 171 | 169 |
| 172 # We could use fnmatch.fnmatch, but that might not do the right thing on
windows. | 170 # We could use fnmatch.fnmatch, but that might not do the right thing on
Windows. |
| 173 existing_files = [path for path, contents in self.files.items() if conte
nts is not None] | 171 existing_files = [path for path, contents in self.files.items() if conte
nts is not None] |
| 174 return filter(path_filter, existing_files) + filter(path_filter, self.di
rs) | 172 return filter(path_filter, existing_files) + filter(path_filter, self.di
rs) |
| 175 | 173 |
| 176 def isabs(self, path): | 174 def isabs(self, path): |
| 177 return path.startswith(self.sep) | 175 return path.startswith(self.sep) |
| 178 | 176 |
| 179 def isfile(self, path): | 177 def isfile(self, path): |
| 180 return path in self.files and self.files[path] is not None | 178 return path in self.files and self.files[path] is not None |
| 181 | 179 |
| 182 def isdir(self, path): | 180 def isdir(self, path): |
| 183 return self.normpath(path) in self.dirs | 181 return self.normpath(path) in self.dirs |
| 184 | 182 |
| 185 def _slow_but_correct_join(self, *comps): | 183 def _slow_but_correct_join(self, *comps): |
| 186 return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps)) | 184 return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps)) |
| 187 | 185 |
| 188 def join(self, *comps): | 186 def join(self, *comps): |
| 189 # This function is called a lot, so we optimize it; there are | 187 # This function is called a lot, so we optimize it; there are |
| 190 # unittests to check that we match _slow_but_correct_join(), above. | 188 # unit tests to check that we match _slow_but_correct_join(), above. |
| 191 path = '' | 189 path = '' |
| 192 sep = self.sep | 190 sep = self.sep |
| 193 for comp in comps: | 191 for comp in comps: |
| 194 if not comp: | 192 if not comp: |
| 195 continue | 193 continue |
| 196 if comp[0] == sep: | 194 if comp[0] == sep: |
| 197 path = comp | 195 path = comp |
| 198 continue | 196 continue |
| 199 if path: | 197 if path: |
| 200 path += sep | 198 path += sep |
| 201 path += comp | 199 path += comp |
| 202 if comps[-1] == '' and path: | 200 if comps[-1] == '' and path: |
| 203 path += '/' | 201 path += '/' |
| 204 path = path.replace(sep + sep, sep) | 202 path = path.replace(sep + sep, sep) |
| 205 return path | 203 return path |
| 206 | 204 |
| 207 def listdir(self, path): | 205 def listdir(self, path): |
| 208 _, dirs, files = list(self.walk(path))[0] | 206 _, directories, files = list(self.walk(path))[0] |
| 209 return dirs + files | 207 return directories + files |
| 210 | 208 |
| 211 def walk(self, top): | 209 def walk(self, top): |
| 212 sep = self.sep | 210 sep = self.sep |
| 213 if not self.isdir(top): | 211 if not self.isdir(top): |
| 214 raise OSError("%s is not a directory" % top) | 212 raise OSError("%s is not a directory" % top) |
| 215 | 213 |
| 216 if not top.endswith(sep): | 214 if not top.endswith(sep): |
| 217 top += sep | 215 top += sep |
| 218 | 216 |
| 219 dirs = [] | 217 directories = [] |
| 220 files = [] | 218 files = [] |
| 221 for f in self.files: | 219 for file_path in self.files: |
| 222 if self.exists(f) and f.startswith(top): | 220 if self.exists(file_path) and file_path.startswith(top): |
| 223 remaining = f[len(top):] | 221 remaining = file_path[len(top):] |
| 224 if sep in remaining: | 222 if sep in remaining: |
| 225 dir = remaining[:remaining.index(sep)] | 223 directory = remaining[:remaining.index(sep)] |
| 226 if not dir in dirs: | 224 if directory not in directories: |
| 227 dirs.append(dir) | 225 directories.append(directory) |
| 228 else: | 226 else: |
| 229 files.append(remaining) | 227 files.append(remaining) |
| 230 file_system_tuples = [(top[:-1], dirs, files)] | 228 file_system_tuples = [(top[:-1], directories, files)] |
| 231 for dir in dirs: | 229 for directory in directories: |
| 232 dir = top + dir | 230 directory = top + directory |
| 233 tuples_from_subdirs = self.walk(dir) | 231 tuples_from_subdirs = self.walk(directory) |
| 234 file_system_tuples += tuples_from_subdirs | 232 file_system_tuples += tuples_from_subdirs |
| 235 return file_system_tuples | 233 return file_system_tuples |
| 236 | 234 |
| 237 def mtime(self, path): | 235 def mtime(self, path): |
| 238 if self.exists(path): | 236 if self.exists(path): |
| 239 return 0 | 237 return 0 |
| 240 self._raise_not_found(path) | 238 self._raise_not_found(path) |
| 241 | 239 |
| 242 def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs): | 240 def _mktemp(self, suffix='', prefix='tmp', dir=None, **_): # pylint: disabl
e=redefined-builtin |
| 243 if dir is None: | 241 if dir is None: |
| 244 dir = self.sep + '__im_tmp' | 242 dir = self.sep + '__im_tmp' |
| 245 curno = self.current_tmpno | 243 curno = self.current_tmpno |
| 246 self.current_tmpno += 1 | 244 self.current_tmpno += 1 |
| 247 self.last_tmpdir = self.join(dir, '%s_%u_%s' % (prefix, curno, suffix)) | 245 self.last_tmpdir = self.join(dir, '%s_%u_%s' % (prefix, curno, suffix)) |
| 248 return self.last_tmpdir | 246 return self.last_tmpdir |
| 249 | 247 |
| 250 def mkdtemp(self, **kwargs): | 248 def mkdtemp(self, **kwargs): |
| 251 class TemporaryDirectory(object): | 249 class TemporaryDirectory(object): |
| 252 | 250 |
| 253 def __init__(self, fs, **kwargs): | 251 def __init__(self, fs, **kwargs): |
| 254 self._kwargs = kwargs | 252 self._kwargs = kwargs |
| 255 self._filesystem = fs | 253 self._filesystem = fs |
| 256 self._directory_path = fs._mktemp(**kwargs) | 254 self._directory_path = fs._mktemp(**kwargs) # pylint: disable=p
rotected-access |
| 257 fs.maybe_make_directory(self._directory_path) | 255 fs.maybe_make_directory(self._directory_path) |
| 258 | 256 |
| 259 def __str__(self): | 257 def __str__(self): |
| 260 return self._directory_path | 258 return self._directory_path |
| 261 | 259 |
| 262 def __enter__(self): | 260 def __enter__(self): |
| 263 return self._directory_path | 261 return self._directory_path |
| 264 | 262 |
| 265 def __exit__(self, type, value, traceback): | 263 def __exit__(self, exception_type, exception_value, traceback): |
| 266 # Only self-delete if necessary. | 264 # Only self-delete if necessary. |
| 267 | 265 |
| 268 # FIXME: Should we delete non-empty directories? | 266 # FIXME: Should we delete non-empty directories? |
| 269 if self._filesystem.exists(self._directory_path): | 267 if self._filesystem.exists(self._directory_path): |
| 270 self._filesystem.rmtree(self._directory_path) | 268 self._filesystem.rmtree(self._directory_path) |
| 271 | 269 |
| 272 return TemporaryDirectory(fs=self, **kwargs) | 270 return TemporaryDirectory(fs=self, **kwargs) |
| 273 | 271 |
| 274 def maybe_make_directory(self, *path): | 272 def maybe_make_directory(self, *path): |
| 275 norm_path = self.normpath(self.join(*path)) | 273 norm_path = self.normpath(self.join(*path)) |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 rel_path = path[len(common_root) + 1:] | 381 rel_path = path[len(common_root) + 1:] |
| 384 | 382 |
| 385 return dot_dot + rel_path | 383 return dot_dot + rel_path |
| 386 | 384 |
| 387 def remove(self, path): | 385 def remove(self, path): |
| 388 if self.files[path] is None: | 386 if self.files[path] is None: |
| 389 self._raise_not_found(path) | 387 self._raise_not_found(path) |
| 390 self.files[path] = None | 388 self.files[path] = None |
| 391 self.written_files[path] = None | 389 self.written_files[path] = None |
| 392 | 390 |
| 393 def rmtree(self, path): | 391 def rmtree(self, path_to_remove): |
| 394 path = self.normpath(path) | 392 path_to_remove = self.normpath(path_to_remove) |
| 395 | 393 |
| 396 for f in self.files: | 394 for file_path in self.files: |
| 397 # We need to add a trailing separator to path to avoid matching | 395 # We need to add a trailing separator to path_to_remove to avoid mat
ching |
| 398 # cases like path='/foo/b' and f='/foo/bar/baz'. | 396 # cases like path_to_remove='/foo/b' and file_path='/foo/bar/baz'. |
| 399 if f == path or f.startswith(path + self.sep): | 397 if file_path == path_to_remove or file_path.startswith(path_to_remov
e + self.sep): |
| 400 self.files[f] = None | 398 self.files[file_path] = None |
| 401 | 399 |
| 402 self.dirs = set(filter(lambda d: not (d == path or d.startswith(path + s
elf.sep)), self.dirs)) | 400 def should_remove(directory): |
| 401 return directory == path_to_remove or directory.startswith(path_to_r
emove + self.sep) |
| 402 |
| 403 self.dirs = {d for d in self.dirs if not should_remove(d)} |
| 403 | 404 |
| 404 def copytree(self, source, destination): | 405 def copytree(self, source, destination): |
| 405 source = self.normpath(source) | 406 source = self.normpath(source) |
| 406 destination = self.normpath(destination) | 407 destination = self.normpath(destination) |
| 407 | 408 |
| 408 for source_file in list(self.files): | 409 for source_file in list(self.files): |
| 409 if source_file.startswith(source): | 410 if source_file.startswith(source): |
| 410 destination_path = self.join(destination, self.relpath(source_fi
le, source)) | 411 destination_path = self.join(destination, self.relpath(source_fi
le, source)) |
| 411 self.maybe_make_directory(self.dirname(destination_path)) | 412 self.maybe_make_directory(self.dirname(destination_path)) |
| 412 self.files[destination_path] = self.files[source_file] | 413 self.files[destination_path] = self.files[source_file] |
| (...skipping 15 matching lines...) Expand all Loading... |
| 428 | 429 |
| 429 def __init__(self, fs, path): | 430 def __init__(self, fs, path): |
| 430 self.fs = fs | 431 self.fs = fs |
| 431 self.path = path | 432 self.path = path |
| 432 self.closed = False | 433 self.closed = False |
| 433 self.fs.files[path] = "" | 434 self.fs.files[path] = "" |
| 434 | 435 |
| 435 def __enter__(self): | 436 def __enter__(self): |
| 436 return self | 437 return self |
| 437 | 438 |
| 438 def __exit__(self, type, value, traceback): | 439 def __exit__(self, exception_type, exception_value, traceback): |
| 439 self.close() | 440 self.close() |
| 440 | 441 |
| 441 def close(self): | 442 def close(self): |
| 442 self.closed = True | 443 self.closed = True |
| 443 | 444 |
| 444 def write(self, str): | 445 def write(self, string): |
| 445 self.fs.files[self.path] += str | 446 self.fs.files[self.path] += string |
| 446 self.fs.written_files[self.path] = self.fs.files[self.path] | 447 self.fs.written_files[self.path] = self.fs.files[self.path] |
| 447 | 448 |
| 448 | 449 |
| 449 class WritableTextFileObject(WritableBinaryFileObject): | 450 class WritableTextFileObject(WritableBinaryFileObject): |
| 450 | 451 |
| 451 def write(self, str): | 452 def write(self, string): |
| 452 WritableBinaryFileObject.write(self, str.encode('utf-8')) | 453 WritableBinaryFileObject.write(self, string.encode('utf-8')) |
| 453 | 454 |
| 454 | 455 |
| 455 class ReadableBinaryFileObject(object): | 456 class ReadableBinaryFileObject(object): |
| 456 | 457 |
| 457 def __init__(self, fs, path, data): | 458 def __init__(self, fs, path, data): |
| 458 self.fs = fs | 459 self.fs = fs |
| 459 self.path = path | 460 self.path = path |
| 460 self.closed = False | 461 self.closed = False |
| 461 self.data = data | 462 self.data = data |
| 462 self.offset = 0 | 463 self.offset = 0 |
| 463 | 464 |
| 464 def __enter__(self): | 465 def __enter__(self): |
| 465 return self | 466 return self |
| 466 | 467 |
| 467 def __exit__(self, type, value, traceback): | 468 def __exit__(self, exception_type, exception_value, traceback): |
| 468 self.close() | 469 self.close() |
| 469 | 470 |
| 470 def close(self): | 471 def close(self): |
| 471 self.closed = True | 472 self.closed = True |
| 472 | 473 |
| 473 def read(self, bytes=None): | 474 def read(self, num_bytes=None): |
| 474 if not bytes: | 475 if not num_bytes: |
| 475 return self.data[self.offset:] | 476 return self.data[self.offset:] |
| 476 start = self.offset | 477 start = self.offset |
| 477 self.offset += bytes | 478 self.offset += num_bytes |
| 478 return self.data[start:self.offset] | 479 return self.data[start:self.offset] |
| 479 | 480 |
| 480 | 481 |
| 481 class ReadableTextFileObject(ReadableBinaryFileObject): | 482 class ReadableTextFileObject(ReadableBinaryFileObject): |
| 482 | 483 |
| 483 def __init__(self, fs, path, data): | 484 def __init__(self, fs, path, data): |
| 484 super(ReadableTextFileObject, self).__init__(fs, path, StringIO.StringIO
(data.decode("utf-8"))) | 485 super(ReadableTextFileObject, self).__init__(fs, path, StringIO.StringIO
(data.decode("utf-8"))) |
| 485 | 486 |
| 486 def close(self): | 487 def close(self): |
| 487 self.data.close() | 488 self.data.close() |
| 488 super(ReadableTextFileObject, self).close() | 489 super(ReadableTextFileObject, self).close() |
| 489 | 490 |
| 490 def read(self, bytes=-1): | 491 def read(self, num_bytes=-1): |
| 491 return self.data.read(bytes) | 492 return self.data.read(num_bytes) |
| 492 | 493 |
| 493 def readline(self, length=None): | 494 def readline(self, length=None): |
| 494 return self.data.readline(length) | 495 return self.data.readline(length) |
| 495 | 496 |
| 496 def __iter__(self): | 497 def __iter__(self): |
| 497 return self.data.__iter__() | 498 return self.data.__iter__() |
| 498 | 499 |
| 499 def next(self): | 500 def next(self): |
| 500 return self.data.next() | 501 return self.data.next() |
| 501 | 502 |
| 502 def seek(self, offset, whence=os.SEEK_SET): | 503 def seek(self, offset, whence=os.SEEK_SET): |
| 503 self.data.seek(offset, whence) | 504 self.data.seek(offset, whence) |
| OLD | NEW |