filesystem_mock.py 3.2 KB

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