# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import codecs import os import sys import collections import StringIO class WithableStringIO(StringIO.StringIO): def __enter__(self, *args): return self def __exit__(self, *args): pass class FakeFS(object): def __init__(self, initial_filenames_and_contents=None): self._file_contents = {} if initial_filenames_and_contents: for k, v in initial_filenames_and_contents.iteritems(): self._file_contents[k] = v self._bound = False self._real_codecs_open = codecs.open self._real_open = sys.modules['__builtin__'].open self._real_abspath = os.path.abspath self._real_exists = os.path.exists self._real_walk = os.walk self._real_listdir = os.listdir def __enter__(self): self.Bind() return self def __exit__(self, *args): self.Unbind() def Bind(self): assert not self._bound codecs.open = self._FakeCodecsOpen sys.modules['__builtin__'].open = self._FakeOpen os.path.abspath = self._FakeAbspath os.path.exists = self._FakeExists os.walk = self._FakeWalk os.listdir = self._FakeListDir self._bound = True def Unbind(self): assert self._bound codecs.open = self._real_codecs_open sys.modules['__builtin__'].open = self._real_open os.path.abspath = self._real_abspath os.path.exists = self._real_exists os.walk = self._real_walk os.listdir = self._real_listdir self._bound = False def AddFile(self, path, contents): assert path not in self._file_contents path = os.path.normpath(path) self._file_contents[path] = contents def _FakeOpen(self, path, mode=None): if mode is None: mode = 'r' if mode == 'r' or mode == 'rU' or mode == 'rb': if path not in self._file_contents: return self._real_open(path, mode) return WithableStringIO(self._file_contents[path]) raise NotImplementedError() def _FakeCodecsOpen(self, path, mode=None, encoding=None): # pylint: disable=unused-argument if mode is None: mode = 'r' if mode == 'r' or mode == 'rU' or mode == 'rb': if path not in self._file_contents: return self._real_open(path, mode) return WithableStringIO(self._file_contents[path]) raise NotImplementedError() def _FakeAbspath(self, path): """Normalize the path and ensure it starts with os.path.sep. The tests all assume paths start with things like '/my/project', and this abspath implementaion makes that assumption work correctly on Windows. """ normpath = os.path.normpath(path) if not normpath.startswith(os.path.sep): normpath = os.path.sep + normpath return normpath def _FakeExists(self, path): if path in self._file_contents: return True return self._real_exists(path) def _FakeWalk(self, top): assert os.path.isabs(top) all_filenames = self._file_contents.keys() pending_prefixes = collections.deque() pending_prefixes.append(top) visited_prefixes = set() while len(pending_prefixes): prefix = pending_prefixes.popleft() if prefix in visited_prefixes: continue visited_prefixes.add(prefix) if prefix.endswith(os.path.sep): prefix_with_trailing_sep = prefix else: prefix_with_trailing_sep = prefix + os.path.sep dirs = set() files = [] for filename in all_filenames: if not filename.startswith(prefix_with_trailing_sep): continue relative_to_prefix = os.path.relpath(filename, prefix) dirpart = os.path.dirname(relative_to_prefix) if len(dirpart) == 0: files.append(relative_to_prefix) continue parts = dirpart.split(os.sep) if len(parts) == 0: dirs.add(dirpart) else: pending = os.path.join(prefix, parts[0]) dirs.add(parts[0]) pending_prefixes.appendleft(pending) dirs = sorted(dirs) yield prefix, dirs, files def _FakeListDir(self, dirname): raise NotImplementedError()