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