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
182    def test_ismount_non_existent(self):
183        # Non-existent mountpoint.
184        self.assertIs(posixpath.ismount(ABSTFN), False)
185        try:
186            os.mkdir(ABSTFN)
187            self.assertIs(posixpath.ismount(ABSTFN), False)
188        finally:
189            safe_rmdir(ABSTFN)
190
191        self.assertIs(posixpath.ismount('/\udfff'), False)
192        self.assertIs(posixpath.ismount(b'/\xff'), False)
193        self.assertIs(posixpath.ismount('/\x00'), False)
194        self.assertIs(posixpath.ismount(b'/\x00'), False)
195
196    @unittest.skipUnless(os_helper.can_symlink(),
197                         "Test requires symlink support")
198    def test_ismount_symlinks(self):
199        # Symlinks are never mountpoints.
200        try:
201            os.symlink("/", ABSTFN)
202            self.assertIs(posixpath.ismount(ABSTFN), False)
203        finally:
204            os.unlink(ABSTFN)
205
206    @unittest.skipIf(posix is None, "Test requires posix module")
207    def test_ismount_different_device(self):
208        # Simulate the path being on a different device from its parent by
209        # mocking out st_dev.
210        save_lstat = os.lstat
211        def fake_lstat(path):
212            st_ino = 0
213            st_dev = 0
214            if path == ABSTFN:
215                st_dev = 1
216                st_ino = 1
217            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
218        try:
219            os.lstat = fake_lstat
220            self.assertIs(posixpath.ismount(ABSTFN), True)
221        finally:
222            os.lstat = save_lstat
223
224    @unittest.skipIf(posix is None, "Test requires posix module")
225    def test_ismount_directory_not_readable(self):
226        # issue #2466: Simulate ismount run on a directory that is not
227        # readable, which used to return False.
228        save_lstat = os.lstat
229        def fake_lstat(path):
230            st_ino = 0
231            st_dev = 0
232            if path.startswith(ABSTFN) and path != ABSTFN:
233                # ismount tries to read something inside the ABSTFN directory;
234                # simulate this being forbidden (no read permission).
235                raise OSError("Fake [Errno 13] Permission denied")
236            if path == ABSTFN:
237                st_dev = 1
238                st_ino = 1
239            return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0))
240        try:
241            os.lstat = fake_lstat
242            self.assertIs(posixpath.ismount(ABSTFN), True)
243        finally:
244            os.lstat = save_lstat
245
246    def test_expanduser(self):
247        self.assertEqual(posixpath.expanduser("foo"), "foo")
248        self.assertEqual(posixpath.expanduser(b"foo"), b"foo")
249
250    def test_expanduser_home_envvar(self):
251        with os_helper.EnvironmentVarGuard() as env:
252            env['HOME'] = '/home/victor'
253            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
254
255            # expanduser() strips trailing slash
256            env['HOME'] = '/home/victor/'
257            self.assertEqual(posixpath.expanduser("~"), "/home/victor")
258
259            for home in '/', '', '//', '///':
260                with self.subTest(home=home):
261                    env['HOME'] = home
262                    self.assertEqual(posixpath.expanduser("~"), "/")
263                    self.assertEqual(posixpath.expanduser("~/"), "/")
264                    self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
265
266    @unittest.skipIf(sys.platform == "vxworks",
267                     "no home directory on VxWorks")
268    def test_expanduser_pwd(self):
269        pwd = import_helper.import_module('pwd')
270
271        self.assertIsInstance(posixpath.expanduser("~/"), str)
272        self.assertIsInstance(posixpath.expanduser(b"~/"), bytes)
273
274        # if home directory == root directory, this test makes no sense
275        if posixpath.expanduser("~") != '/':
276            self.assertEqual(
277                posixpath.expanduser("~") + "/",
278                posixpath.expanduser("~/")
279            )
280            self.assertEqual(
281                posixpath.expanduser(b"~") + b"/",
282                posixpath.expanduser(b"~/")
283            )
284        self.assertIsInstance(posixpath.expanduser("~root/"), str)
285        self.assertIsInstance(posixpath.expanduser("~foo/"), str)
286        self.assertIsInstance(posixpath.expanduser(b"~root/"), bytes)
287        self.assertIsInstance(posixpath.expanduser(b"~foo/"), bytes)
288
289        with os_helper.EnvironmentVarGuard() as env:
290            # expanduser should fall back to using the password database
291            del env['HOME']
292
293            home = pwd.getpwuid(os.getuid()).pw_dir
294            # $HOME can end with a trailing /, so strip it (see #17809)
295            home = home.rstrip("/") or '/'
296            self.assertEqual(posixpath.expanduser("~"), home)
297
298            # bpo-10496: If the HOME environment variable is not set and the
299            # user (current identifier or name in the path) doesn't exist in
300            # the password database (pwd.getuid() or pwd.getpwnam() fail),
301            # expanduser() must return the path unchanged.
302            with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError), \
303                 mock.patch.object(pwd, 'getpwnam', side_effect=KeyError):
304                for path in ('~', '~/.local', '~vstinner/'):
305                    self.assertEqual(posixpath.expanduser(path), path)
306
307    def test_normpath(self):
308        self.assertEqual(posixpath.normpath(""), ".")
309        self.assertEqual(posixpath.normpath("/"), "/")
310        self.assertEqual(posixpath.normpath("//"), "//")
311        self.assertEqual(posixpath.normpath("///"), "/")
312        self.assertEqual(posixpath.normpath("///foo/.//bar//"), "/foo/bar")
313        self.assertEqual(posixpath.normpath("///foo/.//bar//.//..//.//baz"),
314                         "/foo/baz")
315        self.assertEqual(posixpath.normpath("///..//./foo/.//bar"), "/foo/bar")
316
317        self.assertEqual(posixpath.normpath(b""), b".")
318        self.assertEqual(posixpath.normpath(b"/"), b"/")
319        self.assertEqual(posixpath.normpath(b"//"), b"//")
320        self.assertEqual(posixpath.normpath(b"///"), b"/")
321        self.assertEqual(posixpath.normpath(b"///foo/.//bar//"), b"/foo/bar")
322        self.assertEqual(posixpath.normpath(b"///foo/.//bar//.//..//.//baz"),
323                         b"/foo/baz")
324        self.assertEqual(posixpath.normpath(b"///..//./foo/.//bar"),
325                         b"/foo/bar")
326
327    @skip_if_ABSTFN_contains_backslash
328    def test_realpath_curdir(self):
329        self.assertEqual(realpath('.'), os.getcwd())
330        self.assertEqual(realpath('./.'), os.getcwd())
331        self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
332
333        self.assertEqual(realpath(b'.'), os.getcwdb())
334        self.assertEqual(realpath(b'./.'), os.getcwdb())
335        self.assertEqual(realpath(b'/'.join([b'.'] * 100)), os.getcwdb())
336
337    @skip_if_ABSTFN_contains_backslash
338    def test_realpath_pardir(self):
339        self.assertEqual(realpath('..'), dirname(os.getcwd()))
340        self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
341        self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
342
343        self.assertEqual(realpath(b'..'), dirname(os.getcwdb()))
344        self.assertEqual(realpath(b'../..'), dirname(dirname(os.getcwdb())))
345        self.assertEqual(realpath(b'/'.join([b'..'] * 100)), b'/')
346
347    @unittest.skipUnless(hasattr(os, "symlink"),
348                         "Missing symlink implementation")
349    @skip_if_ABSTFN_contains_backslash
350    def test_realpath_basic(self):
351        # Basic operation.
352        try:
353            os.symlink(ABSTFN+"1", ABSTFN)
354            self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
355        finally:
356            os_helper.unlink(ABSTFN)
357
358    @unittest.skipUnless(hasattr(os, "symlink"),
359                         "Missing symlink implementation")
360    @skip_if_ABSTFN_contains_backslash
361    def test_realpath_strict(self):
362        # Bug #43757: raise FileNotFoundError in strict mode if we encounter
363        # a path that does not exist.
364        try:
365            os.symlink(ABSTFN+"1", ABSTFN)
366            self.assertRaises(FileNotFoundError, realpath, ABSTFN, strict=True)
367            self.assertRaises(FileNotFoundError, realpath, ABSTFN + "2", strict=True)
368        finally:
369            os_helper.unlink(ABSTFN)
370
371    @unittest.skipUnless(hasattr(os, "symlink"),
372                         "Missing symlink implementation")
373    @skip_if_ABSTFN_contains_backslash
374    def test_realpath_relative(self):
375        try:
376            os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN)
377            self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
378        finally:
379            os_helper.unlink(ABSTFN)
380
381    @unittest.skipUnless(hasattr(os, "symlink"),
382                         "Missing symlink implementation")
383    @skip_if_ABSTFN_contains_backslash
384    def test_realpath_symlink_loops(self):
385        # Bug #930024, return the path unchanged if we get into an infinite
386        # symlink loop in non-strict mode (default).
387        try:
388            os.symlink(ABSTFN, ABSTFN)
389            self.assertEqual(realpath(ABSTFN), ABSTFN)
390
391            os.symlink(ABSTFN+"1", ABSTFN+"2")
392            os.symlink(ABSTFN+"2", ABSTFN+"1")
393            self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1")
394            self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2")
395
396            self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x")
397            self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN))
398            self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
399            os.symlink(ABSTFN+"x", ABSTFN+"y")
400            self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
401                             ABSTFN + "y")
402            self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
403                             ABSTFN + "1")
404
405            os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
406            self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b")
407
408            os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
409                       basename(ABSTFN) + "c", ABSTFN+"c")
410            self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
411
412            # Test using relative path as well.
413            with os_helper.change_cwd(dirname(ABSTFN)):
414                self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
415        finally:
416            os_helper.unlink(ABSTFN)
417            os_helper.unlink(ABSTFN+"1")
418            os_helper.unlink(ABSTFN+"2")
419            os_helper.unlink(ABSTFN+"y")
420            os_helper.unlink(ABSTFN+"c")
421            os_helper.unlink(ABSTFN+"a")
422
423    @unittest.skipUnless(hasattr(os, "symlink"),
424                         "Missing symlink implementation")
425    @skip_if_ABSTFN_contains_backslash
426    def test_realpath_symlink_loops_strict(self):
427        # Bug #43757, raise OSError if we get into an infinite symlink loop in
428        # strict mode.
429        try:
430            os.symlink(ABSTFN, ABSTFN)
431            self.assertRaises(OSError, realpath, ABSTFN, strict=True)
432
433            os.symlink(ABSTFN+"1", ABSTFN+"2")
434            os.symlink(ABSTFN+"2", ABSTFN+"1")
435            self.assertRaises(OSError, realpath, ABSTFN+"1", strict=True)
436            self.assertRaises(OSError, realpath, ABSTFN+"2", strict=True)
437
438            self.assertRaises(OSError, realpath, ABSTFN+"1/x", strict=True)
439            self.assertRaises(OSError, realpath, ABSTFN+"1/..", strict=True)
440            self.assertRaises(OSError, realpath, ABSTFN+"1/../x", strict=True)
441            os.symlink(ABSTFN+"x", ABSTFN+"y")
442            self.assertRaises(OSError, realpath,
443                              ABSTFN+"1/../" + basename(ABSTFN) + "y", strict=True)
444            self.assertRaises(OSError, realpath,
445                              ABSTFN+"1/../" + basename(ABSTFN) + "1", strict=True)
446
447            os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
448            self.assertRaises(OSError, realpath, ABSTFN+"a", strict=True)
449
450            os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
451                       basename(ABSTFN) + "c", ABSTFN+"c")
452            self.assertRaises(OSError, realpath, ABSTFN+"c", strict=True)
453
454            # Test using relative path as well.
455            with os_helper.change_cwd(dirname(ABSTFN)):
456                self.assertRaises(OSError, realpath, basename(ABSTFN), strict=True)
457        finally:
458            os_helper.unlink(ABSTFN)
459            os_helper.unlink(ABSTFN+"1")
460            os_helper.unlink(ABSTFN+"2")
461            os_helper.unlink(ABSTFN+"y")
462            os_helper.unlink(ABSTFN+"c")
463            os_helper.unlink(ABSTFN+"a")
464
465    @unittest.skipUnless(hasattr(os, "symlink"),
466                         "Missing symlink implementation")
467    @skip_if_ABSTFN_contains_backslash
468    def test_realpath_repeated_indirect_symlinks(self):
469        # Issue #6975.
470        try:
471            os.mkdir(ABSTFN)
472            os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
473            os.symlink('self/self/self', ABSTFN + '/link')
474            self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
475        finally:
476            os_helper.unlink(ABSTFN + '/self')
477            os_helper.unlink(ABSTFN + '/link')
478            safe_rmdir(ABSTFN)
479
480    @unittest.skipUnless(hasattr(os, "symlink"),
481                         "Missing symlink implementation")
482    @skip_if_ABSTFN_contains_backslash
483    def test_realpath_deep_recursion(self):
484        depth = 10
485        try:
486            os.mkdir(ABSTFN)
487            for i in range(depth):
488                os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
489            os.symlink('.', ABSTFN + '/0')
490            self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
491
492            # Test using relative path as well.
493            with os_helper.change_cwd(ABSTFN):
494                self.assertEqual(realpath('%d' % depth), ABSTFN)
495        finally:
496            for i in range(depth + 1):
497                os_helper.unlink(ABSTFN + '/%d' % i)
498            safe_rmdir(ABSTFN)
499
500    @unittest.skipUnless(hasattr(os, "symlink"),
501                         "Missing symlink implementation")
502    @skip_if_ABSTFN_contains_backslash
503    def test_realpath_resolve_parents(self):
504        # We also need to resolve any symlinks in the parents of a relative
505        # path passed to realpath. E.g.: current working directory is
506        # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
507        # realpath("a"). This should return /usr/share/doc/a/.
508        try:
509            os.mkdir(ABSTFN)
510            os.mkdir(ABSTFN + "/y")
511            os.symlink(ABSTFN + "/y", ABSTFN + "/k")
512
513            with os_helper.change_cwd(ABSTFN + "/k"):
514                self.assertEqual(realpath("a"), ABSTFN + "/y/a")
515        finally:
516            os_helper.unlink(ABSTFN + "/k")
517            safe_rmdir(ABSTFN + "/y")
518            safe_rmdir(ABSTFN)
519
520    @unittest.skipUnless(hasattr(os, "symlink"),
521                         "Missing symlink implementation")
522    @skip_if_ABSTFN_contains_backslash
523    def test_realpath_resolve_before_normalizing(self):
524        # Bug #990669: Symbolic links should be resolved before we
525        # normalize the path. E.g.: if we have directories 'a', 'k' and 'y'
526        # in the following hierarchy:
527        # a/k/y
528        #
529        # and a symbolic link 'link-y' pointing to 'y' in directory 'a',
530        # then realpath("link-y/..") should return 'k', not 'a'.
531        try:
532            os.mkdir(ABSTFN)
533            os.mkdir(ABSTFN + "/k")
534            os.mkdir(ABSTFN + "/k/y")
535            os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y")
536
537            # Absolute path.
538            self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
539            # Relative path.
540            with os_helper.change_cwd(dirname(ABSTFN)):
541                self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
542                                 ABSTFN + "/k")
543        finally:
544            os_helper.unlink(ABSTFN + "/link-y")
545            safe_rmdir(ABSTFN + "/k/y")
546            safe_rmdir(ABSTFN + "/k")
547            safe_rmdir(ABSTFN)
548
549    @unittest.skipUnless(hasattr(os, "symlink"),
550                         "Missing symlink implementation")
551    @skip_if_ABSTFN_contains_backslash
552    def test_realpath_resolve_first(self):
553        # Bug #1213894: The first component of the path, if not absolute,
554        # must be resolved too.
555
556        try:
557            os.mkdir(ABSTFN)
558            os.mkdir(ABSTFN + "/k")
559            os.symlink(ABSTFN, ABSTFN + "link")
560            with os_helper.change_cwd(dirname(ABSTFN)):
561                base = basename(ABSTFN)
562                self.assertEqual(realpath(base + "link"), ABSTFN)
563                self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
564        finally:
565            os_helper.unlink(ABSTFN + "link")
566            safe_rmdir(ABSTFN + "/k")
567            safe_rmdir(ABSTFN)
568
569    def test_relpath(self):
570        (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
571        try:
572            curdir = os.path.split(os.getcwd())[-1]
573            self.assertRaises(ValueError, posixpath.relpath, "")
574            self.assertEqual(posixpath.relpath("a"), "a")
575            self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a")
576            self.assertEqual(posixpath.relpath("a/b"), "a/b")
577            self.assertEqual(posixpath.relpath("../a/b"), "../a/b")
578            self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a")
579            self.assertEqual(posixpath.relpath("a/b", "../c"),
580                             "../"+curdir+"/a/b")
581            self.assertEqual(posixpath.relpath("a", "b/c"), "../../a")
582            self.assertEqual(posixpath.relpath("a", "a"), ".")
583            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x/y/z"), '../../../foo/bar/bat')
584            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/foo/bar"), 'bat')
585            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/"), 'foo/bar/bat')
586            self.assertEqual(posixpath.relpath("/", "/foo/bar/bat"), '../../..')
587            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x"), '../foo/bar/bat')
588            self.assertEqual(posixpath.relpath("/x", "/foo/bar/bat"), '../../../x')
589            self.assertEqual(posixpath.relpath("/", "/"), '.')
590            self.assertEqual(posixpath.relpath("/a", "/a"), '.')
591            self.assertEqual(posixpath.relpath("/a/b", "/a/b"), '.')
592        finally:
593            os.getcwd = real_getcwd
594
595    def test_relpath_bytes(self):
596        (real_getcwdb, os.getcwdb) = (os.getcwdb, lambda: br"/home/user/bar")
597        try:
598            curdir = os.path.split(os.getcwdb())[-1]
599            self.assertRaises(ValueError, posixpath.relpath, b"")
600            self.assertEqual(posixpath.relpath(b"a"), b"a")
601            self.assertEqual(posixpath.relpath(posixpath.abspath(b"a")), b"a")
602            self.assertEqual(posixpath.relpath(b"a/b"), b"a/b")
603            self.assertEqual(posixpath.relpath(b"../a/b"), b"../a/b")
604            self.assertEqual(posixpath.relpath(b"a", b"../b"),
605                             b"../"+curdir+b"/a")
606            self.assertEqual(posixpath.relpath(b"a/b", b"../c"),
607                             b"../"+curdir+b"/a/b")
608            self.assertEqual(posixpath.relpath(b"a", b"b/c"), b"../../a")
609            self.assertEqual(posixpath.relpath(b"a", b"a"), b".")
610            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x/y/z"), b'../../../foo/bar/bat')
611            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/foo/bar"), b'bat')
612            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/"), b'foo/bar/bat')
613            self.assertEqual(posixpath.relpath(b"/", b"/foo/bar/bat"), b'../../..')
614            self.assertEqual(posixpath.relpath(b"/foo/bar/bat", b"/x"), b'../foo/bar/bat')
615            self.assertEqual(posixpath.relpath(b"/x", b"/foo/bar/bat"), b'../../../x')
616            self.assertEqual(posixpath.relpath(b"/", b"/"), b'.')
617            self.assertEqual(posixpath.relpath(b"/a", b"/a"), b'.')
618            self.assertEqual(posixpath.relpath(b"/a/b", b"/a/b"), b'.')
619
620            self.assertRaises(TypeError, posixpath.relpath, b"bytes", "str")
621            self.assertRaises(TypeError, posixpath.relpath, "str", b"bytes")
622        finally:
623            os.getcwdb = real_getcwdb
624
625    def test_commonpath(self):
626        def check(paths, expected):
627            self.assertEqual(posixpath.commonpath(paths), expected)
628            self.assertEqual(posixpath.commonpath([os.fsencode(p) for p in paths]),
629                             os.fsencode(expected))
630        def check_error(exc, paths):
631            self.assertRaises(exc, posixpath.commonpath, paths)
632            self.assertRaises(exc, posixpath.commonpath,
633                              [os.fsencode(p) for p in paths])
634
635        self.assertRaises(ValueError, posixpath.commonpath, [])
636        check_error(ValueError, ['/usr', 'usr'])
637        check_error(ValueError, ['usr', '/usr'])
638
639        check(['/usr/local'], '/usr/local')
640        check(['/usr/local', '/usr/local'], '/usr/local')
641        check(['/usr/local/', '/usr/local'], '/usr/local')
642        check(['/usr/local/', '/usr/local/'], '/usr/local')
643        check(['/usr//local', '//usr/local'], '/usr/local')
644        check(['/usr/./local', '/./usr/local'], '/usr/local')
645        check(['/', '/dev'], '/')
646        check(['/usr', '/dev'], '/')
647        check(['/usr/lib/', '/usr/lib/python3'], '/usr/lib')
648        check(['/usr/lib/', '/usr/lib64/'], '/usr')
649
650        check(['/usr/lib', '/usr/lib64'], '/usr')
651        check(['/usr/lib/', '/usr/lib64'], '/usr')
652
653        check(['spam'], 'spam')
654        check(['spam', 'spam'], 'spam')
655        check(['spam', 'alot'], '')
656        check(['and/jam', 'and/spam'], 'and')
657        check(['and//jam', 'and/spam//'], 'and')
658        check(['and/./jam', './and/spam'], 'and')
659        check(['and/jam', 'and/spam', 'alot'], '')
660        check(['and/jam', 'and/spam', 'and'], 'and')
661
662        check([''], '')
663        check(['', 'spam/alot'], '')
664        check_error(ValueError, ['', '/spam/alot'])
665
666        self.assertRaises(TypeError, posixpath.commonpath,
667                          [b'/usr/lib/', '/usr/lib/python3'])
668        self.assertRaises(TypeError, posixpath.commonpath,
669                          [b'/usr/lib/', 'usr/lib/python3'])
670        self.assertRaises(TypeError, posixpath.commonpath,
671                          [b'usr/lib/', '/usr/lib/python3'])
672        self.assertRaises(TypeError, posixpath.commonpath,
673                          ['/usr/lib/', b'/usr/lib/python3'])
674        self.assertRaises(TypeError, posixpath.commonpath,
675                          ['/usr/lib/', b'usr/lib/python3'])
676        self.assertRaises(TypeError, posixpath.commonpath,
677                          ['usr/lib/', b'/usr/lib/python3'])
678
679
680class PosixCommonTest(test_genericpath.CommonTest, unittest.TestCase):
681    pathmodule = posixpath
682    attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
683
684
685class PathLikeTests(unittest.TestCase):
686
687    path = posixpath
688
689    def setUp(self):
690        self.file_name = os_helper.TESTFN
691        self.file_path = FakePath(os_helper.TESTFN)
692        self.addCleanup(os_helper.unlink, self.file_name)
693        with open(self.file_name, 'xb', 0) as file:
694            file.write(b"test_posixpath.PathLikeTests")
695
696    def assertPathEqual(self, func):
697        self.assertEqual(func(self.file_path), func(self.file_name))
698
699    def test_path_normcase(self):
700        self.assertPathEqual(self.path.normcase)
701
702    def test_path_isabs(self):
703        self.assertPathEqual(self.path.isabs)
704
705    def test_path_join(self):
706        self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
707                         self.path.join('a', 'b', 'c'))
708
709    def test_path_split(self):
710        self.assertPathEqual(self.path.split)
711
712    def test_path_splitext(self):
713        self.assertPathEqual(self.path.splitext)
714
715    def test_path_splitdrive(self):
716        self.assertPathEqual(self.path.splitdrive)
717
718    def test_path_basename(self):
719        self.assertPathEqual(self.path.basename)
720
721    def test_path_dirname(self):
722        self.assertPathEqual(self.path.dirname)
723
724    def test_path_islink(self):
725        self.assertPathEqual(self.path.islink)
726
727    def test_path_lexists(self):
728        self.assertPathEqual(self.path.lexists)
729
730    def test_path_ismount(self):
731        self.assertPathEqual(self.path.ismount)
732
733    def test_path_expanduser(self):
734        self.assertPathEqual(self.path.expanduser)
735
736    def test_path_expandvars(self):
737        self.assertPathEqual(self.path.expandvars)
738
739    def test_path_normpath(self):
740        self.assertPathEqual(self.path.normpath)
741
742    def test_path_abspath(self):
743        self.assertPathEqual(self.path.abspath)
744
745    def test_path_realpath(self):
746        self.assertPathEqual(self.path.realpath)
747
748    def test_path_relpath(self):
749        self.assertPathEqual(self.path.relpath)
750
751    def test_path_commonpath(self):
752        common_path = self.path.commonpath([self.file_path, self.file_name])
753        self.assertEqual(common_path, self.file_name)
754
755
756if __name__=="__main__":
757    unittest.main()
758