|
8 | 8 | import stat |
9 | 9 | import tempfile |
10 | 10 | import unittest |
| 11 | +from unittest import mock |
11 | 12 |
|
12 | 13 | from test import support |
13 | 14 | android_not_root = support.android_not_root |
@@ -1816,6 +1817,35 @@ def test_mkdir_no_parents_file(self): |
1816 | 1817 | p.mkdir(exist_ok=True) |
1817 | 1818 | self.assertEqual(cm.exception.errno, errno.EEXIST) |
1818 | 1819 |
|
| 1820 | + def test_mkdir_concurrent_parent_creation(self): |
| 1821 | + for pattern_num in range(32): |
| 1822 | + p = self.cls(BASE, 'dirCPC%d' % pattern_num) |
| 1823 | + self.assertFalse(p.exists()) |
| 1824 | + |
| 1825 | + def my_mkdir(path, mode=0o777): |
| 1826 | + path = str(path) |
| 1827 | + # Emulate another process that would create the directory |
| 1828 | + # just before we try to create it ourselves. We do it |
| 1829 | + # in all possible pattern combinations, assuming that this |
| 1830 | + # function is called at most 5 times (dirCPC/dir1/dir2, |
| 1831 | + # dirCPC/dir1, dirCPC, dirCPC/dir1, dirCPC/dir1/dir2). |
| 1832 | + if pattern.pop(): |
| 1833 | + os.mkdir(path, mode) # from another process |
| 1834 | + concurrently_created.add(path) |
| 1835 | + os.mkdir(path, mode) # our real call |
| 1836 | + |
| 1837 | + pattern = [bool(pattern_num & (1 << n)) for n in range(5)] |
| 1838 | + concurrently_created = set() |
| 1839 | + p12 = p / 'dir1' / 'dir2' |
| 1840 | + try: |
| 1841 | + with mock.patch("pathlib._normal_accessor.mkdir", my_mkdir): |
| 1842 | + p12.mkdir(parents=True, exist_ok=False) |
| 1843 | + except FileExistsError: |
| 1844 | + self.assertIn(str(p12), concurrently_created) |
| 1845 | + else: |
| 1846 | + self.assertNotIn(str(p12), concurrently_created) |
| 1847 | + self.assertTrue(p.exists()) |
| 1848 | + |
1819 | 1849 | @with_symlinks |
1820 | 1850 | def test_symlink_to(self): |
1821 | 1851 | P = self.cls(BASE) |
|
0 commit comments