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