20
20
from bzrlib import (
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)
28
from bzrlib.tests import (
34
load_tests = scenarios.load_tests_apply_scenarios
51
37
# TODO: Test commit with some added, and added-but-missing files
65
def vary_by_conflicts():
66
for conflict in example_conflicts:
67
yield (conflict.__class__.__name__, {"conflict": conflict})
79
70
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')
108
72
def test_resolve_conflict_dir(self):
109
73
tree = self.make_branch_and_tree('.')
110
74
self.build_tree_contents([('hello', 'hello world4'),
161
125
self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
164
class TestConflictStanzas(tests.TestCase):
128
class TestPerConflict(tests.TestCase):
130
scenarios = scenarios.multiply_scenarios(vary_by_conflicts())
132
def test_stringification(self):
133
text = unicode(self.conflict)
134
self.assertContainsString(text, self.conflict.path)
135
self.assertContainsString(text.lower(), "conflict")
136
self.assertContainsString(repr(self.conflict),
137
self.conflict.__class__.__name__)
166
139
def test_stanza_roundtrip(self):
167
# write and read our example stanza.
168
stanza_iter = example_conflicts.to_stanzas()
169
processed = conflicts.ConflictList.from_stanzas(stanza_iter)
170
for o, p in zip(processed, example_conflicts):
171
self.assertEqual(o, p)
173
self.assertIsInstance(o.path, unicode)
175
if o.file_id is not None:
176
self.assertIsInstance(o.file_id, str)
178
conflict_path = getattr(o, 'conflict_path', None)
179
if conflict_path is not None:
180
self.assertIsInstance(conflict_path, unicode)
182
conflict_file_id = getattr(o, 'conflict_file_id', None)
183
if conflict_file_id is not None:
184
self.assertIsInstance(conflict_file_id, str)
141
o = conflicts.Conflict.factory(**p.as_stanza().as_dict())
142
self.assertEqual(o, p)
144
self.assertIsInstance(o.path, unicode)
146
if o.file_id is not None:
147
self.assertIsInstance(o.file_id, str)
149
conflict_path = getattr(o, 'conflict_path', None)
150
if conflict_path is not None:
151
self.assertIsInstance(conflict_path, unicode)
153
conflict_file_id = getattr(o, 'conflict_file_id', None)
154
if conflict_file_id is not None:
155
self.assertIsInstance(conflict_file_id, str)
186
157
def test_stanzification(self):
187
for stanza in example_conflicts.to_stanzas():
188
if 'file_id' in stanza:
189
# In Stanza form, the file_id has to be unicode.
190
self.assertStartsWith(stanza['file_id'], u'\xeed')
191
self.assertStartsWith(stanza['path'], u'p\xe5th')
192
if 'conflict_path' in stanza:
193
self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
194
if 'conflict_file_id' in stanza:
195
self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
158
stanza = self.conflict.as_stanza()
159
if 'file_id' in stanza:
160
# In Stanza form, the file_id has to be unicode.
161
self.assertStartsWith(stanza['file_id'], u'\xeed')
162
self.assertStartsWith(stanza['path'], u'p\xe5th')
163
if 'conflict_path' in stanza:
164
self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
165
if 'conflict_file_id' in stanza:
166
self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
169
class TestConflictList(tests.TestCase):
171
def test_stanzas_roundtrip(self):
172
stanzas_iter = example_conflicts.to_stanzas()
173
processed = conflicts.ConflictList.from_stanzas(stanzas_iter)
174
self.assertEqual(example_conflicts, processed)
176
def test_stringification(self):
177
for text, o in zip(example_conflicts.to_strings(), example_conflicts):
178
self.assertEqual(text, unicode(o))
198
181
# FIXME: The shell-like tests should be converted to real whitebox tests... or
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
275
"""The scenario list for the conflict type defined by the class.
277
Each scenario is of the form:
278
(common, (left_name, left_dict), (right_name, right_dict))
282
* left_name and right_name are the scenario names that will be combined
284
* left_dict and right_dict are the attributes specific to each half of
285
the scenario. They should include at least 'actions' and 'check' and
286
will be available as '_this' and '_other' test instance attributes.
288
Daughters classes are free to add their specific attributes as they see
289
fit in any of the three dicts.
291
This is a class method so that load_tests can find it.
293
'_base_actions' in the common dict, 'actions' and 'check' in the left
294
and right dicts use names that map to methods in the test classes. Some
295
prefixes are added to these names to get the correspong methods (see
296
_get_actions() and _get_check()). The motivation here is to avoid
297
collisions in the class namespace.
326
301
super(TestParametrizedResolveConflicts, self).setUp()
366
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
368
_conflict_type = conflicts.TextConflict
370
# Set by the scenarios
371
# path and file-id for the file involved in the conflict
375
scenarios = mirror_scenarios(
377
# File modified on both sides
378
(dict(_base_actions='create_file',
379
_path='file', _file_id='file-id'),
381
dict(actions='modify_file_A', check='file_has_content_A')),
383
dict(actions='modify_file_B', check='file_has_content_B')),),
384
# File modified on both sides in dir
385
(dict(_base_actions='create_file_in_dir',
386
_path='dir/file', _file_id='file-id'),
387
('filed_modified_A_in_dir',
388
dict(actions='modify_file_A',
389
check='file_in_dir_has_content_A')),
391
dict(actions='modify_file_B',
392
check='file_in_dir_has_content_B')),),
395
def do_create_file(self, path='file'):
396
return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
398
def do_modify_file_A(self):
399
return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
401
def do_modify_file_B(self):
402
return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
404
def check_file_has_content_A(self, path='file'):
405
self.assertFileEqual('trunk content\nfeature A\n',
406
osutils.pathjoin('branch', path))
408
def check_file_has_content_B(self, path='file'):
409
self.assertFileEqual('trunk content\nfeature B\n',
410
osutils.pathjoin('branch', path))
412
def do_create_file_in_dir(self):
413
return [('add', ('dir', 'dir-id', 'directory', '')),
414
] + self.do_create_file('dir/file')
416
def check_file_in_dir_has_content_A(self):
417
self.check_file_has_content_A('dir/file')
419
def check_file_in_dir_has_content_B(self):
420
self.check_file_has_content_B('dir/file')
422
def _get_resolve_path_arg(self, wt, action):
425
def assertTextConflict(self, wt, c):
426
self.assertEqual(self._file_id, c.file_id)
427
self.assertEqual(self._path, c.path)
428
_assert_conflict = assertTextConflict
391
431
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
393
_conflict_type = conflicts.ContentsConflict,
433
_conflict_type = conflicts.ContentsConflict
395
# Set by load_tests from scenarios()
435
# Set by the scenarios
396
436
# path and file-id for the file involved in the conflict
440
scenarios = mirror_scenarios(
403
442
# File modified/deleted
404
443
(dict(_base_actions='create_file',
405
444
_path='file', _file_id='file-id'),
407
446
dict(actions='modify_file', check='file_has_more_content')),
409
448
dict(actions='delete_file', check='file_doesnt_exist')),),
411
return mirror_scenarios(base_scenarios)
449
# File modified/deleted in dir
450
(dict(_base_actions='create_file_in_dir',
451
_path='dir/file', _file_id='file-id'),
452
('file_modified_in_dir',
453
dict(actions='modify_file_in_dir',
454
check='file_in_dir_has_more_content')),
455
('file_deleted_in_dir',
456
dict(actions='delete_file',
457
check='file_in_dir_doesnt_exist')),),
413
460
def do_create_file(self):
414
461
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
423
470
return [('unversion', 'file-id')]
425
472
def check_file_doesnt_exist(self):
426
self.failIfExists('branch/file')
473
self.assertPathDoesNotExist('branch/file')
475
def do_create_file_in_dir(self):
476
return [('add', ('dir', 'dir-id', 'directory', '')),
477
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
479
def do_modify_file_in_dir(self):
480
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
482
def check_file_in_dir_has_more_content(self):
483
self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
485
def check_file_in_dir_doesnt_exist(self):
486
self.assertPathDoesNotExist('branch/dir/file')
428
488
def _get_resolve_path_arg(self, wt, action):
429
489
return self._path
457
516
# PathConflicts deletion handling requires a special
458
517
# hard-coded value
459
518
path='<deleted>', file_id='file-id')),),
519
# File renamed/deleted in dir
520
(dict(_base_actions='create_file_in_dir'),
521
('file_renamed_in_dir',
522
dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
523
path='dir/new-file', file_id='file-id')),
525
dict(actions='delete_file', check='file_in_dir_doesnt_exist',
526
# PathConflicts deletion handling requires a special
528
path='<deleted>', file_id='file-id')),),
460
529
# File renamed/renamed differently
461
530
(dict(_base_actions='create_file'),
496
564
return [('rename', ('file', 'new-file'))]
498
566
def check_file_renamed(self):
499
self.failIfExists('branch/file')
500
self.failUnlessExists('branch/new-file')
567
self.assertPathDoesNotExist('branch/file')
568
self.assertPathExists('branch/new-file')
502
570
def do_rename_file2(self):
503
571
return [('rename', ('file', 'new-file2'))]
505
573
def check_file_renamed2(self):
506
self.failIfExists('branch/file')
507
self.failUnlessExists('branch/new-file2')
574
self.assertPathDoesNotExist('branch/file')
575
self.assertPathExists('branch/new-file2')
509
577
def do_rename_dir(self):
510
578
return [('rename', ('dir', 'new-dir'))]
512
580
def check_dir_renamed(self):
513
self.failIfExists('branch/dir')
514
self.failUnlessExists('branch/new-dir')
581
self.assertPathDoesNotExist('branch/dir')
582
self.assertPathExists('branch/new-dir')
516
584
def do_rename_dir2(self):
517
585
return [('rename', ('dir', 'new-dir2'))]
519
587
def check_dir_renamed2(self):
520
self.failIfExists('branch/dir')
521
self.failUnlessExists('branch/new-dir2')
588
self.assertPathDoesNotExist('branch/dir')
589
self.assertPathExists('branch/new-dir2')
523
591
def do_delete_file(self):
524
592
return [('unversion', 'file-id')]
526
594
def check_file_doesnt_exist(self):
527
self.failIfExists('branch/file')
595
self.assertPathDoesNotExist('branch/file')
529
597
def do_delete_dir(self):
530
598
return [('unversion', 'dir-id')]
532
600
def check_dir_doesnt_exist(self):
533
self.failIfExists('branch/dir')
601
self.assertPathDoesNotExist('branch/dir')
603
def do_create_file_in_dir(self):
604
return [('add', ('dir', 'dir-id', 'directory', '')),
605
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
607
def do_rename_file_in_dir(self):
608
return [('rename', ('dir/file', 'dir/new-file'))]
610
def check_file_in_dir_renamed(self):
611
self.assertPathDoesNotExist('branch/dir/file')
612
self.assertPathExists('branch/dir/new-file')
614
def check_file_in_dir_doesnt_exist(self):
615
self.assertPathDoesNotExist('branch/dir/file')
535
617
def _get_resolve_path_arg(self, wt, action):
536
618
tpath = self._this['path']
692
767
def test_keep_them_all(self):
693
768
self.run_script("""
694
769
$ bzr resolve dir
695
$ bzr commit --strict -m 'No more conflicts nor unknown files'
770
2>2 conflict(s) resolved, 0 remaining
771
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
698
774
def test_adopt_child(self):
699
775
self.run_script("""
700
$ bzr mv dir/file2 file2
776
$ bzr mv -q dir/file2 file2
777
$ bzr rm -q dir --force
702
778
$ bzr resolve dir
703
$ bzr commit --strict -m 'No more conflicts nor unknown files'
779
2>2 conflict(s) resolved, 0 remaining
780
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
706
783
def test_kill_them_all(self):
707
784
self.run_script("""
785
$ bzr rm -q dir --force
709
786
$ bzr resolve dir
710
$ bzr commit --strict -m 'No more conflicts nor unknown files'
787
2>2 conflict(s) resolved, 0 remaining
788
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
713
791
def test_resolve_taking_this(self):
714
792
self.run_script("""
715
793
$ bzr resolve --take-this dir
716
$ bzr commit --strict -m 'No more conflicts nor unknown files'
795
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
719
798
def test_resolve_taking_other(self):
720
799
self.run_script("""
721
800
$ bzr resolve --take-other dir
722
$ bzr commit --strict -m 'No more conflicts nor unknown files'
802
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
732
813
$ echo 'trunk content' >dir/file
734
$ bzr commit -m 'Create trunk'
736
$ bzr rm dir/file --force
738
$ bzr commit -m 'Remove dir/file'
740
$ bzr branch . -r 1 ../branch
815
$ bzr commit -m 'Create trunk' -q
816
$ bzr rm -q dir/file --force
817
$ bzr rm -q dir --force
818
$ bzr commit -q -m 'Remove dir/file'
819
$ bzr branch -q . -r 1 ../branch
742
821
$ echo 'branch content' >dir/file2
744
$ bzr commit -m 'Add dir/file2 in branch'
822
$ bzr add -q dir/file2
823
$ bzr commit -q -m 'Add dir/file2 in branch'
746
824
$ bzr merge ../trunk
748
826
2>Conflict: can't delete dir because it is not empty. Not deleting.
753
831
def test_keep_them_all(self):
754
832
self.run_script("""
755
833
$ bzr resolve dir
756
$ bzr commit --strict -m 'No more conflicts nor unknown files'
834
2>2 conflict(s) resolved, 0 remaining
835
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
759
838
def test_adopt_child(self):
760
839
self.run_script("""
761
$ bzr mv dir/file2 file2
840
$ bzr mv -q dir/file2 file2
841
$ bzr rm -q dir --force
763
842
$ bzr resolve dir
764
$ bzr commit --strict -m 'No more conflicts nor unknown files'
843
2>2 conflict(s) resolved, 0 remaining
844
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
767
847
def test_kill_them_all(self):
768
848
self.run_script("""
849
$ bzr rm -q dir --force
770
850
$ bzr resolve dir
771
$ bzr commit --strict -m 'No more conflicts nor unknown files'
851
2>2 conflict(s) resolved, 0 remaining
852
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
774
855
def test_resolve_taking_this(self):
775
856
self.run_script("""
776
857
$ bzr resolve --take-this dir
777
$ bzr commit --strict -m 'No more conflicts nor unknown files'
858
2>2 conflict(s) resolved, 0 remaining
859
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
780
862
def test_resolve_taking_other(self):
781
863
self.run_script("""
782
864
$ bzr resolve --take-other dir
783
$ bzr commit --strict -m 'No more conflicts nor unknown files'
867
2>2 conflict(s) resolved, 0 remaining
868
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
787
872
class TestResolveParentLoop(TestParametrizedResolveConflicts):
789
_conflict_type = conflicts.ParentLoop,
874
_conflict_type = conflicts.ParentLoop
791
876
_this_args = None
792
877
_other_args = None
796
# Each side dict additionally defines:
797
# - dir_id: the directory being moved
798
# - target_id: The target directory
799
# - xfail: whether the test is expected to fail if the action is
800
# involved as 'other'
879
# Each side dict additionally defines:
880
# - dir_id: the directory being moved
881
# - target_id: The target directory
882
# - xfail: whether the test is expected to fail if the action is
883
# involved as 'other'
884
scenarios = mirror_scenarios(
802
886
# Dirs moved into each other
803
887
(dict(_base_actions='create_dir1_dir2'),
804
888
('dir1_into_dir2',
826
909
return [('rename', ('dir1', 'dir2/dir1'))]
828
911
def check_dir1_moved(self):
829
self.failIfExists('branch/dir1')
830
self.failUnlessExists('branch/dir2/dir1')
912
self.assertPathDoesNotExist('branch/dir1')
913
self.assertPathExists('branch/dir2/dir1')
832
915
def do_move_dir2_into_dir1(self):
833
916
return [('rename', ('dir2', 'dir1/dir2'))]
835
918
def check_dir2_moved(self):
836
self.failIfExists('branch/dir2')
837
self.failUnlessExists('branch/dir1/dir2')
919
self.assertPathDoesNotExist('branch/dir2')
920
self.assertPathExists('branch/dir1/dir2')
839
922
def do_create_dir1_4(self):
840
923
return [('add', ('dir1', 'dir1-id', 'directory', '')),
846
929
return [('rename', ('dir1', 'dir3/dir4/dir1'))]
848
931
def check_dir1_2_moved(self):
849
self.failIfExists('branch/dir1')
850
self.failUnlessExists('branch/dir3/dir4/dir1')
851
self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
932
self.assertPathDoesNotExist('branch/dir1')
933
self.assertPathExists('branch/dir3/dir4/dir1')
934
self.assertPathExists('branch/dir3/dir4/dir1/dir2')
853
936
def do_move_dir3_into_dir2(self):
854
937
return [('rename', ('dir3', 'dir1/dir2/dir3'))]
856
939
def check_dir3_4_moved(self):
857
self.failIfExists('branch/dir3')
858
self.failUnlessExists('branch/dir1/dir2/dir3')
859
self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
940
self.assertPathDoesNotExist('branch/dir3')
941
self.assertPathExists('branch/dir1/dir2/dir3')
942
self.assertPathExists('branch/dir1/dir2/dir3/dir4')
861
944
def _get_resolve_path_arg(self, wt, action):
862
945
# ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
907
990
def test_take_this(self):
908
991
self.run_script("""
909
$ bzr rm foo.new --force
992
$ bzr rm -q foo.new --force
910
993
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
911
994
# aside ? -- vila 090916
913
996
$ bzr resolve foo.new
914
$ bzr commit --strict -m 'No more conflicts nor unknown files'
997
2>1 conflict(s) resolved, 0 remaining
998
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
917
1001
def test_take_other(self):
918
1002
self.run_script("""
1003
$ bzr rm -q foo --force
1004
$ bzr mv -q foo.new foo
921
1005
$ bzr resolve foo
922
$ bzr commit --strict -m 'No more conflicts nor unknown files'
1006
2>1 conflict(s) resolved, 0 remaining
1007
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
925
1010
def test_resolve_taking_this(self):
926
1011
self.run_script("""
927
1012
$ bzr resolve --take-this foo.new
928
$ bzr commit --strict -m 'No more conflicts nor unknown files'
1014
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
931
1017
def test_resolve_taking_other(self):
932
1018
self.run_script("""
933
1019
$ bzr resolve --take-other foo.new
934
$ bzr commit --strict -m 'No more conflicts nor unknown files'
1021
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'