20
20
from bzrlib import (
28
from bzrlib.tests import (
34
load_tests = scenarios.load_tests_apply_scenarios
29
from bzrlib.tests import script
32
def load_tests(standard_tests, module, loader):
33
result = loader.suiteClass()
35
sp_tests, remaining_tests = tests.split_suite_by_condition(
36
standard_tests, tests.condition_isinstance((
37
TestParametrizedResolveConflicts,
39
# Each test class defines its own scenarios. This is needed for
40
# TestResolvePathConflictBefore531967 that verifies that the same tests as
41
# TestResolvePathConflict still pass.
42
for test in tests.iter_suite_tests(sp_tests):
43
tests.apply_scenarios(test, test.scenarios(), result)
45
# No parametrization for the remaining tests
46
result.addTests(remaining_tests)
37
51
# TODO: Test commit with some added, and added-but-missing files
65
79
class TestConflicts(tests.TestCaseWithTransport):
81
def test_conflicts(self):
82
"""Conflicts are detected properly"""
83
# Use BzrDirFormat6 so we can fake conflicts
84
tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
85
self.build_tree_contents([('hello', 'hello world4'),
86
('hello.THIS', 'hello world2'),
87
('hello.BASE', 'hello world1'),
88
('hello.OTHER', 'hello world3'),
89
('hello.sploo.BASE', 'yellowworld'),
90
('hello.sploo.OTHER', 'yellowworld2'),
93
self.assertLength(6, list(tree.list_files()))
95
tree_conflicts = tree.conflicts()
96
self.assertLength(2, tree_conflicts)
97
self.assertTrue('hello' in tree_conflicts[0].path)
98
self.assertTrue('hello.sploo' in tree_conflicts[1].path)
99
conflicts.restore('hello')
100
conflicts.restore('hello.sploo')
101
self.assertLength(0, tree.conflicts())
102
self.assertFileEqual('hello world2', 'hello')
103
self.assertFalse(os.path.lexists('hello.sploo'))
104
self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
105
self.assertRaises(errors.NotConflicted,
106
conflicts.restore, 'hello.sploo')
67
108
def test_resolve_conflict_dir(self):
68
109
tree = self.make_branch_and_tree('.')
69
110
self.build_tree_contents([('hello', 'hello world4'),
251
"""The scenario list for the conflict type defined by the class.
253
Each scenario is of the form:
254
(common, (left_name, left_dict), (right_name, right_dict))
258
* left_name and right_name are the scenario names that will be combined
260
* left_dict and right_dict are the attributes specific to each half of
261
the scenario. They should include at least 'actions' and 'check' and
262
will be available as '_this' and '_other' test instance attributes.
264
Daughters classes are free to add their specific attributes as they see
265
fit in any of the three dicts.
267
This is a class method so that load_tests can find it.
269
'_base_actions' in the common dict, 'actions' and 'check' in the left
270
and right dicts use names that map to methods in the test classes. Some
271
prefixes are added to these names to get the correspong methods (see
272
_get_actions() and _get_check()). The motivation here is to avoid
273
collisions in the class namespace.
298
"""Return the scenario list for the conflict type defined by the class.
300
Each scenario is of the form:
301
(common, (left_name, left_dict), (right_name, right_dict))
305
* left_name and right_name are the scenario names that will be combined
307
* left_dict and right_dict are the attributes specific to each half of
308
the scenario. They should include at least 'actions' and 'check' and
309
will be available as '_this' and '_other' test instance attributes.
311
Daughters classes are free to add their specific attributes as they see
312
fit in any of the three dicts.
314
This is a class method so that load_tests can find it.
316
'_base_actions' in the common dict, 'actions' and 'check' in the left
317
and right dicts use names that map to methods in the test classes. Some
318
prefixes are added to these names to get the correspong methods (see
319
_get_actions() and _get_check()). The motivation here is to avoid
320
collisions in the class namespace.
322
# Only concrete classes return actual scenarios
277
326
super(TestParametrizedResolveConflicts, self).setUp()
342
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
344
_conflict_type = conflicts.TextConflict
346
# Set by the scenarios
347
# path and file-id for the file involved in the conflict
351
scenarios = mirror_scenarios(
353
# File modified on both sides
354
(dict(_base_actions='create_file',
355
_path='file', _file_id='file-id'),
357
dict(actions='modify_file_A', check='file_has_content_A')),
359
dict(actions='modify_file_B', check='file_has_content_B')),),
360
# File modified on both sides in dir
361
(dict(_base_actions='create_file_in_dir',
362
_path='dir/file', _file_id='file-id'),
363
('filed_modified_A_in_dir',
364
dict(actions='modify_file_A',
365
check='file_in_dir_has_content_A')),
367
dict(actions='modify_file_B',
368
check='file_in_dir_has_content_B')),),
371
def do_create_file(self, path='file'):
372
return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
374
def do_modify_file_A(self):
375
return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
377
def do_modify_file_B(self):
378
return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
380
def check_file_has_content_A(self, path='file'):
381
self.assertFileEqual('trunk content\nfeature A\n',
382
osutils.pathjoin('branch', path))
384
def check_file_has_content_B(self, path='file'):
385
self.assertFileEqual('trunk content\nfeature B\n',
386
osutils.pathjoin('branch', path))
388
def do_create_file_in_dir(self):
389
return [('add', ('dir', 'dir-id', 'directory', '')),
390
] + self.do_create_file('dir/file')
392
def check_file_in_dir_has_content_A(self):
393
self.check_file_has_content_A('dir/file')
395
def check_file_in_dir_has_content_B(self):
396
self.check_file_has_content_B('dir/file')
398
def _get_resolve_path_arg(self, wt, action):
401
def assertTextConflict(self, wt, c):
402
self.assertEqual(self._file_id, c.file_id)
403
self.assertEqual(self._path, c.path)
404
_assert_conflict = assertTextConflict
407
391
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
409
_conflict_type = conflicts.ContentsConflict
393
_conflict_type = conflicts.ContentsConflict,
411
# Set by the scenarios
395
# Set by load_tests from scenarios()
412
396
# path and file-id for the file involved in the conflict
416
scenarios = mirror_scenarios(
418
403
# File modified/deleted
419
404
(dict(_base_actions='create_file',
420
405
_path='file', _file_id='file-id'),
422
407
dict(actions='modify_file', check='file_has_more_content')),
424
409
dict(actions='delete_file', check='file_doesnt_exist')),),
425
# File modified/deleted in dir
426
(dict(_base_actions='create_file_in_dir',
427
_path='dir/file', _file_id='file-id'),
428
('file_modified_in_dir',
429
dict(actions='modify_file_in_dir',
430
check='file_in_dir_has_more_content')),
431
('file_deleted_in_dir',
432
dict(actions='delete_file',
433
check='file_in_dir_doesnt_exist')),),
411
return mirror_scenarios(base_scenarios)
436
413
def do_create_file(self):
437
414
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
446
423
return [('unversion', 'file-id')]
448
425
def check_file_doesnt_exist(self):
449
self.assertPathDoesNotExist('branch/file')
451
def do_create_file_in_dir(self):
452
return [('add', ('dir', 'dir-id', 'directory', '')),
453
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
455
def do_modify_file_in_dir(self):
456
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
458
def check_file_in_dir_has_more_content(self):
459
self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
461
def check_file_in_dir_doesnt_exist(self):
462
self.assertPathDoesNotExist('branch/dir/file')
426
self.failIfExists('branch/file')
464
428
def _get_resolve_path_arg(self, wt, action):
465
429
return self._path
492
457
# PathConflicts deletion handling requires a special
493
458
# hard-coded value
494
459
path='<deleted>', file_id='file-id')),),
495
# File renamed/deleted in dir
496
(dict(_base_actions='create_file_in_dir'),
497
('file_renamed_in_dir',
498
dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
499
path='dir/new-file', file_id='file-id')),
501
dict(actions='delete_file', check='file_in_dir_doesnt_exist',
502
# PathConflicts deletion handling requires a special
504
path='<deleted>', file_id='file-id')),),
505
460
# File renamed/renamed differently
506
461
(dict(_base_actions='create_file'),
540
496
return [('rename', ('file', 'new-file'))]
542
498
def check_file_renamed(self):
543
self.assertPathDoesNotExist('branch/file')
544
self.assertPathExists('branch/new-file')
499
self.failIfExists('branch/file')
500
self.failUnlessExists('branch/new-file')
546
502
def do_rename_file2(self):
547
503
return [('rename', ('file', 'new-file2'))]
549
505
def check_file_renamed2(self):
550
self.assertPathDoesNotExist('branch/file')
551
self.assertPathExists('branch/new-file2')
506
self.failIfExists('branch/file')
507
self.failUnlessExists('branch/new-file2')
553
509
def do_rename_dir(self):
554
510
return [('rename', ('dir', 'new-dir'))]
556
512
def check_dir_renamed(self):
557
self.assertPathDoesNotExist('branch/dir')
558
self.assertPathExists('branch/new-dir')
513
self.failIfExists('branch/dir')
514
self.failUnlessExists('branch/new-dir')
560
516
def do_rename_dir2(self):
561
517
return [('rename', ('dir', 'new-dir2'))]
563
519
def check_dir_renamed2(self):
564
self.assertPathDoesNotExist('branch/dir')
565
self.assertPathExists('branch/new-dir2')
520
self.failIfExists('branch/dir')
521
self.failUnlessExists('branch/new-dir2')
567
523
def do_delete_file(self):
568
524
return [('unversion', 'file-id')]
570
526
def check_file_doesnt_exist(self):
571
self.assertPathDoesNotExist('branch/file')
527
self.failIfExists('branch/file')
573
529
def do_delete_dir(self):
574
530
return [('unversion', 'dir-id')]
576
532
def check_dir_doesnt_exist(self):
577
self.assertPathDoesNotExist('branch/dir')
579
def do_create_file_in_dir(self):
580
return [('add', ('dir', 'dir-id', 'directory', '')),
581
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
583
def do_rename_file_in_dir(self):
584
return [('rename', ('dir/file', 'dir/new-file'))]
586
def check_file_in_dir_renamed(self):
587
self.assertPathDoesNotExist('branch/dir/file')
588
self.assertPathExists('branch/dir/new-file')
590
def check_file_in_dir_doesnt_exist(self):
591
self.assertPathDoesNotExist('branch/dir/file')
533
self.failIfExists('branch/dir')
593
535
def _get_resolve_path_arg(self, wt, action):
594
536
tpath = self._this['path']
840
778
$ bzr resolve --take-other dir
841
779
2>deleted dir/file2
843
2>2 conflict(s) resolved, 0 remaining
844
781
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
848
785
class TestResolveParentLoop(TestParametrizedResolveConflicts):
850
_conflict_type = conflicts.ParentLoop
787
_conflict_type = conflicts.ParentLoop,
852
789
_this_args = None
853
790
_other_args = None
855
# Each side dict additionally defines:
856
# - dir_id: the directory being moved
857
# - target_id: The target directory
858
# - xfail: whether the test is expected to fail if the action is
859
# involved as 'other'
860
scenarios = mirror_scenarios(
794
# Each side dict additionally defines:
795
# - dir_id: the directory being moved
796
# - target_id: The target directory
797
# - xfail: whether the test is expected to fail if the action is
798
# involved as 'other'
862
800
# Dirs moved into each other
863
801
(dict(_base_actions='create_dir1_dir2'),
864
802
('dir1_into_dir2',
885
824
return [('rename', ('dir1', 'dir2/dir1'))]
887
826
def check_dir1_moved(self):
888
self.assertPathDoesNotExist('branch/dir1')
889
self.assertPathExists('branch/dir2/dir1')
827
self.failIfExists('branch/dir1')
828
self.failUnlessExists('branch/dir2/dir1')
891
830
def do_move_dir2_into_dir1(self):
892
831
return [('rename', ('dir2', 'dir1/dir2'))]
894
833
def check_dir2_moved(self):
895
self.assertPathDoesNotExist('branch/dir2')
896
self.assertPathExists('branch/dir1/dir2')
834
self.failIfExists('branch/dir2')
835
self.failUnlessExists('branch/dir1/dir2')
898
837
def do_create_dir1_4(self):
899
838
return [('add', ('dir1', 'dir1-id', 'directory', '')),
905
844
return [('rename', ('dir1', 'dir3/dir4/dir1'))]
907
846
def check_dir1_2_moved(self):
908
self.assertPathDoesNotExist('branch/dir1')
909
self.assertPathExists('branch/dir3/dir4/dir1')
910
self.assertPathExists('branch/dir3/dir4/dir1/dir2')
847
self.failIfExists('branch/dir1')
848
self.failUnlessExists('branch/dir3/dir4/dir1')
849
self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
912
851
def do_move_dir3_into_dir2(self):
913
852
return [('rename', ('dir3', 'dir1/dir2/dir3'))]
915
854
def check_dir3_4_moved(self):
916
self.assertPathDoesNotExist('branch/dir3')
917
self.assertPathExists('branch/dir1/dir2/dir3')
918
self.assertPathExists('branch/dir1/dir2/dir3/dir4')
855
self.failIfExists('branch/dir3')
856
self.failUnlessExists('branch/dir1/dir2/dir3')
857
self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
920
859
def _get_resolve_path_arg(self, wt, action):
921
860
# ParentLoop says: moving <conflict_path> into <path>. Cancelled move.