1import ntpath
2import os
3import sys
4import unittest
5import warnings
6from test.support import os_helper
7from test.support import TestFailed
8from test.support.os_helper import FakePath
9from test import test_genericpath
10from tempfile import TemporaryFile
11
12
13try:
14    import nt
15except ImportError:
16    # Most tests can complete without the nt module,
17    # but for those that require it we import here.
18    nt = None
19
20try:
21    ntpath._getfinalpathname
22except AttributeError:
23    HAVE_GETFINALPATHNAME = False
24else:
25    HAVE_GETFINALPATHNAME = True
26
27try:
28    import ctypes
29except ImportError:
30    HAVE_GETSHORTPATHNAME = False
31else:
32    HAVE_GETSHORTPATHNAME = True
33    def _getshortpathname(path):
34        GSPN = ctypes.WinDLL("kernel32", use_last_error=True).GetShortPathNameW
35        GSPN.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32]
36        GSPN.restype = ctypes.c_uint32
37        result_len = GSPN(path, None, 0)
38        if not result_len:
39            raise OSError("failed to get short path name 0x{:08X}"
40                          .format(ctypes.get_last_error()))
41        result = ctypes.create_unicode_buffer(result_len)
42        result_len = GSPN(path, result, result_len)
43        return result[:result_len]
44
45def _norm(path):
46    if isinstance(path, (bytes, str, os.PathLike)):
47        return ntpath.normcase(os.fsdecode(path))
48    elif hasattr(path, "__iter__"):
49        return tuple(ntpath.normcase(os.fsdecode(p)) for p in path)
50    return path
51
52
53def tester(fn, wantResult):
54    fn = fn.replace("\\", "\\\\")
55    gotResult = eval(fn)
56    if wantResult != gotResult and _norm(wantResult) != _norm(gotResult):
57        raise TestFailed("%s should return: %s but returned: %s" \
58              %(str(fn), str(wantResult), str(gotResult)))
59
60    # then with bytes
61    fn = fn.replace("('", "(b'")
62    fn = fn.replace('("', '(b"')
63    fn = fn.replace("['", "[b'")
64    fn = fn.replace('["', '[b"')
65    fn = fn.replace(", '", ", b'")
66    fn = fn.replace(', "', ', b"')
67    fn = os.fsencode(fn).decode('latin1')
68    fn = fn.encode('ascii', 'backslashreplace').decode('ascii')
69    with warnings.catch_warnings():
70        warnings.simplefilter("ignore", DeprecationWarning)
71        gotResult = eval(fn)
72    if _norm(wantResult) != _norm(gotResult):
73        raise TestFailed("%s should return: %s but returned: %s" \
74              %(str(fn), str(wantResult), repr(gotResult)))
75
76
77class NtpathTestCase(unittest.TestCase):
78    def assertPathEqual(self, path1, path2):
79        if path1 == path2 or _norm(path1) == _norm(path2):
80            return
81        self.assertEqual(path1, path2)
82
83    def assertPathIn(self, path, pathset):
84        self.assertIn(_norm(path), _norm(pathset))
85
86
87class TestNtpath(NtpathTestCase):
88    def test_splitext(self):
89        tester('ntpath.splitext("foo.ext")', ('foo', '.ext'))
90        tester('ntpath.splitext("/foo/foo.ext")', ('/foo/foo', '.ext'))
91        tester('ntpath.splitext(".ext")', ('.ext', ''))
92        tester('ntpath.splitext("\\foo.ext\\foo")', ('\\foo.ext\\foo', ''))
93        tester('ntpath.splitext("foo.ext\\")', ('foo.ext\\', ''))
94        tester('ntpath.splitext("")', ('', ''))
95        tester('ntpath.splitext("foo.bar.ext")', ('foo.bar', '.ext'))
96        tester('ntpath.splitext("xx/foo.bar.ext")', ('xx/foo.bar', '.ext'))
97        tester('ntpath.splitext("xx\\foo.bar.ext")', ('xx\\foo.bar', '.ext'))
98        tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d'))
99
100    def test_splitdrive(self):
101        tester('ntpath.splitdrive("c:\\foo\\bar")',
102               ('c:', '\\foo\\bar'))
103        tester('ntpath.splitdrive("c:/foo/bar")',
104               ('c:', '/foo/bar'))
105        tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")',
106               ('\\\\conky\\mountpoint', '\\foo\\bar'))
107        tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")',
108               ('//conky/mountpoint', '/foo/bar'))
109        tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")',
110            ('', '\\\\\\conky\\mountpoint\\foo\\bar'))
111        tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")',
112            ('', '///conky/mountpoint/foo/bar'))
113        tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")',
114               ('', '\\\\conky\\\\mountpoint\\foo\\bar'))
115        tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")',
116               ('', '//conky//mountpoint/foo/bar'))
117        # Issue #19911: UNC part containing U+0130
118        self.assertEqual(ntpath.splitdrive('//conky/MOUNTPOİNT/foo/bar'),
119                         ('//conky/MOUNTPOİNT', '/foo/bar'))
120
121    def test_split(self):
122        tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
123        tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
124               ('\\\\conky\\mountpoint\\foo', 'bar'))
125
126        tester('ntpath.split("c:\\")', ('c:\\', ''))
127        tester('ntpath.split("\\\\conky\\mountpoint\\")',
128               ('\\\\conky\\mountpoint\\', ''))
129
130        tester('ntpath.split("c:/")', ('c:/', ''))
131        tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
132
133    def test_isabs(self):
134        tester('ntpath.isabs("c:\\")', 1)
135        tester('ntpath.isabs("\\\\conky\\mountpoint\\")', 1)
136        tester('ntpath.isabs("\\foo")', 1)
137        tester('ntpath.isabs("\\foo\\bar")', 1)
138
139    def test_commonprefix(self):
140        tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])',
141               "/home/swen")
142        tester('ntpath.commonprefix(["\\home\\swen\\spam", "\\home\\swen\\eggs"])',
143               "\\home\\swen\\")
144        tester('ntpath.commonprefix(["/home/swen/spam", "/home/swen/spam"])',
145               "/home/swen/spam")
146
147    def test_join(self):
148        tester('ntpath.join("")', '')
149        tester('ntpath.join("", "", "")', '')
150        tester('ntpath.join("a")', 'a')
151        tester('ntpath.join("/a")', '/a')
152        tester('ntpath.join("\\a")', '\\a')
153        tester('ntpath.join("a:")', 'a:')
154        tester('ntpath.join("a:", "\\b")', 'a:\\b')
155        tester('ntpath.join("a", "\\b")', '\\b')
156        tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
157        tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
158        tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
159        tester('ntpath.join("a", "b", "\\c")', '\\c')
160        tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
161        tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
162
163        tester("ntpath.join('', 'a')", 'a')
164        tester("ntpath.join('', '', '', '', 'a')", 'a')
165        tester("ntpath.join('a', '')", 'a\\')
166        tester("ntpath.join('a', '', '', '', '')", 'a\\')
167        tester("ntpath.join('a\\', '')", 'a\\')
168        tester("ntpath.join('a\\', '', '', '', '')", 'a\\')
169        tester("ntpath.join('a/', '')", 'a/')
170
171        tester("ntpath.join('a/b', 'x/y')", 'a/b\\x/y')
172        tester("ntpath.join('/a/b', 'x/y')", '/a/b\\x/y')
173        tester("ntpath.join('/a/b/', 'x/y')", '/a/b/x/y')
174        tester("ntpath.join('c:', 'x/y')", 'c:x/y')
175        tester("ntpath.join('c:a/b', 'x/y')", 'c:a/b\\x/y')
176        tester("ntpath.join('c:a/b/', 'x/y')", 'c:a/b/x/y')
177        tester("ntpath.join('c:/', 'x/y')", 'c:/x/y')
178        tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y')
179        tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y')
180        tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y')
181        tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y')
182        tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y')
183
184        tester("ntpath.join('a/b', '/x/y')", '/x/y')
185        tester("ntpath.join('/a/b', '/x/y')", '/x/y')
186        tester("ntpath.join('c:', '/x/y')", 'c:/x/y')
187        tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y')
188        tester("ntpath.join('c:/', '/x/y')", 'c:/x/y')
189        tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y')
190        tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y')
191        tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y')
192        tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y')
193
194        tester("ntpath.join('c:', 'C:x/y')", 'C:x/y')
195        tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y')
196        tester("ntpath.join('c:/', 'C:x/y')", 'C:/x/y')
197        tester("ntpath.join('c:/a/b', 'C:x/y')", 'C:/a/b\\x/y')
198
199        for x in ('', 'a/b', '/a/b', 'c:', 'c:a/b', 'c:/', 'c:/a/b',
200                  '//computer/share', '//computer/share/', '//computer/share/a/b'):
201            for y in ('d:', 'd:x/y', 'd:/', 'd:/x/y',
202                      '//machine/common', '//machine/common/', '//machine/common/x/y'):
203                tester("ntpath.join(%r, %r)" % (x, y), y)
204
205        tester("ntpath.join('\\\\computer\\share\\', 'a', 'b')", '\\\\computer\\share\\a\\b')
206        tester("ntpath.join('\\\\computer\\share', 'a', 'b')", '\\\\computer\\share\\a\\b')
207        tester("ntpath.join('\\\\computer\\share', 'a\\b')", '\\\\computer\\share\\a\\b')
208        tester("ntpath.join('//computer/share/', 'a', 'b')", '//computer/share/a\\b')
209        tester("ntpath.join('//computer/share', 'a', 'b')", '//computer/share\\a\\b')
210        tester("ntpath.join('//computer/share', 'a/b')", '//computer/share\\a/b')
211
212    def test_normpath(self):
213        tester("ntpath.normpath('A//////././//.//B')", r'A\B')
214        tester("ntpath.normpath('A/./B')", r'A\B')
215        tester("ntpath.normpath('A/foo/../B')", r'A\B')
216        tester("ntpath.normpath('C:A//B')", r'C:A\B')
217        tester("ntpath.normpath('D:A/./B')", r'D:A\B')
218        tester("ntpath.normpath('e:A/foo/../B')", r'e:A\B')
219
220        tester("ntpath.normpath('C:///A//B')", r'C:\A\B')
221        tester("ntpath.normpath('D:///A/./B')", r'D:\A\B')
222        tester("ntpath.normpath('e:///A/foo/../B')", r'e:\A\B')
223
224        tester("ntpath.normpath('..')", r'..')
225        tester("ntpath.normpath('.')", r'.')
226        tester("ntpath.normpath('')", r'.')
227        tester("ntpath.normpath('/')", '\\')
228        tester("ntpath.normpath('c:/')", 'c:\\')
229        tester("ntpath.normpath('/../.././..')", '\\')
230        tester("ntpath.normpath('c:/../../..')", 'c:\\')
231        tester("ntpath.normpath('../.././..')", r'..\..\..')
232        tester("ntpath.normpath('K:../.././..')", r'K:..\..\..')
233        tester("ntpath.normpath('C:////a/b')", r'C:\a\b')
234        tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b')
235
236        tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL')
237        tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z')
238
239    def test_realpath_curdir(self):
240        expected = ntpath.normpath(os.getcwd())
241        tester("ntpath.realpath('.')", expected)
242        tester("ntpath.realpath('./.')", expected)
243        tester("ntpath.realpath('/'.join(['.'] * 100))", expected)
244        tester("ntpath.realpath('.\\.')", expected)
245        tester("ntpath.realpath('\\'.join(['.'] * 100))", expected)
246
247    def test_realpath_pardir(self):
248        expected = ntpath.normpath(os.getcwd())
249        tester("ntpath.realpath('..')", ntpath.dirname(expected))
250        tester("ntpath.realpath('../..')",
251               ntpath.dirname(ntpath.dirname(expected)))
252        tester("ntpath.realpath('/'.join(['..'] * 50))",
253               ntpath.splitdrive(expected)[0] + '\\')
254        tester("ntpath.realpath('..\\..')",
255               ntpath.dirname(ntpath.dirname(expected)))
256        tester("ntpath.realpath('\\'.join(['..'] * 50))",
257               ntpath.splitdrive(expected)[0] + '\\')
258
259    @os_helper.skip_unless_symlink
260    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
261    def test_realpath_basic(self):
262        ABSTFN = ntpath.abspath(os_helper.TESTFN)
263        open(ABSTFN, "wb").close()
264        self.addCleanup(os_helper.unlink, ABSTFN)
265        self.addCleanup(os_helper.unlink, ABSTFN + "1")
266
267        os.symlink(ABSTFN, ABSTFN + "1")
268        self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN)
269        self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")),
270                         os.fsencode(ABSTFN))
271
272    @os_helper.skip_unless_symlink
273    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
274    def test_realpath_strict(self):
275        # Bug #43757: raise FileNotFoundError in strict mode if we encounter
276        # a path that does not exist.
277        ABSTFN = ntpath.abspath(os_helper.TESTFN)
278        os.symlink(ABSTFN + "1", ABSTFN)
279        self.addCleanup(os_helper.unlink, ABSTFN)
280        self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True)
281        self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True)
282
283    @os_helper.skip_unless_symlink
284    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
285    def test_realpath_relative(self):
286        ABSTFN = ntpath.abspath(os_helper.TESTFN)
287        open(ABSTFN, "wb").close()
288        self.addCleanup(os_helper.unlink, ABSTFN)
289        self.addCleanup(os_helper.unlink, ABSTFN + "1")
290
291        os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1"))
292        self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN)
293
294    @os_helper.skip_unless_symlink
295    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
296    def test_realpath_broken_symlinks(self):
297        ABSTFN = ntpath.abspath(os_helper.TESTFN)
298        os.mkdir(ABSTFN)
299        self.addCleanup(os_helper.rmtree, ABSTFN)
300
301        with os_helper.change_cwd(ABSTFN):
302            os.mkdir("subdir")
303            os.chdir("subdir")
304            os.symlink(".", "recursive")
305            os.symlink("..", "parent")
306            os.chdir("..")
307            os.symlink(".", "self")
308            os.symlink("missing", "broken")
309            os.symlink(r"broken\bar", "broken1")
310            os.symlink(r"self\self\broken", "broken2")
311            os.symlink(r"subdir\parent\subdir\parent\broken", "broken3")
312            os.symlink(ABSTFN + r"\broken", "broken4")
313            os.symlink(r"recursive\..\broken", "broken5")
314
315            self.assertPathEqual(ntpath.realpath("broken"),
316                                 ABSTFN + r"\missing")
317            self.assertPathEqual(ntpath.realpath(r"broken\foo"),
318                                 ABSTFN + r"\missing\foo")
319            # bpo-38453: We no longer recursively resolve segments of relative
320            # symlinks that the OS cannot resolve.
321            self.assertPathEqual(ntpath.realpath(r"broken1"),
322                                 ABSTFN + r"\broken\bar")
323            self.assertPathEqual(ntpath.realpath(r"broken1\baz"),
324                                 ABSTFN + r"\broken\bar\baz")
325            self.assertPathEqual(ntpath.realpath("broken2"),
326                                 ABSTFN + r"\self\self\missing")
327            self.assertPathEqual(ntpath.realpath("broken3"),
328                                 ABSTFN + r"\subdir\parent\subdir\parent\missing")
329            self.assertPathEqual(ntpath.realpath("broken4"),
330                                 ABSTFN + r"\missing")
331            self.assertPathEqual(ntpath.realpath("broken5"),
332                                 ABSTFN + r"\missing")
333
334            self.assertPathEqual(ntpath.realpath(b"broken"),
335                                 os.fsencode(ABSTFN + r"\missing"))
336            self.assertPathEqual(ntpath.realpath(rb"broken\foo"),
337                                 os.fsencode(ABSTFN + r"\missing\foo"))
338            self.assertPathEqual(ntpath.realpath(rb"broken1"),
339                                 os.fsencode(ABSTFN + r"\broken\bar"))
340            self.assertPathEqual(ntpath.realpath(rb"broken1\baz"),
341                                 os.fsencode(ABSTFN + r"\broken\bar\baz"))
342            self.assertPathEqual(ntpath.realpath(b"broken2"),
343                                 os.fsencode(ABSTFN + r"\self\self\missing"))
344            self.assertPathEqual(ntpath.realpath(rb"broken3"),
345                                 os.fsencode(ABSTFN + r"\subdir\parent\subdir\parent\missing"))
346            self.assertPathEqual(ntpath.realpath(b"broken4"),
347                                 os.fsencode(ABSTFN + r"\missing"))
348            self.assertPathEqual(ntpath.realpath(b"broken5"),
349                                 os.fsencode(ABSTFN + r"\missing"))
350
351    @os_helper.skip_unless_symlink
352    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
353    def test_realpath_symlink_loops(self):
354        # Symlink loops in non-strict mode are non-deterministic as to which
355        # path is returned, but it will always be the fully resolved path of
356        # one member of the cycle
357        ABSTFN = ntpath.abspath(os_helper.TESTFN)
358        self.addCleanup(os_helper.unlink, ABSTFN)
359        self.addCleanup(os_helper.unlink, ABSTFN + "1")
360        self.addCleanup(os_helper.unlink, ABSTFN + "2")
361        self.addCleanup(os_helper.unlink, ABSTFN + "y")
362        self.addCleanup(os_helper.unlink, ABSTFN + "c")
363        self.addCleanup(os_helper.unlink, ABSTFN + "a")
364
365        os.symlink(ABSTFN, ABSTFN)
366        self.assertPathEqual(ntpath.realpath(ABSTFN), ABSTFN)
367
368        os.symlink(ABSTFN + "1", ABSTFN + "2")
369        os.symlink(ABSTFN + "2", ABSTFN + "1")
370        expected = (ABSTFN + "1", ABSTFN + "2")
371        self.assertPathIn(ntpath.realpath(ABSTFN + "1"), expected)
372        self.assertPathIn(ntpath.realpath(ABSTFN + "2"), expected)
373
374        self.assertPathIn(ntpath.realpath(ABSTFN + "1\\x"),
375                          (ntpath.join(r, "x") for r in expected))
376        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\.."),
377                             ntpath.dirname(ABSTFN))
378        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x"),
379                             ntpath.dirname(ABSTFN) + "\\x")
380        os.symlink(ABSTFN + "x", ABSTFN + "y")
381        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\"
382                                             + ntpath.basename(ABSTFN) + "y"),
383                             ABSTFN + "x")
384        self.assertPathIn(ntpath.realpath(ABSTFN + "1\\..\\"
385                                          + ntpath.basename(ABSTFN) + "1"),
386                          expected)
387
388        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
389        self.assertPathEqual(ntpath.realpath(ABSTFN + "a"), ABSTFN + "a")
390
391        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
392                   + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
393        self.assertPathEqual(ntpath.realpath(ABSTFN + "c"), ABSTFN + "c")
394
395        # Test using relative path as well.
396        self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN)
397
398    @os_helper.skip_unless_symlink
399    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
400    def test_realpath_symlink_loops_strict(self):
401        # Symlink loops raise OSError in strict mode
402        ABSTFN = ntpath.abspath(os_helper.TESTFN)
403        self.addCleanup(os_helper.unlink, ABSTFN)
404        self.addCleanup(os_helper.unlink, ABSTFN + "1")
405        self.addCleanup(os_helper.unlink, ABSTFN + "2")
406        self.addCleanup(os_helper.unlink, ABSTFN + "y")
407        self.addCleanup(os_helper.unlink, ABSTFN + "c")
408        self.addCleanup(os_helper.unlink, ABSTFN + "a")
409
410        os.symlink(ABSTFN, ABSTFN)
411        self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=True)
412
413        os.symlink(ABSTFN + "1", ABSTFN + "2")
414        os.symlink(ABSTFN + "2", ABSTFN + "1")
415        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1", strict=True)
416        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2", strict=True)
417        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x", strict=True)
418        # Windows eliminates '..' components before resolving links, so the
419        # following call is not expected to raise.
420        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..", strict=True),
421                             ntpath.dirname(ABSTFN))
422        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\x", strict=True)
423        os.symlink(ABSTFN + "x", ABSTFN + "y")
424        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\"
425                                             + ntpath.basename(ABSTFN) + "y",
426                                             strict=True)
427        self.assertRaises(OSError, ntpath.realpath,
428                          ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1",
429                          strict=True)
430
431        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
432        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a", strict=True)
433
434        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
435                   + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
436        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c", strict=True)
437
438        # Test using relative path as well.
439        self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN),
440                          strict=True)
441
442    @os_helper.skip_unless_symlink
443    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
444    def test_realpath_symlink_prefix(self):
445        ABSTFN = ntpath.abspath(os_helper.TESTFN)
446        self.addCleanup(os_helper.unlink, ABSTFN + "3")
447        self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.")
448        self.addCleanup(os_helper.unlink, ABSTFN + "3link")
449        self.addCleanup(os_helper.unlink, ABSTFN + "3.link")
450
451        with open(ABSTFN + "3", "wb") as f:
452            f.write(b'0')
453        os.symlink(ABSTFN + "3", ABSTFN + "3link")
454
455        with open("\\\\?\\" + ABSTFN + "3.", "wb") as f:
456            f.write(b'1')
457        os.symlink("\\\\?\\" + ABSTFN + "3.", ABSTFN + "3.link")
458
459        self.assertPathEqual(ntpath.realpath(ABSTFN + "3link"),
460                             ABSTFN + "3")
461        self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link"),
462                             "\\\\?\\" + ABSTFN + "3.")
463
464        # Resolved paths should be usable to open target files
465        with open(ntpath.realpath(ABSTFN + "3link"), "rb") as f:
466            self.assertEqual(f.read(), b'0')
467        with open(ntpath.realpath(ABSTFN + "3.link"), "rb") as f:
468            self.assertEqual(f.read(), b'1')
469
470        # When the prefix is included, it is not stripped
471        self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link"),
472                             "\\\\?\\" + ABSTFN + "3")
473        self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link"),
474                             "\\\\?\\" + ABSTFN + "3.")
475
476    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
477    def test_realpath_nul(self):
478        tester("ntpath.realpath('NUL')", r'\\.\NUL')
479
480    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
481    @unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname')
482    def test_realpath_cwd(self):
483        ABSTFN = ntpath.abspath(os_helper.TESTFN)
484
485        os_helper.unlink(ABSTFN)
486        os_helper.rmtree(ABSTFN)
487        os.mkdir(ABSTFN)
488        self.addCleanup(os_helper.rmtree, ABSTFN)
489
490        test_dir_long = ntpath.join(ABSTFN, "MyVeryLongDirectoryName")
491        os.mkdir(test_dir_long)
492
493        test_dir_short = _getshortpathname(test_dir_long)
494        test_file_long = ntpath.join(test_dir_long, "file.txt")
495        test_file_short = ntpath.join(test_dir_short, "file.txt")
496
497        with open(test_file_long, "wb") as f:
498            f.write(b"content")
499
500        self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short))
501
502        with os_helper.change_cwd(test_dir_long):
503            self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
504        with os_helper.change_cwd(test_dir_long.lower()):
505            self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
506        with os_helper.change_cwd(test_dir_short):
507            self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
508
509    def test_expandvars(self):
510        with os_helper.EnvironmentVarGuard() as env:
511            env.clear()
512            env["foo"] = "bar"
513            env["{foo"] = "baz1"
514            env["{foo}"] = "baz2"
515            tester('ntpath.expandvars("foo")', "foo")
516            tester('ntpath.expandvars("$foo bar")', "bar bar")
517            tester('ntpath.expandvars("${foo}bar")', "barbar")
518            tester('ntpath.expandvars("$[foo]bar")', "$[foo]bar")
519            tester('ntpath.expandvars("$bar bar")', "$bar bar")
520            tester('ntpath.expandvars("$?bar")', "$?bar")
521            tester('ntpath.expandvars("$foo}bar")', "bar}bar")
522            tester('ntpath.expandvars("${foo")', "${foo")
523            tester('ntpath.expandvars("${{foo}}")', "baz1}")
524            tester('ntpath.expandvars("$foo$foo")', "barbar")
525            tester('ntpath.expandvars("$bar$bar")', "$bar$bar")
526            tester('ntpath.expandvars("%foo% bar")', "bar bar")
527            tester('ntpath.expandvars("%foo%bar")', "barbar")
528            tester('ntpath.expandvars("%foo%%foo%")', "barbar")
529            tester('ntpath.expandvars("%%foo%%foo%foo%")', "%foo%foobar")
530            tester('ntpath.expandvars("%?bar%")', "%?bar%")
531            tester('ntpath.expandvars("%foo%%bar")', "bar%bar")
532            tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
533            tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%")
534
535    @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
536    def test_expandvars_nonascii(self):
537        def check(value, expected):
538            tester('ntpath.expandvars(%r)' % value, expected)
539        with os_helper.EnvironmentVarGuard() as env:
540            env.clear()
541            nonascii = os_helper.FS_NONASCII
542            env['spam'] = nonascii
543            env[nonascii] = 'ham' + nonascii
544            check('$spam bar', '%s bar' % nonascii)
545            check('$%s bar' % nonascii, '$%s bar' % nonascii)
546            check('${spam}bar', '%sbar' % nonascii)
547            check('${%s}bar' % nonascii, 'ham%sbar' % nonascii)
548            check('$spam}bar', '%s}bar' % nonascii)
549            check('$%s}bar' % nonascii, '$%s}bar' % nonascii)
550            check('%spam% bar', '%s bar' % nonascii)
551            check('%{}% bar'.format(nonascii), 'ham%s bar' % nonascii)
552            check('%spam%bar', '%sbar' % nonascii)
553            check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
554
555    def test_expanduser(self):
556        tester('ntpath.expanduser("test")', 'test')
557
558        with os_helper.EnvironmentVarGuard() as env:
559            env.clear()
560            tester('ntpath.expanduser("~test")', '~test')
561
562            env['HOMEDRIVE'] = 'C:\\'
563            env['HOMEPATH'] = 'Users\\eric'
564            env['USERNAME'] = 'eric'
565            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
566            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
567
568            del env['HOMEDRIVE']
569            tester('ntpath.expanduser("~test")', 'Users\\test')
570            tester('ntpath.expanduser("~")', 'Users\\eric')
571
572            env.clear()
573            env['USERPROFILE'] = 'C:\\Users\\eric'
574            env['USERNAME'] = 'eric'
575            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
576            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
577            tester('ntpath.expanduser("~test\\foo\\bar")',
578                   'C:\\Users\\test\\foo\\bar')
579            tester('ntpath.expanduser("~test/foo/bar")',
580                   'C:\\Users\\test/foo/bar')
581            tester('ntpath.expanduser("~\\foo\\bar")',
582                   'C:\\Users\\eric\\foo\\bar')
583            tester('ntpath.expanduser("~/foo/bar")',
584                   'C:\\Users\\eric/foo/bar')
585
586            # bpo-36264: ignore `HOME` when set on windows
587            env.clear()
588            env['HOME'] = 'F:\\'
589            env['USERPROFILE'] = 'C:\\Users\\eric'
590            env['USERNAME'] = 'eric'
591            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
592            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
593
594            # bpo-39899: don't guess another user's home directory if
595            # `%USERNAME% != basename(%USERPROFILE%)`
596            env.clear()
597            env['USERPROFILE'] = 'C:\\Users\\eric'
598            env['USERNAME'] = 'idle'
599            tester('ntpath.expanduser("~test")', '~test')
600            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
601
602
603
604    @unittest.skipUnless(nt, "abspath requires 'nt' module")
605    def test_abspath(self):
606        tester('ntpath.abspath("C:\\")', "C:\\")
607        with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: # bpo-31047
608            tester('ntpath.abspath("")', cwd_dir)
609            tester('ntpath.abspath(" ")', cwd_dir + "\\ ")
610            tester('ntpath.abspath("?")', cwd_dir + "\\?")
611            drive, _ = ntpath.splitdrive(cwd_dir)
612            tester('ntpath.abspath("/abc/")', drive + "\\abc")
613
614    def test_relpath(self):
615        tester('ntpath.relpath("a")', 'a')
616        tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
617        tester('ntpath.relpath("a/b")', 'a\\b')
618        tester('ntpath.relpath("../a/b")', '..\\a\\b')
619        with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir:
620            currentdir = ntpath.basename(cwd_dir)
621            tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a')
622            tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b')
623        tester('ntpath.relpath("a", "b/c")', '..\\..\\a')
624        tester('ntpath.relpath("c:/foo/bar/bat", "c:/x/y")', '..\\..\\foo\\bar\\bat')
625        tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a')
626        tester('ntpath.relpath("a", "a")', '.')
627        tester('ntpath.relpath("/foo/bar/bat", "/x/y/z")', '..\\..\\..\\foo\\bar\\bat')
628        tester('ntpath.relpath("/foo/bar/bat", "/foo/bar")', 'bat')
629        tester('ntpath.relpath("/foo/bar/bat", "/")', 'foo\\bar\\bat')
630        tester('ntpath.relpath("/", "/foo/bar/bat")', '..\\..\\..')
631        tester('ntpath.relpath("/foo/bar/bat", "/x")', '..\\foo\\bar\\bat')
632        tester('ntpath.relpath("/x", "/foo/bar/bat")', '..\\..\\..\\x')
633        tester('ntpath.relpath("/", "/")', '.')
634        tester('ntpath.relpath("/a", "/a")', '.')
635        tester('ntpath.relpath("/a/b", "/a/b")', '.')
636        tester('ntpath.relpath("c:/foo", "C:/FOO")', '.')
637
638    def test_commonpath(self):
639        def check(paths, expected):
640            tester(('ntpath.commonpath(%r)' % paths).replace('\\\\', '\\'),
641                   expected)
642        def check_error(exc, paths):
643            self.assertRaises(exc, ntpath.commonpath, paths)
644            self.assertRaises(exc, ntpath.commonpath,
645                              [os.fsencode(p) for p in paths])
646
647        self.assertRaises(ValueError, ntpath.commonpath, [])
648        check_error(ValueError, ['C:\\Program Files', 'Program Files'])
649        check_error(ValueError, ['C:\\Program Files', 'C:Program Files'])
650        check_error(ValueError, ['\\Program Files', 'Program Files'])
651        check_error(ValueError, ['Program Files', 'C:\\Program Files'])
652        check(['C:\\Program Files'], 'C:\\Program Files')
653        check(['C:\\Program Files', 'C:\\Program Files'], 'C:\\Program Files')
654        check(['C:\\Program Files\\', 'C:\\Program Files'],
655              'C:\\Program Files')
656        check(['C:\\Program Files\\', 'C:\\Program Files\\'],
657              'C:\\Program Files')
658        check(['C:\\\\Program Files', 'C:\\Program Files\\\\'],
659              'C:\\Program Files')
660        check(['C:\\.\\Program Files', 'C:\\Program Files\\.'],
661              'C:\\Program Files')
662        check(['C:\\', 'C:\\bin'], 'C:\\')
663        check(['C:\\Program Files', 'C:\\bin'], 'C:\\')
664        check(['C:\\Program Files', 'C:\\Program Files\\Bar'],
665              'C:\\Program Files')
666        check(['C:\\Program Files\\Foo', 'C:\\Program Files\\Bar'],
667              'C:\\Program Files')
668        check(['C:\\Program Files', 'C:\\Projects'], 'C:\\')
669        check(['C:\\Program Files\\', 'C:\\Projects'], 'C:\\')
670
671        check(['C:\\Program Files\\Foo', 'C:/Program Files/Bar'],
672              'C:\\Program Files')
673        check(['C:\\Program Files\\Foo', 'c:/program files/bar'],
674              'C:\\Program Files')
675        check(['c:/program files/bar', 'C:\\Program Files\\Foo'],
676              'c:\\program files')
677
678        check_error(ValueError, ['C:\\Program Files', 'D:\\Program Files'])
679
680        check(['spam'], 'spam')
681        check(['spam', 'spam'], 'spam')
682        check(['spam', 'alot'], '')
683        check(['and\\jam', 'and\\spam'], 'and')
684        check(['and\\\\jam', 'and\\spam\\\\'], 'and')
685        check(['and\\.\\jam', '.\\and\\spam'], 'and')
686        check(['and\\jam', 'and\\spam', 'alot'], '')
687        check(['and\\jam', 'and\\spam', 'and'], 'and')
688        check(['C:and\\jam', 'C:and\\spam'], 'C:and')
689
690        check([''], '')
691        check(['', 'spam\\alot'], '')
692        check_error(ValueError, ['', '\\spam\\alot'])
693
694        self.assertRaises(TypeError, ntpath.commonpath,
695                          [b'C:\\Program Files', 'C:\\Program Files\\Foo'])
696        self.assertRaises(TypeError, ntpath.commonpath,
697                          [b'C:\\Program Files', 'Program Files\\Foo'])
698        self.assertRaises(TypeError, ntpath.commonpath,
699                          [b'Program Files', 'C:\\Program Files\\Foo'])
700        self.assertRaises(TypeError, ntpath.commonpath,
701                          ['C:\\Program Files', b'C:\\Program Files\\Foo'])
702        self.assertRaises(TypeError, ntpath.commonpath,
703                          ['C:\\Program Files', b'Program Files\\Foo'])
704        self.assertRaises(TypeError, ntpath.commonpath,
705                          ['Program Files', b'C:\\Program Files\\Foo'])
706
707    def test_sameopenfile(self):
708        with TemporaryFile() as tf1, TemporaryFile() as tf2:
709            # Make sure the same file is really the same
710            self.assertTrue(ntpath.sameopenfile(tf1.fileno(), tf1.fileno()))
711            # Make sure different files are really different
712            self.assertFalse(ntpath.sameopenfile(tf1.fileno(), tf2.fileno()))
713            # Make sure invalid values don't cause issues on win32
714            if sys.platform == "win32":
715                with self.assertRaises(OSError):
716                    # Invalid file descriptors shouldn't display assert
717                    # dialogs (#4804)
718                    ntpath.sameopenfile(-1, -1)
719
720    def test_ismount(self):
721        self.assertTrue(ntpath.ismount("c:\\"))
722        self.assertTrue(ntpath.ismount("C:\\"))
723        self.assertTrue(ntpath.ismount("c:/"))
724        self.assertTrue(ntpath.ismount("C:/"))
725        self.assertTrue(ntpath.ismount("\\\\.\\c:\\"))
726        self.assertTrue(ntpath.ismount("\\\\.\\C:\\"))
727
728        self.assertTrue(ntpath.ismount(b"c:\\"))
729        self.assertTrue(ntpath.ismount(b"C:\\"))
730        self.assertTrue(ntpath.ismount(b"c:/"))
731        self.assertTrue(ntpath.ismount(b"C:/"))
732        self.assertTrue(ntpath.ismount(b"\\\\.\\c:\\"))
733        self.assertTrue(ntpath.ismount(b"\\\\.\\C:\\"))
734
735        with os_helper.temp_dir() as d:
736            self.assertFalse(ntpath.ismount(d))
737
738        if sys.platform == "win32":
739            #
740            # Make sure the current folder isn't the root folder
741            # (or any other volume root). The drive-relative
742            # locations below cannot then refer to mount points
743            #
744            drive, path = ntpath.splitdrive(sys.executable)
745            with os_helper.change_cwd(ntpath.dirname(sys.executable)):
746                self.assertFalse(ntpath.ismount(drive.lower()))
747                self.assertFalse(ntpath.ismount(drive.upper()))
748
749            self.assertTrue(ntpath.ismount("\\\\localhost\\c$"))
750            self.assertTrue(ntpath.ismount("\\\\localhost\\c$\\"))
751
752            self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
753            self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
754
755    def assertEqualCI(self, s1, s2):
756        """Assert that two strings are equal ignoring case differences."""
757        self.assertEqual(s1.lower(), s2.lower())
758
759    @unittest.skipUnless(nt, "OS helpers require 'nt' module")
760    def test_nt_helpers(self):
761        # Trivial validation that the helpers do not break, and support both
762        # unicode and bytes (UTF-8) paths
763
764        executable = nt._getfinalpathname(sys.executable)
765
766        for path in executable, os.fsencode(executable):
767            volume_path = nt._getvolumepathname(path)
768            path_drive = ntpath.splitdrive(path)[0]
769            volume_path_drive = ntpath.splitdrive(volume_path)[0]
770            self.assertEqualCI(path_drive, volume_path_drive)
771
772        cap, free = nt._getdiskusage(sys.exec_prefix)
773        self.assertGreater(cap, 0)
774        self.assertGreater(free, 0)
775        b_cap, b_free = nt._getdiskusage(sys.exec_prefix.encode())
776        # Free space may change, so only test the capacity is equal
777        self.assertEqual(b_cap, cap)
778        self.assertGreater(b_free, 0)
779
780        for path in [sys.prefix, sys.executable]:
781            final_path = nt._getfinalpathname(path)
782            self.assertIsInstance(final_path, str)
783            self.assertGreater(len(final_path), 0)
784
785            b_final_path = nt._getfinalpathname(path.encode())
786            self.assertIsInstance(b_final_path, bytes)
787            self.assertGreater(len(b_final_path), 0)
788
789class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
790    pathmodule = ntpath
791    attributes = ['relpath']
792
793
794class PathLikeTests(NtpathTestCase):
795
796    path = ntpath
797
798    def setUp(self):
799        self.file_name = os_helper.TESTFN
800        self.file_path = FakePath(os_helper.TESTFN)
801        self.addCleanup(os_helper.unlink, self.file_name)
802        with open(self.file_name, 'xb', 0) as file:
803            file.write(b"test_ntpath.PathLikeTests")
804
805    def _check_function(self, func):
806        self.assertPathEqual(func(self.file_path), func(self.file_name))
807
808    def test_path_normcase(self):
809        self._check_function(self.path.normcase)
810
811    def test_path_isabs(self):
812        self._check_function(self.path.isabs)
813
814    def test_path_join(self):
815        self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
816                         self.path.join('a', 'b', 'c'))
817
818    def test_path_split(self):
819        self._check_function(self.path.split)
820
821    def test_path_splitext(self):
822        self._check_function(self.path.splitext)
823
824    def test_path_splitdrive(self):
825        self._check_function(self.path.splitdrive)
826
827    def test_path_basename(self):
828        self._check_function(self.path.basename)
829
830    def test_path_dirname(self):
831        self._check_function(self.path.dirname)
832
833    def test_path_islink(self):
834        self._check_function(self.path.islink)
835
836    def test_path_lexists(self):
837        self._check_function(self.path.lexists)
838
839    def test_path_ismount(self):
840        self._check_function(self.path.ismount)
841
842    def test_path_expanduser(self):
843        self._check_function(self.path.expanduser)
844
845    def test_path_expandvars(self):
846        self._check_function(self.path.expandvars)
847
848    def test_path_normpath(self):
849        self._check_function(self.path.normpath)
850
851    def test_path_abspath(self):
852        self._check_function(self.path.abspath)
853
854    def test_path_realpath(self):
855        self._check_function(self.path.realpath)
856
857    def test_path_relpath(self):
858        self._check_function(self.path.relpath)
859
860    def test_path_commonpath(self):
861        common_path = self.path.commonpath([self.file_path, self.file_name])
862        self.assertPathEqual(common_path, self.file_name)
863
864    def test_path_isdir(self):
865        self._check_function(self.path.isdir)
866
867
868if __name__ == "__main__":
869    unittest.main()
870