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