test.py 7.5 KB

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