33
54
# The order of 'path' here is important - do not let it
34
55
# be a sorted list.
35
example_conflicts = ConflictList([
36
MissingParent('Not deleting', 'pathg', 'idg'),
37
ContentsConflict('patha', 'ida'),
38
TextConflict('patha'),
39
PathConflict('pathb', 'pathc', 'idb'),
40
DuplicateID('Unversioned existing file', 'pathc', 'pathc2', 'idc', 'idc'),
41
DuplicateEntry('Moved existing file to', 'pathdd.moved', 'pathd', 'idd',
43
ParentLoop('Cancelled move', 'pathe', 'path2e', None, 'id2e'),
44
UnversionedParent('Versioned directory', 'pathf', 'idf'),
56
# u'\xe5' == a with circle
57
# '\xc3\xae' == u'\xee' == i with hat
58
# So these are u'path' and 'id' only with a circle and a hat. (shappo?)
59
example_conflicts = conflicts.ConflictList(
60
[conflicts.MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
61
conflicts.ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
62
conflicts.TextConflict(u'p\xe5tha'),
63
conflicts.PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
64
conflicts.DuplicateID('Unversioned existing file',
65
u'p\xe5thc', u'p\xe5thc2',
66
'\xc3\xaedc', '\xc3\xaedc'),
67
conflicts.DuplicateEntry('Moved existing file to',
68
u'p\xe5thdd.moved', u'p\xe5thd',
70
conflicts.ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
72
conflicts.UnversionedParent('Versioned directory',
73
u'p\xe5thf', '\xc3\xaedf'),
74
conflicts.NonDirectoryParent('Created directory',
75
u'p\xe5thg', '\xc3\xaedg'),
48
class TestConflicts(TestCaseWithTransport):
79
class TestConflicts(tests.TestCaseWithTransport):
50
81
def test_conflicts(self):
51
82
"""Conflicts are detected properly"""
52
tree = self.make_branch_and_tree('.',
53
format=bzrdir.BzrDirFormat6())
55
file('hello', 'w').write('hello world4')
56
file('hello.THIS', 'w').write('hello world2')
57
file('hello.BASE', 'w').write('hello world1')
58
file('hello.OTHER', 'w').write('hello world3')
59
file('hello.sploo.BASE', 'w').write('yellow world')
60
file('hello.sploo.OTHER', 'w').write('yellow world2')
61
self.assertEqual(len(list(tree.list_files())), 6)
62
conflicts = tree.conflicts()
63
self.assertEqual(len(conflicts), 2)
64
self.assert_('hello' in conflicts[0].path)
65
self.assert_('hello.sploo' in conflicts[1].path)
67
restore('hello.sploo')
68
self.assertEqual(len(tree.conflicts()), 0)
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())
69
102
self.assertFileEqual('hello world2', 'hello')
70
assert not os.path.lexists('hello.sploo')
71
self.assertRaises(NotConflicted, restore, 'hello')
72
self.assertRaises(NotConflicted, restore, 'hello.sploo')
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')
74
108
def test_resolve_conflict_dir(self):
75
109
tree = self.make_branch_and_tree('.')
77
file('hello', 'w').write('hello world4')
110
self.build_tree_contents([('hello', 'hello world4'),
111
('hello.THIS', 'hello world2'),
112
('hello.BASE', 'hello world1'),
114
os.mkdir('hello.OTHER')
78
115
tree.add('hello', 'q')
79
file('hello.THIS', 'w').write('hello world2')
80
file('hello.BASE', 'w').write('hello world1')
81
os.mkdir('hello.OTHER')
82
l = ConflictList([TextConflict('hello')])
116
l = conflicts.ConflictList([conflicts.TextConflict('hello')])
83
117
l.remove_files(tree)
86
class TestConflictStanzas(TestCase):
119
def test_select_conflicts(self):
120
tree = self.make_branch_and_tree('.')
121
clist = conflicts.ConflictList
123
def check_select(not_selected, selected, paths, **kwargs):
125
(not_selected, selected),
126
tree_conflicts.select_conflicts(tree, paths, **kwargs))
128
foo = conflicts.ContentsConflict('foo')
129
bar = conflicts.ContentsConflict('bar')
130
tree_conflicts = clist([foo, bar])
132
check_select(clist([bar]), clist([foo]), ['foo'])
133
check_select(clist(), tree_conflicts,
134
[''], ignore_misses=True, recurse=True)
136
foobaz = conflicts.ContentsConflict('foo/baz')
137
tree_conflicts = clist([foobaz, bar])
139
check_select(clist([bar]), clist([foobaz]),
140
['foo'], ignore_misses=True, recurse=True)
142
qux = conflicts.PathConflict('qux', 'foo/baz')
143
tree_conflicts = clist([qux])
145
check_select(clist(), tree_conflicts,
146
['foo'], ignore_misses=True, recurse=True)
147
check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
149
def test_resolve_conflicts_recursive(self):
150
tree = self.make_branch_and_tree('.')
151
self.build_tree(['dir/', 'dir/hello'])
152
tree.add(['dir', 'dir/hello'])
154
dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
155
tree.set_conflicts(dirhello)
157
conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
158
self.assertEqual(dirhello, tree.conflicts())
160
conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
161
self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
164
class TestConflictStanzas(tests.TestCase):
88
166
def test_stanza_roundtrip(self):
89
167
# write and read our example stanza.
90
168
stanza_iter = example_conflicts.to_stanzas()
91
processed = ConflictList.from_stanzas(stanza_iter)
92
for o,p in zip(processed, example_conflicts):
169
processed = conflicts.ConflictList.from_stanzas(stanza_iter)
170
for o, p in zip(processed, example_conflicts):
93
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)
95
186
def test_stanzification(self):
96
187
for stanza in example_conflicts.to_stanzas():
98
self.assertStartsWith(stanza['file_id'], 'id')
101
self.assertStartsWith(stanza['path'], 'path')
103
self.assertStartsWith(stanza['conflict_file_id'], 'id')
104
self.assertStartsWith(stanza['conflict_file_path'], 'path')
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')
198
# FIXME: The shell-like tests should be converted to real whitebox tests... or
199
# moved to a blackbox module -- vila 20100205
201
# FIXME: test missing for multiple conflicts
203
# FIXME: Tests missing for DuplicateID conflict type
204
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
206
preamble = None # The setup script set by daughter classes
209
super(TestResolveConflicts, self).setUp()
210
self.run_script(self.preamble)
213
class TestResolveTextConflicts(TestResolveConflicts):
218
def mirror_scenarios(base_scenarios):
219
"""Return a list of mirrored scenarios.
221
Each scenario in base_scenarios is duplicated switching the roles of 'this'
225
for common, (lname, ldict), (rname, rdict) in base_scenarios:
226
a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
227
[(rname, dict(_other=rdict))])
228
b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
229
[(lname, dict(_other=ldict))])
230
# Inject the common parameters in all scenarios
231
for name, d in a + b:
233
scenarios.extend(a + b)
237
# FIXME: Get rid of parametrized (in the class name) once we delete
238
# TestResolveConflicts -- vila 20100308
239
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
240
"""This class provides a base to test single conflict resolution.
242
Since all conflict objects are created with specific semantics for their
243
attributes, each class should implement the necessary functions and
244
attributes described below.
246
Each class should define the scenarios that create the expected (single)
249
Each scenario describes:
250
* how to create 'base' tree (and revision)
251
* how to create 'left' tree (and revision, parent rev 'base')
252
* how to create 'right' tree (and revision, parent rev 'base')
253
* how to check that changes in 'base'->'left' have been taken
254
* how to check that changes in 'base'->'right' have been taken
256
From each base scenario, we generate two concrete scenarios where:
257
* this=left, other=right
258
* this=right, other=left
260
Then the test case verifies each concrete scenario by:
261
* creating a branch containing the 'base', 'this' and 'other' revisions
262
* creating a working tree for the 'this' revision
263
* performing the merge of 'other' into 'this'
264
* verifying the expected conflict was generated
265
* resolving with --take-this or --take-other, and running the corresponding
266
checks (for either 'base'->'this', or 'base'->'other')
268
:cvar _conflict_type: The expected class of the generated conflict.
270
:cvar _assert_conflict: A method receiving the working tree and the
271
conflict object and checking its attributes.
273
:cvar _base_actions: The branchbuilder actions to create the 'base'
276
:cvar _this: The dict related to 'base' -> 'this'. It contains at least:
277
* 'actions': The branchbuilder actions to create the 'this'
279
* 'check': how to check the changes after resolution with --take-this.
281
:cvar _other: The dict related to 'base' -> 'other'. It contains at least:
282
* 'actions': The branchbuilder actions to create the 'other'
284
* 'check': how to check the changes after resolution with --take-other.
287
# Set by daughter classes
288
_conflict_type = None
289
_assert_conflict = None
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
326
super(TestParametrizedResolveConflicts, self).setUp()
327
builder = self.make_branch_builder('trunk')
328
builder.start_series()
330
# Create an empty trunk
331
builder.build_snapshot('start', None, [
332
('add', ('', 'root-id', 'directory', ''))])
333
# Add a minimal base content
334
base_actions = self._get_actions(self._base_actions)()
335
builder.build_snapshot('base', ['start'], base_actions)
336
# Modify the base content in branch
337
actions_other = self._get_actions(self._other['actions'])()
338
builder.build_snapshot('other', ['base'], actions_other)
339
# Modify the base content in trunk
340
actions_this = self._get_actions(self._this['actions'])()
341
builder.build_snapshot('this', ['base'], actions_this)
342
# builder.get_branch() tip is now 'this'
344
builder.finish_series()
345
self.builder = builder
347
def _get_actions(self, name):
348
return getattr(self, 'do_%s' % name)
350
def _get_check(self, name):
351
return getattr(self, 'check_%s' % name)
353
def _merge_other_into_this(self):
354
b = self.builder.get_branch()
355
wt = b.bzrdir.sprout('branch').open_workingtree()
356
wt.merge_from_branch(b, 'other')
359
def assertConflict(self, wt):
360
confs = wt.conflicts()
361
self.assertLength(1, confs)
363
self.assertIsInstance(c, self._conflict_type)
364
self._assert_conflict(wt, c)
366
def _get_resolve_path_arg(self, wt, action):
367
raise NotImplementedError(self._get_resolve_path_arg)
369
def check_resolved(self, wt, action):
370
path = self._get_resolve_path_arg(wt, action)
371
conflicts.resolve(wt, [path], action=action)
372
# Check that we don't have any conflicts nor unknown left
373
self.assertLength(0, wt.conflicts())
374
self.assertLength(0, list(wt.unknowns()))
376
def test_resolve_taking_this(self):
377
wt = self._merge_other_into_this()
378
self.assertConflict(wt)
379
self.check_resolved(wt, 'take_this')
380
check_this = self._get_check(self._this['check'])
383
def test_resolve_taking_other(self):
384
wt = self._merge_other_into_this()
385
self.assertConflict(wt)
386
self.check_resolved(wt, 'take_other')
387
check_other = self._get_check(self._other['check'])
391
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
393
_conflict_type = conflicts.ContentsConflict,
395
# Set by load_tests from scenarios()
396
# path and file-id for the file involved in the conflict
403
# File modified/deleted
404
(dict(_base_actions='create_file',
405
_path='file', _file_id='file-id'),
407
dict(actions='modify_file', check='file_has_more_content')),
409
dict(actions='delete_file', check='file_doesnt_exist')),),
411
return mirror_scenarios(base_scenarios)
413
def do_create_file(self):
414
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
416
def do_modify_file(self):
417
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
419
def check_file_has_more_content(self):
420
self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
422
def do_delete_file(self):
423
return [('unversion', 'file-id')]
425
def check_file_doesnt_exist(self):
426
self.failIfExists('branch/file')
428
def _get_resolve_path_arg(self, wt, action):
431
def assertContentsConflict(self, wt, c):
432
self.assertEqual(self._file_id, c.file_id)
433
self.assertEqual(self._path, c.path)
434
_assert_conflict = assertContentsConflict
437
class TestResolvePathConflict(TestParametrizedResolveConflicts):
439
_conflict_type = conflicts.PathConflict,
441
def do_nothing(self):
446
# Each side dict additionally defines:
447
# - path path involved (can be '<deleted>')
450
# File renamed/deleted
451
(dict(_base_actions='create_file'),
453
dict(actions='rename_file', check='file_renamed',
454
path='new-file', file_id='file-id')),
456
dict(actions='delete_file', check='file_doesnt_exist',
457
# PathConflicts deletion handling requires a special
459
path='<deleted>', file_id='file-id')),),
460
# File renamed/renamed differently
461
(dict(_base_actions='create_file'),
463
dict(actions='rename_file', check='file_renamed',
464
path='new-file', file_id='file-id')),
466
dict(actions='rename_file2', check='file_renamed2',
467
path='new-file2', file_id='file-id')),),
468
# Dir renamed/deleted
469
(dict(_base_actions='create_dir'),
471
dict(actions='rename_dir', check='dir_renamed',
472
path='new-dir', file_id='dir-id')),
474
dict(actions='delete_dir', check='dir_doesnt_exist',
475
# PathConflicts deletion handling requires a special
477
path='<deleted>', file_id='dir-id')),),
478
# Dir renamed/renamed differently
479
(dict(_base_actions='create_dir'),
481
dict(actions='rename_dir', check='dir_renamed',
482
path='new-dir', file_id='dir-id')),
484
dict(actions='rename_dir2', check='dir_renamed2',
485
path='new-dir2', file_id='dir-id')),),
487
return mirror_scenarios(base_scenarios)
489
def do_create_file(self):
490
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
492
def do_create_dir(self):
493
return [('add', ('dir', 'dir-id', 'directory', ''))]
495
def do_rename_file(self):
496
return [('rename', ('file', 'new-file'))]
498
def check_file_renamed(self):
499
self.failIfExists('branch/file')
500
self.failUnlessExists('branch/new-file')
502
def do_rename_file2(self):
503
return [('rename', ('file', 'new-file2'))]
505
def check_file_renamed2(self):
506
self.failIfExists('branch/file')
507
self.failUnlessExists('branch/new-file2')
509
def do_rename_dir(self):
510
return [('rename', ('dir', 'new-dir'))]
512
def check_dir_renamed(self):
513
self.failIfExists('branch/dir')
514
self.failUnlessExists('branch/new-dir')
516
def do_rename_dir2(self):
517
return [('rename', ('dir', 'new-dir2'))]
519
def check_dir_renamed2(self):
520
self.failIfExists('branch/dir')
521
self.failUnlessExists('branch/new-dir2')
523
def do_delete_file(self):
524
return [('unversion', 'file-id')]
526
def check_file_doesnt_exist(self):
527
self.failIfExists('branch/file')
529
def do_delete_dir(self):
530
return [('unversion', 'dir-id')]
532
def check_dir_doesnt_exist(self):
533
self.failIfExists('branch/dir')
535
def _get_resolve_path_arg(self, wt, action):
536
tpath = self._this['path']
537
opath = self._other['path']
538
if tpath == '<deleted>':
544
def assertPathConflict(self, wt, c):
545
tpath = self._this['path']
546
tfile_id = self._this['file_id']
547
opath = self._other['path']
548
ofile_id = self._other['file_id']
549
self.assertEqual(tfile_id, ofile_id) # Sanity check
550
self.assertEqual(tfile_id, c.file_id)
551
self.assertEqual(tpath, c.path)
552
self.assertEqual(opath, c.conflict_path)
553
_assert_conflict = assertPathConflict
556
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
557
"""Same as TestResolvePathConflict but a specific conflict object.
560
def assertPathConflict(self, c):
561
# We create a conflict object as it was created before the fix and
562
# inject it into the working tree, the test will exercise the
563
# compatibility code.
564
old_c = conflicts.PathConflict('<deleted>', self._item_path,
566
wt.set_conflicts(conflicts.ConflictList([old_c]))
569
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
571
_conflict_type = conflicts.DuplicateEntry,
575
# Each side dict additionally defines:
579
# File created with different file-ids
580
(dict(_base_actions='nothing'),
582
dict(actions='create_file_a', check='file_content_a',
583
path='file', file_id='file-a-id')),
585
dict(actions='create_file_b', check='file_content_b',
586
path='file', file_id='file-b-id')),),
588
return mirror_scenarios(base_scenarios)
590
def do_nothing(self):
593
def do_create_file_a(self):
594
return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
596
def check_file_content_a(self):
597
self.assertFileEqual('file a content\n', 'branch/file')
599
def do_create_file_b(self):
600
return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
602
def check_file_content_b(self):
603
self.assertFileEqual('file b content\n', 'branch/file')
605
def _get_resolve_path_arg(self, wt, action):
606
return self._this['path']
608
def assertDuplicateEntry(self, wt, c):
609
tpath = self._this['path']
610
tfile_id = self._this['file_id']
611
opath = self._other['path']
612
ofile_id = self._other['file_id']
613
self.assertEqual(tpath, opath) # Sanity check
614
self.assertEqual(tfile_id, c.file_id)
615
self.assertEqual(tpath + '.moved', c.path)
616
self.assertEqual(tpath, c.conflict_path)
617
_assert_conflict = assertDuplicateEntry
620
class TestResolveUnversionedParent(TestResolveConflicts):
622
# FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
624
# FIXME: While this *creates* UnversionedParent conflicts, this really only
625
# tests MissingParent resolution :-/
632
$ bzr commit -m 'Create trunk' -q
633
$ echo 'trunk content' >dir/file
634
$ bzr add -q dir/file
635
$ bzr commit -q -m 'Add dir/file in trunk'
636
$ bzr branch -q . -r 1 ../branch
639
$ bzr commit -q -m 'Remove dir in branch'
643
2>Conflict adding files to dir. Created directory.
644
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
645
2>2 conflicts encountered.
648
def test_take_this(self):
650
$ bzr rm -q dir --force
652
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
655
def test_take_other(self):
658
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
662
class TestResolveMissingParent(TestResolveConflicts):
669
$ echo 'trunk content' >dir/file
671
$ bzr commit -m 'Create trunk' -q
672
$ echo 'trunk content' >dir/file2
673
$ bzr add -q dir/file2
674
$ bzr commit -q -m 'Add dir/file2 in branch'
675
$ bzr branch -q . -r 1 ../branch
677
$ bzr rm -q dir/file --force
679
$ bzr commit -q -m 'Remove dir/file'
683
2>Conflict adding files to dir. Created directory.
684
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
685
2>2 conflicts encountered.
688
def test_keep_them_all(self):
691
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
694
def test_adopt_child(self):
696
$ bzr mv -q dir/file2 file2
697
$ bzr rm -q dir --force
699
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
702
def test_kill_them_all(self):
704
$ bzr rm -q dir --force
706
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
709
def test_resolve_taking_this(self):
711
$ bzr resolve --take-this dir
713
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
716
def test_resolve_taking_other(self):
718
$ bzr resolve --take-other dir
720
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
724
class TestResolveDeletingParent(TestResolveConflicts):
731
$ echo 'trunk content' >dir/file
733
$ bzr commit -m 'Create trunk' -q
734
$ bzr rm -q dir/file --force
735
$ bzr rm -q dir --force
736
$ bzr commit -q -m 'Remove dir/file'
737
$ bzr branch -q . -r 1 ../branch
739
$ echo 'branch content' >dir/file2
740
$ bzr add -q dir/file2
741
$ bzr commit -q -m 'Add dir/file2 in branch'
744
2>Conflict: can't delete dir because it is not empty. Not deleting.
745
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
746
2>2 conflicts encountered.
749
def test_keep_them_all(self):
752
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
755
def test_adopt_child(self):
757
$ bzr mv -q dir/file2 file2
758
$ bzr rm -q dir --force
760
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
763
def test_kill_them_all(self):
765
$ bzr rm -q dir --force
767
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
770
def test_resolve_taking_this(self):
772
$ bzr resolve --take-this dir
773
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
776
def test_resolve_taking_other(self):
778
$ bzr resolve --take-other dir
781
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
785
class TestResolveParentLoop(TestParametrizedResolveConflicts):
787
_conflict_type = conflicts.ParentLoop,
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'
800
# Dirs moved into each other
801
(dict(_base_actions='create_dir1_dir2'),
803
dict(actions='move_dir1_into_dir2', check='dir1_moved',
804
dir_id='dir1-id', target_id='dir2-id', xfail=False)),
806
dict(actions='move_dir2_into_dir1', check='dir2_moved',
807
dir_id='dir2-id', target_id='dir1-id', xfail=False))),
808
# Subdirs moved into each other
809
(dict(_base_actions='create_dir1_4'),
811
dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
812
dir_id='dir1-id', target_id='dir4-id', xfail=True)),
814
dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
815
dir_id='dir3-id', target_id='dir2-id', xfail=True))),
817
return mirror_scenarios(base_scenarios)
819
def do_create_dir1_dir2(self):
820
return [('add', ('dir1', 'dir1-id', 'directory', '')),
821
('add', ('dir2', 'dir2-id', 'directory', '')),]
823
def do_move_dir1_into_dir2(self):
824
return [('rename', ('dir1', 'dir2/dir1'))]
826
def check_dir1_moved(self):
827
self.failIfExists('branch/dir1')
828
self.failUnlessExists('branch/dir2/dir1')
830
def do_move_dir2_into_dir1(self):
831
return [('rename', ('dir2', 'dir1/dir2'))]
833
def check_dir2_moved(self):
834
self.failIfExists('branch/dir2')
835
self.failUnlessExists('branch/dir1/dir2')
837
def do_create_dir1_4(self):
838
return [('add', ('dir1', 'dir1-id', 'directory', '')),
839
('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
840
('add', ('dir3', 'dir3-id', 'directory', '')),
841
('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
843
def do_move_dir1_into_dir4(self):
844
return [('rename', ('dir1', 'dir3/dir4/dir1'))]
846
def check_dir1_2_moved(self):
847
self.failIfExists('branch/dir1')
848
self.failUnlessExists('branch/dir3/dir4/dir1')
849
self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
851
def do_move_dir3_into_dir2(self):
852
return [('rename', ('dir3', 'dir1/dir2/dir3'))]
854
def check_dir3_4_moved(self):
855
self.failIfExists('branch/dir3')
856
self.failUnlessExists('branch/dir1/dir2/dir3')
857
self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
859
def _get_resolve_path_arg(self, wt, action):
860
# ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
861
# But since <path> doesn't exist in the working tree, we need to use
862
# <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
863
return wt.id2path(self._other['dir_id'])
865
def assertParentLoop(self, wt, c):
866
self.assertEqual(self._other['dir_id'], c.file_id)
867
self.assertEqual(self._other['target_id'], c.conflict_file_id)
868
# The conflict paths are irrelevant (they are deterministic but not
869
# worth checking since they don't provide the needed information
871
if self._other['xfail']:
872
# It's a bit hackish to raise from here relying on being called for
873
# both tests but this avoid overriding test_resolve_taking_other
874
raise tests.KnownFailure(
875
"ParentLoop doesn't carry enough info to resolve --take-other")
876
_assert_conflict = assertParentLoop
879
class TestResolveNonDirectoryParent(TestResolveConflicts):
887
$ bzr commit -m 'Create trunk' -q
888
$ echo "Boing" >foo/bar
890
$ bzr commit -q -m 'Add foo/bar'
891
$ bzr branch -q . -r 1 ../branch
895
$ bzr commit -q -m 'foo is now a file'
899
# FIXME: The message is misleading, foo.new *is* a directory when the message
900
# is displayed -- vila 090916
901
2>Conflict: foo.new is not a directory, but has files in it. Created directory.
902
2>1 conflicts encountered.
905
def test_take_this(self):
907
$ bzr rm -q foo.new --force
908
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
909
# aside ? -- vila 090916
911
$ bzr resolve foo.new
912
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
915
def test_take_other(self):
917
$ bzr rm -q foo --force
918
$ bzr mv -q foo.new foo
920
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
923
def test_resolve_taking_this(self):
925
$ bzr resolve --take-this foo.new
927
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
930
def test_resolve_taking_other(self):
932
$ bzr resolve --take-other foo.new
934
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
938
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
940
def test_bug_430129(self):
941
# This is nearly like TestResolveNonDirectoryParent but with branch and
942
# trunk switched. As such it should certainly produce the same
950
$ bzr commit -m 'Create trunk' -q
953
$ bzr commit -m 'foo is now a file' -q
954
$ bzr branch -q . -r 1 ../branch -q
956
$ echo "Boing" >foo/bar
957
$ bzr add -q foo/bar -q
958
$ bzr commit -m 'Add foo/bar' -q
960
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
964
class TestResolveActionOption(tests.TestCase):
967
super(TestResolveActionOption, self).setUp()
968
self.options = [conflicts.ResolveActionOption()]
969
self.parser = option.get_optparser(dict((o.name, o)
970
for o in self.options))
972
def parse(self, args):
973
return self.parser.parse_args(args)
975
def test_unknown_action(self):
976
self.assertRaises(errors.BadOptionValue,
977
self.parse, ['--action', 'take-me-to-the-moon'])
980
opts, args = self.parse(['--action', 'done'])
981
self.assertEqual({'action':'done'}, opts)
983
def test_take_this(self):
984
opts, args = self.parse(['--action', 'take-this'])
985
self.assertEqual({'action': 'take_this'}, opts)
986
opts, args = self.parse(['--take-this'])
987
self.assertEqual({'action': 'take_this'}, opts)
989
def test_take_other(self):
990
opts, args = self.parse(['--action', 'take-other'])
991
self.assertEqual({'action': 'take_other'}, opts)
992
opts, args = self.parse(['--take-other'])
993
self.assertEqual({'action': 'take_other'}, opts)