1#
2# Copyright 2009 Google Inc. All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Unit tests for fake_os.FakeOsModule."""
17
18import errno
19import os
20import stat
21import sys
22import unittest
23
24from pyfakefs.helpers import IN_DOCKER, IS_PYPY, get_uid, get_gid
25
26from pyfakefs import fake_filesystem, fake_os, fake_open, fake_file
27from pyfakefs.fake_filesystem import (
28    FakeFileOpen,
29    is_root,
30    set_uid,
31    set_gid,
32)
33from pyfakefs.extra_packages import (
34    use_scandir,
35    use_scandir_package,
36    use_builtin_scandir,
37)
38
39from pyfakefs.tests.test_utils import TestCase, RealFsTestCase
40
41
42class FakeOsModuleTestBase(RealFsTestCase):
43    def createTestFile(self, path):
44        self.create_file(path)
45        self.assertTrue(self.os.path.exists(path))
46        st = self.os.stat(path)
47        self.assertEqual(0o666, stat.S_IMODE(st.st_mode))
48        self.assertTrue(st.st_mode & stat.S_IFREG)
49        self.assertFalse(st.st_mode & stat.S_IFDIR)
50
51    def createTestDirectory(self, path):
52        self.create_dir(path)
53        self.assertTrue(self.os.path.exists(path))
54        st = self.os.stat(path)
55        self.assertEqual(0o777, stat.S_IMODE(st.st_mode))
56        self.assertFalse(st.st_mode & stat.S_IFREG)
57        self.assertTrue(st.st_mode & stat.S_IFDIR)
58
59
60class FakeOsModuleTest(FakeOsModuleTestBase):
61    def setUp(self):
62        super(FakeOsModuleTest, self).setUp()
63        self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK
64        self.rw = self.os.R_OK | self.os.W_OK
65
66    def test_chdir(self):
67        """chdir should work on a directory."""
68        directory = self.make_path("foo")
69        self.create_dir(directory)
70        self.os.chdir(directory)
71
72    def test_chdir_fails_non_exist(self):
73        """chdir should raise OSError if the target does not exist."""
74        directory = self.make_path("no", "such", "directory")
75        self.assert_raises_os_error(errno.ENOENT, self.os.chdir, directory)
76
77    def test_chdir_fails_non_directory(self):
78        """chdir should raise OSError if the target is not a directory."""
79        filename = self.make_path("foo", "bar")
80        self.create_file(filename)
81        self.assert_raises_os_error(errno.ENOTDIR, self.os.chdir, filename)
82
83    def test_consecutive_chdir(self):
84        """Consecutive relative chdir calls should work."""
85        dir1 = self.make_path("foo")
86        dir2 = "bar"
87        full_dirname = self.os.path.join(dir1, dir2)
88        self.create_dir(full_dirname)
89        self.os.chdir(dir1)
90        self.os.chdir(dir2)
91        # use real path to handle symlink /var to /private/var in MacOs
92        self.assertEqual(
93            os.path.realpath(self.os.getcwd()), os.path.realpath(full_dirname)
94        )
95
96    def test_backwards_chdir(self):
97        """chdir into '..' should behave appropriately."""
98        # skipping real fs test - can't test root dir
99        self.skip_real_fs()
100        rootdir = self.os.getcwd()
101        dirname = "foo"
102        abs_dirname = self.os.path.abspath(dirname)
103        self.filesystem.create_dir(dirname)
104        self.os.chdir(dirname)
105        self.assertEqual(abs_dirname, self.os.getcwd())
106        self.os.chdir("..")
107        self.assertEqual(rootdir, self.os.getcwd())
108        self.os.chdir(self.os.path.join(dirname, ".."))
109        self.assertEqual(rootdir, self.os.getcwd())
110
111    def test_get_cwd(self):
112        # skipping real fs test - can't test root dir
113        self.skip_real_fs()
114        dirname = self.make_path("foo", "bar")
115        self.create_dir(dirname)
116        self.assertEqual(self.filesystem.root_dir_name, self.os.getcwd())
117        self.os.chdir(dirname)
118        self.assertEqual(dirname, self.os.getcwd())
119
120    def test_listdir(self):
121        self.assert_raises_os_error(
122            errno.ENOENT, self.os.listdir, "non_existing/fake_dir"
123        )
124        directory = self.make_path("xyzzy", "plugh")
125        files = ["foo", "bar", "baz"]
126        for f in files:
127            self.create_file(self.os.path.join(directory, f))
128        files.sort()
129        self.assertEqual(files, sorted(self.os.listdir(directory)))
130
131    def test_listdir_uses_open_fd_as_path(self):
132        self.check_posix_only()
133        if os.listdir not in os.supports_fd:
134            self.skip_real_fs()
135        self.assert_raises_os_error(errno.EBADF, self.os.listdir, 500)
136        dir_path = self.make_path("xyzzy", "plugh")
137        files = ["foo", "bar", "baz"]
138        for f in files:
139            self.create_file(self.os.path.join(dir_path, f))
140        files.sort()
141
142        path_des = self.os.open(dir_path, os.O_RDONLY)
143        self.assertEqual(files, sorted(self.os.listdir(path_des)))
144
145    def test_listdir_returns_list(self):
146        directory_root = self.make_path("xyzzy")
147        self.os.mkdir(directory_root)
148        directory = self.os.path.join(directory_root, "bug")
149        self.os.mkdir(directory)
150        self.create_file(self.make_path(directory, "foo"))
151        self.assertEqual(["foo"], self.os.listdir(directory))
152
153    def test_listdir_on_symlink(self):
154        self.skip_if_symlink_not_supported()
155        directory = self.make_path("xyzzy")
156        files = ["foo", "bar", "baz"]
157        for f in files:
158            self.create_file(self.make_path(directory, f))
159        self.create_symlink(self.make_path("symlink"), self.make_path("xyzzy"))
160        files.sort()
161        self.assertEqual(files, sorted(self.os.listdir(self.make_path("symlink"))))
162
163    def test_listdir_error(self):
164        file_path = self.make_path("foo", "bar", "baz")
165        self.create_file(file_path)
166        self.assert_raises_os_error(errno.ENOTDIR, self.os.listdir, file_path)
167
168    def test_exists_current_dir(self):
169        self.assertTrue(self.os.path.exists("."))
170
171    def test_listdir_current(self):
172        files = ["foo", "bar", "baz"]
173        for f in files:
174            self.create_file(self.make_path(f))
175        files.sort()
176        self.assertEqual(files, sorted(self.os.listdir(self.base_path)))
177
178    def test_fdopen(self):
179        file_path1 = self.make_path("some_file1")
180        self.create_file(file_path1, contents="contents here1")
181        with self.open(file_path1, "r") as fake_file1:
182            fileno = fake_file1.fileno()
183            fake_file2 = self.os.fdopen(fileno)
184            self.assertNotEqual(fake_file2, fake_file1)
185
186        with self.assertRaises(TypeError):
187            self.os.fdopen(None)
188        with self.assertRaises(TypeError):
189            self.os.fdopen("a string")
190
191    def test_out_of_range_fdopen(self):
192        # test some file descriptor that is clearly out of range
193        self.assert_raises_os_error(errno.EBADF, self.os.fdopen, 500)
194
195    def test_closed_file_descriptor(self):
196        first_path = self.make_path("some_file1")
197        second_path = self.make_path("some_file2")
198        third_path = self.make_path("some_file3")
199        self.create_file(first_path, contents="contents here1")
200        self.create_file(second_path, contents="contents here2")
201        self.create_file(third_path, contents="contents here3")
202
203        fake_file1 = self.open(first_path, "r")
204        fake_file2 = self.open(second_path, "r")
205        fake_file3 = self.open(third_path, "r")
206        fileno1 = fake_file1.fileno()
207        fileno2 = fake_file2.fileno()
208        fileno3 = fake_file3.fileno()
209
210        self.os.close(fileno2)
211        self.assert_raises_os_error(errno.EBADF, self.os.close, fileno2)
212        self.assertEqual(fileno1, fake_file1.fileno())
213        self.assertEqual(fileno3, fake_file3.fileno())
214
215        with self.os.fdopen(fileno1) as f:
216            self.assertFalse(f is fake_file1)
217        with self.os.fdopen(fileno3) as f:
218            self.assertFalse(f is fake_file3)
219        self.assert_raises_os_error(errno.EBADF, self.os.fdopen, fileno2)
220
221    def test_fdopen_mode(self):
222        self.skip_real_fs()
223        file_path1 = self.make_path("some_file1")
224        self.create_file(file_path1, contents="contents here1")
225        self.os.chmod(file_path1, (stat.S_IFREG | 0o666) ^ stat.S_IWRITE)
226
227        fake_file1 = self.open(file_path1, "r")
228        fileno1 = fake_file1.fileno()
229        self.os.fdopen(fileno1)
230        self.os.fdopen(fileno1, "r")
231        if not is_root():
232            with self.assertRaises(OSError):
233                self.os.fdopen(fileno1, "w")
234        else:
235            self.os.fdopen(fileno1, "w")
236            self.os.close(fileno1)
237
238    def test_fstat(self):
239        directory = self.make_path("xyzzy")
240        file_path = self.os.path.join(directory, "plugh")
241        self.create_file(file_path, contents="ABCDE")
242        with self.open(file_path) as file_obj:
243            fileno = file_obj.fileno()
244            self.assertTrue(stat.S_IFREG & self.os.fstat(fileno)[stat.ST_MODE])
245            self.assertTrue(stat.S_IFREG & self.os.fstat(fileno).st_mode)
246            self.assertEqual(5, self.os.fstat(fileno)[stat.ST_SIZE])
247
248    def test_stat(self):
249        directory = self.make_path("xyzzy")
250        file_path = self.os.path.join(directory, "plugh")
251        self.create_file(file_path, contents="ABCDE")
252        self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE])
253        self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE])
254        self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode)
255        self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE])
256
257    def test_st_blocks(self):
258        self.check_posix_only()
259        file_path = self.make_path("foo1")
260        self.create_file(file_path, contents=b"")
261        self.assertEqual(0, self.os.stat(file_path).st_blocks)
262        file_path = self.make_path("foo2")
263        self.create_file(file_path, contents=b"t")
264        self.assertEqual(8, self.os.stat(file_path).st_blocks)
265        file_path = self.make_path("foo3")
266        self.create_file(file_path, contents=b"t" * 4095)
267        self.assertEqual(8, self.os.stat(file_path).st_blocks)
268        file_path = self.make_path("foo4")
269        self.create_file(file_path, contents=b"t" * 4096)
270        self.assertEqual(8, self.os.stat(file_path).st_blocks)
271        file_path = self.make_path("foo5")
272        self.create_file(file_path, contents=b"t" * 4097)
273        self.assertEqual(16, self.os.stat(file_path).st_blocks)
274
275    def test_no_st_blocks_in_windows(self):
276        self.check_windows_only()
277        file_path = self.make_path("foo")
278        self.create_file(file_path, contents=b"")
279        with self.assertRaises(AttributeError):
280            self.os.stat(file_path).st_blocks
281
282    def test_stat_with_unc_path(self):
283        self.skip_real_fs()
284        self.check_windows_only()
285        directory = "//root/share/dir"
286        file_path = self.os.path.join(directory, "plugh")
287        self.create_file(file_path, contents="ABCDE")
288        self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE])
289        self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE])
290        self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode)
291        self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE])
292
293    def test_stat_with_drive(self):
294        self.skip_real_fs()
295        self.check_windows_only()
296        directory = "C:/foo/dir"
297        file_path = self.os.path.join(directory, "plugh")
298        self.create_file(file_path, contents="ABCDE")
299        self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE])
300        self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE])
301        self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode)
302        self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE])
303
304    def test_stat_uses_open_fd_as_path(self):
305        self.skip_real_fs()
306        self.assert_raises_os_error(errno.EBADF, self.os.stat, 5)
307        file_path = self.make_path("foo", "bar")
308        self.create_file(file_path)
309
310        with self.open(file_path) as f:
311            self.assertTrue(stat.S_IFREG & self.os.stat(f.filedes)[stat.ST_MODE])
312
313    def test_stat_no_follow_symlinks_posix(self):
314        """Test that stat with follow_symlinks=False behaves like lstat."""
315        self.check_posix_only()
316        directory = self.make_path("xyzzy")
317        base_name = "plugh"
318        file_contents = "frobozz"
319        # Just make sure we didn't accidentally make our test data meaningless.
320        self.assertNotEqual(len(base_name), len(file_contents))
321        file_path = self.os.path.join(directory, base_name)
322        link_path = self.os.path.join(directory, "link")
323        self.create_file(file_path, contents=file_contents)
324        self.create_symlink(link_path, base_name)
325        self.assertEqual(
326            len(file_contents),
327            self.os.stat(file_path, follow_symlinks=False)[stat.ST_SIZE],
328        )
329        self.assertEqual(
330            len(base_name),
331            self.os.stat(link_path, follow_symlinks=False)[stat.ST_SIZE],
332        )
333
334    def test_stat_no_follow_symlinks_windows(self):
335        """Test that stat with follow_symlinks=False behaves like lstat."""
336        self.check_windows_only()
337        self.skip_if_symlink_not_supported()
338        directory = self.make_path("xyzzy")
339        base_name = "plugh"
340        file_contents = "frobozz"
341        # Just make sure we didn't accidentally make our test data meaningless.
342        self.assertNotEqual(len(base_name), len(file_contents))
343        file_path = self.os.path.join(directory, base_name)
344        link_path = self.os.path.join(directory, "link")
345        self.create_file(file_path, contents=file_contents)
346        self.create_symlink(link_path, base_name)
347        self.assertEqual(
348            len(file_contents),
349            self.os.stat(file_path, follow_symlinks=False)[stat.ST_SIZE],
350        )
351        self.assertEqual(
352            0, self.os.stat(link_path, follow_symlinks=False)[stat.ST_SIZE]
353        )
354
355    def test_lstat_size_posix(self):
356        self.check_posix_only()
357        directory = self.make_path("xyzzy")
358        base_name = "plugh"
359        file_contents = "frobozz"
360        # Just make sure we didn't accidentally make our test data meaningless.
361        self.assertNotEqual(len(base_name), len(file_contents))
362        file_path = self.os.path.join(directory, base_name)
363        link_path = self.os.path.join(directory, "link")
364        self.create_file(file_path, contents=file_contents)
365        self.create_symlink(link_path, base_name)
366        self.assertEqual(len(file_contents), self.os.lstat(file_path)[stat.ST_SIZE])
367        self.assertEqual(len(base_name), self.os.lstat(link_path)[stat.ST_SIZE])
368
369    def test_lstat_size_windows(self):
370        self.check_windows_only()
371        self.skip_if_symlink_not_supported()
372        directory = self.make_path("xyzzy")
373        base_name = "plugh"
374        file_contents = "frobozz"
375        # Just make sure we didn't accidentally make our test data meaningless.
376        self.assertNotEqual(len(base_name), len(file_contents))
377        file_path = self.os.path.join(directory, base_name)
378        link_path = self.os.path.join(directory, "link")
379        self.create_file(file_path, contents=file_contents)
380        self.create_symlink(link_path, base_name)
381        self.assertEqual(len(file_contents), self.os.lstat(file_path)[stat.ST_SIZE])
382        self.assertEqual(0, self.os.lstat(link_path)[stat.ST_SIZE])
383
384    def test_lstat_trailing_sep(self):
385        # regression test for #342
386        stat_result = self.os.lstat(self.base_path)
387        self.assertEqual(
388            stat_result, self.os.lstat(self.base_path + self.path_separator())
389        )
390        self.assertEqual(
391            stat_result,
392            self.os.lstat(
393                self.base_path + self.path_separator() + self.path_separator()
394            ),
395        )
396
397    def test_stat_with_byte_string(self):
398        stat_str = self.os.stat(self.base_path)
399        base_path_bytes = self.base_path.encode("utf8")
400        stat_bytes = self.os.stat(base_path_bytes)
401        self.assertEqual(stat_bytes, stat_str)
402
403    def test_lstat_with_byte_string(self):
404        stat_str = self.os.lstat(self.base_path)
405        base_path_bytes = self.base_path.encode("utf8")
406        stat_bytes = self.os.lstat(base_path_bytes)
407        self.assertEqual(stat_bytes, stat_str)
408
409    def test_stat_with_current_dir(self):
410        # regression test for #516
411        stat_result = self.os.stat(".")
412        lstat_result = self.os.lstat(".")
413        self.assertEqual(stat_result, lstat_result)
414
415    def test_exists_with_trailing_sep(self):
416        # regression test for #364
417        file_path = self.make_path("alpha")
418        self.create_file(file_path)
419        self.assertFalse(self.os.path.exists(file_path + self.os.sep))
420
421    def test_mkdir_with_trailing_sep(self):
422        # regression test for #367
423        dir_path = self.make_path("foo")
424        self.os.mkdir(dir_path + self.os.sep + self.os.sep)
425        self.assertTrue(self.os.path.exists(dir_path))
426
427    def test_readlink_empty_path(self):
428        self.check_posix_only()
429        self.assert_raises_os_error(errno.ENOENT, self.os.readlink, "")
430
431    def test_readlink_ending_with_sep_posix(self):
432        # regression test for #359
433        self.check_posix_only()
434        link_path = self.make_path("foo")
435        self.os.symlink(self.base_path, link_path)
436        self.assert_raises_os_error(
437            errno.EINVAL, self.os.readlink, link_path + self.os.sep
438        )
439
440    def test_lstat_symlink_with_trailing_sep_linux(self):
441        # regression test for #366
442        self.check_linux_only()
443        self.skip_if_symlink_not_supported()
444        link_path = self.make_path("foo")
445        self.os.symlink(self.base_path, link_path)
446        # used to raise
447        self.assertTrue(self.os.lstat(link_path + self.os.sep).st_mode)
448
449    def test_lstat_symlink_with_trailing_sep_macos(self):
450        # regression test for #366
451        self.check_macos_only()
452        self.skip_if_symlink_not_supported()
453        link_path = self.make_path("foo")
454        self.os.symlink(self.base_path, link_path)
455        # used to raise
456        self.assertTrue(self.os.lstat(link_path + self.os.sep).st_mode)
457
458    def test_readlink_ending_with_sep_windows(self):
459        self.check_windows_only()
460        self.skip_if_symlink_not_supported()
461        link_path = self.make_path("foo")
462        self.os.symlink(self.base_path, link_path)
463        self.assert_equal_paths(
464            self.base_path, self.os.readlink(link_path + self.os.sep)
465        )
466
467    def test_islink_with_trailing_sep_windows(self):
468        self.check_windows_only()
469        self.skip_if_symlink_not_supported()
470        link_path = self.make_path("foo")
471        self.os.symlink(self.base_path, link_path)
472        self.assertTrue(self.os.path.islink(link_path + self.os.path.sep))
473
474    def test_islink_with_trailing_sep_linux(self):
475        self.check_linux_only()
476        link_path = self.make_path("foo")
477        self.os.symlink(self.base_path, link_path)
478        self.assertFalse(self.os.path.islink(link_path + self.os.sep))
479
480    def test_islink_with_trailing_sep_macos(self):
481        self.check_macos_only()
482        link_path = self.make_path("foo")
483        self.os.symlink(self.base_path, link_path)
484        self.assertFalse(self.os.path.islink(link_path + self.os.sep))
485
486    def check_getsize_raises_with_trailing_separator(self, error_nr):
487        file_path = self.make_path("bar")
488        self.create_file(file_path)
489        self.assert_raises_os_error(
490            error_nr, self.os.path.getsize, file_path + self.os.sep
491        )
492
493    def test_getsize_raises_with_trailing_separator_posix(self):
494        self.check_posix_only()
495        self.check_getsize_raises_with_trailing_separator(errno.ENOTDIR)
496
497    def test_getsize_raises_with_trailing_separator_windows(self):
498        self.check_windows_only()
499        self.check_getsize_raises_with_trailing_separator(errno.EINVAL)
500
501    def check_remove_link_ending_with_sep(self, error_nr):
502        # regression test for #360
503        link_path = self.make_path("foo")
504        self.os.symlink(self.base_path, link_path)
505        self.assert_raises_os_error(error_nr, self.os.remove, link_path + self.os.sep)
506
507    def test_remove_link_ending_with_sep_linux(self):
508        self.check_linux_only()
509        self.check_remove_link_ending_with_sep(errno.ENOTDIR)
510
511    def test_remove_link_ending_with_sep_macos(self):
512        self.check_macos_only()
513        self.check_remove_link_ending_with_sep(errno.EPERM)
514
515    def test_remove_link_ending_with_sep_windows(self):
516        self.check_windows_only()
517        self.skip_if_symlink_not_supported()
518        self.check_remove_link_ending_with_sep(errno.EACCES)
519
520    def test_lstat_uses_open_fd_as_path(self):
521        self.skip_if_symlink_not_supported()
522        if os.lstat not in os.supports_fd:
523            self.skip_real_fs()
524        self.assert_raises_os_error(errno.EBADF, self.os.lstat, 5)
525        file_path = self.make_path("foo", "bar")
526        link_path = self.make_path("foo", "link")
527        file_contents = b"contents"
528        self.create_file(file_path, contents=file_contents)
529        self.create_symlink(link_path, file_path)
530
531        with self.open(file_path) as f:
532            self.assertEqual(len(file_contents), self.os.lstat(f.filedes)[stat.ST_SIZE])
533
534    def test_stat_non_existent_file(self):
535        # set up
536        file_path = self.make_path("non", "existent", "file")
537        self.assertFalse(self.os.path.exists(file_path))
538        # actual tests
539        try:
540            # Use try-catch to check exception attributes.
541            self.os.stat(file_path)
542            self.fail("Exception is expected.")  # COV_NF_LINE
543        except OSError as os_error:
544            self.assertEqual(errno.ENOENT, os_error.errno)
545            self.assertEqual(file_path, os_error.filename)
546
547    def check_open_raises_with_trailing_separator(self, error_nr):
548        file_path = self.make_path("bar") + self.os.sep
549        self.assert_raises_os_error(
550            error_nr,
551            self.os.open,
552            file_path,
553            os.O_CREAT | os.O_WRONLY | os.O_TRUNC,
554        )
555
556    def test_open_raises_with_trailing_separator_linux(self):
557        self.check_linux_only()
558        self.check_open_raises_with_trailing_separator(errno.EISDIR)
559
560    def test_open_raises_with_trailing_separator_macos(self):
561        self.check_macos_only()
562        self.check_open_raises_with_trailing_separator(errno.ENOENT)
563
564    def test_open_raises_with_trailing_separator_windows(self):
565        self.check_windows_only()
566        self.check_open_raises_with_trailing_separator(errno.EINVAL)
567
568    def test_lexists_with_trailing_separator_linux_windows(self):
569        self.check_linux_and_windows()
570        self.skip_if_symlink_not_supported()
571        file_path = self.make_path("foo")
572        self.os.symlink(file_path, file_path)
573        self.assertFalse(self.os.path.lexists(file_path + self.os.sep))
574
575    def test_lexists_with_trailing_separator_macos(self):
576        # regression test for #373
577        self.check_macos_only()
578        file_path = self.make_path("foo")
579        self.os.symlink(file_path, file_path)
580        self.assertTrue(self.os.path.lexists(file_path + self.os.sep))
581
582    def test_islink_with_trailing_separator_linux_windows(self):
583        self.check_linux_and_windows()
584        self.skip_if_symlink_not_supported()
585        file_path = self.make_path("foo")
586        self.os.symlink(file_path, file_path)
587        self.assertFalse(self.os.path.islink(file_path + self.os.sep))
588
589    def test_islink_with_trailing_separator_macos(self):
590        # regression test for #373
591        self.check_macos_only()
592        file_path = self.make_path("foo")
593        self.os.symlink(file_path, file_path)
594        self.assertTrue(self.os.path.islink(file_path + self.os.sep))
595
596    def test_isfile_with_trailing_separator_linux_windows(self):
597        self.check_linux_and_windows()
598        file_path = self.make_path("foo")
599        self.create_file(file_path)
600        self.assertFalse(self.os.path.isfile(file_path + self.os.sep))
601
602    def test_isfile_with_trailing_separator_macos(self):
603        # regression test for #374
604        self.check_macos_only()
605        file_path = self.make_path("foo")
606        self.create_file(file_path)
607        self.assertFalse(self.os.path.isfile(file_path + self.os.sep))
608
609    def test_isfile_not_readable_file(self):
610        file_path = self.make_path("foo")
611        self.create_file(file_path, perm=0)
612        self.assertTrue(self.os.path.isfile(file_path))
613
614    def check_stat_with_trailing_separator(self, error_nr):
615        # regression test for #376
616        file_path = self.make_path("foo")
617        self.create_file(file_path)
618        self.assert_raises_os_error(error_nr, self.os.stat, file_path + self.os.sep)
619
620    def test_stat_with_trailing_separator_posix(self):
621        self.check_posix_only()
622        self.check_stat_with_trailing_separator(errno.ENOTDIR)
623
624    def test_stat_with_trailing_separator_windows(self):
625        self.check_windows_only()
626        self.check_stat_with_trailing_separator(errno.EINVAL)
627
628    def check_remove_with_trailing_separator(self, error_nr):
629        # regression test for #377
630        file_path = self.make_path("foo")
631        self.create_file(file_path)
632        self.assert_raises_os_error(error_nr, self.os.remove, file_path + self.os.sep)
633
634    def test_remove_with_trailing_separator_posix(self):
635        self.check_posix_only()
636        self.check_remove_with_trailing_separator(errno.ENOTDIR)
637
638    def test_remove_with_trailing_separator_windows(self):
639        self.check_windows_only()
640        self.check_remove_with_trailing_separator(errno.EINVAL)
641
642    def test_readlink(self):
643        self.skip_if_symlink_not_supported()
644        link_path = self.make_path("foo", "bar", "baz")
645        target = self.make_path("tarJAY")
646        self.create_symlink(link_path, target)
647        self.assert_equal_paths(self.os.readlink(link_path), target)
648
649    def check_readlink_raises_if_path_is_not_a_link(self):
650        file_path = self.make_path("foo", "bar", "eleventyone")
651        self.create_file(file_path)
652        self.assert_raises_os_error(errno.EINVAL, self.os.readlink, file_path)
653
654    def test_readlink_raises_if_path_is_not_a_link_windows(self):
655        self.check_windows_only()
656        self.skip_if_symlink_not_supported()
657        self.check_readlink_raises_if_path_is_not_a_link()
658
659    def test_readlink_raises_if_path_is_not_a_link_posix(self):
660        self.check_posix_only()
661        self.check_readlink_raises_if_path_is_not_a_link()
662
663    def check_readlink_raises_if_path_has_file(self, error_subtype):
664        self.create_file(self.make_path("a_file"))
665        file_path = self.make_path("a_file", "foo")
666        self.assert_raises_os_error(error_subtype, self.os.readlink, file_path)
667        file_path = self.make_path("a_file", "foo", "bar")
668        self.assert_raises_os_error(error_subtype, self.os.readlink, file_path)
669
670    def test_readlink_raises_if_path_has_file_windows(self):
671        self.check_windows_only()
672        self.skip_if_symlink_not_supported()
673        self.check_readlink_raises_if_path_has_file(errno.ENOENT)
674
675    def test_readlink_raises_if_path_has_file_posix(self):
676        self.check_posix_only()
677        self.check_readlink_raises_if_path_has_file(errno.ENOTDIR)
678
679    def test_readlink_raises_if_path_does_not_exist(self):
680        self.skip_if_symlink_not_supported()
681        self.assert_raises_os_error(
682            errno.ENOENT, self.os.readlink, "/this/path/does/not/exist"
683        )
684
685    def test_readlink_raises_if_path_is_none(self):
686        self.skip_if_symlink_not_supported()
687        with self.assertRaises(TypeError):
688            self.os.readlink(None)
689
690    def test_broken_symlink_with_trailing_separator_linux(self):
691        self.check_linux_only()
692        file_path = self.make_path("foo")
693        link_path = self.make_path("link")
694        self.os.symlink(file_path, link_path)
695        self.assert_raises_os_error(
696            errno.EEXIST,
697            self.os.symlink,
698            link_path + self.os.sep,
699            link_path + self.os.sep,
700        )
701
702    def test_broken_symlink_with_trailing_separator_macos(self):
703        # regression test for #371
704        self.check_macos_only()
705        file_path = self.make_path("foo")
706        link_path = self.make_path("link")
707        self.os.symlink(file_path, link_path)
708        self.os.symlink(link_path + self.os.sep, link_path + self.os.sep)
709
710    def test_broken_symlink_with_trailing_separator_windows(self):
711        self.check_windows_only()
712        self.skip_if_symlink_not_supported()
713        file_path = self.make_path("foo")
714        link_path = self.make_path("link")
715        self.os.symlink(file_path, link_path)
716        self.assert_raises_os_error(
717            errno.EINVAL,
718            self.os.symlink,
719            link_path + self.os.sep,
720            link_path + self.os.sep,
721        )
722
723    def test_circular_readlink_with_trailing_separator_linux(self):
724        # Regression test for #372
725        self.check_linux_only()
726        file_path = self.make_path("foo")
727        self.os.symlink(file_path, file_path)
728        self.assert_raises_os_error(
729            errno.ELOOP, self.os.readlink, file_path + self.os.sep
730        )
731
732    def test_circular_readlink_with_trailing_separator_macos(self):
733        # Regression test for #372
734        self.check_macos_only()
735        file_path = self.make_path("foo")
736        self.os.symlink(file_path, file_path)
737        self.os.readlink(file_path + self.os.sep)
738
739    def test_circular_readlink_with_trailing_separator_windows(self):
740        # Regression test for #372
741        self.check_windows_only()
742        self.skip_if_symlink_not_supported()
743        file_path = self.make_path("foo")
744        self.os.symlink(file_path, file_path)
745        self.assert_raises_os_error(
746            errno.EINVAL, self.os.readlink, file_path + self.os.sep
747        )
748
749    def test_readlink_with_links_in_path(self):
750        self.skip_if_symlink_not_supported()
751        self.create_symlink(
752            self.make_path("meyer", "lemon", "pie"), self.make_path("yum")
753        )
754        self.create_symlink(self.make_path("geo", "metro"), self.make_path("meyer"))
755        self.assert_equal_paths(
756            self.make_path("yum"),
757            self.os.readlink(self.make_path("geo", "metro", "lemon", "pie")),
758        )
759
760    def test_readlink_with_chained_links_in_path(self):
761        self.skip_if_symlink_not_supported()
762        self.create_symlink(
763            self.make_path("eastern", "european", "wolfhounds", "chase"),
764            self.make_path("cats"),
765        )
766        self.create_symlink(
767            self.make_path("russian"), self.make_path("eastern", "european")
768        )
769        self.create_symlink(
770            self.make_path("dogs"), self.make_path("russian", "wolfhounds")
771        )
772        self.assert_equal_paths(
773            self.make_path("cats"),
774            self.os.readlink(self.make_path("dogs", "chase")),
775        )
776
777    def check_remove_dir(self, dir_error):
778        directory = self.make_path("xyzzy")
779        dir_path = self.os.path.join(directory, "plugh")
780        self.create_dir(dir_path)
781        self.assertTrue(self.os.path.exists(dir_path))
782        self.assert_raises_os_error(dir_error, self.os.remove, dir_path)
783        self.assertTrue(self.os.path.exists(dir_path))
784        self.os.chdir(directory)
785        self.assert_raises_os_error(dir_error, self.os.remove, dir_path)
786        self.assertTrue(self.os.path.exists(dir_path))
787        self.assert_raises_os_error(errno.ENOENT, self.os.remove, "/plugh")
788
789    def test_remove_dir_linux(self):
790        self.check_linux_only()
791        self.check_remove_dir(errno.EISDIR)
792
793    def test_remove_dir_mac_os(self):
794        self.check_macos_only()
795        self.check_remove_dir(errno.EPERM)
796
797    def test_remove_dir_windows(self):
798        self.check_windows_only()
799        self.check_remove_dir(errno.EACCES)
800
801    def test_remove_dir_with_drive(self):
802        # regression test for issue #337
803        self.check_windows_only()
804        self.skip_real_fs()
805        dir_path = self.os.path.join("C:", "test")
806        self.filesystem.create_dir(dir_path)
807        self.assert_raises_os_error(errno.EACCES, self.os.remove, dir_path)
808
809    def test_remove_file(self):
810        directory = self.make_path("zzy")
811        file_path = self.os.path.join(directory, "plugh")
812        self.create_file(file_path)
813        self.assertTrue(self.os.path.exists(file_path))
814        self.os.remove(file_path)
815        self.assertFalse(self.os.path.exists(file_path))
816
817    def test_remove_file_no_directory(self):
818        directory = self.make_path("zzy")
819        file_name = "plugh"
820        file_path = self.os.path.join(directory, file_name)
821        self.create_file(file_path)
822        self.assertTrue(self.os.path.exists(file_path))
823        self.os.chdir(directory)
824        self.os.remove(file_name)
825        self.assertFalse(self.os.path.exists(file_path))
826
827    def test_remove_file_with_read_permission_raises_in_windows(self):
828        self.check_windows_only()
829        path = self.make_path("foo", "bar")
830        self.create_file(path)
831        self.os.chmod(path, 0o444)
832        self.assert_raises_os_error(errno.EACCES, self.os.remove, path)
833        self.os.chmod(path, 0o666)
834
835    def test_remove_file_with_read_permission_shall_succeed_in_posix(self):
836        self.check_posix_only()
837        path = self.make_path("foo", "bar")
838        self.create_file(path)
839        self.os.chmod(path, 0o444)
840        self.os.remove(path)
841        self.assertFalse(self.os.path.exists(path))
842
843    def test_remove_file_without_parent_permission_raises_in_posix(self):
844        self.check_posix_only()
845        parent_dir = self.make_path("foo")
846        path = self.os.path.join(parent_dir, "bar")
847        self.create_file(path)
848        self.os.chmod(parent_dir, 0o666)  # missing execute permission
849        if not is_root():
850            self.assert_raises_os_error(errno.EACCES, self.os.remove, path)
851        else:
852            self.os.remove(path)
853            self.assertFalse(self.os.path.exists(path))
854            self.create_file(path)
855        self.os.chmod(parent_dir, 0o555)  # missing write permission
856        if not is_root():
857            self.assert_raises_os_error(errno.EACCES, self.os.remove, path)
858        else:
859            self.os.remove(path)
860            self.assertFalse(self.os.path.exists(path))
861            self.create_file(path)
862        self.os.chmod(parent_dir, 0o333)
863        self.os.remove(path)
864        self.assertFalse(self.os.path.exists(path))
865
866    def test_remove_open_file_fails_under_windows(self):
867        self.check_windows_only()
868        path = self.make_path("foo", "bar")
869        self.create_file(path)
870        with self.open(path, "r"):
871            self.assert_raises_os_error(errno.EACCES, self.os.remove, path)
872        self.assertTrue(self.os.path.exists(path))
873
874    def test_remove_open_file_possible_under_posix(self):
875        self.check_posix_only()
876        path = self.make_path("foo", "bar")
877        self.create_file(path)
878        self.open(path, "r")
879        self.os.remove(path)
880        self.assertFalse(self.os.path.exists(path))
881
882    def test_remove_file_relative_path(self):
883        self.skip_real_fs()
884        original_dir = self.os.getcwd()
885        directory = self.make_path("zzy")
886        subdirectory = self.os.path.join(directory, "zzy")
887        file_name = "plugh"
888        file_path = self.os.path.join(directory, file_name)
889        file_path_relative = self.os.path.join("..", file_name)
890        self.create_file(file_path)
891        self.assertTrue(self.os.path.exists(file_path))
892        self.create_dir(subdirectory)
893        self.assertTrue(self.os.path.exists(subdirectory))
894        self.os.chdir(subdirectory)
895        self.os.remove(file_path_relative)
896        self.assertFalse(self.os.path.exists(file_path_relative))
897        self.os.chdir(original_dir)
898        self.assertFalse(self.os.path.exists(file_path))
899
900    def check_remove_dir_raises_error(self, dir_error):
901        directory = self.make_path("zzy")
902        self.create_dir(directory)
903        self.assert_raises_os_error(dir_error, self.os.remove, directory)
904
905    def test_remove_dir_raises_error_linux(self):
906        self.check_linux_only()
907        self.check_remove_dir_raises_error(errno.EISDIR)
908
909    def test_remove_dir_raises_error_mac_os(self):
910        self.check_macos_only()
911        self.check_remove_dir_raises_error(errno.EPERM)
912
913    def test_remove_dir_raises_error_windows(self):
914        self.check_windows_only()
915        self.check_remove_dir_raises_error(errno.EACCES)
916
917    def test_remove_symlink_to_dir(self):
918        self.skip_if_symlink_not_supported()
919        directory = self.make_path("zzy")
920        link = self.make_path("link_to_dir")
921        self.create_dir(directory)
922        self.os.symlink(directory, link)
923        self.assertTrue(self.os.path.exists(directory))
924        self.assertTrue(self.os.path.exists(link))
925        self.os.remove(link)
926        self.assertTrue(self.os.path.exists(directory))
927        self.assertFalse(self.os.path.exists(link))
928
929    def test_unlink_raises_if_not_exist(self):
930        file_path = self.make_path("file", "does", "not", "exist")
931        self.assertFalse(self.os.path.exists(file_path))
932        self.assert_raises_os_error(errno.ENOENT, self.os.unlink, file_path)
933
934    def test_rename_to_nonexistent_file(self):
935        """Can rename a file to an unused name."""
936        directory = self.make_path("xyzzy")
937        old_file_path = self.os.path.join(directory, "plugh_old")
938        new_file_path = self.os.path.join(directory, "plugh_new")
939        self.create_file(old_file_path, contents="test contents")
940        self.assertTrue(self.os.path.exists(old_file_path))
941        self.assertFalse(self.os.path.exists(new_file_path))
942        self.os.rename(old_file_path, new_file_path)
943        self.assertFalse(self.os.path.exists(old_file_path))
944        self.assertTrue(self.os.path.exists(new_file_path))
945        self.check_contents(new_file_path, "test contents")
946
947    def test_rename_dir_to_symlink_posix(self):
948        self.check_posix_only()
949        link_path = self.make_path("link")
950        dir_path = self.make_path("dir")
951        link_target = self.os.path.join(dir_path, "link_target")
952        self.create_dir(dir_path)
953        self.os.symlink(link_target, link_path)
954        self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, dir_path, link_path)
955
956    def test_rename_dir_to_symlink_windows(self):
957        self.check_windows_only()
958        self.skip_if_symlink_not_supported()
959        link_path = self.make_path("link")
960        dir_path = self.make_path("dir")
961        link_target = self.os.path.join(dir_path, "link_target")
962        self.create_dir(dir_path)
963        self.os.symlink(link_target, link_path)
964        self.assert_raises_os_error(errno.EEXIST, self.os.rename, dir_path, link_path)
965
966    def test_rename_file_to_symlink(self):
967        self.check_posix_only()
968        link_path = self.make_path("file_link")
969        file_path = self.make_path("file")
970        self.os.symlink(file_path, link_path)
971        self.create_file(file_path)
972        self.os.rename(file_path, link_path)
973        self.assertFalse(self.os.path.exists(file_path))
974        self.assertTrue(self.os.path.exists(link_path))
975        self.assertTrue(self.os.path.isfile(link_path))
976
977    def test_rename_symlink_to_symlink(self):
978        self.check_posix_only()
979        base_path = self.make_path("foo", "bar")
980        self.create_dir(base_path)
981        link_path1 = self.os.path.join(base_path, "link1")
982        link_path2 = self.os.path.join(base_path, "link2")
983        self.os.symlink(base_path, link_path1)
984        self.os.symlink(base_path, link_path2)
985        self.os.rename(link_path1, link_path2)
986        self.assertFalse(self.os.path.exists(link_path1))
987        self.assertTrue(self.os.path.exists(link_path2))
988
989    def test_rename_symlink_to_symlink_for_parent_raises(self):
990        self.check_posix_only()
991        dir_link = self.make_path("dir_link")
992        dir_path = self.make_path("dir")
993        dir_in_dir_path = self.os.path.join(dir_link, "inner_dir")
994        self.create_dir(dir_path)
995        self.os.symlink(dir_path, dir_link)
996        self.create_dir(dir_in_dir_path)
997        self.assert_raises_os_error(
998            errno.EINVAL, self.os.rename, dir_path, dir_in_dir_path
999        )
1000
1001    def check_rename_case_with_symlink(self, result):
1002        self.skip_if_symlink_not_supported()
1003        self.check_case_insensitive_fs()
1004        dir_path_lower = self.make_path("beta")
1005        self.create_dir(dir_path_lower)
1006        link_path = self.make_path("b")
1007        self.os.symlink(self.base_path, link_path)
1008        path1 = self.os.path.join(link_path, "Beta")
1009        dir_path_upper = self.make_path("Beta")
1010        self.os.rename(path1, dir_path_upper)
1011        self.assertEqual(result, sorted(self.os.listdir(self.base_path)))
1012
1013    def test_rename_case_with_symlink_mac(self):
1014        # Regression test for #322
1015        self.check_macos_only()
1016        self.check_rename_case_with_symlink(["b", "beta"])
1017
1018    def test_rename_case_with_symlink_windows(self):
1019        self.check_windows_only()
1020        self.check_rename_case_with_symlink(["Beta", "b"])
1021
1022    def test_recursive_rename_raises(self):
1023        self.check_posix_only()
1024        base_path = self.make_path("foo", "bar")
1025        self.create_dir(base_path)
1026        new_path = self.os.path.join(base_path, "new_dir")
1027        self.assert_raises_os_error(errno.EINVAL, self.os.rename, base_path, new_path)
1028
1029    def test_rename_file_to_parent_dir_file(self):
1030        # Regression test for issue 230
1031        dir_path = self.make_path("dir")
1032        self.create_dir(dir_path)
1033        file_path = self.make_path("old_file")
1034        new_file_path = self.os.path.join(dir_path, "new_file")
1035        self.create_file(file_path)
1036        self.os.rename(file_path, new_file_path)
1037
1038    def test_rename_with_target_parent_file_raises_posix(self):
1039        self.check_posix_only()
1040        file_path = self.make_path("foo", "baz")
1041        self.create_file(file_path)
1042        self.assert_raises_os_error(
1043            errno.ENOTDIR, self.os.rename, file_path, file_path + "/new"
1044        )
1045
1046    def test_rename_with_target_parent_file_raises_windows(self):
1047        self.check_windows_only()
1048        file_path = self.make_path("foo", "baz")
1049        self.create_file(file_path)
1050        self.assert_raises_os_error(
1051            errno.EACCES,
1052            self.os.rename,
1053            file_path,
1054            self.os.path.join(file_path, "new"),
1055        )
1056
1057    def test_rename_symlink_to_source(self):
1058        self.check_posix_only()
1059        base_path = self.make_path("foo")
1060        link_path = self.os.path.join(base_path, "slink")
1061        file_path = self.os.path.join(base_path, "file")
1062        self.create_file(file_path)
1063        self.os.symlink(file_path, link_path)
1064        self.os.rename(link_path, file_path)
1065        self.assertFalse(self.os.path.exists(file_path))
1066
1067    def test_rename_symlink_to_dir_raises(self):
1068        self.check_posix_only()
1069        base_path = self.make_path("foo", "bar")
1070        link_path = self.os.path.join(base_path, "dir_link")
1071        dir_path = self.os.path.join(base_path, "dir")
1072        self.create_dir(dir_path)
1073        self.os.symlink(dir_path, link_path)
1074        self.assert_raises_os_error(errno.EISDIR, self.os.rename, link_path, dir_path)
1075
1076    def test_rename_broken_symlink(self):
1077        self.check_posix_only()
1078        base_path = self.make_path("foo")
1079        self.create_dir(base_path)
1080        link_path = self.os.path.join(base_path, "slink")
1081        file_path = self.os.path.join(base_path, "file")
1082        self.os.symlink(file_path, link_path)
1083        self.os.rename(link_path, file_path)
1084        self.assertFalse(self.os.path.exists(file_path))
1085        self.assertTrue(self.os.path.lexists(file_path))
1086        self.assertFalse(self.os.path.exists(link_path))
1087
1088    def test_rename_directory(self):
1089        """Can rename a directory to an unused name."""
1090        for old_path, new_path in [("wxyyw", "xyzzy"), ("abccb", "cdeed")]:
1091            old_path = self.make_path(old_path)
1092            new_path = self.make_path(new_path)
1093            self.create_file(self.os.path.join(old_path, "plugh"), contents="test")
1094            self.assertTrue(self.os.path.exists(old_path))
1095            self.assertFalse(self.os.path.exists(new_path))
1096            self.os.rename(old_path, new_path)
1097            self.assertFalse(self.os.path.exists(old_path))
1098            self.assertTrue(self.os.path.exists(new_path))
1099            self.check_contents(self.os.path.join(new_path, "plugh"), "test")
1100            if not self.use_real_fs():
1101                self.assertEqual(3, self.filesystem.get_object(new_path).st_nlink)
1102
1103    def check_rename_directory_to_existing_file_raises(self, error_nr):
1104        dir_path = self.make_path("dir")
1105        file_path = self.make_path("file")
1106        self.create_dir(dir_path)
1107        self.create_file(file_path)
1108        self.assert_raises_os_error(error_nr, self.os.rename, dir_path, file_path)
1109
1110    def test_rename_directory_to_existing_file_raises_posix(self):
1111        self.check_posix_only()
1112        self.check_rename_directory_to_existing_file_raises(errno.ENOTDIR)
1113
1114    def test_rename_directory_to_existing_file_raises_windows(self):
1115        self.check_windows_only()
1116        self.check_rename_directory_to_existing_file_raises(errno.EEXIST)
1117
1118    def test_rename_to_existing_directory_should_raise_under_windows(self):
1119        """Renaming to an existing directory raises OSError under Windows."""
1120        self.check_windows_only()
1121        old_path = self.make_path("foo", "bar")
1122        new_path = self.make_path("foo", "baz")
1123        self.create_dir(old_path)
1124        self.create_dir(new_path)
1125        self.assert_raises_os_error(errno.EEXIST, self.os.rename, old_path, new_path)
1126
1127    def test_rename_to_a_hardlink_of_same_file_should_do_nothing(self):
1128        self.skip_real_fs_failure(skip_posix=False)
1129        self.skip_if_symlink_not_supported()
1130        file_path = self.make_path("dir", "file")
1131        self.create_file(file_path)
1132        link_path = self.make_path("link")
1133        self.os.link(file_path, link_path)
1134        self.os.rename(file_path, link_path)
1135        self.assertTrue(self.os.path.exists(file_path))
1136        self.assertTrue(self.os.path.exists(link_path))
1137
1138    def test_hardlink_works_with_symlink(self):
1139        self.skip_if_symlink_not_supported()
1140        base_path = self.make_path("foo")
1141        self.create_dir(base_path)
1142        symlink_path = self.os.path.join(base_path, "slink")
1143        self.os.symlink(base_path, symlink_path)
1144        file_path = self.os.path.join(base_path, "slink", "beta")
1145        self.create_file(file_path)
1146        link_path = self.os.path.join(base_path, "slink", "gamma")
1147        self.os.link(file_path, link_path)
1148        self.assertTrue(self.os.path.exists(link_path))
1149        self.assertFalse(self.os.path.islink(link_path))
1150
1151    def test_replace_existing_directory_should_raise_under_windows(self):
1152        """Renaming to an existing directory raises OSError under Windows."""
1153        self.check_windows_only()
1154        old_path = self.make_path("foo", "bar")
1155        new_path = self.make_path("foo", "baz")
1156        self.create_dir(old_path)
1157        self.create_dir(new_path)
1158        self.assert_raises_os_error(errno.EACCES, self.os.replace, old_path, new_path)
1159
1160    def test_rename_to_existing_directory_under_posix(self):
1161        """Renaming to an existing directory changes the existing directory
1162        under Posix."""
1163        self.check_posix_only()
1164        old_path = self.make_path("foo", "bar")
1165        new_path = self.make_path("xyzzy")
1166        self.create_dir(self.os.path.join(old_path, "sub"))
1167        self.create_dir(new_path)
1168        self.os.rename(old_path, new_path)
1169        self.assertTrue(self.os.path.exists(self.os.path.join(new_path, "sub")))
1170        self.assertFalse(self.os.path.exists(old_path))
1171
1172    def test_rename_file_to_existing_directory_raises_under_posix(self):
1173        self.check_posix_only()
1174        file_path = self.make_path("foo", "bar", "baz")
1175        new_path = self.make_path("xyzzy")
1176        self.create_file(file_path)
1177        self.create_dir(new_path)
1178        self.assert_raises_os_error(errno.EISDIR, self.os.rename, file_path, new_path)
1179
1180    def test_rename_to_existing_dir_under_posix_raises_if_not_empty(self):
1181        """Renaming to an existing directory changes the existing directory
1182        under Posix."""
1183        self.check_posix_only()
1184        old_path = self.make_path("foo", "bar")
1185        new_path = self.make_path("foo", "baz")
1186        self.create_dir(self.os.path.join(old_path, "sub"))
1187        self.create_dir(self.os.path.join(new_path, "sub"))
1188
1189        # not testing specific subtype:
1190        # raises errno.ENOTEMPTY under Ubuntu 16.04, MacOS and pyfakefs
1191        # but raises errno.EEXIST at least under Ubunto 14.04
1192        with self.assertRaises(OSError):
1193            self.os.rename(old_path, new_path)
1194
1195    def test_rename_to_another_device_should_raise(self):
1196        """Renaming to another filesystem device raises OSError."""
1197        self.skip_real_fs()
1198        self.filesystem.add_mount_point("/mount")
1199        old_path = "/foo/bar"
1200        new_path = "/mount/bar"
1201        self.filesystem.create_file(old_path)
1202        self.assert_raises_os_error(errno.EXDEV, self.os.rename, old_path, new_path)
1203
1204    def test_rename_to_existent_file_posix(self):
1205        """Can rename a file to a used name under Unix."""
1206        self.check_posix_only()
1207        directory = self.make_path("xyzzy")
1208        old_file_path = self.os.path.join(directory, "plugh_old")
1209        new_file_path = self.os.path.join(directory, "plugh_new")
1210        self.create_file(old_file_path, contents="test contents 1")
1211        self.create_file(new_file_path, contents="test contents 2")
1212        self.assertTrue(self.os.path.exists(old_file_path))
1213        self.assertTrue(self.os.path.exists(new_file_path))
1214        self.os.rename(old_file_path, new_file_path)
1215        self.assertFalse(self.os.path.exists(old_file_path))
1216        self.assertTrue(self.os.path.exists(new_file_path))
1217        self.check_contents(new_file_path, "test contents 1")
1218
1219    def test_rename_to_existent_file_windows(self):
1220        """Renaming a file to a used name raises OSError under Windows."""
1221        self.check_windows_only()
1222        directory = self.make_path("xyzzy")
1223        old_file_path = self.os.path.join(directory, "plugh_old")
1224        new_file_path = self.os.path.join(directory, "plugh_new")
1225        self.create_file(old_file_path, contents="test contents 1")
1226        self.create_file(new_file_path, contents="test contents 2")
1227        self.assertTrue(self.os.path.exists(old_file_path))
1228        self.assertTrue(self.os.path.exists(new_file_path))
1229        self.assert_raises_os_error(
1230            errno.EEXIST, self.os.rename, old_file_path, new_file_path
1231        )
1232
1233    def test_replace_to_existent_file(self):
1234        """Replaces an existing file (does not work with `rename()` under
1235        Windows)."""
1236        directory = self.make_path("xyzzy")
1237        old_file_path = self.os.path.join(directory, "plugh_old")
1238        new_file_path = self.os.path.join(directory, "plugh_new")
1239        self.create_file(old_file_path, contents="test contents 1")
1240        self.create_file(new_file_path, contents="test contents 2")
1241        self.assertTrue(self.os.path.exists(old_file_path))
1242        self.assertTrue(self.os.path.exists(new_file_path))
1243        self.os.replace(old_file_path, new_file_path)
1244        self.assertFalse(self.os.path.exists(old_file_path))
1245        self.assertTrue(self.os.path.exists(new_file_path))
1246        self.check_contents(new_file_path, "test contents 1")
1247
1248    def test_rename_to_nonexistent_dir(self):
1249        """Can rename a file to a name in a nonexistent dir."""
1250        directory = self.make_path("xyzzy")
1251        old_file_path = self.os.path.join(directory, "plugh_old")
1252        new_file_path = self.os.path.join(directory, "no_such_path", "plugh_new")
1253        self.create_file(old_file_path, contents="test contents")
1254        self.assertTrue(self.os.path.exists(old_file_path))
1255        self.assertFalse(self.os.path.exists(new_file_path))
1256        self.assert_raises_os_error(
1257            errno.ENOENT, self.os.rename, old_file_path, new_file_path
1258        )
1259        self.assertTrue(self.os.path.exists(old_file_path))
1260        self.assertFalse(self.os.path.exists(new_file_path))
1261        self.check_contents(old_file_path, "test contents")
1262
1263    def test_rename_nonexistent_file_should_raise_error(self):
1264        """Can't rename a file that doesn't exist."""
1265        self.assert_raises_os_error(
1266            errno.ENOENT,
1267            self.os.rename,
1268            "nonexistent-foo",
1269            "doesn't-matter-bar",
1270        )
1271
1272    def test_rename_empty_dir(self):
1273        """Test a rename of an empty directory."""
1274        directory = self.make_path("xyzzy")
1275        before_dir = self.os.path.join(directory, "empty")
1276        after_dir = self.os.path.join(directory, "unused")
1277        self.create_dir(before_dir)
1278        self.assertTrue(self.os.path.exists(self.os.path.join(before_dir, ".")))
1279        self.assertFalse(self.os.path.exists(after_dir))
1280        self.os.rename(before_dir, after_dir)
1281        self.assertFalse(self.os.path.exists(before_dir))
1282        self.assertTrue(self.os.path.exists(self.os.path.join(after_dir, ".")))
1283
1284    def test_rename_symlink(self):
1285        self.check_posix_only()
1286        base_path = self.make_path("foo", "bar")
1287        self.create_dir(base_path)
1288        link_path = self.os.path.join(base_path, "link")
1289        self.os.symlink(base_path, link_path)
1290        file_path = self.os.path.join(link_path, "file")
1291        new_file_path = self.os.path.join(link_path, "new")
1292        self.create_file(file_path)
1293        self.os.rename(file_path, new_file_path)
1294        self.assertFalse(self.os.path.exists(file_path))
1295        self.assertTrue(self.os.path.exists(new_file_path))
1296
1297    def check_append_mode_tell_after_truncate(self, tell_result):
1298        file_path = self.make_path("baz")
1299        with self.open(file_path, "w") as f0:
1300            with self.open(file_path, "a") as f1:
1301                f1.write("abcde")
1302                f0.seek(2)
1303                f0.truncate()
1304                self.assertEqual(tell_result, f1.tell())
1305        with self.open(file_path, mode="rb") as f:
1306            self.assertEqual(b"\0\0abcde", f.read())
1307
1308    def test_append_mode_tell_linux_windows(self):
1309        # Regression test for #300
1310        self.check_linux_and_windows()
1311        self.check_append_mode_tell_after_truncate(7)
1312
1313    def test_append_mode_tell_macos(self):
1314        # Regression test for #300
1315        self.check_macos_only()
1316        self.check_append_mode_tell_after_truncate(7)
1317
1318    def test_tell_after_seek_in_append_mode(self):
1319        # Regression test for #363
1320        file_path = self.make_path("foo")
1321        with self.open(file_path, "a") as f:
1322            f.seek(1)
1323            self.assertEqual(1, f.tell())
1324
1325    def test_tell_after_seekback_in_append_mode(self):
1326        # Regression test for #414
1327        file_path = self.make_path("foo")
1328        with self.open(file_path, "a") as f:
1329            f.write("aa")
1330            f.seek(1)
1331            self.assertEqual(1, f.tell())
1332
1333    def test_dir_with_trailing_sep_is_dir(self):
1334        # regression test for #387
1335        self.assertTrue(self, self.os.path.isdir(self.base_path + self.os.sep))
1336
1337    def check_rename_dir_with_trailing_sep(self, error):
1338        dir_path = self.make_path("dir") + self.os.sep
1339        self.os.mkdir(dir_path)
1340        self.assert_raises_os_error(error, self.os.rename, dir_path, self.base_path)
1341
1342    def test_rename_dir_with_trailing_sep_posix(self):
1343        # regression test for #406
1344        self.check_posix_only()
1345        self.check_rename_dir_with_trailing_sep(errno.ENOTEMPTY)
1346
1347    def test_rename_dir_with_trailing_sep_windows(self):
1348        self.check_windows_only()
1349        self.check_rename_dir_with_trailing_sep(errno.EEXIST)
1350
1351    def test_rename_dir(self):
1352        """Test a rename of a directory."""
1353        directory = self.make_path("xyzzy")
1354        before_dir = self.os.path.join(directory, "before")
1355        before_file = self.os.path.join(directory, "before", "file")
1356        after_dir = self.os.path.join(directory, "after")
1357        after_file = self.os.path.join(directory, "after", "file")
1358        self.create_dir(before_dir)
1359        self.create_file(before_file, contents="payload")
1360        self.assertTrue(self.os.path.exists(before_dir))
1361        self.assertTrue(self.os.path.exists(before_file))
1362        self.assertFalse(self.os.path.exists(after_dir))
1363        self.assertFalse(self.os.path.exists(after_file))
1364        self.os.rename(before_dir, after_dir)
1365        self.assertFalse(self.os.path.exists(before_dir))
1366        self.assertFalse(self.os.path.exists(before_file))
1367        self.assertTrue(self.os.path.exists(after_dir))
1368        self.assertTrue(self.os.path.exists(after_file))
1369        self.check_contents(after_file, "payload")
1370
1371    def test_rename_preserves_stat(self):
1372        """Test if rename preserves mtime."""
1373        self.check_posix_only()
1374        self.skip_real_fs()
1375        directory = self.make_path("xyzzy")
1376        old_file_path = self.os.path.join(directory, "plugh_old")
1377        new_file_path = self.os.path.join(directory, "plugh_new")
1378        self.create_file(old_file_path)
1379        old_file = self.filesystem.get_object(old_file_path)
1380        old_file.st_mtime = old_file.st_mtime - 3600
1381        self.os.chown(old_file_path, 200, 200)
1382        self.os.chmod(old_file_path, 0o222)
1383        self.create_file(new_file_path)
1384        new_file = self.filesystem.get_object(new_file_path)
1385        self.assertNotEqual(new_file.st_mtime, old_file.st_mtime)
1386        self.os.rename(old_file_path, new_file_path)
1387        new_file = self.filesystem.get_object(new_file_path, check_read_perm=False)
1388        self.assertEqual(new_file.st_mtime, old_file.st_mtime)
1389        self.assertEqual(new_file.st_mode, old_file.st_mode)
1390        self.assertEqual(new_file.st_uid, old_file.st_uid)
1391        self.assertEqual(new_file.st_gid, old_file.st_gid)
1392
1393    def test_rename_same_filenames(self):
1394        """Test renaming when old and new names are the same."""
1395        directory = self.make_path("xyzzy")
1396        file_contents = "Spam eggs"
1397        file_path = self.os.path.join(directory, "eggs")
1398        self.create_file(file_path, contents=file_contents)
1399        self.os.rename(file_path, file_path)
1400        self.check_contents(file_path, file_contents)
1401
1402    def test_rmdir(self):
1403        """Can remove a directory."""
1404        directory = self.make_path("xyzzy")
1405        sub_dir = self.make_path("xyzzy", "abccd")
1406        other_dir = self.make_path("xyzzy", "cdeed")
1407        self.create_dir(directory)
1408        self.assertTrue(self.os.path.exists(directory))
1409        self.os.rmdir(directory)
1410        self.assertFalse(self.os.path.exists(directory))
1411        self.create_dir(sub_dir)
1412        self.create_dir(other_dir)
1413        self.os.chdir(sub_dir)
1414        self.os.rmdir("../cdeed")
1415        self.assertFalse(self.os.path.exists(other_dir))
1416        self.os.chdir("..")
1417        self.os.rmdir("abccd")
1418        self.assertFalse(self.os.path.exists(sub_dir))
1419
1420    def test_rmdir_raises_if_not_empty(self):
1421        """Raises an exception if the target directory is not empty."""
1422        directory = self.make_path("xyzzy")
1423        file_path = self.os.path.join(directory, "plugh")
1424        self.create_file(file_path)
1425        self.assertTrue(self.os.path.exists(file_path))
1426        self.assert_raises_os_error(errno.ENOTEMPTY, self.os.rmdir, directory)
1427
1428    def check_rmdir_raises_if_not_directory(self, error_nr):
1429        """Raises an exception if the target is not a directory."""
1430        directory = self.make_path("xyzzy")
1431        file_path = self.os.path.join(directory, "plugh")
1432        self.create_file(file_path)
1433        self.assertTrue(self.os.path.exists(file_path))
1434        self.assert_raises_os_error(errno.ENOTDIR, self.os.rmdir, file_path)
1435        self.assert_raises_os_error(error_nr, self.os.rmdir, ".")
1436
1437    def test_rmdir_raises_if_not_directory_posix(self):
1438        self.check_posix_only()
1439        self.check_rmdir_raises_if_not_directory(errno.EINVAL)
1440
1441    def test_rmdir_raises_if_not_directory_windows(self):
1442        self.check_windows_only()
1443        self.check_rmdir_raises_if_not_directory(errno.EACCES)
1444
1445    def test_rmdir_raises_if_not_exist(self):
1446        """Raises an exception if the target does not exist."""
1447        directory = self.make_path("xyzzy")
1448        self.assertFalse(self.os.path.exists(directory))
1449        self.assert_raises_os_error(errno.ENOENT, self.os.rmdir, directory)
1450
1451    def test_rmdir_via_symlink(self):
1452        self.check_windows_only()
1453        self.skip_if_symlink_not_supported()
1454        base_path = self.make_path("foo", "bar")
1455        dir_path = self.os.path.join(base_path, "alpha")
1456        self.create_dir(dir_path)
1457        link_path = self.os.path.join(base_path, "beta")
1458        self.os.symlink(base_path, link_path)
1459        self.os.rmdir(link_path + "/alpha")
1460        self.assertFalse(self.os.path.exists(dir_path))
1461
1462    def remove_dirs_check(self, directory):
1463        self.assertTrue(self.os.path.exists(directory))
1464        self.os.removedirs(directory)
1465        return not self.os.path.exists(directory)
1466
1467    def test_removedirs(self):
1468        # no exception raised
1469        self.skip_real_fs()
1470        data = [
1471            "test1",
1472            ("test1", "test2"),
1473            ("test1", "extra"),
1474            ("test1", "test2", "test3"),
1475        ]
1476        for directory in data:
1477            self.create_dir(self.make_path(directory))
1478            self.assertTrue(self.os.path.exists(self.make_path(directory)))
1479        self.assert_raises_os_error(
1480            errno.ENOTEMPTY, self.remove_dirs_check, self.make_path(data[0])
1481        )
1482        self.assert_raises_os_error(
1483            errno.ENOTEMPTY, self.remove_dirs_check, self.make_path(data[1])
1484        )
1485
1486        self.assertTrue(self.remove_dirs_check(self.make_path(data[3])))
1487        self.assertTrue(self.os.path.exists(self.make_path(data[0])))
1488        self.assertFalse(self.os.path.exists(self.make_path(data[1])))
1489        self.assertTrue(self.os.path.exists(self.make_path(data[2])))
1490
1491        # Should raise because '/test1/extra' is all that is left, and
1492        # removedirs('/test1/extra') will eventually try to rmdir('/').
1493        self.assert_raises_os_error(
1494            errno.EBUSY, self.remove_dirs_check, self.make_path(data[2])
1495        )
1496
1497        # However, it will still delete '/test1') in the process.
1498        self.assertFalse(self.os.path.exists(self.make_path(data[0])))
1499
1500        self.create_dir(self.make_path("test1", "test2"))
1501        # Add this to the root directory to avoid raising an exception.
1502        self.filesystem.create_dir(self.make_path("test3"))
1503        self.assertTrue(self.remove_dirs_check(self.make_path("test1", "test2")))
1504        self.assertFalse(self.os.path.exists(self.make_path("test1", "test2")))
1505        self.assertFalse(self.os.path.exists(self.make_path("test1")))
1506
1507    def test_removedirs_raises_if_removing_root(self):
1508        """Raises exception if asked to remove '/'."""
1509        self.skip_real_fs()
1510        self.os.rmdir(self.base_path)
1511        directory = self.os.path.splitdrive(self.base_path)[0] + self.os.path.sep
1512        self.assertTrue(self.os.path.exists(directory))
1513        self.assert_raises_os_error(errno.EBUSY, self.os.removedirs, directory)
1514
1515    def test_removedirs_raises_if_cascade_removing_root(self):
1516        """Raises exception if asked to remove '/' as part of a
1517        larger operation.
1518
1519        All of other directories should still be removed, though.
1520        """
1521        self.skip_real_fs()
1522        directory = self.make_path("foo", "bar")
1523        self.create_dir(directory)
1524        self.assertTrue(self.os.path.exists(directory))
1525        self.assert_raises_os_error(errno.EBUSY, self.os.removedirs, directory)
1526        head, unused_tail = self.os.path.split(directory)
1527        while self.os.path.splitdrive(head)[1] != self.os.path.sep:
1528            self.assertFalse(self.os.path.exists(directory))
1529            head, unused_tail = self.os.path.split(head)
1530
1531    def test_removedirs_with_trailing_slash(self):
1532        """removedirs works on directory names with trailing slashes."""
1533        # separate this case from the removing-root-directory case
1534        self.create_dir(self.make_path("baz"))
1535        directory = self.make_path("foo", "bar")
1536        self.create_dir(directory)
1537        self.assertTrue(self.os.path.exists(directory))
1538        self.os.removedirs(directory)
1539        self.assertFalse(self.os.path.exists(directory))
1540
1541    def test_remove_dirs_with_top_symlink_fails(self):
1542        self.check_posix_only()
1543        dir_path = self.make_path("dir")
1544        dir_link = self.make_path("dir_link")
1545        self.create_dir(dir_path)
1546        self.os.symlink(dir_path, dir_link)
1547        self.assert_raises_os_error(errno.ENOTDIR, self.os.removedirs, dir_link)
1548
1549    def test_remove_dirs_with_non_top_symlink_succeeds(self):
1550        self.check_posix_only()
1551        dir_path = self.make_path("dir")
1552        dir_link = self.make_path("dir_link")
1553        self.create_dir(dir_path)
1554        self.os.symlink(dir_path, dir_link)
1555        dir_in_dir = self.os.path.join(dir_link, "dir2")
1556        self.create_dir(dir_in_dir)
1557        self.os.removedirs(dir_in_dir)
1558        self.assertFalse(self.os.path.exists(dir_in_dir))
1559        # ensure that the symlink is not removed
1560        self.assertTrue(self.os.path.exists(dir_link))
1561
1562    def test_mkdir(self):
1563        """mkdir can create a relative directory."""
1564        self.skip_real_fs()
1565        directory = "xyzzy"
1566        self.assertFalse(self.filesystem.exists(directory))
1567        self.os.mkdir(directory)
1568        self.assertTrue(self.filesystem.exists("/%s" % directory))
1569        self.os.chdir(directory)
1570        self.os.mkdir(directory)
1571        self.assertTrue(self.filesystem.exists("/%s/%s" % (directory, directory)))
1572        self.os.chdir(directory)
1573        self.os.mkdir("../abccb")
1574        self.assertTrue(self.os.path.exists("/%s/abccb" % directory))
1575
1576    def test_mkdir_with_trailing_slash(self):
1577        """mkdir can create a directory named with a trailing slash."""
1578        directory = self.make_path("foo")
1579        self.assertFalse(self.os.path.exists(directory))
1580        self.os.mkdir(directory)
1581        self.assertTrue(self.os.path.exists(directory))
1582        self.assertTrue(self.os.path.exists(self.make_path("foo")))
1583
1584    def test_mkdir_raises_if_empty_directory_name(self):
1585        """mkdir raises exception if creating directory named ''."""
1586        directory = ""
1587        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1588
1589    def test_mkdir_raises_if_no_parent(self):
1590        """mkdir raises exception if parent directory does not exist."""
1591        parent = "xyzzy"
1592        directory = "%s/foo" % (parent,)
1593        self.assertFalse(self.os.path.exists(parent))
1594        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1595
1596    def test_mkdir_raises_on_symlink_in_posix(self):
1597        self.check_posix_only()
1598        base_path = self.make_path("foo", "bar")
1599        link_path = self.os.path.join(base_path, "link_to_dir")
1600        dir_path = self.os.path.join(base_path, "dir")
1601        self.create_dir(dir_path)
1602        self.os.symlink(dir_path, link_path)
1603        self.assert_raises_os_error(errno.ENOTDIR, self.os.rmdir, link_path)
1604
1605    def test_mkdir_removes_symlink_in_windows(self):
1606        self.check_windows_only()
1607        self.skip_if_symlink_not_supported()
1608        base_path = self.make_path("foo", "bar")
1609        link_path = self.os.path.join(base_path, "link_to_dir")
1610        dir_path = self.os.path.join(base_path, "dir")
1611        self.create_dir(dir_path)
1612        self.os.symlink(dir_path, link_path)
1613        self.os.rmdir(link_path)
1614        self.assertFalse(self.os.path.exists(link_path))
1615        self.assertTrue(self.os.path.exists(dir_path))
1616
1617    def test_mkdir_raises_if_directory_exists(self):
1618        """mkdir raises exception if directory already exists."""
1619        directory = self.make_path("xyzzy")
1620        self.create_dir(directory)
1621        self.assertTrue(self.os.path.exists(directory))
1622        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory)
1623
1624    def test_mkdir_raises_if_file_exists(self):
1625        """mkdir raises exception if name already exists as a file."""
1626        directory = self.make_path("xyzzy")
1627        file_path = self.os.path.join(directory, "plugh")
1628        self.create_file(file_path)
1629        self.assertTrue(self.os.path.exists(file_path))
1630        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, file_path)
1631
1632    def check_mkdir_raises_if_parent_is_file(self, error_type):
1633        """mkdir raises exception if name already exists as a file."""
1634        directory = self.make_path("xyzzy")
1635        file_path = self.os.path.join(directory, "plugh")
1636        self.create_file(file_path)
1637        self.assert_raises_os_error(
1638            error_type, self.os.mkdir, self.os.path.join(file_path, "ff")
1639        )
1640
1641    def test_mkdir_raises_if_parent_is_file_posix(self):
1642        self.check_posix_only()
1643        self.check_mkdir_raises_if_parent_is_file(errno.ENOTDIR)
1644
1645    def test_mkdir_raises_if_parent_is_file_windows(self):
1646        self.check_windows_only()
1647        self.check_mkdir_raises_if_parent_is_file(errno.ENOENT)
1648
1649    def test_mkdir_raises_with_slash_dot_posix(self):
1650        """mkdir raises exception if mkdir foo/. (trailing /.)."""
1651        self.check_posix_only()
1652        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, self.os.sep + ".")
1653        directory = self.make_path("xyzzy", ".")
1654        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1655        self.create_dir(self.make_path("xyzzy"))
1656        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory)
1657
1658    def test_mkdir_raises_with_slash_dot_windows(self):
1659        """mkdir raises exception if mkdir foo/. (trailing /.)."""
1660        self.check_windows_only()
1661        self.assert_raises_os_error(errno.EACCES, self.os.mkdir, self.os.sep + ".")
1662        directory = self.make_path("xyzzy", ".")
1663        self.os.mkdir(directory)
1664        self.create_dir(self.make_path("xyzzy"))
1665        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory)
1666
1667    def test_mkdir_raises_with_double_dots_posix(self):
1668        """mkdir raises exception if mkdir foo/foo2/../foo3."""
1669        self.check_posix_only()
1670        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, self.os.sep + "..")
1671        directory = self.make_path("xyzzy", "dir1", "dir2", "..", "..", "dir3")
1672        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1673        self.create_dir(self.make_path("xyzzy"))
1674        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1675        self.create_dir(self.make_path("xyzzy", "dir1"))
1676        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1677        self.create_dir(self.make_path("xyzzy", "dir1", "dir2"))
1678        self.os.mkdir(directory)
1679        self.assertTrue(self.os.path.exists(directory))
1680        directory = self.make_path("xyzzy", "dir1", "..")
1681        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory)
1682
1683    def test_mkdir_raises_with_double_dots_windows(self):
1684        """mkdir raises exception if mkdir foo/foo2/../foo3."""
1685        self.check_windows_only()
1686        self.assert_raises_os_error(errno.EACCES, self.os.mkdir, self.os.sep + "..")
1687        directory = self.make_path("xyzzy", "dir1", "dir2", "..", "..", "dir3")
1688        self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory)
1689        self.create_dir(self.make_path("xyzzy"))
1690        self.os.mkdir(directory)
1691        self.assertTrue(self.os.path.exists(directory))
1692        directory = self.make_path("xyzzy", "dir1", "..")
1693        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory)
1694
1695    def test_mkdir_raises_if_parent_is_read_only(self):
1696        """mkdir raises exception if parent is read only."""
1697        self.check_posix_only()
1698        directory = self.make_path("a")
1699        self.os.mkdir(directory)
1700
1701        # Change directory permissions to be read only.
1702        self.os.chmod(directory, 0o400)
1703
1704        directory = self.make_path("a", "b")
1705        if not is_root():
1706            self.assert_raises_os_error(errno.EACCES, self.os.mkdir, directory)
1707        else:
1708            self.os.mkdir(directory)
1709            self.assertTrue(self.os.path.exists(directory))
1710
1711    def test_mkdir_with_with_symlink_parent(self):
1712        self.check_posix_only()
1713        dir_path = self.make_path("foo", "bar")
1714        self.create_dir(dir_path)
1715        link_path = self.make_path("foo", "link")
1716        self.os.symlink(dir_path, link_path)
1717        new_dir = self.os.path.join(link_path, "new_dir")
1718        self.os.mkdir(new_dir)
1719        self.assertTrue(self.os.path.exists(new_dir))
1720
1721    def test_makedirs(self):
1722        """makedirs can create a directory even if parent does not exist."""
1723        parent = self.make_path("xyzzy")
1724        directory = self.os.path.join(parent, "foo")
1725        self.assertFalse(self.os.path.exists(parent))
1726        self.os.makedirs(directory)
1727        self.assertTrue(self.os.path.exists(directory))
1728
1729    def check_makedirs_raises_if_parent_is_file(self, error_type):
1730        """makedirs raises exception if a parent component exists as a file."""
1731        file_path = self.make_path("xyzzy")
1732        directory = self.os.path.join(file_path, "plugh")
1733        self.create_file(file_path)
1734        self.assertTrue(self.os.path.exists(file_path))
1735        self.assert_raises_os_error(error_type, self.os.makedirs, directory)
1736
1737    def test_makedirs_raises_if_parent_is_file_posix(self):
1738        self.check_posix_only()
1739        self.check_makedirs_raises_if_parent_is_file(errno.ENOTDIR)
1740
1741    def test_makedirs_raises_if_parent_is_file_windows(self):
1742        self.check_windows_only()
1743        self.check_makedirs_raises_if_parent_is_file(errno.ENOENT)
1744
1745    def test_makedirs_raises_if_parent_is_broken_link(self):
1746        self.check_posix_only()
1747        link_path = self.make_path("broken_link")
1748        self.os.symlink(self.make_path("bogus"), link_path)
1749        self.assert_raises_os_error(
1750            errno.ENOENT,
1751            self.os.makedirs,
1752            self.os.path.join(link_path, "newdir"),
1753        )
1754
1755    def test_makedirs_raises_if_parent_is_looping_link(self):
1756        self.skip_if_symlink_not_supported()
1757        link_path = self.make_path("link")
1758        link_target = self.os.path.join(link_path, "link")
1759        self.os.symlink(link_target, link_path)
1760        self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, link_path)
1761
1762    def test_makedirs_if_parent_is_symlink(self):
1763        self.check_posix_only()
1764        base_dir = self.make_path("foo", "bar")
1765        self.create_dir(base_dir)
1766        link_dir = self.os.path.join(base_dir, "linked")
1767        self.os.symlink(base_dir, link_dir)
1768        new_dir = self.os.path.join(link_dir, "f")
1769        self.os.makedirs(name=new_dir)
1770        self.assertTrue(self.os.path.exists(new_dir))
1771
1772    def test_makedirs_raises_if_access_denied(self):
1773        """makedirs raises exception if access denied."""
1774        self.check_posix_only()
1775        directory = self.make_path("a")
1776        self.os.mkdir(directory)
1777
1778        # Change directory permissions to be read only.
1779        self.os.chmod(directory, 0o400)
1780
1781        directory = self.make_path("a", "b")
1782        if not is_root():
1783            with self.assertRaises(OSError):
1784                self.os.makedirs(directory)
1785        else:
1786            self.os.makedirs(directory)
1787            self.assertTrue(self.os.path.exists(directory))
1788
1789    def test_makedirs_exist_ok(self):
1790        """makedirs uses the exist_ok argument"""
1791        directory = self.make_path("xyzzy", "foo")
1792        self.create_dir(directory)
1793        self.assertTrue(self.os.path.exists(directory))
1794
1795        self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, directory)
1796        self.os.makedirs(directory, exist_ok=True)
1797        self.assertTrue(self.os.path.exists(directory))
1798
1799    def test_makedirs_in_write_protected_dir(self):
1800        self.check_posix_only()
1801        directory = self.make_path("foo")
1802        self.os.mkdir(directory, mode=0o555)
1803        subdir = self.os.path.join(directory, "bar")
1804        if not is_root():
1805            self.assert_raises_os_error(
1806                errno.EACCES, self.os.makedirs, subdir, exist_ok=True
1807            )
1808            self.assert_raises_os_error(
1809                errno.EACCES, self.os.makedirs, subdir, exist_ok=False
1810            )
1811        else:
1812            self.os.makedirs(subdir)
1813            self.assertTrue(self.os.path.exists(subdir))
1814
1815    def test_makedirs_raises_on_empty_path(self):
1816        self.assert_raises_os_error(errno.ENOENT, self.os.makedirs, "", exist_ok=False)
1817        self.assert_raises_os_error(errno.ENOENT, self.os.makedirs, "", exist_ok=True)
1818
1819    # test fsync and fdatasync
1820    def test_fsync_raises_on_non_int(self):
1821        with self.assertRaises(TypeError):
1822            self.os.fsync("zero")
1823
1824    def test_fdatasync_raises_on_non_int(self):
1825        self.check_linux_only()
1826        self.assertRaises(TypeError, self.os.fdatasync, "zero")
1827
1828    def test_fsync_raises_on_invalid_fd(self):
1829        self.assert_raises_os_error(errno.EBADF, self.os.fsync, 500)
1830
1831    def test_fdatasync_raises_on_invalid_fd(self):
1832        # No open files yet
1833        self.check_linux_only()
1834        self.assert_raises_os_error(errno.EINVAL, self.os.fdatasync, 0)
1835        self.assert_raises_os_error(errno.EBADF, self.os.fdatasync, 500)
1836
1837    def test_fsync_pass_posix(self):
1838        self.check_posix_only()
1839        test_file_path = self.make_path("test_file")
1840        self.create_file(test_file_path, contents="dummy file contents")
1841        with self.open(test_file_path, "r") as test_file:
1842            test_fd = test_file.fileno()
1843            # Test that this doesn't raise anything
1844            self.os.fsync(test_fd)
1845            # And just for sanity, double-check that this still raises
1846            self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd + 500)
1847
1848    def test_fsync_pass_windows(self):
1849        self.check_windows_only()
1850        test_file_path = self.make_path("test_file")
1851        self.create_file(test_file_path, contents="dummy file contents")
1852        with self.open(test_file_path, "r+") as test_file:
1853            test_fd = test_file.fileno()
1854            # Test that this doesn't raise anything
1855            self.os.fsync(test_fd)
1856            # And just for sanity, double-check that this still raises
1857            self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd + 500)
1858        with self.open(test_file_path, "r") as test_file:
1859            test_fd = test_file.fileno()
1860            self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd)
1861
1862    def test_fdatasync_pass(self):
1863        # setup
1864        self.check_linux_only()
1865        test_file_path = self.make_path("test_file")
1866        self.create_file(test_file_path, contents="dummy file contents")
1867        test_file = self.open(test_file_path, "r")
1868        test_fd = test_file.fileno()
1869        # Test that this doesn't raise anything
1870        self.os.fdatasync(test_fd)
1871        # And just for sanity, double-check that this still raises
1872        self.assert_raises_os_error(errno.EBADF, self.os.fdatasync, test_fd + 500)
1873
1874    def test_access700(self):
1875        # set up
1876        self.check_posix_only()
1877        path = self.make_path("some_file")
1878        self.createTestFile(path)
1879        self.os.chmod(path, 0o700)
1880        self.assert_mode_equal(0o700, self.os.stat(path).st_mode)
1881        # actual tests
1882        self.assertTrue(self.os.access(path, self.os.F_OK))
1883        self.assertTrue(self.os.access(path, self.os.R_OK))
1884        self.assertTrue(self.os.access(path, self.os.W_OK))
1885        self.assertTrue(self.os.access(path, self.os.X_OK))
1886        self.assertTrue(self.os.access(path, self.rwx))
1887
1888    def test_access600(self):
1889        # set up
1890        self.check_posix_only()
1891        path = self.make_path("some_file")
1892        self.createTestFile(path)
1893        self.os.chmod(path, 0o600)
1894        self.assert_mode_equal(0o600, self.os.stat(path).st_mode)
1895        # actual tests
1896        self.assertTrue(self.os.access(path, self.os.F_OK))
1897        self.assertTrue(self.os.access(path, self.os.R_OK))
1898        self.assertTrue(self.os.access(path, self.os.W_OK))
1899        self.assertFalse(self.os.access(path, self.os.X_OK))
1900        self.assertFalse(self.os.access(path, self.rwx))
1901        self.assertTrue(self.os.access(path, self.rw))
1902
1903    def test_access400(self):
1904        # set up
1905        self.check_posix_only()
1906        path = self.make_path("some_file")
1907        self.createTestFile(path)
1908        self.os.chmod(path, 0o400)
1909        self.assert_mode_equal(0o400, self.os.stat(path).st_mode)
1910        # actual tests
1911        self.assertTrue(self.os.access(path, self.os.F_OK))
1912        self.assertTrue(self.os.access(path, self.os.R_OK))
1913        self.assertFalse(self.os.access(path, self.os.X_OK))
1914        self.assertFalse(self.os.access(path, self.rwx))
1915        if is_root():
1916            self.assertTrue(self.os.access(path, self.os.W_OK))
1917            self.assertTrue(self.os.access(path, self.rw))
1918        else:
1919            self.assertFalse(self.os.access(path, self.os.W_OK))
1920            self.assertFalse(self.os.access(path, self.rw))
1921
1922    def test_access_symlink(self):
1923        self.skip_if_symlink_not_supported()
1924        self.skip_real_fs()
1925        path = self.make_path("some_file")
1926        self.createTestFile(path)
1927        link_path = self.make_path("link_to_some_file")
1928        self.create_symlink(link_path, path)
1929        self.os.chmod(link_path, 0o400)
1930
1931        # test file
1932        self.assertTrue(self.os.access(link_path, self.os.F_OK))
1933        self.assertTrue(self.os.access(link_path, self.os.R_OK))
1934        if is_root():
1935            self.assertTrue(self.os.access(link_path, self.os.W_OK))
1936            self.assertTrue(self.os.access(link_path, self.rw))
1937        else:
1938            self.assertFalse(self.os.access(link_path, self.os.W_OK))
1939            self.assertFalse(self.os.access(link_path, self.rw))
1940        self.assertFalse(self.os.access(link_path, self.os.X_OK))
1941        self.assertFalse(self.os.access(link_path, self.rwx))
1942
1943        # test link itself
1944        self.assertTrue(self.os.access(link_path, self.os.F_OK, follow_symlinks=False))
1945        self.assertTrue(self.os.access(link_path, self.os.R_OK, follow_symlinks=False))
1946        self.assertTrue(self.os.access(link_path, self.os.W_OK, follow_symlinks=False))
1947        self.assertTrue(self.os.access(link_path, self.os.X_OK, follow_symlinks=False))
1948        self.assertTrue(self.os.access(link_path, self.rwx, follow_symlinks=False))
1949        self.assertTrue(self.os.access(link_path, self.rw, follow_symlinks=False))
1950
1951    def test_access_non_existent_file(self):
1952        # set up
1953        path = self.make_path("non", "existent", "file")
1954        self.assertFalse(self.os.path.exists(path))
1955        # actual tests
1956        self.assertFalse(self.os.access(path, self.os.F_OK))
1957        self.assertFalse(self.os.access(path, self.os.R_OK))
1958        self.assertFalse(self.os.access(path, self.os.W_OK))
1959        self.assertFalse(self.os.access(path, self.os.X_OK))
1960        self.assertFalse(self.os.access(path, self.rwx))
1961        self.assertFalse(self.os.access(path, self.rw))
1962
1963    def test_effective_ids_not_supported_under_windows(self):
1964        self.check_windows_only()
1965        path = self.make_path("foo", "bar")
1966        with self.assertRaises(NotImplementedError):
1967            self.os.access(path, self.os.F_OK, effective_ids=True)
1968
1969    def test_chmod(self):
1970        # set up
1971        self.check_posix_only()
1972        self.skip_real_fs()
1973        path = self.make_path("some_file")
1974        self.createTestFile(path)
1975        # actual tests
1976        self.os.chmod(path, 0o6543)
1977        st = self.os.stat(path)
1978        self.assert_mode_equal(0o6543, st.st_mode)
1979        self.assertTrue(st.st_mode & stat.S_IFREG)
1980        self.assertFalse(st.st_mode & stat.S_IFDIR)
1981
1982    def test_chmod_uses_open_fd_as_path(self):
1983        self.check_posix_only()
1984        self.skip_real_fs()
1985        self.assert_raises_os_error(errno.EBADF, self.os.chmod, 5, 0o6543)
1986        path = self.make_path("some_file")
1987        self.createTestFile(path)
1988
1989        with self.open(path) as f:
1990            self.os.chmod(f.filedes, 0o6543)
1991            st = self.os.stat(path)
1992            self.assert_mode_equal(0o6543, st.st_mode)
1993
1994    def test_chmod_follow_symlink(self):
1995        self.check_posix_only()
1996        path = self.make_path("some_file")
1997        self.createTestFile(path)
1998        link_path = self.make_path("link_to_some_file")
1999        self.create_symlink(link_path, path)
2000        self.os.chmod(link_path, 0o6543)
2001
2002        st = self.os.stat(link_path)
2003        self.assert_mode_equal(0o6543, st.st_mode)
2004        st = self.os.stat(link_path, follow_symlinks=False)
2005        # the exact mode depends on OS and Python version
2006        self.assertEqual(stat.S_IMODE(0o700), stat.S_IMODE(st.st_mode) & 0o700)
2007
2008    def test_chmod_no_follow_symlink(self):
2009        self.check_posix_only()
2010        path = self.make_path("some_file")
2011        self.createTestFile(path)
2012        link_path = self.make_path("link_to_some_file")
2013        self.create_symlink(link_path, path)
2014        if self.os.chmod not in self.os.supports_follow_symlinks or IS_PYPY:
2015            with self.assertRaises(NotImplementedError):
2016                self.os.chmod(link_path, 0o6543, follow_symlinks=False)
2017        else:
2018            self.os.chmod(link_path, 0o6543, follow_symlinks=False)
2019            st = self.os.stat(link_path)
2020            self.assert_mode_equal(0o666, st.st_mode)
2021            st = self.os.stat(link_path, follow_symlinks=False)
2022            self.assert_mode_equal(0o6543, st.st_mode)
2023
2024    def test_lchmod(self):
2025        """lchmod shall behave like chmod with follow_symlinks=True."""
2026        self.check_posix_only()
2027        self.skip_real_fs()
2028        path = self.make_path("some_file")
2029        self.createTestFile(path)
2030        link_path = self.make_path("link_to_some_file")
2031        self.create_symlink(link_path, path)
2032        self.os.lchmod(link_path, 0o6543)
2033
2034        st = self.os.stat(link_path)
2035        self.assert_mode_equal(0o666, st.st_mode)
2036        st = self.os.lstat(link_path)
2037        self.assert_mode_equal(0o6543, st.st_mode)
2038
2039    def test_chmod_dir(self):
2040        # set up
2041        self.check_posix_only()
2042        self.skip_real_fs()
2043        path = self.make_path("some_dir")
2044        self.createTestDirectory(path)
2045        # actual tests
2046        self.os.chmod(path, 0o1434)
2047        st = self.os.stat(path)
2048        self.assert_mode_equal(0o1434, st.st_mode)
2049        self.assertFalse(st.st_mode & stat.S_IFREG)
2050        self.assertTrue(st.st_mode & stat.S_IFDIR)
2051
2052    def test_chmod_non_existent(self):
2053        # set up
2054        path = self.make_path("non", "existent", "file")
2055        self.assertFalse(self.os.path.exists(path))
2056        # actual tests
2057        try:
2058            # Use try-catch to check exception attributes.
2059            self.os.chmod(path, 0o777)
2060            self.fail("Exception is expected.")  # COV_NF_LINE
2061        except OSError as os_error:
2062            self.assertEqual(errno.ENOENT, os_error.errno)
2063            self.assertEqual(path, os_error.filename)
2064
2065    def test_chown_existing_file(self):
2066        # set up
2067        self.skip_real_fs()
2068        file_path = self.make_path("some_file")
2069        self.create_file(file_path)
2070        # first set it make sure it's set
2071        self.os.chown(file_path, 100, 101)
2072        st = self.os.stat(file_path)
2073        self.assertEqual(st[stat.ST_UID], 100)
2074        self.assertEqual(st[stat.ST_GID], 101)
2075        # we can make sure it changed
2076        self.os.chown(file_path, 200, 201)
2077        st = self.os.stat(file_path)
2078        self.assertEqual(st[stat.ST_UID], 200)
2079        self.assertEqual(st[stat.ST_GID], 201)
2080        # setting a value to -1 leaves it unchanged
2081        self.os.chown(file_path, -1, -1)
2082        st = self.os.stat(file_path)
2083        self.assertEqual(st[stat.ST_UID], 200)
2084        self.assertEqual(st[stat.ST_GID], 201)
2085
2086    def test_chown_uses_open_fd_as_path(self):
2087        self.check_posix_only()
2088        self.skip_real_fs()
2089        self.assert_raises_os_error(errno.EBADF, self.os.chown, 5, 100, 101)
2090        file_path = self.make_path("foo", "bar")
2091        self.create_file(file_path)
2092
2093        with self.open(file_path) as f:
2094            self.os.chown(f.filedes, 100, 101)
2095            st = self.os.stat(file_path)
2096            self.assertEqual(st[stat.ST_UID], 100)
2097
2098    def test_chown_follow_symlink(self):
2099        self.skip_real_fs()
2100        file_path = self.make_path("some_file")
2101        self.create_file(file_path)
2102        link_path = self.make_path("link_to_some_file")
2103        self.create_symlink(link_path, file_path)
2104
2105        self.os.chown(link_path, 100, 101)
2106        st = self.os.stat(link_path)
2107        self.assertEqual(st[stat.ST_UID], 100)
2108        self.assertEqual(st[stat.ST_GID], 101)
2109        st = self.os.stat(link_path, follow_symlinks=False)
2110        self.assertNotEqual(st[stat.ST_UID], 100)
2111        self.assertNotEqual(st[stat.ST_GID], 101)
2112
2113    def test_chown_no_follow_symlink(self):
2114        self.skip_real_fs()
2115        file_path = self.make_path("some_file")
2116        self.create_file(file_path)
2117        link_path = self.make_path("link_to_some_file")
2118        self.create_symlink(link_path, file_path)
2119
2120        self.os.chown(link_path, 100, 101, follow_symlinks=False)
2121        st = self.os.stat(link_path)
2122        self.assertNotEqual(st[stat.ST_UID], 100)
2123        self.assertNotEqual(st[stat.ST_GID], 101)
2124        st = self.os.stat(link_path, follow_symlinks=False)
2125        self.assertEqual(st[stat.ST_UID], 100)
2126        self.assertEqual(st[stat.ST_GID], 101)
2127
2128    def test_chown_bad_arguments(self):
2129        """os.chown() with bad args (Issue #30)"""
2130        self.check_posix_only()
2131        file_path = self.make_path("some_file")
2132        self.create_file(file_path)
2133        self.assertRaises(TypeError, self.os.chown, file_path, "username", -1)
2134        self.assertRaises(TypeError, self.os.chown, file_path, -1, "groupname")
2135
2136    def test_chown_nonexisting_file_should_raise_os_error(self):
2137        self.check_posix_only()
2138        file_path = self.make_path("some_file")
2139        self.assertFalse(self.os.path.exists(file_path))
2140        self.assert_raises_os_error(errno.ENOENT, self.os.chown, file_path, 100, 100)
2141
2142    def test_classify_directory_contents(self):
2143        """Directory classification should work correctly."""
2144        root_directory = self.make_path("foo")
2145        test_directories = ["bar1", "baz2"]
2146        test_files = ["baz1", "bar2", "baz3"]
2147        self.create_dir(root_directory)
2148        for directory in test_directories:
2149            directory = self.os.path.join(root_directory, directory)
2150            self.create_dir(directory)
2151        for test_file in test_files:
2152            test_file = self.os.path.join(root_directory, test_file)
2153            self.create_file(test_file)
2154
2155        test_directories.sort()
2156        test_files.sort()
2157        generator = self.os.walk(root_directory)
2158        root, dirs, files = next(generator)
2159        dirs.sort()
2160        files.sort()
2161        self.assertEqual(root_directory, root)
2162        self.assertEqual(test_directories, dirs)
2163        self.assertEqual(test_files, files)
2164
2165    # os.mknod does not work under MacOS due to permission issues
2166    # so we test it under Linux only
2167    def test_mk_nod_can_create_a_file(self):
2168        self.check_linux_only()
2169        filename = self.make_path("foo")
2170        self.assertFalse(self.os.path.exists(filename))
2171        self.os.mknod(filename)
2172        self.assertTrue(self.os.path.exists(filename))
2173        self.assertEqual(stat.S_IFREG | 0o600, self.os.stat(filename).st_mode)
2174
2175    def test_mk_nod_raises_if_empty_file_name(self):
2176        self.check_linux_only()
2177        filename = ""
2178        self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename)
2179
2180    def test_mk_nod_raises_if_parent_dir_doesnt_exist(self):
2181        self.check_linux_only()
2182        parent = self.make_path("xyzzy")
2183        filename = self.os.path.join(parent, "foo")
2184        self.assertFalse(self.os.path.exists(parent))
2185        self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename)
2186
2187    def test_mk_nod_raises_if_file_exists(self):
2188        self.check_linux_only()
2189        filename = self.make_path("tmp", "foo")
2190        self.create_file(filename)
2191        self.assertTrue(self.os.path.exists(filename))
2192        self.assert_raises_os_error(errno.EEXIST, self.os.mknod, filename)
2193
2194    def test_mk_nod_raises_if_filename_is_dot(self):
2195        self.check_linux_only()
2196        filename = self.make_path("tmp", ".")
2197        self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename)
2198
2199    def test_mk_nod_raises_if_filename_is_double_dot(self):
2200        self.check_linux_only()
2201        filename = self.make_path("tmp", "..")
2202        self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename)
2203
2204    def test_mknod_empty_tail_for_existing_file_raises(self):
2205        self.check_linux_only()
2206        filename = self.make_path("foo")
2207        self.create_file(filename)
2208        self.assertTrue(self.os.path.exists(filename))
2209        self.assert_raises_os_error(errno.EEXIST, self.os.mknod, filename)
2210
2211    def test_mknod_empty_tail_for_nonexistent_file_raises(self):
2212        self.check_linux_only()
2213        filename = self.make_path("tmp", "foo")
2214        self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename)
2215
2216    def test_mknod_raises_if_filename_is_empty_string(self):
2217        self.check_linux_only()
2218        filename = ""
2219        self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename)
2220
2221    def test_mknod_raises_if_unsupported_options(self):
2222        self.check_posix_only()
2223        # behavior seems to have changed in ubuntu-20.04, version 20210606.1
2224        # skipping real fs tests for now
2225        self.skip_real_fs()
2226        filename = "abcde"
2227        if not is_root():
2228            self.assert_raises_os_error(
2229                errno.EPERM, self.os.mknod, filename, stat.S_IFCHR
2230            )
2231        else:
2232            self.os.mknod(filename, stat.S_IFCHR)
2233            self.os.remove(filename)
2234
2235    def test_mknod_raises_if_parent_is_not_a_directory(self):
2236        self.check_linux_only()
2237        filename1 = self.make_path("foo")
2238        self.create_file(filename1)
2239        self.assertTrue(self.os.path.exists(filename1))
2240        filename2 = self.make_path("foo", "bar")
2241        self.assert_raises_os_error(errno.ENOTDIR, self.os.mknod, filename2)
2242
2243    def test_symlink(self):
2244        self.skip_if_symlink_not_supported()
2245        file_path = self.make_path("foo", "bar", "baz")
2246        self.create_dir(self.make_path("foo", "bar"))
2247        self.os.symlink("bogus", file_path)
2248        self.assertTrue(self.os.path.lexists(file_path))
2249        self.assertFalse(self.os.path.exists(file_path))
2250        self.create_file(self.make_path("foo", "bar", "bogus"))
2251        self.assertTrue(self.os.path.lexists(file_path))
2252        self.assertTrue(self.os.path.exists(file_path))
2253
2254    def test_symlink_on_nonexisting_path_raises(self):
2255        self.check_posix_only()
2256        dir_path = self.make_path("bar")
2257        link_path = self.os.path.join(dir_path, "bar")
2258        self.assert_raises_os_error(errno.ENOENT, self.os.symlink, link_path, link_path)
2259        self.assert_raises_os_error(errno.ENOENT, self.os.symlink, dir_path, link_path)
2260
2261    def test_symlink_with_path_ending_with_sep_in_posix(self):
2262        self.check_posix_only()
2263        dir_path = self.make_path("dir")
2264        self.create_dir(dir_path)
2265        self.assert_raises_os_error(
2266            errno.EEXIST,
2267            self.os.symlink,
2268            self.base_path,
2269            dir_path + self.os.sep,
2270        )
2271
2272        dir_path = self.make_path("bar")
2273        self.assert_raises_os_error(
2274            errno.ENOENT,
2275            self.os.symlink,
2276            self.base_path,
2277            dir_path + self.os.sep,
2278        )
2279
2280    def test_symlink_with_path_ending_with_sep_in_windows(self):
2281        self.check_windows_only()
2282        self.skip_if_symlink_not_supported()
2283        dir_path = self.make_path("dir")
2284        self.create_dir(dir_path)
2285        self.assert_raises_os_error(
2286            errno.EEXIST,
2287            self.os.symlink,
2288            self.base_path,
2289            dir_path + self.os.sep,
2290        )
2291
2292        dir_path = self.make_path("bar")
2293        # does not raise under Windows
2294        self.os.symlink(self.base_path, dir_path + self.os.sep)
2295
2296    def test_broken_symlink_with_trailing_sep_posix(self):
2297        # Regression test for #390
2298        self.check_linux_only()
2299        path0 = self.make_path("foo") + self.os.sep
2300        self.assert_raises_os_error(errno.ENOENT, self.os.symlink, path0, path0)
2301
2302    def test_broken_symlink_with_trailing_sep_windows(self):
2303        self.check_windows_only()
2304        self.skip_if_symlink_not_supported()
2305        path0 = self.make_path("foo") + self.os.sep
2306        self.assert_raises_os_error(errno.EINVAL, self.os.symlink, path0, path0)
2307
2308    def test_rename_symlink_with_trailing_sep_linux(self):
2309        # Regression test for #391
2310        self.check_linux_only()
2311        path = self.make_path("foo")
2312        self.os.symlink(self.base_path, path)
2313        self.assert_raises_os_error(
2314            errno.ENOTDIR, self.os.rename, path + self.os.sep, self.base_path
2315        )
2316
2317    def test_rename_symlink_with_trailing_sep_macos(self):
2318        # Regression test for #391
2319        self.check_macos_only()
2320        path = self.make_path("foo")
2321        self.os.symlink(self.base_path, path)
2322        self.os.rename(path + self.os.sep, self.base_path)
2323
2324    def test_rename_symlink_with_trailing_sep_windows(self):
2325        self.check_windows_only()
2326        self.skip_if_symlink_not_supported()
2327        path = self.make_path("foo")
2328        self.os.symlink(self.base_path, path)
2329        self.assert_raises_os_error(
2330            errno.EEXIST, self.os.rename, path + self.os.sep, self.base_path
2331        )
2332
2333    def test_rename_symlink_to_other_case(self):
2334        # Regression test for #389
2335        self.skip_if_symlink_not_supported()
2336        link_path = self.make_path("foo")
2337        self.os.symlink(self.base_path, link_path)
2338        link_to_link_path = self.make_path("BAR")
2339        self.os.symlink(link_path, link_to_link_path)
2340        new_link_to_link_path = self.os.path.join(link_path, "bar")
2341        self.os.rename(link_to_link_path, new_link_to_link_path)
2342        self.assertEqual(["bar", "foo"], sorted(self.os.listdir(new_link_to_link_path)))
2343
2344    def create_broken_link_path_with_trailing_sep(self):
2345        # Regression tests for #396
2346        self.skip_if_symlink_not_supported()
2347        link_path = self.make_path("link")
2348        target_path = self.make_path("target")
2349        self.os.symlink(target_path, link_path)
2350        link_path += self.os.sep
2351        return link_path
2352
2353    def test_lstat_broken_link_with_trailing_sep_linux(self):
2354        self.check_linux_only()
2355        link_path = self.create_broken_link_path_with_trailing_sep()
2356        self.assert_raises_os_error(errno.ENOENT, self.os.lstat, link_path)
2357
2358    def test_lstat_broken_link_with_trailing_sep_macos(self):
2359        self.check_macos_only()
2360        link_path = self.create_broken_link_path_with_trailing_sep()
2361        self.assert_raises_os_error(errno.ENOENT, self.os.lstat, link_path)
2362
2363    def test_lstat_broken_link_with_trailing_sep_windows(self):
2364        self.check_windows_only()
2365        link_path = self.create_broken_link_path_with_trailing_sep()
2366        self.assert_raises_os_error(errno.EINVAL, self.os.lstat, link_path)
2367
2368    def test_mkdir_broken_link_with_trailing_sep_linux_windows(self):
2369        self.check_linux_and_windows()
2370        link_path = self.create_broken_link_path_with_trailing_sep()
2371        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, link_path)
2372        self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, link_path)
2373
2374    def test_mkdir_broken_link_with_trailing_sep_macos(self):
2375        self.check_macos_only()
2376        link_path = self.create_broken_link_path_with_trailing_sep()
2377        self.os.mkdir(link_path)  # no error
2378
2379    def test_makedirs_broken_link_with_trailing_sep_macos(self):
2380        self.check_macos_only()
2381        link_path = self.create_broken_link_path_with_trailing_sep()
2382        self.os.makedirs(link_path)  # no error
2383
2384    def test_remove_broken_link_with_trailing_sep_linux(self):
2385        self.check_linux_only()
2386        link_path = self.create_broken_link_path_with_trailing_sep()
2387        self.assert_raises_os_error(errno.ENOTDIR, self.os.remove, link_path)
2388
2389    def test_remove_broken_link_with_trailing_sep_macos(self):
2390        self.check_macos_only()
2391        link_path = self.create_broken_link_path_with_trailing_sep()
2392        self.assert_raises_os_error(errno.ENOENT, self.os.remove, link_path)
2393
2394    def test_remove_broken_link_with_trailing_sep_windows(self):
2395        self.check_windows_only()
2396        link_path = self.create_broken_link_path_with_trailing_sep()
2397        self.assert_raises_os_error(errno.EINVAL, self.os.remove, link_path)
2398
2399    def test_rename_broken_link_with_trailing_sep_linux(self):
2400        self.check_linux_only()
2401        link_path = self.create_broken_link_path_with_trailing_sep()
2402        self.assert_raises_os_error(
2403            errno.ENOTDIR, self.os.rename, link_path, self.make_path("target")
2404        )
2405
2406    def test_rename_broken_link_with_trailing_sep_macos(self):
2407        self.check_macos_only()
2408        link_path = self.create_broken_link_path_with_trailing_sep()
2409        self.assert_raises_os_error(
2410            errno.ENOENT, self.os.rename, link_path, self.make_path("target")
2411        )
2412
2413    def test_rename_broken_link_with_trailing_sep_windows(self):
2414        self.check_windows_only()
2415        link_path = self.create_broken_link_path_with_trailing_sep()
2416        self.assert_raises_os_error(
2417            errno.EINVAL, self.os.rename, link_path, self.make_path("target")
2418        )
2419
2420    def test_readlink_broken_link_with_trailing_sep_posix(self):
2421        self.check_posix_only()
2422        link_path = self.create_broken_link_path_with_trailing_sep()
2423        self.assert_raises_os_error(errno.ENOENT, self.os.readlink, link_path)
2424
2425    def test_readlink_broken_link_with_trailing_sep_windows(self):
2426        self.check_windows_only()
2427        link_path = self.create_broken_link_path_with_trailing_sep()
2428        self.assert_raises_os_error(errno.EINVAL, self.os.readlink, link_path)
2429
2430    def test_islink_broken_link_with_trailing_sep(self):
2431        link_path = self.create_broken_link_path_with_trailing_sep()
2432        self.assertFalse(self.os.path.islink(link_path))
2433
2434    def test_lexists_broken_link_with_trailing_sep(self):
2435        link_path = self.create_broken_link_path_with_trailing_sep()
2436        self.assertFalse(self.os.path.lexists(link_path))
2437
2438    def test_rename_link_with_trailing_sep_to_self_windows(self):
2439        self.check_windows_only()
2440        self.skip_if_symlink_not_supported()
2441        path = self.make_path("foo")
2442        self.os.symlink(self.base_path, path)
2443        self.os.rename(path + self.os.sep, path)  # no error
2444
2445    def test_rename_link_with_trailing_sep_to_self_posix(self):
2446        # Regression test for #395
2447        self.check_posix_only()
2448        path = self.make_path("foo")
2449        self.os.symlink(self.base_path, path)
2450        self.assert_raises_os_error(
2451            errno.ENOTDIR, self.os.rename, path + self.os.sep, path
2452        )
2453
2454    def check_open_broken_symlink_to_path_with_trailing_sep(self, error):
2455        # Regression tests for #397
2456        self.skip_if_symlink_not_supported()
2457        target_path = self.make_path("target") + self.os.sep
2458        link_path = self.make_path("link")
2459        self.os.symlink(target_path, link_path)
2460        self.assert_raises_os_error(error, self.open, link_path, "a")
2461        self.assert_raises_os_error(error, self.open, link_path, "w")
2462
2463    def test_open_broken_symlink_to_path_with_trailing_sep_linux(self):
2464        self.check_linux_only()
2465        self.check_open_broken_symlink_to_path_with_trailing_sep(errno.EISDIR)
2466
2467    def test_open_broken_symlink_to_path_with_trailing_sep_macos(self):
2468        self.check_macos_only()
2469        self.check_open_broken_symlink_to_path_with_trailing_sep(errno.ENOENT)
2470
2471    def test_open_broken_symlink_to_path_with_trailing_sep_windows(self):
2472        self.check_windows_only()
2473        self.check_open_broken_symlink_to_path_with_trailing_sep(errno.EINVAL)
2474
2475    def check_link_path_ending_with_sep(self, error):
2476        # Regression tests for #399
2477        self.skip_if_symlink_not_supported()
2478        file_path = self.make_path("foo")
2479        link_path = self.make_path("link")
2480        with self.open(file_path, "w"):
2481            self.assert_raises_os_error(
2482                error, self.os.link, file_path + self.os.sep, link_path
2483            )
2484
2485    def test_link_path_ending_with_sep_posix(self):
2486        self.check_posix_only()
2487        self.check_link_path_ending_with_sep(errno.ENOTDIR)
2488
2489    def test_link_path_ending_with_sep_windows(self):
2490        self.check_windows_only()
2491        self.check_link_path_ending_with_sep(errno.EINVAL)
2492
2493    def test_link_to_path_ending_with_sep_posix(self):
2494        # regression test for #407
2495        self.check_posix_only()
2496        path0 = self.make_path("foo") + self.os.sep
2497        path1 = self.make_path("bar")
2498        with self.open(path1, "w"):
2499            self.assert_raises_os_error(errno.ENOENT, self.os.link, path1, path0)
2500
2501    def test_link_to_path_ending_with_sep_windows(self):
2502        self.check_windows_only()
2503        self.skip_if_symlink_not_supported()
2504        path0 = self.make_path("foo") + self.os.sep
2505        path1 = self.make_path("bar")
2506        with self.open(path1, "w"):
2507            self.os.link(path1, path0)
2508            self.assertTrue(self.os.path.exists(path1))
2509
2510    def check_rename_to_path_ending_with_sep(self, error):
2511        # Regression tests for #400
2512        file_path = self.make_path("foo")
2513        with self.open(file_path, "w"):
2514            self.assert_raises_os_error(
2515                error, self.os.rename, file_path + self.os.sep, file_path
2516            )
2517
2518    def test_rename_to_path_ending_with_sep_posix(self):
2519        self.check_posix_only()
2520        self.check_rename_to_path_ending_with_sep(errno.ENOTDIR)
2521
2522    def test_rename_to_path_ending_with_sep_windows(self):
2523        self.check_windows_only()
2524        self.check_rename_to_path_ending_with_sep(errno.EINVAL)
2525
2526    def test_rmdir_link_with_trailing_sep_linux(self):
2527        self.check_linux_only()
2528        dir_path = self.make_path("foo")
2529        self.os.mkdir(dir_path)
2530        link_path = self.make_path("link")
2531        self.os.symlink(dir_path, link_path)
2532        self.assert_raises_os_error(
2533            errno.ENOTDIR, self.os.rmdir, link_path + self.os.sep
2534        )
2535
2536    def test_rmdir_link_with_trailing_sep_macos(self):
2537        # Regression test for #398
2538        self.check_macos_only()
2539        dir_path = self.make_path("foo")
2540        self.os.mkdir(dir_path)
2541        link_path = self.make_path("link")
2542        self.os.symlink(dir_path, link_path)
2543        self.os.rmdir(link_path + self.os.sep)
2544        self.assertFalse(self.os.path.exists(link_path))
2545
2546    def test_rmdir_link_with_trailing_sep_windows(self):
2547        self.check_windows_only()
2548        self.skip_if_symlink_not_supported()
2549        dir_path = self.make_path("foo")
2550        self.os.mkdir(dir_path)
2551        link_path = self.make_path("link")
2552        self.os.symlink(dir_path, link_path)
2553        self.os.rmdir(link_path + self.os.sep)
2554        self.assertFalse(self.os.path.exists(link_path))
2555
2556    def test_readlink_circular_link_with_trailing_sep_linux(self):
2557        self.check_linux_only()
2558        path1 = self.make_path("foo")
2559        path0 = self.make_path("bar")
2560        self.os.symlink(path0, path1)
2561        self.os.symlink(path1, path0)
2562        self.assert_raises_os_error(errno.ELOOP, self.os.readlink, path0 + self.os.sep)
2563
2564    def test_readlink_circular_link_with_trailing_sep_macos(self):
2565        # Regression test for #392
2566        self.check_macos_only()
2567        path1 = self.make_path("foo")
2568        path0 = self.make_path("bar")
2569        self.os.symlink(path0, path1)
2570        self.os.symlink(path1, path0)
2571        self.assertEqual(path0, self.os.readlink(path0 + self.os.sep))
2572
2573    def test_readlink_circular_link_with_trailing_sep_windows(self):
2574        self.check_windows_only()
2575        self.skip_if_symlink_not_supported()
2576        path1 = self.make_path("foo")
2577        path0 = self.make_path("bar")
2578        self.os.symlink(path0, path1)
2579        self.os.symlink(path1, path0)
2580        self.assert_raises_os_error(errno.EINVAL, self.os.readlink, path0 + self.os.sep)
2581
2582    # hard link related tests
2583    def test_link_bogus(self):
2584        # trying to create a link from a non-existent file should fail
2585        self.skip_if_symlink_not_supported()
2586        self.assert_raises_os_error(
2587            errno.ENOENT, self.os.link, "/nonexistent_source", "/link_dest"
2588        )
2589
2590    def test_link_delete(self):
2591        self.skip_if_symlink_not_supported()
2592
2593        file1_path = self.make_path("test_file1")
2594        file2_path = self.make_path("test_file2")
2595        contents1 = "abcdef"
2596        # Create file
2597        self.create_file(file1_path, contents=contents1)
2598        # link to second file
2599        self.os.link(file1_path, file2_path)
2600        # delete first file
2601        self.os.unlink(file1_path)
2602        # assert that second file exists, and its contents are the same
2603        self.assertTrue(self.os.path.exists(file2_path))
2604        with self.open(file2_path) as f:
2605            self.assertEqual(f.read(), contents1)
2606
2607    def test_link_update(self):
2608        self.skip_if_symlink_not_supported()
2609
2610        file1_path = self.make_path("test_file1")
2611        file2_path = self.make_path("test_file2")
2612        contents1 = "abcdef"
2613        contents2 = "ghijkl"
2614        # Create file and link
2615        self.create_file(file1_path, contents=contents1)
2616        self.os.link(file1_path, file2_path)
2617        # assert that the second file contains contents1
2618        with self.open(file2_path) as f:
2619            self.assertEqual(f.read(), contents1)
2620        # update the first file
2621        with self.open(file1_path, "w") as f:
2622            f.write(contents2)
2623        # assert that second file contains contents2
2624        with self.open(file2_path) as f:
2625            self.assertEqual(f.read(), contents2)
2626
2627    def test_link_non_existent_parent(self):
2628        self.skip_if_symlink_not_supported()
2629        file1_path = self.make_path("test_file1")
2630        breaking_link_path = self.make_path("nonexistent", "test_file2")
2631        contents1 = "abcdef"
2632        # Create file and link
2633        self.create_file(file1_path, contents=contents1)
2634
2635        # trying to create a link under a non-existent directory should fail
2636        self.assert_raises_os_error(
2637            errno.ENOENT, self.os.link, file1_path, breaking_link_path
2638        )
2639
2640    def test_link_is_existing_file(self):
2641        self.skip_if_symlink_not_supported()
2642        file_path = self.make_path("foo", "bar")
2643        self.create_file(file_path)
2644        self.assert_raises_os_error(errno.EEXIST, self.os.link, file_path, file_path)
2645
2646    def test_link_target_is_dir_windows(self):
2647        self.check_windows_only()
2648        self.skip_if_symlink_not_supported()
2649        dir_path = self.make_path("foo", "bar")
2650        link_path = self.os.path.join(dir_path, "link")
2651        self.create_dir(dir_path)
2652        self.assert_raises_os_error(errno.EACCES, self.os.link, dir_path, link_path)
2653
2654    def test_link_target_is_dir_posix(self):
2655        self.check_posix_only()
2656        dir_path = self.make_path("foo", "bar")
2657        link_path = self.os.path.join(dir_path, "link")
2658        self.create_dir(dir_path)
2659        self.assert_raises_os_error(errno.EPERM, self.os.link, dir_path, link_path)
2660
2661    def test_link_count1(self):
2662        """Test that hard link counts are updated correctly."""
2663        self.skip_if_symlink_not_supported()
2664        file1_path = self.make_path("test_file1")
2665        file2_path = self.make_path("test_file2")
2666        file3_path = self.make_path("test_file3")
2667        self.create_file(file1_path)
2668        # initial link count should be one
2669        self.assertEqual(self.os.stat(file1_path).st_nlink, 1)
2670        self.os.link(file1_path, file2_path)
2671        # the count should be incremented for each hard link created
2672        self.assertEqual(self.os.stat(file1_path).st_nlink, 2)
2673        self.assertEqual(self.os.stat(file2_path).st_nlink, 2)
2674        # Check that the counts are all updated together
2675        self.os.link(file2_path, file3_path)
2676        self.assertEqual(self.os.stat(file1_path).st_nlink, 3)
2677        self.assertEqual(self.os.stat(file2_path).st_nlink, 3)
2678        self.assertEqual(self.os.stat(file3_path).st_nlink, 3)
2679        # Counts should be decremented when links are removed
2680        self.os.unlink(file3_path)
2681        self.assertEqual(self.os.stat(file1_path).st_nlink, 2)
2682        self.assertEqual(self.os.stat(file2_path).st_nlink, 2)
2683        # check that it gets decremented correctly again
2684        self.os.unlink(file1_path)
2685        self.assertEqual(self.os.stat(file2_path).st_nlink, 1)
2686
2687    def test_nlink_for_directories(self):
2688        self.skip_real_fs()
2689        self.create_dir(self.make_path("foo", "bar"))
2690        self.create_file(self.make_path("foo", "baz"))
2691        self.assertEqual(
2692            2,
2693            self.filesystem.get_object(self.make_path("foo", "bar")).st_nlink,
2694        )
2695        self.assertEqual(4, self.filesystem.get_object(self.make_path("foo")).st_nlink)
2696        self.create_file(self.make_path("foo", "baz2"))
2697        self.assertEqual(5, self.filesystem.get_object(self.make_path("foo")).st_nlink)
2698
2699    def test_umask(self):
2700        self.check_posix_only()
2701        umask = os.umask(0o22)
2702        os.umask(umask)
2703        self.assertEqual(umask, self.os.umask(0o22))
2704
2705    def test_mkdir_umask_applied(self):
2706        """mkdir creates a directory with umask applied."""
2707        self.check_posix_only()
2708        self.os.umask(0o22)
2709        dir1 = self.make_path("dir1")
2710        self.os.mkdir(dir1)
2711        self.assert_mode_equal(0o755, self.os.stat(dir1).st_mode)
2712        self.os.umask(0o67)
2713        dir2 = self.make_path("dir2")
2714        self.os.mkdir(dir2)
2715        self.assert_mode_equal(0o710, self.os.stat(dir2).st_mode)
2716
2717    def test_makedirs_umask_applied(self):
2718        """makedirs creates a directories with umask applied."""
2719        self.check_posix_only()
2720        self.os.umask(0o22)
2721        self.os.makedirs(self.make_path("p1", "dir1"))
2722        self.assert_mode_equal(0o755, self.os.stat(self.make_path("p1")).st_mode)
2723        self.assert_mode_equal(
2724            0o755, self.os.stat(self.make_path("p1", "dir1")).st_mode
2725        )
2726        self.os.umask(0o67)
2727        self.os.makedirs(self.make_path("p2", "dir2"))
2728        self.assert_mode_equal(0o710, self.os.stat(self.make_path("p2")).st_mode)
2729        self.assert_mode_equal(
2730            0o710, self.os.stat(self.make_path("p2", "dir2")).st_mode
2731        )
2732
2733    def test_mknod_umask_applied(self):
2734        """mkdir creates a device with umask applied."""
2735        # skipping MacOs due to mknod permission issues
2736        self.check_linux_only()
2737        self.os.umask(0o22)
2738        node1 = self.make_path("nod1")
2739        self.os.mknod(node1, stat.S_IFREG | 0o666)
2740        self.assert_mode_equal(0o644, self.os.stat(node1).st_mode)
2741        self.os.umask(0o27)
2742        node2 = self.make_path("nod2")
2743        self.os.mknod(node2, stat.S_IFREG | 0o666)
2744        self.assert_mode_equal(0o640, self.os.stat(node2).st_mode)
2745
2746    def test_open_umask_applied(self):
2747        """open creates a file with umask applied."""
2748        self.check_posix_only()
2749        self.os.umask(0o22)
2750        file1 = self.make_path("file1")
2751        self.open(file1, "w").close()
2752        self.assert_mode_equal(0o644, self.os.stat(file1).st_mode)
2753        self.os.umask(0o27)
2754        file2 = self.make_path("file2")
2755        self.open(file2, "w").close()
2756        self.assert_mode_equal(0o640, self.os.stat(file2).st_mode)
2757
2758    def test_open_pipe(self):
2759        read_fd, write_fd = self.os.pipe()
2760        self.os.close(read_fd)
2761        self.os.close(write_fd)
2762
2763    def test_open_pipe_with_existing_fd(self):
2764        file1 = self.make_path("file1")
2765        fd = self.os.open(file1, os.O_CREAT)
2766        read_fd, write_fd = self.os.pipe()
2767        self.assertGreater(read_fd, fd)
2768        self.os.close(fd)
2769        self.os.close(read_fd)
2770        self.os.close(write_fd)
2771
2772    def test_open_file_with_existing_pipe(self):
2773        read_fd, write_fd = self.os.pipe()
2774        file1 = self.make_path("file1")
2775        fd = self.os.open(file1, os.O_CREAT)
2776        self.assertGreater(fd, write_fd)
2777        self.os.close(read_fd)
2778        self.os.close(write_fd)
2779        self.os.close(fd)
2780
2781    def test_read_write_pipe(self):
2782        read_fd, write_fd = self.os.pipe()
2783        self.assertEqual(4, self.os.write(write_fd, b"test"))
2784        self.assertEqual(b"test", self.os.read(read_fd, 4))
2785        self.os.close(read_fd)
2786        self.os.close(write_fd)
2787
2788    def test_open_existing_pipe(self):
2789        # create some regular files to ensure that real and fake fd
2790        # are out of sync (see #581)
2791        fds = []
2792        for i in range(5):
2793            path = self.make_path("file" + str(i))
2794            fds.append(self.os.open(path, os.O_CREAT))
2795        file_path = self.make_path("file.txt")
2796        self.create_file(file_path)
2797        with self.open(file_path):
2798            read_fd, write_fd = self.os.pipe()
2799            with self.open(write_fd, "wb") as f:
2800                self.assertEqual(4, f.write(b"test"))
2801            with self.open(read_fd, "rb") as f:
2802                self.assertEqual(b"test", f.read())
2803        for fd in fds:
2804            self.os.close(fd)
2805
2806    def test_write_to_pipe(self):
2807        read_fd, write_fd = self.os.pipe()
2808        self.os.write(write_fd, b"test")
2809        self.assertEqual(b"test", self.os.read(read_fd, 4))
2810        self.os.close(read_fd)
2811        self.os.close(write_fd)
2812
2813    @unittest.skipIf(
2814        sys.platform not in ("win32", "darwin", "linux"),
2815        "Pipe implementation may differ on other platforms",
2816    )
2817    def test_write_to_read_fd(self):
2818        read_fd, write_fd = self.os.pipe()
2819        self.assert_raises_os_error(errno.EBADF, self.os.write, read_fd, b"test")
2820        self.os.close(read_fd)
2821        self.os.close(write_fd)
2822
2823    def test_truncate(self):
2824        file_path = self.make_path("foo", "bar")
2825        self.create_file(file_path, contents="012345678901234567")
2826        self.os.truncate(file_path, 10)
2827        with self.open(file_path) as f:
2828            self.assertEqual("0123456789", f.read())
2829
2830    def test_truncate_non_existing(self):
2831        self.assert_raises_os_error(errno.ENOENT, self.os.truncate, "foo", 10)
2832
2833    def test_truncate_to_larger(self):
2834        file_path = self.make_path("foo", "bar")
2835        self.create_file(file_path, contents="0123456789")
2836        fd = self.os.open(file_path, os.O_RDWR)
2837        self.os.truncate(fd, 20)
2838        self.assertEqual(20, self.os.stat(file_path).st_size)
2839        with self.open(file_path) as f:
2840            self.assertEqual("0123456789" + "\0" * 10, f.read())
2841
2842    def test_truncate_with_fd(self):
2843        if os.truncate not in os.supports_fd:
2844            self.skip_real_fs()
2845        self.assert_raises_os_error(errno.EBADF, self.os.ftruncate, 50, 10)
2846        file_path = self.make_path("some_file")
2847        self.create_file(file_path, contents="01234567890123456789")
2848
2849        fd = self.os.open(file_path, os.O_RDWR)
2850        self.os.truncate(fd, 10)
2851        self.assertEqual(10, self.os.stat(file_path).st_size)
2852        with self.open(file_path) as f:
2853            self.assertEqual("0123456789", f.read())
2854
2855    def test_ftruncate(self):
2856        if self.is_pypy:
2857            # not correctly supported
2858            self.skip_real_fs()
2859        self.assert_raises_os_error(errno.EBADF, self.os.ftruncate, 50, 10)
2860        file_path = self.make_path("some_file")
2861        self.create_file(file_path, contents="0123456789012345")
2862
2863        fd = self.os.open(file_path, os.O_RDWR)
2864        self.os.truncate(fd, 10)
2865        self.assertEqual(10, self.os.stat(file_path).st_size)
2866        with self.open(file_path) as f:
2867            self.assertEqual("0123456789", f.read())
2868
2869    def test_capabilities(self):
2870        """Make sure that the fake capabilities are the same as the real ones."""
2871        self.assertEqual(
2872            self.os.stat in self.os.supports_follow_symlinks,
2873            os.stat in os.supports_follow_symlinks,
2874        )
2875        self.assertEqual(self.os.stat in self.os.supports_fd, os.stat in os.supports_fd)
2876        self.assertEqual(
2877            self.os.stat in self.os.supports_dir_fd, os.stat in os.supports_dir_fd
2878        )
2879        self.assertEqual(
2880            self.os.stat in self.os.supports_effective_ids,
2881            os.stat in os.supports_effective_ids,
2882        )
2883
2884
2885class RealOsModuleTest(FakeOsModuleTest):
2886    def use_real_fs(self):
2887        return True
2888
2889
2890class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase):
2891    def setUp(self):
2892        super(FakeOsModuleTestCaseInsensitiveFS, self).setUp()
2893        self.check_case_insensitive_fs()
2894        self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK
2895        self.rw = self.os.R_OK | self.os.W_OK
2896
2897    def test_chdir_fails_non_directory(self):
2898        """chdir should raise OSError if the target is not a directory."""
2899        filename = self.make_path("foo", "bar")
2900        self.create_file(filename)
2901        filename1 = self.make_path("Foo", "Bar")
2902        self.assert_raises_os_error(errno.ENOTDIR, self.os.chdir, filename1)
2903
2904    def test_listdir_returns_list(self):
2905        directory_root = self.make_path("xyzzy")
2906        self.os.mkdir(directory_root)
2907        directory = self.os.path.join(directory_root, "bug")
2908        self.os.mkdir(directory)
2909        directory_upper = self.make_path("XYZZY", "BUG")
2910        self.create_file(self.make_path(directory, "foo"))
2911        self.assertEqual(["foo"], self.os.listdir(directory_upper))
2912
2913    def test_listdir_on_symlink(self):
2914        self.skip_if_symlink_not_supported()
2915        directory = self.make_path("xyzzy")
2916        files = ["foo", "bar", "baz"]
2917        for f in files:
2918            self.create_file(self.make_path(directory, f))
2919        self.create_symlink(self.make_path("symlink"), self.make_path("xyzzy"))
2920        files.sort()
2921        self.assertEqual(files, sorted(self.os.listdir(self.make_path("SymLink"))))
2922
2923    def test_fdopen_mode(self):
2924        self.skip_real_fs()
2925        file_path1 = self.make_path("some_file1")
2926        file_path2 = self.make_path("Some_File1")
2927        file_path3 = self.make_path("SOME_file1")
2928        self.create_file(file_path1, contents="contents here1")
2929        self.os.chmod(file_path2, (stat.S_IFREG | 0o666) ^ stat.S_IWRITE)
2930
2931        fake_file1 = self.open(file_path3, "r")
2932        fileno1 = fake_file1.fileno()
2933        self.os.fdopen(fileno1)
2934        self.os.fdopen(fileno1, "r")
2935        if not is_root():
2936            self.assertRaises(OSError, self.os.fdopen, fileno1, "w")
2937        else:
2938            self.os.fdopen(fileno1, "w")
2939
2940    def test_stat(self):
2941        directory = self.make_path("xyzzy")
2942        directory1 = self.make_path("XYZZY")
2943        file_path = self.os.path.join(directory, "plugh")
2944        self.create_file(file_path, contents="ABCDE")
2945        self.assertTrue(stat.S_IFDIR & self.os.stat(directory1)[stat.ST_MODE])
2946        file_path1 = self.os.path.join(directory1, "Plugh")
2947        self.assertTrue(stat.S_IFREG & self.os.stat(file_path1)[stat.ST_MODE])
2948        self.assertTrue(stat.S_IFREG & self.os.stat(file_path1).st_mode)
2949        self.assertEqual(5, self.os.stat(file_path1)[stat.ST_SIZE])
2950
2951    def test_stat_no_follow_symlinks_posix(self):
2952        """Test that stat with follow_symlinks=False behaves like lstat."""
2953        self.check_posix_only()
2954        directory = self.make_path("xyzzy")
2955        base_name = "plugh"
2956        file_contents = "frobozz"
2957        # Just make sure we didn't accidentally make our test data meaningless.
2958        self.assertNotEqual(len(base_name), len(file_contents))
2959        file_path = self.os.path.join(directory, base_name)
2960        link_path = self.os.path.join(directory, "link")
2961        self.create_file(file_path, contents=file_contents)
2962        self.create_symlink(link_path, base_name)
2963        self.assertEqual(
2964            len(file_contents),
2965            self.os.stat(file_path.upper(), follow_symlinks=False)[stat.ST_SIZE],
2966        )
2967        self.assertEqual(
2968            len(base_name),
2969            self.os.stat(link_path.upper(), follow_symlinks=False)[stat.ST_SIZE],
2970        )
2971
2972    def test_lstat_posix(self):
2973        self.check_posix_only()
2974        directory = self.make_path("xyzzy")
2975        base_name = "plugh"
2976        file_contents = "frobozz"
2977        # Just make sure we didn't accidentally make our test data meaningless.
2978        self.assertNotEqual(len(base_name), len(file_contents))
2979        file_path = self.os.path.join(directory, base_name)
2980        link_path = self.os.path.join(directory, "link")
2981        self.create_file(file_path, contents=file_contents)
2982        self.create_symlink(link_path, base_name)
2983        self.assertEqual(
2984            len(file_contents), self.os.lstat(file_path.upper())[stat.ST_SIZE]
2985        )
2986        self.assertEqual(len(base_name), self.os.lstat(link_path.upper())[stat.ST_SIZE])
2987
2988    def test_readlink(self):
2989        self.skip_if_symlink_not_supported()
2990        link_path = self.make_path("foo", "bar", "baz")
2991        target = self.make_path("tarJAY")
2992        self.create_symlink(link_path, target)
2993        self.assert_equal_paths(self.os.readlink(link_path.upper()), target)
2994
2995    def check_readlink_raises_if_path_not_a_link(self):
2996        file_path = self.make_path("foo", "bar", "eleventyone")
2997        self.create_file(file_path)
2998        self.assert_raises_os_error(errno.EINVAL, self.os.readlink, file_path.upper())
2999
3000    def test_readlink_raises_if_path_not_a_link_windows(self):
3001        self.check_windows_only()
3002        self.skip_if_symlink_not_supported()
3003        self.check_readlink_raises_if_path_not_a_link()
3004
3005    def test_readlink_raises_if_path_not_a_link_posix(self):
3006        self.check_posix_only()
3007        self.check_readlink_raises_if_path_not_a_link()
3008
3009    def check_readlink_raises_if_path_has_file(self, error_subtype):
3010        self.create_file(self.make_path("a_file"))
3011        file_path = self.make_path("a_file", "foo")
3012        self.assert_raises_os_error(error_subtype, self.os.readlink, file_path.upper())
3013        file_path = self.make_path("a_file", "foo", "bar")
3014        self.assert_raises_os_error(error_subtype, self.os.readlink, file_path.upper())
3015
3016    def test_readlink_raises_if_path_has_file_windows(self):
3017        self.check_windows_only()
3018        self.skip_if_symlink_not_supported()
3019        self.check_readlink_raises_if_path_has_file(errno.ENOENT)
3020
3021    def test_readlink_raises_if_path_has_file_posix(self):
3022        self.check_posix_only()
3023        self.check_readlink_raises_if_path_has_file(errno.ENOTDIR)
3024
3025    def test_readlink_with_links_in_path(self):
3026        self.skip_if_symlink_not_supported()
3027        self.create_symlink(
3028            self.make_path("meyer", "lemon", "pie"), self.make_path("yum")
3029        )
3030        self.create_symlink(self.make_path("geo", "metro"), self.make_path("Meyer"))
3031        self.assert_equal_paths(
3032            self.make_path("yum"),
3033            self.os.readlink(self.make_path("Geo", "Metro", "Lemon", "Pie")),
3034        )
3035
3036    def test_readlink_with_chained_links_in_path(self):
3037        self.skip_if_symlink_not_supported()
3038        self.create_symlink(
3039            self.make_path("eastern", "european", "wolfhounds", "chase"),
3040            self.make_path("cats"),
3041        )
3042        self.create_symlink(
3043            self.make_path("russian"), self.make_path("Eastern", "European")
3044        )
3045        self.create_symlink(
3046            self.make_path("dogs"), self.make_path("Russian", "Wolfhounds")
3047        )
3048        self.assert_equal_paths(
3049            self.make_path("cats"),
3050            self.os.readlink(self.make_path("DOGS", "Chase")),
3051        )
3052
3053    def check_remove_dir(self, dir_error):
3054        directory = self.make_path("xyzzy")
3055        dir_path = self.os.path.join(directory, "plugh")
3056        self.create_dir(dir_path)
3057        dir_path = dir_path.upper()
3058        self.assertTrue(self.os.path.exists(dir_path.upper()))
3059        self.assert_raises_os_error(dir_error, self.os.remove, dir_path)
3060        self.assertTrue(self.os.path.exists(dir_path))
3061        self.os.chdir(directory)
3062        self.assert_raises_os_error(dir_error, self.os.remove, dir_path)
3063        self.assertTrue(self.os.path.exists(dir_path))
3064        self.assert_raises_os_error(errno.ENOENT, self.os.remove, "/Plugh")
3065
3066    def test_remove_dir_mac_os(self):
3067        self.check_macos_only()
3068        self.check_remove_dir(errno.EPERM)
3069
3070    def test_remove_dir_windows(self):
3071        self.check_windows_only()
3072        self.check_remove_dir(errno.EACCES)
3073
3074    def test_remove_file(self):
3075        directory = self.make_path("zzy")
3076        file_path = self.os.path.join(directory, "plugh")
3077        self.create_file(file_path)
3078        self.assertTrue(self.os.path.exists(file_path.upper()))
3079        self.os.remove(file_path.upper())
3080        self.assertFalse(self.os.path.exists(file_path))
3081
3082    def test_remove_file_no_directory(self):
3083        directory = self.make_path("zzy")
3084        file_name = "plugh"
3085        file_path = self.os.path.join(directory, file_name)
3086        self.create_file(file_path)
3087        self.assertTrue(self.os.path.exists(file_path))
3088        self.os.chdir(directory.upper())
3089        self.os.remove(file_name.upper())
3090        self.assertFalse(self.os.path.exists(file_path))
3091
3092    def test_remove_open_file_fails_under_windows(self):
3093        self.check_windows_only()
3094        path = self.make_path("foo", "bar")
3095        self.create_file(path)
3096        with self.open(path, "r"):
3097            self.assert_raises_os_error(errno.EACCES, self.os.remove, path.upper())
3098        self.assertTrue(self.os.path.exists(path))
3099
3100    def test_remove_open_file_possible_under_posix(self):
3101        self.check_posix_only()
3102        path = self.make_path("foo", "bar")
3103        self.create_file(path)
3104        self.open(path, "r")
3105        self.os.remove(path.upper())
3106        self.assertFalse(self.os.path.exists(path))
3107
3108    def test_remove_file_relative_path(self):
3109        self.skip_real_fs()
3110        original_dir = self.os.getcwd()
3111        directory = self.make_path("zzy")
3112        subdirectory = self.os.path.join(directory, "zzy")
3113        file_name = "plugh"
3114        file_path = self.os.path.join(directory, file_name)
3115        file_path_relative = self.os.path.join("..", file_name)
3116        self.create_file(file_path.upper())
3117        self.assertTrue(self.os.path.exists(file_path))
3118        self.create_dir(subdirectory)
3119        self.assertTrue(self.os.path.exists(subdirectory))
3120        self.os.chdir(subdirectory.upper())
3121        self.os.remove(file_path_relative.upper())
3122        self.assertFalse(self.os.path.exists(file_path_relative))
3123        self.os.chdir(original_dir.upper())
3124        self.assertFalse(self.os.path.exists(file_path))
3125
3126    def check_remove_dir_raises_error(self, dir_error):
3127        directory = self.make_path("zzy")
3128        self.create_dir(directory)
3129        self.assert_raises_os_error(dir_error, self.os.remove, directory.upper())
3130
3131    def test_remove_dir_raises_error_mac_os(self):
3132        self.check_macos_only()
3133        self.check_remove_dir_raises_error(errno.EPERM)
3134
3135    def test_remove_dir_raises_error_windows(self):
3136        self.check_windows_only()
3137        self.check_remove_dir_raises_error(errno.EACCES)
3138
3139    def test_remove_symlink_to_dir(self):
3140        self.skip_if_symlink_not_supported()
3141        directory = self.make_path("zzy")
3142        link = self.make_path("link_to_dir")
3143        self.create_dir(directory)
3144        self.os.symlink(directory, link)
3145        self.assertTrue(self.os.path.exists(directory))
3146        self.assertTrue(self.os.path.exists(link))
3147        self.os.remove(link.upper())
3148        self.assertTrue(self.os.path.exists(directory))
3149        self.assertFalse(self.os.path.exists(link))
3150
3151    def test_rename_dir_to_symlink_posix(self):
3152        self.check_posix_only()
3153        link_path = self.make_path("link")
3154        dir_path = self.make_path("dir")
3155        link_target = self.os.path.join(dir_path, "link_target")
3156        self.create_dir(dir_path)
3157        self.os.symlink(link_target.upper(), link_path.upper())
3158        self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, dir_path, link_path)
3159
3160    def test_rename_dir_to_symlink_windows(self):
3161        self.check_windows_only()
3162        self.skip_if_symlink_not_supported()
3163        link_path = self.make_path("link")
3164        dir_path = self.make_path("dir")
3165        link_target = self.os.path.join(dir_path, "link_target")
3166        self.create_dir(dir_path)
3167        self.os.symlink(link_target.upper(), link_path.upper())
3168        self.assert_raises_os_error(errno.EEXIST, self.os.rename, dir_path, link_path)
3169
3170    def test_rename_dir_to_existing_dir(self):
3171        # Regression test for #317
3172        self.check_posix_only()
3173        dest_dir_path = self.make_path("Dest")
3174        # seems to behave differently under different MacOS versions
3175        self.skip_real_fs()
3176        new_dest_dir_path = self.make_path("dest")
3177        self.os.mkdir(dest_dir_path)
3178        source_dir_path = self.make_path("src")
3179        self.os.mkdir(source_dir_path)
3180        self.os.rename(source_dir_path, new_dest_dir_path)
3181        self.assertEqual(["dest"], self.os.listdir(self.base_path))
3182
3183    def test_rename_file_to_symlink(self):
3184        self.check_posix_only()
3185        link_path = self.make_path("file_link")
3186        file_path = self.make_path("file")
3187        self.os.symlink(file_path, link_path)
3188        self.create_file(file_path)
3189        self.os.rename(file_path.upper(), link_path)
3190        self.assertFalse(self.os.path.exists(file_path))
3191        self.assertTrue(self.os.path.exists(link_path.upper()))
3192        self.assertTrue(self.os.path.isfile(link_path.upper()))
3193
3194    def test_rename_symlink_to_symlink(self):
3195        self.check_posix_only()
3196        base_path = self.make_path("foo", "bar")
3197        self.create_dir(base_path)
3198        link_path1 = self.os.path.join(base_path, "link1")
3199        link_path2 = self.os.path.join(base_path, "link2")
3200        self.os.symlink(base_path.upper(), link_path1)
3201        self.os.symlink(base_path, link_path2)
3202        self.os.rename(link_path1.upper(), link_path2.upper())
3203        self.assertFalse(self.os.path.exists(link_path1))
3204        self.assertTrue(self.os.path.exists(link_path2))
3205
3206    def test_rename_symlink_to_symlink_for_parent_raises(self):
3207        self.check_posix_only()
3208        dir_link = self.make_path("dir_link")
3209        dir_path = self.make_path("dir")
3210        dir_in_dir_path = self.os.path.join(dir_link, "inner_dir")
3211        self.create_dir(dir_path)
3212        self.os.symlink(dir_path.upper(), dir_link)
3213        self.create_dir(dir_in_dir_path)
3214        self.assert_raises_os_error(
3215            errno.EINVAL, self.os.rename, dir_path, dir_in_dir_path.upper()
3216        )
3217
3218    def test_rename_directory_to_linked_dir(self):
3219        # Regression test for #314
3220        self.skip_if_symlink_not_supported()
3221        link_path = self.make_path("link")
3222        self.os.symlink(self.base_path, link_path)
3223        link_subdir = self.os.path.join(link_path, "dir")
3224        dir_path = self.make_path("Dir")
3225        self.os.mkdir(dir_path)
3226        self.os.rename(dir_path, link_subdir)
3227        self.assertEqual(["dir", "link"], sorted(self.os.listdir(self.base_path)))
3228
3229    def test_recursive_rename_raises(self):
3230        self.check_posix_only()
3231        base_path = self.make_path("foo", "bar")
3232        self.create_dir(base_path)
3233        new_path = self.os.path.join(base_path, "new_dir")
3234        self.assert_raises_os_error(
3235            errno.EINVAL, self.os.rename, base_path.upper(), new_path
3236        )
3237
3238    def test_rename_with_target_parent_file_raises_posix(self):
3239        self.check_posix_only()
3240        file_path = self.make_path("foo", "baz")
3241        self.create_file(file_path)
3242        self.assert_raises_os_error(
3243            errno.ENOTDIR,
3244            self.os.rename,
3245            file_path,
3246            file_path.upper() + "/new",
3247        )
3248
3249    def test_rename_with_target_parent_file_raises_windows(self):
3250        self.check_windows_only()
3251        file_path = self.make_path("foo", "baz")
3252        self.create_file(file_path)
3253        self.assert_raises_os_error(
3254            errno.EACCES,
3255            self.os.rename,
3256            file_path,
3257            self.os.path.join(file_path.upper(), "new"),
3258        )
3259
3260    def test_rename_looping_symlink(self):
3261        # Regression test for #315
3262        self.skip_if_symlink_not_supported()
3263        path_lower = self.make_path("baz")
3264        path_upper = self.make_path("BAZ")
3265        self.os.symlink(path_lower, path_upper)
3266        self.os.rename(path_upper, path_lower)
3267        self.assertEqual(["baz"], self.os.listdir(self.base_path))
3268
3269    def test_rename_symlink_to_source(self):
3270        self.check_posix_only()
3271        base_path = self.make_path("foo")
3272        link_path = self.os.path.join(base_path, "slink")
3273        file_path = self.os.path.join(base_path, "file")
3274        self.create_file(file_path)
3275        self.os.symlink(file_path, link_path)
3276        self.os.rename(link_path.upper(), file_path.upper())
3277        self.assertFalse(self.os.path.exists(file_path))
3278
3279    def test_rename_symlink_to_dir_raises(self):
3280        self.check_posix_only()
3281        base_path = self.make_path("foo", "bar")
3282        link_path = self.os.path.join(base_path, "dir_link")
3283        dir_path = self.os.path.join(base_path, "dir")
3284        self.create_dir(dir_path)
3285        self.os.symlink(dir_path, link_path.upper())
3286        self.assert_raises_os_error(
3287            errno.EISDIR, self.os.rename, link_path, dir_path.upper()
3288        )
3289
3290    def test_rename_broken_symlink(self):
3291        self.check_posix_only()
3292        base_path = self.make_path("foo")
3293        self.create_dir(base_path)
3294        link_path = self.os.path.join(base_path, "slink")
3295        file_path = self.os.path.join(base_path, "file")
3296        self.os.symlink(file_path.upper(), link_path)
3297        self.os.rename(link_path.upper(), file_path)
3298        self.assertFalse(self.os.path.exists(file_path))
3299        self.assertTrue(self.os.path.lexists(file_path))
3300        self.assertFalse(self.os.path.exists(link_path))
3301
3302    def test_change_case_in_case_insensitive_file_system(self):
3303        """Can use `rename()` to change filename case in a case-insensitive
3304        file system."""
3305        old_file_path = self.make_path("fileName")
3306        new_file_path = self.make_path("FileNAME")
3307        self.create_file(old_file_path, contents="test contents")
3308        self.assertEqual(["fileName"], self.os.listdir(self.base_path))
3309        self.os.rename(old_file_path, new_file_path)
3310        self.assertTrue(self.os.path.exists(old_file_path))
3311        self.assertTrue(self.os.path.exists(new_file_path))
3312        self.assertEqual(["FileNAME"], self.os.listdir(self.base_path))
3313
3314    def test_rename_symlink_with_changed_case(self):
3315        # Regression test for #313
3316        self.skip_if_symlink_not_supported()
3317        link_path = self.make_path("link")
3318        self.os.symlink(self.base_path, link_path)
3319        link_path = self.os.path.join(link_path, "link")
3320        link_path_upper = self.make_path("link", "LINK")
3321        self.os.rename(link_path_upper, link_path)
3322
3323    def test_rename_directory(self):
3324        """Can rename a directory to an unused name."""
3325        for old_path, new_path in [("wxyyw", "xyzzy"), ("abccb", "cdeed")]:
3326            old_path = self.make_path(old_path)
3327            new_path = self.make_path(new_path)
3328            self.create_file(self.os.path.join(old_path, "plugh"), contents="test")
3329            self.assertTrue(self.os.path.exists(old_path))
3330            self.assertFalse(self.os.path.exists(new_path))
3331            self.os.rename(old_path.upper(), new_path.upper())
3332            self.assertFalse(self.os.path.exists(old_path))
3333            self.assertTrue(self.os.path.exists(new_path))
3334            self.check_contents(self.os.path.join(new_path, "plugh"), "test")
3335            if not self.use_real_fs():
3336                self.assertEqual(3, self.filesystem.get_object(new_path).st_nlink)
3337
3338    def check_rename_directory_to_existing_file_raises(self, error_nr):
3339        dir_path = self.make_path("dir")
3340        file_path = self.make_path("file")
3341        self.create_dir(dir_path)
3342        self.create_file(file_path)
3343        self.assert_raises_os_error(
3344            error_nr, self.os.rename, dir_path, file_path.upper()
3345        )
3346
3347    def test_rename_directory_to_existing_file_raises_posix(self):
3348        self.check_posix_only()
3349        self.check_rename_directory_to_existing_file_raises(errno.ENOTDIR)
3350
3351    def test_rename_directory_to_existing_file_raises_windows(self):
3352        self.check_windows_only()
3353        self.check_rename_directory_to_existing_file_raises(errno.EEXIST)
3354
3355    def test_rename_to_existing_directory_should_raise_under_windows(self):
3356        """Renaming to an existing directory raises OSError under Windows."""
3357        self.check_windows_only()
3358        old_path = self.make_path("foo", "bar")
3359        new_path = self.make_path("foo", "baz")
3360        self.create_dir(old_path)
3361        self.create_dir(new_path)
3362        self.assert_raises_os_error(
3363            errno.EEXIST, self.os.rename, old_path.upper(), new_path.upper()
3364        )
3365
3366    def test_rename_to_a_hardlink_of_same_file_should_do_nothing(self):
3367        self.skip_real_fs_failure(skip_posix=False)
3368        self.skip_if_symlink_not_supported()
3369        file_path = self.make_path("dir", "file")
3370        self.create_file(file_path)
3371        link_path = self.make_path("link")
3372        self.os.link(file_path.upper(), link_path)
3373        self.os.rename(file_path, link_path.upper())
3374        self.assertTrue(self.os.path.exists(file_path))
3375        self.assertTrue(self.os.path.exists(link_path))
3376
3377    def test_rename_with_incorrect_source_case(self):
3378        # Regression test for #308
3379        base_path = self.make_path("foo")
3380        path0 = self.os.path.join(base_path, "bar")
3381        path1 = self.os.path.join(base_path, "Bar")
3382        self.create_dir(path0)
3383        self.os.rename(path1, path0)
3384        self.assertTrue(self.os.path.exists(path0))
3385
3386    def test_rename_symlink_to_other_case_does_nothing_in_mac_os(self):
3387        # Regression test for #318
3388        self.check_macos_only()
3389        path0 = self.make_path("beta")
3390        self.os.symlink(self.base_path, path0)
3391        path0 = self.make_path("beta", "Beta")
3392        path1 = self.make_path("Beta")
3393        self.os.rename(path0, path1)
3394        self.assertEqual(["beta"], sorted(self.os.listdir(path0)))
3395
3396    def test_rename_symlink_to_other_case_works_in_windows(self):
3397        self.check_windows_only()
3398        self.skip_if_symlink_not_supported()
3399        path0 = self.make_path("beta")
3400        self.os.symlink(self.base_path, path0)
3401        path0 = self.make_path("beta", "Beta")
3402        path1 = self.make_path("Beta")
3403        self.os.rename(path0, path1)
3404        self.assertEqual(["Beta"], sorted(self.os.listdir(path0)))
3405
3406    def test_renames_creates_missing_dirs(self):
3407        old_path = self.make_path("foo.txt")
3408        self.create_file(old_path)
3409        new_path = self.make_path("new", "dir", "bar.txt")
3410        self.os.renames(old_path, new_path)
3411        self.assertTrue(self.os.path.exists(new_path))
3412        self.assertFalse(self.os.path.exists(old_path))
3413
3414    def test_renames_removes_empty_dirs(self):
3415        old_base_path = self.make_path("old")
3416        old_path = self.make_path("old", "dir1", "dir2", "foo.txt")
3417        other_file = self.os.path.join(old_base_path, "foo.png")
3418        self.create_file(old_path)
3419        self.create_file(other_file)
3420        new_path = self.make_path("new", "bar.txt")
3421        self.os.renames(old_path, new_path)
3422        self.assertTrue(self.os.path.exists(new_path))
3423        self.assertFalse(self.os.path.exists(old_path))
3424        self.assertTrue(self.os.path.exists(old_base_path))
3425        removed_path = self.os.path.join(old_base_path, "dir1")
3426        self.assertFalse(self.os.path.exists(removed_path))
3427
3428    def test_stat_with_mixed_case(self):
3429        # Regression test for #310
3430        self.skip_if_symlink_not_supported()
3431        base_path = self.make_path("foo")
3432        path = self.os.path.join(base_path, "bar")
3433        self.create_dir(path)
3434        path = self.os.path.join(path, "Bar")
3435        self.os.symlink(base_path, path)
3436        path = self.os.path.join(path, "Bar")
3437        # used to raise
3438        self.os.stat(path)
3439
3440    def test_hardlink_works_with_symlink(self):
3441        self.skip_if_symlink_not_supported()
3442        base_path = self.make_path("foo")
3443        self.create_dir(base_path)
3444        symlink_path = self.os.path.join(base_path, "slink")
3445        self.os.symlink(base_path.upper(), symlink_path)
3446        file_path = self.os.path.join(base_path, "slink", "beta")
3447        self.create_file(file_path)
3448        link_path = self.os.path.join(base_path, "Slink", "gamma")
3449        self.os.link(file_path, link_path)
3450        self.assertTrue(self.os.path.exists(link_path))
3451
3452    def test_replace_existing_directory_should_raise_under_windows(self):
3453        """Renaming to an existing directory raises OSError under Windows."""
3454        self.check_windows_only()
3455        old_path = self.make_path("foo", "bar")
3456        new_path = self.make_path("foo", "baz")
3457        self.create_dir(old_path)
3458        self.create_dir(new_path)
3459        self.assert_raises_os_error(
3460            errno.EACCES, self.os.replace, old_path, new_path.upper()
3461        )
3462
3463    def test_rename_to_existing_directory_under_posix(self):
3464        """Renaming to an existing directory changes the existing directory
3465        under Posix."""
3466        self.check_posix_only()
3467        old_path = self.make_path("foo", "bar")
3468        new_path = self.make_path("xyzzy")
3469        self.create_dir(self.os.path.join(old_path, "sub"))
3470        self.create_dir(new_path)
3471        self.os.rename(old_path.upper(), new_path.upper())
3472        self.assertTrue(self.os.path.exists(self.os.path.join(new_path, "sub")))
3473        self.assertFalse(self.os.path.exists(old_path))
3474
3475    def test_rename_file_to_existing_directory_raises_under_posix(self):
3476        self.check_posix_only()
3477        file_path = self.make_path("foo", "bar", "baz")
3478        new_path = self.make_path("xyzzy")
3479        self.create_file(file_path)
3480        self.create_dir(new_path)
3481        self.assert_raises_os_error(
3482            errno.EISDIR, self.os.rename, file_path.upper(), new_path.upper()
3483        )
3484
3485    def test_rename_to_existent_file_posix(self):
3486        """Can rename a file to a used name under Unix."""
3487        self.check_posix_only()
3488        directory = self.make_path("xyzzy")
3489        old_file_path = self.os.path.join(directory, "plugh_old")
3490        new_file_path = self.os.path.join(directory, "plugh_new")
3491        self.create_file(old_file_path, contents="test contents 1")
3492        self.create_file(new_file_path, contents="test contents 2")
3493        self.assertTrue(self.os.path.exists(old_file_path))
3494        self.assertTrue(self.os.path.exists(new_file_path))
3495        self.os.rename(old_file_path.upper(), new_file_path.upper())
3496        self.assertFalse(self.os.path.exists(old_file_path))
3497        self.assertTrue(self.os.path.exists(new_file_path))
3498        self.check_contents(new_file_path, "test contents 1")
3499
3500    def test_rename_to_existent_file_windows(self):
3501        """Renaming a file to a used name raises OSError under Windows."""
3502        self.check_windows_only()
3503        directory = self.make_path("xyzzy")
3504        old_file_path = self.os.path.join(directory, "plugh_old")
3505        new_file_path = self.os.path.join(directory, "plugh_new")
3506        self.create_file(old_file_path, contents="test contents 1")
3507        self.create_file(new_file_path, contents="test contents 2")
3508        self.assertTrue(self.os.path.exists(old_file_path))
3509        self.assertTrue(self.os.path.exists(new_file_path))
3510        self.assert_raises_os_error(
3511            errno.EEXIST,
3512            self.os.rename,
3513            old_file_path.upper(),
3514            new_file_path.upper(),
3515        )
3516
3517    def test_replace_to_existent_file(self):
3518        """Replaces an existing file (does not work with `rename()` under
3519        Windows)."""
3520        directory = self.make_path("xyzzy")
3521        old_file_path = self.os.path.join(directory, "plugh_old")
3522        new_file_path = self.os.path.join(directory, "plugh_new")
3523        self.create_file(old_file_path, contents="test contents 1")
3524        self.create_file(new_file_path, contents="test contents 2")
3525        self.assertTrue(self.os.path.exists(old_file_path))
3526        self.assertTrue(self.os.path.exists(new_file_path))
3527        self.os.replace(old_file_path.upper(), new_file_path.upper())
3528        self.assertFalse(self.os.path.exists(old_file_path))
3529        self.assertTrue(self.os.path.exists(new_file_path))
3530        self.check_contents(new_file_path, "test contents 1")
3531
3532    def test_rename_to_nonexistent_dir(self):
3533        """Can rename a file to a name in a nonexistent dir."""
3534        directory = self.make_path("xyzzy")
3535        old_file_path = self.os.path.join(directory, "plugh_old")
3536        new_file_path = self.os.path.join(directory, "no_such_path", "plugh_new")
3537        self.create_file(old_file_path, contents="test contents")
3538        self.assertTrue(self.os.path.exists(old_file_path))
3539        self.assertFalse(self.os.path.exists(new_file_path))
3540        self.assert_raises_os_error(
3541            errno.ENOENT,
3542            self.os.rename,
3543            old_file_path.upper(),
3544            new_file_path.upper(),
3545        )
3546        self.assertTrue(self.os.path.exists(old_file_path))
3547        self.assertFalse(self.os.path.exists(new_file_path))
3548        self.check_contents(old_file_path, "test contents")
3549
3550    def check_rename_case_only_with_symlink_parent(self):
3551        # Regression test for #319
3552        self.os.symlink(self.base_path, self.make_path("link"))
3553        dir_upper = self.make_path("link", "Alpha")
3554        self.os.mkdir(dir_upper)
3555        dir_lower = self.make_path("alpha")
3556        self.os.rename(dir_upper, dir_lower)
3557        self.assertEqual(["alpha", "link"], sorted(self.os.listdir(self.base_path)))
3558
3559    def test_rename_case_only_with_symlink_parent_windows(self):
3560        self.check_windows_only()
3561        self.skip_if_symlink_not_supported()
3562        self.check_rename_case_only_with_symlink_parent()
3563
3564    def test_rename_case_only_with_symlink_parent_macos(self):
3565        self.check_macos_only()
3566        self.check_rename_case_only_with_symlink_parent()
3567
3568    def test_rename_dir(self):
3569        """Test a rename of a directory."""
3570        directory = self.make_path("xyzzy")
3571        before_dir = self.os.path.join(directory, "before")
3572        before_file = self.os.path.join(directory, "before", "file")
3573        after_dir = self.os.path.join(directory, "after")
3574        after_file = self.os.path.join(directory, "after", "file")
3575        self.create_dir(before_dir)
3576        self.create_file(before_file, contents="payload")
3577        self.assertTrue(self.os.path.exists(before_dir.upper()))
3578        self.assertTrue(self.os.path.exists(before_file.upper()))
3579        self.assertFalse(self.os.path.exists(after_dir.upper()))
3580        self.assertFalse(self.os.path.exists(after_file.upper()))
3581        self.os.rename(before_dir.upper(), after_dir)
3582        self.assertFalse(self.os.path.exists(before_dir.upper()))
3583        self.assertFalse(self.os.path.exists(before_file.upper()))
3584        self.assertTrue(self.os.path.exists(after_dir.upper()))
3585        self.assertTrue(self.os.path.exists(after_file.upper()))
3586        self.check_contents(after_file, "payload")
3587
3588    def test_rename_same_filenames(self):
3589        """Test renaming when old and new names are the same."""
3590        directory = self.make_path("xyzzy")
3591        file_contents = "Spam eggs"
3592        file_path = self.os.path.join(directory, "eggs")
3593        self.create_file(file_path, contents=file_contents)
3594        self.os.rename(file_path, file_path.upper())
3595        self.check_contents(file_path, file_contents)
3596
3597    def test_rmdir(self):
3598        """Can remove a directory."""
3599        directory = self.make_path("xyzzy")
3600        sub_dir = self.make_path("xyzzy", "abccd")
3601        other_dir = self.make_path("xyzzy", "cdeed")
3602        self.create_dir(directory)
3603        self.assertTrue(self.os.path.exists(directory))
3604        self.os.rmdir(directory)
3605        self.assertFalse(self.os.path.exists(directory))
3606        self.create_dir(sub_dir)
3607        self.create_dir(other_dir)
3608        self.os.chdir(sub_dir)
3609        self.os.rmdir("../CDEED")
3610        self.assertFalse(self.os.path.exists(other_dir))
3611        self.os.chdir("..")
3612        self.os.rmdir("AbcCd")
3613        self.assertFalse(self.os.path.exists(sub_dir))
3614
3615    def test_rmdir_via_symlink(self):
3616        self.check_windows_only()
3617        self.skip_if_symlink_not_supported()
3618        base_path = self.make_path("foo", "bar")
3619        dir_path = self.os.path.join(base_path, "alpha")
3620        self.create_dir(dir_path)
3621        link_path = self.os.path.join(base_path, "beta")
3622        self.os.symlink(base_path, link_path)
3623        self.os.rmdir(link_path + "/Alpha")
3624        self.assertFalse(self.os.path.exists(dir_path))
3625
3626    def test_remove_dirs_with_non_top_symlink_succeeds(self):
3627        self.check_posix_only()
3628        dir_path = self.make_path("dir")
3629        dir_link = self.make_path("dir_link")
3630        self.create_dir(dir_path)
3631        self.os.symlink(dir_path, dir_link)
3632        dir_in_dir = self.os.path.join(dir_link, "dir2")
3633        self.create_dir(dir_in_dir)
3634        self.os.removedirs(dir_in_dir.upper())
3635        self.assertFalse(self.os.path.exists(dir_in_dir))
3636        # ensure that the symlink is not removed
3637        self.assertTrue(self.os.path.exists(dir_link))
3638
3639    def test_mkdir_raises_on_symlink_in_posix(self):
3640        self.check_posix_only()
3641        base_path = self.make_path("foo", "bar")
3642        link_path = self.os.path.join(base_path, "link_to_dir")
3643        dir_path = self.os.path.join(base_path, "dir")
3644        self.create_dir(dir_path)
3645        self.os.symlink(dir_path.upper(), link_path.upper())
3646        self.assert_raises_os_error(errno.ENOTDIR, self.os.rmdir, link_path)
3647
3648    def test_mkdir_removes_symlink_in_windows(self):
3649        self.check_windows_only()
3650        self.skip_if_symlink_not_supported()
3651        base_path = self.make_path("foo", "bar")
3652        link_path = self.os.path.join(base_path, "link_to_dir")
3653        dir_path = self.os.path.join(base_path, "dir")
3654        self.create_dir(dir_path)
3655        self.os.symlink(dir_path.upper(), link_path.upper())
3656        self.os.rmdir(link_path)
3657        self.assertFalse(self.os.path.exists(link_path))
3658        self.assertTrue(self.os.path.exists(dir_path))
3659
3660    def test_mkdir_raises_if_directory_exists(self):
3661        """mkdir raises exception if directory already exists."""
3662        directory = self.make_path("xyzzy")
3663        self.create_dir(directory)
3664        self.assertTrue(self.os.path.exists(directory))
3665        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory.upper())
3666
3667    def test_mkdir_raises_if_file_exists(self):
3668        """mkdir raises exception if name already exists as a file."""
3669        directory = self.make_path("xyzzy")
3670        file_path = self.os.path.join(directory, "plugh")
3671        self.create_file(file_path)
3672        self.assertTrue(self.os.path.exists(file_path))
3673        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, file_path.upper())
3674
3675    def test_mkdir_raises_if_symlink_exists(self):
3676        # Regression test for #309
3677        self.skip_if_symlink_not_supported()
3678        path1 = self.make_path("baz")
3679        self.os.symlink(path1, path1)
3680        path2 = self.make_path("Baz")
3681        self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, path2)
3682
3683    def check_mkdir_raises_if_parent_is_file(self, error_type):
3684        """mkdir raises exception if name already exists as a file."""
3685        directory = self.make_path("xyzzy")
3686        file_path = self.os.path.join(directory, "plugh")
3687        self.create_file(file_path)
3688        self.assert_raises_os_error(
3689            error_type,
3690            self.os.mkdir,
3691            self.os.path.join(file_path.upper(), "ff"),
3692        )
3693
3694    def test_mkdir_raises_if_parent_is_file_posix(self):
3695        self.check_posix_only()
3696        self.check_mkdir_raises_if_parent_is_file(errno.ENOTDIR)
3697
3698    def test_mkdir_raises_if_parent_is_file_windows(self):
3699        self.check_windows_only()
3700        self.check_mkdir_raises_if_parent_is_file(errno.ENOENT)
3701
3702    def test_makedirs(self):
3703        """makedirs can create a directory even if parent does not exist."""
3704        parent = self.make_path("xyzzy")
3705        directory = self.os.path.join(parent, "foo")
3706        self.assertFalse(self.os.path.exists(parent))
3707        self.os.makedirs(directory.upper())
3708        self.assertTrue(self.os.path.exists(directory))
3709
3710    def check_makedirs_raises_if_parent_is_file(self, error_type):
3711        """makedirs raises exception if a parent component exists as a file."""
3712        file_path = self.make_path("xyzzy")
3713        directory = self.os.path.join(file_path, "plugh")
3714        self.create_file(file_path)
3715        self.assertTrue(self.os.path.exists(file_path))
3716        self.assert_raises_os_error(error_type, self.os.makedirs, directory.upper())
3717
3718    def test_makedirs_raises_if_parent_is_file_posix(self):
3719        self.check_posix_only()
3720        self.check_makedirs_raises_if_parent_is_file(errno.ENOTDIR)
3721
3722    def test_makedirs_raises_if_parent_is_file_windows(self):
3723        self.check_windows_only()
3724        self.check_makedirs_raises_if_parent_is_file(errno.ENOENT)
3725
3726    def test_makedirs_raises_if_parent_is_broken_link(self):
3727        self.check_posix_only()
3728        link_path = self.make_path("broken_link")
3729        self.os.symlink(self.make_path("bogus"), link_path)
3730        self.assert_raises_os_error(
3731            errno.ENOENT,
3732            self.os.makedirs,
3733            self.os.path.join(link_path.upper(), "newdir"),
3734        )
3735
3736    def test_makedirs_exist_ok(self):
3737        """makedirs uses the exist_ok argument"""
3738        directory = self.make_path("xyzzy", "foo")
3739        self.create_dir(directory)
3740        self.assertTrue(self.os.path.exists(directory))
3741
3742        self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, directory.upper())
3743        self.os.makedirs(directory.upper(), exist_ok=True)
3744        self.assertTrue(self.os.path.exists(directory))
3745
3746    # test fsync and fdatasync
3747    def test_fsync_pass(self):
3748        test_file_path = self.make_path("test_file")
3749        self.create_file(test_file_path, contents="dummy file contents")
3750        test_file = self.open(test_file_path.upper(), "r+")
3751        test_fd = test_file.fileno()
3752        # Test that this doesn't raise anything
3753        self.os.fsync(test_fd)
3754        # And just for sanity, double-check that this still raises
3755        self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd + 10)
3756        test_file.close()
3757
3758    def test_chmod(self):
3759        # set up
3760        self.check_posix_only()
3761        self.skip_real_fs()
3762        path = self.make_path("some_file")
3763        self.createTestFile(path)
3764        # actual tests
3765        self.os.chmod(path.upper(), 0o6543)
3766        st = self.os.stat(path)
3767        self.assert_mode_equal(0o6543, st.st_mode)
3768        self.assertTrue(st.st_mode & stat.S_IFREG)
3769        self.assertFalse(st.st_mode & stat.S_IFDIR)
3770
3771    def test_symlink(self):
3772        self.skip_if_symlink_not_supported()
3773        file_path = self.make_path("foo", "bar", "baz")
3774        self.create_dir(self.make_path("foo", "bar"))
3775        self.os.symlink("bogus", file_path.upper())
3776        self.assertTrue(self.os.path.lexists(file_path))
3777        self.assertFalse(self.os.path.exists(file_path))
3778        self.create_file(self.make_path("Foo", "Bar", "Bogus"))
3779        self.assertTrue(self.os.path.lexists(file_path))
3780        self.assertTrue(self.os.path.exists(file_path))
3781
3782    # hard link related tests
3783    def test_link_delete(self):
3784        self.skip_if_symlink_not_supported()
3785
3786        file1_path = self.make_path("test_file1")
3787        file2_path = self.make_path("test_file2")
3788        contents1 = "abcdef"
3789        # Create file
3790        self.create_file(file1_path, contents=contents1)
3791        # link to second file
3792        self.os.link(file1_path.upper(), file2_path)
3793        # delete first file
3794        self.os.unlink(file1_path)
3795        # assert that second file exists, and its contents are the same
3796        self.assertTrue(self.os.path.exists(file2_path))
3797        with self.open(file2_path.upper()) as f:
3798            self.assertEqual(f.read(), contents1)
3799
3800    def test_link_is_existing_file(self):
3801        self.skip_if_symlink_not_supported()
3802        file_path = self.make_path("foo", "bar")
3803        self.create_file(file_path)
3804        self.assert_raises_os_error(
3805            errno.EEXIST, self.os.link, file_path.upper(), file_path.upper()
3806        )
3807
3808    def test_link_is_broken_symlink(self):
3809        # Regression test for #311
3810        self.skip_if_symlink_not_supported()
3811        self.check_case_insensitive_fs()
3812        file_path = self.make_path("baz")
3813        self.create_file(file_path)
3814        path_lower = self.make_path("foo")
3815        self.os.symlink(path_lower, path_lower)
3816        path_upper = self.make_path("Foo")
3817        self.assert_raises_os_error(errno.EEXIST, self.os.link, file_path, path_upper)
3818
3819    def test_link_with_changed_case(self):
3820        # Regression test for #312
3821        self.skip_if_symlink_not_supported()
3822        self.check_case_insensitive_fs()
3823        link_path = self.make_path("link")
3824        self.os.symlink(self.base_path, link_path)
3825        link_path = self.os.path.join(link_path, "Link")
3826        self.assertTrue(self.os.lstat(link_path))
3827
3828
3829class RealOsModuleTestCaseInsensitiveFS(FakeOsModuleTestCaseInsensitiveFS):
3830    def use_real_fs(self):
3831        return True
3832
3833
3834class FakeOsModuleTimeTest(FakeOsModuleTestBase):
3835    def test_chmod_st_ctime(self):
3836        with self.mock_time(start=200):
3837            file_path = "some_file"
3838            self.filesystem.create_file(file_path)
3839            self.assertTrue(self.os.path.exists(file_path))
3840
3841            st = self.os.stat(file_path)
3842            self.assertEqual(200, st.st_ctime)
3843            # tests
3844            self.os.chmod(file_path, 0o765)
3845            st = self.os.stat(file_path)
3846            self.assertEqual(220, st.st_ctime)
3847
3848    def test_utime_sets_current_time_if_args_is_none(self):
3849        path = self.make_path("some_file")
3850        self.createTestFile(path)
3851
3852        with self.mock_time(start=200):
3853            self.os.utime(path, times=None)
3854            st = self.os.stat(path)
3855            self.assertEqual(200, st.st_atime)
3856            self.assertEqual(200, st.st_mtime)
3857
3858    def test_utime_sets_specified_time(self):
3859        # set up
3860        path = self.make_path("some_file")
3861        self.createTestFile(path)
3862        self.os.stat(path)
3863        # actual tests
3864        self.os.utime(path, times=(1, 2))
3865        st = self.os.stat(path)
3866        self.assertEqual(1, st.st_atime)
3867        self.assertEqual(2, st.st_mtime)
3868
3869    def test_utime_dir(self):
3870        # set up
3871        path = "/some_dir"
3872        self.createTestDirectory(path)
3873        # actual tests
3874        self.os.utime(path, times=(1.0, 2.0))
3875        st = self.os.stat(path)
3876        self.assertEqual(1.0, st.st_atime)
3877        self.assertEqual(2.0, st.st_mtime)
3878
3879    def test_utime_follow_symlinks(self):
3880        path = self.make_path("some_file")
3881        self.createTestFile(path)
3882        link_path = "/link_to_some_file"
3883        self.filesystem.create_symlink(link_path, path)
3884
3885        self.os.utime(link_path, times=(1, 2))
3886        st = self.os.stat(link_path)
3887        self.assertEqual(1, st.st_atime)
3888        self.assertEqual(2, st.st_mtime)
3889
3890    def test_utime_no_follow_symlinks(self):
3891        path = self.make_path("some_file")
3892        self.createTestFile(path)
3893        link_path = "/link_to_some_file"
3894        self.filesystem.create_symlink(link_path, path)
3895
3896        self.os.utime(link_path, times=(1, 2), follow_symlinks=False)
3897        st = self.os.stat(link_path)
3898        self.assertNotEqual(1, st.st_atime)
3899        self.assertNotEqual(2, st.st_mtime)
3900        st = self.os.stat(link_path, follow_symlinks=False)
3901        self.assertEqual(1, st.st_atime)
3902        self.assertEqual(2, st.st_mtime)
3903
3904    def test_utime_non_existent(self):
3905        path = "/non/existent/file"
3906        self.assertFalse(self.os.path.exists(path))
3907        self.assert_raises_os_error(errno.ENOENT, self.os.utime, path, (1, 2))
3908
3909    def test_utime_invalid_times_arg_raises(self):
3910        path = "/some_dir"
3911        self.createTestDirectory(path)
3912
3913        # the error message differs with different Python versions
3914        # we don't expect the same message here
3915        self.assertRaises(TypeError, self.os.utime, path, (1, 2, 3))
3916        self.assertRaises(TypeError, self.os.utime, path, (1, "str"))
3917
3918    def test_utime_sets_specified_time_in_ns(self):
3919        # set up
3920        path = self.make_path("some_file")
3921        self.createTestFile(path)
3922
3923        self.os.stat(path)
3924        # actual tests
3925        self.os.utime(path, ns=(200000000, 400000000))
3926        st = self.os.stat(path)
3927        self.assertEqual(0.2, st.st_atime)
3928        self.assertEqual(0.4, st.st_mtime)
3929
3930    def test_utime_incorrect_ns_argument_raises(self):
3931        file_path = "some_file"
3932        self.filesystem.create_file(file_path)
3933
3934        self.assertRaises(TypeError, self.os.utime, file_path, ns=200000000)
3935        self.assertRaises(TypeError, self.os.utime, file_path, ns=("a", "b"))
3936        self.assertRaises(
3937            ValueError, self.os.utime, file_path, times=(1, 2), ns=(100, 200)
3938        )
3939
3940    def test_utime_uses_open_fd_as_path(self):
3941        if os.utime not in os.supports_fd:
3942            self.skip_real_fs()
3943        self.assert_raises_os_error(errno.EBADF, self.os.utime, 5, (1, 2))
3944        path = self.make_path("some_file")
3945        self.createTestFile(path)
3946
3947        with FakeFileOpen(self.filesystem)(path) as f:
3948            self.os.utime(f.filedes, times=(1, 2))
3949            st = self.os.stat(path)
3950            self.assertEqual(1, st.st_atime)
3951            self.assertEqual(2, st.st_mtime)
3952
3953
3954class FakeOsModuleLowLevelFileOpTest(FakeOsModuleTestBase):
3955    """Test low level functions `os.open()`, `os.read()` and `os.write()`."""
3956
3957    def setUp(self):
3958        os.umask(0o022)
3959        super(FakeOsModuleLowLevelFileOpTest, self).setUp()
3960
3961    def test_open_read_only(self):
3962        file_path = self.make_path("file1")
3963        self.create_file(file_path, contents=b"contents")
3964
3965        file_des = self.os.open(file_path, os.O_RDONLY)
3966        self.assertEqual(b"contents", self.os.read(file_des, 8))
3967        self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b"test")
3968        self.os.close(file_des)
3969
3970    def test_open_read_only_write_zero_bytes_posix(self):
3971        self.check_posix_only()
3972        file_path = self.make_path("file1")
3973        self.create_file(file_path, contents=b"contents")
3974
3975        file_des = self.os.open(file_path, os.O_RDONLY)
3976        self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b"test")
3977        self.os.close(file_des)
3978
3979    def test_open_read_only_write_zero_bytes_windows(self):
3980        # under Windows, writing an empty string to a read only file
3981        # is not an error
3982        self.check_windows_only()
3983        file_path = self.make_path("file1")
3984        self.create_file(file_path, contents=b"contents")
3985        file_des = self.os.open(file_path, os.O_RDONLY)
3986        self.assertEqual(0, self.os.write(file_des, b""))
3987        self.os.close(file_des)
3988
3989    def test_open_write_only(self):
3990        file_path = self.make_path("file1")
3991        self.create_file(file_path, contents=b"contents")
3992
3993        file_des = self.os.open(file_path, os.O_WRONLY)
3994        self.assertEqual(4, self.os.write(file_des, b"test"))
3995        self.check_contents(file_path, b"testents")
3996        self.os.close(file_des)
3997
3998    def test_open_write_only_raises_on_read(self):
3999        file_path = self.make_path("file1")
4000        self.create_file(file_path, contents=b"contents")
4001
4002        file_des = self.os.open(file_path, os.O_WRONLY)
4003        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5)
4004        self.os.close(file_des)
4005        file_des = self.os.open(file_path, os.O_WRONLY | os.O_TRUNC)
4006        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5)
4007        self.os.close(file_des)
4008        file_path2 = self.make_path("file2")
4009        file_des = self.os.open(file_path2, os.O_CREAT | os.O_WRONLY)
4010        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5)
4011        self.os.close(file_des)
4012        file_des = self.os.open(file_path2, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4013        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5)
4014        self.os.close(file_des)
4015
4016    def test_open_write_only_read_zero_bytes_posix(self):
4017        self.check_posix_only()
4018        file_path = self.make_path("file1")
4019        file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY)
4020        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 0)
4021        self.os.close(file_des)
4022
4023    def test_open_write_only_read_zero_bytes_windows(self):
4024        # under Windows, reading 0 bytes from a write only file is not an error
4025        self.check_windows_only()
4026        file_path = self.make_path("file1")
4027        file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY)
4028        self.assertEqual(b"", self.os.read(file_des, 0))
4029        self.os.close(file_des)
4030
4031    def test_open_read_write(self):
4032        file_path = self.make_path("file1")
4033        self.create_file(file_path, contents=b"contents")
4034
4035        file_des = self.os.open(file_path, os.O_RDWR)
4036        self.assertEqual(4, self.os.write(file_des, b"test"))
4037        self.check_contents(file_path, b"testents")
4038        self.os.close(file_des)
4039
4040    def test_open_create_is_read_only(self):
4041        file_path = self.make_path("file1")
4042        file_des = self.os.open(file_path, os.O_CREAT)
4043        self.assertEqual(b"", self.os.read(file_des, 1))
4044        self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b"foo")
4045        self.os.close(file_des)
4046
4047    def test_open_create_truncate_is_read_only(self):
4048        file_path = self.make_path("file1")
4049        file_des = self.os.open(file_path, os.O_CREAT | os.O_TRUNC)
4050        self.assertEqual(b"", self.os.read(file_des, 1))
4051        self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b"foo")
4052        self.os.close(file_des)
4053
4054    def test_open_raises_if_does_not_exist(self):
4055        file_path = self.make_path("file1")
4056        self.assert_raises_os_error(errno.ENOENT, self.os.open, file_path, os.O_RDONLY)
4057        self.assert_raises_os_error(errno.ENOENT, self.os.open, file_path, os.O_WRONLY)
4058        self.assert_raises_os_error(errno.ENOENT, self.os.open, file_path, os.O_RDWR)
4059
4060    def test_exclusive_open_raises_without_create_mode(self):
4061        self.skip_real_fs()
4062        file_path = self.make_path("file1")
4063        self.assertRaises(NotImplementedError, self.os.open, file_path, os.O_EXCL)
4064        self.assertRaises(
4065            NotImplementedError,
4066            self.os.open,
4067            file_path,
4068            os.O_EXCL | os.O_WRONLY,
4069        )
4070        self.assertRaises(
4071            NotImplementedError, self.os.open, file_path, os.O_EXCL | os.O_RDWR
4072        )
4073        self.assertRaises(
4074            NotImplementedError,
4075            self.os.open,
4076            file_path,
4077            os.O_EXCL | os.O_TRUNC | os.O_APPEND,
4078        )
4079
4080    def test_open_raises_if_parent_does_not_exist(self):
4081        path = self.make_path("alpha", "alpha")
4082        self.assert_raises_os_error(
4083            errno.ENOENT,
4084            self.os.open,
4085            path,
4086            os.O_CREAT | os.O_WRONLY | os.O_TRUNC,
4087        )
4088
4089    def test_open_truncate(self):
4090        file_path = self.make_path("file1")
4091        self.create_file(file_path, contents=b"contents")
4092
4093        file_des = self.os.open(file_path, os.O_RDWR | os.O_TRUNC)
4094        self.assertEqual(b"", self.os.read(file_des, 8))
4095        self.assertEqual(4, self.os.write(file_des, b"test"))
4096        self.check_contents(file_path, b"test")
4097        self.os.close(file_des)
4098
4099    @unittest.skipIf(not TestCase.is_windows, "O_TEMPORARY only present in Windows")
4100    def test_temp_file(self):
4101        file_path = self.make_path("file1")
4102        fd = self.os.open(file_path, os.O_CREAT | os.O_RDWR | os.O_TEMPORARY)
4103        self.assertTrue(self.os.path.exists(file_path))
4104        self.os.close(fd)
4105        self.assertFalse(self.os.path.exists(file_path))
4106
4107    def test_open_append(self):
4108        file_path = self.make_path("file1")
4109        self.create_file(file_path, contents=b"contents")
4110
4111        file_des = self.os.open(file_path, os.O_WRONLY | os.O_APPEND)
4112        self.assertEqual(4, self.os.write(file_des, b"test"))
4113        self.check_contents(file_path, b"contentstest")
4114        self.os.close(file_des)
4115
4116    def test_open_create(self):
4117        file_path = self.make_path("file1")
4118        file_des = self.os.open(file_path, os.O_RDWR | os.O_CREAT)
4119        self.assertTrue(self.os.path.exists(file_path))
4120        self.assertEqual(4, self.os.write(file_des, b"test"))
4121        self.check_contents(file_path, "test")
4122        self.os.close(file_des)
4123
4124    def test_can_read_after_create_exclusive(self):
4125        self.check_posix_only()
4126        path1 = self.make_path("alpha")
4127        file_des = self.os.open(path1, os.O_CREAT | os.O_EXCL)
4128        self.assertEqual(b"", self.os.read(file_des, 0))
4129        self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b"")
4130        self.os.close(file_des)
4131
4132    def test_open_create_mode_posix(self):
4133        self.check_posix_only()
4134        file_path = self.make_path("file1")
4135        file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o700)
4136        self.assertTrue(self.os.path.exists(file_path))
4137        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5)
4138        self.assertEqual(4, self.os.write(file_des, b"test"))
4139        self.assert_mode_equal(0o700, self.os.stat(file_path).st_mode)
4140        self.os.close(file_des)
4141
4142    def test_open_create_mode_windows(self):
4143        self.check_windows_only()
4144        file_path = self.make_path("file1")
4145        file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o700)
4146        self.assertTrue(self.os.path.exists(file_path))
4147        self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5)
4148        self.assertEqual(4, self.os.write(file_des, b"test"))
4149        self.assert_mode_equal(0o666, self.os.stat(file_path).st_mode)
4150        self.os.close(file_des)
4151
4152    def testOpenCreateMode444Windows(self):
4153        self.check_windows_only()
4154        file_path = self.make_path("file1")
4155        file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o442)
4156        self.assert_mode_equal(0o444, self.os.stat(file_path).st_mode)
4157        self.os.close(file_des)
4158        self.os.chmod(file_path, 0o666)
4159
4160    def testOpenCreateMode666Windows(self):
4161        self.check_windows_only()
4162        file_path = self.make_path("file1")
4163        file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o224)
4164        self.assert_mode_equal(0o666, self.os.stat(file_path).st_mode)
4165        self.os.close(file_des)
4166
4167    def test_open_exclusive(self):
4168        file_path = self.make_path("file1")
4169        file_des = self.os.open(file_path, os.O_RDWR | os.O_EXCL | os.O_CREAT)
4170        self.assertTrue(self.os.path.exists(file_path))
4171        self.os.close(file_des)
4172
4173    def test_open_exclusive_raises_if_file_exists(self):
4174        file_path = self.make_path("file1")
4175        self.create_file(file_path, contents=b"contents")
4176        self.assert_raises_os_error(
4177            errno.EEXIST,
4178            self.os.open,
4179            file_path,
4180            os.O_RDWR | os.O_EXCL | os.O_CREAT,
4181        )
4182        self.assert_raises_os_error(
4183            errno.EEXIST,
4184            self.os.open,
4185            file_path,
4186            os.O_RDWR | os.O_EXCL | os.O_CREAT,
4187        )
4188
4189    def test_open_exclusive_raises_if_symlink_exists_in_posix(self):
4190        self.check_posix_only()
4191        link_path = self.make_path("link")
4192        link_target = self.make_path("link_target")
4193        self.os.symlink(link_target, link_path)
4194        self.assert_raises_os_error(
4195            errno.EEXIST,
4196            self.os.open,
4197            link_path,
4198            os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_EXCL,
4199        )
4200
4201    def test_open_exclusive_if_symlink_exists_works_in_windows(self):
4202        self.check_windows_only()
4203        self.skip_if_symlink_not_supported()
4204        link_path = self.make_path("link")
4205        link_target = self.make_path("link_target")
4206        self.os.symlink(link_target, link_path)
4207        fd = self.os.open(link_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_EXCL)
4208        self.os.close(fd)
4209
4210    def test_open_directory_raises_under_windows(self):
4211        self.check_windows_only()
4212        dir_path = self.make_path("dir")
4213        self.create_dir(dir_path)
4214        self.assert_raises_os_error(errno.EACCES, self.os.open, dir_path, os.O_RDONLY)
4215        self.assert_raises_os_error(errno.EACCES, self.os.open, dir_path, os.O_WRONLY)
4216        self.assert_raises_os_error(errno.EACCES, self.os.open, dir_path, os.O_RDWR)
4217
4218    def test_open_directory_for_writing_raises_under_posix(self):
4219        self.check_posix_only()
4220        dir_path = self.make_path("dir")
4221        self.create_dir(dir_path)
4222        self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_WRONLY)
4223        self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_RDWR)
4224
4225    def test_open_directory_read_only_under_posix(self):
4226        self.check_posix_only()
4227        self.skip_real_fs()
4228        dir_path = self.make_path("dir")
4229        self.create_dir(dir_path)
4230        file_des = self.os.open(dir_path, os.O_RDONLY)
4231        self.assertEqual(3, file_des)
4232        self.os.close(file_des)
4233
4234    def test_opening_existing_directory_in_creation_mode(self):
4235        self.check_linux_only()
4236        dir_path = self.make_path("alpha")
4237        self.os.mkdir(dir_path)
4238        self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_CREAT)
4239
4240    def test_writing_to_existing_directory(self):
4241        self.check_macos_only()
4242        dir_path = self.make_path("alpha")
4243        self.os.mkdir(dir_path)
4244        fd = self.os.open(dir_path, os.O_CREAT)
4245        self.assert_raises_os_error(errno.EBADF, self.os.write, fd, b"")
4246
4247    def test_opening_existing_directory_in_write_mode(self):
4248        self.check_posix_only()
4249        dir_path = self.make_path("alpha")
4250        self.os.mkdir(dir_path)
4251        self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_WRONLY)
4252
4253    def test_open_mode_posix(self):
4254        self.check_posix_only()
4255        self.skip_real_fs()
4256        file_path = self.make_path("baz")
4257        file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4258        stat0 = self.os.fstat(file_des)
4259        # not a really good test as this replicates the code,
4260        # but we don't know the umask at the test system
4261        self.assertEqual(0o100777 & ~self.os._umask(), stat0.st_mode)
4262        self.os.close(file_des)
4263
4264    def test_open_mode_windows(self):
4265        self.check_windows_only()
4266        file_path = self.make_path("baz")
4267        file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4268        stat0 = self.os.fstat(file_des)
4269        self.assertEqual(0o100666, stat0.st_mode)
4270        self.os.close(file_des)
4271
4272    def test_write_read(self):
4273        file_path = self.make_path("file1")
4274        self.create_file(file_path, contents=b"orig contents")
4275        new_contents = b"1234567890abcdef"
4276
4277        with self.open(file_path, "wb") as fh:
4278            fileno = fh.fileno()
4279            self.assertEqual(len(new_contents), self.os.write(fileno, new_contents))
4280            self.check_contents(file_path, new_contents)
4281
4282        with self.open(file_path, "rb") as fh:
4283            fileno = fh.fileno()
4284            self.assertEqual(b"", self.os.read(fileno, 0))
4285            self.assertEqual(new_contents[0:2], self.os.read(fileno, 2))
4286            self.assertEqual(new_contents[2:10], self.os.read(fileno, 8))
4287            self.assertEqual(new_contents[10:], self.os.read(fileno, 100))
4288            self.assertEqual(b"", self.os.read(fileno, 10))
4289
4290        self.assert_raises_os_error(errno.EBADF, self.os.write, fileno, new_contents)
4291        self.assert_raises_os_error(errno.EBADF, self.os.read, fileno, 10)
4292
4293    def test_write_from_different_f_ds(self):
4294        # Regression test for #211
4295        file_path = self.make_path("baz")
4296        fd0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4297        fd1 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4298        self.os.write(fd0, b"aaaa")
4299        self.os.write(fd1, b"bb")
4300        self.assertEqual(4, self.os.path.getsize(file_path))
4301        self.check_contents(file_path, b"bbaa")
4302        self.os.close(fd1)
4303        self.os.close(fd0)
4304
4305    def test_write_from_different_fds_with_append(self):
4306        # Regression test for #268
4307        file_path = self.make_path("baz")
4308        fd0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4309        fd1 = self.os.open(file_path, os.O_WRONLY | os.O_APPEND)
4310        self.os.write(fd0, b"aaa")
4311        self.os.write(fd1, b"bbb")
4312        self.assertEqual(6, self.os.path.getsize(file_path))
4313        self.check_contents(file_path, b"aaabbb")
4314        self.os.close(fd1)
4315        self.os.close(fd0)
4316
4317    def test_read_only_read_after_write(self):
4318        # Regression test for #269
4319        self.check_posix_only()
4320        file_path = self.make_path("foo", "bar", "baz")
4321        self.create_file(file_path, contents=b"test")
4322        fd0 = self.os.open(file_path, os.O_CREAT)
4323        fd1 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4324        self.assertEqual(b"", self.os.read(fd0, 0))
4325        self.os.close(fd1)
4326        self.os.close(fd0)
4327
4328    def test_read_after_closing_write_descriptor(self):
4329        # Regression test for #271
4330        file_path = self.make_path("baz")
4331        fd0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4332        fd1 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4333        fd2 = self.os.open(file_path, os.O_CREAT)
4334        self.os.write(fd1, b"abc")
4335        self.os.close(fd0)
4336        self.assertEqual(b"abc", self.os.read(fd2, 3))
4337        self.os.close(fd2)
4338        self.os.close(fd1)
4339
4340    def test_writing_behind_end_of_file(self):
4341        # Regression test for #273
4342        file_path = self.make_path("baz")
4343        fd1 = self.os.open(file_path, os.O_CREAT)
4344        fd2 = self.os.open(file_path, os.O_RDWR)
4345        self.os.write(fd2, b"m")
4346        fd3 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC)
4347        self.assertEqual(b"", self.os.read(fd2, 1))
4348        self.os.write(fd2, b"m")
4349        self.assertEqual(b"\x00m", self.os.read(fd1, 2))
4350        self.os.close(fd1)
4351        self.os.close(fd2)
4352        self.os.close(fd3)
4353
4354    def test_devnull_posix(self):
4355        self.check_posix_only()
4356        # make sure os.devnull is correctly set after changing the filesystem
4357        self.setup_fake_fs()
4358        self.assertTrue(self.os.path.exists(self.os.devnull))
4359
4360    def test_devnull_windows(self):
4361        self.check_windows_only()
4362        # make sure os.devnull is correctly set after changing the filesystem
4363        self.setup_fake_fs()
4364        if sys.version_info < (3, 8):
4365            self.assertFalse(self.os.path.exists(self.os.devnull))
4366        else:
4367            self.assertTrue(self.os.path.exists(self.os.devnull))
4368
4369    def test_write_devnull(self):
4370        fd = self.os.open(self.os.devnull, os.O_RDWR)
4371        self.assertEqual(4, self.os.write(fd, b"test"))
4372        self.assertEqual(b"", self.os.read(fd, 4))
4373        self.os.close(fd)
4374        fd = self.os.open(self.os.devnull, os.O_RDONLY)
4375        self.assertEqual(b"", self.os.read(fd, 4))
4376        self.os.close(fd)
4377
4378    def test_sendfile_with_invalid_fd(self):
4379        self.check_linux_only()
4380        self.assert_raises_os_error(errno.EBADF, self.os.sendfile, 100, 101, 0, 100)
4381        src_file_path = self.make_path("foo")
4382        dst_file_path = self.make_path("bar")
4383        self.create_file(src_file_path, "testcontent")
4384        self.create_file(dst_file_path)
4385        fd1 = self.os.open(src_file_path, os.O_RDONLY)
4386        fd2 = self.os.open(dst_file_path, os.O_RDONLY)
4387        self.assert_raises_os_error(errno.EBADF, self.os.sendfile, fd2, fd1, 0, 4)
4388
4389    def test_sendfile_no_offset(self):
4390        self.check_linux_only()
4391        src_file_path = self.make_path("foo")
4392        dst_file_path = self.make_path("bar")
4393        self.create_file(src_file_path, "testcontent")
4394        self.create_file(dst_file_path)
4395        fd1 = self.os.open(src_file_path, os.O_RDONLY)
4396        fd2 = self.os.open(dst_file_path, os.O_RDWR)
4397        self.os.sendfile(fd2, fd1, 0, 3)
4398        self.os.close(fd2)
4399        self.os.close(fd1)
4400        with self.open(dst_file_path) as f:
4401            self.assertEqual("tes", f.read())
4402
4403    def test_sendfile_with_offset(self):
4404        self.check_linux_only()
4405        src_file_path = self.make_path("foo")
4406        dst_file_path = self.make_path("bar")
4407        self.create_file(src_file_path, "testcontent")
4408        self.create_file(dst_file_path)
4409        fd1 = self.os.open(src_file_path, os.O_RDONLY)
4410        fd2 = self.os.open(dst_file_path, os.O_RDWR)
4411        self.os.sendfile(fd2, fd1, 4, 4)
4412        self.os.close(fd2)
4413        self.os.close(fd1)
4414        with self.open(dst_file_path) as f:
4415            self.assertEqual("cont", f.read())
4416
4417    def test_sendfile_twice(self):
4418        self.check_linux_only()
4419        src_file_path = self.make_path("foo")
4420        dst_file_path = self.make_path("bar")
4421        self.create_file(src_file_path, "testcontent")
4422        self.create_file(dst_file_path)
4423        fd1 = self.os.open(src_file_path, os.O_RDONLY)
4424        fd2 = self.os.open(dst_file_path, os.O_RDWR)
4425        self.os.sendfile(fd2, fd1, 4, 4)
4426        self.os.sendfile(fd2, fd1, 4, 4)
4427        self.os.close(fd2)
4428        self.os.close(fd1)
4429        with self.open(dst_file_path) as f:
4430            self.assertEqual("contcont", f.read())
4431
4432    def test_sendfile_offset_none(self):
4433        self.check_linux_only()
4434        src_file_path = self.make_path("foo")
4435        dst_file_path = self.make_path("bar")
4436        self.create_file(src_file_path, "testcontent")
4437        self.create_file(dst_file_path)
4438        fd1 = self.os.open(src_file_path, os.O_RDONLY)
4439        fd2 = self.os.open(dst_file_path, os.O_RDWR)
4440        self.os.sendfile(fd2, fd1, None, 4)
4441        self.os.sendfile(fd2, fd1, None, 3)
4442        self.os.close(fd2)
4443        self.os.close(fd1)
4444        with self.open(dst_file_path) as f:
4445            self.assertEqual("testcon", f.read())
4446
4447    @unittest.skipIf(not TestCase.is_macos, "Testing MacOs only behavior")
4448    def test_no_sendfile_to_regular_file_under_macos(self):
4449        src_file_path = self.make_path("foo")
4450        dst_file_path = self.make_path("bar")
4451        self.create_file(src_file_path, "testcontent")
4452        self.create_file(dst_file_path)
4453        fd1 = self.os.open(src_file_path, os.O_RDONLY)
4454        fd2 = self.os.open(dst_file_path, os.O_RDWR)
4455        # raises socket operation on non-socket
4456        self.assertRaises(OSError, self.os.sendfile, fd2, fd1, 0, 3)
4457        self.os.close(fd2)
4458        self.os.close(fd1)
4459
4460
4461class RealOsModuleLowLevelFileOpTest(FakeOsModuleLowLevelFileOpTest):
4462    def use_real_fs(self):
4463        return True
4464
4465
4466class FakeOsModuleWalkTest(FakeOsModuleTestBase):
4467    def assertWalkResults(self, expected, top, topdown=True, followlinks=False):
4468        # as the result of walk is unsorted, we have to check against
4469        # sorted results
4470        result = list(
4471            step for step in self.os.walk(top, topdown=topdown, followlinks=followlinks)
4472        )
4473        result = sorted(result, key=lambda lst: lst[0])
4474        expected = sorted(expected, key=lambda lst: lst[0])
4475        self.assertEqual(len(expected), len(result))
4476        for entry, expected_entry in zip(result, expected):
4477            self.assertEqual(expected_entry[0], entry[0])
4478            self.assertEqual(expected_entry[1], sorted(entry[1]))
4479            self.assertEqual(expected_entry[2], sorted(entry[2]))
4480
4481    def ResetErrno(self):
4482        """Reset the last seen errno."""
4483        self.last_errno = False
4484
4485    def StoreErrno(self, os_error):
4486        """Store the last errno we saw."""
4487        self.last_errno = os_error.errno
4488
4489    def GetErrno(self):
4490        """Return the last errno we saw."""
4491        return self.last_errno
4492
4493    def test_walk_top_down(self):
4494        """Walk down ordering is correct."""
4495        base_dir = self.make_path("foo")
4496        self.create_file(self.os.path.join(base_dir, "1.txt"))
4497        self.create_file(self.os.path.join(base_dir, "bar1", "2.txt"))
4498        self.create_file(self.os.path.join(base_dir, "bar1", "baz", "3.txt"))
4499        self.create_file(self.os.path.join(base_dir, "bar2", "4.txt"))
4500        expected = [
4501            (base_dir, ["bar1", "bar2"], ["1.txt"]),
4502            (self.os.path.join(base_dir, "bar1"), ["baz"], ["2.txt"]),
4503            (self.os.path.join(base_dir, "bar1", "baz"), [], ["3.txt"]),
4504            (self.os.path.join(base_dir, "bar2"), [], ["4.txt"]),
4505        ]
4506        self.assertWalkResults(expected, base_dir)
4507
4508    def test_walk_bottom_up(self):
4509        """Walk up ordering is correct."""
4510        base_dir = self.make_path("foo")
4511        self.create_file(self.os.path.join(base_dir, "bar1", "baz", "1.txt"))
4512        self.create_file(self.os.path.join(base_dir, "bar1", "2.txt"))
4513        self.create_file(self.os.path.join(base_dir, "bar2", "3.txt"))
4514        self.create_file(self.os.path.join(base_dir, "4.txt"))
4515
4516        expected = [
4517            (self.os.path.join(base_dir, "bar1", "baz"), [], ["1.txt"]),
4518            (self.os.path.join(base_dir, "bar1"), ["baz"], ["2.txt"]),
4519            (self.os.path.join(base_dir, "bar2"), [], ["3.txt"]),
4520            (base_dir, ["bar1", "bar2"], ["4.txt"]),
4521        ]
4522        self.assertWalkResults(expected, self.make_path("foo"), topdown=False)
4523
4524    def test_walk_raises_if_non_existent(self):
4525        """Raises an exception when attempting to walk
4526        non-existent directory."""
4527        directory = self.make_path("foo", "bar")
4528        self.assertEqual(False, self.os.path.exists(directory))
4529        generator = self.os.walk(directory)
4530        self.assertRaises(StopIteration, next, generator)
4531
4532    def test_walk_raises_if_not_directory(self):
4533        """Raises an exception when attempting to walk a non-directory."""
4534        filename = self.make_path("foo", "bar")
4535        self.create_file(filename)
4536        generator = self.os.walk(filename)
4537        self.assertRaises(StopIteration, next, generator)
4538
4539    def test_walk_calls_on_error_if_non_existent(self):
4540        """Calls onerror with correct errno when walking
4541        non-existent directory."""
4542        self.ResetErrno()
4543        directory = self.make_path("foo", "bar")
4544        self.assertEqual(False, self.os.path.exists(directory))
4545        # Calling os.walk on a non-existent directory should trigger
4546        # a call to the onerror method.
4547        # We do not actually care what, if anything, is returned.
4548        for _ in self.os.walk(directory, onerror=self.StoreErrno):
4549            pass
4550        self.assertTrue(self.GetErrno() in (errno.ENOTDIR, errno.ENOENT))
4551
4552    def test_walk_calls_on_error_if_not_directory(self):
4553        """Calls onerror with correct errno when walking non-directory."""
4554        self.ResetErrno()
4555        filename = self.make_path("foo" "bar")
4556        self.create_file(filename)
4557        self.assertEqual(True, self.os.path.exists(filename))
4558        # Calling `os.walk` on a file should trigger a call to the
4559        # `onerror` method.
4560        # We do not actually care what, if anything, is returned.
4561        for _ in self.os.walk(filename, onerror=self.StoreErrno):
4562            pass
4563        self.assertTrue(self.GetErrno() in (errno.ENOTDIR, errno.EACCES))
4564
4565    def test_walk_skips_removed_directories(self):
4566        """Caller can modify list of directories to visit while walking."""
4567        root = self.make_path("foo")
4568        visit = "visit"
4569        no_visit = "no_visit"
4570        self.create_file(self.os.path.join(root, "bar"))
4571        self.create_file(self.os.path.join(root, visit, "1.txt"))
4572        self.create_file(self.os.path.join(root, visit, "2.txt"))
4573        self.create_file(self.os.path.join(root, no_visit, "3.txt"))
4574        self.create_file(self.os.path.join(root, no_visit, "4.txt"))
4575
4576        generator = self.os.walk(self.make_path("foo"))
4577        root_contents = next(generator)
4578        root_contents[1].remove(no_visit)
4579
4580        visited_visit_directory = False
4581
4582        for root, _dirs, _files in iter(generator):
4583            self.assertEqual(False, root.endswith(self.os.path.sep + no_visit))
4584            if root.endswith(self.os.path.sep + visit):
4585                visited_visit_directory = True
4586
4587        self.assertEqual(True, visited_visit_directory)
4588
4589    def test_walk_followsymlink_disabled(self):
4590        self.check_posix_only()
4591        base_dir = self.make_path("foo")
4592        link_dir = self.make_path("linked")
4593        self.create_file(self.os.path.join(link_dir, "subfile"))
4594        self.create_file(self.os.path.join(base_dir, "bar", "baz"))
4595        self.create_file(self.os.path.join(base_dir, "bar", "xyzzy", "plugh"))
4596        self.create_symlink(self.os.path.join(base_dir, "created_link"), link_dir)
4597
4598        expected = [
4599            (base_dir, ["bar", "created_link"], []),
4600            (self.os.path.join(base_dir, "bar"), ["xyzzy"], ["baz"]),
4601            (self.os.path.join(base_dir, "bar", "xyzzy"), [], ["plugh"]),
4602        ]
4603        self.assertWalkResults(expected, base_dir, followlinks=False)
4604
4605        expected = [(self.os.path.join(base_dir, "created_link"), [], ["subfile"])]
4606        self.assertWalkResults(
4607            expected,
4608            self.os.path.join(base_dir, "created_link"),
4609            followlinks=False,
4610        )
4611
4612    def test_walk_followsymlink_enabled(self):
4613        self.check_posix_only()
4614        base_dir = self.make_path("foo")
4615        link_dir = self.make_path("linked")
4616        self.create_file(self.os.path.join(link_dir, "subfile"))
4617        self.create_file(self.os.path.join(base_dir, "bar", "baz"))
4618        self.create_file(self.os.path.join(base_dir, "bar", "xyzzy", "plugh"))
4619        self.create_symlink(
4620            self.os.path.join(base_dir, "created_link"),
4621            self.os.path.join(link_dir),
4622        )
4623
4624        expected = [
4625            (base_dir, ["bar", "created_link"], []),
4626            (self.os.path.join(base_dir, "bar"), ["xyzzy"], ["baz"]),
4627            (self.os.path.join(base_dir, "bar", "xyzzy"), [], ["plugh"]),
4628            (self.os.path.join(base_dir, "created_link"), [], ["subfile"]),
4629        ]
4630        self.assertWalkResults(expected, base_dir, followlinks=True)
4631
4632        expected = [(self.os.path.join(base_dir, "created_link"), [], ["subfile"])]
4633        self.assertWalkResults(
4634            expected,
4635            self.os.path.join(base_dir, "created_link"),
4636            followlinks=True,
4637        )
4638
4639    def test_walk_linked_file_in_subdir(self):
4640        # regression test for #559 (tested for link on incomplete path)
4641        self.check_posix_only()
4642        # need to have a top-level link to reproduce the bug - skip real fs
4643        self.skip_real_fs()
4644        file_path = "/foo/bar/baz"
4645        self.create_file(file_path)
4646        self.create_symlink("bar", file_path)
4647        expected = [("/foo", ["bar"], []), ("/foo/bar", [], ["baz"])]
4648        self.assertWalkResults(expected, "/foo")
4649
4650    def test_base_dirpath(self):
4651        # regression test for #512
4652        file_path = self.make_path("foo", "bar", "baz")
4653        self.create_file(file_path)
4654        variants = [
4655            self.make_path("foo", "bar"),
4656            self.make_path("foo", "..", "foo", "bar"),
4657            self.make_path("foo", "..", "foo", "bar") + self.os.path.sep * 3,
4658            self.make_path("foo") + self.os.path.sep * 3 + "bar",
4659        ]
4660        for base_dir in variants:
4661            for dirpath, _dirnames, _filenames in self.os.walk(base_dir):
4662                self.assertEqual(dirpath, base_dir)
4663
4664        file_path = self.make_path("foo", "bar", "dir", "baz")
4665        self.create_file(file_path)
4666        for base_dir in variants:
4667            for dirpath, _dirnames, _filenames in self.os.walk(base_dir):
4668                self.assertTrue(dirpath.startswith(base_dir))
4669
4670
4671class RealOsModuleWalkTest(FakeOsModuleWalkTest):
4672    def use_real_fs(self):
4673        return True
4674
4675
4676class FakeOsModuleDirFdTest(FakeOsModuleTestBase):
4677    def setUp(self):
4678        super(FakeOsModuleDirFdTest, self).setUp()
4679        self.os.supports_dir_fd.clear()
4680        self.filesystem.is_windows_fs = False
4681        self.filesystem.create_dir("/foo/bar")
4682        self.dir_fd = self.os.open("/foo", os.O_RDONLY)
4683        self.filesystem.create_file("/foo/baz")
4684
4685    def test_access(self):
4686        self.assertRaises(
4687            NotImplementedError,
4688            self.os.access,
4689            "baz",
4690            self.os.F_OK,
4691            dir_fd=self.dir_fd,
4692        )
4693        self.os.supports_dir_fd.add(self.os.access)
4694        self.assertTrue(self.os.access("baz", self.os.F_OK, dir_fd=self.dir_fd))
4695
4696    def test_chmod(self):
4697        self.assertRaises(
4698            NotImplementedError,
4699            self.os.chmod,
4700            "baz",
4701            0o6543,
4702            dir_fd=self.dir_fd,
4703        )
4704        self.os.supports_dir_fd.add(self.os.chmod)
4705        self.os.chmod("baz", 0o6543, dir_fd=self.dir_fd)
4706        st = self.os.stat("/foo/baz")
4707        self.assert_mode_equal(0o6543, st.st_mode)
4708
4709    @unittest.skipIf(not hasattr(os, "chown"), "chown not on all platforms available")
4710    def test_chown(self):
4711        self.assertRaises(
4712            NotImplementedError,
4713            self.os.chown,
4714            "baz",
4715            100,
4716            101,
4717            dir_fd=self.dir_fd,
4718        )
4719        self.os.supports_dir_fd.add(self.os.chown)
4720        self.os.chown("baz", 100, 101, dir_fd=self.dir_fd)
4721        st = self.os.stat("/foo/baz")
4722        self.assertEqual(st[stat.ST_UID], 100)
4723        self.assertEqual(st[stat.ST_GID], 101)
4724
4725    def test_link_src_fd(self):
4726        self.assertRaises(
4727            NotImplementedError,
4728            self.os.link,
4729            "baz",
4730            "/bat",
4731            src_dir_fd=self.dir_fd,
4732        )
4733        self.os.supports_dir_fd.add(self.os.link)
4734        self.os.link("baz", "/bat", src_dir_fd=self.dir_fd)
4735        self.assertTrue(self.os.path.exists("/bat"))
4736
4737    def test_link_dst_fd(self):
4738        self.assertRaises(
4739            NotImplementedError,
4740            self.os.link,
4741            "baz",
4742            "/bat",
4743            dst_dir_fd=self.dir_fd,
4744        )
4745        self.os.supports_dir_fd.add(self.os.link)
4746        self.os.link("/foo/baz", "bat", dst_dir_fd=self.dir_fd)
4747        self.assertTrue(self.os.path.exists("/foo/bat"))
4748
4749    def test_symlink(self):
4750        self.assertRaises(
4751            NotImplementedError,
4752            self.os.symlink,
4753            "baz",
4754            "/bat",
4755            dir_fd=self.dir_fd,
4756        )
4757        self.os.supports_dir_fd.add(self.os.symlink)
4758        self.os.symlink("baz", "/bat", dir_fd=self.dir_fd)
4759        self.assertTrue(self.os.path.exists("/bat"))
4760
4761    def test_readlink(self):
4762        self.skip_if_symlink_not_supported()
4763        self.filesystem.create_symlink("/meyer/lemon/pie", "/foo/baz")
4764        self.filesystem.create_symlink("/geo/metro", "/meyer")
4765        self.assertRaises(
4766            NotImplementedError,
4767            self.os.readlink,
4768            "/geo/metro/lemon/pie",
4769            dir_fd=self.dir_fd,
4770        )
4771        self.os.supports_dir_fd.add(self.os.readlink)
4772        self.assertEqual(
4773            "/foo/baz",
4774            self.os.readlink("/geo/metro/lemon/pie", dir_fd=self.dir_fd),
4775        )
4776
4777    def test_stat(self):
4778        self.assertRaises(NotImplementedError, self.os.stat, "baz", dir_fd=self.dir_fd)
4779        self.os.supports_dir_fd.add(self.os.stat)
4780        st = self.os.stat("baz", dir_fd=self.dir_fd)
4781        self.assertEqual(st.st_mode, 0o100666)
4782
4783    def test_lstat(self):
4784        self.assertRaises(NotImplementedError, self.os.lstat, "baz", dir_fd=self.dir_fd)
4785        self.os.supports_dir_fd.add(self.os.lstat)
4786        st = self.os.lstat("baz", dir_fd=self.dir_fd)
4787        self.assertEqual(st.st_mode, 0o100666)
4788
4789    def test_mkdir(self):
4790        self.assertRaises(
4791            NotImplementedError, self.os.mkdir, "newdir", dir_fd=self.dir_fd
4792        )
4793        self.os.supports_dir_fd.add(self.os.mkdir)
4794        self.os.mkdir("newdir", dir_fd=self.dir_fd)
4795        self.assertTrue(self.os.path.exists("/foo/newdir"))
4796
4797    def test_rmdir(self):
4798        self.assertRaises(NotImplementedError, self.os.rmdir, "bar", dir_fd=self.dir_fd)
4799        self.os.supports_dir_fd.add(self.os.rmdir)
4800        self.os.rmdir("bar", dir_fd=self.dir_fd)
4801        self.assertFalse(self.os.path.exists("/foo/bar"))
4802
4803    @unittest.skipIf(not hasattr(os, "mknod"), "mknod not on all platforms available")
4804    def test_mknod(self):
4805        self.assertRaises(
4806            NotImplementedError, self.os.mknod, "newdir", dir_fd=self.dir_fd
4807        )
4808        self.os.supports_dir_fd.add(self.os.mknod)
4809        self.os.mknod("newdir", dir_fd=self.dir_fd)
4810        self.assertTrue(self.os.path.exists("/foo/newdir"))
4811
4812    def test_rename_src_fd(self):
4813        self.assertRaises(
4814            NotImplementedError,
4815            self.os.rename,
4816            "baz",
4817            "/foo/batz",
4818            src_dir_fd=self.dir_fd,
4819        )
4820        self.os.supports_dir_fd.add(self.os.rename)
4821        self.os.rename("bar", "/foo/batz", src_dir_fd=self.dir_fd)
4822        self.assertTrue(self.os.path.exists("/foo/batz"))
4823
4824    def test_rename_dst_fd(self):
4825        self.assertRaises(
4826            NotImplementedError,
4827            self.os.rename,
4828            "baz",
4829            "/foo/batz",
4830            dst_dir_fd=self.dir_fd,
4831        )
4832        self.os.supports_dir_fd.add(self.os.rename)
4833        self.os.rename("/foo/bar", "batz", dst_dir_fd=self.dir_fd)
4834        self.assertTrue(self.os.path.exists("/foo/batz"))
4835
4836    def test_replace_src_fd(self):
4837        self.assertRaises(
4838            NotImplementedError,
4839            self.os.rename,
4840            "baz",
4841            "/foo/batz",
4842            src_dir_fd=self.dir_fd,
4843        )
4844        self.os.supports_dir_fd.add(self.os.rename)
4845        self.os.replace("bar", "/foo/batz", src_dir_fd=self.dir_fd)
4846        self.assertTrue(self.os.path.exists("/foo/batz"))
4847
4848    def test_replace_dst_fd(self):
4849        self.assertRaises(
4850            NotImplementedError,
4851            self.os.rename,
4852            "baz",
4853            "/foo/batz",
4854            dst_dir_fd=self.dir_fd,
4855        )
4856        self.os.supports_dir_fd.add(self.os.rename)
4857        self.os.replace("/foo/bar", "batz", dst_dir_fd=self.dir_fd)
4858        self.assertTrue(self.os.path.exists("/foo/batz"))
4859
4860    def test_remove(self):
4861        self.assertRaises(
4862            NotImplementedError, self.os.remove, "baz", dir_fd=self.dir_fd
4863        )
4864        self.os.supports_dir_fd.add(self.os.remove)
4865        self.os.remove("baz", dir_fd=self.dir_fd)
4866        self.assertFalse(self.os.path.exists("/foo/baz"))
4867
4868    def test_unlink(self):
4869        self.assertRaises(
4870            NotImplementedError, self.os.unlink, "baz", dir_fd=self.dir_fd
4871        )
4872        self.os.supports_dir_fd.add(self.os.unlink)
4873        self.os.unlink("baz", dir_fd=self.dir_fd)
4874        self.assertFalse(self.os.path.exists("/foo/baz"))
4875
4876    def test_utime(self):
4877        self.assertRaises(
4878            NotImplementedError,
4879            self.os.utime,
4880            "baz",
4881            (1, 2),
4882            dir_fd=self.dir_fd,
4883        )
4884        self.os.supports_dir_fd.add(self.os.utime)
4885        self.os.utime("baz", times=(1, 2), dir_fd=self.dir_fd)
4886        st = self.os.stat("/foo/baz")
4887        self.assertEqual(1, st.st_atime)
4888        self.assertEqual(2, st.st_mtime)
4889
4890    def test_open(self):
4891        self.assertRaises(
4892            NotImplementedError,
4893            self.os.open,
4894            "baz",
4895            os.O_RDONLY,
4896            dir_fd=self.dir_fd,
4897        )
4898        self.os.supports_dir_fd.add(self.os.open)
4899        fd = self.os.open("baz", os.O_RDONLY, dir_fd=self.dir_fd)
4900        self.assertLess(0, fd)
4901
4902
4903class StatPropagationTest(TestCase):
4904    def setUp(self):
4905        self.filesystem = fake_filesystem.FakeFilesystem(path_separator="/")
4906        self.os = fake_os.FakeOsModule(self.filesystem)
4907        self.open = fake_open.FakeFileOpen(self.filesystem)
4908
4909    def test_file_size_updated_via_close(self):
4910        """test that file size gets updated via close()."""
4911        file_dir = "xyzzy"
4912        file_path = "xyzzy/close"
4913        content = "This is a test."
4914        self.os.mkdir(file_dir)
4915        fh = self.open(file_path, "w")
4916        self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
4917        self.assertEqual("", self.filesystem.get_object(file_path).contents)
4918        fh.write(content)
4919        self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
4920        self.assertEqual("", self.filesystem.get_object(file_path).contents)
4921        fh.close()
4922        self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
4923        self.assertEqual(content, self.filesystem.get_object(file_path).contents)
4924
4925    def test_file_size_not_reset_after_close(self):
4926        file_dir = "xyzzy"
4927        file_path = "xyzzy/close"
4928        self.os.mkdir(file_dir)
4929        size = 1234
4930        # The file has size, but no content. When the file is opened for
4931        # reading, its size should be preserved.
4932        self.filesystem.create_file(file_path, st_size=size)
4933        fh = self.open(file_path, "r")
4934        fh.close()
4935        self.assertEqual(size, self.open(file_path, "r").size())
4936
4937    def test_file_size_after_write(self):
4938        file_path = "test_file"
4939        original_content = "abcdef"
4940        original_size = len(original_content)
4941        self.filesystem.create_file(file_path, contents=original_content)
4942        added_content = "foo bar"
4943        expected_size = original_size + len(added_content)
4944        fh = self.open(file_path, "a")
4945        fh.write(added_content)
4946        self.assertEqual(original_size, fh.size())
4947        fh.close()
4948        self.assertEqual(expected_size, self.open(file_path, "r").size())
4949
4950    def test_large_file_size_after_write(self):
4951        file_path = "test_file"
4952        original_content = "abcdef"
4953        original_size = len(original_content)
4954        self.filesystem.create_file(file_path, st_size=original_size)
4955        added_content = "foo bar"
4956        fh = self.open(file_path, "a")
4957        self.assertRaises(
4958            fake_file.FakeLargeFileIoException,
4959            lambda: fh.write(added_content),
4960        )
4961
4962    def test_file_size_updated_via_flush(self):
4963        """test that file size gets updated via flush()."""
4964        file_dir = "xyzzy"
4965        file_name = "flush"
4966        file_path = self.os.path.join(file_dir, file_name)
4967        content = "This might be a test."
4968        self.os.mkdir(file_dir)
4969        fh = self.open(file_path, "w")
4970        self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
4971        self.assertEqual("", self.filesystem.get_object(file_path).contents)
4972        fh.write(content)
4973        self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
4974        self.assertEqual("", self.filesystem.get_object(file_path).contents)
4975        fh.flush()
4976        self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
4977        self.assertEqual(content, self.filesystem.get_object(file_path).contents)
4978        fh.close()
4979        self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
4980        self.assertEqual(content, self.filesystem.get_object(file_path).contents)
4981
4982    def test_file_size_truncation(self):
4983        """test that file size gets updated via open()."""
4984        file_dir = "xyzzy"
4985        file_path = "xyzzy/truncation"
4986        content = "AAA content."
4987
4988        # pre-create file with content
4989        self.os.mkdir(file_dir)
4990        fh = self.open(file_path, "w")
4991        fh.write(content)
4992        fh.close()
4993        self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE])
4994        self.assertEqual(content, self.filesystem.get_object(file_path).contents)
4995
4996        # test file truncation
4997        fh = self.open(file_path, "w")
4998        self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE])
4999        self.assertEqual("", self.filesystem.get_object(file_path).contents)
5000        fh.close()
5001
5002
5003@unittest.skipIf(not use_scandir, "only run if scandir is available")
5004class FakeScandirTest(FakeOsModuleTestBase):
5005    FILE_SIZE = 50
5006    LINKED_FILE_SIZE = 10
5007
5008    def setUp(self):
5009        super(FakeScandirTest, self).setUp()
5010        self.supports_symlinks = not self.is_windows or not self.use_real_fs()
5011
5012        if use_scandir_package:
5013            if self.use_real_fs():
5014                from scandir import scandir
5015            else:
5016                import pyfakefs.fake_scandir
5017
5018                def fake_scan_dir(p):
5019                    return pyfakefs.fake_scandir.scandir(self.filesystem, p)
5020
5021                scandir = fake_scan_dir
5022        else:
5023            scandir = self.os.scandir
5024        self.scandir = scandir
5025
5026        self.directory = self.make_path("xyzzy", "plugh")
5027        link_dir = self.make_path("linked", "plugh")
5028        self.linked_file_path = self.os.path.join(link_dir, "file")
5029        self.linked_dir_path = self.os.path.join(link_dir, "dir")
5030        self.rel_linked_dir_path = self.os.path.join(
5031            "..", "..", "linked", "plugh", "dir"
5032        )
5033        self.rel_linked_file_path = self.os.path.join(
5034            "..", "..", "linked", "plugh", "file"
5035        )
5036        self.dir_path = self.os.path.join(self.directory, "dir")
5037        self.file_path = self.os.path.join(self.directory, "file")
5038        self.file_link_path = self.os.path.join(self.directory, "link_file")
5039        self.dir_link_path = self.os.path.join(self.directory, "link_dir")
5040        self.file_rel_link_path = self.os.path.join(self.directory, "rel_link_file")
5041        self.dir_rel_link_path = self.os.path.join(self.directory, "rel_link_dir")
5042
5043        self.create_dir(self.dir_path)
5044        self.create_file(self.file_path, contents=b"b" * self.FILE_SIZE)
5045        if self.supports_symlinks:
5046            self.create_dir(self.linked_dir_path)
5047            self.create_file(
5048                self.linked_file_path, contents=b"a" * self.LINKED_FILE_SIZE
5049            ),
5050            self.create_symlink(self.dir_link_path, self.linked_dir_path)
5051            self.create_symlink(self.file_link_path, self.linked_file_path)
5052            self.create_symlink(self.dir_rel_link_path, self.rel_linked_dir_path)
5053            self.create_symlink(self.file_rel_link_path, self.rel_linked_file_path)
5054
5055        # Changing the working directory below is to make sure relative paths
5056        # to the files and directories created above are reasonable.
5057        # Corner-cases about relative paths are better checked in tests created
5058        # for that purpose.
5059        #
5060        # WARNING: This is self.pretest_cwd and not self.cwd as the latter is
5061        # used by superclass RealFsTestCase.
5062        self.pretest_cwd = self.os.getcwd()
5063        self.os.chdir(self.base_path)
5064
5065        self.dir_entries = list(self.do_scandir())
5066        self.dir_entries.sort(key=lambda entry: entry.name)
5067
5068    def tearDown(self):
5069        self.os.chdir(self.pretest_cwd)
5070        super().tearDown()
5071
5072    def do_scandir(self):
5073        """Hook to override how scandir is called."""
5074        return self.scandir(self.directory)
5075
5076    def scandir_path(self):
5077        """Hook to override the expected scandir() path in DirEntry.path."""
5078        return self.directory
5079
5080    def test_paths(self):
5081        sorted_names = ["dir", "file"]
5082        if self.supports_symlinks:
5083            sorted_names.extend(
5084                ["link_dir", "link_file", "rel_link_dir", "rel_link_file"]
5085            )
5086
5087        self.assertEqual(len(sorted_names), len(self.dir_entries))
5088        self.assertEqual(sorted_names, [entry.name for entry in self.dir_entries])
5089        sorted_paths = [
5090            self.os.path.join(self.scandir_path(), name) for name in sorted_names
5091        ]
5092        self.assertEqual(sorted_paths, [entry.path for entry in self.dir_entries])
5093
5094    def test_isfile(self):
5095        self.assertFalse(self.dir_entries[0].is_file())
5096        self.assertTrue(self.dir_entries[1].is_file())
5097        if self.supports_symlinks:
5098            self.assertFalse(self.dir_entries[2].is_file())
5099            self.assertFalse(self.dir_entries[2].is_file(follow_symlinks=False))
5100            self.assertTrue(self.dir_entries[3].is_file())
5101            self.assertFalse(self.dir_entries[3].is_file(follow_symlinks=False))
5102            self.assertFalse(self.dir_entries[4].is_file())
5103            self.assertFalse(self.dir_entries[4].is_file(follow_symlinks=False))
5104            self.assertTrue(self.dir_entries[5].is_file())
5105            self.assertFalse(self.dir_entries[5].is_file(follow_symlinks=False))
5106
5107    def test_isdir(self):
5108        self.assertTrue(self.dir_entries[0].is_dir())
5109        self.assertFalse(self.dir_entries[1].is_dir())
5110        if self.supports_symlinks:
5111            self.assertTrue(self.dir_entries[2].is_dir())
5112            self.assertFalse(self.dir_entries[2].is_dir(follow_symlinks=False))
5113            self.assertFalse(self.dir_entries[3].is_dir())
5114            self.assertFalse(self.dir_entries[3].is_dir(follow_symlinks=False))
5115            self.assertTrue(self.dir_entries[4].is_dir())
5116            self.assertFalse(self.dir_entries[4].is_dir(follow_symlinks=False))
5117            self.assertFalse(self.dir_entries[5].is_dir())
5118            self.assertFalse(self.dir_entries[5].is_dir(follow_symlinks=False))
5119
5120    def test_is_link(self):
5121        if self.supports_symlinks:
5122            self.assertFalse(self.dir_entries[0].is_symlink())
5123            self.assertFalse(self.dir_entries[1].is_symlink())
5124            self.assertTrue(self.dir_entries[2].is_symlink())
5125            self.assertTrue(self.dir_entries[3].is_symlink())
5126            self.assertTrue(self.dir_entries[4].is_symlink())
5127            self.assertTrue(self.dir_entries[5].is_symlink())
5128
5129    def test_path_links_not_resolved(self):
5130        # regression test for #350
5131        self.skip_if_symlink_not_supported()
5132        dir_path = self.make_path("A", "B", "C")
5133        self.os.makedirs(self.os.path.join(dir_path, "D"))
5134        link_path = self.make_path("A", "C")
5135        self.os.symlink(dir_path, link_path)
5136        self.assertEqual(
5137            [self.os.path.join(link_path, "D")],
5138            [f.path for f in self.scandir(link_path)],
5139        )
5140
5141    def test_inode(self):
5142        if use_scandir and self.use_real_fs():
5143            if self.is_windows:
5144                self.skipTest("inode seems not to work in scandir module under Windows")
5145            if IN_DOCKER:
5146                self.skipTest("inode seems not to work in a Docker container")
5147        self.assertEqual(
5148            self.os.stat(self.dir_path).st_ino, self.dir_entries[0].inode()
5149        )
5150        self.assertEqual(
5151            self.os.stat(self.file_path).st_ino, self.dir_entries[1].inode()
5152        )
5153        if self.supports_symlinks:
5154            self.assertEqual(
5155                self.os.lstat(self.dir_link_path).st_ino,
5156                self.dir_entries[2].inode(),
5157            )
5158            self.assertEqual(
5159                self.os.lstat(self.file_link_path).st_ino,
5160                self.dir_entries[3].inode(),
5161            )
5162            self.assertEqual(
5163                self.os.lstat(self.dir_rel_link_path).st_ino,
5164                self.dir_entries[4].inode(),
5165            )
5166            self.assertEqual(
5167                self.os.lstat(self.file_rel_link_path).st_ino,
5168                self.dir_entries[5].inode(),
5169            )
5170
5171    def test_scandir_stat_nlink(self):
5172        # regression test for #350
5173        stat_nlink = self.os.stat(self.file_path).st_nlink
5174        self.assertEqual(1, stat_nlink)
5175        dir_iter = self.scandir(self.directory)
5176        for item in dir_iter:
5177            if item.path == self.file_path:
5178                scandir_stat_nlink = item.stat().st_nlink
5179                if self.is_windows_fs:
5180                    self.assertEqual(0, scandir_stat_nlink)
5181                else:
5182                    self.assertEqual(1, scandir_stat_nlink)
5183                self.assertEqual(1, self.os.stat(self.file_path).st_nlink)
5184
5185    @unittest.skipIf(not hasattr(os, "O_DIRECTORY"), "opening directory not supported")
5186    @unittest.skipIf(sys.version_info < (3, 7), "fd not supported for scandir")
5187    def test_scandir_with_fd(self):
5188        # regression test for #723
5189        temp_dir = self.make_path("tmp", "dir")
5190        self.create_dir(temp_dir)
5191        self.create_file(self.os.path.join(temp_dir, "file1"))
5192        self.create_file(self.os.path.join(temp_dir, "file2"))
5193        self.create_dir(self.os.path.join(temp_dir, "subdir"))
5194        self.os.chdir(temp_dir)
5195        fd = self.os.open(temp_dir, flags=os.O_RDONLY | os.O_DIRECTORY)
5196        children = [dir_entry.name for dir_entry in self.os.scandir(fd)]
5197        assert sorted(children) == ["file1", "file2", "subdir"]
5198
5199    def check_stat(
5200        self, absolute_symlink_expected_size, relative_symlink_expected_size
5201    ):
5202        self.assertEqual(self.FILE_SIZE, self.dir_entries[1].stat().st_size)
5203        if not self.is_windows_fs or sys.version_info < (3, 12):
5204            # behavior of st_ctime changed in 3.12, to be adapted later
5205            self.assertEqual(
5206                int(self.os.stat(self.dir_path).st_ctime),
5207                int(self.dir_entries[0].stat().st_ctime),
5208            )
5209
5210        if self.supports_symlinks:
5211            self.assertEqual(self.LINKED_FILE_SIZE, self.dir_entries[3].stat().st_size)
5212            self.assertEqual(
5213                absolute_symlink_expected_size,
5214                self.dir_entries[3].stat(follow_symlinks=False).st_size,
5215            )
5216            self.assertEqual(
5217                int(self.os.stat(self.linked_dir_path).st_mtime),
5218                int(self.dir_entries[2].stat().st_mtime),
5219            )
5220            self.assertEqual(self.LINKED_FILE_SIZE, self.dir_entries[5].stat().st_size)
5221            self.assertEqual(
5222                relative_symlink_expected_size,
5223                self.dir_entries[5].stat(follow_symlinks=False).st_size,
5224            )
5225            self.assertEqual(
5226                int(self.os.stat(self.linked_dir_path).st_mtime),
5227                int(self.dir_entries[4].stat().st_mtime),
5228            )
5229
5230    @unittest.skipIf(TestCase.is_windows, "POSIX specific behavior")
5231    def test_stat_posix(self):
5232        self.check_stat(len(self.linked_file_path), len(self.rel_linked_file_path))
5233
5234    @unittest.skipIf(not TestCase.is_windows, "Windows specific behavior")
5235    def test_stat_windows(self):
5236        self.check_stat(0, 0)
5237
5238    def test_index_access_to_stat_times_returns_int(self):
5239        if not self.is_windows_fs or sys.version_info < (3, 12):
5240            # behavior of st_ctime changed in 3.12, to be adapted later
5241            self.assertEqual(
5242                self.os.stat(self.dir_path)[stat.ST_CTIME],
5243                int(self.dir_entries[0].stat().st_ctime),
5244            )
5245        if self.supports_symlinks:
5246            self.assertEqual(
5247                self.os.stat(self.linked_dir_path)[stat.ST_MTIME],
5248                int(self.dir_entries[2].stat().st_mtime),
5249            )
5250            self.assertEqual(
5251                self.os.stat(self.linked_dir_path)[stat.ST_MTIME],
5252                int(self.dir_entries[4].stat().st_mtime),
5253            )
5254
5255    def test_stat_ino_dev(self):
5256        if self.supports_symlinks:
5257            file_stat = self.os.stat(self.linked_file_path)
5258            self.assertEqual(file_stat.st_ino, self.dir_entries[3].stat().st_ino)
5259            self.assertEqual(file_stat.st_dev, self.dir_entries[3].stat().st_dev)
5260            self.assertEqual(file_stat.st_ino, self.dir_entries[5].stat().st_ino)
5261            self.assertEqual(file_stat.st_dev, self.dir_entries[5].stat().st_dev)
5262
5263    @unittest.skipIf(
5264        sys.version_info < (3, 6) or not use_builtin_scandir,
5265        "Path-like objects have been introduced in Python 3.6",
5266    )
5267    def test_path_like(self):
5268        self.assertTrue(isinstance(self.dir_entries[0], os.PathLike))
5269        self.assertEqual(
5270            self.os.path.join(self.scandir_path(), "dir"),
5271            os.fspath(self.dir_entries[0]),
5272        )
5273        self.assertEqual(
5274            self.os.path.join(self.scandir_path(), "file"),
5275            os.fspath(self.dir_entries[1]),
5276        )
5277
5278    def test_non_existing_dir(self):
5279        # behaves differently in different systems, so we skip the real fs test
5280        self.skip_real_fs()
5281        self.assert_raises_os_error(errno.ENOENT, self.scandir, "non_existing/fake_dir")
5282
5283
5284class RealScandirTest(FakeScandirTest):
5285    def use_real_fs(self):
5286        return True
5287
5288
5289class FakeScandirRelTest(FakeScandirTest):
5290    def scandir_path(self):
5291        # When scandir is called with a relative path, that relative path is
5292        # used in the path attribute of the DirEntry objects.
5293        return self.os.path.relpath(self.directory)
5294
5295    def do_scandir(self):
5296        return self.scandir(self.os.path.relpath(self.directory))
5297
5298
5299class RealScandirRelTest(FakeScandirRelTest):
5300    def use_real_fs(self):
5301        return True
5302
5303
5304@unittest.skipIf(TestCase.is_windows, "dir_fd not supported for os.scandir in Windows")
5305@unittest.skipIf(use_scandir_package, "no dir_fd support for scandir package")
5306class FakeScandirFdTest(FakeScandirTest):
5307    def tearDown(self):
5308        self.os.close(self.dir_fd)
5309        super(FakeScandirFdTest, self).tearDown()
5310
5311    def scandir_path(self):
5312        # When scandir is called with a filedescriptor, only the name of the
5313        # entry is returned in the path attribute of the DirEntry objects.
5314        return ""
5315
5316    def do_scandir(self):
5317        self.dir_fd = self.os.open(self.directory, os.O_RDONLY)
5318        return self.scandir(self.dir_fd)
5319
5320
5321class RealScandirFdTest(FakeScandirFdTest):
5322    def use_real_fs(self):
5323        return True
5324
5325
5326class FakeScandirFdRelTest(FakeScandirFdTest):
5327    def do_scandir(self):
5328        self.dir_fd = self.os.open(self.os.path.relpath(self.directory), os.O_RDONLY)
5329        return self.scandir(self.dir_fd)
5330
5331
5332class RealScandirFdRelTest(FakeScandirFdRelTest):
5333    def use_real_fs(self):
5334        return True
5335
5336
5337class FakeExtendedAttributeTest(FakeOsModuleTestBase):
5338    def setUp(self):
5339        super(FakeExtendedAttributeTest, self).setUp()
5340        self.check_linux_only()
5341        self.dir_path = self.make_path("foo")
5342        self.file_path = self.os.path.join(self.dir_path, "bar")
5343        self.create_file(self.file_path)
5344
5345    def test_empty_xattr(self):
5346        self.assertEqual([], self.os.listxattr(self.dir_path))
5347        self.assertEqual([], self.os.listxattr(self.file_path))
5348
5349    def test_setxattr(self):
5350        self.assertRaises(TypeError, self.os.setxattr, self.file_path, "test", "value")
5351        self.assert_raises_os_error(
5352            errno.EEXIST,
5353            self.os.setxattr,
5354            self.file_path,
5355            "test",
5356            b"value",
5357            self.os.XATTR_REPLACE,
5358        )
5359        self.os.setxattr(self.file_path, "test", b"value")
5360        self.assertEqual(b"value", self.os.getxattr(self.file_path, "test"))
5361        self.assert_raises_os_error(
5362            errno.ENODATA,
5363            self.os.setxattr,
5364            self.file_path,
5365            "test",
5366            b"value",
5367            self.os.XATTR_CREATE,
5368        )
5369
5370    def test_removeattr(self):
5371        self.os.removexattr(self.file_path, "test")
5372        self.assertEqual([], self.os.listxattr(self.file_path))
5373        self.os.setxattr(self.file_path, b"test", b"value")
5374        self.assertEqual(["test"], self.os.listxattr(self.file_path))
5375        self.assertEqual(b"value", self.os.getxattr(self.file_path, "test"))
5376        self.os.removexattr(self.file_path, "test")
5377        self.assertEqual([], self.os.listxattr(self.file_path))
5378        self.assertIsNone(self.os.getxattr(self.file_path, "test"))
5379
5380    def test_default_path(self):
5381        self.os.chdir(self.dir_path)
5382        self.os.setxattr(self.dir_path, b"test", b"value")
5383        self.assertEqual(["test"], self.os.listxattr())
5384        self.assertEqual(b"value", self.os.getxattr(self.dir_path, "test"))
5385
5386
5387class FakeOsUnreadableDirTest(FakeOsModuleTestBase):
5388    def setUp(self):
5389        if self.use_real_fs():
5390            # unreadable dirs in Windows are only simulated
5391            # and cannot be created in the real OS using file system
5392            # functions only
5393            self.check_posix_only()
5394        super(FakeOsUnreadableDirTest, self).setUp()
5395        self.dir_path = self.make_path("some_dir")
5396        self.file_path = self.os.path.join(self.dir_path, "some_file")
5397        self.create_file(self.file_path)
5398        self.chmod(self.dir_path, 0o000)
5399
5400    def chmod(self, path, mode):
5401        if self.is_windows_fs:
5402            self.filesystem.chmod(path, mode, force_unix_mode=True)
5403        else:
5404            self.os.chmod(path, mode)
5405
5406    def test_getuid(self):
5407        self.skip_real_fs()  # won't change user in real fs
5408        self.check_posix_only()
5409        uid = self.os.getuid()
5410        set_uid(uid + 10)
5411        self.assertEqual(uid + 10, self.os.getuid())
5412        self.assertEqual(uid + 10, get_uid())
5413        set_uid(uid)
5414        self.assertEqual(uid, self.os.getuid())
5415
5416    def test_getgid(self):
5417        self.skip_real_fs()  # won't change group in real fs
5418        self.check_posix_only()
5419        gid = self.os.getgid()
5420        set_gid(gid + 10)
5421        self.assertEqual(gid + 10, self.os.getgid())
5422        self.assertEqual(gid + 10, get_gid())
5423        set_gid(gid)
5424        self.assertEqual(gid, self.os.getgid())
5425
5426    def test_listdir_unreadable_dir(self):
5427        if not is_root():
5428            self.assert_raises_os_error(errno.EACCES, self.os.listdir, self.dir_path)
5429        else:
5430            self.assertEqual(["some_file"], self.os.listdir(self.dir_path))
5431
5432    def test_listdir_user_readable_dir(self):
5433        self.chmod(self.dir_path, 0o600)
5434        self.assertEqual(["some_file"], self.os.listdir(self.dir_path))
5435        self.chmod(self.dir_path, 0o000)
5436
5437    def test_listdir_user_readable_dir_from_other_user(self):
5438        self.skip_real_fs()  # won't change user in real fs
5439        self.check_posix_only()
5440        user_id = get_uid()
5441        set_uid(user_id + 1)
5442        dir_path = self.make_path("dir1")
5443        self.create_dir(dir_path, perm=0o600)
5444        self.assertTrue(self.os.path.exists(dir_path))
5445        set_uid(user_id)
5446        if not is_root():
5447            with self.assertRaises(PermissionError):
5448                self.os.listdir(dir_path)
5449        else:
5450            self.assertEqual(["some_file"], self.os.listdir(self.dir_path))
5451
5452    def test_listdir_group_readable_dir_from_other_user(self):
5453        self.skip_real_fs()  # won't change user in real fs
5454        user_id = get_uid()
5455        set_uid(user_id + 1)
5456        dir_path = self.make_path("dir1")
5457        self.create_dir(dir_path, perm=0o660)
5458        self.assertTrue(self.os.path.exists(dir_path))
5459        set_uid(user_id)
5460        self.assertEqual([], self.os.listdir(dir_path))
5461
5462    def test_listdir_group_readable_dir_from_other_group(self):
5463        self.skip_real_fs()  # won't change user in real fs
5464        self.check_posix_only()
5465        group_id = self.os.getgid()
5466        set_gid(group_id + 1)
5467        dir_path = self.make_path("dir1")
5468        self.create_dir(dir_path, perm=0o060)
5469        self.assertTrue(self.os.path.exists(dir_path))
5470        set_gid(group_id)
5471        if not is_root():
5472            with self.assertRaises(PermissionError):
5473                self.os.listdir(dir_path)
5474        else:
5475            self.assertEqual([], self.os.listdir(dir_path))
5476
5477    def test_listdir_other_readable_dir_from_other_group(self):
5478        self.skip_real_fs()  # won't change user in real fs
5479        group_id = get_gid()
5480        set_gid(group_id + 1)
5481        dir_path = self.make_path("dir1")
5482        self.create_dir(dir_path, perm=0o004)
5483        self.assertTrue(self.os.path.exists(dir_path))
5484        set_gid(group_id)
5485        self.assertEqual([], self.os.listdir(dir_path))
5486
5487    def test_stat_unreadable_dir(self):
5488        self.assertEqual(0, self.os.stat(self.dir_path).st_mode & 0o666)
5489
5490    def test_chmod_unreadable_dir(self):
5491        self.chmod(self.dir_path, 0o666)
5492        self.assertEqual(0o666, self.os.stat(self.dir_path).st_mode & 0o666)
5493        self.chmod(self.dir_path, 0o000)
5494        self.assertEqual(0, self.os.stat(self.dir_path).st_mode & 0o666)
5495
5496    def test_stat_file_in_unreadable_dir(self):
5497        if not is_root():
5498            self.assert_raises_os_error(errno.EACCES, self.os.stat, self.file_path)
5499        else:
5500            self.assertEqual(0, self.os.stat(self.file_path).st_size)
5501
5502    def test_remove_unreadable_dir(self):
5503        self.check_posix_only()
5504        dir_path = self.make_path("dir1")
5505        self.create_dir(dir_path, perm=0o000)
5506        self.assertTrue(self.os.path.exists(dir_path))
5507        self.os.rmdir(dir_path)
5508        self.assertFalse(self.os.path.exists(dir_path))
5509
5510    def test_remove_unreadable_dir_from_other_user(self):
5511        self.skip_real_fs()  # won't change user in real fs
5512        user_id = get_uid()
5513        set_uid(user_id + 1)
5514        dir_path = self.make_path("dir1")
5515        self.create_dir(dir_path, perm=0o000)
5516        self.assertTrue(self.os.path.exists(dir_path))
5517        set_uid(user_id)
5518        if not is_root():
5519            with self.assertRaises(PermissionError):
5520                self.os.rmdir(dir_path)
5521            self.assertTrue(self.os.path.exists(dir_path))
5522        else:
5523            self.os.rmdir(dir_path)
5524            self.assertFalse(self.os.path.exists(dir_path))
5525
5526
5527class RealOsUnreadableDirTest(FakeOsUnreadableDirTest):
5528    def use_real_fs(self):
5529        return True
5530