• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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