• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import inspect
2import ntpath
3import os
4import string
5import subprocess
6import sys
7import unittest
8import warnings
9from test.support import cpython_only, 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
80class NtpathTestCase(unittest.TestCase):
81    def assertPathEqual(self, path1, path2):
82        if path1 == path2 or _norm(path1) == _norm(path2):
83            return
84        self.assertEqual(path1, path2)
85
86    def assertPathIn(self, path, pathset):
87        self.assertIn(_norm(path), _norm(pathset))
88
89
90class TestNtpath(NtpathTestCase):
91    def test_splitext(self):
92        tester('ntpath.splitext("foo.ext")', ('foo', '.ext'))
93        tester('ntpath.splitext("/foo/foo.ext")', ('/foo/foo', '.ext'))
94        tester('ntpath.splitext(".ext")', ('.ext', ''))
95        tester('ntpath.splitext("\\foo.ext\\foo")', ('\\foo.ext\\foo', ''))
96        tester('ntpath.splitext("foo.ext\\")', ('foo.ext\\', ''))
97        tester('ntpath.splitext("")', ('', ''))
98        tester('ntpath.splitext("foo.bar.ext")', ('foo.bar', '.ext'))
99        tester('ntpath.splitext("xx/foo.bar.ext")', ('xx/foo.bar', '.ext'))
100        tester('ntpath.splitext("xx\\foo.bar.ext")', ('xx\\foo.bar', '.ext'))
101        tester('ntpath.splitext("c:a/b\\c.d")', ('c:a/b\\c', '.d'))
102
103    def test_splitdrive(self):
104        tester("ntpath.splitdrive('')", ('', ''))
105        tester("ntpath.splitdrive('foo')", ('', 'foo'))
106        tester("ntpath.splitdrive('foo\\bar')", ('', 'foo\\bar'))
107        tester("ntpath.splitdrive('foo/bar')", ('', 'foo/bar'))
108        tester("ntpath.splitdrive('\\')", ('', '\\'))
109        tester("ntpath.splitdrive('/')", ('', '/'))
110        tester("ntpath.splitdrive('\\foo\\bar')", ('', '\\foo\\bar'))
111        tester("ntpath.splitdrive('/foo/bar')", ('', '/foo/bar'))
112        tester('ntpath.splitdrive("c:foo\\bar")', ('c:', 'foo\\bar'))
113        tester('ntpath.splitdrive("c:foo/bar")', ('c:', 'foo/bar'))
114        tester('ntpath.splitdrive("c:\\foo\\bar")', ('c:', '\\foo\\bar'))
115        tester('ntpath.splitdrive("c:/foo/bar")', ('c:', '/foo/bar'))
116        tester("ntpath.splitdrive('\\\\')", ('\\\\', ''))
117        tester("ntpath.splitdrive('//')", ('//', ''))
118        tester('ntpath.splitdrive("\\\\conky\\mountpoint\\foo\\bar")',
119               ('\\\\conky\\mountpoint', '\\foo\\bar'))
120        tester('ntpath.splitdrive("//conky/mountpoint/foo/bar")',
121               ('//conky/mountpoint', '/foo/bar'))
122        tester('ntpath.splitdrive("\\\\?\\UNC\\server\\share\\dir")',
123               ("\\\\?\\UNC\\server\\share", "\\dir"))
124        tester('ntpath.splitdrive("//?/UNC/server/share/dir")',
125               ("//?/UNC/server/share", "/dir"))
126
127    def test_splitroot(self):
128        tester("ntpath.splitroot('')", ('', '', ''))
129        tester("ntpath.splitroot('foo')", ('', '', 'foo'))
130        tester("ntpath.splitroot('foo\\bar')", ('', '', 'foo\\bar'))
131        tester("ntpath.splitroot('foo/bar')", ('', '', 'foo/bar'))
132        tester("ntpath.splitroot('\\')", ('', '\\', ''))
133        tester("ntpath.splitroot('/')", ('', '/', ''))
134        tester("ntpath.splitroot('\\foo\\bar')", ('', '\\', 'foo\\bar'))
135        tester("ntpath.splitroot('/foo/bar')", ('', '/', 'foo/bar'))
136        tester('ntpath.splitroot("c:foo\\bar")', ('c:', '', 'foo\\bar'))
137        tester('ntpath.splitroot("c:foo/bar")', ('c:', '', 'foo/bar'))
138        tester('ntpath.splitroot("c:\\foo\\bar")', ('c:', '\\', 'foo\\bar'))
139        tester('ntpath.splitroot("c:/foo/bar")', ('c:', '/', 'foo/bar'))
140
141        # Redundant slashes are not included in the root.
142        tester("ntpath.splitroot('c:\\\\a')", ('c:', '\\', '\\a'))
143        tester("ntpath.splitroot('c:\\\\\\a/b')", ('c:', '\\', '\\\\a/b'))
144
145        # Mixed path separators.
146        tester("ntpath.splitroot('c:/\\')", ('c:', '/', '\\'))
147        tester("ntpath.splitroot('c:\\/')", ('c:', '\\', '/'))
148        tester("ntpath.splitroot('/\\a/b\\/\\')", ('/\\a/b', '\\', '/\\'))
149        tester("ntpath.splitroot('\\/a\\b/\\/')", ('\\/a\\b', '/', '\\/'))
150
151        # UNC paths.
152        tester("ntpath.splitroot('\\\\')", ('\\\\', '', ''))
153        tester("ntpath.splitroot('//')", ('//', '', ''))
154        tester('ntpath.splitroot("\\\\conky\\mountpoint\\foo\\bar")',
155               ('\\\\conky\\mountpoint', '\\', 'foo\\bar'))
156        tester('ntpath.splitroot("//conky/mountpoint/foo/bar")',
157               ('//conky/mountpoint', '/', 'foo/bar'))
158        tester('ntpath.splitroot("\\\\\\conky\\mountpoint\\foo\\bar")',
159            ('\\\\\\conky', '\\', 'mountpoint\\foo\\bar'))
160        tester('ntpath.splitroot("///conky/mountpoint/foo/bar")',
161            ('///conky', '/', 'mountpoint/foo/bar'))
162        tester('ntpath.splitroot("\\\\conky\\\\mountpoint\\foo\\bar")',
163               ('\\\\conky\\', '\\', 'mountpoint\\foo\\bar'))
164        tester('ntpath.splitroot("//conky//mountpoint/foo/bar")',
165               ('//conky/', '/', 'mountpoint/foo/bar'))
166
167        # Issue #19911: UNC part containing U+0130
168        self.assertEqual(ntpath.splitroot('//conky/MOUNTPOİNT/foo/bar'),
169                         ('//conky/MOUNTPOİNT', '/', 'foo/bar'))
170
171        # gh-81790: support device namespace, including UNC drives.
172        tester('ntpath.splitroot("//?/c:")', ("//?/c:", "", ""))
173        tester('ntpath.splitroot("//./c:")', ("//./c:", "", ""))
174        tester('ntpath.splitroot("//?/c:/")', ("//?/c:", "/", ""))
175        tester('ntpath.splitroot("//?/c:/dir")', ("//?/c:", "/", "dir"))
176        tester('ntpath.splitroot("//?/UNC")', ("//?/UNC", "", ""))
177        tester('ntpath.splitroot("//?/UNC/")', ("//?/UNC/", "", ""))
178        tester('ntpath.splitroot("//?/UNC/server/")', ("//?/UNC/server/", "", ""))
179        tester('ntpath.splitroot("//?/UNC/server/share")', ("//?/UNC/server/share", "", ""))
180        tester('ntpath.splitroot("//?/UNC/server/share/dir")', ("//?/UNC/server/share", "/", "dir"))
181        tester('ntpath.splitroot("//?/VOLUME{00000000-0000-0000-0000-000000000000}/spam")',
182               ('//?/VOLUME{00000000-0000-0000-0000-000000000000}', '/', 'spam'))
183        tester('ntpath.splitroot("//?/BootPartition/")', ("//?/BootPartition", "/", ""))
184        tester('ntpath.splitroot("//./BootPartition/")', ("//./BootPartition", "/", ""))
185        tester('ntpath.splitroot("//./PhysicalDrive0")', ("//./PhysicalDrive0", "", ""))
186        tester('ntpath.splitroot("//./nul")', ("//./nul", "", ""))
187
188        tester('ntpath.splitroot("\\\\?\\c:")', ("\\\\?\\c:", "", ""))
189        tester('ntpath.splitroot("\\\\.\\c:")', ("\\\\.\\c:", "", ""))
190        tester('ntpath.splitroot("\\\\?\\c:\\")', ("\\\\?\\c:", "\\", ""))
191        tester('ntpath.splitroot("\\\\?\\c:\\dir")', ("\\\\?\\c:", "\\", "dir"))
192        tester('ntpath.splitroot("\\\\?\\UNC")', ("\\\\?\\UNC", "", ""))
193        tester('ntpath.splitroot("\\\\?\\UNC\\")', ("\\\\?\\UNC\\", "", ""))
194        tester('ntpath.splitroot("\\\\?\\UNC\\server\\")', ("\\\\?\\UNC\\server\\", "", ""))
195        tester('ntpath.splitroot("\\\\?\\UNC\\server\\share")',
196               ("\\\\?\\UNC\\server\\share", "", ""))
197        tester('ntpath.splitroot("\\\\?\\UNC\\server\\share\\dir")',
198               ("\\\\?\\UNC\\server\\share", "\\", "dir"))
199        tester('ntpath.splitroot("\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}\\spam")',
200               ('\\\\?\\VOLUME{00000000-0000-0000-0000-000000000000}', '\\', 'spam'))
201        tester('ntpath.splitroot("\\\\?\\BootPartition\\")', ("\\\\?\\BootPartition", "\\", ""))
202        tester('ntpath.splitroot("\\\\.\\BootPartition\\")', ("\\\\.\\BootPartition", "\\", ""))
203        tester('ntpath.splitroot("\\\\.\\PhysicalDrive0")', ("\\\\.\\PhysicalDrive0", "", ""))
204        tester('ntpath.splitroot("\\\\.\\nul")', ("\\\\.\\nul", "", ""))
205
206        # gh-96290: support partial/invalid UNC drives
207        tester('ntpath.splitroot("//")', ("//", "", ""))  # empty server & missing share
208        tester('ntpath.splitroot("///")', ("///", "", ""))  # empty server & empty share
209        tester('ntpath.splitroot("///y")', ("///y", "", ""))  # empty server & non-empty share
210        tester('ntpath.splitroot("//x")', ("//x", "", ""))  # non-empty server & missing share
211        tester('ntpath.splitroot("//x/")', ("//x/", "", ""))  # non-empty server & empty share
212
213        # gh-101363: match GetFullPathNameW() drive letter parsing behaviour
214        tester('ntpath.splitroot(" :/foo")', (" :", "/", "foo"))
215        tester('ntpath.splitroot("/:/foo")', ("", "/", ":/foo"))
216
217    def test_split(self):
218        tester('ntpath.split("c:\\foo\\bar")', ('c:\\foo', 'bar'))
219        tester('ntpath.split("\\\\conky\\mountpoint\\foo\\bar")',
220               ('\\\\conky\\mountpoint\\foo', 'bar'))
221
222        tester('ntpath.split("c:\\")', ('c:\\', ''))
223        tester('ntpath.split("\\\\conky\\mountpoint\\")',
224               ('\\\\conky\\mountpoint\\', ''))
225
226        tester('ntpath.split("c:/")', ('c:/', ''))
227        tester('ntpath.split("//conky/mountpoint/")', ('//conky/mountpoint/', ''))
228
229    def test_isabs(self):
230        tester('ntpath.isabs("foo\\bar")', 0)
231        tester('ntpath.isabs("foo/bar")', 0)
232        tester('ntpath.isabs("c:\\")', 1)
233        tester('ntpath.isabs("c:\\foo\\bar")', 1)
234        tester('ntpath.isabs("c:/foo/bar")', 1)
235        tester('ntpath.isabs("\\\\conky\\mountpoint\\")', 1)
236
237        # gh-44626: paths with only a drive or root are not absolute.
238        tester('ntpath.isabs("\\foo\\bar")', 0)
239        tester('ntpath.isabs("/foo/bar")', 0)
240        tester('ntpath.isabs("c:foo\\bar")', 0)
241        tester('ntpath.isabs("c:foo/bar")', 0)
242
243        # gh-96290: normal UNC paths and device paths without trailing backslashes
244        tester('ntpath.isabs("\\\\conky\\mountpoint")', 1)
245        tester('ntpath.isabs("\\\\.\\C:")', 1)
246
247    def test_commonprefix(self):
248        tester('ntpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"])',
249               "/home/swen")
250        tester('ntpath.commonprefix(["\\home\\swen\\spam", "\\home\\swen\\eggs"])',
251               "\\home\\swen\\")
252        tester('ntpath.commonprefix(["/home/swen/spam", "/home/swen/spam"])',
253               "/home/swen/spam")
254
255    def test_join(self):
256        tester('ntpath.join("")', '')
257        tester('ntpath.join("", "", "")', '')
258        tester('ntpath.join("a")', 'a')
259        tester('ntpath.join("/a")', '/a')
260        tester('ntpath.join("\\a")', '\\a')
261        tester('ntpath.join("a:")', 'a:')
262        tester('ntpath.join("a:", "\\b")', 'a:\\b')
263        tester('ntpath.join("a", "\\b")', '\\b')
264        tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
265        tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
266        tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
267        tester('ntpath.join("a", "b", "c\\")', 'a\\b\\c\\')
268        tester('ntpath.join("a", "b", "\\c")', '\\c')
269        tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
270        tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
271
272        tester("ntpath.join('', 'a')", 'a')
273        tester("ntpath.join('', '', '', '', 'a')", 'a')
274        tester("ntpath.join('a', '')", 'a\\')
275        tester("ntpath.join('a', '', '', '', '')", 'a\\')
276        tester("ntpath.join('a\\', '')", 'a\\')
277        tester("ntpath.join('a\\', '', '', '', '')", 'a\\')
278        tester("ntpath.join('a/', '')", 'a/')
279
280        tester("ntpath.join('a/b', 'x/y')", 'a/b\\x/y')
281        tester("ntpath.join('/a/b', 'x/y')", '/a/b\\x/y')
282        tester("ntpath.join('/a/b/', 'x/y')", '/a/b/x/y')
283        tester("ntpath.join('c:', 'x/y')", 'c:x/y')
284        tester("ntpath.join('c:a/b', 'x/y')", 'c:a/b\\x/y')
285        tester("ntpath.join('c:a/b/', 'x/y')", 'c:a/b/x/y')
286        tester("ntpath.join('c:/', 'x/y')", 'c:/x/y')
287        tester("ntpath.join('c:/a/b', 'x/y')", 'c:/a/b\\x/y')
288        tester("ntpath.join('c:/a/b/', 'x/y')", 'c:/a/b/x/y')
289        tester("ntpath.join('//computer/share', 'x/y')", '//computer/share\\x/y')
290        tester("ntpath.join('//computer/share/', 'x/y')", '//computer/share/x/y')
291        tester("ntpath.join('//computer/share/a/b', 'x/y')", '//computer/share/a/b\\x/y')
292
293        tester("ntpath.join('a/b', '/x/y')", '/x/y')
294        tester("ntpath.join('/a/b', '/x/y')", '/x/y')
295        tester("ntpath.join('c:', '/x/y')", 'c:/x/y')
296        tester("ntpath.join('c:a/b', '/x/y')", 'c:/x/y')
297        tester("ntpath.join('c:/', '/x/y')", 'c:/x/y')
298        tester("ntpath.join('c:/a/b', '/x/y')", 'c:/x/y')
299        tester("ntpath.join('//computer/share', '/x/y')", '//computer/share/x/y')
300        tester("ntpath.join('//computer/share/', '/x/y')", '//computer/share/x/y')
301        tester("ntpath.join('//computer/share/a', '/x/y')", '//computer/share/x/y')
302
303        tester("ntpath.join('c:', 'C:x/y')", 'C:x/y')
304        tester("ntpath.join('c:a/b', 'C:x/y')", 'C:a/b\\x/y')
305        tester("ntpath.join('c:/', 'C:x/y')", 'C:/x/y')
306        tester("ntpath.join('c:/a/b', 'C:x/y')", 'C:/a/b\\x/y')
307
308        for x in ('', 'a/b', '/a/b', 'c:', 'c:a/b', 'c:/', 'c:/a/b',
309                  '//computer/share', '//computer/share/', '//computer/share/a/b'):
310            for y in ('d:', 'd:x/y', 'd:/', 'd:/x/y',
311                      '//machine/common', '//machine/common/', '//machine/common/x/y'):
312                tester("ntpath.join(%r, %r)" % (x, y), y)
313
314        tester("ntpath.join('\\\\computer\\share\\', 'a', 'b')", '\\\\computer\\share\\a\\b')
315        tester("ntpath.join('\\\\computer\\share', 'a', 'b')", '\\\\computer\\share\\a\\b')
316        tester("ntpath.join('\\\\computer\\share', 'a\\b')", '\\\\computer\\share\\a\\b')
317        tester("ntpath.join('//computer/share/', 'a', 'b')", '//computer/share/a\\b')
318        tester("ntpath.join('//computer/share', 'a', 'b')", '//computer/share\\a\\b')
319        tester("ntpath.join('//computer/share', 'a/b')", '//computer/share\\a/b')
320
321        tester("ntpath.join('\\\\', 'computer')", '\\\\computer')
322        tester("ntpath.join('\\\\computer\\', 'share')", '\\\\computer\\share')
323        tester("ntpath.join('\\\\computer\\share\\', 'a')", '\\\\computer\\share\\a')
324        tester("ntpath.join('\\\\computer\\share\\a\\', 'b')", '\\\\computer\\share\\a\\b')
325        # Second part is anchored, so that the first part is ignored.
326        tester("ntpath.join('a', 'Z:b', 'c')", 'Z:b\\c')
327        tester("ntpath.join('a', 'Z:\\b', 'c')", 'Z:\\b\\c')
328        tester("ntpath.join('a', '\\\\b\\c', 'd')", '\\\\b\\c\\d')
329        # Second part has a root but not drive.
330        tester("ntpath.join('a', '\\b', 'c')", '\\b\\c')
331        tester("ntpath.join('Z:/a', '/b', 'c')", 'Z:\\b\\c')
332        tester("ntpath.join('//?/Z:/a', '/b', 'c')",  '\\\\?\\Z:\\b\\c')
333        tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b')
334        tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b')
335
336    def test_normpath(self):
337        tester("ntpath.normpath('A//////././//.//B')", r'A\B')
338        tester("ntpath.normpath('A/./B')", r'A\B')
339        tester("ntpath.normpath('A/foo/../B')", r'A\B')
340        tester("ntpath.normpath('C:A//B')", r'C:A\B')
341        tester("ntpath.normpath('D:A/./B')", r'D:A\B')
342        tester("ntpath.normpath('e:A/foo/../B')", r'e:A\B')
343
344        tester("ntpath.normpath('C:///A//B')", r'C:\A\B')
345        tester("ntpath.normpath('D:///A/./B')", r'D:\A\B')
346        tester("ntpath.normpath('e:///A/foo/../B')", r'e:\A\B')
347
348        tester("ntpath.normpath('..')", r'..')
349        tester("ntpath.normpath('.')", r'.')
350        tester("ntpath.normpath('c:.')", 'c:')
351        tester("ntpath.normpath('')", r'.')
352        tester("ntpath.normpath('/')", '\\')
353        tester("ntpath.normpath('c:/')", 'c:\\')
354        tester("ntpath.normpath('/../.././..')", '\\')
355        tester("ntpath.normpath('c:/../../..')", 'c:\\')
356        tester("ntpath.normpath('/./a/b')", r'\a\b')
357        tester("ntpath.normpath('c:/./a/b')", r'c:\a\b')
358        tester("ntpath.normpath('../.././..')", r'..\..\..')
359        tester("ntpath.normpath('K:../.././..')", r'K:..\..\..')
360        tester("ntpath.normpath('./a/b')", r'a\b')
361        tester("ntpath.normpath('c:./a/b')", r'c:a\b')
362        tester("ntpath.normpath('C:////a/b')", r'C:\a\b')
363        tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b')
364
365        tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL')
366        tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z')
367        tester("ntpath.normpath('handbook/../../Tests/image.png')", r'..\Tests\image.png')
368        tester("ntpath.normpath('handbook/../../../Tests/image.png')", r'..\..\Tests\image.png')
369        tester("ntpath.normpath('handbook///../a/.././../b/c')", r'..\b\c')
370        tester("ntpath.normpath('handbook/a/../..///../../b/c')", r'..\..\b\c')
371
372        tester("ntpath.normpath('//server/share/..')" ,    '\\\\server\\share\\')
373        tester("ntpath.normpath('//server/share/../')" ,   '\\\\server\\share\\')
374        tester("ntpath.normpath('//server/share/../..')",  '\\\\server\\share\\')
375        tester("ntpath.normpath('//server/share/../../')", '\\\\server\\share\\')
376
377        # gh-96290: don't normalize partial/invalid UNC drives as rooted paths.
378        tester("ntpath.normpath('\\\\foo\\\\')", '\\\\foo\\\\')
379        tester("ntpath.normpath('\\\\foo\\')", '\\\\foo\\')
380        tester("ntpath.normpath('\\\\foo')", '\\\\foo')
381        tester("ntpath.normpath('\\\\')", '\\\\')
382        tester("ntpath.normpath('//?/UNC/server/share/..')", '\\\\?\\UNC\\server\\share\\')
383
384    def test_realpath_curdir(self):
385        expected = ntpath.normpath(os.getcwd())
386        tester("ntpath.realpath('.')", expected)
387        tester("ntpath.realpath('./.')", expected)
388        tester("ntpath.realpath('/'.join(['.'] * 100))", expected)
389        tester("ntpath.realpath('.\\.')", expected)
390        tester("ntpath.realpath('\\'.join(['.'] * 100))", expected)
391
392    def test_realpath_pardir(self):
393        expected = ntpath.normpath(os.getcwd())
394        tester("ntpath.realpath('..')", ntpath.dirname(expected))
395        tester("ntpath.realpath('../..')",
396               ntpath.dirname(ntpath.dirname(expected)))
397        tester("ntpath.realpath('/'.join(['..'] * 50))",
398               ntpath.splitdrive(expected)[0] + '\\')
399        tester("ntpath.realpath('..\\..')",
400               ntpath.dirname(ntpath.dirname(expected)))
401        tester("ntpath.realpath('\\'.join(['..'] * 50))",
402               ntpath.splitdrive(expected)[0] + '\\')
403
404    @os_helper.skip_unless_symlink
405    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
406    def test_realpath_basic(self):
407        ABSTFN = ntpath.abspath(os_helper.TESTFN)
408        open(ABSTFN, "wb").close()
409        self.addCleanup(os_helper.unlink, ABSTFN)
410        self.addCleanup(os_helper.unlink, ABSTFN + "1")
411
412        os.symlink(ABSTFN, ABSTFN + "1")
413        self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN)
414        self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")),
415                         os.fsencode(ABSTFN))
416
417        # gh-88013: call ntpath.realpath with binary drive name may raise a
418        # TypeError. The drive should not exist to reproduce the bug.
419        drives = {f"{c}:\\" for c in string.ascii_uppercase} - set(os.listdrives())
420        d = drives.pop().encode()
421        self.assertEqual(ntpath.realpath(d), d)
422
423        # gh-106242: Embedded nulls and non-strict fallback to abspath
424        self.assertEqual(ABSTFN + "\0spam",
425                         ntpath.realpath(os_helper.TESTFN + "\0spam", strict=False))
426
427    @os_helper.skip_unless_symlink
428    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
429    def test_realpath_strict(self):
430        # Bug #43757: raise FileNotFoundError in strict mode if we encounter
431        # a path that does not exist.
432        ABSTFN = ntpath.abspath(os_helper.TESTFN)
433        os.symlink(ABSTFN + "1", ABSTFN)
434        self.addCleanup(os_helper.unlink, ABSTFN)
435        self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN, strict=True)
436        self.assertRaises(FileNotFoundError, ntpath.realpath, ABSTFN + "2", strict=True)
437        # gh-106242: Embedded nulls should raise OSError (not ValueError)
438        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "\0spam", strict=True)
439
440    @os_helper.skip_unless_symlink
441    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
442    def test_realpath_relative(self):
443        ABSTFN = ntpath.abspath(os_helper.TESTFN)
444        open(ABSTFN, "wb").close()
445        self.addCleanup(os_helper.unlink, ABSTFN)
446        self.addCleanup(os_helper.unlink, ABSTFN + "1")
447
448        os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1"))
449        self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN)
450
451    @os_helper.skip_unless_symlink
452    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
453    def test_realpath_broken_symlinks(self):
454        ABSTFN = ntpath.abspath(os_helper.TESTFN)
455        os.mkdir(ABSTFN)
456        self.addCleanup(os_helper.rmtree, ABSTFN)
457
458        with os_helper.change_cwd(ABSTFN):
459            os.mkdir("subdir")
460            os.chdir("subdir")
461            os.symlink(".", "recursive")
462            os.symlink("..", "parent")
463            os.chdir("..")
464            os.symlink(".", "self")
465            os.symlink("missing", "broken")
466            os.symlink(r"broken\bar", "broken1")
467            os.symlink(r"self\self\broken", "broken2")
468            os.symlink(r"subdir\parent\subdir\parent\broken", "broken3")
469            os.symlink(ABSTFN + r"\broken", "broken4")
470            os.symlink(r"recursive\..\broken", "broken5")
471
472            self.assertPathEqual(ntpath.realpath("broken"),
473                                 ABSTFN + r"\missing")
474            self.assertPathEqual(ntpath.realpath(r"broken\foo"),
475                                 ABSTFN + r"\missing\foo")
476            # bpo-38453: We no longer recursively resolve segments of relative
477            # symlinks that the OS cannot resolve.
478            self.assertPathEqual(ntpath.realpath(r"broken1"),
479                                 ABSTFN + r"\broken\bar")
480            self.assertPathEqual(ntpath.realpath(r"broken1\baz"),
481                                 ABSTFN + r"\broken\bar\baz")
482            self.assertPathEqual(ntpath.realpath("broken2"),
483                                 ABSTFN + r"\self\self\missing")
484            self.assertPathEqual(ntpath.realpath("broken3"),
485                                 ABSTFN + r"\subdir\parent\subdir\parent\missing")
486            self.assertPathEqual(ntpath.realpath("broken4"),
487                                 ABSTFN + r"\missing")
488            self.assertPathEqual(ntpath.realpath("broken5"),
489                                 ABSTFN + r"\missing")
490
491            self.assertPathEqual(ntpath.realpath(b"broken"),
492                                 os.fsencode(ABSTFN + r"\missing"))
493            self.assertPathEqual(ntpath.realpath(rb"broken\foo"),
494                                 os.fsencode(ABSTFN + r"\missing\foo"))
495            self.assertPathEqual(ntpath.realpath(rb"broken1"),
496                                 os.fsencode(ABSTFN + r"\broken\bar"))
497            self.assertPathEqual(ntpath.realpath(rb"broken1\baz"),
498                                 os.fsencode(ABSTFN + r"\broken\bar\baz"))
499            self.assertPathEqual(ntpath.realpath(b"broken2"),
500                                 os.fsencode(ABSTFN + r"\self\self\missing"))
501            self.assertPathEqual(ntpath.realpath(rb"broken3"),
502                                 os.fsencode(ABSTFN + r"\subdir\parent\subdir\parent\missing"))
503            self.assertPathEqual(ntpath.realpath(b"broken4"),
504                                 os.fsencode(ABSTFN + r"\missing"))
505            self.assertPathEqual(ntpath.realpath(b"broken5"),
506                                 os.fsencode(ABSTFN + r"\missing"))
507
508    @os_helper.skip_unless_symlink
509    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
510    def test_realpath_symlink_loops(self):
511        # Symlink loops in non-strict mode are non-deterministic as to which
512        # path is returned, but it will always be the fully resolved path of
513        # one member of the cycle
514        ABSTFN = ntpath.abspath(os_helper.TESTFN)
515        self.addCleanup(os_helper.unlink, ABSTFN)
516        self.addCleanup(os_helper.unlink, ABSTFN + "1")
517        self.addCleanup(os_helper.unlink, ABSTFN + "2")
518        self.addCleanup(os_helper.unlink, ABSTFN + "y")
519        self.addCleanup(os_helper.unlink, ABSTFN + "c")
520        self.addCleanup(os_helper.unlink, ABSTFN + "a")
521
522        os.symlink(ABSTFN, ABSTFN)
523        self.assertPathEqual(ntpath.realpath(ABSTFN), ABSTFN)
524
525        os.symlink(ABSTFN + "1", ABSTFN + "2")
526        os.symlink(ABSTFN + "2", ABSTFN + "1")
527        expected = (ABSTFN + "1", ABSTFN + "2")
528        self.assertPathIn(ntpath.realpath(ABSTFN + "1"), expected)
529        self.assertPathIn(ntpath.realpath(ABSTFN + "2"), expected)
530
531        self.assertPathIn(ntpath.realpath(ABSTFN + "1\\x"),
532                          (ntpath.join(r, "x") for r in expected))
533        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\.."),
534                             ntpath.dirname(ABSTFN))
535        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x"),
536                             ntpath.dirname(ABSTFN) + "\\x")
537        os.symlink(ABSTFN + "x", ABSTFN + "y")
538        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\"
539                                             + ntpath.basename(ABSTFN) + "y"),
540                             ABSTFN + "x")
541        self.assertPathIn(ntpath.realpath(ABSTFN + "1\\..\\"
542                                          + ntpath.basename(ABSTFN) + "1"),
543                          expected)
544
545        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
546        self.assertPathEqual(ntpath.realpath(ABSTFN + "a"), ABSTFN + "a")
547
548        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
549                   + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
550        self.assertPathEqual(ntpath.realpath(ABSTFN + "c"), ABSTFN + "c")
551
552        # Test using relative path as well.
553        self.assertPathEqual(ntpath.realpath(ntpath.basename(ABSTFN)), ABSTFN)
554
555    @os_helper.skip_unless_symlink
556    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
557    def test_realpath_symlink_loops_strict(self):
558        # Symlink loops raise OSError in strict mode
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.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=True)
569
570        os.symlink(ABSTFN + "1", ABSTFN + "2")
571        os.symlink(ABSTFN + "2", ABSTFN + "1")
572        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1", strict=True)
573        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2", strict=True)
574        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x", strict=True)
575        # Windows eliminates '..' components before resolving links, so the
576        # following call is not expected to raise.
577        self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..", strict=True),
578                             ntpath.dirname(ABSTFN))
579        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\x", strict=True)
580        os.symlink(ABSTFN + "x", ABSTFN + "y")
581        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\..\\"
582                                             + ntpath.basename(ABSTFN) + "y",
583                                             strict=True)
584        self.assertRaises(OSError, ntpath.realpath,
585                          ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1",
586                          strict=True)
587
588        os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a")
589        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a", strict=True)
590
591        os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN))
592                   + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c")
593        self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c", strict=True)
594
595        # Test using relative path as well.
596        self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN),
597                          strict=True)
598
599    @os_helper.skip_unless_symlink
600    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
601    def test_realpath_symlink_prefix(self):
602        ABSTFN = ntpath.abspath(os_helper.TESTFN)
603        self.addCleanup(os_helper.unlink, ABSTFN + "3")
604        self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.")
605        self.addCleanup(os_helper.unlink, ABSTFN + "3link")
606        self.addCleanup(os_helper.unlink, ABSTFN + "3.link")
607
608        with open(ABSTFN + "3", "wb") as f:
609            f.write(b'0')
610        os.symlink(ABSTFN + "3", ABSTFN + "3link")
611
612        with open("\\\\?\\" + ABSTFN + "3.", "wb") as f:
613            f.write(b'1')
614        os.symlink("\\\\?\\" + ABSTFN + "3.", ABSTFN + "3.link")
615
616        self.assertPathEqual(ntpath.realpath(ABSTFN + "3link"),
617                             ABSTFN + "3")
618        self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link"),
619                             "\\\\?\\" + ABSTFN + "3.")
620
621        # Resolved paths should be usable to open target files
622        with open(ntpath.realpath(ABSTFN + "3link"), "rb") as f:
623            self.assertEqual(f.read(), b'0')
624        with open(ntpath.realpath(ABSTFN + "3.link"), "rb") as f:
625            self.assertEqual(f.read(), b'1')
626
627        # When the prefix is included, it is not stripped
628        self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link"),
629                             "\\\\?\\" + ABSTFN + "3")
630        self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link"),
631                             "\\\\?\\" + ABSTFN + "3.")
632
633    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
634    def test_realpath_nul(self):
635        tester("ntpath.realpath('NUL')", r'\\.\NUL')
636
637    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
638    @unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname')
639    def test_realpath_cwd(self):
640        ABSTFN = ntpath.abspath(os_helper.TESTFN)
641
642        os_helper.unlink(ABSTFN)
643        os_helper.rmtree(ABSTFN)
644        os.mkdir(ABSTFN)
645        self.addCleanup(os_helper.rmtree, ABSTFN)
646
647        test_dir_long = ntpath.join(ABSTFN, "MyVeryLongDirectoryName")
648        os.mkdir(test_dir_long)
649
650        test_dir_short = _getshortpathname(test_dir_long)
651        test_file_long = ntpath.join(test_dir_long, "file.txt")
652        test_file_short = ntpath.join(test_dir_short, "file.txt")
653
654        with open(test_file_long, "wb") as f:
655            f.write(b"content")
656
657        self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short))
658
659        with os_helper.change_cwd(test_dir_long):
660            self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
661        with os_helper.change_cwd(test_dir_long.lower()):
662            self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
663        with os_helper.change_cwd(test_dir_short):
664            self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
665
666    @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
667    def test_realpath_permission(self):
668        # Test whether python can resolve the real filename of a
669        # shortened file name even if it does not have permission to access it.
670        ABSTFN = ntpath.realpath(os_helper.TESTFN)
671
672        os_helper.unlink(ABSTFN)
673        os_helper.rmtree(ABSTFN)
674        os.mkdir(ABSTFN)
675        self.addCleanup(os_helper.rmtree, ABSTFN)
676
677        test_file = ntpath.join(ABSTFN, "LongFileName123.txt")
678        test_file_short = ntpath.join(ABSTFN, "LONGFI~1.TXT")
679
680        with open(test_file, "wb") as f:
681            f.write(b"content")
682        # Automatic generation of short names may be disabled on
683        # NTFS volumes for the sake of performance.
684        # They're not supported at all on ReFS and exFAT.
685        subprocess.run(
686            # Try to set the short name manually.
687            ['fsutil.exe', 'file', 'setShortName', test_file, 'LONGFI~1.TXT'],
688            creationflags=subprocess.DETACHED_PROCESS
689        )
690
691        try:
692            self.assertPathEqual(test_file, ntpath.realpath(test_file_short))
693        except AssertionError:
694            raise unittest.SkipTest('the filesystem seems to lack support for short filenames')
695
696        # Deny the right to [S]YNCHRONIZE on the file to
697        # force nt._getfinalpathname to fail with ERROR_ACCESS_DENIED.
698        p = subprocess.run(
699            ['icacls.exe', test_file, '/deny', '*S-1-5-32-545:(S)'],
700            creationflags=subprocess.DETACHED_PROCESS
701        )
702
703        if p.returncode:
704            raise unittest.SkipTest('failed to deny access to the test file')
705
706        self.assertPathEqual(test_file, ntpath.realpath(test_file_short))
707
708    def test_expandvars(self):
709        with os_helper.EnvironmentVarGuard() as env:
710            env.clear()
711            env["foo"] = "bar"
712            env["{foo"] = "baz1"
713            env["{foo}"] = "baz2"
714            tester('ntpath.expandvars("foo")', "foo")
715            tester('ntpath.expandvars("$foo bar")', "bar bar")
716            tester('ntpath.expandvars("${foo}bar")', "barbar")
717            tester('ntpath.expandvars("$[foo]bar")', "$[foo]bar")
718            tester('ntpath.expandvars("$bar bar")', "$bar bar")
719            tester('ntpath.expandvars("$?bar")', "$?bar")
720            tester('ntpath.expandvars("$foo}bar")', "bar}bar")
721            tester('ntpath.expandvars("${foo")', "${foo")
722            tester('ntpath.expandvars("${{foo}}")', "baz1}")
723            tester('ntpath.expandvars("$foo$foo")', "barbar")
724            tester('ntpath.expandvars("$bar$bar")', "$bar$bar")
725            tester('ntpath.expandvars("%foo% bar")', "bar bar")
726            tester('ntpath.expandvars("%foo%bar")', "barbar")
727            tester('ntpath.expandvars("%foo%%foo%")', "barbar")
728            tester('ntpath.expandvars("%%foo%%foo%foo%")', "%foo%foobar")
729            tester('ntpath.expandvars("%?bar%")', "%?bar%")
730            tester('ntpath.expandvars("%foo%%bar")', "bar%bar")
731            tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
732            tester('ntpath.expandvars("bar\'%foo%")', "bar\'%foo%")
733
734    @unittest.skipUnless(os_helper.FS_NONASCII, 'need os_helper.FS_NONASCII')
735    def test_expandvars_nonascii(self):
736        def check(value, expected):
737            tester('ntpath.expandvars(%r)' % value, expected)
738        with os_helper.EnvironmentVarGuard() as env:
739            env.clear()
740            nonascii = os_helper.FS_NONASCII
741            env['spam'] = nonascii
742            env[nonascii] = 'ham' + nonascii
743            check('$spam bar', '%s bar' % nonascii)
744            check('$%s bar' % nonascii, '$%s bar' % nonascii)
745            check('${spam}bar', '%sbar' % nonascii)
746            check('${%s}bar' % nonascii, 'ham%sbar' % nonascii)
747            check('$spam}bar', '%s}bar' % nonascii)
748            check('$%s}bar' % nonascii, '$%s}bar' % nonascii)
749            check('%spam% bar', '%s bar' % nonascii)
750            check('%{}% bar'.format(nonascii), 'ham%s bar' % nonascii)
751            check('%spam%bar', '%sbar' % nonascii)
752            check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
753
754    def test_expanduser(self):
755        tester('ntpath.expanduser("test")', 'test')
756
757        with os_helper.EnvironmentVarGuard() as env:
758            env.clear()
759            tester('ntpath.expanduser("~test")', '~test')
760
761            env['HOMEDRIVE'] = 'C:\\'
762            env['HOMEPATH'] = 'Users\\eric'
763            env['USERNAME'] = 'eric'
764            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
765            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
766
767            del env['HOMEDRIVE']
768            tester('ntpath.expanduser("~test")', 'Users\\test')
769            tester('ntpath.expanduser("~")', 'Users\\eric')
770
771            env.clear()
772            env['USERPROFILE'] = 'C:\\Users\\eric'
773            env['USERNAME'] = 'eric'
774            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
775            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
776            tester('ntpath.expanduser("~test\\foo\\bar")',
777                   'C:\\Users\\test\\foo\\bar')
778            tester('ntpath.expanduser("~test/foo/bar")',
779                   'C:\\Users\\test/foo/bar')
780            tester('ntpath.expanduser("~\\foo\\bar")',
781                   'C:\\Users\\eric\\foo\\bar')
782            tester('ntpath.expanduser("~/foo/bar")',
783                   'C:\\Users\\eric/foo/bar')
784
785            # bpo-36264: ignore `HOME` when set on windows
786            env.clear()
787            env['HOME'] = 'F:\\'
788            env['USERPROFILE'] = 'C:\\Users\\eric'
789            env['USERNAME'] = 'eric'
790            tester('ntpath.expanduser("~test")', 'C:\\Users\\test')
791            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
792
793            # bpo-39899: don't guess another user's home directory if
794            # `%USERNAME% != basename(%USERPROFILE%)`
795            env.clear()
796            env['USERPROFILE'] = 'C:\\Users\\eric'
797            env['USERNAME'] = 'idle'
798            tester('ntpath.expanduser("~test")', '~test')
799            tester('ntpath.expanduser("~")', 'C:\\Users\\eric')
800
801
802
803    @unittest.skipUnless(nt, "abspath requires 'nt' module")
804    def test_abspath(self):
805        tester('ntpath.abspath("C:\\")', "C:\\")
806        tester('ntpath.abspath("\\\\?\\C:////spam////eggs. . .")', "\\\\?\\C:\\spam\\eggs")
807        tester('ntpath.abspath("\\\\.\\C:////spam////eggs. . .")', "\\\\.\\C:\\spam\\eggs")
808        tester('ntpath.abspath("//spam//eggs. . .")',     "\\\\spam\\eggs")
809        tester('ntpath.abspath("\\\\spam\\\\eggs. . .")', "\\\\spam\\eggs")
810        tester('ntpath.abspath("C:/spam. . .")',  "C:\\spam")
811        tester('ntpath.abspath("C:\\spam. . .")', "C:\\spam")
812        tester('ntpath.abspath("C:/nul")',  "\\\\.\\nul")
813        tester('ntpath.abspath("C:\\nul")', "\\\\.\\nul")
814        self.assertTrue(ntpath.isabs(ntpath.abspath("C:spam")))
815        self.assertEqual(ntpath.abspath("C:\x00"), ntpath.join(ntpath.abspath("C:"), "\x00"))
816        self.assertEqual(ntpath.abspath("\x00:spam"), "\x00:\\spam")
817        tester('ntpath.abspath("//..")',           "\\\\")
818        tester('ntpath.abspath("//../")',          "\\\\..\\")
819        tester('ntpath.abspath("//../..")',        "\\\\..\\")
820        tester('ntpath.abspath("//../../")',       "\\\\..\\..\\")
821        tester('ntpath.abspath("//../../../")',    "\\\\..\\..\\")
822        tester('ntpath.abspath("//../../../..")',  "\\\\..\\..\\")
823        tester('ntpath.abspath("//../../../../")', "\\\\..\\..\\")
824        tester('ntpath.abspath("//server")',           "\\\\server")
825        tester('ntpath.abspath("//server/")',          "\\\\server\\")
826        tester('ntpath.abspath("//server/..")',        "\\\\server\\")
827        tester('ntpath.abspath("//server/../")',       "\\\\server\\..\\")
828        tester('ntpath.abspath("//server/../..")',     "\\\\server\\..\\")
829        tester('ntpath.abspath("//server/../../")',    "\\\\server\\..\\")
830        tester('ntpath.abspath("//server/../../..")',  "\\\\server\\..\\")
831        tester('ntpath.abspath("//server/../../../")', "\\\\server\\..\\")
832        tester('ntpath.abspath("//server/share")',        "\\\\server\\share")
833        tester('ntpath.abspath("//server/share/")',       "\\\\server\\share\\")
834        tester('ntpath.abspath("//server/share/..")',     "\\\\server\\share\\")
835        tester('ntpath.abspath("//server/share/../")',    "\\\\server\\share\\")
836        tester('ntpath.abspath("//server/share/../..")',  "\\\\server\\share\\")
837        tester('ntpath.abspath("//server/share/../../")', "\\\\server\\share\\")
838        tester('ntpath.abspath("C:\\nul. . .")', "\\\\.\\nul")
839        tester('ntpath.abspath("//... . .")',  "\\\\")
840        tester('ntpath.abspath("//.. . . .")', "\\\\")
841        tester('ntpath.abspath("//../... . .")',  "\\\\..\\")
842        tester('ntpath.abspath("//../.. . . .")', "\\\\..\\")
843        with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir: # bpo-31047
844            tester('ntpath.abspath("")', cwd_dir)
845            tester('ntpath.abspath(" ")', cwd_dir + "\\ ")
846            tester('ntpath.abspath("?")', cwd_dir + "\\?")
847            drive, _ = ntpath.splitdrive(cwd_dir)
848            tester('ntpath.abspath("/abc/")', drive + "\\abc")
849
850    def test_relpath(self):
851        tester('ntpath.relpath("a")', 'a')
852        tester('ntpath.relpath(ntpath.abspath("a"))', 'a')
853        tester('ntpath.relpath("a/b")', 'a\\b')
854        tester('ntpath.relpath("../a/b")', '..\\a\\b')
855        with os_helper.temp_cwd(os_helper.TESTFN) as cwd_dir:
856            currentdir = ntpath.basename(cwd_dir)
857            tester('ntpath.relpath("a", "../b")', '..\\'+currentdir+'\\a')
858            tester('ntpath.relpath("a/b", "../c")', '..\\'+currentdir+'\\a\\b')
859        tester('ntpath.relpath("a", "b/c")', '..\\..\\a')
860        tester('ntpath.relpath("c:/foo/bar/bat", "c:/x/y")', '..\\..\\foo\\bar\\bat')
861        tester('ntpath.relpath("//conky/mountpoint/a", "//conky/mountpoint/b/c")', '..\\..\\a')
862        tester('ntpath.relpath("a", "a")', '.')
863        tester('ntpath.relpath("/foo/bar/bat", "/x/y/z")', '..\\..\\..\\foo\\bar\\bat')
864        tester('ntpath.relpath("/foo/bar/bat", "/foo/bar")', 'bat')
865        tester('ntpath.relpath("/foo/bar/bat", "/")', 'foo\\bar\\bat')
866        tester('ntpath.relpath("/", "/foo/bar/bat")', '..\\..\\..')
867        tester('ntpath.relpath("/foo/bar/bat", "/x")', '..\\foo\\bar\\bat')
868        tester('ntpath.relpath("/x", "/foo/bar/bat")', '..\\..\\..\\x')
869        tester('ntpath.relpath("/", "/")', '.')
870        tester('ntpath.relpath("/a", "/a")', '.')
871        tester('ntpath.relpath("/a/b", "/a/b")', '.')
872        tester('ntpath.relpath("c:/foo", "C:/FOO")', '.')
873
874    def test_commonpath(self):
875        def check(paths, expected):
876            tester(('ntpath.commonpath(%r)' % paths).replace('\\\\', '\\'),
877                   expected)
878        def check_error(paths, expected):
879            self.assertRaisesRegex(ValueError, expected, ntpath.commonpath, paths)
880            self.assertRaisesRegex(ValueError, expected, ntpath.commonpath, paths[::-1])
881            self.assertRaisesRegex(ValueError, expected, ntpath.commonpath,
882                                   [os.fsencode(p) for p in paths])
883            self.assertRaisesRegex(ValueError, expected, ntpath.commonpath,
884                                   [os.fsencode(p) for p in paths[::-1]])
885
886        self.assertRaises(TypeError, ntpath.commonpath, None)
887        self.assertRaises(ValueError, ntpath.commonpath, [])
888        self.assertRaises(ValueError, ntpath.commonpath, iter([]))
889
890        # gh-117381: Logical error messages
891        check_error(['C:\\Foo', 'C:Foo'], "Can't mix absolute and relative paths")
892        check_error(['C:\\Foo', '\\Foo'], "Paths don't have the same drive")
893        check_error(['C:\\Foo', 'Foo'], "Paths don't have the same drive")
894        check_error(['C:Foo', '\\Foo'], "Paths don't have the same drive")
895        check_error(['C:Foo', 'Foo'], "Paths don't have the same drive")
896        check_error(['\\Foo', 'Foo'], "Can't mix rooted and not-rooted paths")
897
898        check(['C:\\Foo'], 'C:\\Foo')
899        check(['C:\\Foo', 'C:\\Foo'], 'C:\\Foo')
900        check(['C:\\Foo\\', 'C:\\Foo'], 'C:\\Foo')
901        check(['C:\\Foo\\', 'C:\\Foo\\'], 'C:\\Foo')
902        check(['C:\\\\Foo', 'C:\\Foo\\\\'], 'C:\\Foo')
903        check(['C:\\.\\Foo', 'C:\\Foo\\.'], 'C:\\Foo')
904        check(['C:\\', 'C:\\baz'], 'C:\\')
905        check(['C:\\Bar', 'C:\\baz'], 'C:\\')
906        check(['C:\\Foo', 'C:\\Foo\\Baz'], 'C:\\Foo')
907        check(['C:\\Foo\\Bar', 'C:\\Foo\\Baz'], 'C:\\Foo')
908        check(['C:\\Bar', 'C:\\Baz'], 'C:\\')
909        check(['C:\\Bar\\', 'C:\\Baz'], 'C:\\')
910
911        check(['C:\\Foo\\Bar', 'C:/Foo/Baz'], 'C:\\Foo')
912        check(['C:\\Foo\\Bar', 'c:/foo/baz'], 'C:\\Foo')
913        check(['c:/foo/bar', 'C:\\Foo\\Baz'], 'c:\\foo')
914
915        # gh-117381: Logical error messages
916        check_error(['C:\\Foo', 'D:\\Foo'], "Paths don't have the same drive")
917        check_error(['C:\\Foo', 'D:Foo'], "Paths don't have the same drive")
918        check_error(['C:Foo', 'D:Foo'], "Paths don't have the same drive")
919
920        check(['spam'], 'spam')
921        check(['spam', 'spam'], 'spam')
922        check(['spam', 'alot'], '')
923        check(['and\\jam', 'and\\spam'], 'and')
924        check(['and\\\\jam', 'and\\spam\\\\'], 'and')
925        check(['and\\.\\jam', '.\\and\\spam'], 'and')
926        check(['and\\jam', 'and\\spam', 'alot'], '')
927        check(['and\\jam', 'and\\spam', 'and'], 'and')
928        check(['C:and\\jam', 'C:and\\spam'], 'C:and')
929
930        check([''], '')
931        check(['', 'spam\\alot'], '')
932
933        # gh-117381: Logical error messages
934        check_error(['', '\\spam\\alot'], "Can't mix rooted and not-rooted paths")
935
936        self.assertRaises(TypeError, ntpath.commonpath, [b'C:\\Foo', 'C:\\Foo\\Baz'])
937        self.assertRaises(TypeError, ntpath.commonpath, [b'C:\\Foo', 'Foo\\Baz'])
938        self.assertRaises(TypeError, ntpath.commonpath, [b'Foo', 'C:\\Foo\\Baz'])
939        self.assertRaises(TypeError, ntpath.commonpath, ['C:\\Foo', b'C:\\Foo\\Baz'])
940        self.assertRaises(TypeError, ntpath.commonpath, ['C:\\Foo', b'Foo\\Baz'])
941        self.assertRaises(TypeError, ntpath.commonpath, ['Foo', b'C:\\Foo\\Baz'])
942
943    @unittest.skipIf(is_emscripten, "Emscripten cannot fstat unnamed files.")
944    def test_sameopenfile(self):
945        with TemporaryFile() as tf1, TemporaryFile() as tf2:
946            # Make sure the same file is really the same
947            self.assertTrue(ntpath.sameopenfile(tf1.fileno(), tf1.fileno()))
948            # Make sure different files are really different
949            self.assertFalse(ntpath.sameopenfile(tf1.fileno(), tf2.fileno()))
950            # Make sure invalid values don't cause issues on win32
951            if sys.platform == "win32":
952                with self.assertRaises(OSError):
953                    # Invalid file descriptors shouldn't display assert
954                    # dialogs (#4804)
955                    ntpath.sameopenfile(-1, -1)
956
957    def test_ismount(self):
958        self.assertTrue(ntpath.ismount("c:\\"))
959        self.assertTrue(ntpath.ismount("C:\\"))
960        self.assertTrue(ntpath.ismount("c:/"))
961        self.assertTrue(ntpath.ismount("C:/"))
962        self.assertTrue(ntpath.ismount("\\\\.\\c:\\"))
963        self.assertTrue(ntpath.ismount("\\\\.\\C:\\"))
964
965        self.assertTrue(ntpath.ismount(b"c:\\"))
966        self.assertTrue(ntpath.ismount(b"C:\\"))
967        self.assertTrue(ntpath.ismount(b"c:/"))
968        self.assertTrue(ntpath.ismount(b"C:/"))
969        self.assertTrue(ntpath.ismount(b"\\\\.\\c:\\"))
970        self.assertTrue(ntpath.ismount(b"\\\\.\\C:\\"))
971
972        with os_helper.temp_dir() as d:
973            self.assertFalse(ntpath.ismount(d))
974
975        if sys.platform == "win32":
976            #
977            # Make sure the current folder isn't the root folder
978            # (or any other volume root). The drive-relative
979            # locations below cannot then refer to mount points
980            #
981            test_cwd = os.getenv("SystemRoot")
982            drive, path = ntpath.splitdrive(test_cwd)
983            with os_helper.change_cwd(test_cwd):
984                self.assertFalse(ntpath.ismount(drive.lower()))
985                self.assertFalse(ntpath.ismount(drive.upper()))
986
987            self.assertTrue(ntpath.ismount("\\\\localhost\\c$"))
988            self.assertTrue(ntpath.ismount("\\\\localhost\\c$\\"))
989
990            self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
991            self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
992
993    def test_isreserved(self):
994        self.assertFalse(ntpath.isreserved(''))
995        self.assertFalse(ntpath.isreserved('.'))
996        self.assertFalse(ntpath.isreserved('..'))
997        self.assertFalse(ntpath.isreserved('/'))
998        self.assertFalse(ntpath.isreserved('/foo/bar'))
999        # A name that ends with a space or dot is reserved.
1000        self.assertTrue(ntpath.isreserved('foo.'))
1001        self.assertTrue(ntpath.isreserved('foo '))
1002        # ASCII control characters are reserved.
1003        self.assertTrue(ntpath.isreserved('\foo'))
1004        # Wildcard characters, colon, and pipe are reserved.
1005        self.assertTrue(ntpath.isreserved('foo*bar'))
1006        self.assertTrue(ntpath.isreserved('foo?bar'))
1007        self.assertTrue(ntpath.isreserved('foo"bar'))
1008        self.assertTrue(ntpath.isreserved('foo<bar'))
1009        self.assertTrue(ntpath.isreserved('foo>bar'))
1010        self.assertTrue(ntpath.isreserved('foo:bar'))
1011        self.assertTrue(ntpath.isreserved('foo|bar'))
1012        # Case-insensitive DOS-device names are reserved.
1013        self.assertTrue(ntpath.isreserved('nul'))
1014        self.assertTrue(ntpath.isreserved('aux'))
1015        self.assertTrue(ntpath.isreserved('prn'))
1016        self.assertTrue(ntpath.isreserved('con'))
1017        self.assertTrue(ntpath.isreserved('conin$'))
1018        self.assertTrue(ntpath.isreserved('conout$'))
1019        # COM/LPT + 1-9 or + superscript 1-3 are reserved.
1020        self.assertTrue(ntpath.isreserved('COM1'))
1021        self.assertTrue(ntpath.isreserved('LPT9'))
1022        self.assertTrue(ntpath.isreserved('com\xb9'))
1023        self.assertTrue(ntpath.isreserved('com\xb2'))
1024        self.assertTrue(ntpath.isreserved('lpt\xb3'))
1025        # DOS-device name matching ignores characters after a dot or
1026        # a colon and also ignores trailing spaces.
1027        self.assertTrue(ntpath.isreserved('NUL.txt'))
1028        self.assertTrue(ntpath.isreserved('PRN  '))
1029        self.assertTrue(ntpath.isreserved('AUX  .txt'))
1030        self.assertTrue(ntpath.isreserved('COM1:bar'))
1031        self.assertTrue(ntpath.isreserved('LPT9   :bar'))
1032        # DOS-device names are only matched at the beginning
1033        # of a path component.
1034        self.assertFalse(ntpath.isreserved('bar.com9'))
1035        self.assertFalse(ntpath.isreserved('bar.lpt9'))
1036        # The entire path is checked, except for the drive.
1037        self.assertTrue(ntpath.isreserved('c:/bar/baz/NUL'))
1038        self.assertTrue(ntpath.isreserved('c:/NUL/bar/baz'))
1039        self.assertFalse(ntpath.isreserved('//./NUL'))
1040        # Bytes are supported.
1041        self.assertFalse(ntpath.isreserved(b''))
1042        self.assertFalse(ntpath.isreserved(b'.'))
1043        self.assertFalse(ntpath.isreserved(b'..'))
1044        self.assertFalse(ntpath.isreserved(b'/'))
1045        self.assertFalse(ntpath.isreserved(b'/foo/bar'))
1046        self.assertTrue(ntpath.isreserved(b'foo.'))
1047        self.assertTrue(ntpath.isreserved(b'nul'))
1048
1049    def assertEqualCI(self, s1, s2):
1050        """Assert that two strings are equal ignoring case differences."""
1051        self.assertEqual(s1.lower(), s2.lower())
1052
1053    @unittest.skipUnless(nt, "OS helpers require 'nt' module")
1054    def test_nt_helpers(self):
1055        # Trivial validation that the helpers do not break, and support both
1056        # unicode and bytes (UTF-8) paths
1057
1058        executable = nt._getfinalpathname(sys.executable)
1059
1060        for path in executable, os.fsencode(executable):
1061            volume_path = nt._getvolumepathname(path)
1062            path_drive = ntpath.splitdrive(path)[0]
1063            volume_path_drive = ntpath.splitdrive(volume_path)[0]
1064            self.assertEqualCI(path_drive, volume_path_drive)
1065
1066        cap, free = nt._getdiskusage(sys.exec_prefix)
1067        self.assertGreater(cap, 0)
1068        self.assertGreater(free, 0)
1069        b_cap, b_free = nt._getdiskusage(sys.exec_prefix.encode())
1070        # Free space may change, so only test the capacity is equal
1071        self.assertEqual(b_cap, cap)
1072        self.assertGreater(b_free, 0)
1073
1074        for path in [sys.prefix, sys.executable]:
1075            final_path = nt._getfinalpathname(path)
1076            self.assertIsInstance(final_path, str)
1077            self.assertGreater(len(final_path), 0)
1078
1079            b_final_path = nt._getfinalpathname(path.encode())
1080            self.assertIsInstance(b_final_path, bytes)
1081            self.assertGreater(len(b_final_path), 0)
1082
1083    @unittest.skipIf(sys.platform != 'win32', "Can only test junctions with creation on win32.")
1084    def test_isjunction(self):
1085        with os_helper.temp_dir() as d:
1086            with os_helper.change_cwd(d):
1087                os.mkdir('tmpdir')
1088
1089                import _winapi
1090                try:
1091                    _winapi.CreateJunction('tmpdir', 'testjunc')
1092                except OSError:
1093                    raise unittest.SkipTest('creating the test junction failed')
1094
1095                self.assertTrue(ntpath.isjunction('testjunc'))
1096                self.assertFalse(ntpath.isjunction('tmpdir'))
1097                self.assertPathEqual(ntpath.realpath('testjunc'), ntpath.realpath('tmpdir'))
1098
1099    @unittest.skipIf(sys.platform != 'win32', "drive letters are a windows concept")
1100    def test_isfile_driveletter(self):
1101        drive = os.environ.get('SystemDrive')
1102        if drive is None or len(drive) != 2 or drive[1] != ':':
1103            raise unittest.SkipTest('SystemDrive is not defined or malformed')
1104        self.assertFalse(os.path.isfile('\\\\.\\' + drive))
1105
1106    @unittest.skipUnless(hasattr(os, 'pipe'), "need os.pipe()")
1107    def test_isfile_anonymous_pipe(self):
1108        pr, pw = os.pipe()
1109        try:
1110            self.assertFalse(ntpath.isfile(pr))
1111        finally:
1112            os.close(pr)
1113            os.close(pw)
1114
1115    @unittest.skipIf(sys.platform != 'win32', "windows only")
1116    def test_isfile_named_pipe(self):
1117        import _winapi
1118        named_pipe = f'//./PIPE/python_isfile_test_{os.getpid()}'
1119        h = _winapi.CreateNamedPipe(named_pipe,
1120                                    _winapi.PIPE_ACCESS_INBOUND,
1121                                    0, 1, 0, 0, 0, 0)
1122        try:
1123            self.assertFalse(ntpath.isfile(named_pipe))
1124        finally:
1125            _winapi.CloseHandle(h)
1126
1127    @unittest.skipIf(sys.platform != 'win32', "windows only")
1128    def test_con_device(self):
1129        self.assertFalse(os.path.isfile(r"\\.\CON"))
1130        self.assertFalse(os.path.isdir(r"\\.\CON"))
1131        self.assertFalse(os.path.islink(r"\\.\CON"))
1132        self.assertTrue(os.path.exists(r"\\.\CON"))
1133
1134    @unittest.skipIf(sys.platform != 'win32', "Fast paths are only for win32")
1135    @cpython_only
1136    def test_fast_paths_in_use(self):
1137        # There are fast paths of these functions implemented in posixmodule.c.
1138        # Confirm that they are being used, and not the Python fallbacks in
1139        # genericpath.py.
1140        self.assertTrue(os.path.splitroot is nt._path_splitroot_ex)
1141        self.assertFalse(inspect.isfunction(os.path.splitroot))
1142        self.assertTrue(os.path.normpath is nt._path_normpath)
1143        self.assertFalse(inspect.isfunction(os.path.normpath))
1144        self.assertTrue(os.path.isdir is nt._path_isdir)
1145        self.assertFalse(inspect.isfunction(os.path.isdir))
1146        self.assertTrue(os.path.isfile is nt._path_isfile)
1147        self.assertFalse(inspect.isfunction(os.path.isfile))
1148        self.assertTrue(os.path.islink is nt._path_islink)
1149        self.assertFalse(inspect.isfunction(os.path.islink))
1150        self.assertTrue(os.path.isjunction is nt._path_isjunction)
1151        self.assertFalse(inspect.isfunction(os.path.isjunction))
1152        self.assertTrue(os.path.exists is nt._path_exists)
1153        self.assertFalse(inspect.isfunction(os.path.exists))
1154        self.assertTrue(os.path.lexists is nt._path_lexists)
1155        self.assertFalse(inspect.isfunction(os.path.lexists))
1156
1157    @unittest.skipIf(os.name != 'nt', "Dev Drives only exist on Win32")
1158    def test_isdevdrive(self):
1159        # Result may be True or False, but shouldn't raise
1160        self.assertIn(ntpath.isdevdrive(os_helper.TESTFN), (True, False))
1161        # ntpath.isdevdrive can handle relative paths
1162        self.assertIn(ntpath.isdevdrive("."), (True, False))
1163        self.assertIn(ntpath.isdevdrive(b"."), (True, False))
1164        # Volume syntax is supported
1165        self.assertIn(ntpath.isdevdrive(os.listvolumes()[0]), (True, False))
1166        # Invalid volume returns False from os.path method
1167        self.assertFalse(ntpath.isdevdrive(r"\\?\Volume{00000000-0000-0000-0000-000000000000}\\"))
1168        # Invalid volume raises from underlying helper
1169        with self.assertRaises(OSError):
1170            nt._path_isdevdrive(r"\\?\Volume{00000000-0000-0000-0000-000000000000}\\")
1171
1172    @unittest.skipIf(os.name == 'nt', "isdevdrive fallback only used off Win32")
1173    def test_isdevdrive_fallback(self):
1174        # Fallback always returns False
1175        self.assertFalse(ntpath.isdevdrive(os_helper.TESTFN))
1176
1177
1178class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
1179    pathmodule = ntpath
1180    attributes = ['relpath']
1181
1182
1183class PathLikeTests(NtpathTestCase):
1184
1185    path = ntpath
1186
1187    def setUp(self):
1188        self.file_name = os_helper.TESTFN
1189        self.file_path = FakePath(os_helper.TESTFN)
1190        self.addCleanup(os_helper.unlink, self.file_name)
1191        with open(self.file_name, 'xb', 0) as file:
1192            file.write(b"test_ntpath.PathLikeTests")
1193
1194    def _check_function(self, func):
1195        self.assertPathEqual(func(self.file_path), func(self.file_name))
1196
1197    def test_path_normcase(self):
1198        self._check_function(self.path.normcase)
1199        if sys.platform == 'win32':
1200            self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
1201            self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
1202
1203    def test_path_isabs(self):
1204        self._check_function(self.path.isabs)
1205
1206    def test_path_join(self):
1207        self.assertEqual(self.path.join('a', FakePath('b'), 'c'),
1208                         self.path.join('a', 'b', 'c'))
1209
1210    def test_path_split(self):
1211        self._check_function(self.path.split)
1212
1213    def test_path_splitext(self):
1214        self._check_function(self.path.splitext)
1215
1216    def test_path_splitdrive(self):
1217        self._check_function(self.path.splitdrive)
1218
1219    def test_path_splitroot(self):
1220        self._check_function(self.path.splitroot)
1221
1222    def test_path_basename(self):
1223        self._check_function(self.path.basename)
1224
1225    def test_path_dirname(self):
1226        self._check_function(self.path.dirname)
1227
1228    def test_path_islink(self):
1229        self._check_function(self.path.islink)
1230
1231    def test_path_lexists(self):
1232        self._check_function(self.path.lexists)
1233
1234    def test_path_ismount(self):
1235        self._check_function(self.path.ismount)
1236
1237    def test_path_expanduser(self):
1238        self._check_function(self.path.expanduser)
1239
1240    def test_path_expandvars(self):
1241        self._check_function(self.path.expandvars)
1242
1243    def test_path_normpath(self):
1244        self._check_function(self.path.normpath)
1245
1246    def test_path_abspath(self):
1247        self._check_function(self.path.abspath)
1248
1249    def test_path_realpath(self):
1250        self._check_function(self.path.realpath)
1251
1252    def test_path_relpath(self):
1253        self._check_function(self.path.relpath)
1254
1255    def test_path_commonpath(self):
1256        common_path = self.path.commonpath([self.file_path, self.file_name])
1257        self.assertPathEqual(common_path, self.file_name)
1258
1259    def test_path_isdir(self):
1260        self._check_function(self.path.isdir)
1261
1262
1263if __name__ == "__main__":
1264    unittest.main()
1265