##// END OF EJS Templates
tests: fixed tests for new source ref checking.
marcink -
r2473:32635607 default
parent child Browse files
Show More
@@ -1,1271 +1,1289 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import datetime
22 22 import mock
23 23 import os
24 24 import sys
25 25 import shutil
26 26
27 27 import pytest
28 28
29 29 from rhodecode.lib.utils import make_db_config
30 30 from rhodecode.lib.vcs.backends.base import Reference
31 31 from rhodecode.lib.vcs.backends.git import (
32 32 GitRepository, GitCommit, discover_git_version)
33 33 from rhodecode.lib.vcs.exceptions import (
34 34 RepositoryError, VCSError, NodeDoesNotExistError)
35 35 from rhodecode.lib.vcs.nodes import (
36 36 NodeKind, FileNode, DirNode, NodeState, SubModuleNode)
37 37 from rhodecode.tests import TEST_GIT_REPO, TEST_GIT_REPO_CLONE, get_new_dir
38 38 from rhodecode.tests.vcs.conftest import BackendTestMixin
39 39
40 40
41 41 pytestmark = pytest.mark.backends("git")
42 42
43 43
44 44 def repo_path_generator():
45 45 """
46 46 Return a different path to be used for cloning repos.
47 47 """
48 48 i = 0
49 49 while True:
50 50 i += 1
51 51 yield '%s-%d' % (TEST_GIT_REPO_CLONE, i)
52 52
53 53
54 54 REPO_PATH_GENERATOR = repo_path_generator()
55 55
56 56
57 57 class TestGitRepository:
58 58
59 59 # pylint: disable=protected-access
60 60
61 61 def __check_for_existing_repo(self):
62 62 if os.path.exists(TEST_GIT_REPO_CLONE):
63 63 self.fail('Cannot test git clone repo as location %s already '
64 64 'exists. You should manually remove it first.'
65 65 % TEST_GIT_REPO_CLONE)
66 66
67 67 @pytest.fixture(autouse=True)
68 68 def prepare(self, request, baseapp):
69 69 self.repo = GitRepository(TEST_GIT_REPO, bare=True)
70 70
71 71 def get_clone_repo(self):
72 72 """
73 73 Return a non bare clone of the base repo.
74 74 """
75 75 clone_path = next(REPO_PATH_GENERATOR)
76 76 repo_clone = GitRepository(
77 77 clone_path, create=True, src_url=self.repo.path, bare=False)
78 78
79 79 return repo_clone
80 80
81 81 def get_empty_repo(self, bare=False):
82 82 """
83 83 Return a non bare empty repo.
84 84 """
85 85 return GitRepository(next(REPO_PATH_GENERATOR), create=True, bare=bare)
86 86
87 87 def test_wrong_repo_path(self):
88 88 wrong_repo_path = '/tmp/errorrepo_git'
89 89 with pytest.raises(RepositoryError):
90 90 GitRepository(wrong_repo_path)
91 91
92 92 def test_repo_clone(self):
93 93 self.__check_for_existing_repo()
94 94 repo = GitRepository(TEST_GIT_REPO)
95 95 repo_clone = GitRepository(
96 96 TEST_GIT_REPO_CLONE,
97 97 src_url=TEST_GIT_REPO, create=True, update_after_clone=True)
98 98 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
99 99 # Checking hashes of commits should be enough
100 100 for commit in repo.get_commits():
101 101 raw_id = commit.raw_id
102 102 assert raw_id == repo_clone.get_commit(raw_id).raw_id
103 103
104 104 def test_repo_clone_without_create(self):
105 105 with pytest.raises(RepositoryError):
106 106 GitRepository(
107 107 TEST_GIT_REPO_CLONE + '_wo_create', src_url=TEST_GIT_REPO)
108 108
109 109 def test_repo_clone_with_update(self):
110 110 repo = GitRepository(TEST_GIT_REPO)
111 111 clone_path = TEST_GIT_REPO_CLONE + '_with_update'
112 112 repo_clone = GitRepository(
113 113 clone_path,
114 114 create=True, src_url=TEST_GIT_REPO, update_after_clone=True)
115 115 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
116 116
117 117 # check if current workdir was updated
118 118 fpath = os.path.join(clone_path, 'MANIFEST.in')
119 119 assert os.path.isfile(fpath)
120 120
121 121 def test_repo_clone_without_update(self):
122 122 repo = GitRepository(TEST_GIT_REPO)
123 123 clone_path = TEST_GIT_REPO_CLONE + '_without_update'
124 124 repo_clone = GitRepository(
125 125 clone_path,
126 126 create=True, src_url=TEST_GIT_REPO, update_after_clone=False)
127 127 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
128 128 # check if current workdir was *NOT* updated
129 129 fpath = os.path.join(clone_path, 'MANIFEST.in')
130 130 # Make sure it's not bare repo
131 131 assert not repo_clone.bare
132 132 assert not os.path.isfile(fpath)
133 133
134 134 def test_repo_clone_into_bare_repo(self):
135 135 repo = GitRepository(TEST_GIT_REPO)
136 136 clone_path = TEST_GIT_REPO_CLONE + '_bare.git'
137 137 repo_clone = GitRepository(
138 138 clone_path, create=True, src_url=repo.path, bare=True)
139 139 assert repo_clone.bare
140 140
141 141 def test_create_repo_is_not_bare_by_default(self):
142 142 repo = GitRepository(get_new_dir('not-bare-by-default'), create=True)
143 143 assert not repo.bare
144 144
145 145 def test_create_bare_repo(self):
146 146 repo = GitRepository(get_new_dir('bare-repo'), create=True, bare=True)
147 147 assert repo.bare
148 148
149 149 def test_update_server_info(self):
150 150 self.repo._update_server_info()
151 151
152 152 def test_fetch(self, vcsbackend_git):
153 153 # Note: This is a git specific part of the API, it's only implemented
154 154 # by the git backend.
155 155 source_repo = vcsbackend_git.repo
156 156 target_repo = vcsbackend_git.create_repo()
157 157 target_repo.fetch(source_repo.path)
158 158 # Note: Get a fresh instance, avoids caching trouble
159 159 target_repo = vcsbackend_git.backend(target_repo.path)
160 160 assert len(source_repo.commit_ids) == len(target_repo.commit_ids)
161 161
162 162 def test_commit_ids(self):
163 163 # there are 112 commits (by now)
164 164 # so we can assume they would be available from now on
165 165 subset = set([
166 166 'c1214f7e79e02fc37156ff215cd71275450cffc3',
167 167 '38b5fe81f109cb111f549bfe9bb6b267e10bc557',
168 168 'fa6600f6848800641328adbf7811fd2372c02ab2',
169 169 '102607b09cdd60e2793929c4f90478be29f85a17',
170 170 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
171 171 '2d1028c054665b962fa3d307adfc923ddd528038',
172 172 'd7e0d30fbcae12c90680eb095a4f5f02505ce501',
173 173 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
174 174 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
175 175 '8430a588b43b5d6da365400117c89400326e7992',
176 176 'd955cd312c17b02143c04fa1099a352b04368118',
177 177 'f67b87e5c629c2ee0ba58f85197e423ff28d735b',
178 178 'add63e382e4aabc9e1afdc4bdc24506c269b7618',
179 179 'f298fe1189f1b69779a4423f40b48edf92a703fc',
180 180 'bd9b619eb41994cac43d67cf4ccc8399c1125808',
181 181 '6e125e7c890379446e98980d8ed60fba87d0f6d1',
182 182 'd4a54db9f745dfeba6933bf5b1e79e15d0af20bd',
183 183 '0b05e4ed56c802098dfc813cbe779b2f49e92500',
184 184 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
185 185 '45223f8f114c64bf4d6f853e3c35a369a6305520',
186 186 'ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
187 187 'f5ea29fc42ef67a2a5a7aecff10e1566699acd68',
188 188 '27d48942240f5b91dfda77accd2caac94708cc7d',
189 189 '622f0eb0bafd619d2560c26f80f09e3b0b0d78af',
190 190 'e686b958768ee96af8029fe19c6050b1a8dd3b2b'])
191 191 assert subset.issubset(set(self.repo.commit_ids))
192 192
193 193 def test_slicing(self):
194 194 # 4 1 5 10 95
195 195 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
196 196 (10, 20, 10), (5, 100, 95)]:
197 197 commit_ids = list(self.repo[sfrom:sto])
198 198 assert len(commit_ids) == size
199 199 assert commit_ids[0] == self.repo.get_commit(commit_idx=sfrom)
200 200 assert commit_ids[-1] == self.repo.get_commit(commit_idx=sto - 1)
201 201
202 202 def test_branches(self):
203 203 # TODO: Need more tests here
204 204 # Removed (those are 'remotes' branches for cloned repo)
205 205 # assert 'master' in self.repo.branches
206 206 # assert 'gittree' in self.repo.branches
207 207 # assert 'web-branch' in self.repo.branches
208 208 for __, commit_id in self.repo.branches.items():
209 209 assert isinstance(self.repo.get_commit(commit_id), GitCommit)
210 210
211 211 def test_tags(self):
212 212 # TODO: Need more tests here
213 213 assert 'v0.1.1' in self.repo.tags
214 214 assert 'v0.1.2' in self.repo.tags
215 215 for __, commit_id in self.repo.tags.items():
216 216 assert isinstance(self.repo.get_commit(commit_id), GitCommit)
217 217
218 218 def _test_single_commit_cache(self, commit_id):
219 219 commit = self.repo.get_commit(commit_id)
220 220 assert commit_id in self.repo.commits
221 221 assert commit is self.repo.commits[commit_id]
222 222
223 223 def test_initial_commit(self):
224 224 commit_id = self.repo.commit_ids[0]
225 225 init_commit = self.repo.get_commit(commit_id)
226 226 init_author = init_commit.author
227 227
228 228 assert init_commit.message == 'initial import\n'
229 229 assert init_author == 'Marcin Kuzminski <marcin@python-blog.com>'
230 230 assert init_author == init_commit.committer
231 231 for path in ('vcs/__init__.py',
232 232 'vcs/backends/BaseRepository.py',
233 233 'vcs/backends/__init__.py'):
234 234 assert isinstance(init_commit.get_node(path), FileNode)
235 235 for path in ('', 'vcs', 'vcs/backends'):
236 236 assert isinstance(init_commit.get_node(path), DirNode)
237 237
238 238 with pytest.raises(NodeDoesNotExistError):
239 239 init_commit.get_node(path='foobar')
240 240
241 241 node = init_commit.get_node('vcs/')
242 242 assert hasattr(node, 'kind')
243 243 assert node.kind == NodeKind.DIR
244 244
245 245 node = init_commit.get_node('vcs')
246 246 assert hasattr(node, 'kind')
247 247 assert node.kind == NodeKind.DIR
248 248
249 249 node = init_commit.get_node('vcs/__init__.py')
250 250 assert hasattr(node, 'kind')
251 251 assert node.kind == NodeKind.FILE
252 252
253 253 def test_not_existing_commit(self):
254 254 with pytest.raises(RepositoryError):
255 255 self.repo.get_commit('f' * 40)
256 256
257 257 def test_commit10(self):
258 258
259 259 commit10 = self.repo.get_commit(self.repo.commit_ids[9])
260 260 README = """===
261 261 VCS
262 262 ===
263 263
264 264 Various Version Control System management abstraction layer for Python.
265 265
266 266 Introduction
267 267 ------------
268 268
269 269 TODO: To be written...
270 270
271 271 """
272 272 node = commit10.get_node('README.rst')
273 273 assert node.kind == NodeKind.FILE
274 274 assert node.content == README
275 275
276 276 def test_head(self):
277 277 assert self.repo.head == self.repo.get_commit().raw_id
278 278
279 279 def test_checkout_with_create(self):
280 280 repo_clone = self.get_clone_repo()
281 281
282 282 new_branch = 'new_branch'
283 283 assert repo_clone._current_branch() == 'master'
284 284 assert set(repo_clone.branches) == set(('master',))
285 285 repo_clone._checkout(new_branch, create=True)
286 286
287 287 # Branches is a lazy property so we need to recrete the Repo object.
288 288 repo_clone = GitRepository(repo_clone.path)
289 289 assert set(repo_clone.branches) == set(('master', new_branch))
290 290 assert repo_clone._current_branch() == new_branch
291 291
292 292 def test_checkout(self):
293 293 repo_clone = self.get_clone_repo()
294 294
295 295 repo_clone._checkout('new_branch', create=True)
296 296 repo_clone._checkout('master')
297 297
298 298 assert repo_clone._current_branch() == 'master'
299 299
300 300 def test_checkout_same_branch(self):
301 301 repo_clone = self.get_clone_repo()
302 302
303 303 repo_clone._checkout('master')
304 304 assert repo_clone._current_branch() == 'master'
305 305
306 306 def test_checkout_branch_already_exists(self):
307 307 repo_clone = self.get_clone_repo()
308 308
309 309 with pytest.raises(RepositoryError):
310 310 repo_clone._checkout('master', create=True)
311 311
312 312 def test_checkout_bare_repo(self):
313 313 with pytest.raises(RepositoryError):
314 314 self.repo._checkout('master')
315 315
316 316 def test_current_branch_bare_repo(self):
317 317 with pytest.raises(RepositoryError):
318 318 self.repo._current_branch()
319 319
320 320 def test_current_branch_empty_repo(self):
321 321 repo = self.get_empty_repo()
322 322 assert repo._current_branch() is None
323 323
324 324 def test_local_clone(self):
325 325 clone_path = next(REPO_PATH_GENERATOR)
326 326 self.repo._local_clone(clone_path, 'master')
327 327 repo_clone = GitRepository(clone_path)
328 328
329 329 assert self.repo.commit_ids == repo_clone.commit_ids
330 330
331 331 def test_local_clone_with_specific_branch(self):
332 332 source_repo = self.get_clone_repo()
333 333
334 334 # Create a new branch in source repo
335 335 new_branch_commit = source_repo.commit_ids[-3]
336 336 source_repo._checkout(new_branch_commit)
337 337 source_repo._checkout('new_branch', create=True)
338 338
339 339 clone_path = next(REPO_PATH_GENERATOR)
340 340 source_repo._local_clone(clone_path, 'new_branch')
341 341 repo_clone = GitRepository(clone_path)
342 342
343 343 assert source_repo.commit_ids[:-3 + 1] == repo_clone.commit_ids
344 344
345 345 clone_path = next(REPO_PATH_GENERATOR)
346 346 source_repo._local_clone(clone_path, 'master')
347 347 repo_clone = GitRepository(clone_path)
348 348
349 349 assert source_repo.commit_ids == repo_clone.commit_ids
350 350
351 351 def test_local_clone_fails_if_target_exists(self):
352 352 with pytest.raises(RepositoryError):
353 353 self.repo._local_clone(self.repo.path, 'master')
354 354
355 355 def test_local_fetch(self):
356 356 target_repo = self.get_empty_repo()
357 357 source_repo = self.get_clone_repo()
358 358
359 359 # Create a new branch in source repo
360 360 master_commit = source_repo.commit_ids[-1]
361 361 new_branch_commit = source_repo.commit_ids[-3]
362 362 source_repo._checkout(new_branch_commit)
363 363 source_repo._checkout('new_branch', create=True)
364 364
365 365 target_repo._local_fetch(source_repo.path, 'new_branch')
366 366 assert target_repo._last_fetch_heads() == [new_branch_commit]
367 367
368 368 target_repo._local_fetch(source_repo.path, 'master')
369 369 assert target_repo._last_fetch_heads() == [master_commit]
370 370
371 371 def test_local_fetch_from_bare_repo(self):
372 372 target_repo = self.get_empty_repo()
373 373 target_repo._local_fetch(self.repo.path, 'master')
374 374
375 375 master_commit = self.repo.commit_ids[-1]
376 376 assert target_repo._last_fetch_heads() == [master_commit]
377 377
378 378 def test_local_fetch_from_same_repo(self):
379 379 with pytest.raises(ValueError):
380 380 self.repo._local_fetch(self.repo.path, 'master')
381 381
382 382 def test_local_fetch_branch_does_not_exist(self):
383 383 target_repo = self.get_empty_repo()
384 384
385 385 with pytest.raises(RepositoryError):
386 386 target_repo._local_fetch(self.repo.path, 'new_branch')
387 387
388 388 def test_local_pull(self):
389 389 target_repo = self.get_empty_repo()
390 390 source_repo = self.get_clone_repo()
391 391
392 392 # Create a new branch in source repo
393 393 master_commit = source_repo.commit_ids[-1]
394 394 new_branch_commit = source_repo.commit_ids[-3]
395 395 source_repo._checkout(new_branch_commit)
396 396 source_repo._checkout('new_branch', create=True)
397 397
398 398 target_repo._local_pull(source_repo.path, 'new_branch')
399 399 target_repo = GitRepository(target_repo.path)
400 400 assert target_repo.head == new_branch_commit
401 401
402 402 target_repo._local_pull(source_repo.path, 'master')
403 403 target_repo = GitRepository(target_repo.path)
404 404 assert target_repo.head == master_commit
405 405
406 406 def test_local_pull_in_bare_repo(self):
407 407 with pytest.raises(RepositoryError):
408 408 self.repo._local_pull(self.repo.path, 'master')
409 409
410 410 def test_local_merge(self):
411 411 target_repo = self.get_empty_repo()
412 412 source_repo = self.get_clone_repo()
413 413
414 414 # Create a new branch in source repo
415 415 master_commit = source_repo.commit_ids[-1]
416 416 new_branch_commit = source_repo.commit_ids[-3]
417 417 source_repo._checkout(new_branch_commit)
418 418 source_repo._checkout('new_branch', create=True)
419 419
420 420 # This is required as one cannot do a -ff-only merge in an empty repo.
421 421 target_repo._local_pull(source_repo.path, 'new_branch')
422 422
423 423 target_repo._local_fetch(source_repo.path, 'master')
424 424 merge_message = 'Merge message\n\nDescription:...'
425 425 user_name = 'Albert Einstein'
426 426 user_email = 'albert@einstein.com'
427 427 target_repo._local_merge(merge_message, user_name, user_email,
428 428 target_repo._last_fetch_heads())
429 429
430 430 target_repo = GitRepository(target_repo.path)
431 431 assert target_repo.commit_ids[-2] == master_commit
432 432 last_commit = target_repo.get_commit(target_repo.head)
433 433 assert last_commit.message.strip() == merge_message
434 434 assert last_commit.author == '%s <%s>' % (user_name, user_email)
435 435
436 436 assert not os.path.exists(
437 437 os.path.join(target_repo.path, '.git', 'MERGE_HEAD'))
438 438
439 439 def test_local_merge_raises_exception_on_conflict(self, vcsbackend_git):
440 440 target_repo = vcsbackend_git.create_repo(number_of_commits=1)
441 441 vcsbackend_git.ensure_file('README', 'I will conflict with you!!!')
442 442
443 443 target_repo._local_fetch(self.repo.path, 'master')
444 444 with pytest.raises(RepositoryError):
445 445 target_repo._local_merge(
446 446 'merge_message', 'user name', 'user@name.com',
447 447 target_repo._last_fetch_heads())
448 448
449 449 # Check we are not left in an intermediate merge state
450 450 assert not os.path.exists(
451 451 os.path.join(target_repo.path, '.git', 'MERGE_HEAD'))
452 452
453 453 def test_local_merge_into_empty_repo(self):
454 454 target_repo = self.get_empty_repo()
455 455
456 456 # This is required as one cannot do a -ff-only merge in an empty repo.
457 457 target_repo._local_fetch(self.repo.path, 'master')
458 458 with pytest.raises(RepositoryError):
459 459 target_repo._local_merge(
460 460 'merge_message', 'user name', 'user@name.com',
461 461 target_repo._last_fetch_heads())
462 462
463 463 def test_local_merge_in_bare_repo(self):
464 464 with pytest.raises(RepositoryError):
465 465 self.repo._local_merge(
466 466 'merge_message', 'user name', 'user@name.com', None)
467 467
468 468 def test_local_push_non_bare(self):
469 469 target_repo = self.get_empty_repo()
470 470
471 471 pushed_branch = 'pushed_branch'
472 472 self.repo._local_push('master', target_repo.path, pushed_branch)
473 473 # Fix the HEAD of the target repo, or otherwise GitRepository won't
474 474 # report any branches.
475 475 with open(os.path.join(target_repo.path, '.git', 'HEAD'), 'w') as f:
476 476 f.write('ref: refs/heads/%s' % pushed_branch)
477 477
478 478 target_repo = GitRepository(target_repo.path)
479 479
480 480 assert (target_repo.branches[pushed_branch] ==
481 481 self.repo.branches['master'])
482 482
483 483 def test_local_push_bare(self):
484 484 target_repo = self.get_empty_repo(bare=True)
485 485
486 486 pushed_branch = 'pushed_branch'
487 487 self.repo._local_push('master', target_repo.path, pushed_branch)
488 488 # Fix the HEAD of the target repo, or otherwise GitRepository won't
489 489 # report any branches.
490 490 with open(os.path.join(target_repo.path, 'HEAD'), 'w') as f:
491 491 f.write('ref: refs/heads/%s' % pushed_branch)
492 492
493 493 target_repo = GitRepository(target_repo.path)
494 494
495 495 assert (target_repo.branches[pushed_branch] ==
496 496 self.repo.branches['master'])
497 497
498 498 def test_local_push_non_bare_target_branch_is_checked_out(self):
499 499 target_repo = self.get_clone_repo()
500 500
501 501 pushed_branch = 'pushed_branch'
502 502 # Create a new branch in source repo
503 503 new_branch_commit = target_repo.commit_ids[-3]
504 504 target_repo._checkout(new_branch_commit)
505 505 target_repo._checkout(pushed_branch, create=True)
506 506
507 507 self.repo._local_push('master', target_repo.path, pushed_branch)
508 508
509 509 target_repo = GitRepository(target_repo.path)
510 510
511 511 assert (target_repo.branches[pushed_branch] ==
512 512 self.repo.branches['master'])
513 513
514 514 def test_local_push_raises_exception_on_conflict(self, vcsbackend_git):
515 515 target_repo = vcsbackend_git.create_repo(number_of_commits=1)
516 516 with pytest.raises(RepositoryError):
517 517 self.repo._local_push('master', target_repo.path, 'master')
518 518
519 519 def test_hooks_can_be_enabled_via_env_variable_for_local_push(self):
520 520 target_repo = self.get_empty_repo(bare=True)
521 521
522 522 with mock.patch.object(self.repo, 'run_git_command') as run_mock:
523 523 self.repo._local_push(
524 524 'master', target_repo.path, 'master', enable_hooks=True)
525 525 env = run_mock.call_args[1]['extra_env']
526 526 assert 'RC_SKIP_HOOKS' not in env
527 527
528 528 def _add_failing_hook(self, repo_path, hook_name, bare=False):
529 529 path_components = (
530 530 ['hooks', hook_name] if bare else ['.git', 'hooks', hook_name])
531 531 hook_path = os.path.join(repo_path, *path_components)
532 532 with open(hook_path, 'w') as f:
533 533 script_lines = [
534 534 '#!%s' % sys.executable,
535 535 'import os',
536 536 'import sys',
537 537 'if os.environ.get("RC_SKIP_HOOKS"):',
538 538 ' sys.exit(0)',
539 539 'sys.exit(1)',
540 540 ]
541 541 f.write('\n'.join(script_lines))
542 542 os.chmod(hook_path, 0755)
543 543
544 544 def test_local_push_does_not_execute_hook(self):
545 545 target_repo = self.get_empty_repo()
546 546
547 547 pushed_branch = 'pushed_branch'
548 548 self._add_failing_hook(target_repo.path, 'pre-receive')
549 549 self.repo._local_push('master', target_repo.path, pushed_branch)
550 550 # Fix the HEAD of the target repo, or otherwise GitRepository won't
551 551 # report any branches.
552 552 with open(os.path.join(target_repo.path, '.git', 'HEAD'), 'w') as f:
553 553 f.write('ref: refs/heads/%s' % pushed_branch)
554 554
555 555 target_repo = GitRepository(target_repo.path)
556 556
557 557 assert (target_repo.branches[pushed_branch] ==
558 558 self.repo.branches['master'])
559 559
560 560 def test_local_push_executes_hook(self):
561 561 target_repo = self.get_empty_repo(bare=True)
562 562 self._add_failing_hook(target_repo.path, 'pre-receive', bare=True)
563 563 with pytest.raises(RepositoryError):
564 564 self.repo._local_push(
565 565 'master', target_repo.path, 'master', enable_hooks=True)
566 566
567 567 def test_maybe_prepare_merge_workspace(self):
568 568 workspace = self.repo._maybe_prepare_merge_workspace(
569 'pr2', Reference('branch', 'master', 'unused'))
569 'pr2', Reference('branch', 'master', 'unused'),
570 Reference('branch', 'master', 'unused'))
570 571
571 572 assert os.path.isdir(workspace)
572 573 workspace_repo = GitRepository(workspace)
573 574 assert workspace_repo.branches == self.repo.branches
574 575
575 576 # Calling it a second time should also succeed
576 577 workspace = self.repo._maybe_prepare_merge_workspace(
577 'pr2', Reference('branch', 'master', 'unused'))
578 'pr2', Reference('branch', 'master', 'unused'),
579 Reference('branch', 'master', 'unused'))
580 assert os.path.isdir(workspace)
581
582 def test_maybe_prepare_merge_workspace_different_refs(self):
583 workspace = self.repo._maybe_prepare_merge_workspace(
584 'pr2', Reference('branch', 'master', 'unused'),
585 Reference('branch', 'develop', 'unused'))
586
587 assert os.path.isdir(workspace)
588 workspace_repo = GitRepository(workspace)
589 assert workspace_repo.branches == self.repo.branches
590
591 # Calling it a second time should also succeed
592 workspace = self.repo._maybe_prepare_merge_workspace(
593 'pr2', Reference('branch', 'master', 'unused'),
594 Reference('branch', 'develop', 'unused'))
578 595 assert os.path.isdir(workspace)
579 596
580 597 def test_cleanup_merge_workspace(self):
581 598 workspace = self.repo._maybe_prepare_merge_workspace(
582 'pr3', Reference('branch', 'master', 'unused'))
599 'pr3', Reference('branch', 'master', 'unused'),
600 Reference('branch', 'master', 'unused'))
583 601 self.repo.cleanup_merge_workspace('pr3')
584 602
585 603 assert not os.path.exists(workspace)
586 604
587 605 def test_cleanup_merge_workspace_invalid_workspace_id(self):
588 606 # No assert: because in case of an inexistent workspace this function
589 607 # should still succeed.
590 608 self.repo.cleanup_merge_workspace('pr4')
591 609
592 610 def test_set_refs(self):
593 611 test_ref = 'refs/test-refs/abcde'
594 612 test_commit_id = 'ecb86e1f424f2608262b130db174a7dfd25a6623'
595 613
596 614 self.repo.set_refs(test_ref, test_commit_id)
597 615 stdout, _ = self.repo.run_git_command(['show-ref'])
598 616 assert test_ref in stdout
599 617 assert test_commit_id in stdout
600 618
601 619 def test_remove_ref(self):
602 620 test_ref = 'refs/test-refs/abcde'
603 621 test_commit_id = 'ecb86e1f424f2608262b130db174a7dfd25a6623'
604 622 self.repo.set_refs(test_ref, test_commit_id)
605 623 stdout, _ = self.repo.run_git_command(['show-ref'])
606 624 assert test_ref in stdout
607 625 assert test_commit_id in stdout
608 626
609 627 self.repo.remove_ref(test_ref)
610 628 stdout, _ = self.repo.run_git_command(['show-ref'])
611 629 assert test_ref not in stdout
612 630 assert test_commit_id not in stdout
613 631
614 632
615 633 class TestGitCommit(object):
616 634
617 635 @pytest.fixture(autouse=True)
618 636 def prepare(self):
619 637 self.repo = GitRepository(TEST_GIT_REPO)
620 638
621 639 def test_default_commit(self):
622 640 tip = self.repo.get_commit()
623 641 assert tip == self.repo.get_commit(None)
624 642 assert tip == self.repo.get_commit('tip')
625 643
626 644 def test_root_node(self):
627 645 tip = self.repo.get_commit()
628 646 assert tip.root is tip.get_node('')
629 647
630 648 def test_lazy_fetch(self):
631 649 """
632 650 Test if commit's nodes expands and are cached as we walk through
633 651 the commit. This test is somewhat hard to write as order of tests
634 652 is a key here. Written by running command after command in a shell.
635 653 """
636 654 commit_id = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
637 655 assert commit_id in self.repo.commit_ids
638 656 commit = self.repo.get_commit(commit_id)
639 657 assert len(commit.nodes) == 0
640 658 root = commit.root
641 659 assert len(commit.nodes) == 1
642 660 assert len(root.nodes) == 8
643 661 # accessing root.nodes updates commit.nodes
644 662 assert len(commit.nodes) == 9
645 663
646 664 docs = root.get_node('docs')
647 665 # we haven't yet accessed anything new as docs dir was already cached
648 666 assert len(commit.nodes) == 9
649 667 assert len(docs.nodes) == 8
650 668 # accessing docs.nodes updates commit.nodes
651 669 assert len(commit.nodes) == 17
652 670
653 671 assert docs is commit.get_node('docs')
654 672 assert docs is root.nodes[0]
655 673 assert docs is root.dirs[0]
656 674 assert docs is commit.get_node('docs')
657 675
658 676 def test_nodes_with_commit(self):
659 677 commit_id = '2a13f185e4525f9d4b59882791a2d397b90d5ddc'
660 678 commit = self.repo.get_commit(commit_id)
661 679 root = commit.root
662 680 docs = root.get_node('docs')
663 681 assert docs is commit.get_node('docs')
664 682 api = docs.get_node('api')
665 683 assert api is commit.get_node('docs/api')
666 684 index = api.get_node('index.rst')
667 685 assert index is commit.get_node('docs/api/index.rst')
668 686 assert index is commit.get_node('docs')\
669 687 .get_node('api')\
670 688 .get_node('index.rst')
671 689
672 690 def test_branch_and_tags(self):
673 691 """
674 692 rev0 = self.repo.commit_ids[0]
675 693 commit0 = self.repo.get_commit(rev0)
676 694 assert commit0.branch == 'master'
677 695 assert commit0.tags == []
678 696
679 697 rev10 = self.repo.commit_ids[10]
680 698 commit10 = self.repo.get_commit(rev10)
681 699 assert commit10.branch == 'master'
682 700 assert commit10.tags == []
683 701
684 702 rev44 = self.repo.commit_ids[44]
685 703 commit44 = self.repo.get_commit(rev44)
686 704 assert commit44.branch == 'web-branch'
687 705
688 706 tip = self.repo.get_commit('tip')
689 707 assert 'tip' in tip.tags
690 708 """
691 709 # Those tests would fail - branches are now going
692 710 # to be changed at main API in order to support git backend
693 711 pass
694 712
695 713 def test_file_size(self):
696 714 to_check = (
697 715 ('c1214f7e79e02fc37156ff215cd71275450cffc3',
698 716 'vcs/backends/BaseRepository.py', 502),
699 717 ('d7e0d30fbcae12c90680eb095a4f5f02505ce501',
700 718 'vcs/backends/hg.py', 854),
701 719 ('6e125e7c890379446e98980d8ed60fba87d0f6d1',
702 720 'setup.py', 1068),
703 721
704 722 ('d955cd312c17b02143c04fa1099a352b04368118',
705 723 'vcs/backends/base.py', 2921),
706 724 ('ca1eb7957a54bce53b12d1a51b13452f95bc7c7e',
707 725 'vcs/backends/base.py', 3936),
708 726 ('f50f42baeed5af6518ef4b0cb2f1423f3851a941',
709 727 'vcs/backends/base.py', 6189),
710 728 )
711 729 for commit_id, path, size in to_check:
712 730 node = self.repo.get_commit(commit_id).get_node(path)
713 731 assert node.is_file()
714 732 assert node.size == size
715 733
716 734 def test_file_history_from_commits(self):
717 735 node = self.repo[10].get_node('setup.py')
718 736 commit_ids = [commit.raw_id for commit in node.history]
719 737 assert ['ff7ca51e58c505fec0dd2491de52c622bb7a806b'] == commit_ids
720 738
721 739 node = self.repo[20].get_node('setup.py')
722 740 node_ids = [commit.raw_id for commit in node.history]
723 741 assert ['191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
724 742 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'] == node_ids
725 743
726 744 # special case we check history from commit that has this particular
727 745 # file changed this means we check if it's included as well
728 746 node = self.repo.get_commit('191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e') \
729 747 .get_node('setup.py')
730 748 node_ids = [commit.raw_id for commit in node.history]
731 749 assert ['191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
732 750 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'] == node_ids
733 751
734 752 def test_file_history(self):
735 753 # we can only check if those commits are present in the history
736 754 # as we cannot update this test every time file is changed
737 755 files = {
738 756 'setup.py': [
739 757 '54386793436c938cff89326944d4c2702340037d',
740 758 '51d254f0ecf5df2ce50c0b115741f4cf13985dab',
741 759 '998ed409c795fec2012b1c0ca054d99888b22090',
742 760 '5e0eb4c47f56564395f76333f319d26c79e2fb09',
743 761 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
744 762 '7cb3fd1b6d8c20ba89e2264f1c8baebc8a52d36e',
745 763 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
746 764 '191caa5b2c81ed17c0794bf7bb9958f4dcb0b87e',
747 765 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
748 766 ],
749 767 'vcs/nodes.py': [
750 768 '33fa3223355104431402a888fa77a4e9956feb3e',
751 769 'fa014c12c26d10ba682fadb78f2a11c24c8118e1',
752 770 'e686b958768ee96af8029fe19c6050b1a8dd3b2b',
753 771 'ab5721ca0a081f26bf43d9051e615af2cc99952f',
754 772 'c877b68d18e792a66b7f4c529ea02c8f80801542',
755 773 '4313566d2e417cb382948f8d9d7c765330356054',
756 774 '6c2303a793671e807d1cfc70134c9ca0767d98c2',
757 775 '54386793436c938cff89326944d4c2702340037d',
758 776 '54000345d2e78b03a99d561399e8e548de3f3203',
759 777 '1c6b3677b37ea064cb4b51714d8f7498f93f4b2b',
760 778 '2d03ca750a44440fb5ea8b751176d1f36f8e8f46',
761 779 '2a08b128c206db48c2f0b8f70df060e6db0ae4f8',
762 780 '30c26513ff1eb8e5ce0e1c6b477ee5dc50e2f34b',
763 781 'ac71e9503c2ca95542839af0ce7b64011b72ea7c',
764 782 '12669288fd13adba2a9b7dd5b870cc23ffab92d2',
765 783 '5a0c84f3e6fe3473e4c8427199d5a6fc71a9b382',
766 784 '12f2f5e2b38e6ff3fbdb5d722efed9aa72ecb0d5',
767 785 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
768 786 'f50f42baeed5af6518ef4b0cb2f1423f3851a941',
769 787 'd7e390a45f6aa96f04f5e7f583ad4f867431aa25',
770 788 'f15c21f97864b4f071cddfbf2750ec2e23859414',
771 789 'e906ef056cf539a4e4e5fc8003eaf7cf14dd8ade',
772 790 'ea2b108b48aa8f8c9c4a941f66c1a03315ca1c3b',
773 791 '84dec09632a4458f79f50ddbbd155506c460b4f9',
774 792 '0115510b70c7229dbc5dc49036b32e7d91d23acd',
775 793 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
776 794 '3bf1c5868e570e39569d094f922d33ced2fa3b2b',
777 795 'b8d04012574729d2c29886e53b1a43ef16dd00a1',
778 796 '6970b057cffe4aab0a792aa634c89f4bebf01441',
779 797 'dd80b0f6cf5052f17cc738c2951c4f2070200d7f',
780 798 'ff7ca51e58c505fec0dd2491de52c622bb7a806b',
781 799 ],
782 800 'vcs/backends/git.py': [
783 801 '4cf116ad5a457530381135e2f4c453e68a1b0105',
784 802 '9a751d84d8e9408e736329767387f41b36935153',
785 803 'cb681fb539c3faaedbcdf5ca71ca413425c18f01',
786 804 '428f81bb652bcba8d631bce926e8834ff49bdcc6',
787 805 '180ab15aebf26f98f714d8c68715e0f05fa6e1c7',
788 806 '2b8e07312a2e89e92b90426ab97f349f4bce2a3a',
789 807 '50e08c506174d8645a4bb517dd122ac946a0f3bf',
790 808 '54000345d2e78b03a99d561399e8e548de3f3203',
791 809 ],
792 810 }
793 811 for path, commit_ids in files.items():
794 812 node = self.repo.get_commit(commit_ids[0]).get_node(path)
795 813 node_ids = [commit.raw_id for commit in node.history]
796 814 assert set(commit_ids).issubset(set(node_ids)), (
797 815 "We assumed that %s is subset of commit_ids for which file %s "
798 816 "has been changed, and history of that node returned: %s"
799 817 % (commit_ids, path, node_ids))
800 818
801 819 def test_file_annotate(self):
802 820 files = {
803 821 'vcs/backends/__init__.py': {
804 822 'c1214f7e79e02fc37156ff215cd71275450cffc3': {
805 823 'lines_no': 1,
806 824 'commits': [
807 825 'c1214f7e79e02fc37156ff215cd71275450cffc3',
808 826 ],
809 827 },
810 828 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647': {
811 829 'lines_no': 21,
812 830 'commits': [
813 831 '49d3fd156b6f7db46313fac355dca1a0b94a0017',
814 832 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
815 833 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
816 834 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
817 835 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
818 836 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
819 837 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
820 838 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
821 839 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
822 840 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
823 841 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
824 842 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
825 843 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
826 844 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
827 845 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
828 846 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
829 847 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
830 848 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
831 849 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
832 850 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
833 851 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
834 852 ],
835 853 },
836 854 'e29b67bd158580fc90fc5e9111240b90e6e86064': {
837 855 'lines_no': 32,
838 856 'commits': [
839 857 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
840 858 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
841 859 '5eab1222a7cd4bfcbabc218ca6d04276d4e27378',
842 860 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
843 861 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
844 862 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
845 863 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
846 864 '54000345d2e78b03a99d561399e8e548de3f3203',
847 865 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
848 866 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
849 867 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
850 868 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
851 869 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
852 870 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
853 871 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
854 872 '2a13f185e4525f9d4b59882791a2d397b90d5ddc',
855 873 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
856 874 '78c3f0c23b7ee935ec276acb8b8212444c33c396',
857 875 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
858 876 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
859 877 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
860 878 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
861 879 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
862 880 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
863 881 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
864 882 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
865 883 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
866 884 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
867 885 '992f38217b979d0b0987d0bae3cc26dac85d9b19',
868 886 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
869 887 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
870 888 '16fba1ae9334d79b66d7afed2c2dfbfa2ae53647',
871 889 ],
872 890 },
873 891 },
874 892 }
875 893
876 894 for fname, commit_dict in files.items():
877 895 for commit_id, __ in commit_dict.items():
878 896 commit = self.repo.get_commit(commit_id)
879 897
880 898 l1_1 = [x[1] for x in commit.get_file_annotate(fname)]
881 899 l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(fname)]
882 900 assert l1_1 == l1_2
883 901 l1 = l1_1
884 902 l2 = files[fname][commit_id]['commits']
885 903 assert l1 == l2, (
886 904 "The lists of commit_ids for %s@commit_id %s"
887 905 "from annotation list should match each other, "
888 906 "got \n%s \nvs \n%s " % (fname, commit_id, l1, l2))
889 907
890 908 def test_files_state(self):
891 909 """
892 910 Tests state of FileNodes.
893 911 """
894 912 node = self.repo\
895 913 .get_commit('e6ea6d16e2f26250124a1f4b4fe37a912f9d86a0')\
896 914 .get_node('vcs/utils/diffs.py')
897 915 assert node.state, NodeState.ADDED
898 916 assert node.added
899 917 assert not node.changed
900 918 assert not node.not_changed
901 919 assert not node.removed
902 920
903 921 node = self.repo\
904 922 .get_commit('33fa3223355104431402a888fa77a4e9956feb3e')\
905 923 .get_node('.hgignore')
906 924 assert node.state, NodeState.CHANGED
907 925 assert not node.added
908 926 assert node.changed
909 927 assert not node.not_changed
910 928 assert not node.removed
911 929
912 930 node = self.repo\
913 931 .get_commit('e29b67bd158580fc90fc5e9111240b90e6e86064')\
914 932 .get_node('setup.py')
915 933 assert node.state, NodeState.NOT_CHANGED
916 934 assert not node.added
917 935 assert not node.changed
918 936 assert node.not_changed
919 937 assert not node.removed
920 938
921 939 # If node has REMOVED state then trying to fetch it would raise
922 940 # CommitError exception
923 941 commit = self.repo.get_commit(
924 942 'fa6600f6848800641328adbf7811fd2372c02ab2')
925 943 path = 'vcs/backends/BaseRepository.py'
926 944 with pytest.raises(NodeDoesNotExistError):
927 945 commit.get_node(path)
928 946 # but it would be one of ``removed`` (commit's attribute)
929 947 assert path in [rf.path for rf in commit.removed]
930 948
931 949 commit = self.repo.get_commit(
932 950 '54386793436c938cff89326944d4c2702340037d')
933 951 changed = [
934 952 'setup.py', 'tests/test_nodes.py', 'vcs/backends/hg.py',
935 953 'vcs/nodes.py']
936 954 assert set(changed) == set([f.path for f in commit.changed])
937 955
938 956 def test_unicode_branch_refs(self):
939 957 unicode_branches = {
940 958 'refs/heads/unicode': '6c0ce52b229aa978889e91b38777f800e85f330b',
941 959 u'refs/heads/uniΓ§ΓΆβˆ‚e': 'ΓΌrl',
942 960 }
943 961 with mock.patch(
944 962 ("rhodecode.lib.vcs.backends.git.repository"
945 963 ".GitRepository._refs"),
946 964 unicode_branches):
947 965 branches = self.repo.branches
948 966
949 967 assert 'unicode' in branches
950 968 assert u'uniΓ§ΓΆβˆ‚e' in branches
951 969
952 970 def test_unicode_tag_refs(self):
953 971 unicode_tags = {
954 972 'refs/tags/unicode': '6c0ce52b229aa978889e91b38777f800e85f330b',
955 973 u'refs/tags/uniΓ§ΓΆβˆ‚e': '6c0ce52b229aa978889e91b38777f800e85f330b',
956 974 }
957 975 with mock.patch(
958 976 ("rhodecode.lib.vcs.backends.git.repository"
959 977 ".GitRepository._refs"),
960 978 unicode_tags):
961 979 tags = self.repo.tags
962 980
963 981 assert 'unicode' in tags
964 982 assert u'uniΓ§ΓΆβˆ‚e' in tags
965 983
966 984 def test_commit_message_is_unicode(self):
967 985 for commit in self.repo:
968 986 assert type(commit.message) == unicode
969 987
970 988 def test_commit_author_is_unicode(self):
971 989 for commit in self.repo:
972 990 assert type(commit.author) == unicode
973 991
974 992 def test_repo_files_content_is_unicode(self):
975 993 commit = self.repo.get_commit()
976 994 for node in commit.get_node('/'):
977 995 if node.is_file():
978 996 assert type(node.content) == unicode
979 997
980 998 def test_wrong_path(self):
981 999 # There is 'setup.py' in the root dir but not there:
982 1000 path = 'foo/bar/setup.py'
983 1001 tip = self.repo.get_commit()
984 1002 with pytest.raises(VCSError):
985 1003 tip.get_node(path)
986 1004
987 1005 @pytest.mark.parametrize("author_email, commit_id", [
988 1006 ('marcin@python-blog.com', 'c1214f7e79e02fc37156ff215cd71275450cffc3'),
989 1007 ('lukasz.balcerzak@python-center.pl',
990 1008 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
991 1009 ('none@none', '8430a588b43b5d6da365400117c89400326e7992'),
992 1010 ])
993 1011 def test_author_email(self, author_email, commit_id):
994 1012 commit = self.repo.get_commit(commit_id)
995 1013 assert author_email == commit.author_email
996 1014
997 1015 @pytest.mark.parametrize("author, commit_id", [
998 1016 ('Marcin Kuzminski', 'c1214f7e79e02fc37156ff215cd71275450cffc3'),
999 1017 ('Lukasz Balcerzak', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
1000 1018 ('marcink', '8430a588b43b5d6da365400117c89400326e7992'),
1001 1019 ])
1002 1020 def test_author_username(self, author, commit_id):
1003 1021 commit = self.repo.get_commit(commit_id)
1004 1022 assert author == commit.author_name
1005 1023
1006 1024
1007 1025 class TestLargeFileRepo(object):
1008 1026
1009 1027 def test_large_file(self, backend_git):
1010 1028 conf = make_db_config()
1011 1029 repo = backend_git.create_test_repo('largefiles', conf)
1012 1030
1013 1031 tip = repo.scm_instance().get_commit()
1014 1032
1015 1033 # extract stored LF node into the origin cache
1016 1034 lfs_store = os.path.join(repo.repo_path, repo.repo_name, 'lfs_store')
1017 1035
1018 1036 oid = '7b331c02e313c7599d5a90212e17e6d3cb729bd2e1c9b873c302a63c95a2f9bf'
1019 1037 oid_path = os.path.join(lfs_store, oid)
1020 1038 oid_destination = os.path.join(
1021 1039 conf.get('vcs_git_lfs', 'store_location'), oid)
1022 1040 shutil.copy(oid_path, oid_destination)
1023 1041
1024 1042 node = tip.get_node('1MB.zip')
1025 1043
1026 1044 lf_node = node.get_largefile_node()
1027 1045
1028 1046 assert lf_node.is_largefile() is True
1029 1047 assert lf_node.size == 1024000
1030 1048 assert lf_node.name == '1MB.zip'
1031 1049
1032 1050
1033 1051 @pytest.mark.usefixtures("vcs_repository_support")
1034 1052 class TestGitSpecificWithRepo(BackendTestMixin):
1035 1053
1036 1054 @classmethod
1037 1055 def _get_commits(cls):
1038 1056 return [
1039 1057 {
1040 1058 'message': 'Initial',
1041 1059 'author': 'Joe Doe <joe.doe@example.com>',
1042 1060 'date': datetime.datetime(2010, 1, 1, 20),
1043 1061 'added': [
1044 1062 FileNode('foobar/static/js/admin/base.js', content='base'),
1045 1063 FileNode(
1046 1064 'foobar/static/admin', content='admin',
1047 1065 mode=0120000), # this is a link
1048 1066 FileNode('foo', content='foo'),
1049 1067 ],
1050 1068 },
1051 1069 {
1052 1070 'message': 'Second',
1053 1071 'author': 'Joe Doe <joe.doe@example.com>',
1054 1072 'date': datetime.datetime(2010, 1, 1, 22),
1055 1073 'added': [
1056 1074 FileNode('foo2', content='foo2'),
1057 1075 ],
1058 1076 },
1059 1077 ]
1060 1078
1061 1079 def test_paths_slow_traversing(self):
1062 1080 commit = self.repo.get_commit()
1063 1081 assert commit.get_node('foobar').get_node('static').get_node('js')\
1064 1082 .get_node('admin').get_node('base.js').content == 'base'
1065 1083
1066 1084 def test_paths_fast_traversing(self):
1067 1085 commit = self.repo.get_commit()
1068 1086 assert (
1069 1087 commit.get_node('foobar/static/js/admin/base.js').content ==
1070 1088 'base')
1071 1089
1072 1090 def test_get_diff_runs_git_command_with_hashes(self):
1073 1091 self.repo.run_git_command = mock.Mock(return_value=['', ''])
1074 1092 self.repo.get_diff(self.repo[0], self.repo[1])
1075 1093 self.repo.run_git_command.assert_called_once_with(
1076 1094 ['diff', '-U3', '--full-index', '--binary', '-p', '-M',
1077 1095 '--abbrev=40', self.repo._get_commit_id(0),
1078 1096 self.repo._get_commit_id(1)])
1079 1097
1080 1098 def test_get_diff_runs_git_command_with_str_hashes(self):
1081 1099 self.repo.run_git_command = mock.Mock(return_value=['', ''])
1082 1100 self.repo.get_diff(self.repo.EMPTY_COMMIT, self.repo[1])
1083 1101 self.repo.run_git_command.assert_called_once_with(
1084 1102 ['show', '-U3', '--full-index', '--binary', '-p', '-M',
1085 1103 '--abbrev=40', self.repo._get_commit_id(1)])
1086 1104
1087 1105 def test_get_diff_runs_git_command_with_path_if_its_given(self):
1088 1106 self.repo.run_git_command = mock.Mock(return_value=['', ''])
1089 1107 self.repo.get_diff(self.repo[0], self.repo[1], 'foo')
1090 1108 self.repo.run_git_command.assert_called_once_with(
1091 1109 ['diff', '-U3', '--full-index', '--binary', '-p', '-M',
1092 1110 '--abbrev=40', self.repo._get_commit_id(0),
1093 1111 self.repo._get_commit_id(1), '--', 'foo'])
1094 1112
1095 1113
1096 1114 @pytest.mark.usefixtures("vcs_repository_support")
1097 1115 class TestGitRegression(BackendTestMixin):
1098 1116
1099 1117 @classmethod
1100 1118 def _get_commits(cls):
1101 1119 return [
1102 1120 {
1103 1121 'message': 'Initial',
1104 1122 'author': 'Joe Doe <joe.doe@example.com>',
1105 1123 'date': datetime.datetime(2010, 1, 1, 20),
1106 1124 'added': [
1107 1125 FileNode('bot/__init__.py', content='base'),
1108 1126 FileNode('bot/templates/404.html', content='base'),
1109 1127 FileNode('bot/templates/500.html', content='base'),
1110 1128 ],
1111 1129 },
1112 1130 {
1113 1131 'message': 'Second',
1114 1132 'author': 'Joe Doe <joe.doe@example.com>',
1115 1133 'date': datetime.datetime(2010, 1, 1, 22),
1116 1134 'added': [
1117 1135 FileNode('bot/build/migrations/1.py', content='foo2'),
1118 1136 FileNode('bot/build/migrations/2.py', content='foo2'),
1119 1137 FileNode(
1120 1138 'bot/build/static/templates/f.html', content='foo2'),
1121 1139 FileNode(
1122 1140 'bot/build/static/templates/f1.html', content='foo2'),
1123 1141 FileNode('bot/build/templates/err.html', content='foo2'),
1124 1142 FileNode('bot/build/templates/err2.html', content='foo2'),
1125 1143 ],
1126 1144 },
1127 1145 ]
1128 1146
1129 1147 @pytest.mark.parametrize("path, expected_paths", [
1130 1148 ('bot', [
1131 1149 'bot/build',
1132 1150 'bot/templates',
1133 1151 'bot/__init__.py']),
1134 1152 ('bot/build', [
1135 1153 'bot/build/migrations',
1136 1154 'bot/build/static',
1137 1155 'bot/build/templates']),
1138 1156 ('bot/build/static', [
1139 1157 'bot/build/static/templates']),
1140 1158 ('bot/build/static/templates', [
1141 1159 'bot/build/static/templates/f.html',
1142 1160 'bot/build/static/templates/f1.html']),
1143 1161 ('bot/build/templates', [
1144 1162 'bot/build/templates/err.html',
1145 1163 'bot/build/templates/err2.html']),
1146 1164 ('bot/templates/', [
1147 1165 'bot/templates/404.html',
1148 1166 'bot/templates/500.html']),
1149 1167 ])
1150 1168 def test_similar_paths(self, path, expected_paths):
1151 1169 commit = self.repo.get_commit()
1152 1170 paths = [n.path for n in commit.get_nodes(path)]
1153 1171 assert paths == expected_paths
1154 1172
1155 1173
1156 1174 class TestDiscoverGitVersion:
1157 1175
1158 1176 def test_returns_git_version(self, baseapp):
1159 1177 version = discover_git_version()
1160 1178 assert version
1161 1179
1162 1180 def test_returns_empty_string_without_vcsserver(self):
1163 1181 mock_connection = mock.Mock()
1164 1182 mock_connection.discover_git_version = mock.Mock(
1165 1183 side_effect=Exception)
1166 1184 with mock.patch('rhodecode.lib.vcs.connection.Git', mock_connection):
1167 1185 version = discover_git_version()
1168 1186 assert version == ''
1169 1187
1170 1188
1171 1189 class TestGetSubmoduleUrl(object):
1172 1190 def test_submodules_file_found(self):
1173 1191 commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1)
1174 1192 node = mock.Mock()
1175 1193 with mock.patch.object(
1176 1194 commit, 'get_node', return_value=node) as get_node_mock:
1177 1195 node.content = (
1178 1196 '[submodule "subrepo1"]\n'
1179 1197 '\tpath = subrepo1\n'
1180 1198 '\turl = https://code.rhodecode.com/dulwich\n'
1181 1199 )
1182 1200 result = commit._get_submodule_url('subrepo1')
1183 1201 get_node_mock.assert_called_once_with('.gitmodules')
1184 1202 assert result == 'https://code.rhodecode.com/dulwich'
1185 1203
1186 1204 def test_complex_submodule_path(self):
1187 1205 commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1)
1188 1206 node = mock.Mock()
1189 1207 with mock.patch.object(
1190 1208 commit, 'get_node', return_value=node) as get_node_mock:
1191 1209 node.content = (
1192 1210 '[submodule "complex/subrepo/path"]\n'
1193 1211 '\tpath = complex/subrepo/path\n'
1194 1212 '\turl = https://code.rhodecode.com/dulwich\n'
1195 1213 )
1196 1214 result = commit._get_submodule_url('complex/subrepo/path')
1197 1215 get_node_mock.assert_called_once_with('.gitmodules')
1198 1216 assert result == 'https://code.rhodecode.com/dulwich'
1199 1217
1200 1218 def test_submodules_file_not_found(self):
1201 1219 commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1)
1202 1220 with mock.patch.object(
1203 1221 commit, 'get_node', side_effect=NodeDoesNotExistError):
1204 1222 result = commit._get_submodule_url('complex/subrepo/path')
1205 1223 assert result is None
1206 1224
1207 1225 def test_path_not_found(self):
1208 1226 commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1)
1209 1227 node = mock.Mock()
1210 1228 with mock.patch.object(
1211 1229 commit, 'get_node', return_value=node) as get_node_mock:
1212 1230 node.content = (
1213 1231 '[submodule "subrepo1"]\n'
1214 1232 '\tpath = subrepo1\n'
1215 1233 '\turl = https://code.rhodecode.com/dulwich\n'
1216 1234 )
1217 1235 result = commit._get_submodule_url('subrepo2')
1218 1236 get_node_mock.assert_called_once_with('.gitmodules')
1219 1237 assert result is None
1220 1238
1221 1239 def test_returns_cached_values(self):
1222 1240 commit = GitCommit(repository=mock.Mock(), raw_id='abcdef12', idx=1)
1223 1241 node = mock.Mock()
1224 1242 with mock.patch.object(
1225 1243 commit, 'get_node', return_value=node) as get_node_mock:
1226 1244 node.content = (
1227 1245 '[submodule "subrepo1"]\n'
1228 1246 '\tpath = subrepo1\n'
1229 1247 '\turl = https://code.rhodecode.com/dulwich\n'
1230 1248 )
1231 1249 for _ in range(3):
1232 1250 commit._get_submodule_url('subrepo1')
1233 1251 get_node_mock.assert_called_once_with('.gitmodules')
1234 1252
1235 1253 def test_get_node_returns_a_link(self):
1236 1254 repository = mock.Mock()
1237 1255 repository.alias = 'git'
1238 1256 commit = GitCommit(repository=repository, raw_id='abcdef12', idx=1)
1239 1257 submodule_url = 'https://code.rhodecode.com/dulwich'
1240 1258 get_id_patch = mock.patch.object(
1241 1259 commit, '_get_id_for_path', return_value=(1, 'link'))
1242 1260 get_submodule_patch = mock.patch.object(
1243 1261 commit, '_get_submodule_url', return_value=submodule_url)
1244 1262
1245 1263 with get_id_patch, get_submodule_patch as submodule_mock:
1246 1264 node = commit.get_node('/abcde')
1247 1265
1248 1266 submodule_mock.assert_called_once_with('/abcde')
1249 1267 assert type(node) == SubModuleNode
1250 1268 assert node.url == submodule_url
1251 1269
1252 1270 def test_get_nodes_returns_links(self):
1253 1271 repository = mock.MagicMock()
1254 1272 repository.alias = 'git'
1255 1273 repository._remote.tree_items.return_value = [
1256 1274 ('subrepo', 'stat', 1, 'link')
1257 1275 ]
1258 1276 commit = GitCommit(repository=repository, raw_id='abcdef12', idx=1)
1259 1277 submodule_url = 'https://code.rhodecode.com/dulwich'
1260 1278 get_id_patch = mock.patch.object(
1261 1279 commit, '_get_id_for_path', return_value=(1, 'tree'))
1262 1280 get_submodule_patch = mock.patch.object(
1263 1281 commit, '_get_submodule_url', return_value=submodule_url)
1264 1282
1265 1283 with get_id_patch, get_submodule_patch as submodule_mock:
1266 1284 nodes = commit.get_nodes('/abcde')
1267 1285
1268 1286 submodule_mock.assert_called_once_with('/abcde/subrepo')
1269 1287 assert len(nodes) == 1
1270 1288 assert type(nodes[0]) == SubModuleNode
1271 1289 assert nodes[0].url == submodule_url
@@ -1,1180 +1,1183 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22
23 23 import mock
24 24 import pytest
25 25
26 26 from rhodecode.lib.utils import make_db_config
27 27 from rhodecode.lib.vcs import backends
28 28 from rhodecode.lib.vcs.backends.base import (
29 29 Reference, MergeResponse, MergeFailureReason)
30 30 from rhodecode.lib.vcs.backends.hg import MercurialRepository, MercurialCommit
31 31 from rhodecode.lib.vcs.exceptions import (
32 32 RepositoryError, VCSError, NodeDoesNotExistError, CommitDoesNotExistError)
33 33 from rhodecode.lib.vcs.nodes import FileNode, NodeKind, NodeState
34 34 from rhodecode.tests import TEST_HG_REPO, TEST_HG_REPO_CLONE
35 35
36 36
37 37 pytestmark = pytest.mark.backends("hg")
38 38
39 39
40 40 def repo_path_generator():
41 41 """
42 42 Return a different path to be used for cloning repos.
43 43 """
44 44 i = 0
45 45 while True:
46 46 i += 1
47 47 yield '%s-%d' % (TEST_HG_REPO_CLONE, i)
48 48
49 49
50 50 REPO_PATH_GENERATOR = repo_path_generator()
51 51
52 52
53 53 @pytest.fixture(scope='class', autouse=True)
54 54 def repo(request, baseapp):
55 55 repo = MercurialRepository(TEST_HG_REPO)
56 56 if request.cls:
57 57 request.cls.repo = repo
58 58 return repo
59 59
60 60
61 61 class TestMercurialRepository:
62 62
63 63 # pylint: disable=protected-access
64 64
65 65 def get_clone_repo(self):
66 66 """
67 67 Return a clone of the base repo.
68 68 """
69 69 clone_path = next(REPO_PATH_GENERATOR)
70 70 repo_clone = MercurialRepository(
71 71 clone_path, create=True, src_url=self.repo.path)
72 72
73 73 return repo_clone
74 74
75 75 def get_empty_repo(self):
76 76 """
77 77 Return an empty repo.
78 78 """
79 79 return MercurialRepository(next(REPO_PATH_GENERATOR), create=True)
80 80
81 81 def test_wrong_repo_path(self):
82 82 wrong_repo_path = '/tmp/errorrepo_hg'
83 83 with pytest.raises(RepositoryError):
84 84 MercurialRepository(wrong_repo_path)
85 85
86 86 def test_unicode_path_repo(self):
87 87 with pytest.raises(VCSError):
88 88 MercurialRepository(u'iShouldFail')
89 89
90 90 def test_unicode_commit_id(self):
91 91 with pytest.raises(CommitDoesNotExistError):
92 92 self.repo.get_commit(u'unicode-commit-id')
93 93 with pytest.raises(CommitDoesNotExistError):
94 94 self.repo.get_commit(u'unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-commit-id')
95 95
96 96 def test_unicode_bookmark(self):
97 97 self.repo.bookmark(u'unicode-bookmark')
98 98 self.repo.bookmark(u'unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-bookmark')
99 99
100 100 def test_unicode_branch(self):
101 101 with pytest.raises(KeyError):
102 102 self.repo.branches[u'unicode-branch']
103 103 with pytest.raises(KeyError):
104 104 self.repo.branches[u'unΓ­cΓΈde-spéçial-chΓ€rΓ₯cter-branch']
105 105
106 106 def test_repo_clone(self):
107 107 if os.path.exists(TEST_HG_REPO_CLONE):
108 108 self.fail(
109 109 'Cannot test mercurial clone repo as location %s already '
110 110 'exists. You should manually remove it first.'
111 111 % TEST_HG_REPO_CLONE)
112 112
113 113 repo = MercurialRepository(TEST_HG_REPO)
114 114 repo_clone = MercurialRepository(TEST_HG_REPO_CLONE,
115 115 src_url=TEST_HG_REPO)
116 116 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
117 117 # Checking hashes of commits should be enough
118 118 for commit in repo.get_commits():
119 119 raw_id = commit.raw_id
120 120 assert raw_id == repo_clone.get_commit(raw_id).raw_id
121 121
122 122 def test_repo_clone_with_update(self):
123 123 repo = MercurialRepository(TEST_HG_REPO)
124 124 repo_clone = MercurialRepository(
125 125 TEST_HG_REPO_CLONE + '_w_update',
126 126 src_url=TEST_HG_REPO, update_after_clone=True)
127 127 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
128 128
129 129 # check if current workdir was updated
130 130 assert os.path.isfile(
131 131 os.path.join(TEST_HG_REPO_CLONE + '_w_update', 'MANIFEST.in'))
132 132
133 133 def test_repo_clone_without_update(self):
134 134 repo = MercurialRepository(TEST_HG_REPO)
135 135 repo_clone = MercurialRepository(
136 136 TEST_HG_REPO_CLONE + '_wo_update',
137 137 src_url=TEST_HG_REPO, update_after_clone=False)
138 138 assert len(repo.commit_ids) == len(repo_clone.commit_ids)
139 139 assert not os.path.isfile(
140 140 os.path.join(TEST_HG_REPO_CLONE + '_wo_update', 'MANIFEST.in'))
141 141
142 142 def test_commit_ids(self):
143 143 # there are 21 commits at bitbucket now
144 144 # so we can assume they would be available from now on
145 145 subset = set([
146 146 'b986218ba1c9b0d6a259fac9b050b1724ed8e545',
147 147 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
148 148 '6cba7170863a2411822803fa77a0a264f1310b35',
149 149 '56349e29c2af3ac913b28bde9a2c6154436e615b',
150 150 '2dda4e345facb0ccff1a191052dd1606dba6781d',
151 151 '6fff84722075f1607a30f436523403845f84cd9e',
152 152 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
153 153 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
154 154 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
155 155 'be90031137367893f1c406e0a8683010fd115b79',
156 156 'db8e58be770518cbb2b1cdfa69146e47cd481481',
157 157 '84478366594b424af694a6c784cb991a16b87c21',
158 158 '17f8e105dddb9f339600389c6dc7175d395a535c',
159 159 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
160 160 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
161 161 '786facd2c61deb9cf91e9534735124fb8fc11842',
162 162 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
163 163 'aa6a0de05b7612707db567078e130a6cd114a9a7',
164 164 'eada5a770da98ab0dd7325e29d00e0714f228d09'
165 165 ])
166 166 assert subset.issubset(set(self.repo.commit_ids))
167 167
168 168 # check if we have the proper order of commits
169 169 org = [
170 170 'b986218ba1c9b0d6a259fac9b050b1724ed8e545',
171 171 '3d8f361e72ab303da48d799ff1ac40d5ac37c67e',
172 172 '6cba7170863a2411822803fa77a0a264f1310b35',
173 173 '56349e29c2af3ac913b28bde9a2c6154436e615b',
174 174 '2dda4e345facb0ccff1a191052dd1606dba6781d',
175 175 '6fff84722075f1607a30f436523403845f84cd9e',
176 176 '7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7',
177 177 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb',
178 178 'dc5d2c0661b61928834a785d3e64a3f80d3aad9c',
179 179 'be90031137367893f1c406e0a8683010fd115b79',
180 180 'db8e58be770518cbb2b1cdfa69146e47cd481481',
181 181 '84478366594b424af694a6c784cb991a16b87c21',
182 182 '17f8e105dddb9f339600389c6dc7175d395a535c',
183 183 '20a662e756499bde3095ffc9bc0643d1def2d0eb',
184 184 '2e319b85e70a707bba0beff866d9f9de032aa4f9',
185 185 '786facd2c61deb9cf91e9534735124fb8fc11842',
186 186 '94593d2128d38210a2fcd1aabff6dda0d6d9edf8',
187 187 'aa6a0de05b7612707db567078e130a6cd114a9a7',
188 188 'eada5a770da98ab0dd7325e29d00e0714f228d09',
189 189 '2c1885c735575ca478bf9e17b0029dca68824458',
190 190 'd9bcd465040bf869799b09ad732c04e0eea99fe9',
191 191 '469e9c847fe1f6f7a697b8b25b4bc5b48780c1a7',
192 192 '4fb8326d78e5120da2c7468dcf7098997be385da',
193 193 '62b4a097164940bd66030c4db51687f3ec035eed',
194 194 '536c1a19428381cfea92ac44985304f6a8049569',
195 195 '965e8ab3c44b070cdaa5bf727ddef0ada980ecc4',
196 196 '9bb326a04ae5d98d437dece54be04f830cf1edd9',
197 197 'f8940bcb890a98c4702319fbe36db75ea309b475',
198 198 'ff5ab059786ebc7411e559a2cc309dfae3625a3b',
199 199 '6b6ad5f82ad5bb6190037671bd254bd4e1f4bf08',
200 200 'ee87846a61c12153b51543bf860e1026c6d3dcba',
201 201 ]
202 202 assert org == self.repo.commit_ids[:31]
203 203
204 204 def test_iter_slice(self):
205 205 sliced = list(self.repo[:10])
206 206 itered = list(self.repo)[:10]
207 207 assert sliced == itered
208 208
209 209 def test_slicing(self):
210 210 # 4 1 5 10 95
211 211 for sfrom, sto, size in [(0, 4, 4), (1, 2, 1), (10, 15, 5),
212 212 (10, 20, 10), (5, 100, 95)]:
213 213 indexes = list(self.repo[sfrom:sto])
214 214 assert len(indexes) == size
215 215 assert indexes[0] == self.repo.get_commit(commit_idx=sfrom)
216 216 assert indexes[-1] == self.repo.get_commit(commit_idx=sto - 1)
217 217
218 218 def test_branches(self):
219 219 # TODO: Need more tests here
220 220
221 221 # active branches
222 222 assert 'default' in self.repo.branches
223 223 assert 'stable' in self.repo.branches
224 224
225 225 # closed
226 226 assert 'git' in self.repo._get_branches(closed=True)
227 227 assert 'web' in self.repo._get_branches(closed=True)
228 228
229 229 for name, id in self.repo.branches.items():
230 230 assert isinstance(self.repo.get_commit(id), MercurialCommit)
231 231
232 232 def test_tip_in_tags(self):
233 233 # tip is always a tag
234 234 assert 'tip' in self.repo.tags
235 235
236 236 def test_tip_commit_in_tags(self):
237 237 tip = self.repo.get_commit()
238 238 assert self.repo.tags['tip'] == tip.raw_id
239 239
240 240 def test_initial_commit(self):
241 241 init_commit = self.repo.get_commit(commit_idx=0)
242 242 init_author = init_commit.author
243 243
244 244 assert init_commit.message == 'initial import'
245 245 assert init_author == 'Marcin Kuzminski <marcin@python-blog.com>'
246 246 assert init_author == init_commit.committer
247 247 assert sorted(init_commit._file_paths) == sorted([
248 248 'vcs/__init__.py',
249 249 'vcs/backends/BaseRepository.py',
250 250 'vcs/backends/__init__.py',
251 251 ])
252 252 assert sorted(init_commit._dir_paths) == sorted(
253 253 ['', 'vcs', 'vcs/backends'])
254 254
255 255 assert init_commit._dir_paths + init_commit._file_paths == \
256 256 init_commit._paths
257 257
258 258 with pytest.raises(NodeDoesNotExistError):
259 259 init_commit.get_node(path='foobar')
260 260
261 261 node = init_commit.get_node('vcs/')
262 262 assert hasattr(node, 'kind')
263 263 assert node.kind == NodeKind.DIR
264 264
265 265 node = init_commit.get_node('vcs')
266 266 assert hasattr(node, 'kind')
267 267 assert node.kind == NodeKind.DIR
268 268
269 269 node = init_commit.get_node('vcs/__init__.py')
270 270 assert hasattr(node, 'kind')
271 271 assert node.kind == NodeKind.FILE
272 272
273 273 def test_not_existing_commit(self):
274 274 # rawid
275 275 with pytest.raises(RepositoryError):
276 276 self.repo.get_commit('abcd' * 10)
277 277 # shortid
278 278 with pytest.raises(RepositoryError):
279 279 self.repo.get_commit('erro' * 4)
280 280 # numeric
281 281 with pytest.raises(RepositoryError):
282 282 self.repo.get_commit(commit_idx=self.repo.count() + 1)
283 283
284 284 # Small chance we ever get to this one
285 285 idx = pow(2, 30)
286 286 with pytest.raises(RepositoryError):
287 287 self.repo.get_commit(commit_idx=idx)
288 288
289 289 def test_commit10(self):
290 290 commit10 = self.repo.get_commit(commit_idx=10)
291 291 README = """===
292 292 VCS
293 293 ===
294 294
295 295 Various Version Control System management abstraction layer for Python.
296 296
297 297 Introduction
298 298 ------------
299 299
300 300 TODO: To be written...
301 301
302 302 """
303 303 node = commit10.get_node('README.rst')
304 304 assert node.kind == NodeKind.FILE
305 305 assert node.content == README
306 306
307 307 def test_local_clone(self):
308 308 clone_path = next(REPO_PATH_GENERATOR)
309 309 self.repo._local_clone(clone_path)
310 310 repo_clone = MercurialRepository(clone_path)
311 311
312 312 assert self.repo.commit_ids == repo_clone.commit_ids
313 313
314 314 def test_local_clone_fails_if_target_exists(self):
315 315 with pytest.raises(RepositoryError):
316 316 self.repo._local_clone(self.repo.path)
317 317
318 318 def test_update(self):
319 319 repo_clone = self.get_clone_repo()
320 320 branches = repo_clone.branches
321 321
322 322 repo_clone._update('default')
323 323 assert branches['default'] == repo_clone._identify()
324 324 repo_clone._update('stable')
325 325 assert branches['stable'] == repo_clone._identify()
326 326
327 327 def test_local_pull_branch(self):
328 328 target_repo = self.get_empty_repo()
329 329 source_repo = self.get_clone_repo()
330 330
331 331 default = Reference(
332 332 'branch', 'default', source_repo.branches['default'])
333 333 target_repo._local_pull(source_repo.path, default)
334 334 target_repo = MercurialRepository(target_repo.path)
335 335 assert (target_repo.branches['default'] ==
336 336 source_repo.branches['default'])
337 337
338 338 stable = Reference('branch', 'stable', source_repo.branches['stable'])
339 339 target_repo._local_pull(source_repo.path, stable)
340 340 target_repo = MercurialRepository(target_repo.path)
341 341 assert target_repo.branches['stable'] == source_repo.branches['stable']
342 342
343 343 def test_local_pull_bookmark(self):
344 344 target_repo = self.get_empty_repo()
345 345 source_repo = self.get_clone_repo()
346 346
347 347 commits = list(source_repo.get_commits(branch_name='default'))
348 348 foo1_id = commits[-5].raw_id
349 349 foo1 = Reference('book', 'foo1', foo1_id)
350 350 source_repo._update(foo1_id)
351 351 source_repo.bookmark('foo1')
352 352
353 353 foo2_id = commits[-3].raw_id
354 354 foo2 = Reference('book', 'foo2', foo2_id)
355 355 source_repo._update(foo2_id)
356 356 source_repo.bookmark('foo2')
357 357
358 358 target_repo._local_pull(source_repo.path, foo1)
359 359 target_repo = MercurialRepository(target_repo.path)
360 360 assert target_repo.branches['default'] == commits[-5].raw_id
361 361
362 362 target_repo._local_pull(source_repo.path, foo2)
363 363 target_repo = MercurialRepository(target_repo.path)
364 364 assert target_repo.branches['default'] == commits[-3].raw_id
365 365
366 366 def test_local_pull_commit(self):
367 367 target_repo = self.get_empty_repo()
368 368 source_repo = self.get_clone_repo()
369 369
370 370 commits = list(source_repo.get_commits(branch_name='default'))
371 371 commit_id = commits[-5].raw_id
372 372 commit = Reference('rev', commit_id, commit_id)
373 373 target_repo._local_pull(source_repo.path, commit)
374 374 target_repo = MercurialRepository(target_repo.path)
375 375 assert target_repo.branches['default'] == commit_id
376 376
377 377 commit_id = commits[-3].raw_id
378 378 commit = Reference('rev', commit_id, commit_id)
379 379 target_repo._local_pull(source_repo.path, commit)
380 380 target_repo = MercurialRepository(target_repo.path)
381 381 assert target_repo.branches['default'] == commit_id
382 382
383 383 def test_local_pull_from_same_repo(self):
384 384 reference = Reference('branch', 'default', None)
385 385 with pytest.raises(ValueError):
386 386 self.repo._local_pull(self.repo.path, reference)
387 387
388 388 def test_validate_pull_reference_raises_on_missing_reference(
389 389 self, vcsbackend_hg):
390 390 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
391 391 reference = Reference(
392 392 'book', 'invalid_reference', 'a' * 40)
393 393
394 394 with pytest.raises(CommitDoesNotExistError):
395 395 target_repo._validate_pull_reference(reference)
396 396
397 397 def test_heads(self):
398 398 assert set(self.repo._heads()) == set(self.repo.branches.values())
399 399
400 400 def test_ancestor(self):
401 401 commits = [
402 402 c.raw_id for c in self.repo.get_commits(branch_name='default')]
403 403 assert self.repo._ancestor(commits[-3], commits[-5]) == commits[-5]
404 404 assert self.repo._ancestor(commits[-5], commits[-3]) == commits[-5]
405 405
406 406 def test_local_push(self):
407 407 target_repo = self.get_empty_repo()
408 408
409 409 revisions = list(self.repo.get_commits(branch_name='default'))
410 410 revision = revisions[-5].raw_id
411 411 self.repo._local_push(revision, target_repo.path)
412 412
413 413 target_repo = MercurialRepository(target_repo.path)
414 414
415 415 assert target_repo.branches['default'] == revision
416 416
417 417 def test_hooks_can_be_enabled_for_local_push(self):
418 418 revision = 'deadbeef'
419 419 repo_path = 'test_group/test_repo'
420 420 with mock.patch.object(self.repo, '_remote') as remote_mock:
421 421 self.repo._local_push(revision, repo_path, enable_hooks=True)
422 422 remote_mock.push.assert_called_once_with(
423 423 [revision], repo_path, hooks=True, push_branches=False)
424 424
425 425 def test_local_merge(self, vcsbackend_hg):
426 426 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
427 427 source_repo = vcsbackend_hg.clone_repo(target_repo)
428 428 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
429 429 target_repo = MercurialRepository(target_repo.path)
430 430 target_rev = target_repo.branches['default']
431 431 target_ref = Reference(
432 432 type='branch', name='default', commit_id=target_rev)
433 433 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
434 434 source_repo = MercurialRepository(source_repo.path)
435 435 source_rev = source_repo.branches['default']
436 436 source_ref = Reference(
437 437 type='branch', name='default', commit_id=source_rev)
438 438
439 439 target_repo._local_pull(source_repo.path, source_ref)
440 440
441 441 merge_message = 'Merge message\n\nDescription:...'
442 442 user_name = 'Albert Einstein'
443 443 user_email = 'albert@einstein.com'
444 444 merge_commit_id, needs_push = target_repo._local_merge(
445 445 target_ref, merge_message, user_name, user_email, source_ref)
446 446 assert needs_push
447 447
448 448 target_repo = MercurialRepository(target_repo.path)
449 449 assert target_repo.commit_ids[-3] == target_rev
450 450 assert target_repo.commit_ids[-2] == source_rev
451 451 last_commit = target_repo.get_commit(merge_commit_id)
452 452 assert last_commit.message.strip() == merge_message
453 453 assert last_commit.author == '%s <%s>' % (user_name, user_email)
454 454
455 455 assert not os.path.exists(
456 456 os.path.join(target_repo.path, '.hg', 'merge', 'state'))
457 457
458 458 def test_local_merge_source_is_fast_forward(self, vcsbackend_hg):
459 459 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
460 460 source_repo = vcsbackend_hg.clone_repo(target_repo)
461 461 target_rev = target_repo.branches['default']
462 462 target_ref = Reference(
463 463 type='branch', name='default', commit_id=target_rev)
464 464 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
465 465 source_repo = MercurialRepository(source_repo.path)
466 466 source_rev = source_repo.branches['default']
467 467 source_ref = Reference(
468 468 type='branch', name='default', commit_id=source_rev)
469 469
470 470 target_repo._local_pull(source_repo.path, source_ref)
471 471
472 472 merge_message = 'Merge message\n\nDescription:...'
473 473 user_name = 'Albert Einstein'
474 474 user_email = 'albert@einstein.com'
475 475 merge_commit_id, needs_push = target_repo._local_merge(
476 476 target_ref, merge_message, user_name, user_email, source_ref)
477 477 assert merge_commit_id == source_rev
478 478 assert needs_push
479 479
480 480 target_repo = MercurialRepository(target_repo.path)
481 481 assert target_repo.commit_ids[-2] == target_rev
482 482 assert target_repo.commit_ids[-1] == source_rev
483 483
484 484 assert not os.path.exists(
485 485 os.path.join(target_repo.path, '.hg', 'merge', 'state'))
486 486
487 487 def test_local_merge_source_is_integrated(self, vcsbackend_hg):
488 488 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
489 489 target_rev = target_repo.branches['default']
490 490 target_ref = Reference(
491 491 type='branch', name='default', commit_id=target_rev)
492 492
493 493 merge_message = 'Merge message\n\nDescription:...'
494 494 user_name = 'Albert Einstein'
495 495 user_email = 'albert@einstein.com'
496 496 merge_commit_id, needs_push = target_repo._local_merge(
497 497 target_ref, merge_message, user_name, user_email, target_ref)
498 498 assert merge_commit_id == target_rev
499 499 assert not needs_push
500 500
501 501 target_repo = MercurialRepository(target_repo.path)
502 502 assert target_repo.commit_ids[-1] == target_rev
503 503
504 504 assert not os.path.exists(
505 505 os.path.join(target_repo.path, '.hg', 'merge', 'state'))
506 506
507 507 def test_local_merge_raises_exception_on_conflict(self, vcsbackend_hg):
508 508 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
509 509 source_repo = vcsbackend_hg.clone_repo(target_repo)
510 510 vcsbackend_hg.add_file(target_repo, 'README_MERGE', 'Version 1')
511 511 target_repo = MercurialRepository(target_repo.path)
512 512 target_rev = target_repo.branches['default']
513 513 target_ref = Reference(
514 514 type='branch', name='default', commit_id=target_rev)
515 515 vcsbackend_hg.add_file(source_repo, 'README_MERGE', 'Version 2')
516 516 source_repo = MercurialRepository(source_repo.path)
517 517 source_rev = source_repo.branches['default']
518 518 source_ref = Reference(
519 519 type='branch', name='default', commit_id=source_rev)
520 520
521 521 target_repo._local_pull(source_repo.path, source_ref)
522 522 with pytest.raises(RepositoryError):
523 523 target_repo._local_merge(
524 524 target_ref, 'merge_message', 'user name', 'user@name.com',
525 525 source_ref)
526 526
527 527 # Check we are not left in an intermediate merge state
528 528 assert not os.path.exists(
529 529 os.path.join(target_repo.path, '.hg', 'merge', 'state'))
530 530
531 531 def test_local_merge_of_two_branches_of_the_same_repo(self, backend_hg):
532 532 commits = [
533 533 {'message': 'a'},
534 534 {'message': 'b', 'branch': 'b'},
535 535 ]
536 536 repo = backend_hg.create_repo(commits)
537 537 commit_ids = backend_hg.commit_ids
538 538 target_ref = Reference(
539 539 type='branch', name='default', commit_id=commit_ids['a'])
540 540 source_ref = Reference(
541 541 type='branch', name='b', commit_id=commit_ids['b'])
542 542 merge_message = 'Merge message\n\nDescription:...'
543 543 user_name = 'Albert Einstein'
544 544 user_email = 'albert@einstein.com'
545 545 vcs_repo = repo.scm_instance()
546 546 merge_commit_id, needs_push = vcs_repo._local_merge(
547 547 target_ref, merge_message, user_name, user_email, source_ref)
548 548 assert merge_commit_id != source_ref.commit_id
549 549 assert needs_push is True
550 550 commit = vcs_repo.get_commit(merge_commit_id)
551 551 assert commit.merge is True
552 552 assert commit.message == merge_message
553 553
554 554 def test_maybe_prepare_merge_workspace(self):
555 workspace = self.repo._maybe_prepare_merge_workspace('pr2', 'unused')
555 workspace = self.repo._maybe_prepare_merge_workspace(
556 'pr2', 'unused', 'unused2')
556 557
557 558 assert os.path.isdir(workspace)
558 559 workspace_repo = MercurialRepository(workspace)
559 560 assert workspace_repo.branches == self.repo.branches
560 561
561 562 # Calling it a second time should also succeed
562 workspace = self.repo._maybe_prepare_merge_workspace('pr2', 'unused')
563 workspace = self.repo._maybe_prepare_merge_workspace(
564 'pr2', 'unused', 'unused2')
563 565 assert os.path.isdir(workspace)
564 566
565 567 def test_cleanup_merge_workspace(self):
566 workspace = self.repo._maybe_prepare_merge_workspace('pr3', 'unused')
568 workspace = self.repo._maybe_prepare_merge_workspace(
569 'pr3', 'unused', 'unused2')
567 570 self.repo.cleanup_merge_workspace('pr3')
568 571
569 572 assert not os.path.exists(workspace)
570 573
571 574 def test_cleanup_merge_workspace_invalid_workspace_id(self):
572 575 # No assert: because in case of an inexistent workspace this function
573 576 # should still succeed.
574 577 self.repo.cleanup_merge_workspace('pr4')
575 578
576 579 def test_merge_target_is_bookmark(self, vcsbackend_hg):
577 580 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
578 581 source_repo = vcsbackend_hg.clone_repo(target_repo)
579 582 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
580 583 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
581 584 imc = source_repo.in_memory_commit
582 585 imc.add(FileNode('file_x', content=source_repo.name))
583 586 imc.commit(
584 587 message=u'Automatic commit from repo merge test',
585 588 author=u'Automatic')
586 589 target_commit = target_repo.get_commit()
587 590 source_commit = source_repo.get_commit()
588 591 default_branch = target_repo.DEFAULT_BRANCH_NAME
589 592 bookmark_name = 'bookmark'
590 593 target_repo._update(default_branch)
591 594 target_repo.bookmark(bookmark_name)
592 595 target_ref = Reference('book', bookmark_name, target_commit.raw_id)
593 596 source_ref = Reference('branch', default_branch, source_commit.raw_id)
594 597 workspace = 'test-merge'
595 598
596 599 merge_response = target_repo.merge(
597 600 target_ref, source_repo, source_ref, workspace,
598 601 'test user', 'test@rhodecode.com', 'merge message 1',
599 602 dry_run=False)
600 603 expected_merge_response = MergeResponse(
601 604 True, True, merge_response.merge_ref,
602 605 MergeFailureReason.NONE)
603 606 assert merge_response == expected_merge_response
604 607
605 608 target_repo = backends.get_backend(vcsbackend_hg.alias)(
606 609 target_repo.path)
607 610 target_commits = list(target_repo.get_commits())
608 611 commit_ids = [c.raw_id for c in target_commits[:-1]]
609 612 assert source_ref.commit_id in commit_ids
610 613 assert target_ref.commit_id in commit_ids
611 614
612 615 merge_commit = target_commits[-1]
613 616 assert merge_commit.raw_id == merge_response.merge_ref.commit_id
614 617 assert merge_commit.message.strip() == 'merge message 1'
615 618 assert merge_commit.author == 'test user <test@rhodecode.com>'
616 619
617 620 # Check the bookmark was updated in the target repo
618 621 assert (
619 622 target_repo.bookmarks[bookmark_name] ==
620 623 merge_response.merge_ref.commit_id)
621 624
622 625 def test_merge_source_is_bookmark(self, vcsbackend_hg):
623 626 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
624 627 source_repo = vcsbackend_hg.clone_repo(target_repo)
625 628 imc = source_repo.in_memory_commit
626 629 imc.add(FileNode('file_x', content=source_repo.name))
627 630 imc.commit(
628 631 message=u'Automatic commit from repo merge test',
629 632 author=u'Automatic')
630 633 target_commit = target_repo.get_commit()
631 634 source_commit = source_repo.get_commit()
632 635 default_branch = target_repo.DEFAULT_BRANCH_NAME
633 636 bookmark_name = 'bookmark'
634 637 target_ref = Reference('branch', default_branch, target_commit.raw_id)
635 638 source_repo._update(default_branch)
636 639 source_repo.bookmark(bookmark_name)
637 640 source_ref = Reference('book', bookmark_name, source_commit.raw_id)
638 641 workspace = 'test-merge'
639 642
640 643 merge_response = target_repo.merge(
641 644 target_ref, source_repo, source_ref, workspace,
642 645 'test user', 'test@rhodecode.com', 'merge message 1',
643 646 dry_run=False)
644 647 expected_merge_response = MergeResponse(
645 648 True, True, merge_response.merge_ref,
646 649 MergeFailureReason.NONE)
647 650 assert merge_response == expected_merge_response
648 651
649 652 target_repo = backends.get_backend(vcsbackend_hg.alias)(
650 653 target_repo.path)
651 654 target_commits = list(target_repo.get_commits())
652 655 commit_ids = [c.raw_id for c in target_commits]
653 656 assert source_ref.commit_id == commit_ids[-1]
654 657 assert target_ref.commit_id == commit_ids[-2]
655 658
656 659 def test_merge_target_has_multiple_heads(self, vcsbackend_hg):
657 660 target_repo = vcsbackend_hg.create_repo(number_of_commits=2)
658 661 source_repo = vcsbackend_hg.clone_repo(target_repo)
659 662 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
660 663 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
661 664
662 665 # add an extra head to the target repo
663 666 imc = target_repo.in_memory_commit
664 667 imc.add(FileNode('file_x', content='foo'))
665 668 commits = list(target_repo.get_commits())
666 669 imc.commit(
667 670 message=u'Automatic commit from repo merge test',
668 671 author=u'Automatic', parents=commits[0:1])
669 672
670 673 target_commit = target_repo.get_commit()
671 674 source_commit = source_repo.get_commit()
672 675 default_branch = target_repo.DEFAULT_BRANCH_NAME
673 676 target_repo._update(default_branch)
674 677
675 678 target_ref = Reference('branch', default_branch, target_commit.raw_id)
676 679 source_ref = Reference('branch', default_branch, source_commit.raw_id)
677 680 workspace = 'test-merge'
678 681
679 682 assert len(target_repo._heads(branch='default')) == 2
680 683 expected_merge_response = MergeResponse(
681 684 False, False, None,
682 685 MergeFailureReason.HG_TARGET_HAS_MULTIPLE_HEADS)
683 686 merge_response = target_repo.merge(
684 687 target_ref, source_repo, source_ref, workspace,
685 688 'test user', 'test@rhodecode.com', 'merge message 1',
686 689 dry_run=False)
687 690 assert merge_response == expected_merge_response
688 691
689 692 def test_merge_rebase_source_is_updated_bookmark(self, vcsbackend_hg):
690 693 target_repo = vcsbackend_hg.create_repo(number_of_commits=1)
691 694 source_repo = vcsbackend_hg.clone_repo(target_repo)
692 695 vcsbackend_hg.add_file(target_repo, 'README_MERGE1', 'Version 1')
693 696 vcsbackend_hg.add_file(source_repo, 'README_MERGE2', 'Version 2')
694 697 imc = source_repo.in_memory_commit
695 698 imc.add(FileNode('file_x', content=source_repo.name))
696 699 imc.commit(
697 700 message=u'Automatic commit from repo merge test',
698 701 author=u'Automatic')
699 702 target_commit = target_repo.get_commit()
700 703 source_commit = source_repo.get_commit()
701 704
702 705 vcsbackend_hg.add_file(source_repo, 'LICENSE', 'LICENSE Info')
703 706
704 707 default_branch = target_repo.DEFAULT_BRANCH_NAME
705 708 bookmark_name = 'bookmark'
706 709 source_repo._update(default_branch)
707 710 source_repo.bookmark(bookmark_name)
708 711
709 712 target_ref = Reference('branch', default_branch, target_commit.raw_id)
710 713 source_ref = Reference('book', bookmark_name, source_commit.raw_id)
711 714 workspace = 'test-merge'
712 715
713 716 merge_response = target_repo.merge(
714 717 target_ref, source_repo, source_ref, workspace,
715 718 'test user', 'test@rhodecode.com', 'merge message 1',
716 719 dry_run=False, use_rebase=True)
717 720
718 721 expected_merge_response = MergeResponse(
719 722 True, True, merge_response.merge_ref,
720 723 MergeFailureReason.NONE)
721 724 assert merge_response == expected_merge_response
722 725
723 726 target_repo = backends.get_backend(vcsbackend_hg.alias)(
724 727 target_repo.path)
725 728 last_commit = target_repo.get_commit()
726 729 assert last_commit.message == source_commit.message
727 730 assert last_commit.author == source_commit.author
728 731 # This checks that we effectively did a rebase
729 732 assert last_commit.raw_id != source_commit.raw_id
730 733
731 734 # Check the target has only 4 commits: 2 were already in target and
732 735 # only two should have been added
733 736 assert len(target_repo.commit_ids) == 2 + 2
734 737
735 738
736 739 class TestGetShadowInstance(object):
737 740
738 741 @pytest.fixture
739 742 def repo(self, vcsbackend_hg, monkeypatch):
740 743 repo = vcsbackend_hg.repo
741 744 monkeypatch.setattr(repo, 'config', mock.Mock())
742 745 monkeypatch.setattr('rhodecode.lib.vcs.connection.Hg', mock.Mock())
743 746 return repo
744 747
745 748 def test_passes_config(self, repo):
746 749 shadow = repo._get_shadow_instance(repo.path)
747 750 assert shadow.config == repo.config.copy()
748 751
749 752 def test_disables_hooks(self, repo):
750 753 shadow = repo._get_shadow_instance(repo.path)
751 754 shadow.config.clear_section.assert_called_once_with('hooks')
752 755
753 756 def test_allows_to_keep_hooks(self, repo):
754 757 shadow = repo._get_shadow_instance(repo.path, enable_hooks=True)
755 758 assert not shadow.config.clear_section.called
756 759
757 760
758 761 class TestMercurialCommit(object):
759 762
760 763 def _test_equality(self, commit):
761 764 idx = commit.idx
762 765 assert commit == self.repo.get_commit(commit_idx=idx)
763 766
764 767 def test_equality(self):
765 768 indexes = [0, 10, 20]
766 769 commits = [self.repo.get_commit(commit_idx=idx) for idx in indexes]
767 770 for commit in commits:
768 771 self._test_equality(commit)
769 772
770 773 def test_default_commit(self):
771 774 tip = self.repo.get_commit('tip')
772 775 assert tip == self.repo.get_commit()
773 776 assert tip == self.repo.get_commit(commit_id=None)
774 777 assert tip == self.repo.get_commit(commit_idx=None)
775 778 assert tip == list(self.repo[-1:])[0]
776 779
777 780 def test_root_node(self):
778 781 tip = self.repo.get_commit('tip')
779 782 assert tip.root is tip.get_node('')
780 783
781 784 def test_lazy_fetch(self):
782 785 """
783 786 Test if commit's nodes expands and are cached as we walk through
784 787 the commit. This test is somewhat hard to write as order of tests
785 788 is a key here. Written by running command after command in a shell.
786 789 """
787 790 commit = self.repo.get_commit(commit_idx=45)
788 791 assert len(commit.nodes) == 0
789 792 root = commit.root
790 793 assert len(commit.nodes) == 1
791 794 assert len(root.nodes) == 8
792 795 # accessing root.nodes updates commit.nodes
793 796 assert len(commit.nodes) == 9
794 797
795 798 docs = root.get_node('docs')
796 799 # we haven't yet accessed anything new as docs dir was already cached
797 800 assert len(commit.nodes) == 9
798 801 assert len(docs.nodes) == 8
799 802 # accessing docs.nodes updates commit.nodes
800 803 assert len(commit.nodes) == 17
801 804
802 805 assert docs is commit.get_node('docs')
803 806 assert docs is root.nodes[0]
804 807 assert docs is root.dirs[0]
805 808 assert docs is commit.get_node('docs')
806 809
807 810 def test_nodes_with_commit(self):
808 811 commit = self.repo.get_commit(commit_idx=45)
809 812 root = commit.root
810 813 docs = root.get_node('docs')
811 814 assert docs is commit.get_node('docs')
812 815 api = docs.get_node('api')
813 816 assert api is commit.get_node('docs/api')
814 817 index = api.get_node('index.rst')
815 818 assert index is commit.get_node('docs/api/index.rst')
816 819 assert index is commit.get_node(
817 820 'docs').get_node('api').get_node('index.rst')
818 821
819 822 def test_branch_and_tags(self):
820 823 commit0 = self.repo.get_commit(commit_idx=0)
821 824 assert commit0.branch == 'default'
822 825 assert commit0.tags == []
823 826
824 827 commit10 = self.repo.get_commit(commit_idx=10)
825 828 assert commit10.branch == 'default'
826 829 assert commit10.tags == []
827 830
828 831 commit44 = self.repo.get_commit(commit_idx=44)
829 832 assert commit44.branch == 'web'
830 833
831 834 tip = self.repo.get_commit('tip')
832 835 assert 'tip' in tip.tags
833 836
834 837 def test_bookmarks(self):
835 838 commit0 = self.repo.get_commit(commit_idx=0)
836 839 assert commit0.bookmarks == []
837 840
838 841 def _test_file_size(self, idx, path, size):
839 842 node = self.repo.get_commit(commit_idx=idx).get_node(path)
840 843 assert node.is_file()
841 844 assert node.size == size
842 845
843 846 def test_file_size(self):
844 847 to_check = (
845 848 (10, 'setup.py', 1068),
846 849 (20, 'setup.py', 1106),
847 850 (60, 'setup.py', 1074),
848 851
849 852 (10, 'vcs/backends/base.py', 2921),
850 853 (20, 'vcs/backends/base.py', 3936),
851 854 (60, 'vcs/backends/base.py', 6189),
852 855 )
853 856 for idx, path, size in to_check:
854 857 self._test_file_size(idx, path, size)
855 858
856 859 def test_file_history_from_commits(self):
857 860 node = self.repo[10].get_node('setup.py')
858 861 commit_ids = [commit.raw_id for commit in node.history]
859 862 assert ['3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'] == commit_ids
860 863
861 864 node = self.repo[20].get_node('setup.py')
862 865 node_ids = [commit.raw_id for commit in node.history]
863 866 assert ['eada5a770da98ab0dd7325e29d00e0714f228d09',
864 867 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'] == node_ids
865 868
866 869 # special case we check history from commit that has this particular
867 870 # file changed this means we check if it's included as well
868 871 node = self.repo.get_commit('eada5a770da98ab0dd7325e29d00e0714f228d09')\
869 872 .get_node('setup.py')
870 873 node_ids = [commit.raw_id for commit in node.history]
871 874 assert ['eada5a770da98ab0dd7325e29d00e0714f228d09',
872 875 '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'] == node_ids
873 876
874 877 def test_file_history(self):
875 878 # we can only check if those commits are present in the history
876 879 # as we cannot update this test every time file is changed
877 880 files = {
878 881 'setup.py': [7, 18, 45, 46, 47, 69, 77],
879 882 'vcs/nodes.py': [
880 883 7, 8, 24, 26, 30, 45, 47, 49, 56, 57, 58, 59, 60, 61, 73, 76],
881 884 'vcs/backends/hg.py': [
882 885 4, 5, 6, 11, 12, 13, 14, 15, 16, 21, 22, 23, 26, 27, 28, 30,
883 886 31, 33, 35, 36, 37, 38, 39, 40, 41, 44, 45, 47, 48, 49, 53, 54,
884 887 55, 58, 60, 61, 67, 68, 69, 70, 73, 77, 78, 79, 82],
885 888 }
886 889 for path, indexes in files.items():
887 890 tip = self.repo.get_commit(commit_idx=indexes[-1])
888 891 node = tip.get_node(path)
889 892 node_indexes = [commit.idx for commit in node.history]
890 893 assert set(indexes).issubset(set(node_indexes)), (
891 894 "We assumed that %s is subset of commits for which file %s "
892 895 "has been changed, and history of that node returned: %s"
893 896 % (indexes, path, node_indexes))
894 897
895 898 def test_file_annotate(self):
896 899 files = {
897 900 'vcs/backends/__init__.py': {
898 901 89: {
899 902 'lines_no': 31,
900 903 'commits': [
901 904 32, 32, 61, 32, 32, 37, 32, 32, 32, 44,
902 905 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
903 906 32, 32, 32, 32, 37, 32, 37, 37, 32,
904 907 32, 32
905 908 ]
906 909 },
907 910 20: {
908 911 'lines_no': 1,
909 912 'commits': [4]
910 913 },
911 914 55: {
912 915 'lines_no': 31,
913 916 'commits': [
914 917 32, 32, 45, 32, 32, 37, 32, 32, 32, 44,
915 918 37, 37, 37, 37, 45, 37, 44, 37, 37, 37,
916 919 32, 32, 32, 32, 37, 32, 37, 37, 32,
917 920 32, 32
918 921 ]
919 922 }
920 923 },
921 924 'vcs/exceptions.py': {
922 925 89: {
923 926 'lines_no': 18,
924 927 'commits': [
925 928 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
926 929 16, 16, 17, 16, 16, 18, 18, 18
927 930 ]
928 931 },
929 932 20: {
930 933 'lines_no': 18,
931 934 'commits': [
932 935 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
933 936 16, 16, 17, 16, 16, 18, 18, 18
934 937 ]
935 938 },
936 939 55: {
937 940 'lines_no': 18,
938 941 'commits': [
939 942 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
940 943 17, 16, 16, 18, 18, 18
941 944 ]
942 945 }
943 946 },
944 947 'MANIFEST.in': {
945 948 89: {
946 949 'lines_no': 5,
947 950 'commits': [7, 7, 7, 71, 71]
948 951 },
949 952 20: {
950 953 'lines_no': 3,
951 954 'commits': [7, 7, 7]
952 955 },
953 956 55: {
954 957 'lines_no': 3,
955 958 'commits': [7, 7, 7]
956 959 }
957 960 }
958 961 }
959 962
960 963 for fname, commit_dict in files.items():
961 964 for idx, __ in commit_dict.items():
962 965 commit = self.repo.get_commit(commit_idx=idx)
963 966 l1_1 = [x[1] for x in commit.get_file_annotate(fname)]
964 967 l1_2 = [x[2]().raw_id for x in commit.get_file_annotate(fname)]
965 968 assert l1_1 == l1_2
966 969 l1 = l1_2 = [
967 970 x[2]().idx for x in commit.get_file_annotate(fname)]
968 971 l2 = files[fname][idx]['commits']
969 972 assert l1 == l2, (
970 973 "The lists of commit for %s@commit_id%s"
971 974 "from annotation list should match each other,"
972 975 "got \n%s \nvs \n%s " % (fname, idx, l1, l2))
973 976
974 977 def test_commit_state(self):
975 978 """
976 979 Tests which files have been added/changed/removed at particular commit
977 980 """
978 981
979 982 # commit_id 46ad32a4f974:
980 983 # hg st --rev 46ad32a4f974
981 984 # changed: 13
982 985 # added: 20
983 986 # removed: 1
984 987 changed = set([
985 988 '.hgignore', 'README.rst', 'docs/conf.py', 'docs/index.rst',
986 989 'setup.py', 'tests/test_hg.py', 'tests/test_nodes.py',
987 990 'vcs/__init__.py', 'vcs/backends/__init__.py',
988 991 'vcs/backends/base.py', 'vcs/backends/hg.py', 'vcs/nodes.py',
989 992 'vcs/utils/__init__.py'])
990 993
991 994 added = set([
992 995 'docs/api/backends/hg.rst', 'docs/api/backends/index.rst',
993 996 'docs/api/index.rst', 'docs/api/nodes.rst',
994 997 'docs/api/web/index.rst', 'docs/api/web/simplevcs.rst',
995 998 'docs/installation.rst', 'docs/quickstart.rst', 'setup.cfg',
996 999 'vcs/utils/baseui_config.py', 'vcs/utils/web.py',
997 1000 'vcs/web/__init__.py', 'vcs/web/exceptions.py',
998 1001 'vcs/web/simplevcs/__init__.py', 'vcs/web/simplevcs/exceptions.py',
999 1002 'vcs/web/simplevcs/middleware.py', 'vcs/web/simplevcs/models.py',
1000 1003 'vcs/web/simplevcs/settings.py', 'vcs/web/simplevcs/utils.py',
1001 1004 'vcs/web/simplevcs/views.py'])
1002 1005
1003 1006 removed = set(['docs/api.rst'])
1004 1007
1005 1008 commit64 = self.repo.get_commit('46ad32a4f974')
1006 1009 assert set((node.path for node in commit64.added)) == added
1007 1010 assert set((node.path for node in commit64.changed)) == changed
1008 1011 assert set((node.path for node in commit64.removed)) == removed
1009 1012
1010 1013 # commit_id b090f22d27d6:
1011 1014 # hg st --rev b090f22d27d6
1012 1015 # changed: 13
1013 1016 # added: 20
1014 1017 # removed: 1
1015 1018 commit88 = self.repo.get_commit('b090f22d27d6')
1016 1019 assert set((node.path for node in commit88.added)) == set()
1017 1020 assert set((node.path for node in commit88.changed)) == \
1018 1021 set(['.hgignore'])
1019 1022 assert set((node.path for node in commit88.removed)) == set()
1020 1023
1021 1024 #
1022 1025 # 85:
1023 1026 # added: 2 [
1024 1027 # 'vcs/utils/diffs.py', 'vcs/web/simplevcs/views/diffs.py']
1025 1028 # changed: 4 ['vcs/web/simplevcs/models.py', ...]
1026 1029 # removed: 1 ['vcs/utils/web.py']
1027 1030 commit85 = self.repo.get_commit(commit_idx=85)
1028 1031 assert set((node.path for node in commit85.added)) == set([
1029 1032 'vcs/utils/diffs.py',
1030 1033 'vcs/web/simplevcs/views/diffs.py'])
1031 1034 assert set((node.path for node in commit85.changed)) == set([
1032 1035 'vcs/web/simplevcs/models.py',
1033 1036 'vcs/web/simplevcs/utils.py',
1034 1037 'vcs/web/simplevcs/views/__init__.py',
1035 1038 'vcs/web/simplevcs/views/repository.py',
1036 1039 ])
1037 1040 assert set((node.path for node in commit85.removed)) == \
1038 1041 set(['vcs/utils/web.py'])
1039 1042
1040 1043 def test_files_state(self):
1041 1044 """
1042 1045 Tests state of FileNodes.
1043 1046 """
1044 1047 commit = self.repo.get_commit(commit_idx=85)
1045 1048 node = commit.get_node('vcs/utils/diffs.py')
1046 1049 assert node.state, NodeState.ADDED
1047 1050 assert node.added
1048 1051 assert not node.changed
1049 1052 assert not node.not_changed
1050 1053 assert not node.removed
1051 1054
1052 1055 commit = self.repo.get_commit(commit_idx=88)
1053 1056 node = commit.get_node('.hgignore')
1054 1057 assert node.state, NodeState.CHANGED
1055 1058 assert not node.added
1056 1059 assert node.changed
1057 1060 assert not node.not_changed
1058 1061 assert not node.removed
1059 1062
1060 1063 commit = self.repo.get_commit(commit_idx=85)
1061 1064 node = commit.get_node('setup.py')
1062 1065 assert node.state, NodeState.NOT_CHANGED
1063 1066 assert not node.added
1064 1067 assert not node.changed
1065 1068 assert node.not_changed
1066 1069 assert not node.removed
1067 1070
1068 1071 # If node has REMOVED state then trying to fetch it would raise
1069 1072 # CommitError exception
1070 1073 commit = self.repo.get_commit(commit_idx=2)
1071 1074 path = 'vcs/backends/BaseRepository.py'
1072 1075 with pytest.raises(NodeDoesNotExistError):
1073 1076 commit.get_node(path)
1074 1077 # but it would be one of ``removed`` (commit's attribute)
1075 1078 assert path in [rf.path for rf in commit.removed]
1076 1079
1077 1080 def test_commit_message_is_unicode(self):
1078 1081 for cm in self.repo:
1079 1082 assert type(cm.message) == unicode
1080 1083
1081 1084 def test_commit_author_is_unicode(self):
1082 1085 for cm in self.repo:
1083 1086 assert type(cm.author) == unicode
1084 1087
1085 1088 def test_repo_files_content_is_unicode(self):
1086 1089 test_commit = self.repo.get_commit(commit_idx=100)
1087 1090 for node in test_commit.get_node('/'):
1088 1091 if node.is_file():
1089 1092 assert type(node.content) == unicode
1090 1093
1091 1094 def test_wrong_path(self):
1092 1095 # There is 'setup.py' in the root dir but not there:
1093 1096 path = 'foo/bar/setup.py'
1094 1097 with pytest.raises(VCSError):
1095 1098 self.repo.get_commit().get_node(path)
1096 1099
1097 1100 def test_author_email(self):
1098 1101 assert 'marcin@python-blog.com' == \
1099 1102 self.repo.get_commit('b986218ba1c9').author_email
1100 1103 assert 'lukasz.balcerzak@python-center.pl' == \
1101 1104 self.repo.get_commit('3803844fdbd3').author_email
1102 1105 assert '' == self.repo.get_commit('84478366594b').author_email
1103 1106
1104 1107 def test_author_username(self):
1105 1108 assert 'Marcin Kuzminski' == \
1106 1109 self.repo.get_commit('b986218ba1c9').author_name
1107 1110 assert 'Lukasz Balcerzak' == \
1108 1111 self.repo.get_commit('3803844fdbd3').author_name
1109 1112 assert 'marcink' == \
1110 1113 self.repo.get_commit('84478366594b').author_name
1111 1114
1112 1115
1113 1116 class TestLargeFileRepo(object):
1114 1117
1115 1118 def test_large_file(self, backend_hg):
1116 1119 repo = backend_hg.create_test_repo('largefiles', make_db_config())
1117 1120
1118 1121 tip = repo.scm_instance().get_commit()
1119 1122 node = tip.get_node('.hglf/thisfileislarge')
1120 1123
1121 1124 lf_node = node.get_largefile_node()
1122 1125
1123 1126 assert lf_node.is_largefile() is True
1124 1127 assert lf_node.size == 1024000
1125 1128 assert lf_node.name == '.hglf/thisfileislarge'
1126 1129
1127 1130
1128 1131 class TestGetBranchName(object):
1129 1132 def test_returns_ref_name_when_type_is_branch(self):
1130 1133 ref = self._create_ref('branch', 'fake-name')
1131 1134 result = self.repo._get_branch_name(ref)
1132 1135 assert result == ref.name
1133 1136
1134 1137 @pytest.mark.parametrize("type_", ("book", "tag"))
1135 1138 def test_queries_remote_when_type_is_not_branch(self, type_):
1136 1139 ref = self._create_ref(type_, 'wrong-fake-name')
1137 1140 with mock.patch.object(self.repo, "_remote") as remote_mock:
1138 1141 remote_mock.ctx_branch.return_value = "fake-name"
1139 1142 result = self.repo._get_branch_name(ref)
1140 1143 assert result == "fake-name"
1141 1144 remote_mock.ctx_branch.assert_called_once_with(ref.commit_id)
1142 1145
1143 1146 def _create_ref(self, type_, name):
1144 1147 ref = mock.Mock()
1145 1148 ref.type = type_
1146 1149 ref.name = 'wrong-fake-name'
1147 1150 ref.commit_id = "deadbeef"
1148 1151 return ref
1149 1152
1150 1153
1151 1154 class TestIsTheSameBranch(object):
1152 1155 def test_returns_true_when_branches_are_equal(self):
1153 1156 source_ref = mock.Mock(name="source-ref")
1154 1157 target_ref = mock.Mock(name="target-ref")
1155 1158 branch_name_patcher = mock.patch.object(
1156 1159 self.repo, "_get_branch_name", return_value="default")
1157 1160 with branch_name_patcher as branch_name_mock:
1158 1161 result = self.repo._is_the_same_branch(source_ref, target_ref)
1159 1162
1160 1163 expected_calls = [mock.call(source_ref), mock.call(target_ref)]
1161 1164 assert branch_name_mock.call_args_list == expected_calls
1162 1165 assert result is True
1163 1166
1164 1167 def test_returns_false_when_branches_are_not_equal(self):
1165 1168 source_ref = mock.Mock(name="source-ref")
1166 1169 source_ref.name = "source-branch"
1167 1170 target_ref = mock.Mock(name="target-ref")
1168 1171 source_ref.name = "target-branch"
1169 1172
1170 1173 def side_effect(ref):
1171 1174 return ref.name
1172 1175
1173 1176 branch_name_patcher = mock.patch.object(
1174 1177 self.repo, "_get_branch_name", side_effect=side_effect)
1175 1178 with branch_name_patcher as branch_name_mock:
1176 1179 result = self.repo._is_the_same_branch(source_ref, target_ref)
1177 1180
1178 1181 expected_calls = [mock.call(source_ref), mock.call(target_ref)]
1179 1182 assert branch_name_mock.call_args_list == expected_calls
1180 1183 assert result is False
@@ -1,183 +1,183 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import pytest
22 22
23 23 from mock import call, patch
24 24
25 25 from rhodecode.lib.vcs.backends.base import Reference
26 26
27 27
28 28 class TestMercurialRemoteRepoInvalidation(object):
29 29 """
30 30 If the VCSServer is running with multiple processes or/and instances.
31 31 Operations on repositories are potentially handled by different processes
32 32 in a random fashion. The mercurial repository objects used in the VCSServer
33 33 are caching the commits of the repo. Therefore we have to invalidate the
34 34 VCSServer caching of these objects after a writing operation.
35 35 """
36 36
37 37 # Default reference used as a dummy during tests.
38 38 default_ref = Reference('branch', 'default', None)
39 39
40 40 # Methods of vcsserver.hg.HgRemote that are "writing" operations.
41 41 writing_methods = [
42 42 'bookmark',
43 43 'commit',
44 44 'merge',
45 45 'pull',
46 46 'pull_cmd',
47 47 'rebase',
48 48 'strip',
49 49 'tag',
50 50 ]
51 51
52 52 @pytest.mark.parametrize('method_name, method_args', [
53 53 ('_local_merge', [default_ref, None, None, None, default_ref]),
54 54 ('_local_pull', ['', default_ref]),
55 55 ('bookmark', [None]),
56 56 ('pull', ['', default_ref]),
57 57 ('remove_tag', ['mytag', None]),
58 58 ('strip', [None]),
59 59 ('tag', ['newtag', None]),
60 60 ])
61 61 def test_method_invokes_invalidate_on_remote_repo(
62 62 self, method_name, method_args, backend_hg):
63 63 """
64 64 Check that the listed methods are invalidating the VCSServer cache
65 65 after invoking a writing method of their remote repository object.
66 66 """
67 67 tags = {'mytag': 'mytag-id'}
68 68
69 69 def add_tag(name, raw_id, *args, **kwds):
70 70 tags[name] = raw_id
71 71
72 72 repo = backend_hg.repo.scm_instance()
73 73 with patch.object(repo, '_remote') as remote:
74 74 remote.lookup.return_value = ('commit-id', 'commit-idx')
75 75 remote.tags.return_value = tags
76 76 remote._get_tags.return_value = tags
77 77 remote.tag.side_effect = add_tag
78 78
79 79 # Invoke method.
80 80 method = getattr(repo, method_name)
81 81 method(*method_args)
82 82
83 83 # Assert that every "writing" method is followed by an invocation
84 84 # of the cache invalidation method.
85 85 for counter, method_call in enumerate(remote.method_calls):
86 86 call_name = method_call[0]
87 87 if call_name in self.writing_methods:
88 88 next_call = remote.method_calls[counter + 1]
89 89 assert next_call == call.invalidate_vcs_cache()
90 90
91 91 def _prepare_shadow_repo(self, pull_request):
92 92 """
93 93 Helper that creates a shadow repo that can be used to reproduce the
94 94 CommitDoesNotExistError when pulling in from target and source
95 95 references.
96 96 """
97 97 from rhodecode.model.pull_request import PullRequestModel
98 98
99 99 target_vcs = pull_request.target_repo.scm_instance()
100 100 target_ref = pull_request.target_ref_parts
101 101 source_ref = pull_request.source_ref_parts
102 102
103 103 # Create shadow repository.
104 104 pr = PullRequestModel()
105 105 workspace_id = pr._workspace_id(pull_request)
106 106 shadow_repository_path = target_vcs._maybe_prepare_merge_workspace(
107 workspace_id, target_ref)
107 workspace_id, target_ref, source_ref)
108 108 shadow_repo = target_vcs._get_shadow_instance(shadow_repository_path)
109 109
110 110 # This will populate the cache of the mercurial repository object
111 111 # inside of the VCSServer.
112 112 shadow_repo.get_commit()
113 113
114 114 return shadow_repo, source_ref, target_ref
115 115
116 116 @pytest.mark.backends('hg')
117 117 def test_commit_does_not_exist_error_happens(self, pr_util, app):
118 118 """
119 119 This test is somewhat special. It does not really test the system
120 120 instead it is more or less a precondition for the
121 121 "test_commit_does_not_exist_error_does_not_happen". It deactivates the
122 122 cache invalidation and asserts that the error occurs.
123 123 """
124 124 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
125 125
126 126 pull_request = pr_util.create_pull_request()
127 127 target_vcs = pull_request.target_repo.scm_instance()
128 128 source_vcs = pull_request.source_repo.scm_instance()
129 129 shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(
130 130 pull_request)
131 131
132 132 # Pull from target and source references but without invalidation of
133 133 # RemoteRepo objects and without VCSServer caching of mercurial
134 134 # repository objects.
135 135 with patch.object(shadow_repo._remote, 'invalidate_vcs_cache'):
136 136 # NOTE: Do not use patch.dict() to disable the cache because it
137 137 # restores the WHOLE dict and not only the patched keys.
138 138 shadow_repo._remote._wire['cache'] = False
139 139 shadow_repo._local_pull(target_vcs.path, target_ref)
140 140 shadow_repo._local_pull(source_vcs.path, source_ref)
141 141 shadow_repo._remote._wire.pop('cache')
142 142
143 143 # Try to lookup the target_ref in shadow repo. This should work because
144 144 # the shadow repo is a clone of the target and always contains all off
145 145 # it's commits in the initial cache.
146 146 shadow_repo.get_commit(target_ref.commit_id)
147 147
148 148 # If we try to lookup the source_ref it should fail because the shadow
149 149 # repo commit cache doesn't get invalidated. (Due to patched
150 150 # invalidation and caching above).
151 151 with pytest.raises(CommitDoesNotExistError):
152 152 shadow_repo.get_commit(source_ref.commit_id)
153 153
154 154 @pytest.mark.backends('hg')
155 155 def test_commit_does_not_exist_error_does_not_happen(self, pr_util, app):
156 156 """
157 157 This test simulates a pull request merge in which the pull operations
158 158 are handled by a different VCSServer process than all other operations.
159 159 Without correct cache invalidation this leads to an error when
160 160 retrieving the pulled commits afterwards.
161 161 """
162 162
163 163 pull_request = pr_util.create_pull_request()
164 164 target_vcs = pull_request.target_repo.scm_instance()
165 165 source_vcs = pull_request.source_repo.scm_instance()
166 166 shadow_repo, source_ref, target_ref = self._prepare_shadow_repo(
167 167 pull_request)
168 168
169 169 # Pull from target and source references without without VCSServer
170 170 # caching of mercurial repository objects but with active invalidation
171 171 # of RemoteRepo objects.
172 172 # NOTE: Do not use patch.dict() to disable the cache because it
173 173 # restores the WHOLE dict and not only the patched keys.
174 174 shadow_repo._remote._wire['cache'] = False
175 175 shadow_repo._local_pull(target_vcs.path, target_ref)
176 176 shadow_repo._local_pull(source_vcs.path, source_ref)
177 177 shadow_repo._remote._wire.pop('cache')
178 178
179 179 # Try to lookup the target and source references in shadow repo. This
180 180 # should work because the RemoteRepo object gets invalidated during the
181 181 # above pull operations.
182 182 shadow_repo.get_commit(target_ref.commit_id)
183 183 shadow_repo.get_commit(source_ref.commit_id)
General Comments 0
You need to be logged in to leave comments. Login now