~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Tools/Scripts/webkitpy/common/system/filesystem_mock.py

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2009 Google Inc. All rights reserved.
 
2
#
 
3
# Redistribution and use in source and binary forms, with or without
 
4
# modification, are permitted provided that the following conditions are
 
5
# met:
 
6
#
 
7
#    * Redistributions of source code must retain the above copyright
 
8
# notice, this list of conditions and the following disclaimer.
 
9
#    * Redistributions in binary form must reproduce the above
 
10
# copyright notice, this list of conditions and the following disclaimer
 
11
# in the documentation and/or other materials provided with the
 
12
# distribution.
 
13
#    * Neither the name of Google Inc. nor the names of its
 
14
# contributors may be used to endorse or promote products derived from
 
15
# this software without specific prior written permission.
 
16
#
 
17
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
18
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
19
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
20
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 
21
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
22
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 
23
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 
24
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 
25
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
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.
 
28
 
 
29
import StringIO
 
30
import errno
 
31
import hashlib
 
32
import os
 
33
import re
 
34
 
 
35
from webkitpy.common.system import path
 
36
 
 
37
 
 
38
class MockFileSystem(object):
 
39
    sep = '/'
 
40
    pardir = '..'
 
41
 
 
42
    def __init__(self, files=None, dirs=None, cwd='/'):
 
43
        """Initializes a "mock" filesystem that can be used to completely
 
44
        stub out a filesystem.
 
45
 
 
46
        Args:
 
47
            files: a dict of filenames -> file contents. A file contents
 
48
                value of None is used to indicate that the file should
 
49
                not exist.
 
50
        """
 
51
        self.files = files or {}
 
52
        self.written_files = {}
 
53
        self.last_tmpdir = None
 
54
        self.current_tmpno = 0
 
55
        self.cwd = cwd
 
56
        self.dirs = set(dirs or [])
 
57
        self.dirs.add(cwd)
 
58
        for f in self.files:
 
59
            d = self.dirname(f)
 
60
            while not d in self.dirs:
 
61
                self.dirs.add(d)
 
62
                d = self.dirname(d)
 
63
 
 
64
    def clear_written_files(self):
 
65
        # This function can be used to track what is written between steps in a test.
 
66
        self.written_files = {}
 
67
 
 
68
    def _raise_not_found(self, path):
 
69
        raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT))
 
70
 
 
71
    def _split(self, path):
 
72
        # This is not quite a full implementation of os.path.split
 
73
        # http://docs.python.org/library/os.path.html#os.path.split
 
74
        if self.sep in path:
 
75
            return path.rsplit(self.sep, 1)
 
76
        return ('', path)
 
77
 
 
78
    def abspath(self, path):
 
79
        if os.path.isabs(path):
 
80
            return self.normpath(path)
 
81
        return self.abspath(self.join(self.cwd, path))
 
82
 
 
83
    def realpath(self, path):
 
84
        return self.abspath(path)
 
85
 
 
86
    def basename(self, path):
 
87
        return self._split(path)[1]
 
88
 
 
89
    def expanduser(self, path):
 
90
        if path[0] != "~":
 
91
            return path
 
92
        parts = path.split(self.sep, 1)
 
93
        home_directory = self.sep + "Users" + self.sep + "mock"
 
94
        if len(parts) == 1:
 
95
            return home_directory
 
96
        return home_directory + self.sep + parts[1]
 
97
 
 
98
    def path_to_module(self, module_name):
 
99
        return "/mock-checkout/Tools/Scripts/" + module_name.replace('.', '/') + ".py"
 
100
 
 
101
    def chdir(self, path):
 
102
        path = self.normpath(path)
 
103
        if not self.isdir(path):
 
104
            raise OSError(errno.ENOENT, path, os.strerror(errno.ENOENT))
 
105
        self.cwd = path
 
106
 
 
107
    def copyfile(self, source, destination):
 
108
        if not self.exists(source):
 
109
            self._raise_not_found(source)
 
110
        if self.isdir(source):
 
111
            raise IOError(errno.EISDIR, source, os.strerror(errno.EISDIR))
 
112
        if self.isdir(destination):
 
113
            raise IOError(errno.EISDIR, destination, os.strerror(errno.EISDIR))
 
114
        if not self.exists(self.dirname(destination)):
 
115
            raise IOError(errno.ENOENT, destination, os.strerror(errno.ENOENT))
 
116
 
 
117
        self.files[destination] = self.files[source]
 
118
        self.written_files[destination] = self.files[source]
 
119
 
 
120
    def dirname(self, path):
 
121
        return self._split(path)[0]
 
122
 
 
123
    def exists(self, path):
 
