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