GH-112675: Move path joining tests into test_posixpath and test_ntpath (#112676)

In `test_pathlib`, the `check_drive_root_parts` test methods evaluated
both joining and parsing/normalisation of paths. This dates from a time
when pathlib implemented both functions itself, but nowadays path joining
is done with `posixpath.join()` and `ntpath.join()`.

This commit moves the joining-related test cases into `test_posixpath` and
`test_ntpath`.
This commit is contained in:
Barney Gale 2023-12-07 21:45:40 +00:00 committed by GitHub
parent 2c3906bc4b
commit 28b2b7407c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 95 additions and 105 deletions

View file

@ -256,6 +256,7 @@ class TestNtpath(NtpathTestCase):
tester('ntpath.join("a", "b", "c")', 'a\\b\\c') tester('ntpath.join("a", "b", "c")', 'a\\b\\c')
tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c') tester('ntpath.join("a\\", "b", "c")', 'a\\b\\c')
tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c') tester('ntpath.join("a", "b\\", "c")', 'a\\b\\c')
tester('ntpath.join("a", "b", "c\\")', 'a\\b\\c\\')
tester('ntpath.join("a", "b", "\\c")', '\\c') tester('ntpath.join("a", "b", "\\c")', '\\c')
tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep') tester('ntpath.join("d:\\", "\\pleep")', 'd:\\pleep')
tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b') tester('ntpath.join("d:\\", "a", "b")', 'd:\\a\\b')
@ -313,6 +314,16 @@ class TestNtpath(NtpathTestCase):
tester("ntpath.join('\\\\computer\\', 'share')", '\\\\computer\\share') tester("ntpath.join('\\\\computer\\', 'share')", '\\\\computer\\share')
tester("ntpath.join('\\\\computer\\share\\', 'a')", '\\\\computer\\share\\a') tester("ntpath.join('\\\\computer\\share\\', 'a')", '\\\\computer\\share\\a')
tester("ntpath.join('\\\\computer\\share\\a\\', 'b')", '\\\\computer\\share\\a\\b') tester("ntpath.join('\\\\computer\\share\\a\\', 'b')", '\\\\computer\\share\\a\\b')
# Second part is anchored, so that the first part is ignored.
tester("ntpath.join('a', 'Z:b', 'c')", 'Z:b\\c')
tester("ntpath.join('a', 'Z:\\b', 'c')", 'Z:\\b\\c')
tester("ntpath.join('a', '\\\\b\\c', 'd')", '\\\\b\\c\\d')
# Second part has a root but not drive.
tester("ntpath.join('a', '\\b', 'c')", '\\b\\c')
tester("ntpath.join('Z:/a', '/b', 'c')", 'Z:\\b\\c')
tester("ntpath.join('//?/Z:/a', '/b', 'c')", '\\\\?\\Z:\\b\\c')
tester("ntpath.join('D:a', './c:b')", 'D:a\\.\\c:b')
tester("ntpath.join('D:/a', './c:b')", 'D:\\a\\.\\c:b')
def test_normpath(self): def test_normpath(self):
tester("ntpath.normpath('A//////././//.//B')", r'A\B') tester("ntpath.normpath('A//////././//.//B')", r'A\B')

View file

@ -160,45 +160,30 @@ class PurePathTest(unittest.TestCase):
for parent in p.parents: for parent in p.parents:
self.assertEqual(42, parent.session_id) self.assertEqual(42, parent.session_id)
def _get_drive_root_parts(self, parts): def _check_parse_path(self, raw_path, *expected):
path = self.cls(*parts)
return path.drive, path.root, path.parts
def _check_drive_root_parts(self, arg, *expected):
sep = self.pathmod.sep sep = self.pathmod.sep
actual = self._get_drive_root_parts([x.replace('/', sep) for x in arg]) actual = self.cls._parse_path(raw_path.replace('/', sep))
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
if altsep := self.pathmod.altsep: if altsep := self.pathmod.altsep:
actual = self._get_drive_root_parts([x.replace('/', altsep) for x in arg]) actual = self.cls._parse_path(raw_path.replace('/', altsep))
self.assertEqual(actual, expected) self.assertEqual(actual, expected)
def test_drive_root_parts_common(self): def test_parse_path_common(self):
check = self._check_drive_root_parts check = self._check_parse_path
sep = self.pathmod.sep sep = self.pathmod.sep
# Unanchored parts. check('', '', '', [])
check((), '', '', ()) check('a', '', '', ['a'])
check(('a',), '', '', ('a',)) check('a/', '', '', ['a'])
check(('a/',), '', '', ('a',)) check('a/b', '', '', ['a', 'b'])
check(('a', 'b'), '', '', ('a', 'b')) check('a/b/', '', '', ['a', 'b'])
# Expansion. check('a/b/c/d', '', '', ['a', 'b', 'c', 'd'])
check(('a/b',), '', '', ('a', 'b')) check('a/b//c/d', '', '', ['a', 'b', 'c', 'd'])
check(('a/b/',), '', '', ('a', 'b')) check('a/b/c/d', '', '', ['a', 'b', 'c', 'd'])
check(('a', 'b/c', 'd'), '', '', ('a', 'b', 'c', 'd')) check('.', '', '', [])
# Collapsing and stripping excess slashes. check('././b', '', '', ['b'])
check(('a', 'b//c', 'd'), '', '', ('a', 'b', 'c', 'd')) check('a/./b', '', '', ['a', 'b'])
check(('a', 'b/c/', 'd'), '', '', ('a', 'b', 'c', 'd')) check('a/./.', '', '', ['a'])
# Eliminating standalone dots. check('/a/b', '', sep, ['a', 'b'])
check(('.',), '', '', ())
check(('.', '.', 'b'), '', '', ('b',))
check(('a', '.', 'b'), '', '', ('a', 'b'))
check(('a', '.', '.'), '', '', ('a',))
# The first part is anchored.
check(('/a/b',), '', sep, (sep, 'a', 'b'))
check(('/a', 'b'), '', sep, (sep, 'a', 'b'))
check(('/a/', 'b'), '', sep, (sep, 'a', 'b'))
# Ignoring parts before an anchored part.
check(('a', '/b', 'c'), '', sep, (sep, 'b', 'c'))
check(('a', '/b', '/c'), '', sep, (sep, 'c'))
def test_join_common(self): def test_join_common(self):
P = self.cls P = self.cls
@ -792,17 +777,17 @@ class PurePathTest(unittest.TestCase):
class PurePosixPathTest(PurePathTest): class PurePosixPathTest(PurePathTest):
cls = pathlib.PurePosixPath cls = pathlib.PurePosixPath
def test_drive_root_parts(self): def test_parse_path(self):
check = self._check_drive_root_parts check = self._check_parse_path
# Collapsing of excess leading slashes, except for the double-slash # Collapsing of excess leading slashes, except for the double-slash
# special case. # special case.
check(('//a', 'b'), '', '//', ('//', 'a', 'b')) check('//a/b', '', '//', ['a', 'b'])
check(('///a', 'b'), '', '/', ('/', 'a', 'b')) check('///a/b', '', '/', ['a', 'b'])
check(('////a', 'b'), '', '/', ('/', 'a', 'b')) check('////a/b', '', '/', ['a', 'b'])
# Paths which look like NT paths aren't treated specially. # Paths which look like NT paths aren't treated specially.
check(('c:a',), '', '', ('c:a',)) check('c:a', '', '', ['c:a',])
check(('c:\\a',), '', '', ('c:\\a',)) check('c:\\a', '', '', ['c:\\a',])
check(('\\a',), '', '', ('\\a',)) check('\\a', '', '', ['\\a',])
def test_root(self): def test_root(self):
P = self.cls P = self.cls
@ -900,67 +885,53 @@ class PureWindowsPathTest(PurePathTest):
], ],
}) })
def test_drive_root_parts(self): def test_parse_path(self):
check = self._check_drive_root_parts check = self._check_parse_path
# First part is anchored. # First part is anchored.
check(('c:',), 'c:', '', ('c:',)) check('c:', 'c:', '', [])
check(('c:/',), 'c:', '\\', ('c:\\',)) check('c:/', 'c:', '\\', [])
check(('/',), '', '\\', ('\\',)) check('/', '', '\\', [])
check(('c:a',), 'c:', '', ('c:', 'a')) check('c:a', 'c:', '', ['a'])
check(('c:/a',), 'c:', '\\', ('c:\\', 'a')) check('c:/a', 'c:', '\\', ['a'])
check(('/a',), '', '\\', ('\\', 'a')) check('/a', '', '\\', ['a'])
# UNC paths. # UNC paths.
check(('//',), '\\\\', '', ('\\\\',)) check('//', '\\\\', '', [])
check(('//a',), '\\\\a', '', ('\\\\a',)) check('//a', '\\\\a', '', [])
check(('//a/',), '\\\\a\\', '', ('\\\\a\\',)) check('//a/', '\\\\a\\', '', [])
check(('//a/b',), '\\\\a\\b', '\\', ('\\\\a\\b\\',)) check('//a/b', '\\\\a\\b', '\\', [])
check(('//a/b/',), '\\\\a\\b', '\\', ('\\\\a\\b\\',)) check('//a/b/', '\\\\a\\b', '\\', [])
check(('//a/b/c',), '\\\\a\\b', '\\', ('\\\\a\\b\\', 'c')) check('//a/b/c', '\\\\a\\b', '\\', ['c'])
# Second part is anchored, so that the first part is ignored.
check(('a', 'Z:b', 'c'), 'Z:', '', ('Z:', 'b', 'c'))
check(('a', 'Z:/b', 'c'), 'Z:', '\\', ('Z:\\', 'b', 'c'))
# UNC paths.
check(('a', '//b/c', 'd'), '\\\\b\\c', '\\', ('\\\\b\\c\\', 'd'))
# Collapsing and stripping excess slashes. # Collapsing and stripping excess slashes.
check(('a', 'Z://b//c/', 'd/'), 'Z:', '\\', ('Z:\\', 'b', 'c', 'd')) check('Z://b//c/d/', 'Z:', '\\', ['b', 'c', 'd'])
# UNC paths. # UNC paths.
check(('a', '//b/c//', 'd'), '\\\\b\\c', '\\', ('\\\\b\\c\\', 'd')) check('//b/c//d', '\\\\b\\c', '\\', ['d'])
# Extended paths. # Extended paths.
check(('//./c:',), '\\\\.\\c:', '', ('\\\\.\\c:',)) check('//./c:', '\\\\.\\c:', '', [])
check(('//?/c:/',), '\\\\?\\c:', '\\', ('\\\\?\\c:\\',)) check('//?/c:/', '\\\\?\\c:', '\\', [])
check(('//?/c:/a',), '\\\\?\\c:', '\\', ('\\\\?\\c:\\', 'a')) check('//?/c:/a', '\\\\?\\c:', '\\', ['a'])
check(('//?/c:/a', '/b'), '\\\\?\\c:', '\\', ('\\\\?\\c:\\', 'b'))
# Extended UNC paths (format is "\\?\UNC\server\share"). # Extended UNC paths (format is "\\?\UNC\server\share").
check(('//?',), '\\\\?', '', ('\\\\?',)) check('//?', '\\\\?', '', [])
check(('//?/',), '\\\\?\\', '', ('\\\\?\\',)) check('//?/', '\\\\?\\', '', [])
check(('//?/UNC',), '\\\\?\\UNC', '', ('\\\\?\\UNC',)) check('//?/UNC', '\\\\?\\UNC', '', [])
check(('//?/UNC/',), '\\\\?\\UNC\\', '', ('\\\\?\\UNC\\',)) check('//?/UNC/', '\\\\?\\UNC\\', '', [])
check(('//?/UNC/b',), '\\\\?\\UNC\\b', '', ('\\\\?\\UNC\\b',)) check('//?/UNC/b', '\\\\?\\UNC\\b', '', [])
check(('//?/UNC/b/',), '\\\\?\\UNC\\b\\', '', ('\\\\?\\UNC\\b\\',)) check('//?/UNC/b/', '\\\\?\\UNC\\b\\', '', [])
check(('//?/UNC/b/c',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\',)) check('//?/UNC/b/c', '\\\\?\\UNC\\b\\c', '\\', [])
check(('//?/UNC/b/c/',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\',)) check('//?/UNC/b/c/', '\\\\?\\UNC\\b\\c', '\\', [])
check(('//?/UNC/b/c/d',), '\\\\?\\UNC\\b\\c', '\\', ('\\\\?\\UNC\\b\\c\\', 'd')) check('//?/UNC/b/c/d', '\\\\?\\UNC\\b\\c', '\\', ['d'])
# UNC device paths # UNC device paths
check(('//./BootPartition/',), '\\\\.\\BootPartition', '\\', ('\\\\.\\BootPartition\\',)) check('//./BootPartition/', '\\\\.\\BootPartition', '\\', [])
check(('//?/BootPartition/',), '\\\\?\\BootPartition', '\\', ('\\\\?\\BootPartition\\',)) check('//?/BootPartition/', '\\\\?\\BootPartition', '\\', [])
check(('//./PhysicalDrive0',), '\\\\.\\PhysicalDrive0', '', ('\\\\.\\PhysicalDrive0',)) check('//./PhysicalDrive0', '\\\\.\\PhysicalDrive0', '', [])
check(('//?/Volume{}/',), '\\\\?\\Volume{}', '\\', ('\\\\?\\Volume{}\\',)) check('//?/Volume{}/', '\\\\?\\Volume{}', '\\', [])
check(('//./nul',), '\\\\.\\nul', '', ('\\\\.\\nul',)) check('//./nul', '\\\\.\\nul', '', [])
# Second part has a root but not drive.
check(('a', '/b', 'c'), '', '\\', ('\\', 'b', 'c'))
check(('Z:/a', '/b', 'c'), 'Z:', '\\', ('Z:\\', 'b', 'c'))
check(('//?/Z:/a', '/b', 'c'), '\\\\?\\Z:', '\\', ('\\\\?\\Z:\\', 'b', 'c'))
# Joining with the same drive => the first path is appended to if
# the second path is relative.
check(('c:/a/b', 'c:x/y'), 'c:', '\\', ('c:\\', 'a', 'b', 'x', 'y'))
check(('c:/a/b', 'c:/x/y'), 'c:', '\\', ('c:\\', 'x', 'y'))
# Paths to files with NTFS alternate data streams # Paths to files with NTFS alternate data streams
check(('./c:s',), '', '', ('c:s',)) check('./c:s', '', '', ['c:s'])
check(('cc:s',), '', '', ('cc:s',)) check('cc:s', '', '', ['cc:s'])
check(('C:c:s',), 'C:', '', ('C:', 'c:s')) check('C:c:s', 'C:', '', ['c:s'])
check(('C:/c:s',), 'C:', '\\', ('C:\\', 'c:s')) check('C:/c:s', 'C:', '\\', ['c:s'])
check(('D:a', './c:b'), 'D:', '', ('D:', 'a', 'c:b')) check('D:a/c:b', 'D:', '', ['a', 'c:b'])
check(('D:/a', './c:b'), 'D:', '\\', ('D:\\', 'a', 'c:b')) check('D:/a/c:b', 'D:', '\\', ['a', 'c:b'])
def test_str(self): def test_str(self):
p = self.cls('a/b/c') p = self.cls('a/b/c')

View file

@ -47,18 +47,26 @@ class PosixPathTest(unittest.TestCase):
safe_rmdir(os_helper.TESTFN + suffix) safe_rmdir(os_helper.TESTFN + suffix)
def test_join(self): def test_join(self):
self.assertEqual(posixpath.join("/foo", "bar", "/bar", "baz"), fn = posixpath.join
"/bar/baz") self.assertEqual(fn("/foo", "bar", "/bar", "baz"), "/bar/baz")
self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz") self.assertEqual(fn("/foo", "bar", "baz"), "/foo/bar/baz")
self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), self.assertEqual(fn("/foo/", "bar/", "baz/"), "/foo/bar/baz/")
"/foo/bar/baz/")
self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"), self.assertEqual(fn(b"/foo", b"bar", b"/bar", b"baz"), b"/bar/baz")
b"/bar/baz") self.assertEqual(fn(b"/foo", b"bar", b"baz"), b"/foo/bar/baz")
self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"), self.assertEqual(fn(b"/foo/", b"bar/", b"baz/"), b"/foo/bar/baz/")
b"/foo/bar/baz")
self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"), self.assertEqual(fn("a", "b"), "a/b")
b"/foo/bar/baz/") self.assertEqual(fn("a", "b/"), "a/b/")
self.assertEqual(fn("a/", "b"), "a/b")
self.assertEqual(fn("a/", "b/"), "a/b/")
self.assertEqual(fn("a", "b/c", "d"), "a/b/c/d")
self.assertEqual(fn("a", "b//c", "d"), "a/b//c/d")
self.assertEqual(fn("a", "b/c/", "d"), "a/b/c/d")
self.assertEqual(fn("/a", "b"), "/a/b")
self.assertEqual(fn("/a/", "b"), "/a/b")
self.assertEqual(fn("a", "/b", "c"), "/b/c")
self.assertEqual(fn("a", "/b", "/c"), "/c")
def test_split(self): def test_split(self):
self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar")) self.assertEqual(posixpath.split("/foo/bar"), ("/foo", "bar"))