1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5from __future__ import absolute_import 6from __future__ import division 7from __future__ import print_function 8 9import codecs 10import collections 11import os 12import sys 13 14import six 15 16 17class WithableStringIO(six.StringIO): 18 19 def __enter__(self, *args): 20 return self 21 22 def __exit__(self, *args): 23 pass 24 25 26class FakeFS(object): 27 28 def __init__(self, initial_filenames_and_contents=None): 29 self._file_contents = {} 30 if initial_filenames_and_contents: 31 for k, v in six.iteritems(initial_filenames_and_contents): 32 self._file_contents[k] = v 33 34 self._bound = False 35 self._real_codecs_open = codecs.open 36 self._real_open = sys.modules['__builtin__'].open 37 self._real_abspath = os.path.abspath 38 self._real_exists = os.path.exists 39 self._real_walk = os.walk 40 self._real_listdir = os.listdir 41 42 def __enter__(self): 43 self.Bind() 44 return self 45 46 def __exit__(self, *args): 47 self.Unbind() 48 49 def Bind(self): 50 assert not self._bound 51 codecs.open = self._FakeCodecsOpen 52 sys.modules['__builtin__'].open = self._FakeOpen 53 os.path.abspath = self._FakeAbspath 54 os.path.exists = self._FakeExists 55 os.walk = self._FakeWalk 56 os.listdir = self._FakeListDir 57 self._bound = True 58 59 def Unbind(self): 60 assert self._bound 61 codecs.open = self._real_codecs_open 62 sys.modules['__builtin__'].open = self._real_open 63 os.path.abspath = self._real_abspath 64 os.path.exists = self._real_exists 65 os.walk = self._real_walk 66 os.listdir = self._real_listdir 67 self._bound = False 68 69 def AddFile(self, path, contents): 70 assert path not in self._file_contents 71 path = os.path.normpath(path) 72 self._file_contents[path] = contents 73 74 def _FakeOpen(self, path, mode=None): 75 if mode is None: 76 mode = 'r' 77 if mode == 'r' or mode == 'rU' or mode == 'rb': 78 if path not in self._file_contents: 79 return self._real_open(path, mode) 80 return WithableStringIO(self._file_contents[path]) 81 82 raise NotImplementedError() 83 84 def _FakeCodecsOpen(self, path, mode=None, 85 encoding=None): # pylint: disable=unused-argument 86 if mode is None: 87 mode = 'r' 88 if mode == 'r' or mode == 'rU' or mode == 'rb': 89 if path not in self._file_contents: 90 return self._real_open(path, mode) 91 return WithableStringIO(self._file_contents[path]) 92 93 raise NotImplementedError() 94 95 def _FakeAbspath(self, path): 96 """Normalize the path and ensure it starts with os.path.sep. 97 98 The tests all assume paths start with things like '/my/project', 99 and this abspath implementaion makes that assumption work correctly 100 on Windows. 101 """ 102 normpath = os.path.normpath(path) 103 if not normpath.startswith(os.path.sep): 104 normpath = os.path.sep + normpath 105 return normpath 106 107 def _FakeExists(self, path): 108 if path in self._file_contents: 109 return True 110 return self._real_exists(path) 111 112 def _FakeWalk(self, top): 113 assert os.path.isabs(top) 114 all_filenames = list(self._file_contents.keys()) 115 pending_prefixes = collections.deque() 116 pending_prefixes.append(top) 117 visited_prefixes = set() 118 while len(pending_prefixes): 119 prefix = pending_prefixes.popleft() 120 if prefix in visited_prefixes: 121 continue 122 visited_prefixes.add(prefix) 123 if prefix.endswith(os.path.sep): 124 prefix_with_trailing_sep = prefix 125 else: 126 prefix_with_trailing_sep = prefix + os.path.sep 127 128 dirs = set() 129 files = [] 130 for filename in all_filenames: 131 if not filename.startswith(prefix_with_trailing_sep): 132 continue 133 relative_to_prefix = os.path.relpath(filename, prefix) 134 135 dirpart = os.path.dirname(relative_to_prefix) 136 if len(dirpart) == 0: 137 files.append(relative_to_prefix) 138 continue 139 parts = dirpart.split(os.sep) 140 if len(parts) == 0: 141 dirs.add(dirpart) 142 else: 143 pending = os.path.join(prefix, parts[0]) 144 dirs.add(parts[0]) 145 pending_prefixes.appendleft(pending) 146 147 dirs = sorted(dirs) 148 yield prefix, dirs, files 149 150 def _FakeListDir(self, dirname): 151 raise NotImplementedError() 152