PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/python/pants_test/util/test_dirutil.py

https://gitlab.com/Ivy001/pants
Python | 335 lines | 321 code | 10 blank | 4 comment | 4 complexity | 770bb132a40e9fa6224ec02773d31a1b MD5 | raw file
  1. # coding=utf-8
  2. # Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
  3. # Licensed under the Apache License, Version 2.0 (see LICENSE).
  4. from __future__ import (absolute_import, division, generators, nested_scopes, print_function,
  5. unicode_literals, with_statement)
  6. import errno
  7. import os
  8. import time
  9. import unittest
  10. import mock
  11. import six
  12. from pants.util import dirutil
  13. from pants.util.contextutil import pushd, temporary_dir
  14. from pants.util.dirutil import (_mkdtemp_unregister_cleaner, absolute_symlink, fast_relpath,
  15. get_basedir, read_file, relative_symlink, relativize_paths, rm_rf,
  16. safe_concurrent_creation, safe_file_dump, safe_mkdir, safe_mkdtemp,
  17. safe_rm_oldest_items_in_dir, safe_rmtree, touch)
  18. def strict_patch(target, **kwargs):
  19. return mock.patch(target, autospec=True, spec_set=True, **kwargs)
  20. class DirutilTest(unittest.TestCase):
  21. def setUp(self):
  22. # Ensure we start in a clean state.
  23. _mkdtemp_unregister_cleaner()
  24. def test_fast_relpath(self):
  25. def assertRelpath(expected, path, start):
  26. self.assertEquals(expected, fast_relpath(path, start))
  27. assertRelpath('c', '/a/b/c', '/a/b')
  28. assertRelpath('c', '/a/b/c', '/a/b/')
  29. assertRelpath('c', 'b/c', 'b')
  30. assertRelpath('c', 'b/c', 'b/')
  31. assertRelpath('c/', 'b/c/', 'b')
  32. assertRelpath('c/', 'b/c/', 'b/')
  33. assertRelpath('', 'c/', 'c/')
  34. assertRelpath('', 'c', 'c')
  35. assertRelpath('c/', 'c/', '')
  36. assertRelpath('c', 'c', '')
  37. def test_fast_relpath_invalid(self):
  38. with self.assertRaises(ValueError):
  39. fast_relpath('/a/b', '/a/baseball')
  40. with self.assertRaises(ValueError):
  41. fast_relpath('/a/baseball', '/a/b')
  42. @strict_patch('atexit.register')
  43. @strict_patch('os.getpid')
  44. @strict_patch('pants.util.dirutil.safe_rmtree')
  45. @strict_patch('tempfile.mkdtemp')
  46. def test_mkdtemp_setup_teardown(self,
  47. tempfile_mkdtemp,
  48. dirutil_safe_rmtree,
  49. os_getpid,
  50. atexit_register):
  51. def faux_cleaner():
  52. pass
  53. DIR1, DIR2 = 'fake_dir1__does_not_exist', 'fake_dir2__does_not_exist'
  54. # Make sure other "pids" are not cleaned.
  55. dirutil._MKDTEMP_DIRS['fluffypants'].add('yoyo')
  56. tempfile_mkdtemp.side_effect = (DIR1, DIR2)
  57. os_getpid.return_value = 'unicorn'
  58. try:
  59. self.assertEquals(DIR1, dirutil.safe_mkdtemp(dir='1', cleaner=faux_cleaner))
  60. self.assertEquals(DIR2, dirutil.safe_mkdtemp(dir='2', cleaner=faux_cleaner))
  61. self.assertIn('unicorn', dirutil._MKDTEMP_DIRS)
  62. self.assertEquals({DIR1, DIR2}, dirutil._MKDTEMP_DIRS['unicorn'])
  63. dirutil._mkdtemp_atexit_cleaner()
  64. self.assertNotIn('unicorn', dirutil._MKDTEMP_DIRS)
  65. self.assertEquals({'yoyo'}, dirutil._MKDTEMP_DIRS['fluffypants'])
  66. finally:
  67. dirutil._MKDTEMP_DIRS.pop('unicorn', None)
  68. dirutil._MKDTEMP_DIRS.pop('fluffypants', None)
  69. dirutil._mkdtemp_unregister_cleaner()
  70. atexit_register.assert_called_once_with(faux_cleaner)
  71. self.assertTrue(os_getpid.called)
  72. self.assertEqual([mock.call(dir='1'), mock.call(dir='2')], tempfile_mkdtemp.mock_calls)
  73. self.assertEqual([mock.call(DIR1), mock.call(DIR2)], dirutil_safe_rmtree.mock_calls)
  74. def test_safe_walk(self):
  75. """Test that directory names are correctly represented as unicode strings"""
  76. # This test is unnecessary in python 3 since all strings are unicode there is no
  77. # unicode constructor.
  78. with temporary_dir() as tmpdir:
  79. safe_mkdir(os.path.join(tmpdir, '中文'))
  80. if isinstance(tmpdir, six.text_type):
  81. tmpdir = tmpdir.encode('utf-8')
  82. for _, dirs, _ in dirutil.safe_walk(tmpdir):
  83. self.assertTrue(all(isinstance(dirname, six.text_type) for dirname in dirs))
  84. def test_relativize_paths(self):
  85. build_root = '/build-root'
  86. jar_outside_build_root = os.path.join('/outside-build-root', 'bar.jar')
  87. classpath = [os.path.join(build_root, 'foo.jar'), jar_outside_build_root]
  88. relativized_classpath = relativize_paths(classpath, build_root)
  89. jar_relpath = os.path.relpath(jar_outside_build_root, build_root)
  90. self.assertEquals(['foo.jar', jar_relpath], relativized_classpath)
  91. def test_relative_symlink(self):
  92. with temporary_dir() as tmpdir_1: # source and link in same dir
  93. source = os.path.join(tmpdir_1, 'source')
  94. link = os.path.join(tmpdir_1, 'link')
  95. rel_path = os.path.relpath(source, os.path.dirname(link))
  96. relative_symlink(source, link)
  97. self.assertTrue(os.path.islink(link))
  98. self.assertEquals(rel_path, os.readlink(link))
  99. def test_relative_symlink_source_parent(self):
  100. with temporary_dir() as tmpdir_1: # source in parent dir of link
  101. child = os.path.join(tmpdir_1, 'child')
  102. os.mkdir(child)
  103. source = os.path.join(tmpdir_1, 'source')
  104. link = os.path.join(child, 'link')
  105. relative_symlink(source, link)
  106. rel_path = os.path.relpath(source, os.path.dirname(link))
  107. self.assertTrue(os.path.islink(link))
  108. self.assertEquals(rel_path, os.readlink(link))
  109. def test_relative_symlink_link_parent(self):
  110. with temporary_dir() as tmpdir_1: # link in parent dir of source
  111. child = os.path.join(tmpdir_1, 'child')
  112. source = os.path.join(child, 'source')
  113. link = os.path.join(tmpdir_1, 'link')
  114. relative_symlink(source, link)
  115. rel_path = os.path.relpath(source, os.path.dirname(link))
  116. self.assertTrue(os.path.islink(link))
  117. self.assertEquals(rel_path, os.readlink(link))
  118. def test_relative_symlink_same_paths(self):
  119. with temporary_dir() as tmpdir_1: # source is link
  120. source = os.path.join(tmpdir_1, 'source')
  121. with self.assertRaisesRegexp(ValueError, r'Path for link is identical to source'):
  122. relative_symlink(source, source)
  123. def test_relative_symlink_bad_source(self):
  124. with temporary_dir() as tmpdir_1: # source is not absolute
  125. source = os.path.join('foo', 'bar')
  126. link = os.path.join(tmpdir_1, 'link')
  127. with self.assertRaisesRegexp(ValueError, r'Path for source.*absolute'):
  128. relative_symlink(source, link)
  129. def test_relative_symlink_bad_link(self):
  130. with temporary_dir() as tmpdir_1: # link is not absolute
  131. source = os.path.join(tmpdir_1, 'source')
  132. link = os.path.join('foo', 'bar')
  133. with self.assertRaisesRegexp(ValueError, r'Path for link.*absolute'):
  134. relative_symlink(source, link)
  135. def test_get_basedir(self):
  136. self.assertEquals(get_basedir('foo/bar/baz'), 'foo')
  137. self.assertEquals(get_basedir('/foo/bar/baz'), '')
  138. self.assertEquals(get_basedir('foo'), 'foo')
  139. def test_rm_rf_file(self, file_name='./foo'):
  140. with temporary_dir() as td, pushd(td):
  141. touch(file_name)
  142. self.assertTrue(os.path.isfile(file_name))
  143. rm_rf(file_name)
  144. self.assertFalse(os.path.exists(file_name))
  145. def test_rm_rf_dir(self, dir_name='./bar'):
  146. with temporary_dir() as td, pushd(td):
  147. safe_mkdir(dir_name)
  148. self.assertTrue(os.path.isdir(dir_name))
  149. rm_rf(dir_name)
  150. self.assertFalse(os.path.exists(dir_name))
  151. def test_rm_rf_nonexistent(self, file_name='./non_existent_file'):
  152. with temporary_dir() as td, pushd(td):
  153. rm_rf(file_name)
  154. def test_rm_rf_permission_error_raises(self, file_name='./perm_guarded_file'):
  155. with temporary_dir() as td, pushd(td), \
  156. mock.patch('pants.util.dirutil.shutil.rmtree') as mock_rmtree, \
  157. self.assertRaises(OSError):
  158. mock_rmtree.side_effect = OSError(errno.EACCES, os.strerror(errno.EACCES))
  159. touch(file_name)
  160. rm_rf(file_name)
  161. def test_rm_rf_no_such_file_not_an_error(self, file_name='./vanishing_file'):
  162. with temporary_dir() as td, pushd(td), \
  163. mock.patch('pants.util.dirutil.shutil.rmtree') as mock_rmtree:
  164. mock_rmtree.side_effect = OSError(errno.ENOENT, os.strerror(errno.ENOENT))
  165. touch(file_name)
  166. rm_rf(file_name)
  167. def test_readwrite_file(self):
  168. with temporary_dir() as td:
  169. test_filename = os.path.join(td, 'test.out')
  170. test_content = '3333'
  171. safe_file_dump(test_filename, test_content)
  172. self.assertEqual(read_file(test_filename), test_content)
  173. def test_safe_concurrent_creation(self):
  174. with temporary_dir() as td:
  175. expected_file = os.path.join(td, 'expected_file')
  176. with safe_concurrent_creation(expected_file) as tmp_expected_file:
  177. os.mkdir(tmp_expected_file)
  178. self.assertTrue(os.path.exists(tmp_expected_file))
  179. self.assertFalse(os.path.exists(expected_file))
  180. self.assertTrue(os.path.exists(expected_file))
  181. def test_safe_concurrent_creation_noop(self):
  182. with temporary_dir() as td:
  183. expected_file = os.path.join(td, 'parent_dir', 'expected_file')
  184. # Ensure safe_concurrent_creation() doesn't bomb if we don't write the expected files.
  185. with safe_concurrent_creation(expected_file):
  186. pass
  187. self.assertFalse(os.path.exists(expected_file))
  188. self.assertTrue(os.path.exists(os.path.dirname(expected_file)))
  189. def test_safe_concurrent_creation_exception_still_renames(self):
  190. with temporary_dir() as td:
  191. expected_file = os.path.join(td, 'expected_file')
  192. with self.assertRaises(ZeroDivisionError):
  193. with safe_concurrent_creation(expected_file) as safe_path:
  194. os.mkdir(safe_path)
  195. self.assertTrue(os.path.exists(safe_path))
  196. raise ZeroDivisionError('zomg')
  197. self.assertFalse(os.path.exists(safe_path))
  198. self.assertTrue(os.path.exists(expected_file))
  199. def test_safe_rm_oldest_items_in_dir(self):
  200. with temporary_dir() as td:
  201. touch(os.path.join(td, 'file1'))
  202. safe_mkdir(os.path.join(td, 'file2'))
  203. # Time modified is only accurate to second.
  204. time.sleep(1.1)
  205. touch(os.path.join(td, 'file3'))
  206. touch(os.path.join(td, 'file4'))
  207. safe_mkdir(os.path.join(td, 'file5'))
  208. safe_rm_oldest_items_in_dir(td, 3)
  209. self.assertFalse(os.path.exists(os.path.join(td, 'file1')))
  210. self.assertFalse(os.path.exists(os.path.join(td, 'file2')))
  211. self.assertTrue(os.path.exists(os.path.join(td, 'file3')))
  212. self.assertTrue(os.path.exists(os.path.join(td, 'file4')))
  213. self.assertTrue(os.path.exists(os.path.join(td, 'file5')))
  214. def test_safe_rm_oldest_items_in_dir_with_excludes(self):
  215. with temporary_dir() as td:
  216. touch(os.path.join(td, 'file1'))
  217. touch(os.path.join(td, 'file2'))
  218. touch(os.path.join(td, 'file3'))
  219. # Time modified is only accurate to second.
  220. time.sleep(1.1)
  221. touch(os.path.join(td, 'file4'))
  222. excludes = [os.path.join(td, 'file1'),
  223. os.path.join(td, 'file2')]
  224. safe_rm_oldest_items_in_dir(td, 1, excludes)
  225. self.assertTrue(os.path.exists(os.path.join(td, 'file1')))
  226. self.assertTrue(os.path.exists(os.path.join(td, 'file2')))
  227. self.assertTrue(os.path.exists(os.path.join(td, 'file4')))
  228. self.assertFalse(os.path.exists(os.path.join(td, 'file3')))
  229. def test_safe_rm_oldest_items_in_dir_noop(self):
  230. with temporary_dir() as td:
  231. safe_rm_oldest_items_in_dir(td, 1)
  232. touch(os.path.join(td, 'file1'))
  233. self.assertEqual(len(os.listdir(td)), 1)
  234. class AbsoluteSymlinkTest(unittest.TestCase):
  235. def setUp(self):
  236. self.td = safe_mkdtemp()
  237. self.addCleanup(safe_rmtree, self.td)
  238. self.source = os.path.join(self.td, 'source')
  239. self.link = os.path.join(self.td, 'link')
  240. def _create_and_check_link(self, source, link):
  241. absolute_symlink(source, link)
  242. self.assertTrue(os.path.islink(link))
  243. self.assertEquals(source, os.readlink(link))
  244. def test_link(self):
  245. # Check if parent dirs will be created for the link
  246. link = os.path.join(self.td, 'a', 'b', 'c', 'self.link')
  247. self._create_and_check_link(self.source, link)
  248. def test_overwrite_link_link(self):
  249. # Do it twice, to make sure we can overwrite existing link
  250. self._create_and_check_link(self.source, self.link)
  251. self._create_and_check_link(self.source, self.link)
  252. def test_overwrite_link_file(self):
  253. with open(self.source, 'w') as fp:
  254. fp.write('evidence')
  255. # Do it twice, to make sure we can overwrite existing link
  256. self._create_and_check_link(self.source, self.link)
  257. self._create_and_check_link(self.source, self.link)
  258. # The link should have been deleted (over-written), not the file it pointed to.
  259. with open(self.source) as fp:
  260. self.assertEqual('evidence', fp.read())
  261. def test_overwrite_link_dir(self):
  262. nested_dir = os.path.join(self.source, 'a', 'b', 'c')
  263. os.makedirs(nested_dir)
  264. # Do it twice, to make sure we can overwrite existing link
  265. self._create_and_check_link(self.source, self.link)
  266. self._create_and_check_link(self.source, self.link)
  267. # The link should have been deleted (over-written), not the dir it pointed to.
  268. self.assertTrue(os.path.isdir(nested_dir))
  269. def test_overwrite_file(self):
  270. touch(self.link)
  271. self._create_and_check_link(self.source, self.link)
  272. def test_overwrite_dir(self):
  273. os.makedirs(os.path.join(self.link, 'a', 'b', 'c'))
  274. self._create_and_check_link(self.source, self.link)