##// END OF EJS Templates
tests: fix Git on Windows sometimes failing on ' or ` in file:/// URLs...
domruf -
r5758:078136fd default
parent child Browse files
Show More
@@ -1,298 +1,299 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.compare
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 compare controller for pylons showing differences between two
19 19 repos, branches, bookmarks or tips
20 20
21 21 This file was forked by the Kallithea project in July 2014.
22 22 Original author and date, and relevant copyright and licensing information is below:
23 23 :created_on: May 6, 2012
24 24 :author: marcink
25 25 :copyright: (c) 2013 RhodeCode GmbH, and others.
26 26 :license: GPLv3, see LICENSE.md for more details.
27 27 """
28 28
29 29
30 30 import logging
31 31 import re
32 32
33 33 from pylons import request, tmpl_context as c, url
34 34 from pylons.i18n.translation import _
35 35 from webob.exc import HTTPFound, HTTPBadRequest
36 36
37 37 from kallithea.lib.utils2 import safe_str
38 38 from kallithea.lib.vcs.utils.hgcompat import unionrepo
39 39 from kallithea.lib import helpers as h
40 40 from kallithea.lib.base import BaseRepoController, render
41 41 from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from kallithea.lib import diffs
43 43 from kallithea.model.db import Repository
44 44 from kallithea.lib.diffs import LimitedDiffContainer
45 45 from kallithea.controllers.changeset import _ignorews_url, _context_url
46 46 from kallithea.lib.graphmod import graph_data
47 47 from kallithea.lib.compat import json
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class CompareController(BaseRepoController):
53 53
54 54 def __before__(self):
55 55 super(CompareController, self).__before__()
56 56
57 57 @staticmethod
58 58 def _get_changesets(alias, org_repo, org_rev, other_repo, other_rev):
59 59 """
60 60 Returns lists of changesets that can be merged from org_repo@org_rev
61 61 to other_repo@other_rev
62 62 ... and the other way
63 63 ... and the ancestor that would be used for merge
64 64
65 65 :param org_repo: repo object, that is most likely the original repo we forked from
66 66 :param org_rev: the revision we want our compare to be made
67 67 :param other_repo: repo object, most likely the fork of org_repo. It has
68 68 all changesets that we need to obtain
69 69 :param other_rev: revision we want out compare to be made on other_repo
70 70 """
71 71 ancestor = None
72 72 if org_rev == other_rev:
73 73 org_changesets = []
74 74 other_changesets = []
75 75 ancestor = org_rev
76 76
77 77 elif alias == 'hg':
78 78 #case two independent repos
79 79 if org_repo != other_repo:
80 80 hgrepo = unionrepo.unionrepository(other_repo.baseui,
81 81 other_repo.path,
82 82 org_repo.path)
83 83 # all ancestors of other_rev will be in other_repo and
84 84 # rev numbers from hgrepo can be used in other_repo - org_rev ancestors cannot
85 85
86 86 #no remote compare do it on the same repository
87 87 else:
88 88 hgrepo = other_repo._repo
89 89
90 90 if org_repo.EMPTY_CHANGESET in (org_rev, other_rev):
91 91 # work around unexpected behaviour in Mercurial < 3.4
92 92 ancestor = org_repo.EMPTY_CHANGESET
93 93 else:
94 94 ancestors = hgrepo.revs("ancestor(id(%s), id(%s))", org_rev, other_rev)
95 95 if ancestors:
96 96 # FIXME: picks arbitrary ancestor - but there is usually only one
97 97 try:
98 98 ancestor = hgrepo[ancestors.first()].hex()
99 99 except AttributeError:
100 100 # removed in hg 3.2
101 101 ancestor = hgrepo[ancestors[0]].hex()
102 102
103 103 other_revs = hgrepo.revs("ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
104 104 other_rev, org_rev, org_rev)
105 105 other_changesets = [other_repo.get_changeset(rev) for rev in other_revs]
106 106 org_revs = hgrepo.revs("ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
107 107 org_rev, other_rev, other_rev)
108 108
109 109 org_changesets = [org_repo.get_changeset(hgrepo[rev].hex()) for rev in org_revs]
110 110
111 111 elif alias == 'git':
112 112 if org_repo != other_repo:
113 113 from dulwich.repo import Repo
114 114 from dulwich.client import SubprocessGitClient
115 115
116 116 gitrepo = Repo(org_repo.path)
117 117 SubprocessGitClient(thin_packs=False).fetch(safe_str(other_repo.path), gitrepo)
118 118
119 119 gitrepo_remote = Repo(other_repo.path)
120 120 SubprocessGitClient(thin_packs=False).fetch(safe_str(org_repo.path), gitrepo_remote)
121 121
122 122 revs = []
123 123 for x in gitrepo_remote.get_walker(include=[other_rev],
124 124 exclude=[org_rev]):
125 125 revs.append(x.commit.id)
126 126
127 127 other_changesets = [other_repo.get_changeset(rev) for rev in reversed(revs)]
128 128 if other_changesets:
129 129 ancestor = other_changesets[0].parents[0].raw_id
130 130 else:
131 131 # no changesets from other repo, ancestor is the other_rev
132 132 ancestor = other_rev
133
133 134 # dulwich 0.9.9 doesn't have a Repo.close() so we have to mess with internals:
134 135 gitrepo.object_store.close()
135 136 gitrepo_remote.object_store.close()
136 137
137 138 else:
138 139 so, se = org_repo.run_git_command(
139 140 ['log', '--reverse', '--pretty=format:%H',
140 141 '-s', '%s..%s' % (org_rev, other_rev)]
141 142 )
142 143 other_changesets = [org_repo.get_changeset(cs)
143 144 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
144 145 so, se = org_repo.run_git_command(
145 146 ['merge-base', org_rev, other_rev]
146 147 )
147 148 ancestor = re.findall(r'[0-9a-fA-F]{40}', so)[0]
148 149 org_changesets = []
149 150
150 151 else:
151 152 raise Exception('Bad alias only git and hg is allowed')
152 153
153 154 return other_changesets, org_changesets, ancestor
154 155
155 156 @LoginRequired()
156 157 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
157 158 'repository.admin')
158 159 def index(self, repo_name):
159 160 c.compare_home = True
160 161 org_repo = c.db_repo.repo_name
161 162 other_repo = request.GET.get('other_repo', org_repo)
162 163 c.a_repo = Repository.get_by_repo_name(org_repo)
163 164 c.cs_repo = Repository.get_by_repo_name(other_repo)
164 165 c.a_ref_name = c.cs_ref_name = _('Select changeset')
165 166 return render('compare/compare_diff.html')
166 167
167 168 @LoginRequired()
168 169 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
169 170 'repository.admin')
170 171 def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type, other_ref_name):
171 172 org_ref_name = org_ref_name.strip()
172 173 other_ref_name = other_ref_name.strip()
173 174
174 175 org_repo = c.db_repo.repo_name
175 176 other_repo = request.GET.get('other_repo', org_repo)
176 177 # If merge is True:
177 178 # Show what org would get if merged with other:
178 179 # List changesets that are ancestors of other but not of org.
179 180 # New changesets in org is thus ignored.
180 181 # Diff will be from common ancestor, and merges of org to other will thus be ignored.
181 182 # If merge is False:
182 183 # Make a raw diff from org to other, no matter if related or not.
183 184 # Changesets in one and not in the other will be ignored
184 185 merge = bool(request.GET.get('merge'))
185 186 # fulldiff disables cut_off_limit
186 187 c.fulldiff = request.GET.get('fulldiff')
187 188 # partial uses compare_cs.html template directly
188 189 partial = request.environ.get('HTTP_X_PARTIAL_XHR')
189 190 # as_form puts hidden input field with changeset revisions
190 191 c.as_form = partial and request.GET.get('as_form')
191 192 # swap url for compare_diff page - never partial and never as_form
192 193 c.swap_url = h.url('compare_url',
193 194 repo_name=other_repo,
194 195 org_ref_type=other_ref_type, org_ref_name=other_ref_name,
195 196 other_repo=org_repo,
196 197 other_ref_type=org_ref_type, other_ref_name=org_ref_name,
197 198 merge=merge or '')
198 199
199 200 # set callbacks for generating markup for icons
200 201 c.ignorews_url = _ignorews_url
201 202 c.context_url = _context_url
202 203 ignore_whitespace = request.GET.get('ignorews') == '1'
203 204 line_context = request.GET.get('context', 3)
204 205
205 206 org_repo = Repository.get_by_repo_name(org_repo)
206 207 other_repo = Repository.get_by_repo_name(other_repo)
207 208
208 209 if org_repo is None:
209 210 msg = 'Could not find org repo %s' % org_repo
210 211 log.error(msg)
211 212 h.flash(msg, category='error')
212 213 raise HTTPFound(location=url('compare_home', repo_name=c.repo_name))
213 214
214 215 if other_repo is None:
215 216 msg = 'Could not find other repo %s' % other_repo
216 217 log.error(msg)
217 218 h.flash(msg, category='error')
218 219 raise HTTPFound(location=url('compare_home', repo_name=c.repo_name))
219 220
220 221 if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
221 222 msg = 'compare of two different kind of remote repos not available'
222 223 log.error(msg)
223 224 h.flash(msg, category='error')
224 225 raise HTTPFound(location=url('compare_home', repo_name=c.repo_name))
225 226
226 227 c.a_rev = self._get_ref_rev(org_repo, org_ref_type, org_ref_name,
227 228 returnempty=True)
228 229 c.cs_rev = self._get_ref_rev(other_repo, other_ref_type, other_ref_name)
229 230
230 231 c.compare_home = False
231 232 c.a_repo = org_repo
232 233 c.a_ref_name = org_ref_name
233 234 c.a_ref_type = org_ref_type
234 235 c.cs_repo = other_repo
235 236 c.cs_ref_name = other_ref_name
236 237 c.cs_ref_type = other_ref_type
237 238
238 239 c.cs_ranges, c.cs_ranges_org, c.ancestor = self._get_changesets(
239 240 org_repo.scm_instance.alias, org_repo.scm_instance, c.a_rev,
240 241 other_repo.scm_instance, c.cs_rev)
241 242 raw_ids = [x.raw_id for x in c.cs_ranges]
242 243 c.cs_comments = other_repo.get_comments(raw_ids)
243 244 c.statuses = other_repo.statuses(raw_ids)
244 245
245 246 revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
246 247 c.jsdata = json.dumps(graph_data(c.cs_repo.scm_instance, revs))
247 248
248 249 if partial:
249 250 return render('compare/compare_cs.html')
250 251 if merge and c.ancestor:
251 252 # case we want a simple diff without incoming changesets,
252 253 # previewing what will be merged.
253 254 # Make the diff on the other repo (which is known to have other_rev)
254 255 log.debug('Using ancestor %s as rev1 instead of %s',
255 256 c.ancestor, c.a_rev)
256 257 rev1 = c.ancestor
257 258 org_repo = other_repo
258 259 else: # comparing tips, not necessarily linearly related
259 260 if merge:
260 261 log.error('Unable to find ancestor revision')
261 262 if org_repo != other_repo:
262 263 # TODO: we could do this by using hg unionrepo
263 264 log.error('cannot compare across repos %s and %s', org_repo, other_repo)
264 265 h.flash(_('Cannot compare repositories without using common ancestor'), category='error')
265 266 raise HTTPBadRequest
266 267 rev1 = c.a_rev
267 268
268 269 diff_limit = self.cut_off_limit if not c.fulldiff else None
269 270
270 271 log.debug('running diff between %s and %s in %s',
271 272 rev1, c.cs_rev, org_repo.scm_instance.path)
272 273 txtdiff = org_repo.scm_instance.get_diff(rev1=rev1, rev2=c.cs_rev,
273 274 ignore_whitespace=ignore_whitespace,
274 275 context=line_context)
275 276
276 277 diff_processor = diffs.DiffProcessor(txtdiff or '', format='gitdiff',
277 278 diff_limit=diff_limit)
278 279 _parsed = diff_processor.prepare()
279 280
280 281 c.limited_diff = False
281 282 if isinstance(_parsed, LimitedDiffContainer):
282 283 c.limited_diff = True
283 284
284 285 c.files = []
285 286 c.changes = {}
286 287 c.lines_added = 0
287 288 c.lines_deleted = 0
288 289 for f in _parsed:
289 290 st = f['stats']
290 291 if not st['binary']:
291 292 c.lines_added += st['added']
292 293 c.lines_deleted += st['deleted']
293 294 fid = h.FID('', f['filename'])
294 295 c.files.append([fid, f['operation'], f['filename'], f['stats']])
295 296 htmldiff = diff_processor.as_html(enable_comments=False, parsed_lines=[f])
296 297 c.changes[fid] = [f['operation'], f['filename'], htmldiff]
297 298
298 299 return render('compare/compare_diff.html')
@@ -1,838 +1,840 b''
1 1
2 2 import os
3 3 import sys
4 4 import mock
5 5 import datetime
6 6 import urllib2
7 7 import tempfile
8 8
9 9 import pytest
10 10
11 11 from kallithea.lib.vcs.backends.git import GitRepository, GitChangeset
12 12 from kallithea.lib.vcs.exceptions import RepositoryError, VCSError, NodeDoesNotExistError
13 13 from kallithea.lib.vcs.nodes import NodeKind, FileNode, DirNode, NodeState
14 14 from kallithea.lib.vcs.utils.compat import unittest
15 15 from kallithea.model.scm import ScmModel
16 16 from kallithea.tests.vcs.base import _BackendTestMixin
17 17 from kallithea.tests.vcs.conf import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
18 18
19 19
20 20 class GitRepositoryTest(unittest.TestCase):
21 21
22 22 def __check_for_existing_repo(self):
23 23 if os.path.exists(TEST_GIT_REPO_CLONE):
24 24 pytest.fail('Cannot test git clone repo as location %s already '
25 25 'exists. You should manually remove it first.'
26 26 % TEST_GIT_REPO_CLONE)
27 27
28 28 def setUp(self):
29 29 self.repo = GitRepository(TEST_GIT_REPO)
30 30
31 31 def test_wrong_repo_path(self):
32 32 wrong_repo_path = os.path.join(tempfile.gettempdir(), 'errorrepo')
33 33 self.assertRaises(RepositoryError, GitRepository, wrong_repo_path)
34 34
35 35 def test_git_cmd_injection(self):
36 36 repo_inject_path = TEST_GIT_REPO + '; echo "Cake";'
37 37 with self.assertRaises(urllib2.URLError):
38 38 # Should fail because URL will contain the parts after ; too
39 39 urlerror_fail_repo = GitRepository(get_new_dir('injection-repo'), src_url=repo_inject_path, update_after_clone=True, create=True)
40 40
41 41 with self.assertRaises(RepositoryError):
42 42 # Should fail on direct clone call, which as of this writing does not happen outside of class
43 43 clone_fail_repo = GitRepository(get_new_dir('injection-repo'), create=True)
44 44 clone_fail_repo.clone(repo_inject_path, update_after_clone=True,)
45 45
46 46 # Verify correct quoting of evil characters that should work on posix file systems
47 47 if sys.platform == 'win32':
48 48 # windows does not allow '"' in dir names
49 tricky_path = get_new_dir("tricky-path-repo-$'`")
49 # and some versions of the git client don't like ` and '
50 tricky_path = get_new_dir("tricky-path-repo-$")
50 51 else:
51 52 tricky_path = get_new_dir("tricky-path-repo-$'\"`")
52 53 successfully_cloned = GitRepository(tricky_path, src_url=TEST_GIT_REPO, update_after_clone=True, create=True)
53 54 # Repo should have been created
54 55 self.assertFalse(successfully_cloned._repo.bare)
55 56
56 57 if sys.platform == 'win32':
57 58 # windows does not allow '"' in dir names
58 tricky_path_2 = get_new_dir("tricky-path-2-repo-$'`")
59 # and some versions of the git client don't like ` and '
60 tricky_path_2 = get_new_dir("tricky-path-2-repo-$")
59 61 else:
60 62 tricky_path_2 = get_new_dir("tricky-path-2-repo-$'\"`")
61 63 successfully_cloned2 = GitRepository(tricky_path_2, src_url=tricky_path, bare=True, create=True)
62 64 # Repo should have been created and thus used correct quoting for clone
63 65 self.assertTrue(successfully_cloned2._repo.bare)
64 66
65 67 # Should pass because URL has been properly quoted
66 68 successfully_cloned.pull(tricky_path_2)
67 69 successfully_cloned2.fetch(tricky_path)
68 70
69 71 def test_repo_create_with_spaces_in_path(self):
70 72 repo_path = get_new_dir("path with spaces")
71 73 repo = GitRepository(repo_path, src_url=None, bare=True, create=True)
72 74 # Repo should have been created
73 75 self.assertTrue(repo._repo.bare)
74 76
75 77 def test_repo_clone(self):
76 78 self.__check_for_existing_repo()
77 79 repo = GitRepository(TEST_GIT_REPO)
78 80 repo_clone = GitRepository(TEST_GIT_REPO_CLONE,
79 81 src_url=TEST_GIT_REPO, create=True, update_after_clone=True)
80 82 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
81 83 # Checking hashes of changesets should be enough
82 84 for changeset in repo.get_changesets():
83 85 raw_id = changeset.raw_id
84 86 self.assertEqual(raw_id, repo_clone.get_changeset(raw_id).raw_id)
85 87
86 88 def test_repo_clone_with_spaces_in_path(self):
87 89 repo_path = get_new_dir("path with spaces")
88 90 successfully_cloned = GitRepository(repo_path, src_url=TEST_GIT_REPO, update_after_clone=True, create=True)
89 91 # Repo should have been created
90 92 self.assertFalse(successfully_cloned._repo.bare)
91 93
92 94 successfully_cloned.pull(TEST_GIT_REPO)
93 95 self.repo.fetch(repo_path)
94 96
95 97 def test_repo_clone_without_create(self):
96 98 self.assertRaises(RepositoryError, GitRepository,
97 99 TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO)
98 100
99 101 def test_repo_clone_with_update(self):
100 102 repo = GitRepository(TEST_GIT_REPO)
101 103 clone_path = TEST_GIT_REPO_CLONE + '_with_update'
102 104 repo_clone = GitRepository(clone_path,
103 105 create=True, src_url=TEST_GIT_REPO, update_after_clone=True)
104 106 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
105 107
106 108 #check if current workdir was updated
107 109 fpath = os.path.join(clone_path, 'MANIFEST.in')
108 110 self.assertEqual(True, os.path.isfile(fpath),
109 111 'Repo was cloned and updated but file %s could not be found'
110 112 % fpath)
111 113
112 114 def test_repo_clone_without_update(self):
113 115 repo = GitRepository(TEST_GIT_REPO)
114 116 clone_path = TEST_GIT_REPO_CLONE + '_without_update'
115 117 repo_clone = GitRepository(clone_path,
116 118 create=True, src_url=TEST_GIT_REPO, update_after_clone=False)
117 119 self.assertEqual(len(repo.revisions), len(repo_clone.revisions))
118 120 #check if current workdir was *NOT* updated
119 121 fpath = os.path.join(clone_path, 'MANIFEST.in')
120 122 # Make sure it's not bare repo
121 123 self.assertFalse(repo_clone._repo.bare)
122 124 self.assertEqual(False, os.path.isfile(fpath),
123 125 'Repo was cloned and updated but file %s was found'
124 126 % fpath)
125 127
126 128 def test_repo_clone_into_bare_repo(self):
127 129 repo = GitRepository(TEST_GIT_REPO)
128 130 clone_path = TEST_GIT_REPO_CLONE + '_bare.git'
129 131 repo_clone = GitRepository(clone_path, create=True,
130 132 src_url=repo.path, bare=True)
131 133 self.assertTrue(repo_clone._repo.bare)
132 134
133 135 def test_create_repo_is_not_bare_by_default(self):
134 136 repo = GitRepository(get_new_dir('not-bare-by-default'), create=True)
135 137 self.assertFalse(repo._repo.bare)
136 138
137 139 def test_create_bare_repo(self):
138 140 repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True)
139 141 self.assertTrue(repo._repo.bare)
140 142
141 143 def test_revisions(self):
142 144 # there are 112 revisions (by now)
143 145 # so we can assume they would be available from now on
144 146 subset = set([
145 147 'c1214f7e79e02fc37156ff215cd71275450cffc3',
146 148 '38b5fe81f109cb111f549bfe9bb6b267e10bc557',
147 149 'fa6600f6848800641328adbf7811fd2372c02ab2',
148 150 '102607b09cdd60e2793929c4f90478be29f85a17',
149 151 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
150 152 '2d1028c054665b962fa3d307adfc923ddd528038',
151 153 'd7e0d30fbcae12c90680eb095a4f5f02505ce501',
152 154 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
153 155 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
154 156 '8430a588b43b5d6da365400117c89400326e7992',
155 157 'd955cd312c17b02143c04fa1099a352b04368118',
156 158 'f67b87e5c629c2ee0ba58f85197e423ff28d735b',
157 159 'add63e382e4aabc9e1afdc4bdc24506c269b7618',
158 160 'f298fe1189f1b69779a4423f40b48edf92a703fc',
159 161 'bd9b619eb41994cac43d67cf4ccc8399c1125808',
160 162 '6e125e7c890379446e98980d8ed60fba87d0f6d1',
161 163 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd',
162 164 '0b05e4ed56c802098dfc813cbe779b2f49e92500',
163 165 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
164 166 '45223f8f114c64bf4d6f853e3c35a369a6305520',
165 167 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
166 168 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68',
167 169 '27d48942240f5b91dfda77accd2caac94708cc7d',
168 170 '622f0eb0bafd619d2560c26f80f09e3b0b0d78af',
169 171 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'])
170 172 self.assertTrue(subset.issubset(set(self.repo.revisions)))
171 173
172 174
173 175
174 176 def test_slicing(self):
175 177 #4 1 5 10 95
176 178 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
177 179 (10, 20, 10), (5, 100, 95)]:
178 180 revs = list(self.repo[sfrom:sto])
179 181 self.assertEqual(len(revs), size)
180 182 self.assertEqual(revs[0], self.repo.get_changeset(sfrom))
181 183 self.assertEqual(revs[-1], self.repo.get_changeset(sto - 1))
182 184
183 185
184 186 def test_branches(self):
185 187 # TODO: Need more tests here
186 188 # Removed (those are 'remotes' branches for cloned repo)
187 189 #self.assertTrue('master' in self.repo.branches)
188 190 #self.assertTrue('gittree' in self.repo.branches)
189 191 #self.assertTrue('web-branch' in self.repo.branches)
190 192 for name, id in self.repo.branches.items():
191 193 self.assertTrue(isinstance(
192 194 self.repo.get_changeset(id), GitChangeset))
193 195
194 196 def test_tags(self):
195 197 # TODO: Need more tests here
196 198 self.assertTrue('v0.1.1' in self.repo.tags)
197 199 self.assertTrue('v0.1.2' in self.repo.tags)
198 200 for name, id in self.repo.tags.items():
199 201 self.assertTrue(isinstance(
200 202 self.repo.get_changeset(id), GitChangeset))
201 203
202 204 def _test_single_changeset_cache(self, revision):
203 205 chset = self.repo.get_changeset(revision)
204 206 self.assertTrue(revision in self.repo.changesets)
205 207 self.assertTrue(chset is self.repo.changesets[revision])
206 208
207 209 def test_initial_changeset(self):
208 210 id = self.repo.revisions[0]
209 211 init_chset = self.repo.get_changeset(id)
210 212 self.assertEqual(init_chset.message, 'initial import\n')
211 213 self.assertEqual(init_chset.author,
212 214 'Marcin Kuzminski <marcin@python-blog.com>')
213 215 for path in ('vcs/__init__.py',
214 216 'vcs/backends/BaseRepository.py',
215 217 'vcs/backends/__init__.py'):
216 218 self.assertTrue(isinstance(init_chset.get_node(path), FileNode))
217 219 for path in ('', 'vcs', 'vcs/backends'):
218 220 self.assertTrue(isinstance(init_chset.get_node(path), DirNode))
219 221
220 222 self.assertRaises(NodeDoesNotExistError, init_chset.get_node, path='foobar')
221 223
222 224 node = init_chset.get_node('vcs/')
223 225 self.assertTrue(hasattr(node, 'kind'))
224 226 self.assertEqual(node.kind, NodeKind.DIR)
225 227
226 228 node = init_chset.get_node('vcs')
227 229 self.assertTrue(hasattr(node, 'kind'))
228 230 self.assertEqual(node.kind, NodeKind.DIR)
229 231
230 232 node = init_chset.get_node('vcs/__init__.py')
231 233 self.assertTrue(hasattr(node, 'kind'))
232 234 self.assertEqual(node.kind, NodeKind.FILE)
233 235
234 236 def test_not_existing_changeset(self):
235 237 self.assertRaises(RepositoryError, self.repo.get_changeset,
236 238 'f' * 40)
237 239
238 240 def test_changeset10(self):
239 241
240 242 chset10 = self.repo.get_changeset(self.repo.revisions[9])
241 243 README = """===
242 244 VCS
243 245 ===
244 246
245 247 Various Version Control System management abstraction layer for Python.
246 248
247 249 Introduction
248 250 ------------
249 251
250 252 TODO: To be written...
251 253
252 254 """
253 255 node = chset10.get_node('README.rst')
254 256 self.assertEqual(node.kind, NodeKind.FILE)
255 257 self.assertEqual(node.content, README)
256 258
257 259
258 260 class GitChangesetTest(unittest.TestCase):
259 261
260 262 def setUp(self):
261 263 self.repo = GitRepository(TEST_GIT_REPO)
262 264
263 265 def test_default_changeset(self):
264 266 tip = self.repo.get_changeset()
265 267 self.assertEqual(tip, self.repo.get_changeset(None))
266 268 self.assertEqual(tip, self.repo.get_changeset('tip'))
267 269
268 270 def test_root_node(self):
269 271 tip = self.repo.get_changeset()
270 272 self.assertTrue(tip.root is tip.get_node(''))
271 273
272 274 def test_lazy_fetch(self):
273 275 """
274 276 Test if changeset's nodes expands and are cached as we walk through
275 277 the revision. This test is somewhat hard to write as order of tests
276 278 is a key here. Written by running command after command in a shell.
277 279 """
278 280 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
279 281 self.assertTrue(hex in self.repo.revisions)
280 282 chset = self.repo.get_changeset(hex)
281 283 self.assertTrue(len(chset.nodes) == 0)
282 284 root = chset.root
283 285 self.assertTrue(len(chset.nodes) == 1)
284 286 self.assertTrue(len(root.nodes) == 8)
285 287 # accessing root.nodes updates chset.nodes
286 288 self.assertTrue(len(chset.nodes) == 9)
287 289
288 290 docs = root.get_node('docs')
289 291 # we haven't yet accessed anything new as docs dir was already cached
290 292 self.assertTrue(len(chset.nodes) == 9)
291 293 self.assertTrue(len(docs.nodes) == 8)
292 294 # accessing docs.nodes updates chset.nodes
293 295 self.assertTrue(len(chset.nodes) == 17)
294 296
295 297 self.assertTrue(docs is chset.get_node('docs'))
296 298 self.assertTrue(docs is root.nodes[0])
297 299 self.assertTrue(docs is root.dirs[0])
298 300 self.assertTrue(docs is chset.get_node('docs'))
299 301
300 302 def test_nodes_with_changeset(self):
301 303 hex = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
302 304 chset = self.repo.get_changeset(hex)
303 305 root = chset.root
304 306 docs = root.get_node('docs')
305 307 self.assertTrue(docs is chset.get_node('docs'))
306 308 api = docs.get_node('api')
307 309 self.assertTrue(api is chset.get_node('docs/api'))
308 310 index = api.get_node('index.rst')
309 311 self.assertTrue(index is chset.get_node('docs/api/index.rst'))
310 312 self.assertTrue(index is chset.get_node('docs') \
311 313 .get_node('api') \
312 314 .get_node('index.rst'))
313 315
314 316 def test_branch_and_tags(self):
315 317 """
316 318 rev0 = self.repo.revisions[0]
317 319 chset0 = self.repo.get_changeset(rev0)
318 320 self.assertEqual(chset0.branch, 'master')
319 321 self.assertEqual(chset0.tags, [])
320 322
321 323 rev10 = self.repo.revisions[10]
322 324 chset10 = self.repo.get_changeset(rev10)
323 325 self.assertEqual(chset10.branch, 'master')
324 326 self.assertEqual(chset10.tags, [])
325 327
326 328 rev44 = self.repo.revisions[44]
327 329 chset44 = self.repo.get_changeset(rev44)
328 330 self.assertEqual(chset44.branch, 'web-branch')
329 331
330 332 tip = self.repo.get_changeset('tip')
331 333 self.assertTrue('tip' in tip.tags)
332 334 """
333 335 # Those tests would fail - branches are now going
334 336 # to be changed at main API in order to support git backend
335 337 pass
336 338
337 339 def _test_slices(self, limit, offset):
338 340 count = self.repo.count()
339 341 changesets = self.repo.get_changesets(limit=limit, offset=offset)
340 342 idx = 0
341 343 for changeset in changesets:
342 344 rev = offset + idx
343 345 idx += 1
344 346 rev_id = self.repo.revisions[rev]
345 347 if idx > limit:
346 348 pytest.fail("Exceeded limit already (getting revision %s, "
347 349 "there are %s total revisions, offset=%s, limit=%s)"
348 350 % (rev_id, count, offset, limit))
349 351 self.assertEqual(changeset, self.repo.get_changeset(rev_id))
350 352 result = list(self.repo.get_changesets(limit=limit, offset=offset))
351 353 start = offset
352 354 end = limit and offset + limit or None
353 355 sliced = list(self.repo[start:end])
354 356 pytest.failUnlessEqual(result, sliced,
355 357 msg="Comparison failed for limit=%s, offset=%s"
356 358 "(get_changeset returned: %s and sliced: %s"
357 359 % (limit, offset, result, sliced))
358 360
359 361 def _test_file_size(self, revision, path, size):
360 362 node = self.repo.get_changeset(revision).get_node(path)
361 363 self.assertTrue(node.is_file())
362 364 self.assertEqual(node.size, size)
363 365
364 366 def test_file_size(self):
365 367 to_check = (
366 368 ('c1214f7e79e02fc37156ff215cd71275450cffc3',
367 369 'vcs/backends/BaseRepository.py', 502),
368 370 ('d7e0d30fbcae12c90680eb095a4f5f02505ce501',
369 371 'vcs/backends/hg.py', 854),
370 372 ('6e125e7c890379446e98980d8ed60fba87d0f6d1',
371 373 'setup.py', 1068),
372 374
373 375 ('d955cd312c17b02143c04fa1099a352b04368118',
374 376 'vcs/backends/base.py', 2921),
375 377 ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
376 378 'vcs/backends/base.py', 3936),
377 379 ('f50f42baeed5af6518ef4b0cb2f1423f3851a941',
378 380 'vcs/backends/base.py', 6189),
379 381 )
380 382 for revision, path, size in to_check:
381 383 self._test_file_size(revision, path, size)
382 384
383 385 def test_file_history(self):
384 386 # we can only check if those revisions are present in the history
385 387 # as we cannot update this test every time file is changed
386 388 files = {
387 389 'setup.py': [
388 390 '54386793436c938cff89326944d4c2702340037d',
389 391 '51d254f0ecf5df2ce50c0b115741f4cf13985dab',
390 392 '998ed409c795fec2012b1c0ca054d99888b22090',
391 393 '5e0eb4c47f56564395f76333f319d26c79e2fb09',
392 394 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
393 395 '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e',
394 396 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
395 397 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
396 398 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
397 399 ],
398 400 'vcs/nodes.py': [
399 401 '33fa3223355104431402a888fa77a4e9956feb3e',
400 402 'fa014c12c26d10ba682fadb78f2a11c24c8118e1',
401 403 'e686b958768ee96af8029fe19c6050b1a8dd3b2b',
402 404 'ab5721ca0a081f26bf43d9051e615af2cc99952f',
403 405 'c877b68d18e792a66b7f4c529ea02c8f80801542',
404 406 '4313566d2e417cb382948f8d9d7c765330356054',
405 407 '6c2303a793671e807d1cfc70134c9ca0767d98c2',
406 408 '54386793436c938cff89326944d4c2702340037d',
407 409 '54000345d2e78b03a99d561399e8e548de3f3203',
408 410 '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b',
409 411 '2d03ca750a44440fb5ea8b751176d1f36f8e8f46',
410 412 '2a08b128c206db48c2f0b8f70df060e6db0ae4f8',
411 413 '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b',
412 414 'ac71e9503c2ca95542839af0ce7b64011b72ea7c',
413 415 '12669288fd13adba2a9b7dd5b870cc23ffab92d2',
414 416 '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382',
415 417 '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5',
416 418 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
417 419 'f50f42baeed5af6518ef4b0cb2f1423f3851a941',
418 420 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25',
419 421 'f15c21f97864b4f071cddfbf2750ec2e23859414',
420 422 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade',
421 423 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b',
422 424 '84dec09632a4458f79f50ddbbd155506c460b4f9',
423 425 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
424 426 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
425 427 '3bf1c5868e570e39569d094f922d33ced2fa3b2b',
426 428 'b8d04012574729d2c29886e53b1a43ef16dd00a1',
427 429 '6970b057cffe4aab0a792aa634c89f4bebf01441',
428 430 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
429 431 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
430 432 ],
431 433 'vcs/backends/git.py': [
432 434 '4cf116ad5a457530381135e2f4c453e68a1b0105',
433 435 '9a751d84d8e9408e736329767387f41b36935153',
434 436 'cb681fb539c3faaedbcdf5ca71ca413425c18f01',
435 437 '428f81bb652bcba8d631bce926e8834ff49bdcc6',
436 438 '180ab15aebf26f98f714d8c68715e0f05fa6e1c7',
437 439 '2b8e07312a2e89e92b90426ab97f349f4bce2a3a',
438 440 '50e08c506174d8645a4bb517dd122ac946a0f3bf',
439 441 '54000345d2e78b03a99d561399e8e548de3f3203',
440 442 ],
441 443 }
442 444 for path, revs in files.items():
443 445 node = self.repo.get_changeset(revs[0]).get_node(path)
444 446 node_revs = [chset.raw_id for chset in node.history]
445 447 self.assertTrue(set(revs).issubset(set(node_revs)),
446 448 "We assumed that %s is subset of revisions for which file %s "
447 449 "has been changed, and history of that node returned: %s"
448 450 % (revs, path, node_revs))
449 451
450 452 def test_file_annotate(self):
451 453 files = {
452 454 'vcs/backends/__init__.py': {
453 455 'c1214f7e79e02fc37156ff215cd71275450cffc3': {
454 456 'lines_no': 1,
455 457 'changesets': [
456 458 'c1214f7e79e02fc37156ff215cd71275450cffc3',
457 459 ],
458 460 },
459 461 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': {
460 462 'lines_no': 21,
461 463 'changesets': [
462 464 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
463 465 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
464 466 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
465 467 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
466 468 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
467 469 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
468 470 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
469 471 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
470 472 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
471 473 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
472 474 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
473 475 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
474 476 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
475 477 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
476 478 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
477 479 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
478 480 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
479 481 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
480 482 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
481 483 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
482 484 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
483 485 ],
484 486 },
485 487 'e29b67bd158580fc90fc5e9111240b90e6e86064': {
486 488 'lines_no': 32,
487 489 'changesets': [
488 490 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
489 491 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
490 492 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
491 493 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
492 494 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
493 495 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
494 496 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
495 497 '54000345d2e78b03a99d561399e8e548de3f3203',
496 498 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
497 499 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
498 500 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
499 501 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
500 502 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
501 503 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
502 504 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
503 505 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
504 506 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
505 507 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
506 508 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
507 509 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
508 510 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
509 511 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
510 512 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
511 513 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
512 514 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
513 515 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
514 516 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
515 517 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
516 518 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
517 519 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
518 520 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
519 521 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
520 522 ],
521 523 },
522 524 },
523 525 }
524 526
525 527 for fname, revision_dict in files.items():
526 528 for rev, data in revision_dict.items():
527 529 cs = self.repo.get_changeset(rev)
528 530
529 531 l1_1 = [x[1] for x in cs.get_file_annotate(fname)]
530 532 l1_2 = [x[2]().raw_id for x in cs.get_file_annotate(fname)]
531 533 self.assertEqual(l1_1, l1_2)
532 534 l1 = l1_1
533 535 l2 = files[fname][rev]['changesets']
534 536 self.assertTrue(l1 == l2 , "The lists of revision for %s@rev %s"
535 537 "from annotation list should match each other, "
536 538 "got \n%s \nvs \n%s " % (fname, rev, l1, l2))
537 539
538 540 def test_files_state(self):
539 541 """
540 542 Tests state of FileNodes.
541 543 """
542 544 node = self.repo \
543 545 .get_changeset('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0') \
544 546 .get_node('vcs/utils/diffs.py')
545 547 self.assertTrue(node.state, NodeState.ADDED)
546 548 self.assertTrue(node.added)
547 549 self.assertFalse(node.changed)
548 550 self.assertFalse(node.not_changed)
549 551 self.assertFalse(node.removed)
550 552
551 553 node = self.repo \
552 554 .get_changeset('33fa3223355104431402a888fa77a4e9956feb3e') \
553 555 .get_node('.hgignore')
554 556 self.assertTrue(node.state, NodeState.CHANGED)
555 557 self.assertFalse(node.added)
556 558 self.assertTrue(node.changed)
557 559 self.assertFalse(node.not_changed)
558 560 self.assertFalse(node.removed)
559 561
560 562 node = self.repo \
561 563 .get_changeset('e29b67bd158580fc90fc5e9111240b90e6e86064') \
562 564 .get_node('setup.py')
563 565 self.assertTrue(node.state, NodeState.NOT_CHANGED)
564 566 self.assertFalse(node.added)
565 567 self.assertFalse(node.changed)
566 568 self.assertTrue(node.not_changed)
567 569 self.assertFalse(node.removed)
568 570
569 571 # If node has REMOVED state then trying to fetch it would raise
570 572 # ChangesetError exception
571 573 chset = self.repo.get_changeset(
572 574 'fa6600f6848800641328adbf7811fd2372c02ab2')
573 575 path = 'vcs/backends/BaseRepository.py'
574 576 self.assertRaises(NodeDoesNotExistError, chset.get_node, path)
575 577 # but it would be one of ``removed`` (changeset's attribute)
576 578 self.assertTrue(path in [rf.path for rf in chset.removed])
577 579
578 580 chset = self.repo.get_changeset(
579 581 '54386793436c938cff89326944d4c2702340037d')
580 582 changed = ['setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py',
581 583 'vcs/nodes.py']
582 584 self.assertEqual(set(changed), set([f.path for f in chset.changed]))
583 585
584 586 def test_commit_message_is_unicode(self):
585 587 for cs in self.repo:
586 588 self.assertEqual(type(cs.message), unicode)
587 589
588 590 def test_changeset_author_is_unicode(self):
589 591 for cs in self.repo:
590 592 self.assertEqual(type(cs.author), unicode)
591 593
592 594 def test_repo_files_content_is_unicode(self):
593 595 changeset = self.repo.get_changeset()
594 596 for node in changeset.get_node('/'):
595 597 if node.is_file():
596 598 self.assertEqual(type(node.content), unicode)
597 599
598 600 def test_wrong_path(self):
599 601 # There is 'setup.py' in the root dir but not there:
600 602 path = 'foo/bar/setup.py'
601 603 tip = self.repo.get_changeset()
602 604 self.assertRaises(VCSError, tip.get_node, path)
603 605
604 606 def test_author_email(self):
605 607 self.assertEqual('marcin@python-blog.com',
606 608 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
607 609 .author_email)
608 610 self.assertEqual('lukasz.balcerzak@python-center.pl',
609 611 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
610 612 .author_email)
611 613 self.assertEqual('',
612 614 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
613 615 .author_email)
614 616
615 617 def test_author_username(self):
616 618 self.assertEqual('Marcin Kuzminski',
617 619 self.repo.get_changeset('c1214f7e79e02fc37156ff215cd71275450cffc3') \
618 620 .author_name)
619 621 self.assertEqual('Lukasz Balcerzak',
620 622 self.repo.get_changeset('ff7ca51e58c505fec0dd2491de52c622bb7a806b') \
621 623 .author_name)
622 624 self.assertEqual('marcink none@none',
623 625 self.repo.get_changeset('8430a588b43b5d6da365400117c89400326e7992') \
624 626 .author_name)
625 627
626 628
627 629 class GitSpecificTest(unittest.TestCase):
628 630
629 631 def test_error_is_raised_for_added_if_diff_name_status_is_wrong(self):
630 632 repo = mock.MagicMock()
631 633 changeset = GitChangeset(repo, 'foobar')
632 634 changeset._diff_name_status = 'foobar'
633 635 with self.assertRaises(VCSError):
634 636 changeset.added
635 637
636 638 def test_error_is_raised_for_changed_if_diff_name_status_is_wrong(self):
637 639 repo = mock.MagicMock()
638 640 changeset = GitChangeset(repo, 'foobar')
639 641 changeset._diff_name_status = 'foobar'
640 642 with self.assertRaises(VCSError):
641 643 changeset.added
642 644
643 645 def test_error_is_raised_for_removed_if_diff_name_status_is_wrong(self):
644 646 repo = mock.MagicMock()
645 647 changeset = GitChangeset(repo, 'foobar')
646 648 changeset._diff_name_status = 'foobar'
647 649 with self.assertRaises(VCSError):
648 650 changeset.added
649 651
650 652
651 653 class GitSpecificWithRepoTest(_BackendTestMixin, unittest.TestCase):
652 654 backend_alias = 'git'
653 655
654 656 @classmethod
655 657 def _get_commits(cls):
656 658 return [
657 659 {
658 660 'message': 'Initial',
659 661 'author': 'Joe Doe <joe.doe@example.com>',
660 662 'date': datetime.datetime(2010, 1, 1, 20),
661 663 'added': [
662 664 FileNode('foobar/static/js/admin/base.js', content='base'),
663 665 FileNode('foobar/static/admin', content='admin',
664 666 mode=0120000), # this is a link
665 667 FileNode('foo', content='foo'),
666 668 ],
667 669 },
668 670 {
669 671 'message': 'Second',
670 672 'author': 'Joe Doe <joe.doe@example.com>',
671 673 'date': datetime.datetime(2010, 1, 1, 22),
672 674 'added': [
673 675 FileNode('foo2', content='foo2'),
674 676 ],
675 677 },
676 678 ]
677 679
678 680 def test_paths_slow_traversing(self):
679 681 cs = self.repo.get_changeset()
680 682 self.assertEqual(cs.get_node('foobar').get_node('static').get_node('js')
681 683 .get_node('admin').get_node('base.js').content, 'base')
682 684
683 685 def test_paths_fast_traversing(self):
684 686 cs = self.repo.get_changeset()
685 687 self.assertEqual(cs.get_node('foobar/static/js/admin/base.js').content,
686 688 'base')
687 689
688 690 def test_workdir_get_branch(self):
689 691 self.repo.run_git_command(['checkout', '-b', 'production'])
690 692 # Regression test: one of following would fail if we don't check
691 693 # .git/HEAD file
692 694 self.repo.run_git_command(['checkout', 'production'])
693 695 self.assertEqual(self.repo.workdir.get_branch(), 'production')
694 696 self.repo.run_git_command(['checkout', 'master'])
695 697 self.assertEqual(self.repo.workdir.get_branch(), 'master')
696 698
697 699 def test_get_diff_runs_git_command_with_hashes(self):
698 700 self.repo.run_git_command = mock.Mock(return_value=['', ''])
699 701 self.repo.get_diff(0, 1)
700 702 self.repo.run_git_command.assert_called_once_with(
701 703 ['diff', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
702 704 self.repo._get_revision(0), self.repo._get_revision(1)])
703 705
704 706 def test_get_diff_runs_git_command_with_str_hashes(self):
705 707 self.repo.run_git_command = mock.Mock(return_value=['', ''])
706 708 self.repo.get_diff(self.repo.EMPTY_CHANGESET, 1)
707 709 self.repo.run_git_command.assert_called_once_with(
708 710 ['show', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
709 711 self.repo._get_revision(1)])
710 712
711 713 def test_get_diff_runs_git_command_with_path_if_its_given(self):
712 714 self.repo.run_git_command = mock.Mock(return_value=['', ''])
713 715 self.repo.get_diff(0, 1, 'foo')
714 716 self.repo.run_git_command.assert_called_once_with(
715 717 ['diff', '-U3', '--full-index', '--binary', '-p', '-M', '--abbrev=40',
716 718 self.repo._get_revision(0), self.repo._get_revision(1), '--', 'foo'])
717 719
718 720
719 721 class GitRegressionTest(_BackendTestMixin, unittest.TestCase):
720 722 backend_alias = 'git'
721 723
722 724 @classmethod
723 725 def _get_commits(cls):
724 726 return [
725 727 {
726 728 'message': 'Initial',
727 729 'author': 'Joe Doe <joe.doe@example.com>',
728 730 'date': datetime.datetime(2010, 1, 1, 20),
729 731 'added': [
730 732 FileNode('bot/__init__.py', content='base'),
731 733 FileNode('bot/templates/404.html', content='base'),
732 734 FileNode('bot/templates/500.html', content='base'),
733 735 ],
734 736 },
735 737 {
736 738 'message': 'Second',
737 739 'author': 'Joe Doe <joe.doe@example.com>',
738 740 'date': datetime.datetime(2010, 1, 1, 22),
739 741 'added': [
740 742 FileNode('bot/build/migrations/1.py', content='foo2'),
741 743 FileNode('bot/build/migrations/2.py', content='foo2'),
742 744 FileNode('bot/build/static/templates/f.html', content='foo2'),
743 745 FileNode('bot/build/static/templates/f1.html', content='foo2'),
744 746 FileNode('bot/build/templates/err.html', content='foo2'),
745 747 FileNode('bot/build/templates/err2.html', content='foo2'),
746 748 ],
747 749 },
748 750 ]
749 751
750 752 def test_similar_paths(self):
751 753 cs = self.repo.get_changeset()
752 754 paths = lambda *n:[x.path for x in n]
753 755 self.assertEqual(paths(*cs.get_nodes('bot')), ['bot/build', 'bot/templates', 'bot/__init__.py'])
754 756 self.assertEqual(paths(*cs.get_nodes('bot/build')), ['bot/build/migrations', 'bot/build/static', 'bot/build/templates'])
755 757 self.assertEqual(paths(*cs.get_nodes('bot/build/static')), ['bot/build/static/templates'])
756 758 # this get_nodes below causes troubles !
757 759 self.assertEqual(paths(*cs.get_nodes('bot/build/static/templates')), ['bot/build/static/templates/f.html', 'bot/build/static/templates/f1.html'])
758 760 self.assertEqual(paths(*cs.get_nodes('bot/build/templates')), ['bot/build/templates/err.html', 'bot/build/templates/err2.html'])
759 761 self.assertEqual(paths(*cs.get_nodes('bot/templates/')), ['bot/templates/404.html', 'bot/templates/500.html'])
760 762
761 763
762 764 class GitHooksTest(unittest.TestCase):
763 765 """
764 766 Tests related to hook functionality of Git repositories.
765 767 """
766 768
767 769 def setUp(self):
768 770 # For each run we want a fresh repo.
769 771 self.repo_directory = get_new_dir("githookrepo")
770 772 self.repo = GitRepository(self.repo_directory, create=True)
771 773
772 774 # Create a dictionary where keys are hook names, and values are paths to
773 775 # them. Deduplicates code in tests a bit.
774 776 self.hook_directory = self.repo.get_hook_location()
775 777 self.kallithea_hooks = {h: os.path.join(self.hook_directory, h) for h in ("pre-receive", "post-receive")}
776 778
777 779 def test_hooks_created_if_missing(self):
778 780 """
779 781 Tests if hooks are installed in repository if they are missing.
780 782 """
781 783
782 784 for hook, hook_path in self.kallithea_hooks.iteritems():
783 785 if os.path.exists(hook_path):
784 786 os.remove(hook_path)
785 787
786 788 ScmModel().install_git_hooks(repo=self.repo)
787 789
788 790 for hook, hook_path in self.kallithea_hooks.iteritems():
789 791 self.assertTrue(os.path.exists(hook_path))
790 792
791 793 def test_kallithea_hooks_updated(self):
792 794 """
793 795 Tests if hooks are updated if they are Kallithea hooks already.
794 796 """
795 797
796 798 for hook, hook_path in self.kallithea_hooks.iteritems():
797 799 with open(hook_path, "w") as f:
798 800 f.write("KALLITHEA_HOOK_VER=0.0.0\nJUST_BOGUS")
799 801
800 802 ScmModel().install_git_hooks(repo=self.repo)
801 803
802 804 for hook, hook_path in self.kallithea_hooks.iteritems():
803 805 with open(hook_path) as f:
804 806 self.assertNotIn("JUST_BOGUS", f.read())
805 807
806 808 def test_custom_hooks_untouched(self):
807 809 """
808 810 Tests if hooks are left untouched if they are not Kallithea hooks.
809 811 """
810 812
811 813 for hook, hook_path in self.kallithea_hooks.iteritems():
812 814 with open(hook_path, "w") as f:
813 815 f.write("#!/bin/bash\n#CUSTOM_HOOK")
814 816
815 817 ScmModel().install_git_hooks(repo=self.repo)
816 818
817 819 for hook, hook_path in self.kallithea_hooks.iteritems():
818 820 with open(hook_path) as f:
819 821 self.assertIn("CUSTOM_HOOK", f.read())
820 822
821 823 def test_custom_hooks_forced_update(self):
822 824 """
823 825 Tests if hooks are forcefully updated even though they are custom hooks.
824 826 """
825 827
826 828 for hook, hook_path in self.kallithea_hooks.iteritems():
827 829 with open(hook_path, "w") as f:
828 830 f.write("#!/bin/bash\n#CUSTOM_HOOK")
829 831
830 832 ScmModel().install_git_hooks(repo=self.repo, force_create=True)
831 833
832 834 for hook, hook_path in self.kallithea_hooks.iteritems():
833 835 with open(hook_path) as f:
834 836 self.assertIn("KALLITHEA_HOOK_VER", f.read())
835 837
836 838
837 839 if __name__ == '__main__':
838 840 unittest.main()
General Comments 0
You need to be logged in to leave comments. Login now