__init__.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # vim: set fileencoding=utf-8 :
  2. #
  3. # (C) 2012 Intel Corporation <markus.lehtonen@linux.intel.com>
  4. # 2013 Guido Günther <agx@sigxcpu.org>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, please see
  18. # <http://www.gnu.org/licenses/>
  19. """
  20. Module for testing individual command line tools of the git-buildpackage suite
  21. """
  22. import hashlib
  23. import os
  24. import shutil
  25. import tempfile
  26. import unittest
  27. from unittest import skipUnless
  28. from nose import SkipTest
  29. from nose.tools import eq_, ok_ # pylint: disable=E0611
  30. from .. testutils import GbpLogTester
  31. from gbp.git import GitRepository, GitRepositoryError
  32. __all__ = ['ComponentTestGitRepository', 'ComponentTestBase', 'GbpLogTester', 'skipUnless']
  33. class ComponentTestGitRepository(GitRepository):
  34. """Git repository class for component tests"""
  35. def submodule_status(self):
  36. """
  37. Determine submodules and their status
  38. """
  39. out, err, ret = self._git_inout('submodule', ['status'],
  40. capture_stderr=True)
  41. if ret:
  42. raise GitRepositoryError("Cannot get submodule status: %s" %
  43. err.strip())
  44. submodules = {}
  45. for line in out.splitlines():
  46. module = line.strip()
  47. # Uninitialized
  48. status = module[0]
  49. if status == '-':
  50. sha1, path = module[1:].rsplit(' ', 1)
  51. else:
  52. commitpath = module[1:].rsplit(' ', 1)[0]
  53. sha1, path = commitpath.split(' ', 1)
  54. submodules[path] = (status, sha1)
  55. return submodules
  56. @classmethod
  57. def check_testdata(cls, data):
  58. """Check whether the testdata is current"""
  59. try:
  60. repo = cls('.')
  61. except GitRepositoryError:
  62. raise SkipTest("Skipping '%s', since this is not a git checkout."
  63. % __name__)
  64. submodules = repo.submodule_status()
  65. try:
  66. status = submodules[data]
  67. except KeyError:
  68. raise SkipTest("Skipping '%s', testdata directory not a known "
  69. "submodule." % __name__)
  70. if status[0] == '-':
  71. raise SkipTest("Skipping '%s', testdata directory not initialized. "
  72. "Consider doing 'git submodule update'" % __name__)
  73. def ls_tree(self, treeish):
  74. """List contents (blobs) in a git treeish"""
  75. objs = self.list_tree(treeish, True)
  76. blobs = [obj[3] for obj in objs if obj[1] == 'blob']
  77. return set(blobs)
  78. class ComponentTestBase(unittest.TestCase, GbpLogTester):
  79. """Base class for testing cmdline tools of git-buildpackage"""
  80. @classmethod
  81. def setUpClass(cls):
  82. """Test class case setup"""
  83. # Don't let git see that we're (possibly) under a git directory
  84. cls.orig_env = os.environ.copy()
  85. os.environ['GIT_CEILING_DIRECTORIES'] = os.getcwd()
  86. # Create a top-level tmpdir for the test
  87. cls._tmproot = tempfile.mkdtemp(prefix='gbp_%s_' % cls.__name__,
  88. dir='.')
  89. cls._tmproot = os.path.abspath(cls._tmproot)
  90. # Prevent local config files from messing up the tests
  91. os.environ['GBP_CONF_FILES'] = ':'.join(['%(top_dir)s/.gbp.conf',
  92. '%(top_dir)s/debian/gbp.conf',
  93. '%(git_dir)s/gbp.conf'])
  94. @classmethod
  95. def tearDownClass(cls):
  96. """Test class case teardown"""
  97. # Return original environment
  98. os.environ.clear()
  99. os.environ.update(cls.orig_env)
  100. # Remove top-level tmpdir
  101. if not os.getenv("GBP_TESTS_NOCLEAN"):
  102. shutil.rmtree(cls._tmproot)
  103. def __init__(self, methodName='runTest'):
  104. """Object initialization"""
  105. self._orig_dir = None
  106. self._tmpdir = None
  107. unittest.TestCase.__init__(self, methodName)
  108. GbpLogTester.__init__(self)
  109. def setUp(self):
  110. """Test case setup"""
  111. # Change to a temporary directory
  112. self._orig_dir = os.getcwd()
  113. self._tmpdir = tempfile.mkdtemp(prefix='tmp_%s_' % self._testMethodName,
  114. dir=self._tmproot)
  115. os.chdir(self._tmpdir)
  116. self._capture_log(True)
  117. def tearDown(self):
  118. """Test case teardown"""
  119. # Restore original working dir
  120. os.chdir(self._orig_dir)
  121. if not os.getenv("GBP_TESTS_NOCLEAN"):
  122. shutil.rmtree(self._tmpdir)
  123. self._capture_log(False)
  124. @staticmethod
  125. def check_files(reference, filelist):
  126. """Compare two file lists"""
  127. extra = set(filelist) - set(reference)
  128. missing = set(reference) - set(filelist)
  129. assert_msg = "Unexpected files: %s, Missing files: %s" % \
  130. (list(extra), list(missing))
  131. assert not extra and not missing, assert_msg
  132. @classmethod
  133. def check_tags(cls, repo, tags):
  134. local_tags = repo.tags
  135. assert_msg = "Tags: expected %s, found %s" % (tags,
  136. local_tags)
  137. eq_(set(local_tags), set(tags), assert_msg)
  138. @classmethod
  139. def _check_repo_state(cls, repo, current_branch, branches, files=None,
  140. dirs=None, tags=None):
  141. """
  142. Check that repository is clean and given branches, tags, files
  143. and dirs exist
  144. """
  145. branch = repo.branch
  146. eq_(branch, current_branch)
  147. ok_(repo.is_clean())
  148. local_branches = repo.get_local_branches()
  149. assert_msg = "Branches: expected %s, found %s" % (branches,
  150. local_branches)
  151. eq_(set(local_branches), set(branches), assert_msg)
  152. if files is not None or dirs is not None:
  153. # Get files of the working copy recursively
  154. local_f = set()
  155. local_d = set()
  156. for dirpath, dirnames, filenames in os.walk(repo.path):
  157. # Skip git dir(s)
  158. if '.git' in dirnames:
  159. dirnames.remove('.git')
  160. for filename in filenames:
  161. local_f.add(os.path.relpath(os.path.join(dirpath, filename),
  162. repo.path))
  163. for dirname in dirnames:
  164. local_d.add(os.path.relpath(os.path.join(dirpath, dirname),
  165. repo.path) + '/')
  166. if files is not None:
  167. cls.check_files(files, local_f)
  168. if dirs is not None:
  169. cls.check_files(dirs, local_d)
  170. if tags is not None:
  171. cls.check_tags(repo, tags)
  172. @classmethod
  173. def rem_refs(cls, repo, refs):
  174. """Remember the SHA1 of the given refs"""
  175. rem = []
  176. for name in refs:
  177. rem.append((name, repo.rev_parse(name)))
  178. return rem
  179. @classmethod
  180. def check_refs(cls, repo, rem):
  181. """
  182. Check that the heads given n (head, sha1) tuples are
  183. still pointing to the given sha1
  184. """
  185. for (h, s) in rem:
  186. n = repo.rev_parse(h)
  187. ok_(n == s, "Head '%s' points to %s' instead of '%s'" % (h, n, s))
  188. @staticmethod
  189. def hash_file(filename):
  190. h = hashlib.md5()
  191. with open(filename, 'rb') as f:
  192. buf = f.read()
  193. h.update(buf)
  194. return h.hexdigest()
  195. @staticmethod
  196. def check_hook_vars(name, expected):
  197. """
  198. Check that a hook had the given vars in
  199. it's environment.
  200. This assumes the hook was set too
  201. printenv > hookname.out
  202. """
  203. with open('%s.out' % name) as f:
  204. parsed = dict([line[:-1].split('=', 1) for line in f.readlines() if line.startswith("GBP_")])
  205. for var in expected:
  206. if len(var) == 2:
  207. k, v = var
  208. else:
  209. k, v = var, None
  210. ok_(k in parsed, "%s not found in %s" % (k, parsed))
  211. if v is not None:
  212. ok_(v == parsed[k],
  213. "Got %s not expected value %s for %s" % (parsed[k], v, k))