1import contextlib
2import collections.abc
3import io
4import os
5import sys
6import errno
7import pathlib
8import pickle
9import socket
10import stat
11import tempfile
12import unittest
13from unittest import mock
14
15from test.support import import_helper
16from test.support import is_emscripten, is_wasi
17from test.support import os_helper
18from test.support.os_helper import TESTFN, FakePath
19
20try:
21    import grp, pwd
22except ImportError:
23    grp = pwd = None
24
25
26class _BaseFlavourTest(object):
27
28    def _check_parse_parts(self, arg, expected):
29        f = self.flavour.parse_parts
30        sep = self.flavour.sep
31        altsep = self.flavour.altsep
32        actual = f([x.replace('/', sep) for x in arg])
33        self.assertEqual(actual, expected)
34        if altsep:
35            actual = f([x.replace('/', altsep) for x in arg])
36            self.assertEqual(actual, expected)
37
38    def test_parse_parts_common(self):
39        check = self._check_parse_parts
40        sep = self.flavour.sep
41        # Unanchored parts.
42        check([],                   ('', '', []))
43        check(['a'],                ('', '', ['a']))
44        check(['a/'],               ('', '', ['a']))
45        check(['a', 'b'],           ('', '', ['a', 'b']))
46        # Expansion.
47        check(['a/b'],              ('', '', ['a', 'b']))
48        check(['a/b/'],             ('', '', ['a', 'b']))
49        check(['a', 'b/c', 'd'],    ('', '', ['a', 'b', 'c', 'd']))
50        # Collapsing and stripping excess slashes.
51        check(['a', 'b//c', 'd'],   ('', '', ['a', 'b', 'c', 'd']))
52        check(['a', 'b/c/', 'd'],   ('', '', ['a', 'b', 'c', 'd']))
53        # Eliminating standalone dots.
54        check(['.'],                ('', '', []))
55        check(['.', '.', 'b'],      ('', '', ['b']))
56        check(['a', '.', 'b'],      ('', '', ['a', 'b']))
57        check(['a', '.', '.'],      ('', '', ['a']))
58        # The first part is anchored.
59        check(['/a/b'],             ('', sep, [sep, 'a', 'b']))
60        check(['/a', 'b'],          ('', sep, [sep, 'a', 'b']))
61        check(['/a/', 'b'],         ('', sep, [sep, 'a', 'b']))
62        # Ignoring parts before an anchored part.
63        check(['a', '/b', 'c'],     ('', sep, [sep, 'b', 'c']))
64        check(['a', '/b', '/c'],    ('', sep, [sep, 'c']))
65
66
67class PosixFlavourTest(_BaseFlavourTest, unittest.TestCase):
68    flavour = pathlib._posix_flavour
69
70    def test_parse_parts(self):
71        check = self._check_parse_parts
72        # Collapsing of excess leading slashes, except for the double-slash
73        # special case.
74        check(['//a', 'b'],             ('', '//', ['//', 'a', 'b']))
75        check(['///a', 'b'],            ('', '/', ['/', 'a', 'b']))
76        check(['////a', 'b'],           ('', '/', ['/', 'a', 'b']))
77        # Paths which look like NT paths aren't treated specially.
78        check(['c:a'],                  ('', '', ['c:a']))
79        check(['c:\\a'],                ('', '', ['c:\\a']))
80        check(['\\a'],                  ('', '', ['\\a']))
81
82    def test_splitroot(self):
83        f = self.flavour.splitroot
84        self.assertEqual(f(''), ('', '', ''))
85        self.assertEqual(f('a'), ('', '', 'a'))
86        self.assertEqual(f('a/b'), ('', '', 'a/b'))
87        self.assertEqual(f('a/b/'), ('', '', 'a/b/'))
88        self.assertEqual(f('/a'), ('', '/', 'a'))
89        self.assertEqual(f('/a/b'), ('', '/', 'a/b'))
90        self.assertEqual(f('/a/b/'), ('', '/', 'a/b/'))
91        # The root is collapsed when there are redundant slashes
92        # except when there are exactly two leading slashes, which
93        # is a special case in POSIX.
94        self.assertEqual(f('//a'), ('', '//', 'a'))
95        self.assertEqual(f('///a'), ('', '/', 'a'))
96        self.assertEqual(f('///a/b'), ('', '/', 'a/b'))
97        # Paths which look like NT paths aren't treated specially.
98        self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b'))
99        self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b'))
100        self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b'))
101
102
103class NTFlavourTest(_BaseFlavourTest, unittest.TestCase):
104    flavour = pathlib._windows_flavour
105
106    def test_parse_parts(self):
107        check = self._check_parse_parts
108        # First part is anchored.
109        check(['c:'],                   ('c:', '', ['c:']))
110        check(['c:/'],                  ('c:', '\\', ['c:\\']))
111        check(['/'],                    ('', '\\', ['\\']))
112        check(['c:a'],                  ('c:', '', ['c:', 'a']))
113        check(['c:/a'],                 ('c:', '\\', ['c:\\', 'a']))
114        check(['/a'],                   ('', '\\', ['\\', 'a']))
115        # UNC paths.
116        check(['//a/b'],                ('\\\\a\\b', '\\', ['\\\\a\\b\\']))
117        check(['//a/b/'],               ('\\\\a\\b', '\\', ['\\\\a\\b\\']))
118        check(['//a/b/c'],              ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c']))
119        # Second part is anchored, so that the first part is ignored.
120        check(['a', 'Z:b', 'c'],        ('Z:', '', ['Z:', 'b', 'c']))
121        check(['a', 'Z:/b', 'c'],       ('Z:', '\\', ['Z:\\', 'b', 'c']))
122        # UNC paths.
123        check(['a', '//b/c', 'd'],      ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
124        # Collapsing and stripping excess slashes.
125        check(['a', 'Z://b//c/', 'd/'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd']))
126        # UNC paths.
127        check(['a', '//b/c//', 'd'],    ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
128        # Extended paths.
129        check(['//?/c:/'],              ('\\\\?\\c:', '\\', ['\\\\?\\c:\\']))
130        check(['//?/c:/a'],             ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a']))
131        check(['//?/c:/a', '/b'],       ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'b']))
132        # Extended UNC paths (format is "\\?\UNC\server\share").
133        check(['//?/UNC/b/c'],          ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\']))
134        check(['//?/UNC/b/c/d'],        ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd']))
135        # Second part has a root but not drive.
136        check(['a', '/b', 'c'],         ('', '\\', ['\\', 'b', 'c']))
137        check(['Z:/a', '/b', 'c'],      ('Z:', '\\', ['Z:\\', 'b', 'c']))
138        check(['//?/Z:/a', '/b', 'c'],  ('\\\\?\\Z:', '\\', ['\\\\?\\Z:\\', 'b', 'c']))
139
140    def test_splitroot(self):
141        f = self.flavour.splitroot
142        self.assertEqual(f(''), ('', '', ''))
143        self.assertEqual(f('a'), ('', '', 'a'))
144        self.assertEqual(f('a\\b'), ('', '', 'a\\b'))
145        self.assertEqual(f('\\a'), ('', '\\', 'a'))
146        self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b'))
147        self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b'))
148        self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b'))
149        # Redundant slashes in the root are collapsed.
150        self.assertEqual(f('\\\\a'), ('', '\\', 'a'))
151        self.assertEqual(f('\\\\\\a/b'), ('', '\\', 'a/b'))
152        self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a'))
153        self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b'))
154        # Valid UNC paths.
155        self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', ''))
156        self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', ''))
157        self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d'))
158        # These are non-UNC paths (according to ntpath.py and test_ntpath).
159        # However, command.com says such paths are invalid, so it's
160        # difficult to know what the right semantics are.
161        self.assertEqual(f('\\\\\\a\\b'), ('', '\\', 'a\\b'))
162        self.assertEqual(f('\\\\a'), ('', '\\', 'a'))
163
164
165#
166# Tests for the pure classes.
167#
168
169class _BasePurePathTest(object):
170
171    # Keys are canonical paths, values are list of tuples of arguments
172    # supposed to produce equal paths.
173    equivalences = {
174        'a/b': [
175            ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'),
176            ('a/b/',), ('a//b',), ('a//b//',),
177            # Empty components get removed.
178            ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''),
179            ],
180        '/b/c/d': [
181            ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'),
182            ('/a', '/b/c', 'd'),
183            # Empty components get removed.
184            ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'),
185            ],
186    }
187
188    def setUp(self):
189        p = self.cls('a')
190        self.flavour = p._flavour
191        self.sep = self.flavour.sep
192        self.altsep = self.flavour.altsep
193
194    def test_constructor_common(self):
195        P = self.cls
196        p = P('a')
197        self.assertIsInstance(p, P)
198        P('a', 'b', 'c')
199        P('/a', 'b', 'c')
200        P('a/b/c')
201        P('/a/b/c')
202        P(FakePath("a/b/c"))
203        self.assertEqual(P(P('a')), P('a'))
204        self.assertEqual(P(P('a'), 'b'), P('a/b'))
205        self.assertEqual(P(P('a'), P('b')), P('a/b'))
206        self.assertEqual(P(P('a'), P('b'), P('c')), P(FakePath("a/b/c")))
207
208    def _check_str_subclass(self, *args):
209        # Issue #21127: it should be possible to construct a PurePath object
210        # from a str subclass instance, and it then gets converted to
211        # a pure str object.
212        class StrSubclass(str):
213            pass
214        P = self.cls
215        p = P(*(StrSubclass(x) for x in args))
216        self.assertEqual(p, P(*args))
217        for part in p.parts:
218            self.assertIs(type(part), str)
219
220    def test_str_subclass_common(self):
221        self._check_str_subclass('')
222        self._check_str_subclass('.')
223        self._check_str_subclass('a')
224        self._check_str_subclass('a/b.txt')
225        self._check_str_subclass('/a/b.txt')
226
227    def test_join_common(self):
228        P = self.cls
229        p = P('a/b')
230        pp = p.joinpath('c')
231        self.assertEqual(pp, P('a/b/c'))
232        self.assertIs(type(pp), type(p))
233        pp = p.joinpath('c', 'd')
234        self.assertEqual(pp, P('a/b/c/d'))
235        pp = p.joinpath(P('c'))
236        self.assertEqual(pp, P('a/b/c'))
237        pp = p.joinpath('/c')
238        self.assertEqual(pp, P('/c'))
239
240    def test_div_common(self):
241        # Basically the same as joinpath().
242        P = self.cls
243        p = P('a/b')
244        pp = p / 'c'
245        self.assertEqual(pp, P('a/b/c'))
246        self.assertIs(type(pp), type(p))
247        pp = p / 'c/d'
248        self.assertEqual(pp, P('a/b/c/d'))
249        pp = p / 'c' / 'd'
250        self.assertEqual(pp, P('a/b/c/d'))
251        pp = 'c' / p / 'd'
252        self.assertEqual(pp, P('c/a/b/d'))
253        pp = p / P('c')
254        self.assertEqual(pp, P('a/b/c'))
255        pp = p/ '/c'
256        self.assertEqual(pp, P('/c'))
257
258    def _check_str(self, expected, args):
259        p = self.cls(*args)
260        self.assertEqual(str(p), expected.replace('/', self.sep))
261
262    def test_str_common(self):
263        # Canonicalized paths roundtrip.
264        for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
265            self._check_str(pathstr, (pathstr,))
266        # Special case for the empty path.
267        self._check_str('.', ('',))
268        # Other tests for str() are in test_equivalences().
269
270    def test_as_posix_common(self):
271        P = self.cls
272        for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
273            self.assertEqual(P(pathstr).as_posix(), pathstr)
274        # Other tests for as_posix() are in test_equivalences().
275
276    def test_as_bytes_common(self):
277        sep = os.fsencode(self.sep)
278        P = self.cls
279        self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b')
280
281    def test_as_uri_common(self):
282        P = self.cls
283        with self.assertRaises(ValueError):
284            P('a').as_uri()
285        with self.assertRaises(ValueError):
286            P().as_uri()
287
288    def test_repr_common(self):
289        for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
290            p = self.cls(pathstr)
291            clsname = p.__class__.__name__
292            r = repr(p)
293            # The repr() is in the form ClassName("forward-slashes path").
294            self.assertTrue(r.startswith(clsname + '('), r)
295            self.assertTrue(r.endswith(')'), r)
296            inner = r[len(clsname) + 1 : -1]
297            self.assertEqual(eval(inner), p.as_posix())
298            # The repr() roundtrips.
299            q = eval(r, pathlib.__dict__)
300            self.assertIs(q.__class__, p.__class__)
301            self.assertEqual(q, p)
302            self.assertEqual(repr(q), r)
303
304    def test_eq_common(self):
305        P = self.cls
306        self.assertEqual(P('a/b'), P('a/b'))
307        self.assertEqual(P('a/b'), P('a', 'b'))
308        self.assertNotEqual(P('a/b'), P('a'))
309        self.assertNotEqual(P('a/b'), P('/a/b'))
310        self.assertNotEqual(P('a/b'), P())
311        self.assertNotEqual(P('/a/b'), P('/'))
312        self.assertNotEqual(P(), P('/'))
313        self.assertNotEqual(P(), "")
314        self.assertNotEqual(P(), {})
315        self.assertNotEqual(P(), int)
316
317    def test_match_common(self):
318        P = self.cls
319        self.assertRaises(ValueError, P('a').match, '')
320        self.assertRaises(ValueError, P('a').match, '.')
321        # Simple relative pattern.
322        self.assertTrue(P('b.py').match('b.py'))
323        self.assertTrue(P('a/b.py').match('b.py'))
324        self.assertTrue(P('/a/b.py').match('b.py'))
325        self.assertFalse(P('a.py').match('b.py'))
326        self.assertFalse(P('b/py').match('b.py'))
327        self.assertFalse(P('/a.py').match('b.py'))
328        self.assertFalse(P('b.py/c').match('b.py'))
329        # Wildcard relative pattern.
330        self.assertTrue(P('b.py').match('*.py'))
331        self.assertTrue(P('a/b.py').match('*.py'))
332        self.assertTrue(P('/a/b.py').match('*.py'))
333        self.assertFalse(P('b.pyc').match('*.py'))
334        self.assertFalse(P('b./py').match('*.py'))
335        self.assertFalse(P('b.py/c').match('*.py'))
336        # Multi-part relative pattern.
337        self.assertTrue(P('ab/c.py').match('a*/*.py'))
338        self.assertTrue(P('/d/ab/c.py').match('a*/*.py'))
339        self.assertFalse(P('a.py').match('a*/*.py'))
340        self.assertFalse(P('/dab/c.py').match('a*/*.py'))
341        self.assertFalse(P('ab/c.py/d').match('a*/*.py'))
342        # Absolute pattern.
343        self.assertTrue(P('/b.py').match('/*.py'))
344        self.assertFalse(P('b.py').match('/*.py'))
345        self.assertFalse(P('a/b.py').match('/*.py'))
346        self.assertFalse(P('/a/b.py').match('/*.py'))
347        # Multi-part absolute pattern.
348        self.assertTrue(P('/a/b.py').match('/a/*.py'))
349        self.assertFalse(P('/ab.py').match('/a/*.py'))
350        self.assertFalse(P('/a/b/c.py').match('/a/*.py'))
351        # Multi-part glob-style pattern.
352        self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
353        self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
354
355    def test_ordering_common(self):
356        # Ordering is tuple-alike.
357        def assertLess(a, b):
358            self.assertLess(a, b)
359            self.assertGreater(b, a)
360        P = self.cls
361        a = P('a')
362        b = P('a/b')
363        c = P('abc')
364        d = P('b')
365        assertLess(a, b)
366        assertLess(a, c)
367        assertLess(a, d)
368        assertLess(b, c)
369        assertLess(c, d)
370        P = self.cls
371        a = P('/a')
372        b = P('/a/b')
373        c = P('/abc')
374        d = P('/b')
375        assertLess(a, b)
376        assertLess(a, c)
377        assertLess(a, d)
378        assertLess(b, c)
379        assertLess(c, d)
380        with self.assertRaises(TypeError):
381            P() < {}
382
383    def test_parts_common(self):
384        # `parts` returns a tuple.
385        sep = self.sep
386        P = self.cls
387        p = P('a/b')
388        parts = p.parts
389        self.assertEqual(parts, ('a', 'b'))
390        # The object gets reused.
391        self.assertIs(parts, p.parts)
392        # When the path is absolute, the anchor is a separate part.
393        p = P('/a/b')
394        parts = p.parts
395        self.assertEqual(parts, (sep, 'a', 'b'))
396
397    def test_fspath_common(self):
398        P = self.cls
399        p = P('a/b')
400        self._check_str(p.__fspath__(), ('a/b',))
401        self._check_str(os.fspath(p), ('a/b',))
402
403    def test_equivalences(self):
404        for k, tuples in self.equivalences.items():
405            canon = k.replace('/', self.sep)
406            posix = k.replace(self.sep, '/')
407            if canon != posix:
408                tuples = tuples + [
409                    tuple(part.replace('/', self.sep) for part in t)
410                    for t in tuples
411                    ]
412                tuples.append((posix, ))
413            pcanon = self.cls(canon)
414            for t in tuples:
415                p = self.cls(*t)
416                self.assertEqual(p, pcanon, "failed with args {}".format(t))
417                self.assertEqual(hash(p), hash(pcanon))
418                self.assertEqual(str(p), canon)
419                self.assertEqual(p.as_posix(), posix)
420
421    def test_parent_common(self):
422        # Relative
423        P = self.cls
424        p = P('a/b/c')
425        self.assertEqual(p.parent, P('a/b'))
426        self.assertEqual(p.parent.parent, P('a'))
427        self.assertEqual(p.parent.parent.parent, P())
428        self.assertEqual(p.parent.parent.parent.parent, P())
429        # Anchored
430        p = P('/a/b/c')
431        self.assertEqual(p.parent, P('/a/b'))
432        self.assertEqual(p.parent.parent, P('/a'))
433        self.assertEqual(p.parent.parent.parent, P('/'))
434        self.assertEqual(p.parent.parent.parent.parent, P('/'))
435
436    def test_parents_common(self):
437        # Relative
438        P = self.cls
439        p = P('a/b/c')
440        par = p.parents
441        self.assertEqual(len(par), 3)
442        self.assertEqual(par[0], P('a/b'))
443        self.assertEqual(par[1], P('a'))
444        self.assertEqual(par[2], P('.'))
445        self.assertEqual(par[-1], P('.'))
446        self.assertEqual(par[-2], P('a'))
447        self.assertEqual(par[-3], P('a/b'))
448        self.assertEqual(par[0:1], (P('a/b'),))
449        self.assertEqual(par[:2], (P('a/b'), P('a')))
450        self.assertEqual(par[:-1], (P('a/b'), P('a')))
451        self.assertEqual(par[1:], (P('a'), P('.')))
452        self.assertEqual(par[::2], (P('a/b'), P('.')))
453        self.assertEqual(par[::-1], (P('.'), P('a'), P('a/b')))
454        self.assertEqual(list(par), [P('a/b'), P('a'), P('.')])
455        with self.assertRaises(IndexError):
456            par[-4]
457        with self.assertRaises(IndexError):
458            par[3]
459        with self.assertRaises(TypeError):
460            par[0] = p
461        # Anchored
462        p = P('/a/b/c')
463        par = p.parents
464        self.assertEqual(len(par), 3)
465        self.assertEqual(par[0], P('/a/b'))
466        self.assertEqual(par[1], P('/a'))
467        self.assertEqual(par[2], P('/'))
468        self.assertEqual(par[-1], P('/'))
469        self.assertEqual(par[-2], P('/a'))
470        self.assertEqual(par[-3], P('/a/b'))
471        self.assertEqual(par[0:1], (P('/a/b'),))
472        self.assertEqual(par[:2], (P('/a/b'), P('/a')))
473        self.assertEqual(par[:-1], (P('/a/b'), P('/a')))
474        self.assertEqual(par[1:], (P('/a'), P('/')))
475        self.assertEqual(par[::2], (P('/a/b'), P('/')))
476        self.assertEqual(par[::-1], (P('/'), P('/a'), P('/a/b')))
477        self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')])
478        with self.assertRaises(IndexError):
479            par[-4]
480        with self.assertRaises(IndexError):
481            par[3]
482
483    def test_drive_common(self):
484        P = self.cls
485        self.assertEqual(P('a/b').drive, '')
486        self.assertEqual(P('/a/b').drive, '')
487        self.assertEqual(P('').drive, '')
488
489    def test_root_common(self):
490        P = self.cls
491        sep = self.sep
492        self.assertEqual(P('').root, '')
493        self.assertEqual(P('a/b').root, '')
494        self.assertEqual(P('/').root, sep)
495        self.assertEqual(P('/a/b').root, sep)
496
497    def test_anchor_common(self):
498        P = self.cls
499        sep = self.sep
500        self.assertEqual(P('').anchor, '')
501        self.assertEqual(P('a/b').anchor, '')
502        self.assertEqual(P('/').anchor, sep)
503        self.assertEqual(P('/a/b').anchor, sep)
504
505    def test_name_common(self):
506        P = self.cls
507        self.assertEqual(P('').name, '')
508        self.assertEqual(P('.').name, '')
509        self.assertEqual(P('/').name, '')
510        self.assertEqual(P('a/b').name, 'b')
511        self.assertEqual(P('/a/b').name, 'b')
512        self.assertEqual(P('/a/b/.').name, 'b')
513        self.assertEqual(P('a/b.py').name, 'b.py')
514        self.assertEqual(P('/a/b.py').name, 'b.py')
515
516    def test_suffix_common(self):
517        P = self.cls
518        self.assertEqual(P('').suffix, '')
519        self.assertEqual(P('.').suffix, '')
520        self.assertEqual(P('..').suffix, '')
521        self.assertEqual(P('/').suffix, '')
522        self.assertEqual(P('a/b').suffix, '')
523        self.assertEqual(P('/a/b').suffix, '')
524        self.assertEqual(P('/a/b/.').suffix, '')
525        self.assertEqual(P('a/b.py').suffix, '.py')
526        self.assertEqual(P('/a/b.py').suffix, '.py')
527        self.assertEqual(P('a/.hgrc').suffix, '')
528        self.assertEqual(P('/a/.hgrc').suffix, '')
529        self.assertEqual(P('a/.hg.rc').suffix, '.rc')
530        self.assertEqual(P('/a/.hg.rc').suffix, '.rc')
531        self.assertEqual(P('a/b.tar.gz').suffix, '.gz')
532        self.assertEqual(P('/a/b.tar.gz').suffix, '.gz')
533        self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '')
534        self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '')
535
536    def test_suffixes_common(self):
537        P = self.cls
538        self.assertEqual(P('').suffixes, [])
539        self.assertEqual(P('.').suffixes, [])
540        self.assertEqual(P('/').suffixes, [])
541        self.assertEqual(P('a/b').suffixes, [])
542        self.assertEqual(P('/a/b').suffixes, [])
543        self.assertEqual(P('/a/b/.').suffixes, [])
544        self.assertEqual(P('a/b.py').suffixes, ['.py'])
545        self.assertEqual(P('/a/b.py').suffixes, ['.py'])
546        self.assertEqual(P('a/.hgrc').suffixes, [])
547        self.assertEqual(P('/a/.hgrc').suffixes, [])
548        self.assertEqual(P('a/.hg.rc').suffixes, ['.rc'])
549        self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc'])
550        self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz'])
551        self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz'])
552        self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, [])
553        self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, [])
554
555    def test_stem_common(self):
556        P = self.cls
557        self.assertEqual(P('').stem, '')
558        self.assertEqual(P('.').stem, '')
559        self.assertEqual(P('..').stem, '..')
560        self.assertEqual(P('/').stem, '')
561        self.assertEqual(P('a/b').stem, 'b')
562        self.assertEqual(P('a/b.py').stem, 'b')
563        self.assertEqual(P('a/.hgrc').stem, '.hgrc')
564        self.assertEqual(P('a/.hg.rc').stem, '.hg')
565        self.assertEqual(P('a/b.tar.gz').stem, 'b.tar')
566        self.assertEqual(P('a/Some name. Ending with a dot.').stem,
567                         'Some name. Ending with a dot.')
568
569    def test_with_name_common(self):
570        P = self.cls
571        self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml'))
572        self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml'))
573        self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml'))
574        self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml'))
575        self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml'))
576        self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml'))
577        self.assertRaises(ValueError, P('').with_name, 'd.xml')
578        self.assertRaises(ValueError, P('.').with_name, 'd.xml')
579        self.assertRaises(ValueError, P('/').with_name, 'd.xml')
580        self.assertRaises(ValueError, P('a/b').with_name, '')
581        self.assertRaises(ValueError, P('a/b').with_name, '/c')
582        self.assertRaises(ValueError, P('a/b').with_name, 'c/')
583        self.assertRaises(ValueError, P('a/b').with_name, 'c/d')
584
585    def test_with_stem_common(self):
586        P = self.cls
587        self.assertEqual(P('a/b').with_stem('d'), P('a/d'))
588        self.assertEqual(P('/a/b').with_stem('d'), P('/a/d'))
589        self.assertEqual(P('a/b.py').with_stem('d'), P('a/d.py'))
590        self.assertEqual(P('/a/b.py').with_stem('d'), P('/a/d.py'))
591        self.assertEqual(P('/a/b.tar.gz').with_stem('d'), P('/a/d.gz'))
592        self.assertEqual(P('a/Dot ending.').with_stem('d'), P('a/d'))
593        self.assertEqual(P('/a/Dot ending.').with_stem('d'), P('/a/d'))
594        self.assertRaises(ValueError, P('').with_stem, 'd')
595        self.assertRaises(ValueError, P('.').with_stem, 'd')
596        self.assertRaises(ValueError, P('/').with_stem, 'd')
597        self.assertRaises(ValueError, P('a/b').with_stem, '')
598        self.assertRaises(ValueError, P('a/b').with_stem, '/c')
599        self.assertRaises(ValueError, P('a/b').with_stem, 'c/')
600        self.assertRaises(ValueError, P('a/b').with_stem, 'c/d')
601
602    def test_with_suffix_common(self):
603        P = self.cls
604        self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz'))
605        self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz'))
606        self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz'))
607        self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz'))
608        # Stripping suffix.
609        self.assertEqual(P('a/b.py').with_suffix(''), P('a/b'))
610        self.assertEqual(P('/a/b').with_suffix(''), P('/a/b'))
611        # Path doesn't have a "filename" component.
612        self.assertRaises(ValueError, P('').with_suffix, '.gz')
613        self.assertRaises(ValueError, P('.').with_suffix, '.gz')
614        self.assertRaises(ValueError, P('/').with_suffix, '.gz')
615        # Invalid suffix.
616        self.assertRaises(ValueError, P('a/b').with_suffix, 'gz')
617        self.assertRaises(ValueError, P('a/b').with_suffix, '/')
618        self.assertRaises(ValueError, P('a/b').with_suffix, '.')
619        self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz')
620        self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d')
621        self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
622        self.assertRaises(ValueError, P('a/b').with_suffix, './.d')
623        self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.')
624        self.assertRaises(ValueError, P('a/b').with_suffix,
625                          (self.flavour.sep, 'd'))
626
627    def test_relative_to_common(self):
628        P = self.cls
629        p = P('a/b')
630        self.assertRaises(TypeError, p.relative_to)
631        self.assertRaises(TypeError, p.relative_to, b'a')
632        self.assertEqual(p.relative_to(P()), P('a/b'))
633        self.assertEqual(p.relative_to(''), P('a/b'))
634        self.assertEqual(p.relative_to(P('a')), P('b'))
635        self.assertEqual(p.relative_to('a'), P('b'))
636        self.assertEqual(p.relative_to('a/'), P('b'))
637        self.assertEqual(p.relative_to(P('a/b')), P())
638        self.assertEqual(p.relative_to('a/b'), P())
639        # With several args.
640        self.assertEqual(p.relative_to('a', 'b'), P())
641        # Unrelated paths.
642        self.assertRaises(ValueError, p.relative_to, P('c'))
643        self.assertRaises(ValueError, p.relative_to, P('a/b/c'))
644        self.assertRaises(ValueError, p.relative_to, P('a/c'))
645        self.assertRaises(ValueError, p.relative_to, P('/a'))
646        p = P('/a/b')
647        self.assertEqual(p.relative_to(P('/')), P('a/b'))
648        self.assertEqual(p.relative_to('/'), P('a/b'))
649        self.assertEqual(p.relative_to(P('/a')), P('b'))
650        self.assertEqual(p.relative_to('/a'), P('b'))
651        self.assertEqual(p.relative_to('/a/'), P('b'))
652        self.assertEqual(p.relative_to(P('/a/b')), P())
653        self.assertEqual(p.relative_to('/a/b'), P())
654        # Unrelated paths.
655        self.assertRaises(ValueError, p.relative_to, P('/c'))
656        self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
657        self.assertRaises(ValueError, p.relative_to, P('/a/c'))
658        self.assertRaises(ValueError, p.relative_to, P())
659        self.assertRaises(ValueError, p.relative_to, '')
660        self.assertRaises(ValueError, p.relative_to, P('a'))
661
662    def test_is_relative_to_common(self):
663        P = self.cls
664        p = P('a/b')
665        self.assertRaises(TypeError, p.is_relative_to)
666        self.assertRaises(TypeError, p.is_relative_to, b'a')
667        self.assertTrue(p.is_relative_to(P()))
668        self.assertTrue(p.is_relative_to(''))
669        self.assertTrue(p.is_relative_to(P('a')))
670        self.assertTrue(p.is_relative_to('a/'))
671        self.assertTrue(p.is_relative_to(P('a/b')))
672        self.assertTrue(p.is_relative_to('a/b'))
673        # With several args.
674        self.assertTrue(p.is_relative_to('a', 'b'))
675        # Unrelated paths.
676        self.assertFalse(p.is_relative_to(P('c')))
677        self.assertFalse(p.is_relative_to(P('a/b/c')))
678        self.assertFalse(p.is_relative_to(P('a/c')))
679        self.assertFalse(p.is_relative_to(P('/a')))
680        p = P('/a/b')
681        self.assertTrue(p.is_relative_to(P('/')))
682        self.assertTrue(p.is_relative_to('/'))
683        self.assertTrue(p.is_relative_to(P('/a')))
684        self.assertTrue(p.is_relative_to('/a'))
685        self.assertTrue(p.is_relative_to('/a/'))
686        self.assertTrue(p.is_relative_to(P('/a/b')))
687        self.assertTrue(p.is_relative_to('/a/b'))
688        # Unrelated paths.
689        self.assertFalse(p.is_relative_to(P('/c')))
690        self.assertFalse(p.is_relative_to(P('/a/b/c')))
691        self.assertFalse(p.is_relative_to(P('/a/c')))
692        self.assertFalse(p.is_relative_to(P()))
693        self.assertFalse(p.is_relative_to(''))
694        self.assertFalse(p.is_relative_to(P('a')))
695
696    def test_pickling_common(self):
697        P = self.cls
698        p = P('/a/b')
699        for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
700            dumped = pickle.dumps(p, proto)
701            pp = pickle.loads(dumped)
702            self.assertIs(pp.__class__, p.__class__)
703            self.assertEqual(pp, p)
704            self.assertEqual(hash(pp), hash(p))
705            self.assertEqual(str(pp), str(p))
706
707
708class PurePosixPathTest(_BasePurePathTest, unittest.TestCase):
709    cls = pathlib.PurePosixPath
710
711    def test_root(self):
712        P = self.cls
713        self.assertEqual(P('/a/b').root, '/')
714        self.assertEqual(P('///a/b').root, '/')
715        # POSIX special case for two leading slashes.
716        self.assertEqual(P('//a/b').root, '//')
717
718    def test_eq(self):
719        P = self.cls
720        self.assertNotEqual(P('a/b'), P('A/b'))
721        self.assertEqual(P('/a'), P('///a'))
722        self.assertNotEqual(P('/a'), P('//a'))
723
724    def test_as_uri(self):
725        P = self.cls
726        self.assertEqual(P('/').as_uri(), 'file:///')
727        self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c')
728        self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c')
729
730    def test_as_uri_non_ascii(self):
731        from urllib.parse import quote_from_bytes
732        P = self.cls
733        try:
734            os.fsencode('\xe9')
735        except UnicodeEncodeError:
736            self.skipTest("\\xe9 cannot be encoded to the filesystem encoding")
737        self.assertEqual(P('/a/b\xe9').as_uri(),
738                         'file:///a/b' + quote_from_bytes(os.fsencode('\xe9')))
739
740    def test_match(self):
741        P = self.cls
742        self.assertFalse(P('A.py').match('a.PY'))
743
744    def test_is_absolute(self):
745        P = self.cls
746        self.assertFalse(P().is_absolute())
747        self.assertFalse(P('a').is_absolute())
748        self.assertFalse(P('a/b/').is_absolute())
749        self.assertTrue(P('/').is_absolute())
750        self.assertTrue(P('/a').is_absolute())
751        self.assertTrue(P('/a/b/').is_absolute())
752        self.assertTrue(P('//a').is_absolute())
753        self.assertTrue(P('//a/b').is_absolute())
754
755    def test_is_reserved(self):
756        P = self.cls
757        self.assertIs(False, P('').is_reserved())
758        self.assertIs(False, P('/').is_reserved())
759        self.assertIs(False, P('/foo/bar').is_reserved())
760        self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved())
761
762    def test_join(self):
763        P = self.cls
764        p = P('//a')
765        pp = p.joinpath('b')
766        self.assertEqual(pp, P('//a/b'))
767        pp = P('/a').joinpath('//c')
768        self.assertEqual(pp, P('//c'))
769        pp = P('//a').joinpath('/c')
770        self.assertEqual(pp, P('/c'))
771
772    def test_div(self):
773        # Basically the same as joinpath().
774        P = self.cls
775        p = P('//a')
776        pp = p / 'b'
777        self.assertEqual(pp, P('//a/b'))
778        pp = P('/a') / '//c'
779        self.assertEqual(pp, P('//c'))
780        pp = P('//a') / '/c'
781        self.assertEqual(pp, P('/c'))
782
783
784class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
785    cls = pathlib.PureWindowsPath
786
787    equivalences = _BasePurePathTest.equivalences.copy()
788    equivalences.update({
789        'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ],
790        'c:/a': [
791            ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'),
792            ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'),
793            ],
794        '//a/b/': [ ('//a/b',) ],
795        '//a/b/c': [
796            ('//a/b', 'c'), ('//a/b/', 'c'),
797            ],
798    })
799
800    def test_str(self):
801        p = self.cls('a/b/c')
802        self.assertEqual(str(p), 'a\\b\\c')
803        p = self.cls('c:/a/b/c')
804        self.assertEqual(str(p), 'c:\\a\\b\\c')
805        p = self.cls('//a/b')
806        self.assertEqual(str(p), '\\\\a\\b\\')
807        p = self.cls('//a/b/c')
808        self.assertEqual(str(p), '\\\\a\\b\\c')
809        p = self.cls('//a/b/c/d')
810        self.assertEqual(str(p), '\\\\a\\b\\c\\d')
811
812    def test_str_subclass(self):
813        self._check_str_subclass('c:')
814        self._check_str_subclass('c:a')
815        self._check_str_subclass('c:a\\b.txt')
816        self._check_str_subclass('c:\\')
817        self._check_str_subclass('c:\\a')
818        self._check_str_subclass('c:\\a\\b.txt')
819        self._check_str_subclass('\\\\some\\share')
820        self._check_str_subclass('\\\\some\\share\\a')
821        self._check_str_subclass('\\\\some\\share\\a\\b.txt')
822
823    def test_eq(self):
824        P = self.cls
825        self.assertEqual(P('c:a/b'), P('c:a/b'))
826        self.assertEqual(P('c:a/b'), P('c:', 'a', 'b'))
827        self.assertNotEqual(P('c:a/b'), P('d:a/b'))
828        self.assertNotEqual(P('c:a/b'), P('c:/a/b'))
829        self.assertNotEqual(P('/a/b'), P('c:/a/b'))
830        # Case-insensitivity.
831        self.assertEqual(P('a/B'), P('A/b'))
832        self.assertEqual(P('C:a/B'), P('c:A/b'))
833        self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b'))
834
835    def test_as_uri(self):
836        P = self.cls
837        with self.assertRaises(ValueError):
838            P('/a/b').as_uri()
839        with self.assertRaises(ValueError):
840            P('c:a/b').as_uri()
841        self.assertEqual(P('c:/').as_uri(), 'file:///c:/')
842        self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c')
843        self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c')
844        self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9')
845        self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/')
846        self.assertEqual(P('//some/share/a/b.c').as_uri(),
847                         'file://some/share/a/b.c')
848        self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
849                         'file://some/share/a/b%25%23c%C3%A9')
850
851    def test_match_common(self):
852        P = self.cls
853        # Absolute patterns.
854        self.assertTrue(P('c:/b.py').match('/*.py'))
855        self.assertTrue(P('c:/b.py').match('c:*.py'))
856        self.assertTrue(P('c:/b.py').match('c:/*.py'))
857        self.assertFalse(P('d:/b.py').match('c:/*.py'))  # wrong drive
858        self.assertFalse(P('b.py').match('/*.py'))
859        self.assertFalse(P('b.py').match('c:*.py'))
860        self.assertFalse(P('b.py').match('c:/*.py'))
861        self.assertFalse(P('c:b.py').match('/*.py'))
862        self.assertFalse(P('c:b.py').match('c:/*.py'))
863        self.assertFalse(P('/b.py').match('c:*.py'))
864        self.assertFalse(P('/b.py').match('c:/*.py'))
865        # UNC patterns.
866        self.assertTrue(P('//some/share/a.py').match('/*.py'))
867        self.assertTrue(P('//some/share/a.py').match('//some/share/*.py'))
868        self.assertFalse(P('//other/share/a.py').match('//some/share/*.py'))
869        self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py'))
870        # Case-insensitivity.
871        self.assertTrue(P('B.py').match('b.PY'))
872        self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY'))
873        self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY'))
874
875    def test_ordering_common(self):
876        # Case-insensitivity.
877        def assertOrderedEqual(a, b):
878            self.assertLessEqual(a, b)
879            self.assertGreaterEqual(b, a)
880        P = self.cls
881        p = P('c:A/b')
882        q = P('C:a/B')
883        assertOrderedEqual(p, q)
884        self.assertFalse(p < q)
885        self.assertFalse(p > q)
886        p = P('//some/Share/A/b')
887        q = P('//Some/SHARE/a/B')
888        assertOrderedEqual(p, q)
889        self.assertFalse(p < q)
890        self.assertFalse(p > q)
891
892    def test_parts(self):
893        P = self.cls
894        p = P('c:a/b')
895        parts = p.parts
896        self.assertEqual(parts, ('c:', 'a', 'b'))
897        p = P('c:/a/b')
898        parts = p.parts
899        self.assertEqual(parts, ('c:\\', 'a', 'b'))
900        p = P('//a/b/c/d')
901        parts = p.parts
902        self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd'))
903
904    def test_parent(self):
905        # Anchored
906        P = self.cls
907        p = P('z:a/b/c')
908        self.assertEqual(p.parent, P('z:a/b'))
909        self.assertEqual(p.parent.parent, P('z:a'))
910        self.assertEqual(p.parent.parent.parent, P('z:'))
911        self.assertEqual(p.parent.parent.parent.parent, P('z:'))
912        p = P('z:/a/b/c')
913        self.assertEqual(p.parent, P('z:/a/b'))
914        self.assertEqual(p.parent.parent, P('z:/a'))
915        self.assertEqual(p.parent.parent.parent, P('z:/'))
916        self.assertEqual(p.parent.parent.parent.parent, P('z:/'))
917        p = P('//a/b/c/d')
918        self.assertEqual(p.parent, P('//a/b/c'))
919        self.assertEqual(p.parent.parent, P('//a/b'))
920        self.assertEqual(p.parent.parent.parent, P('//a/b'))
921
922    def test_parents(self):
923        # Anchored
924        P = self.cls
925        p = P('z:a/b/')
926        par = p.parents
927        self.assertEqual(len(par), 2)
928        self.assertEqual(par[0], P('z:a'))
929        self.assertEqual(par[1], P('z:'))
930        self.assertEqual(par[0:1], (P('z:a'),))
931        self.assertEqual(par[:-1], (P('z:a'),))
932        self.assertEqual(par[:2], (P('z:a'), P('z:')))
933        self.assertEqual(par[1:], (P('z:'),))
934        self.assertEqual(par[::2], (P('z:a'),))
935        self.assertEqual(par[::-1], (P('z:'), P('z:a')))
936        self.assertEqual(list(par), [P('z:a'), P('z:')])
937        with self.assertRaises(IndexError):
938            par[2]
939        p = P('z:/a/b/')
940        par = p.parents
941        self.assertEqual(len(par), 2)
942        self.assertEqual(par[0], P('z:/a'))
943        self.assertEqual(par[1], P('z:/'))
944        self.assertEqual(par[0:1], (P('z:/a'),))
945        self.assertEqual(par[0:-1], (P('z:/a'),))
946        self.assertEqual(par[:2], (P('z:/a'), P('z:/')))
947        self.assertEqual(par[1:], (P('z:/'),))
948        self.assertEqual(par[::2], (P('z:/a'),))
949        self.assertEqual(par[::-1], (P('z:/'), P('z:/a'),))
950        self.assertEqual(list(par), [P('z:/a'), P('z:/')])
951        with self.assertRaises(IndexError):
952            par[2]
953        p = P('//a/b/c/d')
954        par = p.parents
955        self.assertEqual(len(par), 2)
956        self.assertEqual(par[0], P('//a/b/c'))
957        self.assertEqual(par[1], P('//a/b'))
958        self.assertEqual(par[0:1], (P('//a/b/c'),))
959        self.assertEqual(par[0:-1], (P('//a/b/c'),))
960        self.assertEqual(par[:2], (P('//a/b/c'), P('//a/b')))
961        self.assertEqual(par[1:], (P('//a/b'),))
962        self.assertEqual(par[::2], (P('//a/b/c'),))
963        self.assertEqual(par[::-1], (P('//a/b'), P('//a/b/c')))
964        self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')])
965        with self.assertRaises(IndexError):
966            par[2]
967
968    def test_drive(self):
969        P = self.cls
970        self.assertEqual(P('c:').drive, 'c:')
971        self.assertEqual(P('c:a/b').drive, 'c:')
972        self.assertEqual(P('c:/').drive, 'c:')
973        self.assertEqual(P('c:/a/b/').drive, 'c:')
974        self.assertEqual(P('//a/b').drive, '\\\\a\\b')
975        self.assertEqual(P('//a/b/').drive, '\\\\a\\b')
976        self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b')
977
978    def test_root(self):
979        P = self.cls
980        self.assertEqual(P('c:').root, '')
981        self.assertEqual(P('c:a/b').root, '')
982        self.assertEqual(P('c:/').root, '\\')
983        self.assertEqual(P('c:/a/b/').root, '\\')
984        self.assertEqual(P('//a/b').root, '\\')
985        self.assertEqual(P('//a/b/').root, '\\')
986        self.assertEqual(P('//a/b/c/d').root, '\\')
987
988    def test_anchor(self):
989        P = self.cls
990        self.assertEqual(P('c:').anchor, 'c:')
991        self.assertEqual(P('c:a/b').anchor, 'c:')
992        self.assertEqual(P('c:/').anchor, 'c:\\')
993        self.assertEqual(P('c:/a/b/').anchor, 'c:\\')
994        self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\')
995        self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\')
996        self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\')
997
998    def test_name(self):
999        P = self.cls
1000        self.assertEqual(P('c:').name, '')
1001        self.assertEqual(P('c:/').name, '')
1002        self.assertEqual(P('c:a/b').name, 'b')
1003        self.assertEqual(P('c:/a/b').name, 'b')
1004        self.assertEqual(P('c:a/b.py').name, 'b.py')
1005        self.assertEqual(P('c:/a/b.py').name, 'b.py')
1006        self.assertEqual(P('//My.py/Share.php').name, '')
1007        self.assertEqual(P('//My.py/Share.php/a/b').name, 'b')
1008
1009    def test_suffix(self):
1010        P = self.cls
1011        self.assertEqual(P('c:').suffix, '')
1012        self.assertEqual(P('c:/').suffix, '')
1013        self.assertEqual(P('c:a/b').suffix, '')
1014        self.assertEqual(P('c:/a/b').suffix, '')
1015        self.assertEqual(P('c:a/b.py').suffix, '.py')
1016        self.assertEqual(P('c:/a/b.py').suffix, '.py')
1017        self.assertEqual(P('c:a/.hgrc').suffix, '')
1018        self.assertEqual(P('c:/a/.hgrc').suffix, '')
1019        self.assertEqual(P('c:a/.hg.rc').suffix, '.rc')
1020        self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc')
1021        self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz')
1022        self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz')
1023        self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '')
1024        self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '')
1025        self.assertEqual(P('//My.py/Share.php').suffix, '')
1026        self.assertEqual(P('//My.py/Share.php/a/b').suffix, '')
1027
1028    def test_suffixes(self):
1029        P = self.cls
1030        self.assertEqual(P('c:').suffixes, [])
1031        self.assertEqual(P('c:/').suffixes, [])
1032        self.assertEqual(P('c:a/b').suffixes, [])
1033        self.assertEqual(P('c:/a/b').suffixes, [])
1034        self.assertEqual(P('c:a/b.py').suffixes, ['.py'])
1035        self.assertEqual(P('c:/a/b.py').suffixes, ['.py'])
1036        self.assertEqual(P('c:a/.hgrc').suffixes, [])
1037        self.assertEqual(P('c:/a/.hgrc').suffixes, [])
1038        self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc'])
1039        self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc'])
1040        self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz'])
1041        self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz'])
1042        self.assertEqual(P('//My.py/Share.php').suffixes, [])
1043        self.assertEqual(P('//My.py/Share.php/a/b').suffixes, [])
1044        self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, [])
1045        self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, [])
1046
1047    def test_stem(self):
1048        P = self.cls
1049        self.assertEqual(P('c:').stem, '')
1050        self.assertEqual(P('c:.').stem, '')
1051        self.assertEqual(P('c:..').stem, '..')
1052        self.assertEqual(P('c:/').stem, '')
1053        self.assertEqual(P('c:a/b').stem, 'b')
1054        self.assertEqual(P('c:a/b.py').stem, 'b')
1055        self.assertEqual(P('c:a/.hgrc').stem, '.hgrc')
1056        self.assertEqual(P('c:a/.hg.rc').stem, '.hg')
1057        self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar')
1058        self.assertEqual(P('c:a/Some name. Ending with a dot.').stem,
1059                         'Some name. Ending with a dot.')
1060
1061    def test_with_name(self):
1062        P = self.cls
1063        self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml'))
1064        self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml'))
1065        self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml'))
1066        self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml'))
1067        self.assertRaises(ValueError, P('c:').with_name, 'd.xml')
1068        self.assertRaises(ValueError, P('c:/').with_name, 'd.xml')
1069        self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml')
1070        self.assertRaises(ValueError, P('c:a/b').with_name, 'd:')
1071        self.assertRaises(ValueError, P('c:a/b').with_name, 'd:e')
1072        self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e')
1073        self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share')
1074
1075    def test_with_stem(self):
1076        P = self.cls
1077        self.assertEqual(P('c:a/b').with_stem('d'), P('c:a/d'))
1078        self.assertEqual(P('c:/a/b').with_stem('d'), P('c:/a/d'))
1079        self.assertEqual(P('c:a/Dot ending.').with_stem('d'), P('c:a/d'))
1080        self.assertEqual(P('c:/a/Dot ending.').with_stem('d'), P('c:/a/d'))
1081        self.assertRaises(ValueError, P('c:').with_stem, 'd')
1082        self.assertRaises(ValueError, P('c:/').with_stem, 'd')
1083        self.assertRaises(ValueError, P('//My/Share').with_stem, 'd')
1084        self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:')
1085        self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:e')
1086        self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:/e')
1087        self.assertRaises(ValueError, P('c:a/b').with_stem, '//My/Share')
1088
1089    def test_with_suffix(self):
1090        P = self.cls
1091        self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz'))
1092        self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz'))
1093        self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz'))
1094        self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz'))
1095        # Path doesn't have a "filename" component.
1096        self.assertRaises(ValueError, P('').with_suffix, '.gz')
1097        self.assertRaises(ValueError, P('.').with_suffix, '.gz')
1098        self.assertRaises(ValueError, P('/').with_suffix, '.gz')
1099        self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz')
1100        # Invalid suffix.
1101        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz')
1102        self.assertRaises(ValueError, P('c:a/b').with_suffix, '/')
1103        self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\')
1104        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:')
1105        self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz')
1106        self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz')
1107        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz')
1108        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d')
1109        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d')
1110        self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d')
1111        self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d')
1112
1113    def test_relative_to(self):
1114        P = self.cls
1115        p = P('C:Foo/Bar')
1116        self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
1117        self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
1118        self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
1119        self.assertEqual(p.relative_to('c:foO'), P('Bar'))
1120        self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
1121        self.assertEqual(p.relative_to(P('c:foO/baR')), P())
1122        self.assertEqual(p.relative_to('c:foO/baR'), P())
1123        # Unrelated paths.
1124        self.assertRaises(ValueError, p.relative_to, P())
1125        self.assertRaises(ValueError, p.relative_to, '')
1126        self.assertRaises(ValueError, p.relative_to, P('d:'))
1127        self.assertRaises(ValueError, p.relative_to, P('/'))
1128        self.assertRaises(ValueError, p.relative_to, P('Foo'))
1129        self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1130        self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
1131        self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
1132        self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
1133        p = P('C:/Foo/Bar')
1134        self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar'))
1135        self.assertEqual(p.relative_to('c:'), P('/Foo/Bar'))
1136        self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar')
1137        self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar')
1138        self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
1139        self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
1140        self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
1141        self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
1142        self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
1143        self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
1144        self.assertEqual(p.relative_to('c:/foO/baR'), P())
1145        # Unrelated paths.
1146        self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
1147        self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
1148        self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
1149        self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
1150        self.assertRaises(ValueError, p.relative_to, P('d:'))
1151        self.assertRaises(ValueError, p.relative_to, P('d:/'))
1152        self.assertRaises(ValueError, p.relative_to, P('/'))
1153        self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1154        self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
1155        # UNC paths.
1156        p = P('//Server/Share/Foo/Bar')
1157        self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
1158        self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
1159        self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
1160        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
1161        self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
1162        self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
1163        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
1164        self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
1165        # Unrelated paths.
1166        self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
1167        self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
1168        self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
1169        self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
1170
1171    def test_is_relative_to(self):
1172        P = self.cls
1173        p = P('C:Foo/Bar')
1174        self.assertTrue(p.is_relative_to(P('c:')))
1175        self.assertTrue(p.is_relative_to('c:'))
1176        self.assertTrue(p.is_relative_to(P('c:foO')))
1177        self.assertTrue(p.is_relative_to('c:foO'))
1178        self.assertTrue(p.is_relative_to('c:foO/'))
1179        self.assertTrue(p.is_relative_to(P('c:foO/baR')))
1180        self.assertTrue(p.is_relative_to('c:foO/baR'))
1181        # Unrelated paths.
1182        self.assertFalse(p.is_relative_to(P()))
1183        self.assertFalse(p.is_relative_to(''))
1184        self.assertFalse(p.is_relative_to(P('d:')))
1185        self.assertFalse(p.is_relative_to(P('/')))
1186        self.assertFalse(p.is_relative_to(P('Foo')))
1187        self.assertFalse(p.is_relative_to(P('/Foo')))
1188        self.assertFalse(p.is_relative_to(P('C:/Foo')))
1189        self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz')))
1190        self.assertFalse(p.is_relative_to(P('C:Foo/Baz')))
1191        p = P('C:/Foo/Bar')
1192        self.assertTrue(p.is_relative_to('c:'))
1193        self.assertTrue(p.is_relative_to(P('c:/')))
1194        self.assertTrue(p.is_relative_to(P('c:/foO')))
1195        self.assertTrue(p.is_relative_to('c:/foO/'))
1196        self.assertTrue(p.is_relative_to(P('c:/foO/baR')))
1197        self.assertTrue(p.is_relative_to('c:/foO/baR'))
1198        # Unrelated paths.
1199        self.assertFalse(p.is_relative_to(P('C:/Baz')))
1200        self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz')))
1201        self.assertFalse(p.is_relative_to(P('C:/Foo/Baz')))
1202        self.assertFalse(p.is_relative_to(P('C:Foo')))
1203        self.assertFalse(p.is_relative_to(P('d:')))
1204        self.assertFalse(p.is_relative_to(P('d:/')))
1205        self.assertFalse(p.is_relative_to(P('/')))
1206        self.assertFalse(p.is_relative_to(P('/Foo')))
1207        self.assertFalse(p.is_relative_to(P('//C/Foo')))
1208        # UNC paths.
1209        p = P('//Server/Share/Foo/Bar')
1210        self.assertTrue(p.is_relative_to(P('//sErver/sHare')))
1211        self.assertTrue(p.is_relative_to('//sErver/sHare'))
1212        self.assertTrue(p.is_relative_to('//sErver/sHare/'))
1213        self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo')))
1214        self.assertTrue(p.is_relative_to('//sErver/sHare/Foo'))
1215        self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/'))
1216        self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar')))
1217        self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar'))
1218        # Unrelated paths.
1219        self.assertFalse(p.is_relative_to(P('/Server/Share/Foo')))
1220        self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo')))
1221        self.assertFalse(p.is_relative_to(P('//z/Share/Foo')))
1222        self.assertFalse(p.is_relative_to(P('//Server/z/Foo')))
1223
1224    def test_is_absolute(self):
1225        P = self.cls
1226        # Under NT, only paths with both a drive and a root are absolute.
1227        self.assertFalse(P().is_absolute())
1228        self.assertFalse(P('a').is_absolute())
1229        self.assertFalse(P('a/b/').is_absolute())
1230        self.assertFalse(P('/').is_absolute())
1231        self.assertFalse(P('/a').is_absolute())
1232        self.assertFalse(P('/a/b/').is_absolute())
1233        self.assertFalse(P('c:').is_absolute())
1234        self.assertFalse(P('c:a').is_absolute())
1235        self.assertFalse(P('c:a/b/').is_absolute())
1236        self.assertTrue(P('c:/').is_absolute())
1237        self.assertTrue(P('c:/a').is_absolute())
1238        self.assertTrue(P('c:/a/b/').is_absolute())
1239        # UNC paths are absolute by definition.
1240        self.assertTrue(P('//a/b').is_absolute())
1241        self.assertTrue(P('//a/b/').is_absolute())
1242        self.assertTrue(P('//a/b/c').is_absolute())
1243        self.assertTrue(P('//a/b/c/d').is_absolute())
1244
1245    def test_join(self):
1246        P = self.cls
1247        p = P('C:/a/b')
1248        pp = p.joinpath('x/y')
1249        self.assertEqual(pp, P('C:/a/b/x/y'))
1250        pp = p.joinpath('/x/y')
1251        self.assertEqual(pp, P('C:/x/y'))
1252        # Joining with a different drive => the first path is ignored, even
1253        # if the second path is relative.
1254        pp = p.joinpath('D:x/y')
1255        self.assertEqual(pp, P('D:x/y'))
1256        pp = p.joinpath('D:/x/y')
1257        self.assertEqual(pp, P('D:/x/y'))
1258        pp = p.joinpath('//host/share/x/y')
1259        self.assertEqual(pp, P('//host/share/x/y'))
1260        # Joining with the same drive => the first path is appended to if
1261        # the second path is relative.
1262        pp = p.joinpath('c:x/y')
1263        self.assertEqual(pp, P('C:/a/b/x/y'))
1264        pp = p.joinpath('c:/x/y')
1265        self.assertEqual(pp, P('C:/x/y'))
1266
1267    def test_div(self):
1268        # Basically the same as joinpath().
1269        P = self.cls
1270        p = P('C:/a/b')
1271        self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
1272        self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
1273        self.assertEqual(p / '/x/y', P('C:/x/y'))
1274        self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
1275        # Joining with a different drive => the first path is ignored, even
1276        # if the second path is relative.
1277        self.assertEqual(p / 'D:x/y', P('D:x/y'))
1278        self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
1279        self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
1280        self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
1281        self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
1282        # Joining with the same drive => the first path is appended to if
1283        # the second path is relative.
1284        self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
1285        self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
1286
1287    def test_is_reserved(self):
1288        P = self.cls
1289        self.assertIs(False, P('').is_reserved())
1290        self.assertIs(False, P('/').is_reserved())
1291        self.assertIs(False, P('/foo/bar').is_reserved())
1292        # UNC paths are never reserved.
1293        self.assertIs(False, P('//my/share/nul/con/aux').is_reserved())
1294        # Case-insensitive DOS-device names are reserved.
1295        self.assertIs(True, P('nul').is_reserved())
1296        self.assertIs(True, P('aux').is_reserved())
1297        self.assertIs(True, P('prn').is_reserved())
1298        self.assertIs(True, P('con').is_reserved())
1299        self.assertIs(True, P('conin$').is_reserved())
1300        self.assertIs(True, P('conout$').is_reserved())
1301        # COM/LPT + 1-9 or + superscript 1-3 are reserved.
1302        self.assertIs(True, P('COM1').is_reserved())
1303        self.assertIs(True, P('LPT9').is_reserved())
1304        self.assertIs(True, P('com\xb9').is_reserved())
1305        self.assertIs(True, P('com\xb2').is_reserved())
1306        self.assertIs(True, P('lpt\xb3').is_reserved())
1307        # DOS-device name mataching ignores characters after a dot or
1308        # a colon and also ignores trailing spaces.
1309        self.assertIs(True, P('NUL.txt').is_reserved())
1310        self.assertIs(True, P('PRN  ').is_reserved())
1311        self.assertIs(True, P('AUX  .txt').is_reserved())
1312        self.assertIs(True, P('COM1:bar').is_reserved())
1313        self.assertIs(True, P('LPT9   :bar').is_reserved())
1314        # DOS-device names are only matched at the beginning
1315        # of a path component.
1316        self.assertIs(False, P('bar.com9').is_reserved())
1317        self.assertIs(False, P('bar.lpt9').is_reserved())
1318        # Only the last path component matters.
1319        self.assertIs(True, P('c:/baz/con/NUL').is_reserved())
1320        self.assertIs(False, P('c:/NUL/con/baz').is_reserved())
1321
1322class PurePathTest(_BasePurePathTest, unittest.TestCase):
1323    cls = pathlib.PurePath
1324
1325    def test_concrete_class(self):
1326        p = self.cls('a')
1327        self.assertIs(type(p),
1328            pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath)
1329
1330    def test_different_flavours_unequal(self):
1331        p = pathlib.PurePosixPath('a')
1332        q = pathlib.PureWindowsPath('a')
1333        self.assertNotEqual(p, q)
1334
1335    def test_different_flavours_unordered(self):
1336        p = pathlib.PurePosixPath('a')
1337        q = pathlib.PureWindowsPath('a')
1338        with self.assertRaises(TypeError):
1339            p < q
1340        with self.assertRaises(TypeError):
1341            p <= q
1342        with self.assertRaises(TypeError):
1343            p > q
1344        with self.assertRaises(TypeError):
1345            p >= q
1346
1347
1348#
1349# Tests for the concrete classes.
1350#
1351
1352# Make sure any symbolic links in the base test path are resolved.
1353BASE = os.path.realpath(TESTFN)
1354join = lambda *x: os.path.join(BASE, *x)
1355rel_join = lambda *x: os.path.join(TESTFN, *x)
1356
1357only_nt = unittest.skipIf(os.name != 'nt',
1358                          'test requires a Windows-compatible system')
1359only_posix = unittest.skipIf(os.name == 'nt',
1360                             'test requires a POSIX-compatible system')
1361
1362@only_posix
1363class PosixPathAsPureTest(PurePosixPathTest):
1364    cls = pathlib.PosixPath
1365
1366@only_nt
1367class WindowsPathAsPureTest(PureWindowsPathTest):
1368    cls = pathlib.WindowsPath
1369
1370    def test_owner(self):
1371        P = self.cls
1372        with self.assertRaises(NotImplementedError):
1373            P('c:/').owner()
1374
1375    def test_group(self):
1376        P = self.cls
1377        with self.assertRaises(NotImplementedError):
1378            P('c:/').group()
1379
1380
1381class _BasePathTest(object):
1382    """Tests for the FS-accessing functionalities of the Path classes."""
1383
1384    # (BASE)
1385    #  |
1386    #  |-- brokenLink -> non-existing
1387    #  |-- dirA
1388    #  |   `-- linkC -> ../dirB
1389    #  |-- dirB
1390    #  |   |-- fileB
1391    #  |   `-- linkD -> ../dirB
1392    #  |-- dirC
1393    #  |   |-- dirD
1394    #  |   |   `-- fileD
1395    #  |   `-- fileC
1396    #  |   `-- novel.txt
1397    #  |-- dirE  # No permissions
1398    #  |-- fileA
1399    #  |-- linkA -> fileA
1400    #  |-- linkB -> dirB
1401    #  `-- brokenLinkLoop -> brokenLinkLoop
1402    #
1403
1404    def setUp(self):
1405        def cleanup():
1406            os.chmod(join('dirE'), 0o777)
1407            os_helper.rmtree(BASE)
1408        self.addCleanup(cleanup)
1409        os.mkdir(BASE)
1410        os.mkdir(join('dirA'))
1411        os.mkdir(join('dirB'))
1412        os.mkdir(join('dirC'))
1413        os.mkdir(join('dirC', 'dirD'))
1414        os.mkdir(join('dirE'))
1415        with open(join('fileA'), 'wb') as f:
1416            f.write(b"this is file A\n")
1417        with open(join('dirB', 'fileB'), 'wb') as f:
1418            f.write(b"this is file B\n")
1419        with open(join('dirC', 'fileC'), 'wb') as f:
1420            f.write(b"this is file C\n")
1421        with open(join('dirC', 'novel.txt'), 'wb') as f:
1422            f.write(b"this is a novel\n")
1423        with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
1424            f.write(b"this is file D\n")
1425        os.chmod(join('dirE'), 0)
1426        if os_helper.can_symlink():
1427            # Relative symlinks.
1428            os.symlink('fileA', join('linkA'))
1429            os.symlink('non-existing', join('brokenLink'))
1430            self.dirlink('dirB', join('linkB'))
1431            self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
1432            # This one goes upwards, creating a loop.
1433            self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1434            # Broken symlink (pointing to itself).
1435            os.symlink('brokenLinkLoop',  join('brokenLinkLoop'))
1436
1437    if os.name == 'nt':
1438        # Workaround for http://bugs.python.org/issue13772.
1439        def dirlink(self, src, dest):
1440            os.symlink(src, dest, target_is_directory=True)
1441    else:
1442        def dirlink(self, src, dest):
1443            os.symlink(src, dest)
1444
1445    def assertSame(self, path_a, path_b):
1446        self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
1447                        "%r and %r don't point to the same file" %
1448                        (path_a, path_b))
1449
1450    def assertFileNotFound(self, func, *args, **kwargs):
1451        with self.assertRaises(FileNotFoundError) as cm:
1452            func(*args, **kwargs)
1453        self.assertEqual(cm.exception.errno, errno.ENOENT)
1454
1455    def assertEqualNormCase(self, path_a, path_b):
1456        self.assertEqual(os.path.normcase(path_a), os.path.normcase(path_b))
1457
1458    def _test_cwd(self, p):
1459        q = self.cls(os.getcwd())
1460        self.assertEqual(p, q)
1461        self.assertEqualNormCase(str(p), str(q))
1462        self.assertIs(type(p), type(q))
1463        self.assertTrue(p.is_absolute())
1464
1465    def test_cwd(self):
1466        p = self.cls.cwd()
1467        self._test_cwd(p)
1468
1469    def test_absolute_common(self):
1470        P = self.cls
1471
1472        with mock.patch("os.getcwd") as getcwd:
1473            getcwd.return_value = BASE
1474
1475            # Simple relative paths.
1476            self.assertEqual(str(P().absolute()), BASE)
1477            self.assertEqual(str(P('.').absolute()), BASE)
1478            self.assertEqual(str(P('a').absolute()), os.path.join(BASE, 'a'))
1479            self.assertEqual(str(P('a', 'b', 'c').absolute()), os.path.join(BASE, 'a', 'b', 'c'))
1480
1481            # Symlinks should not be resolved.
1482            self.assertEqual(str(P('linkB', 'fileB').absolute()), os.path.join(BASE, 'linkB', 'fileB'))
1483            self.assertEqual(str(P('brokenLink').absolute()), os.path.join(BASE, 'brokenLink'))
1484            self.assertEqual(str(P('brokenLinkLoop').absolute()), os.path.join(BASE, 'brokenLinkLoop'))
1485
1486            # '..' entries should be preserved and not normalised.
1487            self.assertEqual(str(P('..').absolute()), os.path.join(BASE, '..'))
1488            self.assertEqual(str(P('a', '..').absolute()), os.path.join(BASE, 'a', '..'))
1489            self.assertEqual(str(P('..', 'b').absolute()), os.path.join(BASE, '..', 'b'))
1490
1491    def _test_home(self, p):
1492        q = self.cls(os.path.expanduser('~'))
1493        self.assertEqual(p, q)
1494        self.assertEqualNormCase(str(p), str(q))
1495        self.assertIs(type(p), type(q))
1496        self.assertTrue(p.is_absolute())
1497
1498    @unittest.skipIf(
1499        pwd is None, reason="Test requires pwd module to get homedir."
1500    )
1501    def test_home(self):
1502        with os_helper.EnvironmentVarGuard() as env:
1503            self._test_home(self.cls.home())
1504
1505            env.clear()
1506            env['USERPROFILE'] = os.path.join(BASE, 'userprofile')
1507            self._test_home(self.cls.home())
1508
1509            # bpo-38883: ignore `HOME` when set on windows
1510            env['HOME'] = os.path.join(BASE, 'home')
1511            self._test_home(self.cls.home())
1512
1513    def test_samefile(self):
1514        fileA_path = os.path.join(BASE, 'fileA')
1515        fileB_path = os.path.join(BASE, 'dirB', 'fileB')
1516        p = self.cls(fileA_path)
1517        pp = self.cls(fileA_path)
1518        q = self.cls(fileB_path)
1519        self.assertTrue(p.samefile(fileA_path))
1520        self.assertTrue(p.samefile(pp))
1521        self.assertFalse(p.samefile(fileB_path))
1522        self.assertFalse(p.samefile(q))
1523        # Test the non-existent file case
1524        non_existent = os.path.join(BASE, 'foo')
1525        r = self.cls(non_existent)
1526        self.assertRaises(FileNotFoundError, p.samefile, r)
1527        self.assertRaises(FileNotFoundError, p.samefile, non_existent)
1528        self.assertRaises(FileNotFoundError, r.samefile, p)
1529        self.assertRaises(FileNotFoundError, r.samefile, non_existent)
1530        self.assertRaises(FileNotFoundError, r.samefile, r)
1531        self.assertRaises(FileNotFoundError, r.samefile, non_existent)
1532
1533    def test_empty_path(self):
1534        # The empty path points to '.'
1535        p = self.cls('')
1536        self.assertEqual(p.stat(), os.stat('.'))
1537
1538    @unittest.skipIf(is_wasi, "WASI has no user accounts.")
1539    def test_expanduser_common(self):
1540        P = self.cls
1541        p = P('~')
1542        self.assertEqual(p.expanduser(), P(os.path.expanduser('~')))
1543        p = P('foo')
1544        self.assertEqual(p.expanduser(), p)
1545        p = P('/~')
1546        self.assertEqual(p.expanduser(), p)
1547        p = P('../~')
1548        self.assertEqual(p.expanduser(), p)
1549        p = P(P('').absolute().anchor) / '~'
1550        self.assertEqual(p.expanduser(), p)
1551
1552    def test_exists(self):
1553        P = self.cls
1554        p = P(BASE)
1555        self.assertIs(True, p.exists())
1556        self.assertIs(True, (p / 'dirA').exists())
1557        self.assertIs(True, (p / 'fileA').exists())
1558        self.assertIs(False, (p / 'fileA' / 'bah').exists())
1559        if os_helper.can_symlink():
1560            self.assertIs(True, (p / 'linkA').exists())
1561            self.assertIs(True, (p / 'linkB').exists())
1562            self.assertIs(True, (p / 'linkB' / 'fileB').exists())
1563            self.assertIs(False, (p / 'linkA' / 'bah').exists())
1564        self.assertIs(False, (p / 'foo').exists())
1565        self.assertIs(False, P('/xyzzy').exists())
1566        self.assertIs(False, P(BASE + '\udfff').exists())
1567        self.assertIs(False, P(BASE + '\x00').exists())
1568
1569    def test_open_common(self):
1570        p = self.cls(BASE)
1571        with (p / 'fileA').open('r') as f:
1572            self.assertIsInstance(f, io.TextIOBase)
1573            self.assertEqual(f.read(), "this is file A\n")
1574        with (p / 'fileA').open('rb') as f:
1575            self.assertIsInstance(f, io.BufferedIOBase)
1576            self.assertEqual(f.read().strip(), b"this is file A")
1577        with (p / 'fileA').open('rb', buffering=0) as f:
1578            self.assertIsInstance(f, io.RawIOBase)
1579            self.assertEqual(f.read().strip(), b"this is file A")
1580
1581    def test_read_write_bytes(self):
1582        p = self.cls(BASE)
1583        (p / 'fileA').write_bytes(b'abcdefg')
1584        self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
1585        # Check that trying to write str does not truncate the file.
1586        self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr')
1587        self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
1588
1589    def test_read_write_text(self):
1590        p = self.cls(BASE)
1591        (p / 'fileA').write_text('äbcdefg', encoding='latin-1')
1592        self.assertEqual((p / 'fileA').read_text(
1593            encoding='utf-8', errors='ignore'), 'bcdefg')
1594        # Check that trying to write bytes does not truncate the file.
1595        self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes')
1596        self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg')
1597
1598    def test_write_text_with_newlines(self):
1599        p = self.cls(BASE)
1600        # Check that `\n` character change nothing
1601        (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n')
1602        self.assertEqual((p / 'fileA').read_bytes(),
1603                         b'abcde\r\nfghlk\n\rmnopq')
1604        # Check that `\r` character replaces `\n`
1605        (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r')
1606        self.assertEqual((p / 'fileA').read_bytes(),
1607                         b'abcde\r\rfghlk\r\rmnopq')
1608        # Check that `\r\n` character replaces `\n`
1609        (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n')
1610        self.assertEqual((p / 'fileA').read_bytes(),
1611                         b'abcde\r\r\nfghlk\r\n\rmnopq')
1612        # Check that no argument passed will change `\n` to `os.linesep`
1613        os_linesep_byte = bytes(os.linesep, encoding='ascii')
1614        (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq')
1615        self.assertEqual((p / 'fileA').read_bytes(),
1616                          b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq')
1617
1618    def test_iterdir(self):
1619        P = self.cls
1620        p = P(BASE)
1621        it = p.iterdir()
1622        paths = set(it)
1623        expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA']
1624        if os_helper.can_symlink():
1625            expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop']
1626        self.assertEqual(paths, { P(BASE, q) for q in expected })
1627
1628    @os_helper.skip_unless_symlink
1629    def test_iterdir_symlink(self):
1630        # __iter__ on a symlink to a directory.
1631        P = self.cls
1632        p = P(BASE, 'linkB')
1633        paths = set(p.iterdir())
1634        expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1635        self.assertEqual(paths, expected)
1636
1637    def test_iterdir_nodir(self):
1638        # __iter__ on something that is not a directory.
1639        p = self.cls(BASE, 'fileA')
1640        with self.assertRaises(OSError) as cm:
1641            next(p.iterdir())
1642        # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1643        # (see issue #12802).
1644        self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1645                                           errno.ENOENT, errno.EINVAL))
1646
1647    def test_glob_common(self):
1648        def _check(glob, expected):
1649            self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1650        P = self.cls
1651        p = P(BASE)
1652        it = p.glob("fileA")
1653        self.assertIsInstance(it, collections.abc.Iterator)
1654        _check(it, ["fileA"])
1655        _check(p.glob("fileB"), [])
1656        _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1657        if not os_helper.can_symlink():
1658            _check(p.glob("*A"), ['dirA', 'fileA'])
1659        else:
1660            _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1661        if not os_helper.can_symlink():
1662            _check(p.glob("*B/*"), ['dirB/fileB'])
1663        else:
1664            _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1665                                    'linkB/fileB', 'linkB/linkD'])
1666        if not os_helper.can_symlink():
1667            _check(p.glob("*/fileB"), ['dirB/fileB'])
1668        else:
1669            _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1670
1671        if not os_helper.can_symlink():
1672            _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE"])
1673        else:
1674            _check(p.glob("*/"), ["dirA", "dirB", "dirC", "dirE", "linkB"])
1675
1676    def test_rglob_common(self):
1677        def _check(glob, expected):
1678            self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1679        P = self.cls
1680        p = P(BASE)
1681        it = p.rglob("fileA")
1682        self.assertIsInstance(it, collections.abc.Iterator)
1683        _check(it, ["fileA"])
1684        _check(p.rglob("fileB"), ["dirB/fileB"])
1685        _check(p.rglob("*/fileA"), [])
1686        if not os_helper.can_symlink():
1687            _check(p.rglob("*/fileB"), ["dirB/fileB"])
1688        else:
1689            _check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB",
1690                                        "linkB/fileB", "dirA/linkC/fileB"])
1691        _check(p.rglob("file*"), ["fileA", "dirB/fileB",
1692                                  "dirC/fileC", "dirC/dirD/fileD"])
1693        if not os_helper.can_symlink():
1694            _check(p.rglob("*/"), [
1695                "dirA", "dirB", "dirC", "dirC/dirD", "dirE",
1696            ])
1697        else:
1698            _check(p.rglob("*/"), [
1699                "dirA", "dirA/linkC", "dirB", "dirB/linkD", "dirC",
1700                "dirC/dirD", "dirE", "linkB",
1701            ])
1702        _check(p.rglob(""), ["", "dirA", "dirB", "dirC", "dirE", "dirC/dirD"])
1703
1704        p = P(BASE, "dirC")
1705        _check(p.rglob("*"), ["dirC/fileC", "dirC/novel.txt",
1706                              "dirC/dirD", "dirC/dirD/fileD"])
1707        _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1708        _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1709        _check(p.rglob("*/"), ["dirC/dirD"])
1710        _check(p.rglob(""), ["dirC", "dirC/dirD"])
1711        # gh-91616, a re module regression
1712        _check(p.rglob("*.txt"), ["dirC/novel.txt"])
1713        _check(p.rglob("*.*"), ["dirC/novel.txt"])
1714
1715    @os_helper.skip_unless_symlink
1716    def test_rglob_symlink_loop(self):
1717        # Don't get fooled by symlink loops (Issue #26012).
1718        P = self.cls
1719        p = P(BASE)
1720        given = set(p.rglob('*'))
1721        expect = {'brokenLink',
1722                  'dirA', 'dirA/linkC',
1723                  'dirB', 'dirB/fileB', 'dirB/linkD',
1724                  'dirC', 'dirC/dirD', 'dirC/dirD/fileD',
1725                  'dirC/fileC', 'dirC/novel.txt',
1726                  'dirE',
1727                  'fileA',
1728                  'linkA',
1729                  'linkB',
1730                  'brokenLinkLoop',
1731                  }
1732        self.assertEqual(given, {p / x for x in expect})
1733
1734    def test_glob_many_open_files(self):
1735        depth = 30
1736        P = self.cls
1737        base = P(BASE) / 'deep'
1738        p = P(base, *(['d']*depth))
1739        p.mkdir(parents=True)
1740        pattern = '/'.join(['*'] * depth)
1741        iters = [base.glob(pattern) for j in range(100)]
1742        for it in iters:
1743            self.assertEqual(next(it), p)
1744        iters = [base.rglob('d') for j in range(100)]
1745        p = base
1746        for i in range(depth):
1747            p = p / 'd'
1748            for it in iters:
1749                self.assertEqual(next(it), p)
1750
1751    def test_glob_dotdot(self):
1752        # ".." is not special in globs.
1753        P = self.cls
1754        p = P(BASE)
1755        self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1756        self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1757        self.assertEqual(set(p.glob("../xyzzy")), set())
1758
1759    @os_helper.skip_unless_symlink
1760    def test_glob_permissions(self):
1761        # See bpo-38894
1762        P = self.cls
1763        base = P(BASE) / 'permissions'
1764        base.mkdir()
1765
1766        file1 = base / "file1"
1767        file1.touch()
1768        file2 = base / "file2"
1769        file2.touch()
1770
1771        subdir = base / "subdir"
1772
1773        file3 = base / "file3"
1774        file3.symlink_to(subdir / "other")
1775
1776        # Patching is needed to avoid relying on the filesystem
1777        # to return the order of the files as the error will not
1778        # happen if the symlink is the last item.
1779        real_scandir = os.scandir
1780        def my_scandir(path):
1781            with real_scandir(path) as scandir_it:
1782                entries = list(scandir_it)
1783            entries.sort(key=lambda entry: entry.name)
1784            return contextlib.nullcontext(entries)
1785
1786        with mock.patch("os.scandir", my_scandir):
1787            self.assertEqual(len(set(base.glob("*"))), 3)
1788            subdir.mkdir()
1789            self.assertEqual(len(set(base.glob("*"))), 4)
1790            subdir.chmod(000)
1791            self.assertEqual(len(set(base.glob("*"))), 4)
1792
1793    @os_helper.skip_unless_symlink
1794    def test_glob_long_symlink(self):
1795        # See gh-87695
1796        base = self.cls(BASE) / 'long_symlink'
1797        base.mkdir()
1798        bad_link = base / 'bad_link'
1799        bad_link.symlink_to("bad" * 200)
1800        self.assertEqual(sorted(base.glob('**/*')), [bad_link])
1801
1802    def _check_resolve(self, p, expected, strict=True):
1803        q = p.resolve(strict)
1804        self.assertEqual(q, expected)
1805
1806    # This can be used to check both relative and absolute resolutions.
1807    _check_resolve_relative = _check_resolve_absolute = _check_resolve
1808
1809    @os_helper.skip_unless_symlink
1810    def test_resolve_common(self):
1811        P = self.cls
1812        p = P(BASE, 'foo')
1813        with self.assertRaises(OSError) as cm:
1814            p.resolve(strict=True)
1815        self.assertEqual(cm.exception.errno, errno.ENOENT)
1816        # Non-strict
1817        self.assertEqualNormCase(str(p.resolve(strict=False)),
1818                                 os.path.join(BASE, 'foo'))
1819        p = P(BASE, 'foo', 'in', 'spam')
1820        self.assertEqualNormCase(str(p.resolve(strict=False)),
1821                                 os.path.join(BASE, 'foo', 'in', 'spam'))
1822        p = P(BASE, '..', 'foo', 'in', 'spam')
1823        self.assertEqualNormCase(str(p.resolve(strict=False)),
1824                                 os.path.abspath(os.path.join('foo', 'in', 'spam')))
1825        # These are all relative symlinks.
1826        p = P(BASE, 'dirB', 'fileB')
1827        self._check_resolve_relative(p, p)
1828        p = P(BASE, 'linkA')
1829        self._check_resolve_relative(p, P(BASE, 'fileA'))
1830        p = P(BASE, 'dirA', 'linkC', 'fileB')
1831        self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1832        p = P(BASE, 'dirB', 'linkD', 'fileB')
1833        self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1834        # Non-strict
1835        p = P(BASE, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam')
1836        self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB', 'foo', 'in',
1837                                          'spam'), False)
1838        p = P(BASE, 'dirA', 'linkC', '..', 'foo', 'in', 'spam')
1839        if os.name == 'nt':
1840            # In Windows, if linkY points to dirB, 'dirA\linkY\..'
1841            # resolves to 'dirA' without resolving linkY first.
1842            self._check_resolve_relative(p, P(BASE, 'dirA', 'foo', 'in',
1843                                              'spam'), False)
1844        else:
1845            # In Posix, if linkY points to dirB, 'dirA/linkY/..'
1846            # resolves to 'dirB/..' first before resolving to parent of dirB.
1847            self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
1848        # Now create absolute symlinks.
1849        d = os_helper._longpath(tempfile.mkdtemp(suffix='-dirD',
1850                                                 dir=os.getcwd()))
1851        self.addCleanup(os_helper.rmtree, d)
1852        os.symlink(os.path.join(d), join('dirA', 'linkX'))
1853        os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1854        p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1855        self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1856        # Non-strict
1857        p = P(BASE, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam')
1858        self._check_resolve_relative(p, P(BASE, 'dirB', 'foo', 'in', 'spam'),
1859                                     False)
1860        p = P(BASE, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam')
1861        if os.name == 'nt':
1862            # In Windows, if linkY points to dirB, 'dirA\linkY\..'
1863            # resolves to 'dirA' without resolving linkY first.
1864            self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False)
1865        else:
1866            # In Posix, if linkY points to dirB, 'dirA/linkY/..'
1867            # resolves to 'dirB/..' first before resolving to parent of dirB.
1868            self._check_resolve_relative(p, P(BASE, 'foo', 'in', 'spam'), False)
1869
1870    @os_helper.skip_unless_symlink
1871    def test_resolve_dot(self):
1872        # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks
1873        p = self.cls(BASE)
1874        self.dirlink('.', join('0'))
1875        self.dirlink(os.path.join('0', '0'), join('1'))
1876        self.dirlink(os.path.join('1', '1'), join('2'))
1877        q = p / '2'
1878        self.assertEqual(q.resolve(strict=True), p)
1879        r = q / '3' / '4'
1880        self.assertRaises(FileNotFoundError, r.resolve, strict=True)
1881        # Non-strict
1882        self.assertEqual(r.resolve(strict=False), p / '3' / '4')
1883
1884    def test_resolve_nonexist_relative_issue38671(self):
1885        p = self.cls('non', 'exist')
1886
1887        old_cwd = os.getcwd()
1888        os.chdir(BASE)
1889        try:
1890            self.assertEqual(p.resolve(), self.cls(BASE, p))
1891        finally:
1892            os.chdir(old_cwd)
1893
1894    def test_with(self):
1895        p = self.cls(BASE)
1896        it = p.iterdir()
1897        it2 = p.iterdir()
1898        next(it2)
1899        # bpo-46556: path context managers are deprecated in Python 3.11.
1900        with self.assertWarns(DeprecationWarning):
1901            with p:
1902                pass
1903        # Using a path as a context manager is a no-op, thus the following
1904        # operations should still succeed after the context manage exits.
1905        next(it)
1906        next(it2)
1907        p.exists()
1908        p.resolve()
1909        p.absolute()
1910        with self.assertWarns(DeprecationWarning):
1911            with p:
1912                pass
1913
1914    @os_helper.skip_unless_working_chmod
1915    def test_chmod(self):
1916        p = self.cls(BASE) / 'fileA'
1917        mode = p.stat().st_mode
1918        # Clear writable bit.
1919        new_mode = mode & ~0o222
1920        p.chmod(new_mode)
1921        self.assertEqual(p.stat().st_mode, new_mode)
1922        # Set writable bit.
1923        new_mode = mode | 0o222
1924        p.chmod(new_mode)
1925        self.assertEqual(p.stat().st_mode, new_mode)
1926
1927    # On Windows, os.chmod does not follow symlinks (issue #15411)
1928    @only_posix
1929    @os_helper.skip_unless_working_chmod
1930    def test_chmod_follow_symlinks_true(self):
1931        p = self.cls(BASE) / 'linkA'
1932        q = p.resolve()
1933        mode = q.stat().st_mode
1934        # Clear writable bit.
1935        new_mode = mode & ~0o222
1936        p.chmod(new_mode, follow_symlinks=True)
1937        self.assertEqual(q.stat().st_mode, new_mode)
1938        # Set writable bit
1939        new_mode = mode | 0o222
1940        p.chmod(new_mode, follow_symlinks=True)
1941        self.assertEqual(q.stat().st_mode, new_mode)
1942
1943    # XXX also need a test for lchmod.
1944
1945    @os_helper.skip_unless_working_chmod
1946    def test_stat(self):
1947        p = self.cls(BASE) / 'fileA'
1948        st = p.stat()
1949        self.assertEqual(p.stat(), st)
1950        # Change file mode by flipping write bit.
1951        p.chmod(st.st_mode ^ 0o222)
1952        self.addCleanup(p.chmod, st.st_mode)
1953        self.assertNotEqual(p.stat(), st)
1954
1955    @os_helper.skip_unless_symlink
1956    def test_stat_no_follow_symlinks(self):
1957        p = self.cls(BASE) / 'linkA'
1958        st = p.stat()
1959        self.assertNotEqual(st, p.stat(follow_symlinks=False))
1960
1961    def test_stat_no_follow_symlinks_nosymlink(self):
1962        p = self.cls(BASE) / 'fileA'
1963        st = p.stat()
1964        self.assertEqual(st, p.stat(follow_symlinks=False))
1965
1966    @os_helper.skip_unless_symlink
1967    def test_lstat(self):
1968        p = self.cls(BASE)/ 'linkA'
1969        st = p.stat()
1970        self.assertNotEqual(st, p.lstat())
1971
1972    def test_lstat_nosymlink(self):
1973        p = self.cls(BASE) / 'fileA'
1974        st = p.stat()
1975        self.assertEqual(st, p.lstat())
1976
1977    @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1978    def test_owner(self):
1979        p = self.cls(BASE) / 'fileA'
1980        uid = p.stat().st_uid
1981        try:
1982            name = pwd.getpwuid(uid).pw_name
1983        except KeyError:
1984            self.skipTest(
1985                "user %d doesn't have an entry in the system database" % uid)
1986        self.assertEqual(name, p.owner())
1987
1988    @unittest.skipUnless(grp, "the grp module is needed for this test")
1989    def test_group(self):
1990        p = self.cls(BASE) / 'fileA'
1991        gid = p.stat().st_gid
1992        try:
1993            name = grp.getgrgid(gid).gr_name
1994        except KeyError:
1995            self.skipTest(
1996                "group %d doesn't have an entry in the system database" % gid)
1997        self.assertEqual(name, p.group())
1998
1999    def test_unlink(self):
2000        p = self.cls(BASE) / 'fileA'
2001        p.unlink()
2002        self.assertFileNotFound(p.stat)
2003        self.assertFileNotFound(p.unlink)
2004
2005    def test_unlink_missing_ok(self):
2006        p = self.cls(BASE) / 'fileAAA'
2007        self.assertFileNotFound(p.unlink)
2008        p.unlink(missing_ok=True)
2009
2010    def test_rmdir(self):
2011        p = self.cls(BASE) / 'dirA'
2012        for q in p.iterdir():
2013            q.unlink()
2014        p.rmdir()
2015        self.assertFileNotFound(p.stat)
2016        self.assertFileNotFound(p.unlink)
2017
2018    @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present")
2019    def test_link_to(self):
2020        P = self.cls(BASE)
2021        p = P / 'fileA'
2022        size = p.stat().st_size
2023        # linking to another path.
2024        q = P / 'dirA' / 'fileAA'
2025        try:
2026            with self.assertWarns(DeprecationWarning):
2027                p.link_to(q)
2028        except PermissionError as e:
2029            self.skipTest('os.link(): %s' % e)
2030        self.assertEqual(q.stat().st_size, size)
2031        self.assertEqual(os.path.samefile(p, q), True)
2032        self.assertTrue(p.stat)
2033        # Linking to a str of a relative path.
2034        r = rel_join('fileAAA')
2035        with self.assertWarns(DeprecationWarning):
2036            q.link_to(r)
2037        self.assertEqual(os.stat(r).st_size, size)
2038        self.assertTrue(q.stat)
2039
2040    @unittest.skipUnless(hasattr(os, "link"), "os.link() is not present")
2041    def test_hardlink_to(self):
2042        P = self.cls(BASE)
2043        target = P / 'fileA'
2044        size = target.stat().st_size
2045        # linking to another path.
2046        link = P / 'dirA' / 'fileAA'
2047        link.hardlink_to(target)
2048        self.assertEqual(link.stat().st_size, size)
2049        self.assertTrue(os.path.samefile(target, link))
2050        self.assertTrue(target.exists())
2051        # Linking to a str of a relative path.
2052        link2 = P / 'dirA' / 'fileAAA'
2053        target2 = rel_join('fileA')
2054        link2.hardlink_to(target2)
2055        self.assertEqual(os.stat(target2).st_size, size)
2056        self.assertTrue(link2.exists())
2057
2058    @unittest.skipIf(hasattr(os, "link"), "os.link() is present")
2059    def test_link_to_not_implemented(self):
2060        P = self.cls(BASE)
2061        p = P / 'fileA'
2062        # linking to another path.
2063        q = P / 'dirA' / 'fileAA'
2064        with self.assertRaises(NotImplementedError):
2065            p.link_to(q)
2066
2067    def test_rename(self):
2068        P = self.cls(BASE)
2069        p = P / 'fileA'
2070        size = p.stat().st_size
2071        # Renaming to another path.
2072        q = P / 'dirA' / 'fileAA'
2073        renamed_p = p.rename(q)
2074        self.assertEqual(renamed_p, q)
2075        self.assertEqual(q.stat().st_size, size)
2076        self.assertFileNotFound(p.stat)
2077        # Renaming to a str of a relative path.
2078        r = rel_join('fileAAA')
2079        renamed_q = q.rename(r)
2080        self.assertEqual(renamed_q, self.cls(r))
2081        self.assertEqual(os.stat(r).st_size, size)
2082        self.assertFileNotFound(q.stat)
2083
2084    def test_replace(self):
2085        P = self.cls(BASE)
2086        p = P / 'fileA'
2087        size = p.stat().st_size
2088        # Replacing a non-existing path.
2089        q = P / 'dirA' / 'fileAA'
2090        replaced_p = p.replace(q)
2091        self.assertEqual(replaced_p, q)
2092        self.assertEqual(q.stat().st_size, size)
2093        self.assertFileNotFound(p.stat)
2094        # Replacing another (existing) path.
2095        r = rel_join('dirB', 'fileB')
2096        replaced_q = q.replace(r)
2097        self.assertEqual(replaced_q, self.cls(r))
2098        self.assertEqual(os.stat(r).st_size, size)
2099        self.assertFileNotFound(q.stat)
2100
2101    @os_helper.skip_unless_symlink
2102    def test_readlink(self):
2103        P = self.cls(BASE)
2104        self.assertEqual((P / 'linkA').readlink(), self.cls('fileA'))
2105        self.assertEqual((P / 'brokenLink').readlink(),
2106                         self.cls('non-existing'))
2107        self.assertEqual((P / 'linkB').readlink(), self.cls('dirB'))
2108        with self.assertRaises(OSError):
2109            (P / 'fileA').readlink()
2110
2111    def test_touch_common(self):
2112        P = self.cls(BASE)
2113        p = P / 'newfileA'
2114        self.assertFalse(p.exists())
2115        p.touch()
2116        self.assertTrue(p.exists())
2117        st = p.stat()
2118        old_mtime = st.st_mtime
2119        old_mtime_ns = st.st_mtime_ns
2120        # Rewind the mtime sufficiently far in the past to work around
2121        # filesystem-specific timestamp granularity.
2122        os.utime(str(p), (old_mtime - 10, old_mtime - 10))
2123        # The file mtime should be refreshed by calling touch() again.
2124        p.touch()
2125        st = p.stat()
2126        self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
2127        self.assertGreaterEqual(st.st_mtime, old_mtime)
2128        # Now with exist_ok=False.
2129        p = P / 'newfileB'
2130        self.assertFalse(p.exists())
2131        p.touch(mode=0o700, exist_ok=False)
2132        self.assertTrue(p.exists())
2133        self.assertRaises(OSError, p.touch, exist_ok=False)
2134
2135    def test_touch_nochange(self):
2136        P = self.cls(BASE)
2137        p = P / 'fileA'
2138        p.touch()
2139        with p.open('rb') as f:
2140            self.assertEqual(f.read().strip(), b"this is file A")
2141
2142    def test_mkdir(self):
2143        P = self.cls(BASE)
2144        p = P / 'newdirA'
2145        self.assertFalse(p.exists())
2146        p.mkdir()
2147        self.assertTrue(p.exists())
2148        self.assertTrue(p.is_dir())
2149        with self.assertRaises(OSError) as cm:
2150            p.mkdir()
2151        self.assertEqual(cm.exception.errno, errno.EEXIST)
2152
2153    def test_mkdir_parents(self):
2154        # Creating a chain of directories.
2155        p = self.cls(BASE, 'newdirB', 'newdirC')
2156        self.assertFalse(p.exists())
2157        with self.assertRaises(OSError) as cm:
2158            p.mkdir()
2159        self.assertEqual(cm.exception.errno, errno.ENOENT)
2160        p.mkdir(parents=True)
2161        self.assertTrue(p.exists())
2162        self.assertTrue(p.is_dir())
2163        with self.assertRaises(OSError) as cm:
2164            p.mkdir(parents=True)
2165        self.assertEqual(cm.exception.errno, errno.EEXIST)
2166        # Test `mode` arg.
2167        mode = stat.S_IMODE(p.stat().st_mode)  # Default mode.
2168        p = self.cls(BASE, 'newdirD', 'newdirE')
2169        p.mkdir(0o555, parents=True)
2170        self.assertTrue(p.exists())
2171        self.assertTrue(p.is_dir())
2172        if os.name != 'nt':
2173            # The directory's permissions follow the mode argument.
2174            self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode)
2175        # The parent's permissions follow the default process settings.
2176        self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode)
2177
2178    def test_mkdir_exist_ok(self):
2179        p = self.cls(BASE, 'dirB')
2180        st_ctime_first = p.stat().st_ctime
2181        self.assertTrue(p.exists())
2182        self.assertTrue(p.is_dir())
2183        with self.assertRaises(FileExistsError) as cm:
2184            p.mkdir()
2185        self.assertEqual(cm.exception.errno, errno.EEXIST)
2186        p.mkdir(exist_ok=True)
2187        self.assertTrue(p.exists())
2188        self.assertEqual(p.stat().st_ctime, st_ctime_first)
2189
2190    def test_mkdir_exist_ok_with_parent(self):
2191        p = self.cls(BASE, 'dirC')
2192        self.assertTrue(p.exists())
2193        with self.assertRaises(FileExistsError) as cm:
2194            p.mkdir()
2195        self.assertEqual(cm.exception.errno, errno.EEXIST)
2196        p = p / 'newdirC'
2197        p.mkdir(parents=True)
2198        st_ctime_first = p.stat().st_ctime
2199        self.assertTrue(p.exists())
2200        with self.assertRaises(FileExistsError) as cm:
2201            p.mkdir(parents=True)
2202        self.assertEqual(cm.exception.errno, errno.EEXIST)
2203        p.mkdir(parents=True, exist_ok=True)
2204        self.assertTrue(p.exists())
2205        self.assertEqual(p.stat().st_ctime, st_ctime_first)
2206
2207    @unittest.skipIf(is_emscripten, "FS root cannot be modified on Emscripten.")
2208    def test_mkdir_exist_ok_root(self):
2209        # Issue #25803: A drive root could raise PermissionError on Windows.
2210        self.cls('/').resolve().mkdir(exist_ok=True)
2211        self.cls('/').resolve().mkdir(parents=True, exist_ok=True)
2212
2213    @only_nt  # XXX: not sure how to test this on POSIX.
2214    def test_mkdir_with_unknown_drive(self):
2215        for d in 'ZYXWVUTSRQPONMLKJIHGFEDCBA':
2216            p = self.cls(d + ':\\')
2217            if not p.is_dir():
2218                break
2219        else:
2220            self.skipTest("cannot find a drive that doesn't exist")
2221        with self.assertRaises(OSError):
2222            (p / 'child' / 'path').mkdir(parents=True)
2223
2224    def test_mkdir_with_child_file(self):
2225        p = self.cls(BASE, 'dirB', 'fileB')
2226        self.assertTrue(p.exists())
2227        # An exception is raised when the last path component is an existing
2228        # regular file, regardless of whether exist_ok is true or not.
2229        with self.assertRaises(FileExistsError) as cm:
2230            p.mkdir(parents=True)
2231        self.assertEqual(cm.exception.errno, errno.EEXIST)
2232        with self.assertRaises(FileExistsError) as cm:
2233            p.mkdir(parents=True, exist_ok=True)
2234        self.assertEqual(cm.exception.errno, errno.EEXIST)
2235
2236    def test_mkdir_no_parents_file(self):
2237        p = self.cls(BASE, 'fileA')
2238        self.assertTrue(p.exists())
2239        # An exception is raised when the last path component is an existing
2240        # regular file, regardless of whether exist_ok is true or not.
2241        with self.assertRaises(FileExistsError) as cm:
2242            p.mkdir()
2243        self.assertEqual(cm.exception.errno, errno.EEXIST)
2244        with self.assertRaises(FileExistsError) as cm:
2245            p.mkdir(exist_ok=True)
2246        self.assertEqual(cm.exception.errno, errno.EEXIST)
2247
2248    def test_mkdir_concurrent_parent_creation(self):
2249        for pattern_num in range(32):
2250            p = self.cls(BASE, 'dirCPC%d' % pattern_num)
2251            self.assertFalse(p.exists())
2252
2253            real_mkdir = os.mkdir
2254            def my_mkdir(path, mode=0o777):
2255                path = str(path)
2256                # Emulate another process that would create the directory
2257                # just before we try to create it ourselves.  We do it
2258                # in all possible pattern combinations, assuming that this
2259                # function is called at most 5 times (dirCPC/dir1/dir2,
2260                # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2).
2261                if pattern.pop():
2262                    real_mkdir(path, mode)  # From another process.
2263                    concurrently_created.add(path)
2264                real_mkdir(path, mode)  # Our real call.
2265
2266            pattern = [bool(pattern_num & (1 << n)) for n in range(5)]
2267            concurrently_created = set()
2268            p12 = p / 'dir1' / 'dir2'
2269            try:
2270                with mock.patch("os.mkdir", my_mkdir):
2271                    p12.mkdir(parents=True, exist_ok=False)
2272            except FileExistsError:
2273                self.assertIn(str(p12), concurrently_created)
2274            else:
2275                self.assertNotIn(str(p12), concurrently_created)
2276            self.assertTrue(p.exists())
2277
2278    @os_helper.skip_unless_symlink
2279    def test_symlink_to(self):
2280        P = self.cls(BASE)
2281        target = P / 'fileA'
2282        # Symlinking a path target.
2283        link = P / 'dirA' / 'linkAA'
2284        link.symlink_to(target)
2285        self.assertEqual(link.stat(), target.stat())
2286        self.assertNotEqual(link.lstat(), target.stat())
2287        # Symlinking a str target.
2288        link = P / 'dirA' / 'linkAAA'
2289        link.symlink_to(str(target))
2290        self.assertEqual(link.stat(), target.stat())
2291        self.assertNotEqual(link.lstat(), target.stat())
2292        self.assertFalse(link.is_dir())
2293        # Symlinking to a directory.
2294        target = P / 'dirB'
2295        link = P / 'dirA' / 'linkAAAA'
2296        link.symlink_to(target, target_is_directory=True)
2297        self.assertEqual(link.stat(), target.stat())
2298        self.assertNotEqual(link.lstat(), target.stat())
2299        self.assertTrue(link.is_dir())
2300        self.assertTrue(list(link.iterdir()))
2301
2302    def test_is_dir(self):
2303        P = self.cls(BASE)
2304        self.assertTrue((P / 'dirA').is_dir())
2305        self.assertFalse((P / 'fileA').is_dir())
2306        self.assertFalse((P / 'non-existing').is_dir())
2307        self.assertFalse((P / 'fileA' / 'bah').is_dir())
2308        if os_helper.can_symlink():
2309            self.assertFalse((P / 'linkA').is_dir())
2310            self.assertTrue((P / 'linkB').is_dir())
2311            self.assertFalse((P/ 'brokenLink').is_dir(), False)
2312        self.assertIs((P / 'dirA\udfff').is_dir(), False)
2313        self.assertIs((P / 'dirA\x00').is_dir(), False)
2314
2315    def test_is_file(self):
2316        P = self.cls(BASE)
2317        self.assertTrue((P / 'fileA').is_file())
2318        self.assertFalse((P / 'dirA').is_file())
2319        self.assertFalse((P / 'non-existing').is_file())
2320        self.assertFalse((P / 'fileA' / 'bah').is_file())
2321        if os_helper.can_symlink():
2322            self.assertTrue((P / 'linkA').is_file())
2323            self.assertFalse((P / 'linkB').is_file())
2324            self.assertFalse((P/ 'brokenLink').is_file())
2325        self.assertIs((P / 'fileA\udfff').is_file(), False)
2326        self.assertIs((P / 'fileA\x00').is_file(), False)
2327
2328    @only_posix
2329    def test_is_mount(self):
2330        P = self.cls(BASE)
2331        R = self.cls('/')  # TODO: Work out Windows.
2332        self.assertFalse((P / 'fileA').is_mount())
2333        self.assertFalse((P / 'dirA').is_mount())
2334        self.assertFalse((P / 'non-existing').is_mount())
2335        self.assertFalse((P / 'fileA' / 'bah').is_mount())
2336        self.assertTrue(R.is_mount())
2337        if os_helper.can_symlink():
2338            self.assertFalse((P / 'linkA').is_mount())
2339        self.assertIs(self.cls('/\udfff').is_mount(), False)
2340        self.assertIs(self.cls('/\x00').is_mount(), False)
2341
2342    def test_is_symlink(self):
2343        P = self.cls(BASE)
2344        self.assertFalse((P / 'fileA').is_symlink())
2345        self.assertFalse((P / 'dirA').is_symlink())
2346        self.assertFalse((P / 'non-existing').is_symlink())
2347        self.assertFalse((P / 'fileA' / 'bah').is_symlink())
2348        if os_helper.can_symlink():
2349            self.assertTrue((P / 'linkA').is_symlink())
2350            self.assertTrue((P / 'linkB').is_symlink())
2351            self.assertTrue((P/ 'brokenLink').is_symlink())
2352        self.assertIs((P / 'fileA\udfff').is_file(), False)
2353        self.assertIs((P / 'fileA\x00').is_file(), False)
2354        if os_helper.can_symlink():
2355            self.assertIs((P / 'linkA\udfff').is_file(), False)
2356            self.assertIs((P / 'linkA\x00').is_file(), False)
2357
2358    def test_is_fifo_false(self):
2359        P = self.cls(BASE)
2360        self.assertFalse((P / 'fileA').is_fifo())
2361        self.assertFalse((P / 'dirA').is_fifo())
2362        self.assertFalse((P / 'non-existing').is_fifo())
2363        self.assertFalse((P / 'fileA' / 'bah').is_fifo())
2364        self.assertIs((P / 'fileA\udfff').is_fifo(), False)
2365        self.assertIs((P / 'fileA\x00').is_fifo(), False)
2366
2367    @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
2368    @unittest.skipIf(sys.platform == "vxworks",
2369                    "fifo requires special path on VxWorks")
2370    def test_is_fifo_true(self):
2371        P = self.cls(BASE, 'myfifo')
2372        try:
2373            os.mkfifo(str(P))
2374        except PermissionError as e:
2375            self.skipTest('os.mkfifo(): %s' % e)
2376        self.assertTrue(P.is_fifo())
2377        self.assertFalse(P.is_socket())
2378        self.assertFalse(P.is_file())
2379        self.assertIs(self.cls(BASE, 'myfifo\udfff').is_fifo(), False)
2380        self.assertIs(self.cls(BASE, 'myfifo\x00').is_fifo(), False)
2381
2382    def test_is_socket_false(self):
2383        P = self.cls(BASE)
2384        self.assertFalse((P / 'fileA').is_socket())
2385        self.assertFalse((P / 'dirA').is_socket())
2386        self.assertFalse((P / 'non-existing').is_socket())
2387        self.assertFalse((P / 'fileA' / 'bah').is_socket())
2388        self.assertIs((P / 'fileA\udfff').is_socket(), False)
2389        self.assertIs((P / 'fileA\x00').is_socket(), False)
2390
2391    @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
2392    @unittest.skipIf(
2393        is_emscripten, "Unix sockets are not implemented on Emscripten."
2394    )
2395    @unittest.skipIf(
2396        is_wasi, "Cannot create socket on WASI."
2397    )
2398    def test_is_socket_true(self):
2399        P = self.cls(BASE, 'mysock')
2400        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
2401        self.addCleanup(sock.close)
2402        try:
2403            sock.bind(str(P))
2404        except OSError as e:
2405            if (isinstance(e, PermissionError) or
2406                    "AF_UNIX path too long" in str(e)):
2407                self.skipTest("cannot bind Unix socket: " + str(e))
2408        self.assertTrue(P.is_socket())
2409        self.assertFalse(P.is_fifo())
2410        self.assertFalse(P.is_file())
2411        self.assertIs(self.cls(BASE, 'mysock\udfff').is_socket(), False)
2412        self.assertIs(self.cls(BASE, 'mysock\x00').is_socket(), False)
2413
2414    def test_is_block_device_false(self):
2415        P = self.cls(BASE)
2416        self.assertFalse((P / 'fileA').is_block_device())
2417        self.assertFalse((P / 'dirA').is_block_device())
2418        self.assertFalse((P / 'non-existing').is_block_device())
2419        self.assertFalse((P / 'fileA' / 'bah').is_block_device())
2420        self.assertIs((P / 'fileA\udfff').is_block_device(), False)
2421        self.assertIs((P / 'fileA\x00').is_block_device(), False)
2422
2423    def test_is_char_device_false(self):
2424        P = self.cls(BASE)
2425        self.assertFalse((P / 'fileA').is_char_device())
2426        self.assertFalse((P / 'dirA').is_char_device())
2427        self.assertFalse((P / 'non-existing').is_char_device())
2428        self.assertFalse((P / 'fileA' / 'bah').is_char_device())
2429        self.assertIs((P / 'fileA\udfff').is_char_device(), False)
2430        self.assertIs((P / 'fileA\x00').is_char_device(), False)
2431
2432    def test_is_char_device_true(self):
2433        # Under Unix, /dev/null should generally be a char device.
2434        P = self.cls('/dev/null')
2435        if not P.exists():
2436            self.skipTest("/dev/null required")
2437        self.assertTrue(P.is_char_device())
2438        self.assertFalse(P.is_block_device())
2439        self.assertFalse(P.is_file())
2440        self.assertIs(self.cls('/dev/null\udfff').is_char_device(), False)
2441        self.assertIs(self.cls('/dev/null\x00').is_char_device(), False)
2442
2443    def test_pickling_common(self):
2444        p = self.cls(BASE, 'fileA')
2445        for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
2446            dumped = pickle.dumps(p, proto)
2447            pp = pickle.loads(dumped)
2448            self.assertEqual(pp.stat(), p.stat())
2449
2450    def test_parts_interning(self):
2451        P = self.cls
2452        p = P('/usr/bin/foo')
2453        q = P('/usr/local/bin')
2454        # 'usr'
2455        self.assertIs(p.parts[1], q.parts[1])
2456        # 'bin'
2457        self.assertIs(p.parts[2], q.parts[3])
2458
2459    def _check_complex_symlinks(self, link0_target):
2460        # Test solving a non-looping chain of symlinks (issue #19887).
2461        P = self.cls(BASE)
2462        self.dirlink(os.path.join('link0', 'link0'), join('link1'))
2463        self.dirlink(os.path.join('link1', 'link1'), join('link2'))
2464        self.dirlink(os.path.join('link2', 'link2'), join('link3'))
2465        self.dirlink(link0_target, join('link0'))
2466
2467        # Resolve absolute paths.
2468        p = (P / 'link0').resolve()
2469        self.assertEqual(p, P)
2470        self.assertEqualNormCase(str(p), BASE)
2471        p = (P / 'link1').resolve()
2472        self.assertEqual(p, P)
2473        self.assertEqualNormCase(str(p), BASE)
2474        p = (P / 'link2').resolve()
2475        self.assertEqual(p, P)
2476        self.assertEqualNormCase(str(p), BASE)
2477        p = (P / 'link3').resolve()
2478        self.assertEqual(p, P)
2479        self.assertEqualNormCase(str(p), BASE)
2480
2481        # Resolve relative paths.
2482        old_path = os.getcwd()
2483        os.chdir(BASE)
2484        try:
2485            p = self.cls('link0').resolve()
2486            self.assertEqual(p, P)
2487            self.assertEqualNormCase(str(p), BASE)
2488            p = self.cls('link1').resolve()
2489            self.assertEqual(p, P)
2490            self.assertEqualNormCase(str(p), BASE)
2491            p = self.cls('link2').resolve()
2492            self.assertEqual(p, P)
2493            self.assertEqualNormCase(str(p), BASE)
2494            p = self.cls('link3').resolve()
2495            self.assertEqual(p, P)
2496            self.assertEqualNormCase(str(p), BASE)
2497        finally:
2498            os.chdir(old_path)
2499
2500    @os_helper.skip_unless_symlink
2501    def test_complex_symlinks_absolute(self):
2502        self._check_complex_symlinks(BASE)
2503
2504    @os_helper.skip_unless_symlink
2505    def test_complex_symlinks_relative(self):
2506        self._check_complex_symlinks('.')
2507
2508    @os_helper.skip_unless_symlink
2509    def test_complex_symlinks_relative_dot_dot(self):
2510        self._check_complex_symlinks(os.path.join('dirA', '..'))
2511
2512
2513class PathTest(_BasePathTest, unittest.TestCase):
2514    cls = pathlib.Path
2515
2516    def test_concrete_class(self):
2517        p = self.cls('a')
2518        self.assertIs(type(p),
2519            pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
2520
2521    def test_unsupported_flavour(self):
2522        if os.name == 'nt':
2523            self.assertRaises(NotImplementedError, pathlib.PosixPath)
2524        else:
2525            self.assertRaises(NotImplementedError, pathlib.WindowsPath)
2526
2527    def test_glob_empty_pattern(self):
2528        p = self.cls()
2529        with self.assertRaisesRegex(ValueError, 'Unacceptable pattern'):
2530            list(p.glob(''))
2531
2532
2533@only_posix
2534class PosixPathTest(_BasePathTest, unittest.TestCase):
2535    cls = pathlib.PosixPath
2536
2537    def test_absolute(self):
2538        P = self.cls
2539        self.assertEqual(str(P('/').absolute()), '/')
2540        self.assertEqual(str(P('/a').absolute()), '/a')
2541        self.assertEqual(str(P('/a/b').absolute()), '/a/b')
2542
2543        # '//'-prefixed absolute path (supported by POSIX).
2544        self.assertEqual(str(P('//').absolute()), '//')
2545        self.assertEqual(str(P('//a').absolute()), '//a')
2546        self.assertEqual(str(P('//a/b').absolute()), '//a/b')
2547
2548    def _check_symlink_loop(self, *args, strict=True):
2549        path = self.cls(*args)
2550        with self.assertRaises(RuntimeError):
2551            print(path.resolve(strict))
2552
2553    @unittest.skipIf(
2554        is_emscripten or is_wasi,
2555        "umask is not implemented on Emscripten/WASI."
2556    )
2557    def test_open_mode(self):
2558        old_mask = os.umask(0)
2559        self.addCleanup(os.umask, old_mask)
2560        p = self.cls(BASE)
2561        with (p / 'new_file').open('wb'):
2562            pass
2563        st = os.stat(join('new_file'))
2564        self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
2565        os.umask(0o022)
2566        with (p / 'other_new_file').open('wb'):
2567            pass
2568        st = os.stat(join('other_new_file'))
2569        self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
2570
2571    def test_resolve_root(self):
2572        current_directory = os.getcwd()
2573        try:
2574            os.chdir('/')
2575            p = self.cls('spam')
2576            self.assertEqual(str(p.resolve()), '/spam')
2577        finally:
2578            os.chdir(current_directory)
2579
2580    @unittest.skipIf(
2581        is_emscripten or is_wasi,
2582        "umask is not implemented on Emscripten/WASI."
2583    )
2584    def test_touch_mode(self):
2585        old_mask = os.umask(0)
2586        self.addCleanup(os.umask, old_mask)
2587        p = self.cls(BASE)
2588        (p / 'new_file').touch()
2589        st = os.stat(join('new_file'))
2590        self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
2591        os.umask(0o022)
2592        (p / 'other_new_file').touch()
2593        st = os.stat(join('other_new_file'))
2594        self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
2595        (p / 'masked_new_file').touch(mode=0o750)
2596        st = os.stat(join('masked_new_file'))
2597        self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
2598
2599    @os_helper.skip_unless_symlink
2600    def test_resolve_loop(self):
2601        # Loops with relative symlinks.
2602        os.symlink('linkX/inside', join('linkX'))
2603        self._check_symlink_loop(BASE, 'linkX')
2604        os.symlink('linkY', join('linkY'))
2605        self._check_symlink_loop(BASE, 'linkY')
2606        os.symlink('linkZ/../linkZ', join('linkZ'))
2607        self._check_symlink_loop(BASE, 'linkZ')
2608        # Non-strict
2609        self._check_symlink_loop(BASE, 'linkZ', 'foo', strict=False)
2610        # Loops with absolute symlinks.
2611        os.symlink(join('linkU/inside'), join('linkU'))
2612        self._check_symlink_loop(BASE, 'linkU')
2613        os.symlink(join('linkV'), join('linkV'))
2614        self._check_symlink_loop(BASE, 'linkV')
2615        os.symlink(join('linkW/../linkW'), join('linkW'))
2616        self._check_symlink_loop(BASE, 'linkW')
2617        # Non-strict
2618        self._check_symlink_loop(BASE, 'linkW', 'foo', strict=False)
2619
2620    def test_glob(self):
2621        P = self.cls
2622        p = P(BASE)
2623        given = set(p.glob("FILEa"))
2624        expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given
2625        self.assertEqual(given, expect)
2626        self.assertEqual(set(p.glob("FILEa*")), set())
2627
2628    def test_rglob(self):
2629        P = self.cls
2630        p = P(BASE, "dirC")
2631        given = set(p.rglob("FILEd"))
2632        expect = set() if not os_helper.fs_is_case_insensitive(BASE) else given
2633        self.assertEqual(given, expect)
2634        self.assertEqual(set(p.rglob("FILEd*")), set())
2635
2636    @unittest.skipUnless(hasattr(pwd, 'getpwall'),
2637                         'pwd module does not expose getpwall()')
2638    @unittest.skipIf(sys.platform == "vxworks",
2639                     "no home directory on VxWorks")
2640    def test_expanduser(self):
2641        P = self.cls
2642        import_helper.import_module('pwd')
2643        import pwd
2644        pwdent = pwd.getpwuid(os.getuid())
2645        username = pwdent.pw_name
2646        userhome = pwdent.pw_dir.rstrip('/') or '/'
2647        # Find arbitrary different user (if exists).
2648        for pwdent in pwd.getpwall():
2649            othername = pwdent.pw_name
2650            otherhome = pwdent.pw_dir.rstrip('/')
2651            if othername != username and otherhome:
2652                break
2653        else:
2654            othername = username
2655            otherhome = userhome
2656
2657        fakename = 'fakeuser'
2658        # This user can theoretically exist on a test runner. Create unique name:
2659        try:
2660            while pwd.getpwnam(fakename):
2661                fakename += '1'
2662        except KeyError:
2663            pass  # Non-existent name found
2664
2665        p1 = P('~/Documents')
2666        p2 = P(f'~{username}/Documents')
2667        p3 = P(f'~{othername}/Documents')
2668        p4 = P(f'../~{username}/Documents')
2669        p5 = P(f'/~{username}/Documents')
2670        p6 = P('')
2671        p7 = P(f'~{fakename}/Documents')
2672
2673        with os_helper.EnvironmentVarGuard() as env:
2674            env.pop('HOME', None)
2675
2676            self.assertEqual(p1.expanduser(), P(userhome) / 'Documents')
2677            self.assertEqual(p2.expanduser(), P(userhome) / 'Documents')
2678            self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents')
2679            self.assertEqual(p4.expanduser(), p4)
2680            self.assertEqual(p5.expanduser(), p5)
2681            self.assertEqual(p6.expanduser(), p6)
2682            self.assertRaises(RuntimeError, p7.expanduser)
2683
2684            env['HOME'] = '/tmp'
2685            self.assertEqual(p1.expanduser(), P('/tmp/Documents'))
2686            self.assertEqual(p2.expanduser(), P(userhome) / 'Documents')
2687            self.assertEqual(p3.expanduser(), P(otherhome) / 'Documents')
2688            self.assertEqual(p4.expanduser(), p4)
2689            self.assertEqual(p5.expanduser(), p5)
2690            self.assertEqual(p6.expanduser(), p6)
2691            self.assertRaises(RuntimeError, p7.expanduser)
2692
2693    @unittest.skipIf(sys.platform != "darwin",
2694                     "Bad file descriptor in /dev/fd affects only macOS")
2695    def test_handling_bad_descriptor(self):
2696        try:
2697            file_descriptors = list(pathlib.Path('/dev/fd').rglob("*"))[3:]
2698            if not file_descriptors:
2699                self.skipTest("no file descriptors - issue was not reproduced")
2700            # Checking all file descriptors because there is no guarantee
2701            # which one will fail.
2702            for f in file_descriptors:
2703                f.exists()
2704                f.is_dir()
2705                f.is_file()
2706                f.is_symlink()
2707                f.is_block_device()
2708                f.is_char_device()
2709                f.is_fifo()
2710                f.is_socket()
2711        except OSError as e:
2712            if e.errno == errno.EBADF:
2713                self.fail("Bad file descriptor not handled.")
2714            raise
2715
2716
2717@only_nt
2718class WindowsPathTest(_BasePathTest, unittest.TestCase):
2719    cls = pathlib.WindowsPath
2720
2721    def test_absolute(self):
2722        P = self.cls
2723
2724        # Simple absolute paths.
2725        self.assertEqual(str(P('c:\\').absolute()), 'c:\\')
2726        self.assertEqual(str(P('c:\\a').absolute()), 'c:\\a')
2727        self.assertEqual(str(P('c:\\a\\b').absolute()), 'c:\\a\\b')
2728
2729        # UNC absolute paths.
2730        share = '\\\\server\\share\\'
2731        self.assertEqual(str(P(share).absolute()), share)
2732        self.assertEqual(str(P(share + 'a').absolute()), share + 'a')
2733        self.assertEqual(str(P(share + 'a\\b').absolute()), share + 'a\\b')
2734
2735        # UNC relative paths.
2736        with mock.patch("os.getcwd") as getcwd:
2737            getcwd.return_value = share
2738
2739            self.assertEqual(str(P().absolute()), share)
2740            self.assertEqual(str(P('.').absolute()), share)
2741            self.assertEqual(str(P('a').absolute()), os.path.join(share, 'a'))
2742            self.assertEqual(str(P('a', 'b', 'c').absolute()),
2743                             os.path.join(share, 'a', 'b', 'c'))
2744
2745
2746    def test_glob(self):
2747        P = self.cls
2748        p = P(BASE)
2749        self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
2750        self.assertEqual(set(p.glob("*a\\")), { P(BASE, "dirA") })
2751        self.assertEqual(set(p.glob("F*a")), { P(BASE, "fileA") })
2752        self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\FILEa"})
2753        self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"})
2754
2755    def test_rglob(self):
2756        P = self.cls
2757        p = P(BASE, "dirC")
2758        self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
2759        self.assertEqual(set(p.rglob("*\\")), { P(BASE, "dirC/dirD") })
2760        self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\FILEd"})
2761
2762    def test_expanduser(self):
2763        P = self.cls
2764        with os_helper.EnvironmentVarGuard() as env:
2765            env.pop('HOME', None)
2766            env.pop('USERPROFILE', None)
2767            env.pop('HOMEPATH', None)
2768            env.pop('HOMEDRIVE', None)
2769            env['USERNAME'] = 'alice'
2770
2771            # test that the path returns unchanged
2772            p1 = P('~/My Documents')
2773            p2 = P('~alice/My Documents')
2774            p3 = P('~bob/My Documents')
2775            p4 = P('/~/My Documents')
2776            p5 = P('d:~/My Documents')
2777            p6 = P('')
2778            self.assertRaises(RuntimeError, p1.expanduser)
2779            self.assertRaises(RuntimeError, p2.expanduser)
2780            self.assertRaises(RuntimeError, p3.expanduser)
2781            self.assertEqual(p4.expanduser(), p4)
2782            self.assertEqual(p5.expanduser(), p5)
2783            self.assertEqual(p6.expanduser(), p6)
2784
2785            def check():
2786                env.pop('USERNAME', None)
2787                self.assertEqual(p1.expanduser(),
2788                                 P('C:/Users/alice/My Documents'))
2789                self.assertRaises(RuntimeError, p2.expanduser)
2790                env['USERNAME'] = 'alice'
2791                self.assertEqual(p2.expanduser(),
2792                                 P('C:/Users/alice/My Documents'))
2793                self.assertEqual(p3.expanduser(),
2794                                 P('C:/Users/bob/My Documents'))
2795                self.assertEqual(p4.expanduser(), p4)
2796                self.assertEqual(p5.expanduser(), p5)
2797                self.assertEqual(p6.expanduser(), p6)
2798
2799            env['HOMEPATH'] = 'C:\\Users\\alice'
2800            check()
2801
2802            env['HOMEDRIVE'] = 'C:\\'
2803            env['HOMEPATH'] = 'Users\\alice'
2804            check()
2805
2806            env.pop('HOMEDRIVE', None)
2807            env.pop('HOMEPATH', None)
2808            env['USERPROFILE'] = 'C:\\Users\\alice'
2809            check()
2810
2811            # bpo-38883: ignore `HOME` when set on windows
2812            env['HOME'] = 'C:\\Users\\eve'
2813            check()
2814
2815
2816class CompatiblePathTest(unittest.TestCase):
2817    """
2818    Test that a type can be made compatible with PurePath
2819    derivatives by implementing division operator overloads.
2820    """
2821
2822    class CompatPath:
2823        """
2824        Minimum viable class to test PurePath compatibility.
2825        Simply uses the division operator to join a given
2826        string and the string value of another object with
2827        a forward slash.
2828        """
2829        def __init__(self, string):
2830            self.string = string
2831
2832        def __truediv__(self, other):
2833            return type(self)(f"{self.string}/{other}")
2834
2835        def __rtruediv__(self, other):
2836            return type(self)(f"{other}/{self.string}")
2837
2838    def test_truediv(self):
2839        result = pathlib.PurePath("test") / self.CompatPath("right")
2840        self.assertIsInstance(result, self.CompatPath)
2841        self.assertEqual(result.string, "test/right")
2842
2843        with self.assertRaises(TypeError):
2844            # Verify improper operations still raise a TypeError
2845            pathlib.PurePath("test") / 10
2846
2847    def test_rtruediv(self):
2848        result = self.CompatPath("left") / pathlib.PurePath("test")
2849        self.assertIsInstance(result, self.CompatPath)
2850        self.assertEqual(result.string, "left/test")
2851
2852        with self.assertRaises(TypeError):
2853            # Verify improper operations still raise a TypeError
2854            10 / pathlib.PurePath("test")
2855
2856
2857if __name__ == "__main__":
2858    unittest.main()
2859