diff --git a/CHANGES.md b/CHANGES.md index 1d595111..e30b2019 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,11 @@ The released versions correspond to PyPi releases. ## Unreleased +### Changes +* under Windows, the root path is now effectively `C:\` instead of `\`; a + path starting with `\` points to the current drive as in the real file + system (see [#673](../../issues/673)) + ## [Version 4.5.6](https://pypi.python.org/pypi/pyfakefs/4.5.6) (2022-03-17) Fixes a regression which broke tests with older pytest versions (< 3.9). diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index 29bd1ba9..e9bc3a09 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -102,7 +102,7 @@ import sys import traceback import uuid -from collections import namedtuple +from collections import namedtuple, OrderedDict from doctest import TestResults from enum import Enum from stat import ( @@ -937,7 +937,7 @@ def __init__(self, path_separator: str = os.path.sep, self.is_case_sensitive = not (self.is_windows_fs or self.is_macos) self.root = FakeDirectory(self.path_separator, filesystem=self) - self.cwd = self.root.name + self._cwd = '' # We can't query the current value without changing it: self.umask = os.umask(0o22) @@ -951,8 +951,8 @@ def __init__(self, path_separator: str = os.path.sep, # last used numbers for inodes (st_ino) and devices (st_dev) self.last_ino = 0 self.last_dev = 0 - self.mount_points: Dict[AnyString, Dict] = {} - self.add_mount_point(self.root.name, total_size) + self.mount_points: Dict[AnyString, Dict] = OrderedDict() + self._add_root_mount_point(total_size) self._add_standard_streams() self.dev_null = FakeNullFile(self) # set from outside if needed @@ -963,6 +963,36 @@ def __init__(self, path_separator: str = os.path.sep, def is_linux(self) -> bool: return not self.is_windows_fs and not self.is_macos + @property + def cwd(self) -> str: + """Return the current working directory of the fake filesystem.""" + return self._cwd + + @cwd.setter + def cwd(self, value: str) -> None: + """Set the current working directory of the fake filesystem. + Make sure a new drive or share is auto-mounted under Windows. + """ + self._cwd = value + self._auto_mount_drive_if_needed(value) + + @property + def root_dir(self) -> FakeDirectory: + """Return the root directory, which represents "/" under POSIX, + and the current drive under Windows.""" + if self.is_windows_fs: + return self._mount_point_dir_for_cwd() + return self.root + + @property + def root_dir_name(self) -> str: + """Return the root directory name, which is "/" under POSIX, + and the root path of the current drive under Windows.""" + root_dir = to_string(self.root_dir.name) + if not root_dir.endswith(self.path_separator): + return root_dir + self.path_separator + return root_dir + @property def os(self) -> OSType: """Return the real or simulated type of operating system.""" @@ -985,18 +1015,24 @@ def os(self, value: OSType) -> None: def reset(self, total_size: Optional[int] = None): """Remove all file system contents and reset the root.""" self.root = FakeDirectory(self.path_separator, filesystem=self) - self.cwd = self.root.name self.open_files = [] self._free_fd_heap = [] self.last_ino = 0 self.last_dev = 0 - self.mount_points = {} - self.add_mount_point(self.root.name, total_size) + self.mount_points = OrderedDict() + self._add_root_mount_point(total_size) self._add_standard_streams() from pyfakefs import fake_pathlib fake_pathlib.init_module(self) + def _add_root_mount_point(self, total_size): + mount_point = 'C:' if self.is_windows_fs else self.path_separator + self._cwd = mount_point + if not self.cwd.endswith(self.path_separator): + self._cwd += self.path_separator + self.add_mount_point(mount_point, total_size) + def pause(self) -> None: """Pause the patching of the file system modules until `resume` is called. After that call, all file system calls are executed in the @@ -1034,9 +1070,6 @@ def clear_cache(self) -> None: def line_separator(self) -> str: return '\r\n' if self.is_windows_fs else '\n' - def _error_message(self, err_no: int) -> str: - return os.strerror(err_no) + ' in the fake filesystem' - def raise_os_error(self, err_no: int, filename: Optional[AnyString] = None, winerror: Optional[int] = None) -> NoReturn: @@ -1052,7 +1085,7 @@ def raise_os_error(self, err_no: int, filename: The name of the affected file, if any. winerror: Windows only - the specific Windows error code. """ - message = self._error_message(err_no) + message = os.strerror(err_no) + ' in the fake filesystem' if (winerror is not None and sys.platform == 'win32' and self.is_windows_fs): raise OSError(err_no, message, filename, winerror) @@ -1067,7 +1100,7 @@ def _alternative_path_separator( """Return the alternative path separator as the same type as path""" return matching_string(path, self.alternative_path_separator) - def _starts_with_sep(self, path: AnyStr) -> bool: + def starts_with_sep(self, path: AnyStr) -> bool: """Return True if path starts with a path separator.""" sep = self.get_path_separator(path) altsep = self._alternative_path_separator(path) @@ -1075,7 +1108,8 @@ def _starts_with_sep(self, path: AnyStr) -> bool: path.startswith(altsep)) def add_mount_point(self, path: AnyStr, - total_size: Optional[int] = None) -> Dict: + total_size: Optional[int] = None, + can_exist: bool = False) -> Dict: """Add a new mount point for a filesystem device. The mount point gets a new unique device number. @@ -1085,36 +1119,74 @@ def add_mount_point(self, path: AnyStr, total_size: The new total size of the added filesystem device in bytes. Defaults to infinite size. + can_exist: If True, no error is raised if the mount point + already exists + Returns: The newly created mount point dict. Raises: - OSError: if trying to mount an existing mount point again. - """ - path = self.absnormpath(path) - if path in self.mount_points: - self.raise_os_error(errno.EEXIST, path) + OSError: if trying to mount an existing mount point again, + and `can_exist` is False. + """ + path = self.normpath(self.normcase(path)) + for mount_point in self.mount_points: + if (self.is_case_sensitive and + path == matching_string(path, mount_point) or + not self.is_case_sensitive and + path.lower() == matching_string( + path, mount_point.lower())): + if can_exist: + return self.mount_points[mount_point] + self.raise_os_error(errno.EEXIST, path) + self.last_dev += 1 self.mount_points[path] = { 'idev': self.last_dev, 'total_size': total_size, 'used_size': 0 } - # special handling for root path: has been created before - if path == self.root.name: + if path == matching_string(path, self.root.name): + # special handling for root path: has been created before root_dir = self.root self.last_ino += 1 root_dir.st_ino = self.last_ino else: - root_dir = self.create_dir(path) + root_dir = self._create_mount_point_dir(path) root_dir.st_dev = self.last_dev return self.mount_points[path] - def _auto_mount_drive_if_needed(self, path: AnyStr, - force: bool = False) -> Optional[Dict]: - if (self.is_windows_fs and - (force or not self._mount_point_for_path(path))): + def _create_mount_point_dir( + self, directory_path: AnyPath) -> FakeDirectory: + """A version of `create_dir` for the mount point directory creation, + which avoids circular calls and unneeded checks. + """ + dir_path = self.make_string_path(directory_path) + path_components = self._path_components(dir_path) + current_dir = self.root + + new_dirs = [] + for component in [to_string(p) for p in path_components]: + directory = self._directory_content( + current_dir, to_string(component))[1] + if not directory: + new_dir = FakeDirectory(component, filesystem=self) + new_dirs.append(new_dir) + current_dir.add_entry(new_dir) + current_dir = new_dir + else: + current_dir = cast(FakeDirectory, directory) + + for new_dir in new_dirs: + new_dir.st_mode = S_IFDIR | PERM_DEF + + return current_dir + + def _auto_mount_drive_if_needed(self, path: AnyStr) -> Optional[Dict]: + """Windows only: if `path` is located on an unmounted drive or UNC + mount point, the drive/mount point is added to the mount points.""" + if self.is_windows_fs: drive = self.splitdrive(path)[0] if drive: - return self.add_mount_point(path=drive) + return self.add_mount_point(path=drive, can_exist=True) return None def _mount_point_for_path(self, path: AnyStr) -> Dict: @@ -1132,10 +1204,35 @@ def _mount_point_for_path(self, path: AnyStr) -> Dict: mount_path = root_path if mount_path: return self.mount_points[to_string(mount_path)] - mount_point = self._auto_mount_drive_if_needed(path, force=True) + mount_point = self._auto_mount_drive_if_needed(path) assert mount_point return mount_point + def _mount_point_dir_for_cwd(self) -> FakeDirectory: + """Return the fake directory object of the mount point where the + current working directory points to.""" + def object_from_path(file_path): + path_components = self._path_components(file_path) + target = self.root + for component in path_components: + target = target.get_entry(component) + return target + + path = to_string(self.cwd) + for mount_path in self.mount_points: + if path == to_string(mount_path): + return object_from_path(mount_path) + mount_path = '' + drive = to_string(self.splitdrive(path)[0]) + for root_path in self.mount_points: + str_root_path = to_string(root_path) + if drive and not str_root_path.startswith(drive): + continue + if (path.startswith(str_root_path) and + len(str_root_path) > len(mount_path)): + mount_path = root_path + return object_from_path(mount_path) + def _mount_point_for_device(self, idev: int) -> Optional[Dict]: for mount_point in self.mount_points.values(): if mount_point['idev'] == idev: @@ -1156,7 +1253,7 @@ def get_disk_usage( """ DiskUsage = namedtuple('DiskUsage', 'total, used, free') if path is None: - mount_point = self.mount_points[self.root.name] + mount_point = next(iter(self.mount_points.values())) else: mount_point = self._mount_point_for_path(path) if mount_point and mount_point['total_size'] is not None: @@ -1184,7 +1281,7 @@ def set_disk_usage( OSError: if the new space is smaller than the used size. """ file_path: AnyStr = (path if path is not None # type: ignore - else self.root.name) + else self.root_dir_name) mount_point = self._mount_point_for_path(file_path) if (mount_point['total_size'] is not None and mount_point['used_size'] > total_size): @@ -1341,8 +1438,9 @@ def utime(self, path: AnyStr, file_object.st_atime = current_time file_object.st_mtime = current_time + @staticmethod def _handle_utime_arg_errors( - self, ns: Optional[Tuple[int, int]], + ns: Optional[Tuple[int, int]], times: Optional[Tuple[Union[int, float], Union[int, float]]]): if times is not None and ns is not None: raise ValueError( @@ -1524,13 +1622,17 @@ def components_to_path(): normalized_components):]) sep = self.path_separator normalized_path = sep.join(normalized_components) - if (self._starts_with_sep(path) - and not self._starts_with_sep(normalized_path)): + if (self.starts_with_sep(path) + and not self.starts_with_sep(normalized_path)): normalized_path = sep + normalized_path + if (len(normalized_path) == 2 and + self.starts_with_drive_letter(normalized_path)): + normalized_path += sep return normalized_path if self.is_case_sensitive or not path: return path + path = self.replace_windows_root(path) path_components = self._path_components(path) normalized_components = [] current_dir = self.root @@ -1564,7 +1666,7 @@ def absnormpath(self, path: AnyStr) -> AnyStr: path = self.normcase(path) cwd = matching_string(path, self.cwd) if not path: - path = matching_string(path, self.path_separator) + path = self.get_path_separator(path) if path == matching_string(path, '.'): path = cwd elif not self._starts_with_root_path(path): @@ -1573,8 +1675,8 @@ def absnormpath(self, path: AnyStr) -> AnyStr: empty = matching_string(path, '') path = self.get_path_separator(path).join( (cwd != root_name and cwd or empty, path)) - if path == matching_string(path, '.'): - path = cwd + else: + path = self.replace_windows_root(path) return self.normpath(path) def splitpath(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]: @@ -1751,7 +1853,7 @@ def _path_components(self, path: AnyStr) -> List[AnyStr]: path_components.insert(0, drive) return path_components - def _starts_with_drive_letter(self, file_path: AnyStr) -> bool: + def starts_with_drive_letter(self, file_path: AnyStr) -> bool: """Return True if file_path starts with a drive letter. Args: @@ -1763,7 +1865,7 @@ def _starts_with_drive_letter(self, file_path: AnyStr) -> bool: """ colon = matching_string(file_path, ':') if (len(file_path) >= 2 and - file_path[:1].isalpha and file_path[1:2] == colon): + file_path[0:1].isalpha and file_path[1:2] == colon): if self.is_windows_fs: return True if os.name == 'nt': @@ -1783,14 +1885,42 @@ def _starts_with_root_path(self, file_path: AnyStr) -> bool: return (file_path.startswith(root_name) or not self.is_case_sensitive and file_path.lower().startswith( root_name.lower()) or - self._starts_with_drive_letter(file_path)) + self.starts_with_drive_letter(file_path)) + + def replace_windows_root(self, path: AnyStr) -> AnyStr: + """In windows, if a path starts with a single separator, + it points to the root dir of the current mount point, usually a + drive - replace it with that mount point path to get the real path. + """ + if path and self.is_windows_fs and self.root_dir: + sep = self.get_path_separator(path) + # ignore UNC paths + if path[0:1] == sep and (len(path) == 1 or path[1:2] != sep): + # check if we already have a mount point for that path + for root_path in self.mount_points: + root_path = matching_string(path, root_path) + if path.startswith(root_path): + return path + # must be a pointer to the current drive - replace it + mount_point = matching_string(path, self.root_dir_name) + path = mount_point + path[1:] + return path def _is_root_path(self, file_path: AnyStr) -> bool: root_name = matching_string(file_path, self.root.name) - return (file_path == root_name or not self.is_case_sensitive and - file_path.lower() == root_name.lower() or - 2 <= len(file_path) <= 3 and - self._starts_with_drive_letter(file_path)) + return file_path == root_name or self.is_mount_point(file_path) + + def is_mount_point(self, file_path: AnyStr) -> bool: + """Return `True` if `file_path` points to a mount point.""" + for mount_point in self.mount_points: + mount_point = matching_string(file_path, mount_point) + if (file_path == mount_point or not self.is_case_sensitive and + file_path.lower() == mount_point.lower()): + return True + if self.is_windows_fs: + return (2 <= len(file_path) <= 3 and + self.starts_with_drive_letter(file_path)) + return False def ends_with_path_separator(self, path: Union[int, AnyPath]) -> bool: """Return True if ``file_path`` ends with a valid path separator.""" @@ -1854,7 +1984,7 @@ def exists(self, file_path: AnyPath, check_link: bool = False) -> bool: path = self.resolve_path(path) except OSError: return False - if path == self.root.name: + if self._is_root_path(path): return True path_components: List[str] = self._path_components(path) @@ -1917,13 +2047,16 @@ def resolve_path(self, # all parts of a relative path exist. self.raise_os_error(errno.ENOENT, path) path = self.absnormpath(self._original_path(path)) + path = self.replace_windows_root(path) if self._is_root_path(path): return path if path == matching_string(path, self.dev_null.name): return path path_components = self._path_components(path) resolved_components = self._resolve_components(path_components) - return self._components_to_path(resolved_components) + path = self._components_to_path(resolved_components) + # after resolving links, we have to check again for Windows root + return self.replace_windows_root(path) def _components_to_path(self, component_folders): sep = (self.get_path_separator(component_folders[0]) @@ -2184,7 +2317,7 @@ def add_object(self, file_path: AnyStr, file_object: AnyFile) -> None: directory. """ if not file_path: - target_directory = self.root + target_directory = self.root_dir else: target_directory = cast(FakeDirectory, self.resolve(file_path)) if not S_ISDIR(target_directory.st_mode): @@ -2411,7 +2544,7 @@ def remove_object(self, file_path: AnyStr) -> None: def make_string_path(self, path: AnyPath) -> AnyStr: path_str = make_string_path(path) os_sep = matching_string(path_str, os.sep) - fake_sep = matching_string(path_str, self.path_separator) + fake_sep = self.get_path_separator(path_str) return path_str.replace(os_sep, fake_sep) # type: ignore[return-value] def create_dir(self, directory_path: AnyPath, @@ -2446,6 +2579,8 @@ def create_dir(self, directory_path: AnyPath, if not directory: new_dir = FakeDirectory(component, filesystem=self) new_dirs.append(new_dir) + if self.is_windows_fs and current_dir == self.root: + current_dir = self.root_dir current_dir.add_entry(new_dir) current_dir = new_dir else: @@ -2620,19 +2755,20 @@ def add_real_directory( source_path_str) if not os.path.exists(source_path_str): self.raise_os_error(errno.ENOENT, source_path_str) - target_path = target_path or source_path_str + target_path_str = make_string_path(target_path or source_path_str) + self._auto_mount_drive_if_needed(target_path_str) new_dir: FakeDirectory if lazy_read: - parent_path = os.path.split(target_path)[0] + parent_path = os.path.split(target_path_str)[0] if self.exists(parent_path): parent_dir = self.get_object(parent_path) else: parent_dir = self.create_dir(parent_path) new_dir = FakeDirectoryFromRealDirectory( - source_path_str, self, read_only, target_path) + source_path_str, self, read_only, target_path_str) parent_dir.add_entry(new_dir) else: - new_dir = self.create_dir(target_path) + new_dir = self.create_dir(target_path_str) for base, _, files in os.walk(source_path_str): new_base = os.path.join(new_dir.path, # type: ignore[arg-type] os.path.relpath(base, source_path_str)) @@ -2730,7 +2866,8 @@ def create_file_internally( if not self.exists(parent_directory): if not create_missing_dirs: self.raise_os_error(errno.ENOENT, parent_directory) - self.create_dir(parent_directory) + parent_directory = matching_string( + path, self.create_dir(parent_directory).path) # type: ignore else: parent_directory = self._original_path(parent_directory) if apply_umask: @@ -2979,7 +3116,7 @@ def makedir(self, dir_path: AnyPath, mode: int = PERM_DEF) -> None: dir_name = self.absnormpath(dir_name) if self.exists(dir_name, check_link=True): - if self.is_windows_fs and dir_name == self.path_separator: + if self.is_windows_fs and dir_name == self.root_dir_name: error_nr = errno.EACCES else: error_nr = errno.EEXIST @@ -3032,7 +3169,7 @@ def makedirs(self, dir_name: AnyStr, mode: int = PERM_DEF, # Raise a permission denied error if the first existing directory # is not writeable. - current_dir = self.root + current_dir = self.root_dir for component in path_components: if (not hasattr(current_dir, "entries") or component not in current_dir.entries): @@ -3175,7 +3312,7 @@ def remove(self, path: AnyStr) -> None: error = errno.EISDIR self.raise_os_error(error, norm_path) - if path.endswith(matching_string(path, self.path_separator)): + if path.endswith(self.get_path_separator(path)): if self.is_windows_fs: error = errno.EACCES elif self.is_macos: @@ -3243,7 +3380,7 @@ def listdir(self, target_directory: AnyStr) -> List[AnyStr]: return directory_contents # type: ignore[return-value] def __str__(self) -> str: - return str(self.root) + return str(self.root_dir) def _add_standard_streams(self) -> None: self._add_open_file(StandardStreamWrapper(sys.stdin)) @@ -3274,7 +3411,7 @@ def _add_standard_streams(self) -> None: Deprecator.add(FakeFilesystem, FakeFilesystem.joinpaths, 'JoinPaths') Deprecator.add(FakeFilesystem, FakeFilesystem._path_components, 'GetPathComponents') -Deprecator.add(FakeFilesystem, FakeFilesystem._starts_with_drive_letter, +Deprecator.add(FakeFilesystem, FakeFilesystem.starts_with_drive_letter, 'StartsWithDriveLetter') Deprecator.add(FakeFilesystem, FakeFilesystem.exists, 'Exists') Deprecator.add(FakeFilesystem, FakeFilesystem.resolve_path, 'ResolvePath') @@ -3391,7 +3528,7 @@ def isabs(self, path: AnyStr) -> bool: if self.filesystem.is_windows_fs: path = self.splitdrive(path)[1] path = make_string_path(path) - return self.filesystem._starts_with_sep(path) + return self.filesystem.starts_with_sep(path) def isdir(self, path: AnyStr) -> bool: """Determine if path identifies a directory.""" @@ -3490,9 +3627,9 @@ def getcwd(): if not self.isabs(path): path = self.join(getcwd(), path) elif (self.filesystem.is_windows_fs and - self.filesystem._starts_with_sep(path)): + self.filesystem.starts_with_sep(path)): cwd = getcwd() - if self.filesystem._starts_with_drive_letter(cwd): + if self.filesystem.starts_with_drive_letter(cwd): path = self.join(cwd[:2], path) return self.normpath(path) @@ -3528,17 +3665,19 @@ def relpath(self, path: AnyStr, start: Optional[AnyStr] = None) -> AnyStr: if not path: raise ValueError("no path specified") path = make_string_path(path) + path = self.filesystem.replace_windows_root(path) + sep = matching_string(path, self.filesystem.path_separator) if start is not None: start = make_string_path(start) else: start = matching_string(path, self.filesystem.cwd) + start = self.filesystem.replace_windows_root(start) system_sep = matching_string(path, self._os_path.sep) if self.filesystem.alternative_path_separator is not None: altsep = matching_string( path, self.filesystem.alternative_path_separator) path = path.replace(altsep, system_sep) start = start.replace(altsep, system_sep) - sep = matching_string(path, self.filesystem.path_separator) path = path.replace(sep, system_sep) start = start.replace(sep, system_sep) path = self._os_path.relpath(path, start) @@ -4886,7 +5025,7 @@ def __init__(self, filesystem: FakeFilesystem): """ Args: filesystem: FakeFilesystem used to provide file system - information (currently not used). + information (currently not used). """ self.filesystem = filesystem self._fcntl_module = fcntl diff --git a/pyfakefs/fake_pathlib.py b/pyfakefs/fake_pathlib.py index 88685581..375843f9 100644 --- a/pyfakefs/fake_pathlib.py +++ b/pyfakefs/fake_pathlib.py @@ -641,7 +641,8 @@ def home(cls): home = os.path.join('C:', 'Users', username) else: home = os.path.join('home', username) - cls.filesystem.create_dir(home) + if not cls.filesystem.exists(home): + cls.filesystem.create_dir(home) return cls(home.replace(os.sep, cls.filesystem.path_separator)) def samefile(self, other_path): diff --git a/pyfakefs/tests/fake_filesystem_test.py b/pyfakefs/tests/fake_filesystem_test.py index f131d2fc..445fcd1e 100644 --- a/pyfakefs/tests/fake_filesystem_test.py +++ b/pyfakefs/tests/fake_filesystem_test.py @@ -30,10 +30,10 @@ class FakeDirectoryUnitTest(TestCase): def setUp(self): - self.time = time_mock(10, 1) - self.time.start() self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.os = fake_filesystem.FakeOsModule(self.filesystem) + self.time = time_mock(10, 1) + self.time.start() self.fake_file = fake_filesystem.FakeFile( 'foobar', contents='dummy_file', filesystem=self.filesystem) self.fake_dir = fake_filesystem.FakeDirectory( @@ -46,7 +46,6 @@ def test_new_file_and_directory(self): self.assertTrue(stat.S_IFREG & self.fake_file.st_mode) self.assertTrue(stat.S_IFDIR & self.fake_dir.st_mode) self.assertEqual({}, self.fake_dir.entries) - self.assertEqual(12, self.fake_file.st_ctime) def test_add_entry(self): self.fake_dir.add_entry(self.fake_file) @@ -58,27 +57,31 @@ def test_get_entry(self): self.assertEqual(self.fake_file, self.fake_dir.get_entry('foobar')) def test_path(self): + root_dir = self.filesystem.root_dir_name self.filesystem.root.add_entry(self.fake_dir) self.fake_dir.add_entry(self.fake_file) - self.assertEqual('/somedir/foobar', self.fake_file.path) - self.assertEqual('/somedir', self.fake_dir.path) + self.assertEqual(f'{root_dir}somedir/foobar', self.fake_file.path) + self.assertEqual(f'{root_dir}somedir', self.fake_dir.path) def test_path_with_drive(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() dir_path = 'C:/foo/bar/baz' self.filesystem.create_dir(dir_path) dir_object = self.filesystem.get_object(dir_path) self.assertEqual(dir_path, dir_object.path) def test_path_after_chdir(self): + root_dir = self.filesystem.root_dir_name dir_path = '/foo/bar/baz' self.filesystem.create_dir(dir_path) self.os.chdir(dir_path) dir_object = self.filesystem.get_object(dir_path) - self.assertEqual(dir_path, dir_object.path) + self.assertEqual(f'{root_dir}foo/bar/baz', dir_object.path) def test_path_after_chdir_with_drive(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() dir_path = 'C:/foo/bar/baz' self.filesystem.create_dir(dir_path) self.os.chdir(dir_path) @@ -119,9 +122,11 @@ def test_leave_file_unchanged_if_size_is_equal_to_current_size(self): def test_set_contents_to_dir_raises(self): # Regression test for #276 self.filesystem.is_windows_fs = True + self.filesystem.reset() with self.raises_os_error(errno.EISDIR): self.fake_dir.set_contents('a') self.filesystem.is_windows_fs = False + self.filesystem.reset() with self.raises_os_error(errno.EISDIR): self.fake_dir.set_contents('a') @@ -130,9 +135,9 @@ def test_pads_with_nullbytes_if_size_is_greater_than_current_size(self): self.assertEqual('dummy_file\0\0\0', self.fake_file.contents) def test_set_m_time(self): - self.assertEqual(12, self.fake_file.st_mtime) - self.fake_file.st_mtime = 13 - self.assertEqual(13, self.fake_file.st_mtime) + self.assertEqual(10, self.fake_file.st_mtime) + self.fake_file.st_mtime = 14 + self.assertEqual(14, self.fake_file.st_mtime) self.fake_file.st_mtime = 131 self.assertEqual(131, self.fake_file.st_mtime) @@ -158,6 +163,23 @@ def test_directory_inode(self): dir_obj.st_ino = 43 self.assertEqual(43, fake_os.stat(dirpath)[stat.ST_INO]) + def test_directory_size(self): + fs = fake_filesystem.FakeFilesystem(path_separator='/') + foo_dir = fs.create_dir('/foo') + fs.create_file('/foo/bar.txt', st_size=20) + bar_dir = fs.create_dir('/foo/bar/') + fs.create_file('/foo/bar/baz1.txt', st_size=30) + fs.create_file('/foo/bar/baz2.txt', st_size=40) + foo1_dir = fs.create_dir('/foo1') + fs.create_file('/foo1/bar.txt', st_size=50) + fs.create_file('/foo1/bar/baz/file', st_size=60) + self.assertEqual(90, foo_dir.size) + self.assertEqual(70, bar_dir.size) + self.assertEqual(110, foo1_dir.size) + self.assertEqual(200, fs.root_dir.size) + with self.raises_os_error(errno.EISDIR): + foo1_dir.size = 100 + def test_ordered_dirs(self): filesystem = fake_filesystem.FakeFilesystem(path_separator='/') filesystem.create_dir('/foo') @@ -192,7 +214,7 @@ def test_sets_content_none_if_size_is_non_negative_integer(self): class NormalizePathTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') - self.root_name = '/' + self.root_name = self.filesystem.root_dir_name def test_empty_path_should_get_normalized_to_root_path(self): self.assertEqual(self.root_name, self.filesystem.absnormpath('')) @@ -207,18 +229,21 @@ def test_relative_path_forced_to_cwd(self): self.assertEqual('/foo/bar', self.filesystem.absnormpath(path)) def test_absolute_path_remains_unchanged(self): - path = '/foo/bar' - self.assertEqual(path, self.filesystem.absnormpath(path)) + path = 'foo/bar' + self.assertEqual(self.root_name + path, self.filesystem.absnormpath( + path)) def test_dotted_path_is_normalized(self): path = '/foo/..' - self.assertEqual('/', self.filesystem.absnormpath(path)) + self.assertEqual(self.filesystem.root_dir_name, + self.filesystem.absnormpath(path)) path = 'foo/../bar' - self.assertEqual('/bar', self.filesystem.absnormpath(path)) + self.assertEqual(f'{self.filesystem.root_dir_name}bar', + self.filesystem.absnormpath(path)) def test_dot_path_is_normalized(self): path = '.' - self.assertEqual('/', self.filesystem.absnormpath(path)) + self.assertEqual(self.root_name, self.filesystem.absnormpath(path)) class GetPathComponentsTest(TestCase): @@ -250,7 +275,7 @@ def test_two_level_absolute_path_should_return_components(self): class FakeFilesystemUnitTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') - self.root_name = '/' + self.root_name = self.filesystem.root_dir_name self.fake_file = fake_filesystem.FakeFile( 'foobar', filesystem=self.filesystem) self.fake_child = fake_filesystem.FakeDirectory( @@ -261,8 +286,7 @@ def setUp(self): def test_new_filesystem(self): self.assertEqual('/', self.filesystem.path_separator) self.assertTrue(stat.S_IFDIR & self.filesystem.root.st_mode) - self.assertEqual(self.root_name, self.filesystem.root.name) - self.assertEqual({}, self.filesystem.root.entries) + self.assertEqual({}, self.filesystem.root_dir.entries) def test_none_raises_type_error(self): with self.assertRaises(TypeError): @@ -284,13 +308,22 @@ def test_not_exists_subpath_named_like_file_contents(self): self.assertFalse(self.filesystem.exists(file_path + "/baz")) def test_get_root_object(self): - self.assertEqual(self.filesystem.root, + self.assertEqual(self.filesystem.root_dir, self.filesystem.get_object(self.root_name)) def test_add_object_to_root(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assertEqual({'foobar': self.fake_file}, - self.filesystem.root.entries) + self.filesystem.root_dir.entries) + + def test_windows_root_dir_name(self): + self.filesystem.is_windows_fs = True + self.filesystem.reset() + self.assertEqual('C:/', self.filesystem.root_dir_name) + self.filesystem.cwd = 'E:/foo' + self.assertEqual('E:/', self.filesystem.root_dir_name) + self.filesystem.cwd = '//foo/bar' + self.assertEqual('//foo/bar/', self.filesystem.root_dir_name) def test_exists_added_file(self): self.filesystem.add_object(self.root_name, self.fake_file) @@ -298,6 +331,7 @@ def test_exists_added_file(self): def test_exists_relative_path_posix(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() self.filesystem.create_file('/a/b/file_one') self.filesystem.create_file('/a/c/file_two') self.assertTrue(self.filesystem.exists('a/b/../c/file_two')) @@ -315,6 +349,7 @@ def test_exists_relative_path_posix(self): def test_exists_relative_path_windows(self): self.filesystem.is_windows_fs = True self.filesystem.is_macos = False + self.filesystem.reset() self.filesystem.create_file('/a/b/file_one') self.filesystem.create_file('/a/c/file_two') self.assertTrue(self.filesystem.exists('a/b/../c/file_two')) @@ -322,7 +357,7 @@ def test_exists_relative_path_windows(self): self.assertTrue(self.filesystem.exists('/a/c/../../a/b/file_one')) self.assertFalse(self.filesystem.exists('a/b/../z/d')) self.assertTrue(self.filesystem.exists('a/b/../z/../c/file_two')) - self.filesystem.cwd = '/a/c' + self.filesystem.cwd = 'C:/a/c' self.assertTrue(self.filesystem.exists('../b/file_one')) self.assertTrue(self.filesystem.exists('../../a/b/file_one')) self.assertTrue(self.filesystem.exists('../../a/b/../../a/c/file_two')) @@ -359,16 +394,19 @@ def test_add_object_to_child(self): self.filesystem.add_object(self.fake_child.name, self.fake_file) self.assertEqual( {self.fake_file.name: self.fake_file}, - self.filesystem.root.get_entry(self.fake_child.name).entries) + self.filesystem.root_dir.get_entry(self.fake_child.name).entries) def test_add_object_to_regular_file_error_posix(self): self.filesystem.is_windows_fs = False - self.filesystem.add_object(self.root_name, self.fake_file) + self.filesystem.reset() + self.filesystem.add_object( + self.filesystem.root_dir_name, self.fake_file) with self.raises_os_error(errno.ENOTDIR): self.filesystem.add_object(self.fake_file.name, self.fake_file) def test_add_object_to_regular_file_error_windows(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.filesystem.add_object(self.root_name, self.fake_file) with self.raises_os_error(errno.ENOENT): self.filesystem.add_object(self.fake_file.name, self.fake_file) @@ -480,6 +518,7 @@ def test_create_directory_already_exists_error(self): def test_create_file_in_read_only_directory_raises_in_posix(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() dir_path = '/foo/bar' self.filesystem.create_dir(dir_path, perm_bits=0o555) file_path = dir_path + '/baz' @@ -493,6 +532,7 @@ def test_create_file_in_read_only_directory_raises_in_posix(self): def test_create_file_in_read_only_directory_possible_in_windows(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() dir_path = 'C:/foo/bar' self.filesystem.create_dir(dir_path, perm_bits=0o555) file_path = dir_path + '/baz' @@ -615,10 +655,12 @@ def check_lresolve_object(self): def test_lresolve_object_windows(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.check_lresolve_object() def test_lresolve_object_posix(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() self.check_lresolve_object() def check_directory_access_on_file(self, error_subtype): @@ -630,10 +672,12 @@ def check_directory_access_on_file(self, error_subtype): def test_directory_access_on_file_windows(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.check_directory_access_on_file(errno.ENOENT) def test_directory_access_on_file_posix(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() self.check_directory_access_on_file(errno.ENOTDIR) def test_pickle_fs(self): @@ -690,7 +734,7 @@ def test_create_file_with_different_case_dir(self): def test_resolve_path(self): self.filesystem.create_dir('/foo/baz') self.filesystem.create_symlink('/Foo/Bar', './baz/bip') - self.assertEqual('/foo/baz/bip', + self.assertEqual(f'{self.filesystem.root_dir_name}foo/baz/bip', self.filesystem.resolve_path('/foo/bar')) def test_isdir_isfile(self): @@ -707,6 +751,7 @@ def test_getsize(self): def test_getsize_with_looping_symlink(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() dir_path = '/foo/bar' self.filesystem.create_dir(dir_path) link_path = dir_path + "/link" @@ -844,8 +889,9 @@ def setUp(self): def check_abspath(self, is_windows): # the implementation differs in Windows and Posix, so test both self.filesystem.is_windows_fs = is_windows + self.filesystem.reset() filename = 'foo' - abspath = '!%s' % filename + abspath = self.filesystem.root_dir_name + filename self.filesystem.create_file(abspath) self.assertEqual(abspath, self.path.abspath(abspath)) self.assertEqual(abspath, self.path.abspath(filename)) @@ -861,8 +907,9 @@ def test_abspath_posix(self): def check_abspath_bytes(self, is_windows): """abspath should return a consistent representation of a file.""" self.filesystem.is_windows_fs = is_windows + self.filesystem.reset() filename = b'foo' - abspath = b'!' + filename + abspath = self.filesystem.root_dir_name.encode() + filename self.filesystem.create_file(abspath) self.assertEqual(abspath, self.path.abspath(abspath)) self.assertEqual(abspath, self.path.abspath(filename)) @@ -883,16 +930,18 @@ def test_abspath_deals_with_relative_non_root_path(self): """ filename = '!foo!bar!baz' file_components = filename.split(self.path.sep) - basedir = '!%s' % (file_components[0],) + root_name = self.filesystem.root_dir_name + basedir = f'{root_name}{file_components[0]}' self.filesystem.create_file(filename) self.os.chdir(basedir) self.assertEqual(basedir, self.path.abspath(self.path.curdir)) - self.assertEqual('!', self.path.abspath('..')) + self.assertEqual(root_name, self.path.abspath('..')) self.assertEqual(self.path.join(basedir, file_components[1]), self.path.abspath(file_components[1])) def test_abs_path_with_drive_component(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.filesystem.cwd = 'C:!foo' self.assertEqual('C:!foo!bar', self.path.abspath('bar')) self.assertEqual('C:!foo!bar', self.path.abspath('C:bar')) @@ -900,11 +949,13 @@ def test_abs_path_with_drive_component(self): def test_isabs_with_drive_component(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() self.assertFalse(self.path.isabs('C:!foo')) self.assertFalse(self.path.isabs(b'C:!foo')) self.assertTrue(self.path.isabs('!')) self.assertTrue(self.path.isabs(b'!')) self.filesystem.is_windows_fs = True + self.filesystem.reset() self.assertTrue(self.path.isabs('C:!foo')) self.assertTrue(self.path.isabs(b'C:!foo')) self.assertTrue(self.path.isabs('!')) @@ -928,6 +979,7 @@ def test_relpath(self): def test_realpath_vs_abspath(self): self.filesystem.is_windows_fs = False + self.filesystem.reset() self.filesystem.create_file('!george!washington!bridge') self.filesystem.create_symlink('!first!president', '!george!washington') @@ -942,12 +994,13 @@ def test_realpath_vs_abspath(self): @unittest.skipIf(sys.version_info < (3, 10), "'strict' new in Python 3.10") def test_realpath_strict(self): self.filesystem.create_file('!foo!bar') - self.filesystem.cwd = '!foo' - self.assertEqual('!foo!baz', + root_dir = self.filesystem.root_dir_name + self.filesystem.cwd = f'{root_dir}foo' + self.assertEqual(f'{root_dir}foo!baz', self.os.path.realpath('baz', strict=False)) with self.raises_os_error(errno.ENOENT): self.os.path.realpath('baz', strict=True) - self.assertEqual('!foo!bar', + self.assertEqual(f'{root_dir}foo!bar', self.os.path.realpath('bar', strict=True)) def test_samefile(self): @@ -984,6 +1037,7 @@ def test_lexists(self): def test_dirname_with_drive(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.assertEqual('c:!foo', self.path.dirname('c:!foo!bar')) self.assertEqual(b'c:!', @@ -1074,7 +1128,7 @@ def test_isdir_with_cwd_change(self): self.assertTrue(self.path.isdir('!foo!bar')) self.assertTrue(self.path.isdir('foo')) self.assertTrue(self.path.isdir('foo!bar')) - self.filesystem.cwd = '!foo' + self.filesystem.cwd = f'{self.filesystem.root_dir_name}foo' self.assertTrue(self.path.isdir('!foo')) self.assertTrue(self.path.isdir('!foo!bar')) self.assertTrue(self.path.isdir('bar')) @@ -1135,6 +1189,7 @@ def test_ismount(self): def test_ismount_with_drive_letters(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.assertTrue(self.path.ismount('!')) self.assertTrue(self.path.ismount('c:!')) self.assertFalse(self.path.ismount('c:')) @@ -1145,6 +1200,7 @@ def test_ismount_with_drive_letters(self): def test_ismount_with_unc_paths(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.assertTrue(self.path.ismount('!!a!')) self.assertTrue(self.path.ismount('!!a!b')) self.assertTrue(self.path.ismount('!!a!b!')) @@ -1219,6 +1275,7 @@ def test_ignores_up_level_references_starting_from_root(self): self.assertEqual( '|', self.filesystem.normpath('|..|..|foo|bar|..|..|')) self.filesystem.is_windows_fs = False # not an UNC path + self.filesystem.reset() self.assertEqual('|', self.filesystem.normpath('||..|.|..||')) def test_conserves_up_level_references_starting_from_current_dir(self): @@ -1322,13 +1379,14 @@ def setUp(self): def test_normalize_case(self): self.filesystem.create_file('/Foo/Bar') - self.assertEqual('/Foo/Bar', + self.assertEqual(f'{self.filesystem.root_dir_name}Foo/Bar', self.filesystem._original_path('/foo/bar')) - self.assertEqual('/Foo/Bar', + self.assertEqual(f'{self.filesystem.root_dir_name}Foo/Bar', self.filesystem._original_path('/FOO/BAR')) def test_normalize_case_for_drive(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() self.filesystem.create_file('C:/Foo/Bar') self.assertEqual('C:/Foo/Bar', self.filesystem._original_path('c:/foo/bar')) @@ -1337,9 +1395,9 @@ def test_normalize_case_for_drive(self): def test_normalize_case_for_non_existing_file(self): self.filesystem.create_dir('/Foo/Bar') - self.assertEqual('/Foo/Bar/baz', + self.assertEqual(f'{self.filesystem.root_dir_name}Foo/Bar/baz', self.filesystem._original_path('/foo/bar/baz')) - self.assertEqual('/Foo/Bar/BAZ', + self.assertEqual(f'{self.filesystem.root_dir_name}Foo/Bar/BAZ', self.filesystem._original_path('/FOO/BAR/BAZ')) @unittest.skipIf(not TestCase.is_windows, @@ -1380,7 +1438,8 @@ def test_collapse_path_with_mixed_separators(self): def test_normalize_path_with_mixed_separators(self): path = 'foo?..?bar' - self.assertEqual('!bar', self.filesystem.absnormpath(path)) + self.assertEqual(f'{self.filesystem.root_dir_name}bar', + self.filesystem.absnormpath(path)) def test_exists_with_mixed_separators(self): self.filesystem.create_file('?foo?bar?baz') @@ -1394,6 +1453,7 @@ def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!') self.filesystem.alternative_path_separator = '^' self.filesystem.is_windows_fs = True + self.filesystem.reset() def test_initial_value(self): filesystem = fake_filesystem.FakeFilesystem() @@ -1470,8 +1530,8 @@ def test_characters_before_root_ignored_in_join_paths(self): self.assertEqual('c:d', self.filesystem.joinpaths('b', 'c:', 'd')) def test_resolve_path(self): - self.assertEqual('c:!foo!bar', - self.filesystem.resolve_path('c:!foo!bar')) + self.assertEqual('C:!foo!bar', + self.filesystem.resolve_path('C:!foo!bar')) def test_get_path_components(self): self.assertEqual(['c:', 'foo', 'bar'], @@ -1569,14 +1629,14 @@ def test_split_path_with_unc_path_alt_sep(self): class DiskSpaceTest(TestCase): def setUp(self): - self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!', - total_size=100) - self.os = fake_filesystem.FakeOsModule(self.filesystem) - self.open = fake_filesystem.FakeFileOpen(self.filesystem) + self.fs = fake_filesystem.FakeFilesystem(path_separator='!', + total_size=100) + self.os = fake_filesystem.FakeOsModule(self.fs) + self.open = fake_filesystem.FakeFileOpen(self.fs) def test_disk_usage_on_file_creation(self): total_size = 100 - self.filesystem.add_mount_point('mount', total_size) + self.fs.add_mount_point('!mount', total_size) def create_too_large_file(): with self.open('!mount!file', 'w') as dest: @@ -1585,13 +1645,32 @@ def create_too_large_file(): with self.assertRaises(OSError): create_too_large_file() - self.assertEqual(0, self.filesystem.get_disk_usage('!mount').used) + self.assertEqual(0, self.fs.get_disk_usage('!mount').used) with self.open('!mount!file', 'w') as dest: dest.write('a' * total_size) self.assertEqual(total_size, - self.filesystem.get_disk_usage('!mount').used) + self.fs.get_disk_usage('!mount').used) + + def test_disk_usage_on_automounted_drive(self): + self.fs.is_windows_fs = True + self.fs.reset(total_size=100) + self.fs.create_file('!foo!bar', st_size=50) + self.assertEqual(0, self.fs.get_disk_usage('D:!').used) + self.fs.cwd = 'E:!foo' + self.assertEqual(0, self.fs.get_disk_usage('!foo').used) + + def test_disk_usage_on_mounted_paths(self): + self.fs.add_mount_point('!foo', total_size=200) + self.fs.add_mount_point('!foo!bar', total_size=400) + self.fs.create_file('!baz', st_size=50) + self.fs.create_file('!foo!baz', st_size=60) + self.fs.create_file('!foo!bar!baz', st_size=100) + self.assertEqual(50, self.fs.get_disk_usage('!').used) + self.assertEqual(60, self.fs.get_disk_usage('!foo').used) + self.assertEqual(100, self.fs.get_disk_usage('!foo!bar').used) + self.assertEqual(400, self.fs.get_disk_usage('!foo!bar').total) def test_file_system_size_after_large_file_creation(self): filesystem = fake_filesystem.FakeFilesystem( @@ -1603,84 +1682,84 @@ def test_file_system_size_after_large_file_creation(self): filesystem.get_disk_usage()) def test_file_system_size_after_binary_file_creation(self): - self.filesystem.create_file('!foo!bar', contents=b'xyzzy') - self.assertEqual((100, 5, 95), self.filesystem.get_disk_usage()) + self.fs.create_file('!foo!bar', contents=b'xyzzy') + self.assertEqual((100, 5, 95), self.fs.get_disk_usage()) def test_file_system_size_after_ascii_string_file_creation(self): - self.filesystem.create_file('!foo!bar', contents='complicated') - self.assertEqual((100, 11, 89), self.filesystem.get_disk_usage()) + self.fs.create_file('!foo!bar', contents='complicated') + self.assertEqual((100, 11, 89), self.fs.get_disk_usage()) def test_filesystem_size_after_2byte_unicode_file_creation(self): - self.filesystem.create_file('!foo!bar', contents='сложно', - encoding='utf-8') - self.assertEqual((100, 12, 88), self.filesystem.get_disk_usage()) + self.fs.create_file('!foo!bar', contents='сложно', + encoding='utf-8') + self.assertEqual((100, 12, 88), self.fs.get_disk_usage()) def test_filesystem_size_after_3byte_unicode_file_creation(self): - self.filesystem.create_file('!foo!bar', contents='複雑', - encoding='utf-8') - self.assertEqual((100, 6, 94), self.filesystem.get_disk_usage()) + self.fs.create_file('!foo!bar', contents='複雑', + encoding='utf-8') + self.assertEqual((100, 6, 94), self.fs.get_disk_usage()) def test_file_system_size_after_file_deletion(self): - self.filesystem.create_file('!foo!bar', contents=b'xyzzy') - self.filesystem.create_file('!foo!baz', st_size=20) - self.filesystem.remove_object('!foo!bar') - self.assertEqual((100, 20, 80), self.filesystem.get_disk_usage()) + self.fs.create_file('!foo!bar', contents=b'xyzzy') + self.fs.create_file('!foo!baz', st_size=20) + self.fs.remove_object('!foo!bar') + self.assertEqual((100, 20, 80), self.fs.get_disk_usage()) def test_file_system_size_after_directory_removal(self): - self.filesystem.create_file('!foo!bar', st_size=10) - self.filesystem.create_file('!foo!baz', st_size=20) - self.filesystem.create_file('!foo1!bar', st_size=40) - self.filesystem.remove_object('!foo') - self.assertEqual((100, 40, 60), self.filesystem.get_disk_usage()) + self.fs.create_file('!foo!bar', st_size=10) + self.fs.create_file('!foo!baz', st_size=20) + self.fs.create_file('!foo1!bar', st_size=40) + self.fs.remove_object('!foo') + self.assertEqual((100, 40, 60), self.fs.get_disk_usage()) def test_creating_file_with_fitting_content(self): - initial_usage = self.filesystem.get_disk_usage() + initial_usage = self.fs.get_disk_usage() try: - self.filesystem.create_file('!foo!bar', contents=b'a' * 100) + self.fs.create_file('!foo!bar', contents=b'a' * 100) except OSError: self.fail('File with contents fitting into disk space ' 'could not be written.') self.assertEqual(initial_usage.used + 100, - self.filesystem.get_disk_usage().used) + self.fs.get_disk_usage().used) def test_creating_file_with_content_too_large(self): def create_large_file(): - self.filesystem.create_file('!foo!bar', contents=b'a' * 101) + self.fs.create_file('!foo!bar', contents=b'a' * 101) - initial_usage = self.filesystem.get_disk_usage() + initial_usage = self.fs.get_disk_usage() with self.assertRaises(OSError): create_large_file() - self.assertEqual(initial_usage, self.filesystem.get_disk_usage()) + self.assertEqual(initial_usage, self.fs.get_disk_usage()) def test_creating_file_with_fitting_size(self): - initial_usage = self.filesystem.get_disk_usage() + initial_usage = self.fs.get_disk_usage() try: - self.filesystem.create_file('!foo!bar', st_size=100) + self.fs.create_file('!foo!bar', st_size=100) except OSError: self.fail( 'File with size fitting into disk space could not be written.') self.assertEqual(initial_usage.used + 100, - self.filesystem.get_disk_usage().used) + self.fs.get_disk_usage().used) def test_creating_file_with_size_too_large(self): - initial_usage = self.filesystem.get_disk_usage() + initial_usage = self.fs.get_disk_usage() def create_large_file(): - self.filesystem.create_file('!foo!bar', st_size=101) + self.fs.create_file('!foo!bar', st_size=101) with self.assertRaises(OSError): create_large_file() - self.assertEqual(initial_usage, self.filesystem.get_disk_usage()) + self.assertEqual(initial_usage, self.fs.get_disk_usage()) def test_resize_file_with_fitting_size(self): - file_object = self.filesystem.create_file('!foo!bar', st_size=50) + file_object = self.fs.create_file('!foo!bar', st_size=50) try: file_object.set_large_file_size(100) file_object.set_contents(b'a' * 100) @@ -1689,112 +1768,114 @@ def test_resize_file_with_fitting_size(self): 'Resizing file failed although disk space was sufficient.') def test_resize_file_with_size_too_large(self): - file_object = self.filesystem.create_file('!foo!bar', st_size=50) + file_object = self.fs.create_file('!foo!bar', st_size=50) with self.raises_os_error(errno.ENOSPC): file_object.set_large_file_size(200) with self.raises_os_error(errno.ENOSPC): file_object.set_contents('a' * 150) def test_file_system_size_after_directory_rename(self): - self.filesystem.create_file('!foo!bar', st_size=20) + self.fs.create_file('!foo!bar', st_size=20) self.os.rename('!foo', '!baz') - self.assertEqual(20, self.filesystem.get_disk_usage().used) + self.assertEqual(20, self.fs.get_disk_usage().used) def test_file_system_size_after_file_rename(self): - self.filesystem.create_file('!foo!bar', st_size=20) + self.fs.create_file('!foo!bar', st_size=20) self.os.rename('!foo!bar', '!foo!baz') - self.assertEqual(20, self.filesystem.get_disk_usage().used) + self.assertEqual(20, self.fs.get_disk_usage().used) def test_that_hard_link_does_not_change_used_size(self): file1_path = 'test_file1' file2_path = 'test_file2' - self.filesystem.create_file(file1_path, st_size=20) - self.assertEqual(20, self.filesystem.get_disk_usage().used) + self.fs.create_file(file1_path, st_size=20) + self.assertEqual(20, self.fs.get_disk_usage().used) # creating a hard link shall not increase used space self.os.link(file1_path, file2_path) - self.assertEqual(20, self.filesystem.get_disk_usage().used) + self.assertEqual(20, self.fs.get_disk_usage().used) # removing a file shall not decrease used space # if a hard link still exists self.os.unlink(file1_path) - self.assertEqual(20, self.filesystem.get_disk_usage().used) + self.assertEqual(20, self.fs.get_disk_usage().used) self.os.unlink(file2_path) - self.assertEqual(0, self.filesystem.get_disk_usage().used) + self.assertEqual(0, self.fs.get_disk_usage().used) def test_that_the_size_of_correct_mount_point_is_used(self): - self.filesystem.add_mount_point('!mount_limited', total_size=50) - self.filesystem.add_mount_point('!mount_unlimited') + self.fs.add_mount_point('!mount_limited', total_size=50) + self.fs.add_mount_point('!mount_unlimited') with self.raises_os_error(errno.ENOSPC): - self.filesystem.create_file('!mount_limited!foo', st_size=60) + self.fs.create_file('!mount_limited!foo', st_size=60) with self.raises_os_error(errno.ENOSPC): - self.filesystem.create_file('!bar', st_size=110) + self.fs.create_file('!bar', st_size=110) try: - self.filesystem.create_file('!foo', st_size=60) - self.filesystem.create_file('!mount_limited!foo', st_size=40) - self.filesystem.create_file('!mount_unlimited!foo', - st_size=1000000) + self.fs.create_file('!foo', st_size=60) + self.fs.create_file('!mount_limited!foo', st_size=40) + self.fs.create_file('!mount_unlimited!foo', + st_size=1000000) except OSError: self.fail('File with contents fitting into ' 'disk space could not be written.') def test_that_disk_usage_of_correct_mount_point_is_used(self): - self.filesystem.add_mount_point('!mount1', total_size=20) - self.filesystem.add_mount_point('!mount1!bar!mount2', total_size=50) + self.fs.add_mount_point('!mount1', total_size=20) + self.fs.add_mount_point('!mount1!bar!mount2', total_size=50) - self.filesystem.create_file('!foo!bar', st_size=10) - self.filesystem.create_file('!mount1!foo!bar', st_size=10) - self.filesystem.create_file('!mount1!bar!mount2!foo!bar', st_size=10) + self.fs.create_file('!foo!bar', st_size=10) + self.fs.create_file('!mount1!foo!bar', st_size=10) + self.fs.create_file('!mount1!bar!mount2!foo!bar', st_size=10) - self.assertEqual(90, self.filesystem.get_disk_usage('!foo').free) + self.assertEqual(90, self.fs.get_disk_usage('!foo').free) self.assertEqual(10, - self.filesystem.get_disk_usage('!mount1!foo').free) - self.assertEqual(40, self.filesystem.get_disk_usage( + self.fs.get_disk_usage('!mount1!foo').free) + self.assertEqual(40, self.fs.get_disk_usage( '!mount1!bar!mount2').free) def test_set_larger_disk_size(self): - self.filesystem.add_mount_point('!mount1', total_size=20) + self.fs.add_mount_point('!mount1', total_size=20) with self.raises_os_error(errno.ENOSPC): - self.filesystem.create_file('!mount1!foo', st_size=100) - self.filesystem.set_disk_usage(total_size=200, path='!mount1') - self.filesystem.create_file('!mount1!foo', st_size=100) + self.fs.create_file('!mount1!foo', st_size=100) + self.fs.set_disk_usage(total_size=200, path='!mount1') + self.fs.create_file('!mount1!foo', st_size=100) self.assertEqual(100, - self.filesystem.get_disk_usage('!mount1!foo').free) + self.fs.get_disk_usage('!mount1!foo').free) def test_set_smaller_disk_size(self): - self.filesystem.add_mount_point('!mount1', total_size=200) - self.filesystem.create_file('!mount1!foo', st_size=100) + self.fs.add_mount_point('!mount1', total_size=200) + self.fs.create_file('!mount1!foo', st_size=100) with self.raises_os_error(errno.ENOSPC): - self.filesystem.set_disk_usage(total_size=50, path='!mount1') - self.filesystem.set_disk_usage(total_size=150, path='!mount1') + self.fs.set_disk_usage(total_size=50, path='!mount1') + self.fs.set_disk_usage(total_size=150, path='!mount1') self.assertEqual(50, - self.filesystem.get_disk_usage('!mount1!foo').free) + self.fs.get_disk_usage('!mount1!foo').free) def test_disk_size_on_unlimited_disk(self): - self.filesystem.add_mount_point('!mount1') - self.filesystem.create_file('!mount1!foo', st_size=100) - self.filesystem.set_disk_usage(total_size=1000, path='!mount1') + self.fs.add_mount_point('!mount1') + self.fs.create_file('!mount1!foo', st_size=100) + self.fs.set_disk_usage(total_size=1000, path='!mount1') self.assertEqual(900, - self.filesystem.get_disk_usage('!mount1!foo').free) + self.fs.get_disk_usage('!mount1!foo').free) def test_disk_size_on_auto_mounted_drive_on_file_creation(self): - self.filesystem.is_windows_fs = True + self.fs.is_windows_fs = True + self.fs.reset() # drive d: shall be auto-mounted and the used size adapted - self.filesystem.create_file('d:!foo!bar', st_size=100) - self.filesystem.set_disk_usage(total_size=1000, path='d:') - self.assertEqual(self.filesystem.get_disk_usage('d:!foo').free, 900) + self.fs.create_file('d:!foo!bar', st_size=100) + self.fs.set_disk_usage(total_size=1000, path='d:') + self.assertEqual(self.fs.get_disk_usage('d:!foo').free, 900) def test_disk_size_on_auto_mounted_drive_on_directory_creation(self): - self.filesystem.is_windows_fs = True - self.filesystem.create_dir('d:!foo!bar') - self.filesystem.create_file('d:!foo!bar!baz', st_size=100) - self.filesystem.create_file('d:!foo!baz', st_size=100) - self.filesystem.set_disk_usage(total_size=1000, path='d:') - self.assertEqual(800, self.filesystem.get_disk_usage('d:!foo').free) + self.fs.is_windows_fs = True + self.fs.reset() + self.fs.create_dir('d:!foo!bar') + self.fs.create_file('d:!foo!bar!baz', st_size=100) + self.fs.create_file('d:!foo!baz', st_size=100) + self.fs.set_disk_usage(total_size=1000, path='d:') + self.assertEqual(800, self.fs.get_disk_usage('d:!foo').free) def test_copying_preserves_byte_contents(self): - source_file = self.filesystem.create_file('foo', contents=b'somebytes') - dest_file = self.filesystem.create_file('bar') + source_file = self.fs.create_file('foo', contents=b'somebytes') + dest_file = self.fs.create_file('bar') dest_file.set_contents(source_file.contents) self.assertEqual(dest_file.contents, source_file.contents) @@ -1802,7 +1883,7 @@ def test_diskusage_after_open_write(self): with self.open('bar.txt', 'w') as f: f.write('a' * 60) f.flush() - self.assertEqual(60, self.filesystem.get_disk_usage()[1]) + self.assertEqual(60, self.fs.get_disk_usage()[1]) def test_disk_full_after_reopened(self): with self.open('bar.txt', 'w') as f: @@ -1850,6 +1931,9 @@ class MountPointTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!', total_size=100) + self.add_mount_points() + + def add_mount_points(self): self.filesystem.add_mount_point('!foo') self.filesystem.add_mount_point('!bar') self.filesystem.add_mount_point('!foo!baz') @@ -1880,6 +1964,8 @@ def test_that_mount_point_cannot_be_added_twice(self): def test_that_drives_are_auto_mounted(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() + self.add_mount_points() self.filesystem.create_dir('d:!foo!bar') self.filesystem.create_file('d:!foo!baz') self.filesystem.create_file('z:!foo!baz') @@ -1890,6 +1976,8 @@ def test_that_drives_are_auto_mounted(self): def test_that_drives_are_auto_mounted_case_insensitive(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() + self.add_mount_points() self.filesystem.is_case_sensitive = False self.filesystem.create_dir('D:!foo!bar') self.filesystem.create_file('e:!foo!baz') @@ -1900,6 +1988,8 @@ def test_that_drives_are_auto_mounted_case_insensitive(self): def test_that_unc_paths_are_auto_mounted(self): self.filesystem.is_windows_fs = True + self.filesystem.reset() + self.add_mount_points() self.filesystem.create_dir('!!foo!bar!baz') self.filesystem.create_file('!!foo!bar!bip!bop') self.assertEqual(5, self.filesystem.get_object('!!foo!bar').st_dev) diff --git a/pyfakefs/tests/fake_filesystem_unittest_test.py b/pyfakefs/tests/fake_filesystem_unittest_test.py index f4da6631..97b5f7b6 100644 --- a/pyfakefs/tests/fake_filesystem_unittest_test.py +++ b/pyfakefs/tests/fake_filesystem_unittest_test.py @@ -733,9 +733,11 @@ class PathlibTest(TestCase): @patchfs def test_cwd(self, fs): """Make sure fake file system is used for os in pathlib""" - self.assertEqual(os.path.sep, str(pathlib.Path.cwd())) + is_windows = sys.platform.startswith('win') + root_dir = 'C:' + os.path.sep if is_windows else os.path.sep + self.assertEqual(root_dir, str(pathlib.Path.cwd())) dot_abs = pathlib.Path(".").absolute() - self.assertEqual(os.path.sep, str(dot_abs)) + self.assertEqual(root_dir, str(dot_abs)) self.assertTrue(dot_abs.exists()) @@ -796,13 +798,13 @@ def setUp(self): def test_real_file_with_home(self): """Regression test for #558""" self.fs.is_windows_fs = os.name != 'nt' + if self.fs.is_windows_fs: + self.fs.is_macos = False + self.fs.reset() self.fs.add_real_file(__file__) with open(__file__) as f: self.assertTrue(f.read()) home = Path.home() - if sys.version_info < (3, 6): - # fspath support since Python 3.6 - home = str(home) os.chdir(home) with open(__file__) as f: self.assertTrue(f.read()) @@ -869,5 +871,13 @@ def test_drivelike_path(self): self.assertTrue(os.path.exists(str(file_path.relative_to(folder)))) +@unittest.skipIf(sys.platform != 'win32', 'Windows-specific behavior') +class TestAbsolutePathOnWindows(fake_filesystem_unittest.TestCase): + @patchfs + def test_is_absolute(self, fs): + # regression test for #673 + self.assertTrue(pathlib.Path(".").absolute().is_absolute()) + + if __name__ == "__main__": unittest.main() diff --git a/pyfakefs/tests/fake_filesystem_vs_real_test.py b/pyfakefs/tests/fake_filesystem_vs_real_test.py index 3be10769..d9b194df 100644 --- a/pyfakefs/tests/fake_filesystem_vs_real_test.py +++ b/pyfakefs/tests/fake_filesystem_vs_real_test.py @@ -43,7 +43,7 @@ def _get_errno(raised_error): class TestCase(unittest.TestCase): is_windows = sys.platform.startswith('win') - _FAKE_FS_BASE = sep('/fakefs') + _FAKE_FS_BASE = 'C:\\fakefs' if is_windows else '/fakefs' class FakeFilesystemVsRealTest(TestCase): diff --git a/pyfakefs/tests/fake_os_test.py b/pyfakefs/tests/fake_os_test.py index 7408d17f..b4361aa1 100644 --- a/pyfakefs/tests/fake_os_test.py +++ b/pyfakefs/tests/fake_os_test.py @@ -106,9 +106,9 @@ def test_get_cwd(self): self.skip_real_fs() dirname = self.make_path('foo', 'bar') self.create_dir(dirname) - self.assertEqual(self.os.getcwd(), self.os.path.sep) + self.assertEqual(self.filesystem.root_dir_name, self.os.getcwd()) self.os.chdir(dirname) - self.assertEqual(self.os.getcwd(), dirname) + self.assertEqual(dirname, self.os.getcwd()) def test_listdir(self): self.assert_raises_os_error( diff --git a/pyfakefs/tests/fake_tempfile_test.py b/pyfakefs/tests/fake_tempfile_test.py index 7ddd4e1f..a150fd28 100644 --- a/pyfakefs/tests/fake_tempfile_test.py +++ b/pyfakefs/tests/fake_tempfile_test.py @@ -77,7 +77,7 @@ def test_mkstemp_dir(self): self.assertEqual(2, len(temporary)) self.assertEqual(next_fd, temporary[0]) self.assertTrue(temporary[1].startswith( - os.path.join(os.sep, 'dir', 'tmp'))) + os.path.join(self.fs.root_dir_name, 'dir', 'tmp'))) self.assertTrue(self.fs.exists(temporary[1])) mode = 0o666 if self.fs.is_windows_fs else 0o600 self.assertEqual(self.fs.get_object(temporary[1]).st_mode,