Chromium Code Reviews| 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 [] |
| 129 | |
| 130 def filter_all(fs, dirpath, basename): # pylint: disable=unused-argumen t | |
| 132 return True | 131 return True |
|
jeffcarp
2016/11/15 19:10:01
Would turning this into a lambda get rid of the li
| |
| 133 | 132 |
| 134 file_filter = file_filter or filter_all | 133 file_filter = file_filter or filter_all |
| 135 files = [] | 134 files = [] |
| 136 if self.isfile(path): | 135 if self.isfile(path): |
| 137 if file_filter(self, self.dirname(path), self.basename(path)) and se lf.files[path] is not None: | 136 if file_filter(self, self.dirname(path), self.basename(path)) and se lf.files[path] is not None: |
| 138 files.append(path) | 137 files.append(path) |
| 139 return files | 138 return files |
| 140 | 139 |
| 141 if self.basename(path) in dirs_to_skip: | 140 if self.basename(path) in dirs_to_skip: |
| 142 return [] | 141 return [] |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 162 def getcwd(self): | 161 def getcwd(self): |
| 163 return self.cwd | 162 return self.cwd |
| 164 | 163 |
| 165 def glob(self, glob_string): | 164 def glob(self, glob_string): |
| 166 # FIXME: This handles '*', but not '?', '[', or ']'. | 165 # FIXME: This handles '*', but not '?', '[', or ']'. |
| 167 glob_string = re.escape(glob_string) | 166 glob_string = re.escape(glob_string) |
| 168 glob_string = glob_string.replace('\\*', '[^\\/]*') + '$' | 167 glob_string = glob_string.replace('\\*', '[^\\/]*') + '$' |
| 169 glob_string = glob_string.replace('\\/', '/') | 168 glob_string = glob_string.replace('\\/', '/') |
| 170 path_filter = lambda path: re.match(glob_string, path) | 169 path_filter = lambda path: re.match(glob_string, path) |
| 171 | 170 |
| 172 # We could use fnmatch.fnmatch, but that might not do the right thing on windows. | 171 # 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] | 172 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) | 173 return filter(path_filter, existing_files) + filter(path_filter, self.di rs) |
| 175 | 174 |
| 176 def isabs(self, path): | 175 def isabs(self, path): |
| 177 return path.startswith(self.sep) | 176 return path.startswith(self.sep) |
| 178 | 177 |
| 179 def isfile(self, path): | 178 def isfile(self, path): |
| 180 return path in self.files and self.files[path] is not None | 179 return path in self.files and self.files[path] is not None |
| 181 | 180 |
| 182 def isdir(self, path): | 181 def isdir(self, path): |
| 183 return self.normpath(path) in self.dirs | 182 return self.normpath(path) in self.dirs |
| 184 | 183 |
| 185 def _slow_but_correct_join(self, *comps): | 184 def _slow_but_correct_join(self, *comps): |
| 186 return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps)) | 185 return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps)) |
| 187 | 186 |
| 188 def join(self, *comps): | 187 def join(self, *comps): |
| 189 # This function is called a lot, so we optimize it; there are | 188 # This function is called a lot, so we optimize it; there are |
| 190 # unittests to check that we match _slow_but_correct_join(), above. | 189 # unit tests to check that we match _slow_but_correct_join(), above. |
| 191 path = '' | 190 path = '' |
| 192 sep = self.sep | 191 sep = self.sep |
| 193 for comp in comps: | 192 for comp in comps: |
| 194 if not comp: | 193 if not comp: |
| 195 continue | 194 continue |
| 196 if comp[0] == sep: | 195 if comp[0] == sep: |
| 197 path = comp | 196 path = comp |
| 198 continue | 197 continue |
| 199 if path: | 198 if path: |
| 200 path += sep | 199 path += sep |
| 201 path += comp | 200 path += comp |
| 202 if comps[-1] == '' and path: | 201 if comps[-1] == '' and path: |
| 203 path += '/' | 202 path += '/' |
| 204 path = path.replace(sep + sep, sep) | 203 path = path.replace(sep + sep, sep) |
| 205 return path | 204 return path |
| 206 | 205 |
| 207 def listdir(self, path): | 206 def listdir(self, path): |
| 208 _, dirs, files = list(self.walk(path))[0] | 207 _, directories, files = list(self.walk(path))[0] |
| 209 return dirs + files | 208 return directories + files |
| 210 | 209 |
| 211 def walk(self, top): | 210 def walk(self, top): |
| 212 sep = self.sep | 211 sep = self.sep |
| 213 if not self.isdir(top): | 212 if not self.isdir(top): |
| 214 raise OSError("%s is not a directory" % top) | 213 raise OSError("%s is not a directory" % top) |
| 215 | 214 |
| 216 if not top.endswith(sep): | 215 if not top.endswith(sep): |
| 217 top += sep | 216 top += sep |
| 218 | 217 |
| 219 dirs = [] | 218 directories = [] |
| 220 files = [] | 219 files = [] |
| 221 for f in self.files: | 220 for file_path in self.files: |
| 222 if self.exists(f) and f.startswith(top): | 221 if self.exists(file_path) and file_path.startswith(top): |
| 223 remaining = f[len(top):] | 222 remaining = file_path[len(top):] |
| 224 if sep in remaining: | 223 if sep in remaining: |
| 225 dir = remaining[:remaining.index(sep)] | 224 directory = remaining[:remaining.index(sep)] |
| 226 if not dir in dirs: | 225 if directory not in directories: |
| 227 dirs.append(dir) | 226 directories.append(directory) |
| 228 else: | 227 else: |
| 229 files.append(remaining) | 228 files.append(remaining) |
| 230 file_system_tuples = [(top[:-1], dirs, files)] | 229 file_system_tuples = [(top[:-1], directories, files)] |
| 231 for dir in dirs: | 230 for directory in directories: |
| 232 dir = top + dir | 231 directory = top + directory |
| 233 tuples_from_subdirs = self.walk(dir) | 232 tuples_from_subdirs = self.walk(directory) |
| 234 file_system_tuples += tuples_from_subdirs | 233 file_system_tuples += tuples_from_subdirs |
| 235 return file_system_tuples | 234 return file_system_tuples |
| 236 | 235 |
| 237 def mtime(self, path): | 236 def mtime(self, path): |
| 238 if self.exists(path): | 237 if self.exists(path): |
| 239 return 0 | 238 return 0 |
| 240 self._raise_not_found(path) | 239 self._raise_not_found(path) |
| 241 | 240 |
| 242 def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs): | 241 def _mktemp(self, suffix='', prefix='tmp', dir=None, **_): # pylint: disabl e=redefined-builtin |
| 243 if dir is None: | 242 if dir is None: |
| 244 dir = self.sep + '__im_tmp' | 243 dir = self.sep + '__im_tmp' |
| 245 curno = self.current_tmpno | 244 curno = self.current_tmpno |
| 246 self.current_tmpno += 1 | 245 self.current_tmpno += 1 |
| 247 self.last_tmpdir = self.join(dir, '%s_%u_%s' % (prefix, curno, suffix)) | 246 self.last_tmpdir = self.join(dir, '%s_%u_%s' % (prefix, curno, suffix)) |
| 248 return self.last_tmpdir | 247 return self.last_tmpdir |
| 249 | 248 |
| 250 def mkdtemp(self, **kwargs): | 249 def mkdtemp(self, **kwargs): |
| 251 class TemporaryDirectory(object): | 250 class TemporaryDirectory(object): |
| 252 | 251 |
| 253 def __init__(self, fs, **kwargs): | 252 def __init__(self, fs, **kwargs): |
| 254 self._kwargs = kwargs | 253 self._kwargs = kwargs |
| 255 self._filesystem = fs | 254 self._filesystem = fs |
| 256 self._directory_path = fs._mktemp(**kwargs) | 255 self._directory_path = fs._mktemp(**kwargs) # pylint: disable=p rotected-access |
| 257 fs.maybe_make_directory(self._directory_path) | 256 fs.maybe_make_directory(self._directory_path) |
| 258 | 257 |
| 259 def __str__(self): | 258 def __str__(self): |
| 260 return self._directory_path | 259 return self._directory_path |
| 261 | 260 |
| 262 def __enter__(self): | 261 def __enter__(self): |
| 263 return self._directory_path | 262 return self._directory_path |
| 264 | 263 |
| 265 def __exit__(self, type, value, traceback): | 264 def __exit__(self, exception_type, exception_value, traceback): |
| 266 # Only self-delete if necessary. | 265 # Only self-delete if necessary. |
| 267 | 266 |
| 268 # FIXME: Should we delete non-empty directories? | 267 # FIXME: Should we delete non-empty directories? |
| 269 if self._filesystem.exists(self._directory_path): | 268 if self._filesystem.exists(self._directory_path): |
| 270 self._filesystem.rmtree(self._directory_path) | 269 self._filesystem.rmtree(self._directory_path) |
| 271 | 270 |
| 272 return TemporaryDirectory(fs=self, **kwargs) | 271 return TemporaryDirectory(fs=self, **kwargs) |
| 273 | 272 |
| 274 def maybe_make_directory(self, *path): | 273 def maybe_make_directory(self, *path): |
| 275 norm_path = self.normpath(self.join(*path)) | 274 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:] | 382 rel_path = path[len(common_root) + 1:] |
| 384 | 383 |
| 385 return dot_dot + rel_path | 384 return dot_dot + rel_path |
| 386 | 385 |
| 387 def remove(self, path): | 386 def remove(self, path): |
| 388 if self.files[path] is None: | 387 if self.files[path] is None: |
| 389 self._raise_not_found(path) | 388 self._raise_not_found(path) |
| 390 self.files[path] = None | 389 self.files[path] = None |
| 391 self.written_files[path] = None | 390 self.written_files[path] = None |
| 392 | 391 |
| 393 def rmtree(self, path): | 392 def rmtree(self, path_to_remove): |
| 394 path = self.normpath(path) | 393 path_to_remove = self.normpath(path_to_remove) |
| 395 | 394 |
| 396 for f in self.files: | 395 for file_path in self.files: |
| 397 # We need to add a trailing separator to path to avoid matching | 396 # 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'. | 397 # cases like path_to_remove='/foo/b' and file_path='/foo/bar/baz'. |
| 399 if f == path or f.startswith(path + self.sep): | 398 if file_path == path_to_remove or file_path.startswith(path_to_remov e + self.sep): |
| 400 self.files[f] = None | 399 self.files[file_path] = None |
| 401 | 400 |
| 402 self.dirs = set(filter(lambda d: not (d == path or d.startswith(path + s elf.sep)), self.dirs)) | 401 def should_remove(directory): |
| 402 return directory == path_to_remove or directory.startswith(path_to_r emove + self.sep) | |
| 403 | |
| 404 self.dirs = {d for d in self.dirs if not should_remove(d)} | |
| 403 | 405 |
| 404 def copytree(self, source, destination): | 406 def copytree(self, source, destination): |
| 405 source = self.normpath(source) | 407 source = self.normpath(source) |
| 406 destination = self.normpath(destination) | 408 destination = self.normpath(destination) |
| 407 | 409 |
| 408 for source_file in list(self.files): | 410 for source_file in list(self.files): |
| 409 if source_file.startswith(source): | 411 if source_file.startswith(source): |
| 410 destination_path = self.join(destination, self.relpath(source_fi le, source)) | 412 destination_path = self.join(destination, self.relpath(source_fi le, source)) |
| 411 self.maybe_make_directory(self.dirname(destination_path)) | 413 self.maybe_make_directory(self.dirname(destination_path)) |
| 412 self.files[destination_path] = self.files[source_file] | 414 self.files[destination_path] = self.files[source_file] |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 428 | 430 |
| 429 def __init__(self, fs, path): | 431 def __init__(self, fs, path): |
| 430 self.fs = fs | 432 self.fs = fs |
| 431 self.path = path | 433 self.path = path |
| 432 self.closed = False | 434 self.closed = False |
| 433 self.fs.files[path] = "" | 435 self.fs.files[path] = "" |
| 434 | 436 |
| 435 def __enter__(self): | 437 def __enter__(self): |
| 436 return self | 438 return self |
| 437 | 439 |
| 438 def __exit__(self, type, value, traceback): | 440 def __exit__(self, exception_type, exception_value, traceback): |
| 439 self.close() | 441 self.close() |
| 440 | 442 |
| 441 def close(self): | 443 def close(self): |
| 442 self.closed = True | 444 self.closed = True |
| 443 | 445 |
| 444 def write(self, str): | 446 def write(self, string): |
| 445 self.fs.files[self.path] += str | 447 self.fs.files[self.path] += string |
| 446 self.fs.written_files[self.path] = self.fs.files[self.path] | 448 self.fs.written_files[self.path] = self.fs.files[self.path] |
| 447 | 449 |
| 448 | 450 |
| 449 class WritableTextFileObject(WritableBinaryFileObject): | 451 class WritableTextFileObject(WritableBinaryFileObject): |
| 450 | 452 |
| 451 def write(self, str): | 453 def write(self, string): |
| 452 WritableBinaryFileObject.write(self, str.encode('utf-8')) | 454 WritableBinaryFileObject.write(self, string.encode('utf-8')) |
| 453 | 455 |
| 454 | 456 |
| 455 class ReadableBinaryFileObject(object): | 457 class ReadableBinaryFileObject(object): |
| 456 | 458 |
| 457 def __init__(self, fs, path, data): | 459 def __init__(self, fs, path, data): |
| 458 self.fs = fs | 460 self.fs = fs |
| 459 self.path = path | 461 self.path = path |
| 460 self.closed = False | 462 self.closed = False |
| 461 self.data = data | 463 self.data = data |
| 462 self.offset = 0 | 464 self.offset = 0 |
| 463 | 465 |
| 464 def __enter__(self): | 466 def __enter__(self): |
| 465 return self | 467 return self |
| 466 | 468 |
| 467 def __exit__(self, type, value, traceback): | 469 def __exit__(self, exception_type, exception_value, traceback): |
| 468 self.close() | 470 self.close() |
| 469 | 471 |
| 470 def close(self): | 472 def close(self): |
| 471 self.closed = True | 473 self.closed = True |
| 472 | 474 |
| 473 def read(self, bytes=None): | 475 def read(self, num_bytes=None): |
| 474 if not bytes: | 476 if not num_bytes: |
| 475 return self.data[self.offset:] | 477 return self.data[self.offset:] |
| 476 start = self.offset | 478 start = self.offset |
| 477 self.offset += bytes | 479 self.offset += num_bytes |
| 478 return self.data[start:self.offset] | 480 return self.data[start:self.offset] |
| 479 | 481 |
| 480 | 482 |
| 481 class ReadableTextFileObject(ReadableBinaryFileObject): | 483 class ReadableTextFileObject(ReadableBinaryFileObject): |
| 482 | 484 |
| 483 def __init__(self, fs, path, data): | 485 def __init__(self, fs, path, data): |
| 484 super(ReadableTextFileObject, self).__init__(fs, path, StringIO.StringIO (data.decode("utf-8"))) | 486 super(ReadableTextFileObject, self).__init__(fs, path, StringIO.StringIO (data.decode("utf-8"))) |
| 485 | 487 |
| 486 def close(self): | 488 def close(self): |
| 487 self.data.close() | 489 self.data.close() |
| 488 super(ReadableTextFileObject, self).close() | 490 super(ReadableTextFileObject, self).close() |
| 489 | 491 |
| 490 def read(self, bytes=-1): | 492 def read(self, num_bytes=-1): |
| 491 return self.data.read(bytes) | 493 return self.data.read(num_bytes) |
| 492 | 494 |
| 493 def readline(self, length=None): | 495 def readline(self, length=None): |
| 494 return self.data.readline(length) | 496 return self.data.readline(length) |
| 495 | 497 |
| 496 def __iter__(self): | 498 def __iter__(self): |
| 497 return self.data.__iter__() | 499 return self.data.__iter__() |
| 498 | 500 |
| 499 def next(self): | 501 def next(self): |
| 500 return self.data.next() | 502 return self.data.next() |
| 501 | 503 |
| 502 def seek(self, offset, whence=os.SEEK_SET): | 504 def seek(self, offset, whence=os.SEEK_SET): |
| 503 self.data.seek(offset, whence) | 505 self.data.seek(offset, whence) |
| OLD | NEW |