124
        return self.isfile(path) or self.isdir(path)
 
125
 
 
126
    def files_under(self, path, dirs_to_skip=[], file_filter=None):
 
127
        def filter_all(fs, dirpath, basename):
 
128
            return True
 
129
 
 
130
        file_filter = file_filter or filter_all
 
131
        files = []
 
132
        if self.isfile(path):
 
133
            if file_filter(self, self.dirname(path), self.basename(path)) and self.files[path] is not None:
 
134
                files.append(path)
 
135
            return files
 
136
 
 
137
        if self.basename(path) in dirs_to_skip:
 
138
            return []
 
139
 
 
140
        if not path.endswith(self.sep):
 
141
            path += self.sep
 
142
 
 
143
        dir_substrings = [self.sep + d + self.sep for d in dirs_to_skip]
 
144
        for filename in self.files:
 
145
            if not filename.startswith(path):
 
146
                continue
 
147
 
 
148
            suffix = filename[len(path) - 1:]
 
149
            if any(dir_substring in suffix for dir_substring in dir_substrings):
 
150
                continue
 
151
 
 
152
            dirpath, basename = self._split(filename)
 
153
            if file_filter(self, dirpath, basename) and self.files[filename] is not None:
 
154
                files.append(filename)
 
155
 
 
156
        return files
 
157
 
 
158
    def getcwd(self):
 
159
        return self.cwd
 
160
 
 
161
    def glob(self, glob_string):
 
162
        # FIXME: This handles '*', but not '?', '[', or ']'.
 
163
        glob_string = re.escape(glob_string)
 
164
        glob_string = glob_string.replace('\\*', '[^\\/]*') + '$'
 
165
        glob_string = glob_string.replace('\\/', '/')
 
166
        path_filter = lambda path: re.match(glob_string, path)
 
167
 
 
168
        # We could use fnmatch.fnmatch, but that might not do the right thing on windows.
 
169
        existing_files = [path for path, contents in self.files.items() if contents is not None]
 
170
        return filter(path_filter, existing_files) + filter(path_filter, self.dirs)
 
171
 
 
172
    def isabs(self, path):
 
173
        return path.startswith(self.sep)
 
174
 
 
175
    def isfile(self, path):
 
176
        return path in self.files and self.files[path] is not None
 
177
 
 
178
    def isdir(self, path):
 
179
        return self.normpath(path) in self.dirs
 
180
 
 
181
    def _slow_but_correct_join(self, *comps):
 
182
        return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps))
 
183
 
 
184
    def join(self, *comps):
 
185
        # This function is called a lot, so we optimize it; there are
 
186
        # unittests to check that we match _slow_but_correct_join(), above.
 
187
        path = ''
 
188
        sep = self.sep
 
189
        for comp in comps:
 
190
            if not comp:
 
191
                continue
 
192
            if comp[0] == sep:
 
193
                path = comp
 
194
                continue
 
195
            if path:
 
196
                path += sep
 
197
            path += comp
 
198
        if comps[-1] == '' and path:
 
199
            path += '/'
 
200
        path = path.replace(sep + sep, sep)
 
201
        return path
 
202
 
 
203
    def listdir(self, path):
 
204
        sep = self.sep
 
205
        if not self.isdir(path):
 
206
            raise OSError("%s is not a directory" % path)
 
207
 
 
208
        if not path.endswith(sep):
 
209
            path += sep
 
210
 
 
211
        dirs = []
 
212
        files = []
 
213
        for f in self.files:
 
214
            if self.exists(f) and f.startswith(path):
 
215
                remaining = f[len(path):]
 
216
                if sep in remaining:
 
217
                    dir = remaining[:remaining.index(sep)]
 
218
                    if not dir in dirs:
 
219
                        dirs.append(dir)
 
220
                else:
 
221
                    files.append(remaining)
 
222
        return dirs + files
 
223
 
 
224
    def mtime(self, path):
 
225
        if self.exists(path):
 
226
            return 0
 
227
        self._raise_not_found(path)
 
228
 
 
229
    def _mktemp(self, suffix='', prefix='tmp', dir=None, **kwargs):
 
230
        if dir is None:
 
231
            dir = self.sep + '__im_tmp'
 
232
        curno = self.current_tmpno
 
233
        self.current_tmpno += 1
 
234
        self.last_tmpdir = self.join(dir, '%s_%u_%s' % (prefix, curno, suffix))
 
235
        return self.last_tmpdir
 
236
 
 
237
    def mkdtemp(self, **kwargs):
 
238
        class TemporaryDirectory(object):
 
239
            def __init__(self, fs, **kwargs):
 
240
                self._kwargs = kwargs
 
