filesystem_mock.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. # Copyright (c) 2011 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. import errno
  5. import fnmatch
  6. import os
  7. import re
  8. import sys
  9. if sys.version_info.major == 2:
  10. from StringIO import StringIO
  11. else:
  12. from io import StringIO
  13. def _RaiseNotFound(path):
  14. raise IOError(errno.ENOENT, path, os.strerror(errno.ENOENT))
  15. class MockFileSystem(object):
  16. """Stripped-down version of WebKit's webkitpy.common.system.filesystem_mock
  17. Implements a filesystem-like interface on top of a dict of filenames ->
  18. file contents. A file content value of None indicates that the file should
  19. not exist (IOError will be raised if it is opened;
  20. reading from a missing key raises a KeyError, not an IOError."""
  21. def __init__(self, files=None):
  22. self.files = files or {}
  23. self.written_files = {}
  24. self._sep = '/'
  25. @property
  26. def sep(self):
  27. return self._sep
  28. def abspath(self, path):
  29. if path.endswith(self.sep):
  30. return path[:-1]
  31. return path
  32. def basename(self, path):
  33. if self.sep not in path:
  34. return ''
  35. return self.split(path)[-1] or self.sep
  36. def dirname(self, path):
  37. if self.sep not in path:
  38. return ''
  39. return self.split(path)[0] or self.sep
  40. def exists(self, path):
  41. return self.isfile(path) or self.isdir(path)
  42. def isabs(self, path):
  43. return path.startswith(self.sep)
  44. def isfile(self, path):
  45. return path in self.files and self.files[path] is not None
  46. def isdir(self, path):
  47. if path in self.files:
  48. return False
  49. if not path.endswith(self.sep):
  50. path += self.sep
  51. # We need to use a copy of the keys here in order to avoid switching
  52. # to a different thread and potentially modifying the dict in
  53. # mid-iteration.
  54. files = list(self.files.keys())[:]
  55. return any(f.startswith(path) for f in files)
  56. def join(self, *comps):
  57. # TODO: Might want tests for this and/or a better comment about how
  58. # it works.
  59. return re.sub(re.escape(os.path.sep), self.sep, os.path.join(*comps))
  60. def glob(self, path):
  61. return fnmatch.filter(self.files.keys(), path)
  62. def open_for_reading(self, path):
  63. return StringIO(self.read_binary_file(path))
  64. def normpath(self, path):
  65. # This is not a complete implementation of normpath. Only covers what we
  66. # use in tests.
  67. result = []
  68. for part in path.split(self.sep):
  69. if part == '..':
  70. result.pop()
  71. elif part == '.':
  72. continue
  73. else:
  74. result.append(part)
  75. return self.sep.join(result)
  76. def read_binary_file(self, path):
  77. # Intentionally raises KeyError if we don't recognize the path.
  78. if self.files[path] is None:
  79. _RaiseNotFound(path)
  80. return self.files[path]
  81. def relpath(self, path, base):
  82. # This implementation is wrong in many ways; assert to check them for now.
  83. if not base.endswith(self.sep):
  84. base += self.sep
  85. assert path.startswith(base)
  86. return path[len(base):]
  87. def split(self, path):
  88. return path.rsplit(self.sep, 1)