1"""Unit tests for BazaarClient.""" 2 3from __future__ import unicode_literals 4 5import os 6from hashlib import md5 7 8from nose import SkipTest 9 10from rbtools.clients import RepositoryInfo 11from rbtools.clients.bazaar import BazaarClient 12from rbtools.clients.errors import TooManyRevisionsError 13from rbtools.clients.tests import FOO, FOO1, FOO2, FOO3, SCMClientTests 14from rbtools.utils.filesystem import is_exe_in_path, make_tempdir 15from rbtools.utils.process import execute 16 17 18class BazaarClientTests(SCMClientTests): 19 """Unit tests for BazaarClient.""" 20 21 def setUp(self): 22 if not is_exe_in_path('bzr'): 23 raise SkipTest('bzr not found in path') 24 25 super(BazaarClientTests, self).setUp() 26 27 self.set_user_home( 28 os.path.join(self.testdata_dir, 'homedir')) 29 30 self.original_branch = make_tempdir() 31 self._run_bzr(['init', '.'], cwd=self.original_branch) 32 self._bzr_add_file_commit('foo.txt', FOO, 'initial commit', 33 cwd=self.original_branch) 34 35 self.child_branch = make_tempdir() 36 self._run_bzr(['branch', '--use-existing-dir', self.original_branch, 37 self.child_branch], 38 cwd=self.original_branch) 39 self.client = BazaarClient(options=self.options) 40 41 self.options.parent_branch = None 42 43 def _run_bzr(self, command, *args, **kwargs): 44 return execute(['bzr'] + command, *args, **kwargs) 45 46 def _bzr_add_file_commit(self, filename, data, msg, cwd=None, *args, 47 **kwargs): 48 """Add a file to a Bazaar repository. 49 50 Args: 51 filename (unicode): 52 The name of the file to add. 53 54 data (bytes): 55 The data to write to the file. 56 57 msg (unicode): 58 The commit message to use. 59 60 cwd (unicode, optional): 61 A working directory to use when running the bzr commands. 62 63 *args (list): 64 Positional arguments to pass through to 65 :py:func:`rbtools.utils.process.execute`. 66 67 **kwargs (dict): 68 Keyword arguments to pass through to 69 :py:func:`rbtools.utils.process.execute`. 70 """ 71 if cwd is not None: 72 filename = os.path.join(cwd, filename) 73 74 with open(filename, 'wb') as f: 75 f.write(data) 76 77 self._run_bzr(['add', filename], cwd=cwd, *args, **kwargs) 78 self._run_bzr(['commit', '-m', msg, '--author', 'Test User'], 79 cwd=cwd, *args, **kwargs) 80 81 def _compare_diffs(self, filename, full_diff, expected_diff_digest, 82 change_type='modified'): 83 """Testing that the full_diff for ``filename`` matches the 84 ``expected_diff``.""" 85 diff_lines = full_diff.splitlines() 86 87 self.assertEqual(('=== %s file \'%s\'' 88 % (change_type, filename)).encode('utf-8'), 89 diff_lines[0]) 90 self.assertTrue(diff_lines[1].startswith( 91 ('--- %s\t' % filename).encode('utf-8'))) 92 self.assertTrue(diff_lines[2].startswith( 93 ('+++ %s\t' % filename).encode('utf-8'))) 94 95 diff_body = b'\n'.join(diff_lines[3:]) 96 self.assertEqual(md5(diff_body).hexdigest(), expected_diff_digest) 97 98 def _count_files_in_diff(self, diff): 99 return len([ 100 line 101 for line in diff.split(b'\n') 102 if line.startswith(b'===') 103 ]) 104 105 def test_get_repository_info_original_branch(self): 106 """Testing BazaarClient get_repository_info with original branch""" 107 os.chdir(self.original_branch) 108 ri = self.client.get_repository_info() 109 110 self.assertTrue(isinstance(ri, RepositoryInfo)) 111 self.assertEqual(os.path.realpath(ri.path), 112 os.path.realpath(self.original_branch)) 113 self.assertTrue(ri.supports_parent_diffs) 114 115 self.assertEqual(ri.base_path, '/') 116 self.assertFalse(ri.supports_changesets) 117 118 def test_get_repository_info_child_branch(self): 119 """Testing BazaarClient get_repository_info with child branch""" 120 os.chdir(self.child_branch) 121 ri = self.client.get_repository_info() 122 123 self.assertTrue(isinstance(ri, RepositoryInfo)) 124 self.assertEqual(os.path.realpath(ri.path), 125 os.path.realpath(self.child_branch)) 126 self.assertTrue(ri.supports_parent_diffs) 127 128 self.assertEqual(ri.base_path, "/") 129 self.assertFalse(ri.supports_changesets) 130 131 def test_get_repository_info_no_branch(self): 132 """Testing BazaarClient get_repository_info, no branch""" 133 self.chdir_tmp() 134 ri = self.client.get_repository_info() 135 self.assertEqual(ri, None) 136 137 def test_too_many_revisions(self): 138 """Testing BazaarClient parse_revision_spec with too many revisions""" 139 self.assertRaises(TooManyRevisionsError, 140 self.client.parse_revision_spec, 141 [1, 2, 3]) 142 143 def test_diff_simple(self): 144 """Testing BazaarClient simple diff case""" 145 os.chdir(self.child_branch) 146 147 self._bzr_add_file_commit('foo.txt', FOO1, 'delete and modify stuff') 148 149 revisions = self.client.parse_revision_spec([]) 150 result = self.client.diff(revisions) 151 self.assertTrue(isinstance(result, dict)) 152 self.assertTrue('diff' in result) 153 154 self._compare_diffs('foo.txt', result['diff'], 155 'a6326b53933f8b255a4b840485d8e210') 156 157 def test_diff_exclude(self): 158 """Testing BazaarClient diff with file exclusion""" 159 os.chdir(self.child_branch) 160 161 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 162 self._bzr_add_file_commit('exclude.txt', FOO2, 'commit 2') 163 164 revisions = self.client.parse_revision_spec([]) 165 result = self.client.diff(revisions, exclude_patterns=['exclude.txt']) 166 self.assertTrue(isinstance(result, dict)) 167 self.assertTrue('diff' in result) 168 169 self._compare_diffs('foo.txt', result['diff'], 170 'a6326b53933f8b255a4b840485d8e210') 171 172 self.assertEqual(self._count_files_in_diff(result['diff']), 1) 173 174 def test_diff_exclude_in_subdir(self): 175 """Testing BazaarClient diff with file exclusion in a subdirectory""" 176 os.chdir(self.child_branch) 177 178 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 179 180 os.mkdir('subdir') 181 os.chdir('subdir') 182 183 self._bzr_add_file_commit('exclude.txt', FOO2, 'commit 2') 184 185 revisions = self.client.parse_revision_spec([]) 186 result = self.client.diff(revisions, 187 exclude_patterns=['exclude.txt', '.']) 188 self.assertTrue(isinstance(result, dict)) 189 self.assertTrue('diff' in result) 190 191 self._compare_diffs('foo.txt', result['diff'], 192 'a6326b53933f8b255a4b840485d8e210') 193 194 self.assertEqual(self._count_files_in_diff(result['diff']), 1) 195 196 def test_diff_exclude_root_pattern_in_subdir(self): 197 """Testing BazaarClient diff with file exclusion in the repo root""" 198 os.chdir(self.child_branch) 199 200 self._bzr_add_file_commit('exclude.txt', FOO2, 'commit 1') 201 202 os.mkdir('subdir') 203 os.chdir('subdir') 204 205 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 2') 206 207 revisions = self.client.parse_revision_spec([]) 208 result = self.client.diff( 209 revisions, 210 exclude_patterns=[os.path.sep + 'exclude.txt', 211 os.path.sep + 'subdir']) 212 213 self.assertTrue(isinstance(result, dict)) 214 self.assertTrue('diff' in result) 215 216 self._compare_diffs(os.path.join('subdir', 'foo.txt'), result['diff'], 217 '4deffcb296180fa166eddff2512bd0e4', 218 change_type='added') 219 220 def test_diff_specific_files(self): 221 """Testing BazaarClient diff with specific files""" 222 os.chdir(self.child_branch) 223 224 self._bzr_add_file_commit('foo.txt', FOO1, 'delete and modify stuff') 225 self._bzr_add_file_commit('bar.txt', b'baz', 'added bar') 226 227 revisions = self.client.parse_revision_spec([]) 228 result = self.client.diff(revisions, ['foo.txt']) 229 self.assertTrue(isinstance(result, dict)) 230 self.assertTrue('diff' in result) 231 232 self._compare_diffs('foo.txt', result['diff'], 233 'a6326b53933f8b255a4b840485d8e210') 234 235 def test_diff_simple_multiple(self): 236 """Testing BazaarClient simple diff with multiple commits case""" 237 os.chdir(self.child_branch) 238 239 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 240 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2') 241 self._bzr_add_file_commit('foo.txt', FOO3, 'commit 3') 242 243 revisions = self.client.parse_revision_spec([]) 244 result = self.client.diff(revisions) 245 self.assertTrue(isinstance(result, dict)) 246 self.assertTrue('diff' in result) 247 248 self._compare_diffs('foo.txt', result['diff'], 249 '4109cc082dce22288c2f1baca9b107b6') 250 251 def test_diff_parent(self): 252 """Testing BazaarClient diff with changes only in the parent branch""" 253 self._bzr_add_file_commit('foo.txt', FOO1, 'delete and modify stuff', 254 cwd=self.child_branch) 255 256 grand_child_branch = make_tempdir() 257 self._run_bzr(['branch', '--use-existing-dir', self.child_branch, 258 grand_child_branch], 259 cwd=self.child_branch) 260 os.chdir(grand_child_branch) 261 262 revisions = self.client.parse_revision_spec([]) 263 result = self.client.diff(revisions) 264 self.assertTrue(isinstance(result, dict)) 265 self.assertTrue('diff' in result) 266 267 self.assertEqual(result['diff'], None) 268 269 def test_diff_grand_parent(self): 270 """Testing BazaarClient diff with changes between a 2nd level 271 descendant""" 272 self._bzr_add_file_commit('foo.txt', FOO1, 'delete and modify stuff', 273 cwd=self.child_branch) 274 275 grand_child_branch = make_tempdir() 276 self._run_bzr(['branch', '--use-existing-dir', self.child_branch, 277 grand_child_branch], 278 cwd=self.child_branch) 279 os.chdir(grand_child_branch) 280 281 # Requesting the diff between the grand child branch and its grand 282 # parent: 283 self.options.parent_branch = self.original_branch 284 285 revisions = self.client.parse_revision_spec([]) 286 result = self.client.diff(revisions) 287 self.assertTrue(isinstance(result, dict)) 288 self.assertTrue('diff' in result) 289 290 self._compare_diffs('foo.txt', result['diff'], 291 'a6326b53933f8b255a4b840485d8e210') 292 293 def test_guessed_summary_and_description(self): 294 """Testing BazaarClient guessing summary and description""" 295 os.chdir(self.child_branch) 296 297 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 298 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2') 299 self._bzr_add_file_commit('foo.txt', FOO3, 'commit 3') 300 301 self.options.guess_summary = True 302 self.options.guess_description = True 303 revisions = self.client.parse_revision_spec([]) 304 commit_message = self.client.get_commit_message(revisions) 305 306 self.assertEqual('commit 3', commit_message['summary']) 307 308 description = commit_message['description'] 309 self.assertTrue('commit 1' in description) 310 self.assertTrue('commit 2' in description) 311 self.assertFalse('commit 3' in description) 312 313 def test_guessed_summary_and_description_in_grand_parent_branch(self): 314 """Testing BazaarClient guessing summary and description for grand 315 parent branch""" 316 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1', 317 cwd=self.child_branch) 318 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2', 319 cwd=self.child_branch) 320 self._bzr_add_file_commit('foo.txt', FOO3, 'commit 3', 321 cwd=self.child_branch) 322 323 self.options.guess_summary = True 324 self.options.guess_description = True 325 326 grand_child_branch = make_tempdir() 327 self._run_bzr(['branch', '--use-existing-dir', self.child_branch, 328 grand_child_branch], 329 cwd=self.child_branch) 330 os.chdir(grand_child_branch) 331 332 # Requesting the diff between the grand child branch and its grand 333 # parent: 334 self.options.parent_branch = self.original_branch 335 336 revisions = self.client.parse_revision_spec([]) 337 commit_message = self.client.get_commit_message(revisions) 338 339 self.assertEqual('commit 3', commit_message['summary']) 340 341 description = commit_message['description'] 342 self.assertTrue('commit 1' in description) 343 self.assertTrue('commit 2' in description) 344 self.assertFalse('commit 3' in description) 345 346 def test_guessed_summary_and_description_with_revision_range(self): 347 """Testing BazaarClient guessing summary and description with a 348 revision range""" 349 os.chdir(self.child_branch) 350 351 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 352 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2') 353 self._bzr_add_file_commit('foo.txt', FOO3, 'commit 3') 354 355 self.options.guess_summary = True 356 self.options.guess_description = True 357 revisions = self.client.parse_revision_spec(['2..3']) 358 commit_message = self.client.get_commit_message(revisions) 359 360 self.assertEqual('commit 2', commit_message['summary']) 361 self.assertEqual('commit 2', commit_message['description']) 362 363 def test_parse_revision_spec_no_args(self): 364 """Testing BazaarClient.parse_revision_spec with no specified 365 revisions""" 366 os.chdir(self.child_branch) 367 368 base_commit_id = self.client._get_revno() 369 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 370 tip_commit_id = self.client._get_revno() 371 372 revisions = self.client.parse_revision_spec() 373 self.assertTrue(isinstance(revisions, dict)) 374 self.assertTrue('base' in revisions) 375 self.assertTrue('tip' in revisions) 376 self.assertTrue('parent_base' not in revisions) 377 self.assertEqual(revisions['base'], base_commit_id) 378 self.assertEqual(revisions['tip'], tip_commit_id) 379 380 def test_parse_revision_spec_one_arg(self): 381 """Testing BazaarClient.parse_revision_spec with one specified 382 revision""" 383 os.chdir(self.child_branch) 384 385 base_commit_id = self.client._get_revno() 386 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 387 tip_commit_id = self.client._get_revno() 388 389 revisions = self.client.parse_revision_spec([tip_commit_id]) 390 self.assertTrue(isinstance(revisions, dict)) 391 self.assertTrue('base' in revisions) 392 self.assertTrue('tip' in revisions) 393 self.assertTrue('parent_base' not in revisions) 394 self.assertEqual(revisions['base'], base_commit_id) 395 self.assertEqual(revisions['tip'], tip_commit_id) 396 397 def test_parse_revision_spec_one_arg_parent(self): 398 """Testing BazaarClient.parse_revision_spec with one specified 399 revision and a parent diff""" 400 os.chdir(self.original_branch) 401 parent_base_commit_id = self.client._get_revno() 402 403 grand_child_branch = make_tempdir() 404 self._run_bzr(['branch', '--use-existing-dir', self.child_branch, 405 grand_child_branch]) 406 os.chdir(grand_child_branch) 407 408 base_commit_id = self.client._get_revno() 409 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2') 410 tip_commit_id = self.client._get_revno() 411 412 self.options.parent_branch = self.child_branch 413 414 revisions = self.client.parse_revision_spec([tip_commit_id]) 415 self.assertTrue(isinstance(revisions, dict)) 416 self.assertTrue('parent_base' in revisions) 417 self.assertTrue('base' in revisions) 418 self.assertTrue('tip' in revisions) 419 self.assertEqual(revisions['parent_base'], parent_base_commit_id) 420 self.assertEqual(revisions['base'], base_commit_id) 421 self.assertEqual(revisions['tip'], tip_commit_id) 422 423 def test_parse_revision_spec_one_arg_split(self): 424 """Testing BazaarClient.parse_revision_spec with R1..R2 syntax""" 425 os.chdir(self.child_branch) 426 427 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 428 base_commit_id = self.client._get_revno() 429 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2') 430 tip_commit_id = self.client._get_revno() 431 432 revisions = self.client.parse_revision_spec( 433 ['%s..%s' % (base_commit_id, tip_commit_id)]) 434 self.assertTrue(isinstance(revisions, dict)) 435 self.assertTrue('parent_base' not in revisions) 436 self.assertTrue('base' in revisions) 437 self.assertTrue('tip' in revisions) 438 self.assertEqual(revisions['base'], base_commit_id) 439 self.assertEqual(revisions['tip'], tip_commit_id) 440 441 def test_parse_revision_spec_two_args(self): 442 """Testing BazaarClient.parse_revision_spec with two revisions""" 443 os.chdir(self.child_branch) 444 445 self._bzr_add_file_commit('foo.txt', FOO1, 'commit 1') 446 base_commit_id = self.client._get_revno() 447 self._bzr_add_file_commit('foo.txt', FOO2, 'commit 2') 448 tip_commit_id = self.client._get_revno() 449 450 revisions = self.client.parse_revision_spec( 451 [base_commit_id, tip_commit_id]) 452 self.assertTrue(isinstance(revisions, dict)) 453 self.assertTrue('parent_base' not in revisions) 454 self.assertTrue('base' in revisions) 455 self.assertTrue('tip' in revisions) 456 self.assertEqual(revisions['base'], base_commit_id) 457 self.assertEqual(revisions['tip'], tip_commit_id) 458