241
                self._filesystem = fs
 
242
                self._directory_path = fs._mktemp(**kwargs)
 
243
                fs.maybe_make_directory(self._directory_path)
 
244
 
 
245
            def __str__(self):
 
246
                return self._directory_path
 
247
 
 
248
            def __enter__(self):
 
249
                return self._directory_path
 
250
 
 
251
            def __exit__(self, type, value, traceback):
 
252
                # Only self-delete if necessary.
 
253
 
 
254
                # FIXME: Should we delete non-empty directories?
 
255
                if self._filesystem.exists(self._directory_path):
 
256
                    self._filesystem.rmtree(self._directory_path)
 
257
 
 
258
        return TemporaryDirectory(fs=self, **kwargs)
 
259
 
 
260
    def maybe_make_directory(self, *path):
 
261
        norm_path = self.normpath(self.join(*path))
 
262
        while norm_path and not self.isdir(norm_path):
 
263
            self.dirs.add(norm_path)
 
264
            norm_path = self.dirname(norm_path)
 
265
 
 
266
    def move(self, source, destination):
 
267
        if self.files[source] is None:
 
268
            self._raise_not_found(source)
 
269
        self.files[destination] = self.files[source]
 
270
        self.written_files[destination] = self.files[destination]
 
271
        self.files[source] = None
 
272
        self.written_files[source] = None
 
273
 
 
274
    def _slow_but_correct_normpath(self, path):
 
275
        return re.sub(re.escape(os.path.sep), self.sep, os.path.normpath(path))
 
276
 
 
277
    def normpath(self, path):
 
278
        # This function is called a lot, so we try to optimize the common cases
 
279
        # instead of always calling _slow_but_correct_normpath(), above.
 
280
        if '..' in path or '/./' in path:
 
281
            # This doesn't happen very often; don't bother trying to optimize it.
 
282
            return self._slow_but_correct_normpath(path)
 
283
        if not path:
 
284
            return '.'
 
285
        if path == '/':
 
286
            return path
 
287
        if path == '/.':
 
288
            return '/'
 
289
        if path.endswith('/.'):
 
290
            return path[:-2]
 
291
        if path.endswith('/'):
 
292
            return path[:-1]
 
293
        return path
 
294
 
 
295
    def open_binary_tempfile(self, suffix=''):
 
296
        path = self._mktemp(suffix)
 
297
        return (WritableBinaryFileObject(self, path), path)
 
298
 
 
299
    def open_binary_file_for_reading(self, path):
 
300
        if self.files[path] is None:
 
301
            self._raise_not_found(path)
 
302
        return ReadableBinaryFileObject(self, path, self.files[path])
 
303
 
 
304
    def read_binary_file(self, path):
 
305
        # Intentionally raises KeyError if we don't recognize the path.
 
306
        if self.files[path] is None:
 
307
            self._raise_not_found(path)
 
308
        return self.files[path]
 
309
 
 
310
    def write_binary_file(self, path, contents):
 
311
        # FIXME: should this assert if dirname(path) doesn't exist?
 
312
        self.maybe_make_directory(self.dirname(path))
 
313
        self.files[path] = contents
 
314
        self.written_files[path] = contents
 
315
 
 
316
    def open_text_file_for_reading(self, path):
 
317
        if self.files[path] is None:
 
318
            self._raise_not_found(path)
 
319
        return ReadableTextFileObject(self, path, self.files[path])
 
320
 
 
321
    def open_text_file_for_writing(self, path):
 
322
        return WritableTextFileObject(self, path)
 
323
 
 
324
    def read_text_file(self, path):
 
325
        return self.read_binary_file(path).decode('utf-8')
 
326
 
 
327
    def write_text_file(self, path, contents):
 
328
        return self.write_binary_file(path, contents.encode('utf-8'))
 
329
 
 
330
    def sha1(self, path):
 
331
        contents = self.read_binary_file(path)
 
332
        return hashlib.sha1(contents).hexdigest()
 
333
 
 
334
    def relpath(self, path, start='.'):
 
335
        # Since os.path.relpath() calls os.path.normpath()
 
336
        # (see http://docs.python.org/library/os.path.html#os.path.abspath )
 
337
        # it also removes trailing slashes and converts forward and backward
 
338
        # slashes to the preferred slash os.sep.
 
339
        start = self.abspath(start)
 
340
        path = self.abspath(path)
 
341
 
 
342
        if not path.lower().startswith(start.lower()):
 
343
            # path is outside the directory given by start; compute path from root
 
344
            return '../' * start.count('/') + path
 
345
 
 
346
        rel_path = path[len(start):]
 
