1import os
2import posixpath
3import sys
4import unittest
5from posixpath import realpath, abspath, dirname, basename
6from test import test_genericpath
7from test.support import import_helper
8from test.support import os_helper
9from test.support.os_helper import FakePath
10from unittest import mock
11
12try:
13    import posix
14except ImportError:
15    posix = None
16
17
18# An absolute path to a temporary filename for testing. We can't rely on TESTFN
19# being an absolute path, so we need this.
20
21ABSTFN = abspath(os_helper.TESTFN)
22
23def skip_if_ABSTFN_contains_backslash(test):
24    """
25    On Windows, posixpath.abspath still returns paths with backslashes
26    instead of posix forward slashes. If this is the case, several tests
27    fail, so skip them.
28    """
29    found_backslash = '\\' in ABSTFN
30    msg = "ABSTFN is not a posix path - tests fail"
31    return [test, unittest.skip(msg)(test)][found_backslash]
32
33def safe_rmdir(dirname):
34    try:
35        os.rmdir(dirname)
36    except OSError:
37        pass
38
39class PosixPathTest(unittest.TestCase):
40
41    def setUp(self):
42        self.tearDown()
43
44    def tearDown(self):
45        for suffix in ["", "1", "2"]:
46            os_helper.unlink(os_helper.TESTFN + suffix)
47            safe_rmdir(os_helper.TESTFN + suffix)
48
49    def test_join(self):
50        self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"),
51                         "/bar/baz")
52        self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz")
53        self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"),
54                         "/foo/bar/baz/")
55
56        self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"),
57                         b"/bar/baz")
58        self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"),
59                         b"/foo/bar/baz")
60        self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"),
61                         b"/foo/bar/baz/")
62
63    def test_split(self):
64        self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar"))
65        self.assertEqual(posixpath.split("/"), ("/", ""))
66        self.assertEqual(posixpath.split("foo"), ("", "foo"))
67        self.assertEqual(posixpath.split("////foo"), ("////", "foo"))
68        self.assertEqual(posixpath.split("//foo//bar"), ("//foo", "bar"))
69
70        self.assertEqual(posixpath.split(b"/foo/bar"), (b"/foo", b"bar"))
71        self.assertEqual(posixpath.split(b"/"), (b"/", b""))
72        self.assertEqual(posixpath.split(b"foo"), (b"", b"foo"))
73        self.assertEqual(posixpath.split(b"////foo"), (b"////", b"foo"))
74        self.assertEqual(posixpath.split(b"//foo//bar"), (b"//foo", b"bar"))
75
76    def splitextTest(self, path, filename, ext):
77        self.assertEqual(posixpath.splitext(path), (filename, ext))
78        self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext))
79        self.assertEqual(posixpath.splitext("abc/" + path),
80                         ("abc/" + filename, ext))
81        self.assertEqual(posixpath.splitext("abc.def/" + path),
82                         ("abc.def/" + filename, ext))
83        self.assertEqual(posixpath.splitext("/abc.def/" + path),
84                         ("/abc.def/" + filename, ext))
85        self.assertEqual(posixpath.splitext(path + "/"),
86                         (filename + ext + "/", ""))
87
88        path = bytes(path, "ASCII")
89        filename = bytes(filename, "ASCII")
90        ext = bytes(ext, "ASCII")
91
92        self.assertEqual(posixpath.splitext(path), (filename, ext))
93        self.assertEqual(posixpath.splitext(b"/" + path),
94                         (b"/" + filename, ext))
95        self.assertEqual(posixpath.splitext(b"abc/" + path),
96                         (b"abc/" + filename, ext))
97        self.assertEqual(posixpath.splitext(b"abc.def/" + path),
98                         (b"abc.def/" + filename, ext))
99        self.assertEqual(posixpath.splitext(b"/abc.def/" + path),
100                         (b"/abc.def/" + filename, ext))
101        self.assertEqual(posixpath.splitext(path + b"/"),
102                         (filename + ext + b"/", b""))
103
104    def test_splitext(self):
105        self.splitextTest("foo.bar", "foo", ".bar")
106        self.splitextTest("foo.boo.bar", "foo.boo", ".bar")
107        self.splitextTest("foo.boo.biff.bar", "foo.boo.biff", ".bar")
108        self.splitextTest(".csh.rc", ".csh", ".rc")
109        self.splitextTest("nodots", "nodots", "")
110        self.splitextTest(".cshrc", ".cshrc", "")
111        self.splitextTest("...manydots", "...manydots", "")
112        self.splitextTest("...manydots.ext", "...manydots", ".ext")
113        self.splitextTest(".", ".", "")
114        self.splitextTest("..", "..", "")
115        self.splitextTest("........", "........", "")
116        self.splitextTest("", "", "")
117
118    def test_isabs(self):
119        self.assertIs(posixpath.isabs(""), False)
120        self.assertIs(posixpath.isabs("/"), True)
121        self.assertIs(posixpath.isabs("/foo"), True)
122        self.assertIs(posixpath.isabs("/foo/bar"), True)
123        self.assertIs(posixpath.isabs("foo/bar"), False)
124
125        self.assertIs(posixpath.isabs(b""), False)
126        self.assertIs(posixpath.isabs(b"/"), True)
127        self.assertIs(posixpath.isabs(b"/foo"), True)
128        self.assertIs(posixpath.isabs(b"/foo/bar"), True)
129        self.assertIs(posixpath.isabs(b"foo/bar"), False)
130
131    def test_basename(self):
132        self.assertEqual(posixpath.basename("/foo/bar"), "bar")
133        self.assertEqual(posixpath.basename("/"), "")
134        self.assertEqual(posixpath.basename("foo"), "foo")
135        self.assertEqual(posixpath.basename("////foo"), "foo")
136        self.assertEqual(posixpath.basename("//foo//bar"), "bar")
137
138        self.assertEqual(posixpath.basename(b"/foo/bar"), b"bar")
139        self.assertEqual(posixpath.basename(b"/"), b"")
140        self.assertEqual(posixpath.basename(b"foo"), b"foo")
141        self.assertEqual(posixpath.basename(b"////foo"), b"foo")
142        self.assertEqual(posixpath.basename(b"//foo//bar"), b"bar")
143
144    def test_dirname(self):
145        self.assertEqual(posixpath.dirname("/foo/bar"), "/foo")
146        self.assertEqual(posixpath.dirname("/"), "/")
147        self.assertEqual(posixpath.dirname("foo"), "")
148        self.assertEqual(posixpath.dirname("////foo"), "////")
149        self.assertEqual(posixpath.dirname("//foo//bar"), "//foo")
150
151        self.assertEqual(posixpath.dirname(b"/foo/bar"), b"/foo")
152        self.assertEqual(posixpath.dirname(b"/"), b"/")
153        self.assertEqual(posixpath.dirname(b"foo"), b"")
154        self.assertEqual(posixpath.dirname(b"////foo"), b"////")
155        self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo")
156
157    def test_islink(self):
158        self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False)
159        self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), False)
160
161        with open(os_helper.TESTFN + "1", "wb") as f:
162            f.write(b"foo")
163        self.assertIs(posixpath.islink(os_helper.TESTFN + "1"), False)
164
165        if os_helper.can_symlink():
166            os.symlink(os_helper.TESTFN + "1", os_helper.TESTFN + "2")
167            self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True)
168            os.remove(os_helper.TESTFN + "1")
169            self.assertIs(posixpath.islink(os_helper.TESTFN + "2"), True)
170            self.assertIs(posixpath.exists(os_helper.TESTFN + "2"), False)
171            self.assertIs(posixpath.lexists(os_helper.TESTFN + "2"), True)
172
173        self.assertIs(posixpath.islink(os_helper.TESTFN + "\udfff"), False)
174        self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\xff"), False)
175        self.assertIs(posixpath.islink(os_helper.TESTFN + "\x00"), False)
176        self.assertIs(posixpath.islink(os.fsencode(os_helper.TESTFN) + b"\x00"), False)
177
178    def test_ismount(self):
179        self.assertIs(posixpath.ismount("/"), True)
180        self.assertIs(posixpath.ismount(b"/"), True)
181        self.assertIs(posixpath.ismount(FakePath("/")), True)
182        self.assertIs(posixpath.ismount(FakePath(b"/")), True)
183
184    def test_ismount_non_existent(self):
185        # Non-existent mountpoint.
186        self.assertIs(posixpath.ismount(ABSTFN), False)
187        try:
188            os.mkdir(ABSTFN)
189            self.assertIs(posixpath.ismount(ABSTFN), False)
190        finally:
191            safe_rmdir(ABSTFN)
192
193        self.assertIs(posixpath.ismount('/\udfff'), False)
194        self.assertIs(posixpath.ismount(b'/\xff'), False)
195        self.assertIs(posixpath.ismount('/\x00'), False)
196        self.assertIs(posixpath.ismount(b'/\x00'), False)
197
198    @os_helper.skip_unless_symlink
199    def test_ismount_symlinks(self):
200        # Symlinks are never mountpoints.
201        try:
202            os.symlink("/", ABSTFN)
203            self.assertIs(posixpath.ismount(ABSTFN), False)
204        finally:
205            os.unlink(ABSTFN)
206
207    @unittest.skipIf(posix is None, "Test requires posix module")
208    def test_ismount_different_device(self):
209        # Simulate the path being on a different device from its parent by
210        # mocking out st_dev.
211        save_lstat = os.lstat
212        def fake_lstat(path):
213            st_ino = 0
214            st_dev = 0
215            if path == ABSTFN:
216                st_dev = 1
217                st_ino = 1
218            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
219        try:
220            os.lstat = fake_lstat
221            self.assertIs(posixpath.ismount(ABSTFN), True)
222        finally:
223            os.lstat = save_lstat
224
225    @unittest.skipIf(posix is None, "Test requires posix module")
226    def test_ismount_directory_not_readable(self):
227        # issue #2466: Simulate ismount run on a directory that is not
228        # readable, which used to return False.
229        save_lstat = os.lstat
230        def fake_lstat(path):
231            st_ino = 0
232            st_dev = 0
233            if path.startswith(ABSTFN) and path != ABSTFN:
234                # ismount tries to read something inside the ABSTFN directory;
235                # simulate this being forbidden (no read permission).
236                raise OSError("Fake [Errno 13] Permission denied")
237            if path == ABSTFN:
238                st_dev = 1
239                st_ino = 1
240            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
241        try:
242            os.lstat = fake_lstat
243            self.assertIs(posixpath.ismount(ABSTFN), True)
244        finally:
245            os.lstat = save_lstat
246
247    def test_expanduser(self):
248        self.assertEqual(posixpath.expanduser("foo"), "foo")
249        self.assertEqual(posixpath.expanduser(b"foo"), b"foo")
250
251    def test_expanduser_home_envvar(self):
252        with os_helper.EnvironmentVarGuard() as env:
253            env['HOME'] = '/home/victor'
254            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
255
256            # expanduser() strips trailing slash
257            env['HOME'] = '/home/victor/'
258            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
259
260            for home in '/', '', '//', '///':
261                with self.subTest(home=home):
262                    env['HOME'] = home
263                    self.assertEqual(posixpath.expanduser("~"), "/")
264                    self.assertEqual(posixpath.expanduser("~/"), "/")
265                    self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
266
267    @unittest.skipIf(sys.platform == "vxworks",
268                     "no home directory on VxWorks")
269    def test_expanduser_pwd(self):
270        pwd = import_helper.import_module('pwd')
271
272        self.assertIsInstance(posixpath.expanduser("~/"), str)
273        self.assertIsInstance(posixpath.expanduser(b"~/"), bytes)
274
275        # if home directory == root directory, this test makes no sense
276        if posixpath.expanduser("~") != '/':
277            self.assertEqual(
278                posixpath.expanduser("~") + "/",
279                posixpath.expanduser("~/")
280            )
281            self.assertEqual(
282                posixpath.expanduser(b"~") + b"/",
283                posixpath.expanduser(b"~/")
284            )
285        self.assertIsInstance(posixpath.expanduser("~root/"), str)
286        self.assertIsInstance(posixpath.expanduser("~foo/"), str)
287        self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes)
288        self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes)
289
290        with os_helper.EnvironmentVarGuard() as env:
291            # expanduser should fall back to using the password database
292            del env['HOME']
293
294            home = pwd.getpwuid(os.getuid()).pw_dir
295            # $HOME can end with a trailing /, so strip it (see #17809)
296            home = home.rstrip("/") or '/'
297            self.assertEqual(posixpath.expanduser("~"), home)
298
299            # bpo-10496: If the HOME environment variable is not set and the
300            # user (current identifier or name in the path) doesn't exist in
301            # the password database (pwd.getuid() or pwd.getpwnam() fail),
302            # expanduser() must return the path unchanged.
303            with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \
304                 mock.patch.object(pwd, 'getpwnam', side_effect=KeyError):
305                for path in ('~', '~/.local', '~vstinner/'):
306                    self.assertEqual(posixpath.expanduser(path), path)
307
308    NORMPATH_CASES = [
309        ("", "."),
310        ("/", "/"),
311        ("/.", "/"),
312        ("/./", "/"),
313        ("/.//.", "/"),
314        ("/foo", "/foo"),
315        ("/foo/bar", "/foo/bar"),
316        ("//", "//"),
317        ("///", "/"),
318        ("///foo/.//bar//", "/foo/bar"),
319        ("///foo/.//bar//.//..//.//baz///", "/foo/baz"),
320        ("///..//./foo/.//bar", "/foo/bar"),
321        (".", "."),
322        (".//.", "."),
323        ("..", ".."),
324        ("../", ".."),
325        ("../foo", "../foo"),
326        ("../../foo", "../../foo"),
327        ("../foo/../bar", "../bar"),
328        ("../../foo/../bar/./baz/boom/..", "../../bar/baz"),
329        ("/..", "/"),
330        ("/..", "/"),
331        ("/../", "/"),
332        ("/..//", "/"),
333        ("//.", "//"),
334        ("//..", "//"),
335        ("//...", "//..."),
336        ("//../foo", "//foo"),
337        ("//../../foo", "//foo"),
338        ("/../foo", "/foo"),
339        ("/../../foo", "/foo"),
340        ("/../foo/../", "/"),
341        ("/../foo/../bar", "/bar"),
342        ("/../../foo/../bar/./baz/boom/..", "/bar/baz"),
343        ("/../../foo/../bar/./baz/boom/.", "/bar/baz/boom"),
344        ("foo/../bar/baz", "bar/baz"),
345        ("foo/../../bar/baz", "../bar/baz"),
346        ("foo/../../../bar/baz", "../../bar/baz"),
347        ("foo///../bar/.././../baz/boom", "../baz/boom"),
348        ("foo/bar/../..///../../baz/boom", "../../baz/boom"),
349        ("/foo/..", "/"),
350        ("/foo/../..", "/"),
351        ("//foo/..", "//"),
352        ("//foo/../..", "//"),
353        ("///foo/..", "/"),
354        ("///foo/../..", "/"),
355        ("////foo/..", "/"),
356        ("/////foo/..", "/"),
357    ]
358
359    def test_normpath(self):
360        for path, expected in self.NORMPATH_CASES:
361            with self.subTest(path):
362                result = posixpath.normpath(path)
363                self.assertEqual(result, expected)
364
365            path = path.encode('utf-8')
366            expected = expected.encode('utf-8')
367            with self.subTest(path, type=bytes):
368                result = posixpath.normpath(path)
369                self.assertEqual(result, expected)
370
371    @skip_if_ABSTFN_contains_backslash
372    def test_realpath_curdir(self):
373        self.assertEqual(realpath('.'), os.getcwd())
374        self.assertEqual(realpath('./.'), os.getcwd())
375        self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
376
377        self.assertEqual(realpath(b'.'), os.getcwdb())
378        self.assertEqual(realpath(b'./.'), os.getcwdb())
379        self.assertEqual(realpath(b'/'.join([b'.'] * 100)), os.getcwdb())
380
381    @skip_if_ABSTFN_contains_backslash
382    def test_realpath_pardir(self):
383        self.assertEqual(realpath('..'), dirname(os.getcwd()))
384        self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
385        self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
386
387        self.assertEqual(realpath(b'..'), dirname(os.getcwdb()))
388        self.assertEqual(realpath(b'../..'), dirname(dirname(os.getcwdb())))
389        self.assertEqual(realpath(b'/'.join([b'..'] * 100)), b'/')
390
391    @os_helper.skip_unless_symlink
392    @skip_if_ABSTFN_contains_backslash
393    def test_realpath_basic(self):
394        # Basic operation.
395        try:
396            os.symlink(ABSTFN+"1", ABSTFN)
397            self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
398        finally:
399            os_helper.unlink(ABSTFN)
400
401    @os_helper.skip_unless_symlink
402    @skip_if_ABSTFN_contains_backslash
403    def test_realpath_strict(self):
404        # Bug #43757: raise FileNotFoundError in strict mode if we encounter
405        # a path that does not exist.
406        try:
407            os.symlink(ABSTFN+"1", ABSTFN)
408            self.assertRaises(FileNotFoundError, realpath, ABSTFN, strict=True)
409            self.assertRaises(FileNotFoundError, realpath, ABSTFN + "2", strict=True)
410        finally:
411            os_helper.unlink(ABSTFN)
412
413    @os_helper.skip_unless_symlink
414    @skip_if_ABSTFN_contains_backslash
415    def test_realpath_relative(self):
416        try:
417            os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN)
418            self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
419        finally:
420            os_helper.unlink(ABSTFN)
421
422    @os_helper.skip_unless_symlink
423    @skip_if_ABSTFN_contains_backslash
424    def test_realpath_symlink_loops(self):
425        # Bug #930024, return the path unchanged if we get into an infinite
426        # symlink loop in non-strict mode (default).
427        try:
428            os.symlink(ABSTFN, ABSTFN)
429            self.assertEqual(realpath(ABSTFN), ABSTFN)
430
431            os.symlink(ABSTFN+"1", ABSTFN+"2")
432            os.symlink(ABSTFN+"2", ABSTFN+"1")
433            self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1")
434            self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2")
435
436            self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x")
437            self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN))
438            self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
439            os.symlink(ABSTFN+"x", ABSTFN+"y")
440            self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
441                             ABSTFN + "y")
442            self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
443                             ABSTFN + "1")
444
445            os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
446            self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b")
447
448            os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
449                       basename(ABSTFN) + "c", ABSTFN+"c")
450            self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
451
452            # Test using relative path as well.
453            with os_helper.change_cwd(dirname(ABSTFN)):
454                self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
455        finally:
456            os_helper.unlink(ABSTFN)
457            os_helper.unlink(ABSTFN+"1")
458            os_helper.unlink(ABSTFN+"2")
459            os_helper.unlink(ABSTFN+"y")
460            os_helper.unlink(ABSTFN+"c")
461            os_helper.unlink(ABSTFN+"a")
462
463    @os_helper.skip_unless_symlink
464    @skip_if_ABSTFN_contains_backslash
465    def test_realpath_symlink_loops_strict(self):
466        # Bug #43757, raise OSError if we get into an infinite symlink loop in
467        # strict mode.
468        try:
469            os.symlink(ABSTFN, ABSTFN)
470            self.assertRaises(OSError, realpath, ABSTFN, strict=True)
471
472            os.symlink(ABSTFN+"1", ABSTFN+"2")
473            os.symlink(ABSTFN+"2", ABSTFN+"1")
474            self.assertRaises(OSError, realpath, ABSTFN+"1", strict=True)
475            self.assertRaises(OSError, realpath, ABSTFN+"2", strict=True)
476
477            self.assertRaises(OSError, realpath, ABSTFN+"1/x", strict=True)
478            self.assertRaises(OSError, realpath, ABSTFN+"1/..", strict=True)
479            self.assertRaises(OSError, realpath, ABSTFN+"1/../x", strict=True)
480            os.symlink(ABSTFN+"x", ABSTFN+"y")
481            self.assertRaises(OSError, realpath,
482                              ABSTFN+"1/../" + basename(ABSTFN) + "y", strict=True)
483            self.assertRaises(OSError, realpath,
484                              ABSTFN+"1/../" + basename(ABSTFN) + "1", strict=True)
485
486            os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
487            self.assertRaises(OSError, realpath, ABSTFN+"a", strict=True)
488
489            os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
490                       basename(ABSTFN) + "c", ABSTFN+"c")
491            self.assertRaises(OSError, realpath, ABSTFN+"c", strict=True)
492
493            # Test using relative path as well.
494            with os_helper.change_cwd(dirname(ABSTFN)):
495                self.assertRaises(OSError, realpath, basename(ABSTFN), strict=True)
496        finally:
497            os_helper.unlink(ABSTFN)
498            os_helper.unlink(ABSTFN+"1")
499            os_helper.unlink(ABSTFN+"2")
500            os_helper.unlink(ABSTFN+"y")
501            os_helper.unlink(ABSTFN+"c")
502            os_helper.unlink(ABSTFN+"a")
503
504    @os_helper.skip_unless_symlink
505    @skip_if_ABSTFN_contains_backslash
506    def test_realpath_repeated_indirect_symlinks(self):
507        # Issue #6975.
508        try:
509            os.mkdir(ABSTFN)
510            os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
511            os.symlink('self/self/self', ABSTFN + '/link')
512            self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
513        finally:
514            os_helper.unlink(ABSTFN + '/self')
515            os_helper.unlink(ABSTFN + '/link')
516            safe_rmdir(ABSTFN)
517
518    @os_helper.skip_unless_symlink
519    @skip_if_ABSTFN_contains_backslash
520    def test_realpath_deep_recursion(self):
521        depth = 10
522        try:
523            os.mkdir(ABSTFN)
524            for i in range(depth):
525                os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
526            os.symlink('.', ABSTFN + '/0')
527            self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
528
529            # Test using relative path as well.
530            with os_helper.change_cwd(ABSTFN):
531                self.assertEqual(realpath('%d' % depth), ABSTFN)
532        finally:
533            for i in range(depth + 1):
534                os_helper.unlink(ABSTFN + '/%d' % i)
535            safe_rmdir(ABSTFN)
536
537    @os_helper.skip_unless_symlink
538    @skip_if_ABSTFN_contains_backslash
539    def test_realpath_resolve_parents(self):
540        # We also need to resolve any symlinks in the parents of a relative
541        # path passed to realpath. E.g.: current working directory is
542        # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
543        # realpath("a"). This should return /usr/share/doc/a/.
544        try:
545            os.mkdir(ABSTFN)
546            os.mkdir(ABSTFN + "/y")
547            os.symlink(ABSTFN + "/y", ABSTFN + "/k")
548
549            with os_helper.change_cwd(ABSTFN + "/k"):
550                self.assertEqual(realpath("a"), ABSTFN + "/y/a")
551        finally:
552            os_helper.unlink(ABSTFN + "/k")
553            safe_rmdir(ABSTFN + "/y")
554            safe_rmdir(ABSTFN)
555
556    @os_helper.skip_unless_symlink
557    @skip_if_ABSTFN_contains_backslash
558    def test_realpath_resolve_before_normalizing(self):
559        # Bug #990669: Symbolic links should be resolved before we
560        # normalize the path. E.g.: if we have directories 'a', 'k' and 'y'
561        # in the following hierarchy:
562        # a/k/y
563        #
564        # and a symbolic link 'link-y' pointing to 'y' in directory 'a',
565        # then realpath("link-y/..") should return 'k', not 'a'.
566        try:
567            os.mkdir(ABSTFN)
568            os.mkdir(ABSTFN + "/k")
569            os.mkdir(ABSTFN + "/k/y")
570            os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y")
571
572            # Absolute path.
573            self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
574            # Relative path.
575            with os_helper.change_cwd(dirname(ABSTFN)):
576                self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
577                                 ABSTFN + "/k")
578        finally:
579            os_helper.unlink(ABSTFN + "/link-y")
580            safe_rmdir(ABSTFN + "/k/y")
581            safe_rmdir(ABSTFN + "/k")
582            safe_rmdir(ABSTFN)
583
584    @os_helper.skip_unless_symlink
585    @skip_if_ABSTFN_contains_backslash
586    def test_realpath_resolve_first(self):
587        # Bug #1213894: The first component of the path, if not absolute,
588        # must be resolved too.
589
590        try:
591            os.mkdir(ABSTFN)
592            os.mkdir(ABSTFN + "/k")
593            os.symlink(ABSTFN, ABSTFN + "link")
594            with os_helper.change_cwd(dirname(ABSTFN)):
595                base = basename(ABSTFN)
596                self.assertEqual(realpath(base + "link"), ABSTFN)
597                self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
598        finally:
599            os_helper.unlink(ABSTFN + "link")
600            safe_rmdir(ABSTFN + "/k")
601            safe_rmdir(ABSTFN)
602
603    def test_relpath(self):
604        (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
605        try:
606            curdir = os.path.split(os.getcwd())[-1]
607            self.assertRaises(ValueError, posixpath.relpath, "")
608            self.assertEqual(posixpath.relpath("a"), "a")
609            self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a")
610            self.assertEqual(posixpath.relpath("a/b"), "a/b")
611            self.assertEqual(posixpath.relpath("../a/b"), "../a/b")
612            self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a")
613            self.assertEqual(posixpath.relpath("a/b", "../c"),
614                             "../"+curdir+"/a/b")
615            self.assertEqual(posixpath.relpath("a", "b/c"), "../../a")
616            self.assertEqual(posixpath.relpath("a", "a"), ".")
617            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x/y/z"), '../../../foo/bar/bat')
618            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/foo/bar"), 'bat')
619            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/"), 'foo/bar/bat')
620            self.assertEqual(posixpath.relpath("/", "/foo/bar/bat"), '../../..')
621            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x"), '../foo/bar/bat')
622            self.assertEqual(posixpath.relpath("/x", "/foo/bar/bat"), '../../../x')
623            self.assertEqual(posixpath.relpath("/", "/"), '.')
624            self.assertEqual(posixpath.relpath("/a", "/a"), '.')
625            self.assertEqual(posixpath.relpath("/a/b", "/a/b"), '.')
626        finally:
627            os.getcwd = real_getcwd
628
629    def test_relpath_bytes(self):
630        (real_getcwdb, os.getcwdb) = (os.getcwdb, lambda: br"/home/user/bar")
631        try:
632            curdir = os.path.split(os.getcwdb())[-1]
633            self.assertRaises(ValueError, posixpath.relpath, b"")
634            self.assertEqual(posixpath.relpath(b"a"), b"a")
635            self.assertEqual(posixpath.relpath(posixpath.abspath(b"a")), b"a")
636            self.assertEqual(posixpath.relpath(b"a/b"), b"a/b")
637            self.assertEqual(posixpath.relpath(b"../a/b"), b"../a/b")
638            self.assertEqual(posixpath.relpath(b"a", b"../b"),
639                             b"../"+curdir+b"/a")
640            self.assertEqual(posixpath.relpath(b"a/b", b"../c"),
641                             b"../"+curdir+b"/a/b")
642            self.assertEqual(posixpath.relpath(b"a", b"b/c"), b"../../a")
643            self.assertEqual(posixpath.relpath(b"a", b"a"), b".")
644            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x/y/z"), b'../../../foo/bar/bat')
645            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/foo/bar"), b'bat')
646            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/"), b'foo/bar/bat')
647            self.assertEqual(posixpath.relpath(b"/", b"/foo/bar/bat"), b'../../..')
648            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x"), b'../foo/bar/bat')
649            self.assertEqual(posixpath.relpath(b"/x", b"/foo/bar/bat"), b'../../../x')
650            self.assertEqual(posixpath.relpath(b"/", b"/"), b'.')
651            self.assertEqual(posixpath.relpath(b"/a", b"/a"), b'.')
652            self.assertEqual(posixpath.relpath(b"/a/b", b"/a/b"), b'.')
653
654            self.assertRaises(TypeError, posixpath.relpath, b"bytes", "str")
655            self.assertRaises(TypeError, posixpath.relpath, "str", b"bytes")
656        finally:
657            os.getcwdb = real_getcwdb
658
659    def test_commonpath(self):
660        def check(paths, expected):
661            self.assertEqual(posixpath.commonpath(paths), expected)
662            self.assertEqual(posixpath.commonpath([os.fsencode(p) for p in paths]),
663                             os.fsencode(expected))
664        def check_error(exc, paths):
665            self.assertRaises(exc, posixpath.commonpath, paths)
666            self.assertRaises(exc, posixpath.commonpath,
667                              [os.fsencode(p) for p in paths])
668
669        self.assertRaises(ValueError, posixpath.commonpath, [])
670        check_error(ValueError, ['/usr', 'usr'])
671        check_error(ValueError, ['usr', '/usr'])
672
673        check(['/usr/local'], '/usr/local')
674        check(['/usr/local', '/usr/local'], '/usr/local')
675        check(['/usr/local/', '/usr/local'], '/usr/local')
676        check(['/usr/local/', '/usr/local/'], '/usr/local')
677        check(['/usr//local', '//usr/local'], '/usr/local')
678        check(['/usr/./local', '/./usr/local'], '/usr/local')
679        check(['/', '/dev'], '/')
680        check(['/usr', '/dev'], '/')
681        check(['/usr/lib/', '/usr/lib/python3'], '/usr/lib')
682        check(['/usr/lib/', '/usr/lib64/'], '/usr')
683
684        check(['/usr/lib', '/usr/lib64'], '/usr')
685        check(['/usr/lib/', '/usr/lib64'], '/usr')
686
687        check(['spam'], 'spam')
688        check(['spam', 'spam'], 'spam')
689        check(['spam', 'alot'], '')
690        check(['and/jam', 'and/spam'], 'and')
691        check(['and//jam', 'and/spam//'], 'and')
692        check(['and/./jam', './and/spam'], 'and')
693        check(['and/jam', 'and/spam', 'alot'], '')
694        check(['and/jam', 'and/spam', 'and'], 'and')
695
696        check([''], '')
697        check(['', 'spam/alot'], '')
698        check_error(ValueError, ['', '/spam/alot'])
699
700        self.assertRaises(TypeError, posixpath.commonpath,
701                          [b'/usr/lib/', '/usr/lib/python3'])
702        self.assertRaises(TypeError, posixpath.commonpath,
703                          [b'/usr/lib/', 'usr/lib/python3'])
704        self.assertRaises(TypeError, posixpath.commonpath,
705                          [b'usr/lib/', '/usr/lib/python3'])
706        self.assertRaises(TypeError, posixpath.commonpath,
707                          ['/usr/lib/', b'/usr/lib/python3'])
708        self.assertRaises(TypeError, posixpath.commonpath,
709                          ['/usr/lib/', b'usr/lib/python3'])
710        self.assertRaises(TypeError, posixpath.commonpath,
711                          ['usr/lib/', b'/usr/lib/python3'])
712
713
714class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase):
715    pathmodule = posixpath
716    attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
717
718
719class PathLikeTests(unittest.TestCase):
720
721    path = posixpath
722
723    def setUp(self):
724        self.file_name = os_helper.TESTFN
725        self.file_path = FakePath(os_helper.TESTFN)
726        self.addCleanup(os_helper.unlink, self.file_name)
727        with open(self.file_name, 'xb', 0) as file:
728            file.write(b"test_posixpath.PathLikeTests")
729
730    def assertPathEqual(self, func):
731        self.assertEqual(func(self.file_path), func(self.file_name))
732
733    def test_path_normcase(self):
734        self.assertPathEqual(self.path.normcase)
735
736    def test_path_isabs(self):
737        self.assertPathEqual(self.path.isabs)
738
739    def test_path_join(self):
740        self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
741                         self.path.join('a', 'b', 'c'))
742
743    def test_path_split(self):
744        self.assertPathEqual(self.path.split)
745
746    def test_path_splitext(self):
747        self.assertPathEqual(self.path.splitext)
748
749    def test_path_splitdrive(self):
750        self.assertPathEqual(self.path.splitdrive)
751
752    def test_path_basename(self):
753        self.assertPathEqual(self.path.basename)
754
755    def test_path_dirname(self):
756        self.assertPathEqual(self.path.dirname)
757
758    def test_path_islink(self):
759        self.assertPathEqual(self.path.islink)
760
761    def test_path_lexists(self):
762        self.assertPathEqual(self.path.lexists)
763
764    def test_path_ismount(self):
765        self.assertPathEqual(self.path.ismount)
766
767    def test_path_expanduser(self):
768        self.assertPathEqual(self.path.expanduser)
769
770    def test_path_expandvars(self):
771        self.assertPathEqual(self.path.expandvars)
772
773    def test_path_normpath(self):
774        self.assertPathEqual(self.path.normpath)
775
776    def test_path_abspath(self):
777        self.assertPathEqual(self.path.abspath)
778
779    def test_path_realpath(self):
780        self.assertPathEqual(self.path.realpath)
781
782    def test_path_relpath(self):
783        self.assertPathEqual(self.path.relpath)
784
785    def test_path_commonpath(self):
786        common_path = self.path.commonpath([self.file_path, self.file_name])
787        self.assertEqual(common_path, self.file_name)
788
789
790if __name__=="__main__":
791    unittest.main()
792