From 02d86bd0b817bd9cb670d1282214c6bed3fb0a16 Mon Sep 17 00:00:00 2001 From: mrbean-bremen Date: Fri, 28 Jan 2022 18:17:52 +0100 Subject: [PATCH] Do not set size to 0 on setting initial content - caused file system file to be wrong for non-empty files - tests: add contextmanager version of assert_raises_os_error - fixes #660 --- CHANGES.md | 6 + pyfakefs/fake_filesystem.py | 2 - pyfakefs/tests/fake_filesystem_test.py | 222 +++++++++++++------------ pyfakefs/tests/test_utils.py | 12 ++ 4 files changed, 133 insertions(+), 109 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ae83346f..36da884b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,12 @@ # pyfakefs Release Notes The released versions correspond to PyPi releases. +## Unreleased + +### Fixes +* correctly handle file system space for files opened in write mode + (see [#660](../../issues/660)) + ## [Version 4.5.4](https://pypi.python.org/pypi/pyfakefs/4.5.4) (2022-01-12) Minor bugfix release. diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index a8c5f7a4..2a863d1c 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -439,8 +439,6 @@ def set_initial_contents(self, contents: AnyStr) -> bool: current_size = self.st_size or 0 self.filesystem.change_disk_usage( st_size - current_size, self.name, self.st_dev) - if self._byte_contents: - self.size = 0 self._byte_contents = byte_contents self.st_size = st_size self.epoch += 1 diff --git a/pyfakefs/tests/fake_filesystem_test.py b/pyfakefs/tests/fake_filesystem_test.py index dfaff5ff..95953f14 100644 --- a/pyfakefs/tests/fake_filesystem_test.py +++ b/pyfakefs/tests/fake_filesystem_test.py @@ -93,17 +93,13 @@ def test_remove_entry(self): self.fake_dir.get_entry('foobar') def test_should_throw_if_set_size_is_not_integer(self): - def set_size(): + with self.raises_os_error(errno.ENOSPC): self.fake_file.size = 0.1 - self.assert_raises_os_error(errno.ENOSPC, set_size) - def test_should_throw_if_set_size_is_negative(self): - def set_size(): + with self.raises_os_error(errno.ENOSPC): self.fake_file.size = -1 - self.assert_raises_os_error(errno.ENOSPC, set_size) - def test_produce_empty_file_if_set_size_is_zero(self): self.fake_file.size = 0 self.assertEqual('', self.fake_file.contents) @@ -123,11 +119,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.assert_raises_os_error( - errno.EISDIR, self.fake_dir.set_contents, 'a') + with self.raises_os_error(errno.EISDIR): + self.fake_dir.set_contents('a') self.filesystem.is_windows_fs = False - self.assert_raises_os_error( - errno.EISDIR, self.fake_dir.set_contents, 'a') + with self.raises_os_error(errno.EISDIR): + self.fake_dir.set_contents('a') def test_pads_with_nullbytes_if_size_is_greater_than_current_size(self): self.fake_file.size = 13 @@ -180,12 +176,12 @@ def setUp(self): filesystem=filesystem) def test_should_throw_if_size_is_not_integer(self): - self.assert_raises_os_error(errno.ENOSPC, - self.fake_file.set_large_file_size, 0.1) + with self.raises_os_error(errno.ENOSPC): + self.fake_file.set_large_file_size(0.1) def test_should_throw_if_size_is_negative(self): - self.assert_raises_os_error(errno.ENOSPC, - self.fake_file.set_large_file_size, -1) + with self.raises_os_error(errno.ENOSPC): + self.fake_file.set_large_file_size(-1) def test_sets_content_none_if_size_is_non_negative_integer(self): self.fake_file.set_large_file_size(1000000000) @@ -340,18 +336,18 @@ def test_get_object_from_root(self): def test_get_nonexistent_object_from_root_error(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assertEqual(self.fake_file, self.filesystem.get_object('foobar')) - self.assert_raises_os_error( - errno.ENOENT, self.filesystem.get_object, 'some_bogus_filename') + with self.raises_os_error(errno.ENOENT): + self.filesystem.get_object('some_bogus_filename') def test_remove_object_from_root(self): self.filesystem.add_object(self.root_name, self.fake_file) self.filesystem.remove_object(self.fake_file.name) - self.assert_raises_os_error( - errno.ENOENT, self.filesystem.get_object, self.fake_file.name) + with self.raises_os_error(errno.ENOENT): + self.filesystem.get_object(self.fake_file.name) def test_remove_nonexisten_object_from_root_error(self): - self.assert_raises_os_error( - errno.ENOENT, self.filesystem.remove_object, 'some_bogus_filename') + with self.raises_os_error(errno.ENOENT): + self.filesystem.remove_object('some_bogus_filename') def test_exists_removed_file(self): self.filesystem.add_object(self.root_name, self.fake_file) @@ -368,16 +364,14 @@ def test_add_object_to_child(self): 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.assert_raises_os_error(errno.ENOTDIR, - self.filesystem.add_object, - self.fake_file.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.add_object(self.root_name, self.fake_file) - self.assert_raises_os_error(errno.ENOENT, - self.filesystem.add_object, - self.fake_file.name, self.fake_file) + with self.raises_os_error(errno.ENOENT): + self.filesystem.add_object(self.fake_file.name, self.fake_file) def test_exists_file_added_to_child(self): self.filesystem.add_object(self.root_name, self.fake_child) @@ -397,10 +391,9 @@ def test_get_object_from_child(self): def test_get_nonexistent_object_from_child_error(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) - self.assert_raises_os_error(errno.ENOENT, self.filesystem.get_object, - self.filesystem.joinpaths( - self.fake_child.name, - 'some_bogus_filename')) + with self.raises_os_error(errno.ENOENT): + self.filesystem.get_object(self.filesystem.joinpaths( + self.fake_child.name, 'some_bogus_filename')) def test_remove_object_from_child(self): self.filesystem.add_object(self.root_name, self.fake_child) @@ -408,23 +401,23 @@ def test_remove_object_from_child(self): target_path = self.filesystem.joinpaths(self.fake_child.name, self.fake_file.name) self.filesystem.remove_object(target_path) - self.assert_raises_os_error(errno.ENOENT, self.filesystem.get_object, - target_path) + with self.raises_os_error(errno.ENOENT): + self.filesystem.get_object(target_path) def test_remove_object_from_child_error(self): self.filesystem.add_object(self.root_name, self.fake_child) - self.assert_raises_os_error( - errno.ENOENT, self.filesystem.remove_object, - self.filesystem.joinpaths(self.fake_child.name, - 'some_bogus_filename')) + with self.raises_os_error(errno.ENOENT): + self.filesystem.remove_object( + self.filesystem.joinpaths(self.fake_child.name, + 'some_bogus_filename')) def test_remove_object_from_non_directory_error(self): self.filesystem.add_object(self.root_name, self.fake_file) - self.assert_raises_os_error( - errno.ENOTDIR, self.filesystem.remove_object, - self.filesystem.joinpaths( - '%s' % self.fake_file.name, - 'file_does_not_matter_since_parent_not_a_directory')) + with self.raises_os_error(errno.ENOTDIR): + self.filesystem.remove_object( + self.filesystem.joinpaths( + '%s' % self.fake_file.name, + 'file_does_not_matter_since_parent_not_a_directory')) def test_exists_file_removed_from_child(self): self.filesystem.add_object(self.root_name, self.fake_child) @@ -462,8 +455,8 @@ def test_create_directory_in_root_directory(self): def test_create_directory_in_root_directory_already_exists_error(self): path = 'foo' self.filesystem.create_dir(path) - self.assert_raises_os_error( - errno.EEXIST, self.filesystem.create_dir, path) + with self.raises_os_error(errno.EEXIST): + self.filesystem.create_dir(path) def test_create_directory(self): path = 'foo/bar/baz' @@ -482,8 +475,8 @@ def test_create_directory(self): def test_create_directory_already_exists_error(self): path = 'foo/bar/baz' self.filesystem.create_dir(path) - self.assert_raises_os_error( - errno.EEXIST, self.filesystem.create_dir, path) + with self.raises_os_error(errno.EEXIST): + self.filesystem.create_dir(path) def test_create_file_in_read_only_directory_raises_in_posix(self): self.filesystem.is_windows_fs = False @@ -492,9 +485,8 @@ def test_create_file_in_read_only_directory_raises_in_posix(self): file_path = dir_path + '/baz' if not is_root(): - self.assert_raises_os_error(errno.EACCES, - self.filesystem.create_file, - file_path) + with self.raises_os_error(errno.EACCES): + self.filesystem.create_file(file_path) else: self.filesystem.create_file(file_path) self.assertTrue(self.filesystem.exists(file_path)) @@ -537,8 +529,8 @@ def test_create_file_with_size_but_no_content_creates_large_file(self): def test_create_file_in_root_directory_already_exists_error(self): path = 'foo' self.filesystem.create_file(path) - self.assert_raises_os_error( - errno.EEXIST, self.filesystem.create_file, path) + with self.raises_os_error(errno.EEXIST): + self.filesystem.create_file(path) def test_create_file(self): path = 'foo/bar/baz' @@ -580,8 +572,8 @@ def test_create_file_with_incorrect_mode_type(self): def test_create_file_already_exists_error(self): path = 'foo/bar/baz' self.filesystem.create_file(path, contents='dummy_data') - self.assert_raises_os_error( - errno.EEXIST, self.filesystem.create_file, path) + with self.raises_os_error(errno.EEXIST): + self.filesystem.create_file(path) def test_create_link(self): path = 'foo/bar/baz' @@ -631,10 +623,10 @@ def test_lresolve_object_posix(self): def check_directory_access_on_file(self, error_subtype): self.filesystem.create_file('not_a_dir') - self.assert_raises_os_error( - error_subtype, self.filesystem.resolve, 'not_a_dir/foo') - self.assert_raises_os_error( - error_subtype, self.filesystem.lresolve, 'not_a_dir/foo/bar') + with self.raises_os_error(error_subtype): + self.filesystem.resolve('not_a_dir/foo') + with self.raises_os_error(error_subtype): + self.filesystem.lresolve('not_a_dir/foo/bar') def test_directory_access_on_file_windows(self): self.filesystem.is_windows_fs = True @@ -720,8 +712,8 @@ def test_getsize_with_looping_symlink(self): link_path = dir_path + "/link" link_target = link_path + "/link" self.os.symlink(link_target, link_path) - self.assert_raises_os_error( - errno.ELOOP, self.os.path.getsize, link_path) + with self.raises_os_error(errno.ELOOP): + self.os.path.getsize(link_path) def test_get_mtime(self): test_file = self.filesystem.create_file('foo/bar1.txt') @@ -792,8 +784,8 @@ def test_getsize(self): def test_get_mtime(self): test_file = self.filesystem.create_file('foo/bar1.txt') test_file.st_mtime = 24 - self.assert_raises_os_error( - errno.ENOENT, self.path.getmtime, 'Foo/Bar1.TXT') + with self.raises_os_error(errno.ENOENT): + self.path.getmtime('Foo/Bar1.TXT') class OsPathInjectionRegressionTest(TestCase): @@ -953,9 +945,8 @@ def test_realpath_strict(self): self.filesystem.cwd = '!foo' self.assertEqual('!foo!baz', self.os.path.realpath('baz', strict=False)) - self.assert_raises_os_error(errno.ENOENT, - self.os.path.realpath, - 'baz', strict=True) + with self.raises_os_error(errno.ENOENT): + self.os.path.realpath('baz', strict=True) self.assertEqual('!foo!bar', self.os.path.realpath('bar', strict=True)) @@ -1103,9 +1094,9 @@ def test_get_mtime(self): self.assertEqual(24, self.path.getmtime(b'foo!bar1.txt')) def test_get_mtime_raises_os_error(self): - self.assertFalse(self.path.exists('it_dont_exist')) - self.assert_raises_os_error(errno.ENOENT, self.path.getmtime, - 'it_dont_exist') + self.assertFalse(self.path.exists('does_not_exist')) + with self.raises_os_error(errno.ENOENT): + self.path.getmtime('does_not_exist') def test_islink(self): self.filesystem.create_dir('foo') @@ -1581,15 +1572,14 @@ 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) def test_disk_usage_on_file_creation(self): - fake_open = fake_filesystem.FakeFileOpen(self.filesystem) - total_size = 100 self.filesystem.add_mount_point('mount', total_size) def create_too_large_file(): - with fake_open('!mount!file', 'w') as dest: + with self.open('!mount!file', 'w') as dest: dest.write('a' * (total_size + 1)) with self.assertRaises(OSError): @@ -1597,7 +1587,7 @@ def create_too_large_file(): self.assertEqual(0, self.filesystem.get_disk_usage('!mount').used) - with fake_open('!mount!file', 'w') as dest: + with self.open('!mount!file', 'w') as dest: dest.write('a' * total_size) self.assertEqual(total_size, @@ -1700,10 +1690,10 @@ def test_resize_file_with_fitting_size(self): def test_resize_file_with_size_too_large(self): file_object = self.filesystem.create_file('!foo!bar', st_size=50) - self.assert_raises_os_error(errno.ENOSPC, - file_object.set_large_file_size, 200) - self.assert_raises_os_error(errno.ENOSPC, file_object.set_contents, - 'a' * 150) + 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) @@ -1734,11 +1724,10 @@ 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.assert_raises_os_error(errno.ENOSPC, - self.filesystem.create_file, - '!mount_limited!foo', st_size=60) - self.assert_raises_os_error(errno.ENOSPC, self.filesystem.create_file, - '!bar', st_size=110) + with self.raises_os_error(errno.ENOSPC): + self.filesystem.create_file('!mount_limited!foo', st_size=60) + with self.raises_os_error(errno.ENOSPC): + self.filesystem.create_file('!bar', st_size=110) try: self.filesystem.create_file('!foo', st_size=60) @@ -1765,9 +1754,8 @@ def test_that_disk_usage_of_correct_mount_point_is_used(self): def test_set_larger_disk_size(self): self.filesystem.add_mount_point('!mount1', total_size=20) - self.assert_raises_os_error(errno.ENOSPC, - self.filesystem.create_file, '!mount1!foo', - st_size=100) + 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.assertEqual(100, @@ -1776,9 +1764,8 @@ def test_set_larger_disk_size(self): 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.assert_raises_os_error(errno.ENOSPC, - self.filesystem.set_disk_usage, - total_size=50, path='!mount1') + 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.assertEqual(50, self.filesystem.get_disk_usage('!mount1!foo').free) @@ -1811,6 +1798,31 @@ def test_copying_preserves_byte_contents(self): dest_file.set_contents(source_file.contents) self.assertEqual(dest_file.contents, source_file.contents) + def test_diskusage_after_open_write(self): + with self.open('bar.txt', 'w') as f: + f.write('a' * 60) + f.flush() + self.assertEqual(self.filesystem.get_disk_usage()[1], 60) + + def test_disk_full_after_reopened(self): + with self.open('bar.txt', 'w') as f: + f.write('a' * 60) + with self.open('bar.txt') as f: + self.assertEqual(f.read(), 'a' * 60) + with self.raises_os_error(errno.ENOSPC): + with self.open('bar.txt', 'w') as f: + f.write('b' * 110) + + def test_disk_full_after_reopened_rplus_seek(self): + with self.open('bar.txt', 'w') as f: + f.write('a' * 60) + with self.open('bar.txt') as f: + self.assertEqual(f.read(), 'a' * 60) + with self.raises_os_error(errno.ENOSPC): + with self.open('bar.txt', 'r+') as f: + f.seek(50) + f.write('b' * 60) + class MountPointTest(TestCase): def setUp(self): @@ -1839,10 +1851,10 @@ def test_that_new_files_get_correct_device_number(self): '!foo!baz!foo!bar').st_dev) def test_that_mount_point_cannot_be_added_twice(self): - self.assert_raises_os_error(errno.EEXIST, - self.filesystem.add_mount_point, '!foo') - self.assert_raises_os_error(errno.EEXIST, - self.filesystem.add_mount_point, '!foo!') + with self.raises_os_error(errno.EEXIST): + self.filesystem.add_mount_point('!foo') + with self.raises_os_error(errno.EEXIST): + self.filesystem.add_mount_point('!foo!') def test_that_drives_are_auto_mounted(self): self.filesystem.is_windows_fs = True @@ -1914,23 +1926,20 @@ def test_add_non_existing_real_file_raises(self): def test_add_non_existing_real_directory_raises(self): nonexisting_path = '/nonexisting' - self.assert_raises_os_error(errno.ENOENT, - self.filesystem.add_real_directory, - nonexisting_path) + with self.raises_os_error(errno.ENOENT): + self.filesystem.add_real_directory(nonexisting_path) self.assertFalse(self.filesystem.exists(nonexisting_path)) def test_existing_fake_file_raises(self): real_file_path = __file__ self.filesystem.create_file(real_file_path) - self.assert_raises_os_error(errno.EEXIST, - self.filesystem.add_real_file, - real_file_path) + with self.raises_os_error(errno.EEXIST): + self.filesystem.add_real_file(real_file_path) def test_existing_fake_directory_raises(self): self.filesystem.create_dir(self.root_path) - self.assert_raises_os_error(errno.EEXIST, - self.filesystem.add_real_directory, - self.root_path) + with self.raises_os_error(errno.EEXIST): + self.filesystem.add_real_directory(self.root_path) def check_fake_file_stat(self, fake_file, real_file_path, target_path=None): @@ -1957,8 +1966,8 @@ def check_read_only_file(self, fake_file, real_file_path): real_contents = f.read() self.assertEqual(fake_file.byte_contents, real_contents) if not is_root(): - self.assert_raises_os_error( - errno.EACCES, self.fake_open, real_file_path, 'w') + with self.raises_os_error(errno.EACCES): + self.fake_open(real_file_path, 'w') else: with self.fake_open(real_file_path, 'w'): pass @@ -1995,9 +2004,9 @@ def test_add_existing_real_file_read_write(self): def test_add_real_file_to_existing_path(self): real_file_path = os.path.abspath(__file__) self.filesystem.create_file('/foo/bar') - self.assert_raises_os_error( - errno.EEXIST, self.filesystem.add_real_file, - real_file_path, target_path='/foo/bar') + with self.raises_os_error(errno.EEXIST): + self.filesystem.add_real_file(real_file_path, + target_path='/foo/bar') def test_add_real_file_to_non_existing_path(self): real_file_path = os.path.abspath(__file__) @@ -2172,10 +2181,9 @@ def test_add_existing_real_directory_symlink_lazy_read(self): def test_add_existing_real_directory_tree_to_existing_path(self): self.filesystem.create_dir('/foo/bar') - self.assert_raises_os_error(errno.EEXIST, - self.filesystem.add_real_directory, - self.root_path, - target_path='/foo/bar') + with self.raises_os_error(errno.EEXIST): + self.filesystem.add_real_directory( + self.root_path, target_path='/foo/bar') def test_add_existing_real_directory_tree_to_other_path(self): self.filesystem.add_real_directory(self.root_path, diff --git a/pyfakefs/tests/test_utils.py b/pyfakefs/tests/test_utils.py index 6c940955..eea81c49 100644 --- a/pyfakefs/tests/test_utils.py +++ b/pyfakefs/tests/test_utils.py @@ -21,6 +21,7 @@ import sys import tempfile import unittest +from contextlib import contextmanager from unittest import mock from pyfakefs import fake_filesystem @@ -69,6 +70,17 @@ class TestCase(unittest.TestCase): def assert_mode_equal(self, expected, actual): return self.assertEqual(stat.S_IMODE(expected), stat.S_IMODE(actual)) + @contextmanager + def raises_os_error(self, subtype): + try: + yield + self.fail('No exception was raised, OSError expected') + except OSError as exc: + if isinstance(subtype, list): + self.assertIn(exc.errno, subtype) + else: + self.assertEqual(subtype, exc.errno) + def assert_raises_os_error(self, subtype, expression, *args, **kwargs): """Asserts that a specific subtype of OSError is raised.""" try: