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