347
 
 
348
        if not rel_path:
 
349
            # Then the paths are the same.
 
350
            pass
 
351
        elif rel_path[0] == self.sep:
 
352
            # It is probably sufficient to remove just the first character
 
353
            # since os.path.normpath() collapses separators, but we use
 
354
            # lstrip() just to be sure.
 
355
            rel_path = rel_path.lstrip(self.sep)
 
356
        else:
 
357
            # We are in the case typified by the following example:
 
358
            # path = "/tmp/foobar", start = "/tmp/foo" -> rel_path = "bar"
 
359
            # FIXME: We return a less-than-optimal result here.
 
360
            return '../' * start.count('/') + path
 
361
 
 
362
        return rel_path
 
363
 
 
364
    def remove(self, path):
 
365
        if self.files[path] is None:
 
366
            self._raise_not_found(path)
 
367
        self.files[path] = None
 
368
        self.written_files[path] = None
 
369
 
 
370
    def rmtree(self, path):
 
371
        path = self.normpath(path)
 
372
 
 
373
        for f in self.files:
 
374
            if f.startswith(path):
 
375
                self.files[f] = None
 
376
 
 
377
        self.dirs = set(filter(lambda d: not d.startswith(path), self.dirs))
 
378
 
 
379
    def copytree(self, source, destination):
 
380
        source = self.normpath(source)
 
381
        destination = self.normpath(destination)
 
382
 
 
383
        for source_file in self.files:
 
384
            if source_file.startswith(source):
 
385
                destination_path = self.join(destination, self.relpath(source_file, source))
 
386
                self.maybe_make_directory(self.dirname(destination_path))
 
387
                self.files[destination_path] = self.files[source_file]
 
388
 
 
389
    def split(self, path):
 
390
        idx = path.rfind(self.sep)
 
391
        if idx == -1:
 
392
            return ('', path)
 
393
        return (path[:idx], path[(idx + 1):])
 
394
 
 
395
    def splitext(self, path):
 
396
        idx = path.rfind('.')
 
397
        if idx == -1:
 
398
            idx = 0
 
399
        return (path[0:idx], path[idx:])
 
400
 
 
401
 
 
402
class WritableBinaryFileObject(object):
 
403
    def __init__(self, fs, path):
 
404
        self.fs = fs
 
405
        self.path = path
 
406
        self.closed = False
 
407
        self.fs.files[path] = ""
 
408
 
 
409
    def __enter__(self):
 
410
        return self
 
411
 
 
412
    def __exit__(self, type, value, traceback):
 
413
        self.close()
 
414
 
 
415
    def close(self):
 
416
        self.closed = True
 
417
 
 
418
    def write(self, str):
 
419
        self.fs.files[self.path] += str
 
420
        self.fs.written_files[self.path] = self.fs.files[self.path]
 
421
 
 
422
 
 
423
class WritableTextFileObject(WritableBinaryFileObject):
 
424
    def write(self, str):
 
425
        WritableBinaryFileObject.write(self, str.encode('utf-8'))
 
426
 
 
427
 
 
428
class ReadableBinaryFileObject(object):
 
429
    def __init__(self, fs, path, data):
 
430
        self.fs = fs
 
431
        self.path = path
 
432
        self.closed = False
 
433
        self.data = data
 
434
        self.offset = 0
 
435
 
 
436
    def __enter__(self):
 
437
        return self
 
438
 
 
439
    def __exit__(self, type, value, traceback):
 
440
        self.close()
 
441
 
 
442
    def close(self):
 
443
        self.closed = True
 
444
 
 
445
    def read(self, bytes=None):
 
446
        if not bytes:
 
447
            return self.data[self.offset:]
 
448
        start = self.offset
 
449
        self.offset += bytes
 
450
        return self.data[start:self.offset]
 
451
 
 
452
 
 
453
class ReadableTextFileObject(ReadableBinaryFileObject):
 
454
    def __init__(self, fs, path, data):
 
455
        super(ReadableTextFileObject, self).__init__(fs, path, StringIO.StringIO(data))
 
456
 
 
457
    def close(self):
 
458
        self.data.close()
 
459
        super(ReadableTextFileObject, self).close()
 
460
 
 
461
    def read(self, bytes=-1):
 
462
        return self.data.read(bytes)
 
463
 
 
464
    def readline(self, length=None):
 
465
        return self.data.readline(length)
 
466
 
 
467
    def __iter__(self):
 
468
        return self.data.__iter__()
 
469
 
 
470
    def next(self):
 
471
        return self.data.next()
 
472
 
 
473
    def seek(self, offset, whence=os.SEEK_SET):
 
474
        self.data.seek(offset, whence)