• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import ntpath
2import os
3import string
4import subprocess
5import sys
6import unittest
7import warnings
8from ntpath import ALLOW_MISSING
9from test.support import os_helper
10from test.support import TestFailed, is_emscripten
11from test.support.os_helper import FakePath
12from test import test_genericpath
13from tempfile import TemporaryFile
14
15
16try:
17    import nt
18except ImportError:
19    # Most tests can complete without the nt module,
20    # but for those that require it we import here.
21    nt = None
22
23try:
24    ntpath._getfinalpathname
25except AttributeError:
26    HAVE_GETFINALPATHNAME = False
27else:
28    HAVE_GETFINALPATHNAME = True
29
30try:
31    import ctypes
32except ImportError:
33    HAVE_GETSHORTPATHNAME = False
34else:
35    HAVE_GETSHORTPATHNAME = True
36    def _getshortpathname(path):
37        GSPN = ctypes.WinDLL("kernel32", use_last_error=True).GetShortPathNameW
38        GSPN.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32]
39        GSPN.restype = ctypes.c_uint32
40        result_len = GSPN(path, None, 0)
41        if not result_len:
42            raise OSError("failed to get short path name 0x{:08X}"
43                          .format(ctypes.get_last_error()))
44        result = ctypes.create_unicode_buffer(result_len)
45        result_len = GSPN(path, result, result_len)
46        return result[:result_len]
47
48def _norm(path):
49    if isinstance(path, (bytes, str, os.PathLike)):
50        return ntpath.normcase(os.fsdecode(path))
51    elif hasattr(path, "__iter__"):
52        return tuple(ntpath.normcase(os.fsdecode(p)) for p in path)
53    return path
54
55
56def tester(fn, wantResult):
57    fn = fn.replace("\\", "\\\\")
58    gotResult = eval(fn)
59    if wantResult != gotResult and _norm(wantResult) != _norm(gotResult):
60        raise TestFailed("%s should return: %s but returned: %s" \
61              %(str(fn), str(wantResult), str(gotResult)))
62
63    # then with bytes
64    fn = fn.replace("('", "(b'")
65    fn = fn.replace('("', '(b"')
66    fn = fn.replace("['", "[b'")
67    fn = fn.replace('["', '[b"')
68    fn = fn.replace(", '", ", b'")
69    fn = fn.replace(', "', ', b"')
70    fn = os.fsencode(fn).decode('latin1')
71    fn = fn.encode('ascii', 'backslashreplace').decode('ascii')
72    with warnings.catch_warnings():
73        warnings.simplefilter("ignore", DeprecationWarning)
74        gotResult = eval(fn)
75    if _norm(wantResult) != _norm(gotResult):
76        raise TestFailed("%s should return: %s but returned: %s" \
77              %(str(fn), str(wantResult), repr(gotResult)))
78
79
80def _parameterize(*parameters):
81    """Simplistic decorator to parametrize a test
82
83    Runs the decorated test multiple times in subTest, with a value from
84    'parameters' passed as an extra positional argument.
85    Calls doCleanups() after each run.
86
87    Not for general use. Intended to avoid indenting for easier backports.
88
89    See https://discuss.python.org/t/91827 for discussing generalizations.
90    """
91    def _parametrize_decorator(func):
92        def _parameterized(self, *args, **kwargs):
93            for parameter in parameters:
94                with self.subTest(parameter):
95                    func(self, *args, parameter, **kwargs)
96                self.doCleanups()
97        return _parameterized
98    return _parametrize_decorator
99
100
101class NtpathTestCase(unittest.TestCase):
102    def assertPathEqual(self, path1, path2):
103        if path1 == path2 or _norm(path1) == _norm(path2):
104            return
105        self.assertEqual(path1, path2)
106
107    def assertPathIn(self, path, pathset):
108        self.assertIn(_norm(path), _norm(pathset))
109
110
111class TestNtpath(NtpathTestCase):
112    def test_splitext(self):
113        tester('ntpath.splitext("foo.ext")', ('foo', '.ext'))
114        tester('ntpath.splitext("/foo/foo.ext")', ('/foo/foo', '.ext'))
115        tester('ntpath.splitext(".ext")', ('.ext', ''))
116        tester('ntpath.splitext("\\foo.ext\\foo")', ('\\foo.ext\\foo', ''))
117        tester('ntpath.splitext("foo.ext\\")', ('foo.ext\\', ''))
118        tester('ntpath.splitext("")', ('', ''))
119        tester('ntpath.splitext("foo.bar.ext")', ('foo.bar', '.ext'))
120        tester('ntpath.splitext("xx/foo.bar.ext")', ('xx/foo.bar', '.ext'))
121        tester('ntpath.splitext("xx\\foo.bar.ext")', ('xx\\foo.bar', '.ext'))
122        tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d'))
123
124    def test_splitdrive(self):
125        tester('ntpath.splitdrive("c:\\foo\\bar")',
126               ('c:', '\\foo\\bar'))
127        tester('ntpath.splitdrive("c:/foo/bar")',
128               ('c:', '/foo/bar'))
129        tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")',
130               ('\\\\conky\\mountpoint', '\\foo\\bar'))
131        tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")',
132               ('//conky/mountpoint', '/foo/bar'))
133        tester('ntpath.splitdrive("\\\\\\conky\\mountpoint\\foo\\bar")',
134            ('\\\\\\conky', '\\mountpoint\\foo\\bar'))
135        tester('ntpath.splitdrive("///conky/mountpoint/foo/bar")',
136            ('///conky', '/mountpoint/foo/bar'))
137        tester('ntpath.splitdrive("\\\\conky\\\\mountpoint\\foo\\bar")',
138               ('\\\\conky\\', '\\mountpoint\\foo\\bar'))
139        tester('ntpath.splitdrive("//conky//mountpoint/foo/bar")',
140               ('//conky/', '/mountpoint/foo/bar'))
141        # Issue #19911: UNC part containing U+0130
142        self.assertEqual(ntpath.splitdrive('//conky/MOUNTPOİNT/foo/bar'),
143                         ('//conky/MOUNTPOİNT', '/foo/bar'))
144
145        # gh-81790: support device namespace, including UNC drives.
146        tester('ntpath.splitdrive("//?/c:")', ("//?/c:", ""))
147        tester('ntpath.splitdrive("//?/c:/")', ("//?/c:", "/"))
148        tester('ntpath.splitdrive("//?/c:/dir")', ("//?/c:", "/dir"))
149        tester('ntpath.splitdrive("//?/UNC")', ("//?/UNC", ""))
150        tester('ntpath.splitdrive("//?/UNC/")', ("//?/UNC/", ""))
151        tester('ntpath.splitdrive("//?/UNC/server/")', ("//?/UNC/server/", ""))
152        tester('ntpath.splitdrive("//?/UNC/server/share")', ("//?/UNC/server/share", ""))
153        tester('ntpath.splitdrive("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/dir"))
154        tester('ntpath.splitdrive("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")',
155               ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/spam'))
156        tester('ntpath.splitdrive("//?/BootPartition/")', ("//?/BootPartition", "/"))
157
158        tester('ntpath.splitdrive("\\\\?\\c:")', ("\\\\?\\c:", ""))
159        tester('ntpath.splitdrive("\\\\?\\c:\\")', ("\\\\?\\c:", "\\"))
160        tester('ntpath.splitdrive("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\dir"))
161        tester('ntpath.splitdrive("\\\\?\\UNC")', ("\\\\?\\UNC", ""))
162        tester('ntpath.splitdrive("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", ""))
163        tester('ntpath.splitdrive("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", ""))
164        tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share")', ("\\\\?\\UNC\\server\\share", ""))
165        tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share\\dir")',
166               ("\\\\?\\UNC\\server\\share", "\\dir"))
167        tester('ntpath.splitdrive("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")',
168               ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\spam'))
169        tester('ntpath.splitdrive("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\"))
170
171        # gh-96290: support partial/invalid UNC drives
172        tester('ntpath.splitdrive("//")', ("//", ""))  # empty server & missing share
173        tester('ntpath.splitdrive("///")', ("///", ""))  # empty server & empty share
174        tester('ntpath.splitdrive("///y")', ("///y", ""))  # empty server & non-empty share
175        tester('ntpath.splitdrive("//x")', ("//x", ""))  # non-empty server & missing share
176        tester('ntpath.splitdrive("//x/")', ("//x/", ""))  # non-empty server & empty share
177
178    def test_split(self):
179        tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
180        tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
181               ('\\\\conky\\mountpoint\\foo', 'bar'))
182
183        tester('ntpath.split("c:\\")', ('c:\\', ''))
184        tester('ntpath.split("\\\\conky\\mountpoint\\")',
185               ('\\\\conky\\mountpoint\\', ''))
186
187        tester('ntpath.split("c:/")', ('c:/', ''))
188        tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
189
190    def test_isabs(self):
191        tester('ntpath.isabs("c:\\")', 1)
192        tester('ntpath.isabs("\\\\conky\\mountpoint\\")', 1)
193        tester('ntpath.isabs("\\foo")', 1)
194        tester('ntpath.isabs("\\foo\\bar")', 1)
195
196        # gh-96290: normal UNC paths and device paths without trailing backslashes
197        tester('ntpath.isabs("\\\\conky\\mountpoint")', 1)
198        tester('ntpath.isabs("\\\\.\\C:")', 1)
199
200    def test_commonprefix(self):
201        tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])',
202               "/home/swen")
203        tester('ntpath.commonprefix(["\\home\\swen\\spam", "\\home\\swen\\eggs"])',
204               "\\home\\swen\\")
205        tester('ntpath.commonprefix(["/home/swen/spam", "/home/swen/spam"])',
206               "/home/swen/spam")
207
208    def test_join(self):
209        tester('ntpath.join("")', '')
210        tester('ntpath.join("", "", "")', '')
211        tester('ntpath.join("a")', 'a')
212        tester('ntpath.join("/a")', '/a')
213        tester('ntpath.join("\\a")', '\\a')
214        tester('ntpath.join("a:")', 'a:')
215        tester('ntpath.join("a:", "\\b")', 'a:\\b')
216        tester('ntpath.join("a", "\\b")', '\\b')
217        tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
218        tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
219        tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
220        tester('ntpath.join("a", "b", "\\c")', '\\c')
221        tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
222        tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
223
224        tester("ntpath.join('', 'a')", 'a')
225        tester("ntpath.join('', '', '', '', 'a')", 'a')
226        tester("ntpath.join('a', '')", 'a\\')
227        tester("ntpath.join('a', '', '', '', '')", 'a\\')
228        tester("ntpath.join('a\\', '')", 'a\\')
229        tester("ntpath.join('a\\', '', '', '', '')", 'a\\')
230        tester("ntpath.join('a/', '')", 'a/')
231
232        tester("ntpath.join('a/b', 'x/y')", 'a/b\\x/y')
233        tester("ntpath.join('/a/b', 'x/y')", '/a/b\\x/y')
234        tester("ntpath.join('/a/b/', 'x/y')", '/a/b/x/y')
235        tester("ntpath.join('c:', 'x/y')", 'c:x/y')
236        tester("ntpath.join('c:a/b', 'x/y')", 'c:a/b\\x/y')
237        tester("ntpath.join('c:a/b/', 'x/y')", 'c:a/b/x/y')
238        tester("ntpath.join('c:/', 'x/y')", 'c:/x/y')
239        tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y')
240        tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y')
241        tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y')
242        tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y')
243        tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y')
244
245        tester("ntpath.join('a/b', '/x/y')", '/x/y')
246        tester("ntpath.join('/a/b', '/x/y')", '/x/y')
247        tester("ntpath.join('c:', '/x/y')", 'c:/x/y')
248        tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y')
249        tester("ntpath.join('c:/', '/x/y')", 'c:/x/y')
250        tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y')
251        tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y')
252        tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y')
253        tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y')
254
255        tester("ntpath.join('c:', 'C:x/y')", 'C:x/y')
256        tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y')
257        tester("ntpath.join('c:/', 'C:x/y')", 'C:/x/y')
258        tester("ntpath.join('c:/a/b', 'C:x/y')", 'C:/a/b\\x/y')
259
260        for x in ('', 'a/b', '/a/b', 'c:', 'c:a/b', 'c:/', 'c:/a/b',
261                  '//computer/share', '//computer/share/', '//computer/share/a/b'):
262            for y in ('d:', 'd:x/y', 'd:/', 'd:/x/y',
263                      '//machine/common', '//machine/common/', '//machine/common/x/y'):
264                tester("ntpath.join(%r, %r)" % (x, y), y)
265
266        tester("ntpath.join('\\\\computer\\share\\', 'a', 'b')", '\\\\computer\\share\\a\\b')
267        tester("ntpath.join('\\\\computer\\share', 'a', 'b')", '\\\\computer\\share\\a\\b')
268        tester("ntpath.join('\\\\computer\\share', 'a\\b')", '\\\\computer\\share\\a\\b')
269        tester("ntpath.join('//computer/share/', 'a', 'b')", '//computer/share/a\\b')
270        tester("ntpath.join('//computer/share', 'a', 'b')", '//computer/share\\a\\b')
271        tester("ntpath.join('//computer/share', 'a/b')", '//computer/share\\a/b')
272
273    def test_normpath(self):
274        tester("ntpath.normpath('A//////././//.//B')", r'A\B')
275        tester("ntpath.normpath('A/./B')", r'A\B')
276        tester("ntpath.normpath('A/foo/../B')", r'A\B')
277        tester("ntpath.normpath('C:A//B')", r'C:A\B')
278        tester("ntpath.normpath('D:A/./B')", r'D:A\B')
279        tester("ntpath.normpath('e:A/foo/../B')", r'e:A\B')
280
281        tester("ntpath.normpath('C:///A//B')", r'C:\A\B')
282        tester("ntpath.normpath('D:///A/./B')", r'D:\A\B')
283        tester("ntpath.normpath('e:///A/foo/../B')", r'e:\A\B')
284
285        tester("ntpath.normpath('..')", r'..')
286        tester("ntpath.normpath('.')", r'.')
287        tester("ntpath.normpath('')", r'.')
288        tester("ntpath.normpath('/')", '\\')
289        tester("ntpath.normpath('c:/')", 'c:\\')
290        tester("ntpath.normpath('/../.././..')", '\\')
291        tester("ntpath.normpath('c:/../../..')", 'c:\\')
292        tester("ntpath.normpath('../.././..')", r'..\..\..')
293        tester("ntpath.normpath('K:../.././..')", r'K:..\..\..')
294        tester("ntpath.normpath('C:////a/b')", r'C:\a\b')
295        tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b')
296
297        tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL')
298        tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z')
299        tester("ntpath.normpath('handbook/../../Tests/image.png')", r'..\Tests\image.png')
300        tester("ntpath.normpath('handbook/../../../Tests/image.png')", r'..\..\Tests\image.png')
301        tester("ntpath.normpath('handbook///../a/.././../b/c')", r'..\b\c')
302        tester("ntpath.normpath('handbook/a/../..///../../b/c')", r'..\..\b\c')
303
304        tester("ntpath.normpath('//server/share/..')" ,    '\\\\server\\share\\')
305        tester("ntpath.normpath('//server/share/../')" ,   '\\\\server\\share\\')
306        tester("ntpath.normpath('//server/share/../..')",  '\\\\server\\share\\')
307        tester("ntpath.normpath('//server/share/../../')", '\\\\server\\share\\')
308
309        # gh-96290: don't normalize partial/invalid UNC drives as rooted paths.
310        tester("ntpath.normpath('\\\\foo\\\\')", '\\\\foo\\\\')
311        tester("ntpath.normpath('\\\\foo\\')", '\\\\foo\\')
312        tester("ntpath.normpath('\\\\foo')", '\\\\foo')
313        tester("ntpath.normpath('\\\\')", '\\\\')
314
315    def test_realpath_curdir(self):
316        expected = ntpath.normpath(os.getcwd())
317        tester("ntpath.realpath('.')", expected)
318        tester("ntpath.realpath('./.')", expected)
319        tester("ntpath.realpath('/'.join(['.'] * 100))", expected)
320        tester("ntpath.realpath('.\\.')", expected)
321        tester("ntpath.realpath('\\'.join(['.'] * 100))", expected)
322
323    def test_realpath_curdir_strict(self):
324        expected = ntpath.normpath(os.getcwd())
325        tester("ntpath.realpath('.', strict=True)", expected)
326        tester("ntpath.realpath('./.', strict=True)", expected)
327        tester("ntpath.realpath('/'.join(['.'] * 100), strict=True)", expected)
328        tester("ntpath.realpath('.\\.', strict=True)", expected)
329        tester("ntpath.realpath('\\'.join(['.'] * 100), strict=True)", expected)
330
331    def test_realpath_curdir_missing_ok(self):
332        expected = ntpath.normpath(os.getcwd())
333        tester("ntpath.realpath('.', strict=ALLOW_MISSING)",
334               expected)
335        tester("ntpath.realpath('./.', strict=ALLOW_MISSING)",
336               expected)
337        tester("ntpath.realpath('/'.join(['.'] * 100), strict=ALLOW_MISSING)",
338               expected)
339        tester("ntpath.realpath('.\\.', strict=ALLOW_MISSING)",
340               expected)
341        tester("ntpath.realpath('\\'.join(['.'] * 100), strict=ALLOW_MISSING)",
342               expected)
343
344    def test_realpath_pardir(self):
345        expected = ntpath.normpath(os.getcwd())
346        tester("ntpath.realpath('..')", ntpath.dirname(expected))
347        tester("ntpath.realpath('../..')",
348               ntpath.dirname(ntpath.dirname(expected)))
349        tester("ntpath.realpath('/'.join(['..'] * 50))",
350               ntpath.splitdrive(expected)[0] + '\\')
351        tester("ntpath.realpath('..\\..')",
352               ntpath.dirname(ntpath.dirname(expected)))
353        tester("ntpath.realpath('\\'.join(['..'] * 50))",
354               ntpath.splitdrive(expected)[0] + '\\')
355
356    def test_realpath_pardir_strict(self):
357        expected = ntpath.normpath(os.getcwd())
358        tester("ntpath.realpath('..', strict=True)", ntpath.dirname(expected))
359        tester("ntpath.realpath('../..', strict=True)",
360               ntpath.dirname(ntpath.dirname(expected)))
361        tester("ntpath.realpath('/'.join(['..'] * 50), strict=True)",
362               ntpath.splitdrive(expected)[0] + '\\')
363        tester("ntpath.realpath('..\\..', strict=True)",
364               ntpath.dirname(ntpath.dirname(expected)))
365        tester("ntpath.realpath('\\'.join(['..'] * 50), strict=True)",
366               ntpath.splitdrive(expected)[0] + '\\')
367
368    def test_realpath_pardir_missing_ok(self):
369        expected = ntpath.normpath(os.getcwd())
370        tester("ntpath.realpath('..', strict=ALLOW_MISSING)",
371               ntpath.dirname(expected))
372        tester("ntpath.realpath('../..', strict=ALLOW_MISSING)",
373               ntpath.dirname(ntpath.dirname(expected)))
374        tester("ntpath.realpath('/'.join(['..'] * 50), strict=ALLOW_MISSING)",
375               ntpath.splitdrive(expected)[0] + '\\')
376        tester("ntpath.realpath('..\\..', strict=ALLOW_MISSING)",
377               ntpath.dirname(ntpath.dirname(expected)))
378        tester("ntpath.realpath('\\'.join(['..'] * 50), strict=ALLOW_MISSING)",
379               ntpath.splitdrive(expected)[0] + '\\')
380
381    @os_helper.skip_unless_symlink
382    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
383    @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
384    def test_realpath_basic(self, kwargs):
385        ABSTFN = ntpath.abspath(os_helper.TESTFN)
386        open(ABSTFN, "wb").close()
387        self.addCleanup(os_helper.unlink, ABSTFN)
388        self.addCleanup(os_helper.unlink, ABSTFN + "1")
389
390        os.symlink(ABSTFN, ABSTFN + "1")
391        self.assertPathEqual(ntpath.realpath(ABSTFN + "1", **kwargs), ABSTFN)
392        self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1"), **kwargs),
393                         os.fsencode(ABSTFN))
394
395        # gh-88013: call ntpath.realpath with binary drive name may raise a
396        # TypeError. The drive should not exist to reproduce the bug.
397        for c in string.ascii_uppercase:
398            d = f"{c}:\\"
399            if not ntpath.exists(d):
400                break
401        else:
402            raise OSError("No free drive letters available")
403        self.assertEqual(ntpath.realpath(d), d)
404
405        # gh-106242: Embedded nulls and non-strict fallback to abspath
406        if kwargs:
407            with self.assertRaises(OSError):
408                ntpath.realpath(os_helper.TESTFN + "\0spam",
409                                **kwargs)
410        else:
411            self.assertEqual(ABSTFN + "\0spam",
412                                ntpath.realpath(os_helper.TESTFN + "\0spam", **kwargs))
413
414    @os_helper.skip_unless_symlink
415    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
416    def test_realpath_strict(self):
417        # Bug #43757: raise FileNotFoundError in strict mode if we encounter
418        # a path that does not exist.
419        ABSTFN = ntpath.abspath(os_helper.TESTFN)
420        os.symlink(ABSTFN + "1", ABSTFN)
421        self.addCleanup(os_helper.unlink, ABSTFN)
422        self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True)
423        self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True)
424
425    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
426    def test_realpath_invalid_paths(self):
427        realpath = ntpath.realpath
428        ABSTFN = ntpath.abspath(os_helper.TESTFN)
429        ABSTFNb = os.fsencode(ABSTFN)
430        path = ABSTFN + '\x00'
431        # gh-106242: Embedded nulls and non-strict fallback to abspath
432        self.assertEqual(realpath(path, strict=False), path)
433        # gh-106242: Embedded nulls should raise OSError (not ValueError)
434        self.assertRaises(OSError, ntpath.realpath, path, strict=True)
435        self.assertRaises(OSError, ntpath.realpath, path, strict=ALLOW_MISSING)
436        path = ABSTFNb + b'\x00'
437        self.assertEqual(realpath(path, strict=False), path)
438        self.assertRaises(OSError, ntpath.realpath, path, strict=True)
439        self.assertRaises(OSError, ntpath.realpath, path, strict=ALLOW_MISSING)
440        path = ABSTFN + '\\nonexistent\\x\x00'
441        self.assertEqual(realpath(path, strict=False), path)
442        self.assertRaises(OSError, ntpath.realpath, path, strict=True)
443        self.assertRaises(OSError, ntpath.realpath, path, strict=ALLOW_MISSING)
444        path = ABSTFNb + b'\\nonexistent\\x\x00'
445        self.assertEqual(realpath(path, strict=False), path)
446        self.assertRaises(OSError, ntpath.realpath, path, strict=True)
447        self.assertRaises(OSError, ntpath.realpath, path, strict=ALLOW_MISSING)
448        path = ABSTFN + '\x00\\..'
449        self.assertEqual(realpath(path, strict=False), os.getcwd())
450        self.assertEqual(realpath(path, strict=True), os.getcwd())
451        self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwd())
452        path = ABSTFNb + b'\x00\\..'
453        self.assertEqual(realpath(path, strict=False), os.getcwdb())
454        self.assertEqual(realpath(path, strict=True), os.getcwdb())
455        self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwdb())
456        path = ABSTFN + '\\nonexistent\\x\x00\\..'
457        self.assertEqual(realpath(path, strict=False), ABSTFN + '\\nonexistent')
458        self.assertRaises(OSError, ntpath.realpath, path, strict=True)
459        self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFN + '\\nonexistent')
460        path = ABSTFNb + b'\\nonexistent\\x\x00\\..'
461        self.assertEqual(realpath(path, strict=False), ABSTFNb + b'\\nonexistent')
462        self.assertRaises(OSError, ntpath.realpath, path, strict=True)
463        self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFNb + b'\\nonexistent')
464
465    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
466    @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
467    def test_realpath_invalid_unicode_paths(self, kwargs):
468        realpath = ntpath.realpath
469        ABSTFN = ntpath.abspath(os_helper.TESTFN)
470        ABSTFNb = os.fsencode(ABSTFN)
471        path = ABSTFNb + b'\xff'
472        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
473        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
474        path = ABSTFNb + b'\\nonexistent\\\xff'
475        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
476        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
477        path = ABSTFNb + b'\xff\\..'
478        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
479        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
480        path = ABSTFNb + b'\\nonexistent\\\xff\\..'
481        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
482        self.assertRaises(UnicodeDecodeError, ntpath.realpath, path, **kwargs)
483
484    @os_helper.skip_unless_symlink
485    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
486    @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
487    def test_realpath_relative(self, kwargs):
488        ABSTFN = ntpath.abspath(os_helper.TESTFN)
489        open(ABSTFN, "wb").close()
490        self.addCleanup(os_helper.unlink, ABSTFN)
491        self.addCleanup(os_helper.unlink, ABSTFN + "1")
492
493        os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1"))
494        self.assertPathEqual(ntpath.realpath(ABSTFN + "1", **kwargs), ABSTFN)
495
496    @os_helper.skip_unless_symlink
497    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
498    def test_realpath_broken_symlinks(self):
499        ABSTFN = ntpath.abspath(os_helper.TESTFN)
500        os.mkdir(ABSTFN)
501        self.addCleanup(os_helper.rmtree, ABSTFN)
502
503        with os_helper.change_cwd(ABSTFN):
504            os.mkdir("subdir")
505            os.chdir("subdir")
506            os.symlink(".", "recursive")
507            os.symlink("..", "parent")
508            os.chdir("..")
509            os.symlink(".", "self")
510            os.symlink("missing", "broken")
511            os.symlink(r"broken\bar", "broken1")
512            os.symlink(r"self\self\broken", "broken2")
513            os.symlink(r"subdir\parent\subdir\parent\broken", "broken3")
514            os.symlink(ABSTFN + r"\broken", "broken4")
515            os.symlink(r"recursive\..\broken", "broken5")
516
517            self.assertPathEqual(ntpath.realpath("broken"),
518                                 ABSTFN + r"\missing")
519            self.assertPathEqual(ntpath.realpath(r"broken\foo"),
520                                 ABSTFN + r"\missing\foo")
521            # bpo-38453: We no longer recursively resolve segments of relative
522            # symlinks that the OS cannot resolve.
523            self.assertPathEqual(ntpath.realpath(r"broken1"),
524                                 ABSTFN + r"\broken\bar")
525            self.assertPathEqual(ntpath.realpath(r"broken1\baz"),
526                                 ABSTFN + r"\broken\bar\baz")
527            self.assertPathEqual(ntpath.realpath("broken2"),
528                                 ABSTFN + r"\self\self\missing")
529            self.assertPathEqual(ntpath.realpath("broken3"),
530                                 ABSTFN + r"\subdir\parent\subdir\parent\missing")
531            self.assertPathEqual(ntpath.realpath("broken4"),
532                                 ABSTFN + r"\missing")
533            self.assertPathEqual(ntpath.realpath("broken5"),
534                                 ABSTFN + r"\missing")
535
536            self.assertPathEqual(ntpath.realpath(b"broken"),
537                                 os.fsencode(ABSTFN + r"\missing"))
538            self.assertPathEqual(ntpath.realpath(rb"broken\foo"),
539                                 os.fsencode(ABSTFN + r"\missing\foo"))
540            self.assertPathEqual(ntpath.realpath(rb"broken1"),
541                                 os.fsencode(ABSTFN + r"\broken\bar"))
542            self.assertPathEqual(ntpath.realpath(rb"broken1\baz"),
543                                 os.fsencode(ABSTFN + r"\broken\bar\baz"))
544            self.assertPathEqual(ntpath.realpath(b"broken2"),
545                                 os.fsencode(ABSTFN + r"\self\self\missing"))
546            self.assertPathEqual(ntpath.realpath(rb"broken3"),
547                                 os.fsencode(ABSTFN + r"\subdir\parent\subdir\parent\missing"))
548            self.assertPathEqual(ntpath.realpath(b"broken4"),
549                                 os.fsencode(ABSTFN + r"\missing"))
550            self.assertPathEqual(ntpath.realpath(b"broken5"),
551                                 os.fsencode(ABSTFN + r"\missing"))
552
553    @os_helper.skip_unless_symlink
554    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
555    def test_realpath_symlink_loops(self):
556        # Symlink loops in non-strict mode are non-deterministic as to which
557        # path is returned, but it will always be the fully resolved path of
558        # one member of the cycle
559        ABSTFN = ntpath.abspath(os_helper.TESTFN)
560        self.addCleanup(os_helper.unlink, ABSTFN)
561        self.addCleanup(os_helper.unlink, ABSTFN + "1")
562        self.addCleanup(os_helper.unlink, ABSTFN + "2")
563        self.addCleanup(os_helper.unlink, ABSTFN + "y")
564        self.addCleanup(os_helper.unlink, ABSTFN + "c")
565        self.addCleanup(os_helper.unlink, ABSTFN + "a")
566
567        os.symlink(ABSTFN, ABSTFN)
568        self.assertPathEqual(ntpath.realpath(ABSTFN), ABSTFN)
569
570        os.symlink(ABSTFN + "1", ABSTFN + "2")
571        os.symlink(ABSTFN + "2", ABSTFN + "1")
572        expected = (ABSTFN + "1", ABSTFN + "2")
573        self.assertPathIn(ntpath.realpath(ABSTFN + "1"), expected)
574        self.assertPathIn(ntpath.realpath(ABSTFN + "2"), expected)
575
576        self.assertPathIn(ntpath.realpath(ABSTFN + "1\\x"),
577                          (ntpath.join(r, "x") for r in expected))
578        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\.."),
579                             ntpath.dirname(ABSTFN))
580        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x"),
581                             ntpath.dirname(ABSTFN) + "\\x")
582        os.symlink(ABSTFN + "x", ABSTFN + "y")
583        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\"
584                                             + ntpath.basename(ABSTFN) + "y"),
585                             ABSTFN + "x")
586        self.assertPathIn(ntpath.realpath(ABSTFN + "1\\..\\"
587                                          + ntpath.basename(ABSTFN) + "1"),
588                          expected)
589
590        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
591        self.assertPathEqual(ntpath.realpath(ABSTFN + "a"), ABSTFN + "a")
592
593        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
594                   + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
595        self.assertPathEqual(ntpath.realpath(ABSTFN + "c"), ABSTFN + "c")
596
597        # Test using relative path as well.
598        self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN)
599
600    @os_helper.skip_unless_symlink
601    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
602    def test_realpath_symlink_loops_strict(self):
603        # Symlink loops raise OSError in strict mode
604        ABSTFN = ntpath.abspath(os_helper.TESTFN)
605        self.addCleanup(os_helper.unlink, ABSTFN)
606        self.addCleanup(os_helper.unlink, ABSTFN + "1")
607        self.addCleanup(os_helper.unlink, ABSTFN + "2")
608        self.addCleanup(os_helper.unlink, ABSTFN + "y")
609        self.addCleanup(os_helper.unlink, ABSTFN + "c")
610        self.addCleanup(os_helper.unlink, ABSTFN + "a")
611
612        os.symlink(ABSTFN, ABSTFN)
613        self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=True)
614
615        os.symlink(ABSTFN + "1", ABSTFN + "2")
616        os.symlink(ABSTFN + "2", ABSTFN + "1")
617        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1", strict=True)
618        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2", strict=True)
619        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x", strict=True)
620        # Windows eliminates '..' components before resolving links, so the
621        # following call is not expected to raise.
622        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..", strict=True),
623                             ntpath.dirname(ABSTFN))
624        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\x", strict=True)
625        os.symlink(ABSTFN + "x", ABSTFN + "y")
626        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\"
627                                             + ntpath.basename(ABSTFN) + "y",
628                                             strict=True)
629        self.assertRaises(OSError, ntpath.realpath,
630                          ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1",
631                          strict=True)
632
633        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
634        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a", strict=True)
635
636        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
637                   + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
638        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c", strict=True)
639
640        # Test using relative path as well.
641        self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN),
642                          strict=True)
643
644    @os_helper.skip_unless_symlink
645    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
646    def test_realpath_symlink_loops_raise(self):
647        # Symlink loops raise OSError in ALLOW_MISSING mode
648        ABSTFN = ntpath.abspath(os_helper.TESTFN)
649        self.addCleanup(os_helper.unlink, ABSTFN)
650        self.addCleanup(os_helper.unlink, ABSTFN + "1")
651        self.addCleanup(os_helper.unlink, ABSTFN + "2")
652        self.addCleanup(os_helper.unlink, ABSTFN + "y")
653        self.addCleanup(os_helper.unlink, ABSTFN + "c")
654        self.addCleanup(os_helper.unlink, ABSTFN + "a")
655        self.addCleanup(os_helper.unlink, ABSTFN + "x")
656
657        os.symlink(ABSTFN, ABSTFN)
658        self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=ALLOW_MISSING)
659
660        os.symlink(ABSTFN + "1", ABSTFN + "2")
661        os.symlink(ABSTFN + "2", ABSTFN + "1")
662        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1",
663                            strict=ALLOW_MISSING)
664        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2",
665                            strict=ALLOW_MISSING)
666        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x",
667                            strict=ALLOW_MISSING)
668
669        # Windows eliminates '..' components before resolving links;
670        # realpath is not expected to raise if this removes the loop.
671        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\.."),
672                             ntpath.dirname(ABSTFN))
673        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x"),
674                             ntpath.dirname(ABSTFN) + "\\x")
675
676        os.symlink(ABSTFN + "x", ABSTFN + "y")
677        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\"
678                                             + ntpath.basename(ABSTFN) + "y"),
679                             ABSTFN + "x")
680        self.assertRaises(
681            OSError, ntpath.realpath,
682            ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1",
683            strict=ALLOW_MISSING)
684
685        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
686        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a",
687                            strict=ALLOW_MISSING)
688
689        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
690                + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
691        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c",
692                            strict=ALLOW_MISSING)
693
694        # Test using relative path as well.
695        self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN),
696                            strict=ALLOW_MISSING)
697
698    @os_helper.skip_unless_symlink
699    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
700    @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING})
701    def test_realpath_symlink_prefix(self, kwargs):
702        ABSTFN = ntpath.abspath(os_helper.TESTFN)
703        self.addCleanup(os_helper.unlink, ABSTFN + "3")
704        self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.")
705        self.addCleanup(os_helper.unlink, ABSTFN + "3link")
706        self.addCleanup(os_helper.unlink, ABSTFN + "3.link")
707
708        with open(ABSTFN + "3", "wb") as f:
709            f.write(b'0')
710        os.symlink(ABSTFN + "3", ABSTFN + "3link")
711
712        with open("\\\\?\\" + ABSTFN + "3.", "wb") as f:
713            f.write(b'1')
714        os.symlink("\\\\?\\" + ABSTFN + "3.", ABSTFN + "3.link")
715
716        self.assertPathEqual(ntpath.realpath(ABSTFN + "3link", **kwargs),
717                             ABSTFN + "3")
718        self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link", **kwargs),
719                             "\\\\?\\" + ABSTFN + "3.")
720
721        # Resolved paths should be usable to open target files
722        with open(ntpath.realpath(ABSTFN + "3link"), "rb") as f:
723            self.assertEqual(f.read(), b'0')
724        with open(ntpath.realpath(ABSTFN + "3.link"), "rb") as f:
725            self.assertEqual(f.read(), b'1')
726
727        # When the prefix is included, it is not stripped
728        self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link", **kwargs),
729                             "\\\\?\\" + ABSTFN + "3")
730        self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link", **kwargs),
731                             "\\\\?\\" + ABSTFN + "3.")
732
733    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
734    def test_realpath_nul(self):
735        tester("ntpath.realpath('NUL')", r'\\.\NUL')
736        tester("ntpath.realpath('NUL', strict=False)", r'\\.\NUL')
737        tester("ntpath.realpath('NUL', strict=True)", r'\\.\NUL')
738        tester("ntpath.realpath('NUL', strict=ALLOW_MISSING)", r'\\.\NUL')
739
740    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
741    @unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname')
742    def test_realpath_cwd(self):
743        ABSTFN = ntpath.abspath(os_helper.TESTFN)
744
745        os_helper.unlink(ABSTFN)
746        os_helper.rmtree(ABSTFN)
747        os.mkdir(ABSTFN)
748        self.addCleanup(os_helper.rmtree, ABSTFN)
749
750        test_dir_long = ntpath.join(ABSTFN, "MyVeryLongDirectoryName")
751        os.mkdir(test_dir_long)
752
753        test_dir_short = _getshortpathname(test_dir_long)
754        test_file_long = ntpath.join(test_dir_long, "file.txt")
755        test_file_short = ntpath.join(test_dir_short, "file.txt")
756
757        with open(test_file_long, "wb") as f:
758            f.write(b"content")
759
760        self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short))
761
762        for kwargs in {}, {'strict': True}, {'strict': ALLOW_MISSING}:
763            with self.subTest(**kwargs):
764                with os_helper.change_cwd(test_dir_long):
765                    self.assertPathEqual(
766                        test_file_long,
767                        ntpath.realpath("file.txt", **kwargs))
768                with os_helper.change_cwd(test_dir_long.lower()):
769                    self.assertPathEqual(
770                        test_file_long,
771                        ntpath.realpath("file.txt", **kwargs))
772                with os_helper.change_cwd(test_dir_short):
773                    self.assertPathEqual(
774                        test_file_long,
775                        ntpath.realpath("file.txt", **kwargs))
776
777    def test_expandvars(self):
778        with os_helper.EnvironmentVarGuard() as env:
779            env.clear()
780            env["foo"] = "bar"
781            env["{foo"] = "baz1"
782            env["{foo}"] = "baz2"
783            tester('ntpath.expandvars("foo")', "foo")
784            tester('ntpath.expandvars("$foo bar")', "bar bar")
785            tester('ntpath.expandvars("${foo}bar")', "barbar")
786            tester('ntpath.expandvars("$[foo]bar")', "$[foo]bar")
787            tester('ntpath.expandvars("$bar bar")', "$bar bar")
788            tester('ntpath.expandvars("$?bar")', "$?bar")
789            tester('ntpath.expandvars("$foo}bar")', "bar}bar")
790            tester('ntpath.expandvars("${foo")', "${foo")
791            tester('ntpath.expandvars("${{foo}}")', "baz1}")
792            tester('ntpath.expandvars("$foo$foo")', "barbar")
793            tester('ntpath.expandvars("$bar$bar")', "$bar$bar")
794            tester('ntpath.expandvars("%foo% bar")', "bar bar")
795            tester('ntpath.expandvars("%foo%bar")', "barbar")
796            tester('ntpath.expandvars("%foo%%foo%")', "barbar")
797            tester('ntpath.expandvars("%%foo%%foo%foo%")', "%foo%foobar")
798            tester('ntpath.expandvars("%?bar%")', "%?bar%")
799            tester('ntpath.expandvars("%foo%%bar")', "bar%bar")
800            tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
801            tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%")
802
803    @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
804    def test_expandvars_nonascii(self):
805        def check(value, expected):
806            tester('ntpath.expandvars(%r)' % value, expected)
807        with os_helper.EnvironmentVarGuard() as env:
808            env.clear()
809            nonascii = os_helper.FS_NONASCII
810            env['spam'] = nonascii
811            env[nonascii] = 'ham' + nonascii
812            check('$spam bar', '%s bar' % nonascii)
813            check('$%s bar' % nonascii, '$%s bar' % nonascii)
814            check('${spam}bar', '%sbar' % nonascii)
815            check('${%s}bar' % nonascii, 'ham%sbar' % nonascii)
816            check('$spam}bar', '%s}bar' % nonascii)
817            check('$%s}bar' % nonascii, '$%s}bar' % nonascii)
818            check('%spam% bar', '%s bar' % nonascii)
819            check('%{}% bar'.format(nonascii), 'ham%s bar' % nonascii)
820            check('%spam%bar', '%sbar' % nonascii)
821            check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
822
823    def test_expanduser(self):
824        tester('ntpath.expanduser("test")', 'test')
825
826        with os_helper.EnvironmentVarGuard() as env:
827            env.clear()
828            tester('ntpath.expanduser("~test")', '~test')
829
830            env['HOMEDRIVE'] = 'C:\\'
831            env['HOMEPATH'] = 'Users\\eric'
832            env['USERNAME'] = 'eric'
833            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
834            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
835
836            del env['HOMEDRIVE']
837            tester('ntpath.expanduser("~test")', 'Users\\test')
838            tester('ntpath.expanduser("~")', 'Users\\eric')
839
840            env.clear()
841            env['USERPROFILE'] = 'C:\\Users\\eric'
842            env['USERNAME'] = 'eric'
843            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
844            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
845            tester('ntpath.expanduser("~test\\foo\\bar")',
846                   'C:\\Users\\test\\foo\\bar')
847            tester('ntpath.expanduser("~test/foo/bar")',
848                   'C:\\Users\\test/foo/bar')
849            tester('ntpath.expanduser("~\\foo\\bar")',
850                   'C:\\Users\\eric\\foo\\bar')
851            tester('ntpath.expanduser("~/foo/bar")',
852                   'C:\\Users\\eric/foo/bar')
853
854            # bpo-36264: ignore `HOME` when set on windows
855            env.clear()
856            env['HOME'] = 'F:\\'
857            env['USERPROFILE'] = 'C:\\Users\\eric'
858            env['USERNAME'] = 'eric'
859            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
860            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
861
862            # bpo-39899: don't guess another user's home directory if
863            # `%USERNAME% != basename(%USERPROFILE%)`
864            env.clear()
865            env['USERPROFILE'] = 'C:\\Users\\eric'
866            env['USERNAME'] = 'idle'
867            tester('ntpath.expanduser("~test")', '~test')
868            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
869
870
871
872    @unittest.skipUnless(nt, "abspath requires 'nt' module")
873    def test_abspath(self):
874        tester('ntpath.abspath("C:\\")', "C:\\")
875        tester('ntpath.abspath("\\\\?\\C:////spam////eggs. . .")', "\\\\?\\C:\\spam\\eggs")
876        tester('ntpath.abspath("\\\\.\\C:////spam////eggs. . .")', "\\\\.\\C:\\spam\\eggs")
877        tester('ntpath.abspath("//spam//eggs. . .")',     "\\\\spam\\eggs")
878        tester('ntpath.abspath("\\\\spam\\\\eggs. . .")', "\\\\spam\\eggs")
879        tester('ntpath.abspath("C:/spam. . .")',  "C:\\spam")
880        tester('ntpath.abspath("C:\\spam. . .")', "C:\\spam")
881        tester('ntpath.abspath("C:/nul")',  "\\\\.\\nul")
882        tester('ntpath.abspath("C:\\nul")', "\\\\.\\nul")
883        tester('ntpath.abspath("//..")',           "\\\\")
884        tester('ntpath.abspath("//../")',          "\\\\..\\")
885        tester('ntpath.abspath("//../..")',        "\\\\..\\")
886        tester('ntpath.abspath("//../../")',       "\\\\..\\..\\")
887        tester('ntpath.abspath("//../../../")',    "\\\\..\\..\\")
888        tester('ntpath.abspath("//../../../..")',  "\\\\..\\..\\")
889        tester('ntpath.abspath("//../../../../")', "\\\\..\\..\\")
890        tester('ntpath.abspath("//server")',           "\\\\server")
891        tester('ntpath.abspath("//server/")',          "\\\\server\\")
892        tester('ntpath.abspath("//server/..")',        "\\\\server\\")
893        tester('ntpath.abspath("//server/../")',       "\\\\server\\..\\")
894        tester('ntpath.abspath("//server/../..")',     "\\\\server\\..\\")
895        tester('ntpath.abspath("//server/../../")',    "\\\\server\\..\\")
896        tester('ntpath.abspath("//server/../../..")',  "\\\\server\\..\\")
897        tester('ntpath.abspath("//server/../../../")', "\\\\server\\..\\")
898        tester('ntpath.abspath("//server/share")',        "\\\\server\\share")
899        tester('ntpath.abspath("//server/share/")',       "\\\\server\\share\\")
900        tester('ntpath.abspath("//server/share/..")',     "\\\\server\\share\\")
901        tester('ntpath.abspath("//server/share/../")',    "\\\\server\\share\\")
902        tester('ntpath.abspath("//server/share/../..")',  "\\\\server\\share\\")
903        tester('ntpath.abspath("//server/share/../../")', "\\\\server\\share\\")
904        tester('ntpath.abspath("C:\\nul. . .")', "\\\\.\\nul")
905        tester('ntpath.abspath("//... . .")',  "\\\\")
906        tester('ntpath.abspath("//.. . . .")', "\\\\")
907        tester('ntpath.abspath("//../... . .")',  "\\\\..\\")
908        tester('ntpath.abspath("//../.. . . .")', "\\\\..\\")
909        with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: # bpo-31047
910            tester('ntpath.abspath("")', cwd_dir)
911            tester('ntpath.abspath(" ")', cwd_dir + "\\ ")
912            tester('ntpath.abspath("?")', cwd_dir + "\\?")
913            drive, _ = ntpath.splitdrive(cwd_dir)
914            tester('ntpath.abspath("/abc/")', drive + "\\abc")
915
916    def test_relpath(self):
917        tester('ntpath.relpath("a")', 'a')
918        tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
919        tester('ntpath.relpath("a/b")', 'a\\b')
920        tester('ntpath.relpath("../a/b")', '..\\a\\b')
921        with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir:
922            currentdir = ntpath.basename(cwd_dir)
923            tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a')
924            tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b')
925        tester('ntpath.relpath("a", "b/c")', '..\\..\\a')
926        tester('ntpath.relpath("c:/foo/bar/bat", "c:/x/y")', '..\\..\\foo\\bar\\bat')
927        tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a')
928        tester('ntpath.relpath("a", "a")', '.')
929        tester('ntpath.relpath("/foo/bar/bat", "/x/y/z")', '..\\..\\..\\foo\\bar\\bat')
930        tester('ntpath.relpath("/foo/bar/bat", "/foo/bar")', 'bat')
931        tester('ntpath.relpath("/foo/bar/bat", "/")', 'foo\\bar\\bat')
932        tester('ntpath.relpath("/", "/foo/bar/bat")', '..\\..\\..')
933        tester('ntpath.relpath("/foo/bar/bat", "/x")', '..\\foo\\bar\\bat')
934        tester('ntpath.relpath("/x", "/foo/bar/bat")', '..\\..\\..\\x')
935        tester('ntpath.relpath("/", "/")', '.')
936        tester('ntpath.relpath("/a", "/a")', '.')
937        tester('ntpath.relpath("/a/b", "/a/b")', '.')
938        tester('ntpath.relpath("c:/foo", "C:/FOO")', '.')
939
940    def test_commonpath(self):
941        def check(paths, expected):
942            tester(('ntpath.commonpath(%r)' % paths).replace('\\\\', '\\'),
943                   expected)
944        def check_error(exc, paths):
945            self.assertRaises(exc, ntpath.commonpath, paths)
946            self.assertRaises(exc, ntpath.commonpath,
947                              [os.fsencode(p) for p in paths])
948
949        self.assertRaises(ValueError, ntpath.commonpath, [])
950        check_error(ValueError, ['C:\\Program Files', 'Program Files'])
951        check_error(ValueError, ['C:\\Program Files', 'C:Program Files'])
952        check_error(ValueError, ['\\Program Files', 'Program Files'])
953        check_error(ValueError, ['Program Files', 'C:\\Program Files'])
954        check(['C:\\Program Files'], 'C:\\Program Files')
955        check(['C:\\Program Files', 'C:\\Program Files'], 'C:\\Program Files')
956        check(['C:\\Program Files\\', 'C:\\Program Files'],
957              'C:\\Program Files')
958        check(['C:\\Program Files\\', 'C:\\Program Files\\'],
959              'C:\\Program Files')
960        check(['C:\\\\Program Files', 'C:\\Program Files\\\\'],
961              'C:\\Program Files')
962        check(['C:\\.\\Program Files', 'C:\\Program Files\\.'],
963              'C:\\Program Files')
964        check(['C:\\', 'C:\\bin'], 'C:\\')
965        check(['C:\\Program Files', 'C:\\bin'], 'C:\\')
966        check(['C:\\Program Files', 'C:\\Program Files\\Bar'],
967              'C:\\Program Files')
968        check(['C:\\Program Files\\Foo', 'C:\\Program Files\\Bar'],
969              'C:\\Program Files')
970        check(['C:\\Program Files', 'C:\\Projects'], 'C:\\')
971        check(['C:\\Program Files\\', 'C:\\Projects'], 'C:\\')
972
973        check(['C:\\Program Files\\Foo', 'C:/Program Files/Bar'],
974              'C:\\Program Files')
975        check(['C:\\Program Files\\Foo', 'c:/program files/bar'],
976              'C:\\Program Files')
977        check(['c:/program files/bar', 'C:\\Program Files\\Foo'],
978              'c:\\program files')
979
980        check_error(ValueError, ['C:\\Program Files', 'D:\\Program Files'])
981
982        check(['spam'], 'spam')
983        check(['spam', 'spam'], 'spam')
984        check(['spam', 'alot'], '')
985        check(['and\\jam', 'and\\spam'], 'and')
986        check(['and\\\\jam', 'and\\spam\\\\'], 'and')
987        check(['and\\.\\jam', '.\\and\\spam'], 'and')
988        check(['and\\jam', 'and\\spam', 'alot'], '')
989        check(['and\\jam', 'and\\spam', 'and'], 'and')
990        check(['C:and\\jam', 'C:and\\spam'], 'C:and')
991
992        check([''], '')
993        check(['', 'spam\\alot'], '')
994        check_error(ValueError, ['', '\\spam\\alot'])
995
996        self.assertRaises(TypeError, ntpath.commonpath,
997                          [b'C:\\Program Files', 'C:\\Program Files\\Foo'])
998        self.assertRaises(TypeError, ntpath.commonpath,
999                          [b'C:\\Program Files', 'Program Files\\Foo'])
1000        self.assertRaises(TypeError, ntpath.commonpath,
1001                          [b'Program Files', 'C:\\Program Files\\Foo'])
1002        self.assertRaises(TypeError, ntpath.commonpath,
1003                          ['C:\\Program Files', b'C:\\Program Files\\Foo'])
1004        self.assertRaises(TypeError, ntpath.commonpath,
1005                          ['C:\\Program Files', b'Program Files\\Foo'])
1006        self.assertRaises(TypeError, ntpath.commonpath,
1007                          ['Program Files', b'C:\\Program Files\\Foo'])
1008
1009    @unittest.skipIf(is_emscripten, "Emscripten cannot fstat unnamed files.")
1010    def test_sameopenfile(self):
1011        with TemporaryFile() as tf1, TemporaryFile() as tf2:
1012            # Make sure the same file is really the same
1013            self.assertTrue(ntpath.sameopenfile(tf1.fileno(), tf1.fileno()))
1014            # Make sure different files are really different
1015            self.assertFalse(ntpath.sameopenfile(tf1.fileno(), tf2.fileno()))
1016            # Make sure invalid values don't cause issues on win32
1017            if sys.platform == "win32":
1018                with self.assertRaises(OSError):
1019                    # Invalid file descriptors shouldn't display assert
1020                    # dialogs (#4804)
1021                    ntpath.sameopenfile(-1, -1)
1022
1023    def test_ismount(self):
1024        self.assertTrue(ntpath.ismount("c:\\"))
1025        self.assertTrue(ntpath.ismount("C:\\"))
1026        self.assertTrue(ntpath.ismount("c:/"))
1027        self.assertTrue(ntpath.ismount("C:/"))
1028        self.assertTrue(ntpath.ismount("\\\\.\\c:\\"))
1029        self.assertTrue(ntpath.ismount("\\\\.\\C:\\"))
1030
1031        self.assertTrue(ntpath.ismount(b"c:\\"))
1032        self.assertTrue(ntpath.ismount(b"C:\\"))
1033        self.assertTrue(ntpath.ismount(b"c:/"))
1034        self.assertTrue(ntpath.ismount(b"C:/"))
1035        self.assertTrue(ntpath.ismount(b"\\\\.\\c:\\"))
1036        self.assertTrue(ntpath.ismount(b"\\\\.\\C:\\"))
1037
1038        with os_helper.temp_dir() as d:
1039            self.assertFalse(ntpath.ismount(d))
1040
1041        if sys.platform == "win32":
1042            #
1043            # Make sure the current folder isn't the root folder
1044            # (or any other volume root). The drive-relative
1045            # locations below cannot then refer to mount points
1046            #
1047            test_cwd = os.getenv("SystemRoot")
1048            drive, path = ntpath.splitdrive(test_cwd)
1049            with os_helper.change_cwd(test_cwd):
1050                self.assertFalse(ntpath.ismount(drive.lower()))
1051                self.assertFalse(ntpath.ismount(drive.upper()))
1052
1053            self.assertTrue(ntpath.ismount("\\\\localhost\\c$"))
1054            self.assertTrue(ntpath.ismount("\\\\localhost\\c$\\"))
1055
1056            self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
1057            self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
1058
1059    def assertEqualCI(self, s1, s2):
1060        """Assert that two strings are equal ignoring case differences."""
1061        self.assertEqual(s1.lower(), s2.lower())
1062
1063    @unittest.skipUnless(nt, "OS helpers require 'nt' module")
1064    def test_nt_helpers(self):
1065        # Trivial validation that the helpers do not break, and support both
1066        # unicode and bytes (UTF-8) paths
1067
1068        executable = nt._getfinalpathname(sys.executable)
1069
1070        for path in executable, os.fsencode(executable):
1071            volume_path = nt._getvolumepathname(path)
1072            path_drive = ntpath.splitdrive(path)[0]
1073            volume_path_drive = ntpath.splitdrive(volume_path)[0]
1074            self.assertEqualCI(path_drive, volume_path_drive)
1075
1076        cap, free = nt._getdiskusage(sys.exec_prefix)
1077        self.assertGreater(cap, 0)
1078        self.assertGreater(free, 0)
1079        b_cap, b_free = nt._getdiskusage(sys.exec_prefix.encode())
1080        # Free space may change, so only test the capacity is equal
1081        self.assertEqual(b_cap, cap)
1082        self.assertGreater(b_free, 0)
1083
1084        for path in [sys.prefix, sys.executable]:
1085            final_path = nt._getfinalpathname(path)
1086            self.assertIsInstance(final_path, str)
1087            self.assertGreater(len(final_path), 0)
1088
1089            b_final_path = nt._getfinalpathname(path.encode())
1090            self.assertIsInstance(b_final_path, bytes)
1091            self.assertGreater(len(b_final_path), 0)
1092
1093class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
1094    pathmodule = ntpath
1095    attributes = ['relpath']
1096
1097
1098class PathLikeTests(NtpathTestCase):
1099
1100    path = ntpath
1101
1102    def setUp(self):
1103        self.file_name = os_helper.TESTFN
1104        self.file_path = FakePath(os_helper.TESTFN)
1105        self.addCleanup(os_helper.unlink, self.file_name)
1106        with open(self.file_name, 'xb', 0) as file:
1107            file.write(b"test_ntpath.PathLikeTests")
1108
1109    def _check_function(self, func):
1110        self.assertPathEqual(func(self.file_path), func(self.file_name))
1111
1112    def test_path_normcase(self):
1113        self._check_function(self.path.normcase)
1114        if sys.platform == 'win32':
1115            self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
1116
1117    def test_path_isabs(self):
1118        self._check_function(self.path.isabs)
1119
1120    def test_path_join(self):
1121        self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
1122                         self.path.join('a', 'b', 'c'))
1123
1124    def test_path_split(self):
1125        self._check_function(self.path.split)
1126
1127    def test_path_splitext(self):
1128        self._check_function(self.path.splitext)
1129
1130    def test_path_splitdrive(self):
1131        self._check_function(self.path.splitdrive)
1132
1133    def test_path_basename(self):
1134        self._check_function(self.path.basename)
1135
1136    def test_path_dirname(self):
1137        self._check_function(self.path.dirname)
1138
1139    def test_path_islink(self):
1140        self._check_function(self.path.islink)
1141
1142    def test_path_lexists(self):
1143        self._check_function(self.path.lexists)
1144
1145    def test_path_ismount(self):
1146        self._check_function(self.path.ismount)
1147
1148    def test_path_expanduser(self):
1149        self._check_function(self.path.expanduser)
1150
1151    def test_path_expandvars(self):
1152        self._check_function(self.path.expandvars)
1153
1154    def test_path_normpath(self):
1155        self._check_function(self.path.normpath)
1156
1157    def test_path_abspath(self):
1158        self._check_function(self.path.abspath)
1159
1160    def test_path_realpath(self):
1161        self._check_function(self.path.realpath)
1162
1163    def test_path_relpath(self):
1164        self._check_function(self.path.relpath)
1165
1166    def test_path_commonpath(self):
1167        common_path = self.path.commonpath([self.file_path, self.file_name])
1168        self.assertPathEqual(common_path, self.file_name)
1169
1170    def test_path_isdir(self):
1171        self._check_function(self.path.isdir)
1172
1173
1174if __name__ == "__main__":
1175    unittest.main()
1176