1import unittest
2from test import test_support, test_genericpath
3
4import posixpath, os
5from posixpath import realpath, abspath, dirname, basename
6
7# An absolute path to a temporary filename for testing. We can't rely on TESTFN
8# being an absolute path, so we need this.
9
10ABSTFN = abspath(test_support.TESTFN)
11
12def skip_if_ABSTFN_contains_backslash(test):
13    """
14    On Windows, posixpath.abspath still returns paths with backslashes
15    instead of posix forward slashes. If this is the case, several tests
16    fail, so skip them.
17    """
18    found_backslash = '\\' in ABSTFN
19    msg = "ABSTFN is not a posix path - tests fail"
20    return [test, unittest.skip(msg)(test)][found_backslash]
21
22def safe_rmdir(dirname):
23    try:
24        os.rmdir(dirname)
25    except OSError:
26        pass
27
28class PosixPathTest(unittest.TestCase):
29
30    def setUp(self):
31        self.tearDown()
32
33    def tearDown(self):
34        for suffix in ["", "1", "2"]:
35            test_support.unlink(test_support.TESTFN + suffix)
36            safe_rmdir(test_support.TESTFN + suffix)
37
38    def test_join(self):
39        self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), "/bar/baz")
40        self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz")
41        self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), "/foo/bar/baz/")
42
43    def test_split(self):
44        self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar"))
45        self.assertEqual(posixpath.split("/"), ("/", ""))
46        self.assertEqual(posixpath.split("foo"), ("", "foo"))
47        self.assertEqual(posixpath.split("////foo"), ("////", "foo"))
48        self.assertEqual(posixpath.split("//foo//bar"), ("//foo", "bar"))
49
50    def splitextTest(self, path, filename, ext):
51        self.assertEqual(posixpath.splitext(path), (filename, ext))
52        self.assertEqual(posixpath.splitext("/" + path), ("/" + filename, ext))
53        self.assertEqual(posixpath.splitext("abc/" + path), ("abc/" + filename, ext))
54        self.assertEqual(posixpath.splitext("abc.def/" + path), ("abc.def/" + filename, ext))
55        self.assertEqual(posixpath.splitext("/abc.def/" + path), ("/abc.def/" + filename, ext))
56        self.assertEqual(posixpath.splitext(path + "/"), (filename + ext + "/", ""))
57
58    def test_splitext(self):
59        self.splitextTest("foo.bar", "foo", ".bar")
60        self.splitextTest("foo.boo.bar", "foo.boo", ".bar")
61        self.splitextTest("foo.boo.biff.bar", "foo.boo.biff", ".bar")
62        self.splitextTest(".csh.rc", ".csh", ".rc")
63        self.splitextTest("nodots", "nodots", "")
64        self.splitextTest(".cshrc", ".cshrc", "")
65        self.splitextTest("...manydots", "...manydots", "")
66        self.splitextTest("...manydots.ext", "...manydots", ".ext")
67        self.splitextTest(".", ".", "")
68        self.splitextTest("..", "..", "")
69        self.splitextTest("........", "........", "")
70        self.splitextTest("", "", "")
71
72    def test_isabs(self):
73        self.assertIs(posixpath.isabs(""), False)
74        self.assertIs(posixpath.isabs("/"), True)
75        self.assertIs(posixpath.isabs("/foo"), True)
76        self.assertIs(posixpath.isabs("/foo/bar"), True)
77        self.assertIs(posixpath.isabs("foo/bar"), False)
78
79    def test_basename(self):
80        self.assertEqual(posixpath.basename("/foo/bar"), "bar")
81        self.assertEqual(posixpath.basename("/"), "")
82        self.assertEqual(posixpath.basename("foo"), "foo")
83        self.assertEqual(posixpath.basename("////foo"), "foo")
84        self.assertEqual(posixpath.basename("//foo//bar"), "bar")
85
86    def test_dirname(self):
87        self.assertEqual(posixpath.dirname("/foo/bar"), "/foo")
88        self.assertEqual(posixpath.dirname("/"), "/")
89        self.assertEqual(posixpath.dirname("foo"), "")
90        self.assertEqual(posixpath.dirname("////foo"), "////")
91        self.assertEqual(posixpath.dirname("//foo//bar"), "//foo")
92
93    def test_islink(self):
94        self.assertIs(posixpath.islink(test_support.TESTFN + "1"), False)
95        f = open(test_support.TESTFN + "1", "wb")
96        try:
97            f.write("foo")
98            f.close()
99            self.assertIs(posixpath.islink(test_support.TESTFN + "1"), False)
100            if hasattr(os, "symlink"):
101                os.symlink(test_support.TESTFN + "1", test_support.TESTFN + "2")
102                self.assertIs(posixpath.islink(test_support.TESTFN + "2"), True)
103                os.remove(test_support.TESTFN + "1")
104                self.assertIs(posixpath.islink(test_support.TESTFN + "2"), True)
105                self.assertIs(posixpath.exists(test_support.TESTFN + "2"), False)
106                self.assertIs(posixpath.lexists(test_support.TESTFN + "2"), True)
107        finally:
108            if not f.close():
109                f.close()
110
111    def test_samefile(self):
112        f = open(test_support.TESTFN + "1", "wb")
113        try:
114            f.write("foo")
115            f.close()
116            self.assertIs(
117                posixpath.samefile(
118                    test_support.TESTFN + "1",
119                    test_support.TESTFN + "1"
120                ),
121                True
122            )
123
124            # If we don't have links, assume that os.stat doesn't return
125            # reasonable inode information and thus, that samefile() doesn't
126            # work.
127            if hasattr(os, "symlink"):
128                os.symlink(
129                    test_support.TESTFN + "1",
130                    test_support.TESTFN + "2"
131                )
132                self.assertIs(
133                    posixpath.samefile(
134                        test_support.TESTFN + "1",
135                        test_support.TESTFN + "2"
136                    ),
137                    True
138                )
139                os.remove(test_support.TESTFN + "2")
140                f = open(test_support.TESTFN + "2", "wb")
141                f.write("bar")
142                f.close()
143                self.assertIs(
144                    posixpath.samefile(
145                        test_support.TESTFN + "1",
146                        test_support.TESTFN + "2"
147                    ),
148                    False
149                )
150        finally:
151            if not f.close():
152                f.close()
153
154    def test_samestat(self):
155        f = open(test_support.TESTFN + "1", "wb")
156        try:
157            f.write("foo")
158            f.close()
159            self.assertIs(
160                posixpath.samestat(
161                    os.stat(test_support.TESTFN + "1"),
162                    os.stat(test_support.TESTFN + "1")
163                ),
164                True
165            )
166            # If we don't have links, assume that os.stat() doesn't return
167            # reasonable inode information and thus, that samestat() doesn't
168            # work.
169            if hasattr(os, "symlink"):
170                os.symlink(test_support.TESTFN + "1", test_support.TESTFN + "2")
171                self.assertIs(
172                    posixpath.samestat(
173                        os.stat(test_support.TESTFN + "1"),
174                        os.stat(test_support.TESTFN + "2")
175                    ),
176                    True
177                )
178                os.remove(test_support.TESTFN + "2")
179                f = open(test_support.TESTFN + "2", "wb")
180                f.write("bar")
181                f.close()
182                self.assertIs(
183                    posixpath.samestat(
184                        os.stat(test_support.TESTFN + "1"),
185                        os.stat(test_support.TESTFN + "2")
186                    ),
187                    False
188                )
189        finally:
190            if not f.close():
191                f.close()
192
193    def test_ismount(self):
194        self.assertIs(posixpath.ismount("/"), True)
195
196    def test_expanduser(self):
197        self.assertEqual(posixpath.expanduser("foo"), "foo")
198        try:
199            import pwd
200        except ImportError:
201            pass
202        else:
203            self.assertIsInstance(posixpath.expanduser("~/"), basestring)
204            # if home directory == root directory, this test makes no sense
205            if posixpath.expanduser("~") != '/':
206                self.assertEqual(
207                    posixpath.expanduser("~") + "/",
208                    posixpath.expanduser("~/")
209                )
210            self.assertIsInstance(posixpath.expanduser("~root/"), basestring)
211            self.assertIsInstance(posixpath.expanduser("~foo/"), basestring)
212
213            with test_support.EnvironmentVarGuard() as env:
214                env['HOME'] = '/'
215                self.assertEqual(posixpath.expanduser("~"), "/")
216                self.assertEqual(posixpath.expanduser("~/foo"), "/foo")
217
218    def test_normpath(self):
219        self.assertEqual(posixpath.normpath(""), ".")
220        self.assertEqual(posixpath.normpath("/"), "/")
221        self.assertEqual(posixpath.normpath("//"), "//")
222        self.assertEqual(posixpath.normpath("///"), "/")
223        self.assertEqual(posixpath.normpath("///foo/.//bar//"), "/foo/bar")
224        self.assertEqual(posixpath.normpath("///foo/.//bar//.//..//.//baz"), "/foo/baz")
225        self.assertEqual(posixpath.normpath("///..//./foo/.//bar"), "/foo/bar")
226
227    @skip_if_ABSTFN_contains_backslash
228    def test_realpath_curdir(self):
229        self.assertEqual(realpath('.'), os.getcwd())
230        self.assertEqual(realpath('./.'), os.getcwd())
231        self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd())
232
233    @skip_if_ABSTFN_contains_backslash
234    def test_realpath_pardir(self):
235        self.assertEqual(realpath('..'), dirname(os.getcwd()))
236        self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd())))
237        self.assertEqual(realpath('/'.join(['..'] * 100)), '/')
238
239    if hasattr(os, "symlink"):
240        def test_realpath_basic(self):
241            # Basic operation.
242            try:
243                os.symlink(ABSTFN+"1", ABSTFN)
244                self.assertEqual(realpath(ABSTFN), ABSTFN+"1")
245            finally:
246                test_support.unlink(ABSTFN)
247
248        def test_realpath_symlink_loops(self):
249            # Bug #930024, return the path unchanged if we get into an infinite
250            # symlink loop.
251            try:
252                old_path = abspath('.')
253                os.symlink(ABSTFN, ABSTFN)
254                self.assertEqual(realpath(ABSTFN), ABSTFN)
255
256                os.symlink(ABSTFN+"1", ABSTFN+"2")
257                os.symlink(ABSTFN+"2", ABSTFN+"1")
258                self.assertEqual(realpath(ABSTFN+"1"), ABSTFN+"1")
259                self.assertEqual(realpath(ABSTFN+"2"), ABSTFN+"2")
260
261                self.assertEqual(realpath(ABSTFN+"1/x"), ABSTFN+"1/x")
262                self.assertEqual(realpath(ABSTFN+"1/.."), dirname(ABSTFN))
263                self.assertEqual(realpath(ABSTFN+"1/../x"), dirname(ABSTFN) + "/x")
264                os.symlink(ABSTFN+"x", ABSTFN+"y")
265                self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "y"),
266                                ABSTFN + "y")
267                self.assertEqual(realpath(ABSTFN+"1/../" + basename(ABSTFN) + "1"),
268                                ABSTFN + "1")
269
270                os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a")
271                self.assertEqual(realpath(ABSTFN+"a"), ABSTFN+"a/b")
272
273                os.symlink("../" + basename(dirname(ABSTFN)) + "/" +
274                        basename(ABSTFN) + "c", ABSTFN+"c")
275                self.assertEqual(realpath(ABSTFN+"c"), ABSTFN+"c")
276
277                # Test using relative path as well.
278                os.chdir(dirname(ABSTFN))
279                self.assertEqual(realpath(basename(ABSTFN)), ABSTFN)
280            finally:
281                os.chdir(old_path)
282                test_support.unlink(ABSTFN)
283                test_support.unlink(ABSTFN+"1")
284                test_support.unlink(ABSTFN+"2")
285                test_support.unlink(ABSTFN+"y")
286                test_support.unlink(ABSTFN+"c")
287                test_support.unlink(ABSTFN+"a")
288
289        def test_realpath_repeated_indirect_symlinks(self):
290            # Issue #6975.
291            try:
292                os.mkdir(ABSTFN)
293                os.symlink('../' + basename(ABSTFN), ABSTFN + '/self')
294                os.symlink('self/self/self', ABSTFN + '/link')
295                self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN)
296            finally:
297                test_support.unlink(ABSTFN + '/self')
298                test_support.unlink(ABSTFN + '/link')
299                safe_rmdir(ABSTFN)
300
301        def test_realpath_deep_recursion(self):
302            depth = 10
303            old_path = abspath('.')
304            try:
305                os.mkdir(ABSTFN)
306                for i in range(depth):
307                    os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1))
308                os.symlink('.', ABSTFN + '/0')
309                self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN)
310
311                # Test using relative path as well.
312                os.chdir(ABSTFN)
313                self.assertEqual(realpath('%d' % depth), ABSTFN)
314            finally:
315                os.chdir(old_path)
316                for i in range(depth + 1):
317                    test_support.unlink(ABSTFN + '/%d' % i)
318                safe_rmdir(ABSTFN)
319
320        def test_realpath_resolve_parents(self):
321            # We also need to resolve any symlinks in the parents of a relative
322            # path passed to realpath. E.g.: current working directory is
323            # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call
324            # realpath("a"). This should return /usr/share/doc/a/.
325            try:
326                old_path = abspath('.')
327                os.mkdir(ABSTFN)
328                os.mkdir(ABSTFN + "/y")
329                os.symlink(ABSTFN + "/y", ABSTFN + "/k")
330
331                os.chdir(ABSTFN + "/k")
332                self.assertEqual(realpath("a"), ABSTFN + "/y/a")
333            finally:
334                os.chdir(old_path)
335                test_support.unlink(ABSTFN + "/k")
336                safe_rmdir(ABSTFN + "/y")
337                safe_rmdir(ABSTFN)
338
339        def test_realpath_resolve_before_normalizing(self):
340            # Bug #990669: Symbolic links should be resolved before we
341            # normalize the path. E.g.: if we have directories 'a', 'k' and 'y'
342            # in the following hierarchy:
343            # a/k/y
344            #
345            # and a symbolic link 'link-y' pointing to 'y' in directory 'a',
346            # then realpath("link-y/..") should return 'k', not 'a'.
347            try:
348                old_path = abspath('.')
349                os.mkdir(ABSTFN)
350                os.mkdir(ABSTFN + "/k")
351                os.mkdir(ABSTFN + "/k/y")
352                os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y")
353
354                # Absolute path.
355                self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k")
356                # Relative path.
357                os.chdir(dirname(ABSTFN))
358                self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."),
359                                 ABSTFN + "/k")
360            finally:
361                os.chdir(old_path)
362                test_support.unlink(ABSTFN + "/link-y")
363                safe_rmdir(ABSTFN + "/k/y")
364                safe_rmdir(ABSTFN + "/k")
365                safe_rmdir(ABSTFN)
366
367        def test_realpath_resolve_first(self):
368            # Bug #1213894: The first component of the path, if not absolute,
369            # must be resolved too.
370
371            try:
372                old_path = abspath('.')
373                os.mkdir(ABSTFN)
374                os.mkdir(ABSTFN + "/k")
375                os.symlink(ABSTFN, ABSTFN + "link")
376                os.chdir(dirname(ABSTFN))
377
378                base = basename(ABSTFN)
379                self.assertEqual(realpath(base + "link"), ABSTFN)
380                self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k")
381            finally:
382                os.chdir(old_path)
383                test_support.unlink(ABSTFN + "link")
384                safe_rmdir(ABSTFN + "/k")
385                safe_rmdir(ABSTFN)
386
387    def test_relpath(self):
388        (real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
389        try:
390            curdir = os.path.split(os.getcwd())[-1]
391            self.assertRaises(ValueError, posixpath.relpath, "")
392            self.assertEqual(posixpath.relpath("a"), "a")
393            self.assertEqual(posixpath.relpath(posixpath.abspath("a")), "a")
394            self.assertEqual(posixpath.relpath("a/b"), "a/b")
395            self.assertEqual(posixpath.relpath("../a/b"), "../a/b")
396            self.assertEqual(posixpath.relpath("a", "../b"), "../"+curdir+"/a")
397            self.assertEqual(posixpath.relpath("a/b", "../c"), "../"+curdir+"/a/b")
398            self.assertEqual(posixpath.relpath("a", "b/c"), "../../a")
399            self.assertEqual(posixpath.relpath("a", "a"), ".")
400            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x/y/z"), '../../../foo/bar/bat')
401            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/foo/bar"), 'bat')
402            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/"), 'foo/bar/bat')
403            self.assertEqual(posixpath.relpath("/", "/foo/bar/bat"), '../../..')
404            self.assertEqual(posixpath.relpath("/foo/bar/bat", "/x"), '../foo/bar/bat')
405            self.assertEqual(posixpath.relpath("/x", "/foo/bar/bat"), '../../../x')
406            self.assertEqual(posixpath.relpath("/", "/"), '.')
407            self.assertEqual(posixpath.relpath("/a", "/a"), '.')
408            self.assertEqual(posixpath.relpath("/a/b", "/a/b"), '.')
409        finally:
410            os.getcwd = real_getcwd
411
412
413class PosixCommonTest(test_genericpath.CommonTest):
414    pathmodule = posixpath
415    attributes = ['relpath', 'samefile', 'sameopenfile', 'samestat']
416
417
418def test_main():
419    test_support.run_unittest(PosixPathTest, PosixCommonTest)
420
421
422if __name__=="__main__":
423    test_main()
424