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