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