1
# Copyright (C) 2005-2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
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)
51
# TODO: Test commit with some added, and added-but-missing files
52
# RBC 20060124 is that not tested in test_commit.py ?
54
# The order of 'path' here is important - do not let it
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'),
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')
108
def test_resolve_conflict_dir(self):
109
tree = self.make_branch_and_tree('.')
110
self.build_tree_contents([('hello', 'hello world4'),
111
('hello.THIS', 'hello world2'),
112
('hello.BASE', 'hello world1'),
114
os.mkdir('hello.OTHER')
115
tree.add('hello', 'q')
116
l = conflicts.ConflictList([conflicts.TextConflict('hello')])
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):
166
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)
186
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')
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
# FIXME: Get rid of parametrized (in the class name) once we delete
219
# TestResolveConflicts -- vila 20100308
220
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
221
"""This class provides a base to test single conflict resolution.
223
The aim is to define scenarios in daughter classes (one for each conflict
224
type) that create a single conflict object when one branch is merged in
225
another (and vice versa). Each class can define as many scenarios as
226
needed. Each scenario should define a couple of actions that will be
227
swapped to define the sibling scenarios.
229
From there, both resolutions are tested (--take-this and --take-other).
231
Each conflict type use its attributes in a specific way, so each class
232
should define a specific _assert_conflict method.
234
Since the resolution change the working tree state, each action should
235
define an associated check.
238
# Set by daughter classes
239
_conflict_type = None
240
_assert_conflict = None
245
_other_actions = None
249
# Set by _this_actions and other_actions
250
# FIXME: rename them this_args and other_args so the tests can use them
258
def mirror_scenarios(klass, base_scenarios):
261
"""Modify dict to apply to the given side.
263
'actions' key is turned into '_actions_this' if side is 'this' for
267
# Turn each key into _side_key
268
for k,v in d.iteritems():
269
t['_%s_%s' % (k, side)] = v
271
# Each base scenario is duplicated switching the roles of 'this' and
273
left = [l for l, r, c in base_scenarios]
274
right = [r for l, r, c in base_scenarios]
275
common = [c for l, r, c in base_scenarios]
276
for (lname, ldict), (rname, rdict), common in zip(left, right, common):
277
a = tests.multiply_scenarios([(lname, adapt(ldict, 'this'))],
278
[(rname, adapt(rdict, 'other'))])
279
b = tests.multiply_scenarios(
280
[(rname, adapt(rdict, 'this'))],
281
[(lname, adapt(ldict, 'other'))])
282
# Inject the common parameters in all scenarios
283
for name, d in a + b:
285
scenarios.extend(a + b)
289
def scenarios(klass):
290
# Only concrete classes return actual scenarios
294
super(TestParametrizedResolveConflicts, self).setUp()
295
builder = self.make_branch_builder('trunk')
296
builder.start_series()
298
# Create an empty trunk
299
builder.build_snapshot('start', None, [
300
('add', ('', 'root-id', 'directory', ''))])
301
# Add a minimal base content
302
_, _, actions_base = self._get_actions(self._actions_base)()
303
builder.build_snapshot('base', ['start'], actions_base)
304
# Modify the base content in branch
305
(self._other_path, self._other_id,
306
actions_other) = self._get_actions(self._actions_other)()
307
builder.build_snapshot('other', ['base'], actions_other)
308
# Modify the base content in trunk
309
(self._this_path, self._this_id,
310
actions_this) = self._get_actions(self._actions_this)()
311
builder.build_snapshot('this', ['base'], actions_this)
312
# builder.get_branch() tip is now 'this'
314
builder.finish_series()
315
self.builder = builder
317
def _get_actions(self, name):
318
return getattr(self, 'do_%s' % name)
320
def _get_check(self, name):
321
return getattr(self, 'check_%s' % name)
323
def do_nothing(self):
324
return (None, None, [])
326
def do_create_file(self):
327
return ('file', 'file-id',
328
[('add', ('file', 'file-id', 'file', 'trunk content\n'))])
330
def do_create_file_a(self):
331
return ('file', 'file-a-id',
332
[('add', ('file', 'file-a-id', 'file', 'file a content\n'))])
334
def check_file_content_a(self):
335
self.assertFileEqual('file a content\n', 'branch/file')
337
def do_create_file_b(self):
338
return ('file', 'file-b-id',
339
[('add', ('file', 'file-b-id', 'file', 'file b content\n'))])
341
def check_file_content_b(self):
342
self.assertFileEqual('file b content\n', 'branch/file')
344
def do_create_dir(self):
345
return ('dir', 'dir-id', [('add', ('dir', 'dir-id', 'directory', ''))])
347
def do_modify_file(self):
348
return ('file', 'file-id',
349
[('modify', ('file-id', 'trunk content\nmore content\n'))])
351
def check_file_has_more_content(self):
352
self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
354
def do_delete_file(self):
355
return ('file', 'file-id', [('unversion', 'file-id')])
357
def check_file_doesnt_exist(self):
358
self.failIfExists('branch/file')
360
def do_rename_file(self):
361
return ('new-file', 'file-id', [('rename', ('file', 'new-file'))])
363
def check_file_renamed(self):
364
self.failIfExists('branch/file')
365
self.failUnlessExists('branch/new-file')
367
def do_rename_file2(self):
368
return ('new-file2', 'file-id', [('rename', ('file', 'new-file2'))])
370
def check_file_renamed2(self):
371
self.failIfExists('branch/file')
372
self.failUnlessExists('branch/new-file2')
374
def do_rename_dir(self):
375
return ('new-dir', 'dir-id', [('rename', ('dir', 'new-dir'))])
377
def check_dir_renamed(self):
378
self.failIfExists('branch/dir')
379
self.failUnlessExists('branch/new-dir')
381
def do_rename_dir2(self):
382
return ('new-dir2', 'dir-id', [('rename', ('dir', 'new-dir2'))])
384
def check_dir_renamed2(self):
385
self.failIfExists('branch/dir')
386
self.failUnlessExists('branch/new-dir2')
388
def do_delete_dir(self):
389
return ('<deleted>', 'dir-id', [('unversion', 'dir-id')])
391
def check_dir_doesnt_exist(self):
392
self.failIfExists('branch/dir')
394
def _merge_other_into_this(self):
395
b = self.builder.get_branch()
396
wt = b.bzrdir.sprout('branch').open_workingtree()
397
wt.merge_from_branch(b, 'other')
400
def assertConflict(self, wt):
401
confs = wt.conflicts()
402
self.assertLength(1, confs)
404
self.assertIsInstance(c, self._conflict_type)
405
self._assert_conflict(wt, c)
407
def _get_resolve_path_arg(self, wt, action):
408
return self._item_path
410
def check_resolved(self, wt, action):
411
path = self._get_resolve_path_arg(wt, action)
412
conflicts.resolve(wt, [path], action=action)
413
# Check that we don't have any conflicts nor unknown left
414
self.assertLength(0, wt.conflicts())
415
self.assertLength(0, list(wt.unknowns()))
417
def test_resolve_taking_this(self):
418
wt = self._merge_other_into_this()
419
self.assertConflict(wt)
420
self.check_resolved(wt, 'take_this')
421
check_this = self._get_check(self._check_this)
424
def test_resolve_taking_other(self):
425
wt = self._merge_other_into_this()
426
self.assertConflict(wt)
427
self.check_resolved(wt, 'take_other')
428
check_other = self._get_check(self._check_other)
432
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
434
_conflict_type = conflicts.ContentsConflict,
436
def scenarios(klass):
438
(('file_modified', dict(actions='modify_file',
439
check='file_has_more_content')),
440
('file_deleted', dict(actions='delete_file',
441
check='file_doesnt_exist')),
442
dict(_actions_base='create_file', _item_path='file')),
444
return klass.mirror_scenarios(base_scenarios)
446
def assertContentsConflict(self, wt, c):
447
self.assertEqual(self._other_id, c.file_id)
448
self.assertEqual(self._other_path, c.path)
449
_assert_conflict = assertContentsConflict
453
class TestResolvePathConflict(TestParametrizedResolveConflicts):
455
_conflict_type = conflicts.PathConflict,
458
def scenarios(klass):
459
for_file = dict(_actions_base='create_file',
460
_item_path='new-file', _item_id='file-id',)
461
for_dir = dict(_actions_base='create_dir',
462
_item_path='new-dir', _item_id='dir-id',)
465
dict(actions='rename_file', check='file_renamed')),
467
dict(actions='delete_file', check='file_doesnt_exist')),
470
dict(actions='rename_file', check='file_renamed')),
472
dict(actions='rename_file2', check='file_renamed2')),
475
dict(actions='rename_dir', check='dir_renamed')),
477
dict(actions='delete_dir', check='dir_doesnt_exist')),
480
dict(actions='rename_dir', check='dir_renamed')),
482
dict(actions='rename_dir2', check='dir_renamed2')),
485
return klass.mirror_scenarios(base_scenarios)
487
def do_delete_file(self):
488
sup = super(TestResolvePathConflict, self).do_delete_file()
489
# PathConflicts handle deletion differently and requires a special
491
return ('<deleted>',) + sup[1:]
493
def assertPathConflict(self, wt, c):
494
self.assertEqual(self._item_id, c.file_id)
495
self.assertEqual(self._this_path, c.path)
496
self.assertEqual(self._other_path, c.conflict_path)
497
_assert_conflict = assertPathConflict
500
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
501
"""Same as TestResolvePathConflict but a specific conflict object.
504
def assertPathConflict(self, c):
505
# We create a conflict object as it was created before the fix and
506
# inject it into the working tree, the test will exercise the
507
# compatibility code.
508
old_c = conflicts.PathConflict('<deleted>', self._item_path,
510
wt.set_conflicts(conflicts.ConflictList([old_c]))
513
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
515
_conflict_type = conflicts.DuplicateEntry,
517
def scenarios(klass):
519
(('filea_created', dict(actions='create_file_a',
520
check='file_content_a')),
521
('fileb_created', dict(actions='create_file_b',
522
check='file_content_b')),
523
dict(_actions_base='nothing', _item_path='file')),
525
return klass.mirror_scenarios(base_scenarios)
527
def assertDuplicateEntry(self, wt, c):
528
self.assertEqual(self._this_id, c.file_id)
529
self.assertEqual(self._item_path + '.moved', c.path)
530
self.assertEqual(self._item_path, c.conflict_path)
531
_assert_conflict = assertDuplicateEntry
534
class TestResolveUnversionedParent(TestResolveConflicts):
536
# FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
538
# FIXME: While this *creates* UnversionedParent conflicts, this really only
539
# tests MissingParent resolution :-/
545
$ bzr commit -m 'Create trunk'
547
$ echo 'trunk content' >dir/file
549
$ bzr commit -m 'Add dir/file in trunk'
551
$ bzr branch . -r 1 ../branch
554
$ bzr commit -m 'Remove dir in branch'
559
2>Conflict adding files to dir. Created directory.
560
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
561
2>2 conflicts encountered.
564
def test_take_this(self):
568
$ bzr commit --strict -m 'No more conflicts nor unknown files'
571
def test_take_other(self):
574
$ bzr commit --strict -m 'No more conflicts nor unknown files'
578
class TestResolveMissingParent(TestResolveConflicts):
584
$ echo 'trunk content' >dir/file
586
$ bzr commit -m 'Create trunk'
588
$ echo 'trunk content' >dir/file2
590
$ bzr commit -m 'Add dir/file2 in branch'
592
$ bzr branch . -r 1 ../branch
594
$ bzr rm dir/file --force
596
$ bzr commit -m 'Remove dir/file'
601
2>Conflict adding files to dir. Created directory.
602
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
603
2>2 conflicts encountered.
606
def test_keep_them_all(self):
609
$ bzr commit --strict -m 'No more conflicts nor unknown files'
612
def test_adopt_child(self):
614
$ bzr mv dir/file2 file2
617
$ bzr commit --strict -m 'No more conflicts nor unknown files'
620
def test_kill_them_all(self):
624
$ bzr commit --strict -m 'No more conflicts nor unknown files'
627
def test_resolve_taking_this(self):
629
$ bzr resolve --take-this dir
630
$ bzr commit --strict -m 'No more conflicts nor unknown files'
633
def test_resolve_taking_other(self):
635
$ bzr resolve --take-other dir
636
$ bzr commit --strict -m 'No more conflicts nor unknown files'
640
class TestResolveDeletingParent(TestResolveConflicts):
646
$ echo 'trunk content' >dir/file
648
$ bzr commit -m 'Create trunk'
650
$ bzr rm dir/file --force
652
$ bzr commit -m 'Remove dir/file'
654
$ bzr branch . -r 1 ../branch
656
$ echo 'branch content' >dir/file2
658
$ bzr commit -m 'Add dir/file2 in branch'
662
2>Conflict: can't delete dir because it is not empty. Not deleting.
663
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
664
2>2 conflicts encountered.
667
def test_keep_them_all(self):
670
$ bzr commit --strict -m 'No more conflicts nor unknown files'
673
def test_adopt_child(self):
675
$ bzr mv dir/file2 file2
678
$ bzr commit --strict -m 'No more conflicts nor unknown files'
681
def test_kill_them_all(self):
685
$ bzr commit --strict -m 'No more conflicts nor unknown files'
688
def test_resolve_taking_this(self):
690
$ bzr resolve --take-this dir
691
$ bzr commit --strict -m 'No more conflicts nor unknown files'
694
def test_resolve_taking_other(self):
696
$ bzr resolve --take-other dir
697
$ bzr commit --strict -m 'No more conflicts nor unknown files'
701
class TestResolveParentLoop(TestParametrizedResolveConflicts):
703
_conflict_type = conflicts.ParentLoop,
705
def scenarios(klass):
707
(('dir1_into_dir2', dict(actions='move_dir1_into_dir2',
708
check='dir1_moved')),
709
('dir2_into_dir1', dict(actions='move_dir2_into_dir1',
710
check='dir2_moved')),
711
dict(_actions_base='create_dir1_dir2')),
712
(('dir1_into_dir4', dict(actions='move_dir1_into_dir4',
713
check='dir1_2_moved')),
714
('dir3_into_dir2', dict(actions='move_dir3_into_dir2',
715
check='dir3_4_moved')),
716
dict(_actions_base='create_dir1_4')),
718
return klass.mirror_scenarios(base_scenarios)
720
def do_create_dir1_dir2(self):
722
[('add', ('dir1', 'dir1-id', 'directory', '')),
723
('add', ('dir2', 'dir2-id', 'directory', '')),
726
def do_move_dir1_into_dir2(self):
727
# The arguments are the file-id to move and the targeted file-id dir.
728
return ('dir1-id', 'dir2-id', [('rename', ('dir1', 'dir2/dir1'))])
730
def check_dir1_moved(self):
731
self.failIfExists('branch/dir1')
732
self.failUnlessExists('branch/dir2/dir1')
734
def do_move_dir2_into_dir1(self):
735
# The arguments are the file-id to move and the targeted file-id dir.
736
return ('dir2-id', 'dir1-id', [('rename', ('dir2', 'dir1/dir2'))])
738
def check_dir2_moved(self):
739
self.failIfExists('branch/dir2')
740
self.failUnlessExists('branch/dir1/dir2')
742
def do_create_dir1_4(self):
744
[('add', ('dir1', 'dir1-id', 'directory', '')),
745
('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
746
('add', ('dir3', 'dir3-id', 'directory', '')),
747
('add', ('dir3/dir4', 'dir4-id', 'directory', '')),
750
def do_move_dir1_into_dir4(self):
751
# The arguments are the file-id to move and the targeted file-id dir.
752
return ('dir1-id', 'dir4-id',
753
[('rename', ('dir1', 'dir3/dir4/dir1'))])
755
def check_dir1_2_moved(self):
756
self.failIfExists('branch/dir1')
757
self.failUnlessExists('branch/dir3/dir4/dir1')
758
self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
760
def do_move_dir3_into_dir2(self):
761
# The arguments are the file-id to move and the targeted file-id dir.
762
return ('dir3-id', 'dir2-id',
763
[('rename', ('dir3', 'dir1/dir2/dir3'))])
765
def check_dir3_4_moved(self):
766
self.failIfExists('branch/dir3')
767
self.failUnlessExists('branch/dir1/dir2/dir3')
768
self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
770
def _get_resolve_path_arg(self, wt, action):
771
# ParentLoop is unsual as it says:
772
# moving <conflict_path> into <path>. Cancelled move.
773
# But since <path> doesn't exist in the working tree, we need to use
774
# <conflict_path> instead
775
path = wt.id2path(self._other_id)
778
def assertParentLoop(self, wt, c):
779
if 'taking_other(' in self.id() and 'dir4' in self.id():
780
raise tests.KnownFailure(
781
"ParentLoop doesn't carry enough info to resolve")
782
# The relevant file-ids are other_args swapped (which is the main
783
# reason why they should be renamed other_args instead of Other_path
784
# and other_id). In the conflict object, they represent:
785
# c.file_id: the directory being moved
786
# c.conflict_id_id: The target directory
787
self.assertEqual(self._other_path, c.file_id)
788
self.assertEqual(self._other_id, c.conflict_file_id)
789
# The conflict paths are irrelevant (they are deterministic but not
790
# worth checking since they don't provide the needed information
792
_assert_conflict = assertParentLoop
795
class OldTestResolveParentLoop(TestResolveConflicts):
802
$ bzr commit -m 'Create trunk'
805
$ bzr commit -m 'Moved dir2 into dir1'
807
$ bzr branch . -r 1 ../branch
810
$ bzr commit -m 'Moved dir1 into dir2'
813
2>Conflict moving dir2 into dir2/dir1. Cancelled move.
814
2>1 conflicts encountered.
817
def test_take_this(self):
820
$ bzr commit --strict -m 'No more conflicts nor unknown files'
823
def test_take_other(self):
825
$ bzr mv dir2/dir1 dir1
828
$ bzr commit --strict -m 'No more conflicts nor unknown files'
831
def test_resolve_taking_this(self):
833
$ bzr resolve --take-this dir2
834
$ bzr commit --strict -m 'No more conflicts nor unknown files'
836
self.failUnlessExists('dir2')
838
def test_resolve_taking_other(self):
840
$ bzr resolve --take-other dir2
841
$ bzr commit --strict -m 'No more conflicts nor unknown files'
843
self.failUnlessExists('dir1')
846
class TestResolveNonDirectoryParent(TestResolveConflicts):
852
$ bzr commit -m 'Create trunk'
853
$ echo "Boing" >foo/bar
855
$ bzr commit -m 'Add foo/bar'
857
$ bzr branch . -r 1 ../branch
861
$ bzr commit -m 'foo is now a file'
866
# FIXME: The message is misleading, foo.new *is* a directory when the message
867
# is displayed -- vila 090916
868
2>Conflict: foo.new is not a directory, but has files in it. Created directory.
869
2>1 conflicts encountered.
872
def test_take_this(self):
874
$ bzr rm foo.new --force
875
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
876
# aside ? -- vila 090916
878
$ bzr resolve foo.new
879
$ bzr commit --strict -m 'No more conflicts nor unknown files'
882
def test_take_other(self):
887
$ bzr commit --strict -m 'No more conflicts nor unknown files'
890
def test_resolve_taking_this(self):
892
$ bzr resolve --take-this foo.new
893
$ bzr commit --strict -m 'No more conflicts nor unknown files'
896
def test_resolve_taking_other(self):
898
$ bzr resolve --take-other foo.new
899
$ bzr commit --strict -m 'No more conflicts nor unknown files'
903
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
905
def test_bug_430129(self):
906
# This is nearly like TestResolveNonDirectoryParent but with branch and
907
# trunk switched. As such it should certainly produce the same
913
$ bzr commit -m 'Create trunk'
916
$ bzr commit -m 'foo is now a file'
918
$ bzr branch . -r 1 ../branch
920
$ echo "Boing" >foo/bar
922
$ bzr commit -m 'Add foo/bar'
925
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
929
class TestResolveActionOption(tests.TestCase):
932
super(TestResolveActionOption, self).setUp()
933
self.options = [conflicts.ResolveActionOption()]
934
self.parser = option.get_optparser(dict((o.name, o)
935
for o in self.options))
937
def parse(self, args):
938
return self.parser.parse_args(args)
940
def test_unknown_action(self):
941
self.assertRaises(errors.BadOptionValue,
942
self.parse, ['--action', 'take-me-to-the-moon'])
945
opts, args = self.parse(['--action', 'done'])
946
self.assertEqual({'action':'done'}, opts)
948
def test_take_this(self):
949
opts, args = self.parse(['--action', 'take-this'])
950
self.assertEqual({'action': 'take_this'}, opts)
951
opts, args = self.parse(['--take-this'])
952
self.assertEqual({'action': 'take_this'}, opts)
954
def test_take_other(self):
955
opts, args = self.parse(['--action', 'take-other'])
956
self.assertEqual({'action': 'take_other'}, opts)
957
opts, args = self.parse(['--take-other'])
958
self.assertEqual({'action': 'take_other'}, opts)