test.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. from __future__ import with_statement
  2. import doctest
  3. import filecmp
  4. import os
  5. from StringIO import StringIO
  6. import stat
  7. import sys
  8. import shutil
  9. import tempfile
  10. import unittest
  11. from xattr import xattr, XATTR_NOFOLLOW
  12. from . import helpers, lrucache
  13. from .archiver import Archiver
  14. from .key import suite as KeySuite
  15. from .store import Store, suite as StoreSuite
  16. from .remote import Store, suite as RemoteStoreSuite
  17. class Test(unittest.TestCase):
  18. prefix = ''
  19. def setUp(self):
  20. self.archiver = Archiver()
  21. self.tmpdir = tempfile.mkdtemp()
  22. self.store_path = os.path.join(self.tmpdir, 'store')
  23. self.store_location = self.prefix + self.store_path
  24. self.input_path = os.path.join(self.tmpdir, 'input')
  25. self.output_path = os.path.join(self.tmpdir, 'output')
  26. self.keys_path = os.path.join(self.tmpdir, 'keys')
  27. self.cache_path = os.path.join(self.tmpdir, 'cache')
  28. os.environ['DARC_KEYS_DIR'] = self.keys_path
  29. os.environ['DARC_CACHE_DIR'] = self.cache_path
  30. os.mkdir(self.input_path)
  31. os.mkdir(self.output_path)
  32. os.mkdir(self.keys_path)
  33. os.mkdir(self.cache_path)
  34. os.chdir(self.tmpdir)
  35. def tearDown(self):
  36. shutil.rmtree(self.tmpdir)
  37. def darc(self, *args, **kwargs):
  38. exit_code = kwargs.get('exit_code', 0)
  39. args = list(args)
  40. try:
  41. stdout, stderr = sys.stdout, sys.stderr
  42. output = StringIO()
  43. sys.stdout = sys.stderr = output
  44. ret = self.archiver.run(args)
  45. sys.stdout, sys.stderr = stdout, stderr
  46. if ret != exit_code:
  47. print output.getvalue()
  48. self.assertEqual(exit_code, ret)
  49. return output.getvalue()
  50. finally:
  51. sys.stdout, sys.stderr = stdout, stderr
  52. def create_src_archive(self, name):
  53. src_dir = os.path.join(os.getcwd(), os.path.dirname(__file__), '..')
  54. self.darc('init', self.store_location)
  55. self.darc('create', self.store_location + '::' + name, src_dir)
  56. def create_regual_file(self, name, size=0):
  57. filename = os.path.join(self.input_path, name)
  58. if not os.path.exists(os.path.dirname(filename)):
  59. os.makedirs(os.path.dirname(filename))
  60. with open(filename, 'wbx') as fd:
  61. fd.write('X' * size)
  62. def get_xattrs(self, path):
  63. try:
  64. return dict(xattr(path, XATTR_NOFOLLOW))
  65. except IOError:
  66. return {}
  67. def diff_dirs(self, dir1, dir2):
  68. diff = filecmp.dircmp(dir1, dir2)
  69. self.assertEqual(diff.left_only, [])
  70. self.assertEqual(diff.right_only, [])
  71. self.assertEqual(diff.diff_files, [])
  72. for filename in diff.common:
  73. path1 = os.path.join(dir1, filename)
  74. path2 = os.path.join(dir2, filename)
  75. s1 = os.lstat(path1)
  76. s2 = os.lstat(path2)
  77. attrs = ['st_mode', 'st_uid', 'st_gid', 'st_rdev']
  78. # We can't restore symlink atime/mtime right now
  79. if not os.path.islink(path1):
  80. attrs.append('st_mtime')
  81. d1 = [filename] + [getattr(s1, a) for a in attrs]
  82. d2 = [filename] + [getattr(s2, a) for a in attrs]
  83. d1.append(self.get_xattrs(path1))
  84. d2.append(self.get_xattrs(path2))
  85. self.assertEqual(d1, d2)
  86. def test_basic_functionality(self):
  87. # File
  88. self.create_regual_file('file1', size=1024 * 80)
  89. # Directory
  90. self.create_regual_file('dir2/file2', size=1024 * 80)
  91. # File owner
  92. os.chown('input/file1', 100, 200)
  93. # File mode
  94. os.chmod('input/file1', 0600)
  95. os.chmod('input/dir2', 0700)
  96. # Block device
  97. os.mknod('input/bdev', 0600 | stat.S_IFBLK, os.makedev(10, 20))
  98. # Char device
  99. os.mknod('input/cdev', 0600 | stat.S_IFCHR, os.makedev(30, 40))
  100. # xattr
  101. x = xattr(os.path.join(self.input_path, 'file1'))
  102. x.set('user.foo', 'bar')
  103. # Hard link
  104. os.link(os.path.join(self.input_path, 'file1'),
  105. os.path.join(self.input_path, 'hardlink'))
  106. # Symlink
  107. os.symlink('somewhere', os.path.join(self.input_path, 'link1'))
  108. # FIFO node
  109. os.mkfifo(os.path.join(self.input_path, 'fifo1'))
  110. self.darc('init', self.store_location)
  111. self.darc('create', self.store_location + '::test', 'input')
  112. self.darc('create', self.store_location + '::test.2', 'input')
  113. self.darc('extract', self.store_location + '::test', 'output')
  114. self.assertEqual(len(self.darc('list', self.store_location).splitlines()), 2)
  115. self.assertEqual(len(self.darc('list', self.store_location + '::test').splitlines()), 9)
  116. self.diff_dirs('input', 'output/input')
  117. info_output = self.darc('info', self.store_location + '::test')
  118. shutil.rmtree(self.cache_path)
  119. info_output2 = self.darc('info', self.store_location + '::test')
  120. # info_output2 starts with some "initializing cache" text but should
  121. # end the same way as info_output
  122. assert info_output2.endswith(info_output)
  123. def test_overwrite(self):
  124. self.create_regual_file('file1', size=1024 * 80)
  125. self.create_regual_file('dir2/file2', size=1024 * 80)
  126. self.darc('init', self.store_location)
  127. self.darc('create', self.store_location + '::test', 'input')
  128. # Overwriting regular files and directories should be supported
  129. os.mkdir('output/input')
  130. os.mkdir('output/input/file1')
  131. os.mkdir('output/input/dir2')
  132. self.darc('extract', self.store_location + '::test', 'output')
  133. self.diff_dirs('input', 'output/input')
  134. # But non-empty dirs should fail
  135. os.unlink('output/input/file1')
  136. os.mkdir('output/input/file1')
  137. os.mkdir('output/input/file1/dir')
  138. self.darc('extract', self.store_location + '::test', 'output', exit_code=1)
  139. def test_delete(self):
  140. self.create_regual_file('file1', size=1024 * 80)
  141. self.create_regual_file('dir2/file2', size=1024 * 80)
  142. self.darc('init', self.store_location)
  143. self.darc('create', self.store_location + '::test', 'input')
  144. self.darc('create', self.store_location + '::test.2', 'input')
  145. self.darc('verify', self.store_location + '::test')
  146. self.darc('verify', self.store_location + '::test.2')
  147. self.darc('delete', self.store_location + '::test')
  148. self.darc('verify', self.store_location + '::test.2')
  149. self.darc('delete', self.store_location + '::test.2')
  150. # Make sure all data except the manifest has been deleted
  151. store = Store(self.store_path)
  152. self.assertEqual(store._len(), 1)
  153. def test_corrupted_store(self):
  154. self.create_src_archive('test')
  155. self.darc('verify', self.store_location + '::test')
  156. name = sorted(os.listdir(os.path.join(self.tmpdir, 'store', 'data', '0')), reverse=True)[0]
  157. fd = open(os.path.join(self.tmpdir, 'store', 'data', '0', name), 'r+')
  158. fd.seek(100)
  159. fd.write('X')
  160. fd.close()
  161. self.darc('verify', self.store_location + '::test', exit_code=1)
  162. def test_prune_store(self):
  163. src_dir = os.path.join(os.getcwd(), os.path.dirname(__file__))
  164. self.darc('init', self.store_location)
  165. self.darc('create', self.store_location + '::test1', src_dir)
  166. self.darc('create', self.store_location + '::test2', src_dir)
  167. self.darc('prune', self.store_location, '--daily=2')
  168. output = self.darc('list', self.store_location)
  169. assert 'test1' not in output
  170. assert 'test2' in output
  171. class RemoteTest(Test):
  172. prefix = 'localhost:'
  173. def suite():
  174. suite = unittest.TestSuite()
  175. suite.addTest(unittest.TestLoader().loadTestsFromTestCase(Test))
  176. suite.addTest(unittest.TestLoader().loadTestsFromTestCase(RemoteTest))
  177. suite.addTest(KeySuite())
  178. suite.addTest(StoreSuite())
  179. suite.addTest(RemoteStoreSuite())
  180. suite.addTest(doctest.DocTestSuite(helpers))
  181. suite.addTest(lrucache.suite())
  182. return suite
  183. if __name__ == '__main__':
  184. unittest.TextTestRunner(verbosity=2).run(suite())