1
# Copyright (C) 2005-2011 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
28
from bzrlib.tests import (
34
load_tests = scenarios.load_tests_apply_scenarios
37
# TODO: Test commit with some added, and added-but-missing files
38
# RBC 20060124 is that not tested in test_commit.py ?
40
# The order of 'path' here is important - do not let it
42
# u'\xe5' == a with circle
43
# '\xc3\xae' == u'\xee' == i with hat
44
# So these are u'path' and 'id' only with a circle and a hat. (shappo?)
45
example_conflicts = conflicts.ConflictList(
46
[conflicts.MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
47
conflicts.ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
48
conflicts.TextConflict(u'p\xe5tha'),
49
conflicts.PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
50
conflicts.DuplicateID('Unversioned existing file',
51
u'p\xe5thc', u'p\xe5thc2',
52
'\xc3\xaedc', '\xc3\xaedc'),
53
conflicts.DuplicateEntry('Moved existing file to',
54
u'p\xe5thdd.moved', u'p\xe5thd',
56
conflicts.ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
58
conflicts.UnversionedParent('Versioned directory',
59
u'p\xe5thf', '\xc3\xaedf'),
60
conflicts.NonDirectoryParent('Created directory',
61
u'p\xe5thg', '\xc3\xaedg'),
65
class TestConflicts(tests.TestCaseWithTransport):
67
def test_conflicts(self):
68
"""Conflicts are detected properly"""
69
# Use BzrDirFormat6 so we can fake conflicts
70
tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
71
self.build_tree_contents([('hello', 'hello world4'),
72
('hello.THIS', 'hello world2'),
73
('hello.BASE', 'hello world1'),
74
('hello.OTHER', 'hello world3'),
75
('hello.sploo.BASE', 'yellowworld'),
76
('hello.sploo.OTHER', 'yellowworld2'),
79
self.assertLength(6, list(tree.list_files()))
81
tree_conflicts = tree.conflicts()
82
self.assertLength(2, tree_conflicts)
83
self.assertTrue('hello' in tree_conflicts[0].path)
84
self.assertTrue('hello.sploo' in tree_conflicts[1].path)
85
conflicts.restore('hello')
86
conflicts.restore('hello.sploo')
87
self.assertLength(0, tree.conflicts())
88
self.assertFileEqual('hello world2', 'hello')
89
self.assertFalse(os.path.lexists('hello.sploo'))
90
self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
91
self.assertRaises(errors.NotConflicted,
92
conflicts.restore, 'hello.sploo')
94
def test_resolve_conflict_dir(self):
95
tree = self.make_branch_and_tree('.')
96
self.build_tree_contents([('hello', 'hello world4'),
97
('hello.THIS', 'hello world2'),
98
('hello.BASE', 'hello world1'),
100
os.mkdir('hello.OTHER')
101
tree.add('hello', 'q')
102
l = conflicts.ConflictList([conflicts.TextConflict('hello')])
105
def test_select_conflicts(self):
106
tree = self.make_branch_and_tree('.')
107
clist = conflicts.ConflictList
109
def check_select(not_selected, selected, paths, **kwargs):
111
(not_selected, selected),
112
tree_conflicts.select_conflicts(tree, paths, **kwargs))
114
foo = conflicts.ContentsConflict('foo')
115
bar = conflicts.ContentsConflict('bar')
116
tree_conflicts = clist([foo, bar])
118
check_select(clist([bar]), clist([foo]), ['foo'])
119
check_select(clist(), tree_conflicts,
120
[''], ignore_misses=True, recurse=True)
122
foobaz = conflicts.ContentsConflict('foo/baz')
123
tree_conflicts = clist([foobaz, bar])
125
check_select(clist([bar]), clist([foobaz]),
126
['foo'], ignore_misses=True, recurse=True)
128
qux = conflicts.PathConflict('qux', 'foo/baz')
129
tree_conflicts = clist([qux])
131
check_select(clist(), tree_conflicts,
132
['foo'], ignore_misses=True, recurse=True)
133
check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
135
def test_resolve_conflicts_recursive(self):
136
tree = self.make_branch_and_tree('.')
137
self.build_tree(['dir/', 'dir/hello'])
138
tree.add(['dir', 'dir/hello'])
140
dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
141
tree.set_conflicts(dirhello)
143
conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
144
self.assertEqual(dirhello, tree.conflicts())
146
conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
147
self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
150
class TestConflictStanzas(tests.TestCase):
152
def test_stanza_roundtrip(self):
153
# write and read our example stanza.
154
stanza_iter = example_conflicts.to_stanzas()
155
processed = conflicts.ConflictList.from_stanzas(stanza_iter)
156
for o, p in zip(processed, example_conflicts):
157
self.assertEqual(o, p)
159
self.assertIsInstance(o.path, unicode)
161
if o.file_id is not None:
162
self.assertIsInstance(o.file_id, str)
164
conflict_path = getattr(o, 'conflict_path', None)
165
if conflict_path is not None:
166
self.assertIsInstance(conflict_path, unicode)
168
conflict_file_id = getattr(o, 'conflict_file_id', None)
169
if conflict_file_id is not None:
170
self.assertIsInstance(conflict_file_id, str)
172
def test_stanzification(self):
173
for stanza in example_conflicts.to_stanzas():
174
if 'file_id' in stanza:
175
# In Stanza form, the file_id has to be unicode.
176
self.assertStartsWith(stanza['file_id'], u'\xeed')
177
self.assertStartsWith(stanza['path'], u'p\xe5th')
178
if 'conflict_path' in stanza:
179
self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
180
if 'conflict_file_id' in stanza:
181
self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
184
# FIXME: The shell-like tests should be converted to real whitebox tests... or
185
# moved to a blackbox module -- vila 20100205
187
# FIXME: test missing for multiple conflicts
189
# FIXME: Tests missing for DuplicateID conflict type
190
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
192
preamble = None # The setup script set by daughter classes
195
super(TestResolveConflicts, self).setUp()
196
self.run_script(self.preamble)
199
def mirror_scenarios(base_scenarios):
200
"""Return a list of mirrored scenarios.
202
Each scenario in base_scenarios is duplicated switching the roles of 'this'
206
for common, (lname, ldict), (rname, rdict) in base_scenarios:
207
a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
208
[(rname, dict(_other=rdict))])
209
b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
210
[(lname, dict(_other=ldict))])
211
# Inject the common parameters in all scenarios
212
for name, d in a + b:
214
scenarios.extend(a + b)
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
Since all conflict objects are created with specific semantics for their
224
attributes, each class should implement the necessary functions and
225
attributes described below.
227
Each class should define the scenarios that create the expected (single)
230
Each scenario describes:
231
* how to create 'base' tree (and revision)
232
* how to create 'left' tree (and revision, parent rev 'base')
233
* how to create 'right' tree (and revision, parent rev 'base')
234
* how to check that changes in 'base'->'left' have been taken
235
* how to check that changes in 'base'->'right' have been taken
237
From each base scenario, we generate two concrete scenarios where:
238
* this=left, other=right
239
* this=right, other=left
241
Then the test case verifies each concrete scenario by:
242
* creating a branch containing the 'base', 'this' and 'other' revisions
243
* creating a working tree for the 'this' revision
244
* performing the merge of 'other' into 'this'
245
* verifying the expected conflict was generated
246
* resolving with --take-this or --take-other, and running the corresponding
247
checks (for either 'base'->'this', or 'base'->'other')
249
:cvar _conflict_type: The expected class of the generated conflict.
251
:cvar _assert_conflict: A method receiving the working tree and the
252
conflict object and checking its attributes.
254
:cvar _base_actions: The branchbuilder actions to create the 'base'
257
:cvar _this: The dict related to 'base' -> 'this'. It contains at least:
258
* 'actions': The branchbuilder actions to create the 'this'
260
* 'check': how to check the changes after resolution with --take-this.
262
:cvar _other: The dict related to 'base' -> 'other'. It contains at least:
263
* 'actions': The branchbuilder actions to create the 'other'
265
* 'check': how to check the changes after resolution with --take-other.
268
# Set by daughter classes
269
_conflict_type = None
270
_assert_conflict = None
278
"""The scenario list for the conflict type defined by the class.
280
Each scenario is of the form:
281
(common, (left_name, left_dict), (right_name, right_dict))
285
* left_name and right_name are the scenario names that will be combined
287
* left_dict and right_dict are the attributes specific to each half of
288
the scenario. They should include at least 'actions' and 'check' and
289
will be available as '_this' and '_other' test instance attributes.
291
Daughters classes are free to add their specific attributes as they see
292
fit in any of the three dicts.
294
This is a class method so that load_tests can find it.
296
'_base_actions' in the common dict, 'actions' and 'check' in the left
297
and right dicts use names that map to methods in the test classes. Some
298
prefixes are added to these names to get the correspong methods (see
299
_get_actions() and _get_check()). The motivation here is to avoid
300
collisions in the class namespace.
304
super(TestParametrizedResolveConflicts, self).setUp()
305
builder = self.make_branch_builder('trunk')
306
builder.start_series()
308
# Create an empty trunk
309
builder.build_snapshot('start', None, [
310
('add', ('', 'root-id', 'directory', ''))])
311
# Add a minimal base content
312
base_actions = self._get_actions(self._base_actions)()
313
builder.build_snapshot('base', ['start'], base_actions)
314
# Modify the base content in branch
315
actions_other = self._get_actions(self._other['actions'])()
316
builder.build_snapshot('other', ['base'], actions_other)
317
# Modify the base content in trunk
318
actions_this = self._get_actions(self._this['actions'])()
319
builder.build_snapshot('this', ['base'], actions_this)
320
# builder.get_branch() tip is now 'this'
322
builder.finish_series()
323
self.builder = builder
325
def _get_actions(self, name):
326
return getattr(self, 'do_%s' % name)
328
def _get_check(self, name):
329
return getattr(self, 'check_%s' % name)
331
def _merge_other_into_this(self):
332
b = self.builder.get_branch()
333
wt = b.bzrdir.sprout('branch').open_workingtree()
334
wt.merge_from_branch(b, 'other')
337
def assertConflict(self, wt):
338
confs = wt.conflicts()
339
self.assertLength(1, confs)
341
self.assertIsInstance(c, self._conflict_type)
342
self._assert_conflict(wt, c)
344
def _get_resolve_path_arg(self, wt, action):
345
raise NotImplementedError(self._get_resolve_path_arg)
347
def check_resolved(self, wt, action):
348
path = self._get_resolve_path_arg(wt, action)
349
conflicts.resolve(wt, [path], action=action)
350
# Check that we don't have any conflicts nor unknown left
351
self.assertLength(0, wt.conflicts())
352
self.assertLength(0, list(wt.unknowns()))
354
def test_resolve_taking_this(self):
355
wt = self._merge_other_into_this()
356
self.assertConflict(wt)
357
self.check_resolved(wt, 'take_this')
358
check_this = self._get_check(self._this['check'])
361
def test_resolve_taking_other(self):
362
wt = self._merge_other_into_this()
363
self.assertConflict(wt)
364
self.check_resolved(wt, 'take_other')
365
check_other = self._get_check(self._other['check'])
369
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
371
_conflict_type = conflicts.TextConflict
373
# Set by the scenarios
374
# path and file-id for the file involved in the conflict
378
scenarios = mirror_scenarios(
380
# File modified on both sides
381
(dict(_base_actions='create_file',
382
_path='file', _file_id='file-id'),
384
dict(actions='modify_file_A', check='file_has_content_A')),
386
dict(actions='modify_file_B', check='file_has_content_B')),),
387
# File modified on both sides in dir
388
(dict(_base_actions='create_file_in_dir',
389
_path='dir/file', _file_id='file-id'),
390
('filed_modified_A_in_dir',
391
dict(actions='modify_file_A',
392
check='file_in_dir_has_content_A')),
394
dict(actions='modify_file_B',
395
check='file_in_dir_has_content_B')),),
398
def do_create_file(self, path='file'):
399
return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
401
def do_modify_file_A(self):
402
return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
404
def do_modify_file_B(self):
405
return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
407
def check_file_has_content_A(self, path='file'):
408
self.assertFileEqual('trunk content\nfeature A\n',
409
osutils.pathjoin('branch', path))
411
def check_file_has_content_B(self, path='file'):
412
self.assertFileEqual('trunk content\nfeature B\n',
413
osutils.pathjoin('branch', path))
415
def do_create_file_in_dir(self):
416
return [('add', ('dir', 'dir-id', 'directory', '')),
417
] + self.do_create_file('dir/file')
419
def check_file_in_dir_has_content_A(self):
420
self.check_file_has_content_A('dir/file')
422
def check_file_in_dir_has_content_B(self):
423
self.check_file_has_content_B('dir/file')
425
def _get_resolve_path_arg(self, wt, action):
428
def assertTextConflict(self, wt, c):
429
self.assertEqual(self._file_id, c.file_id)
430
self.assertEqual(self._path, c.path)
431
_assert_conflict = assertTextConflict
434
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
436
_conflict_type = conflicts.ContentsConflict
438
# Set by the scenarios
439
# path and file-id for the file involved in the conflict
443
scenarios = mirror_scenarios(
445
# File modified/deleted
446
(dict(_base_actions='create_file',
447
_path='file', _file_id='file-id'),
449
dict(actions='modify_file', check='file_has_more_content')),
451
dict(actions='delete_file', check='file_doesnt_exist')),),
452
# File modified/deleted in dir
453
(dict(_base_actions='create_file_in_dir',
454
_path='dir/file', _file_id='file-id'),
455
('file_modified_in_dir',
456
dict(actions='modify_file_in_dir',
457
check='file_in_dir_has_more_content')),
458
('file_deleted_in_dir',
459
dict(actions='delete_file',
460
check='file_in_dir_doesnt_exist')),),
463
def do_create_file(self):
464
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
466
def do_modify_file(self):
467
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
469
def check_file_has_more_content(self):
470
self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
472
def do_delete_file(self):
473
return [('unversion', 'file-id')]
475
def check_file_doesnt_exist(self):
476
self.failIfExists('branch/file')
478
def do_create_file_in_dir(self):
479
return [('add', ('dir', 'dir-id', 'directory', '')),
480
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
482
def do_modify_file_in_dir(self):
483
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
485
def check_file_in_dir_has_more_content(self):
486
self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
488
def check_file_in_dir_doesnt_exist(self):
489
self.failIfExists('branch/dir/file')
491
def _get_resolve_path_arg(self, wt, action):
494
def assertContentsConflict(self, wt, c):
495
self.assertEqual(self._file_id, c.file_id)
496
self.assertEqual(self._path, c.path)
497
_assert_conflict = assertContentsConflict
500
class TestResolvePathConflict(TestParametrizedResolveConflicts):
502
_conflict_type = conflicts.PathConflict
504
def do_nothing(self):
507
# Each side dict additionally defines:
508
# - path path involved (can be '<deleted>')
510
scenarios = mirror_scenarios(
512
# File renamed/deleted
513
(dict(_base_actions='create_file'),
515
dict(actions='rename_file', check='file_renamed',
516
path='new-file', file_id='file-id')),
518
dict(actions='delete_file', check='file_doesnt_exist',
519
# PathConflicts deletion handling requires a special
521
path='<deleted>', file_id='file-id')),),
522
# File renamed/deleted in dir
523
(dict(_base_actions='create_file_in_dir'),
524
('file_renamed_in_dir',
525
dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
526
path='dir/new-file', file_id='file-id')),
528
dict(actions='delete_file', check='file_in_dir_doesnt_exist',
529
# PathConflicts deletion handling requires a special
531
path='<deleted>', file_id='file-id')),),
532
# File renamed/renamed differently
533
(dict(_base_actions='create_file'),
535
dict(actions='rename_file', check='file_renamed',
536
path='new-file', file_id='file-id')),
538
dict(actions='rename_file2', check='file_renamed2',
539
path='new-file2', file_id='file-id')),),
540
# Dir renamed/deleted
541
(dict(_base_actions='create_dir'),
543
dict(actions='rename_dir', check='dir_renamed',
544
path='new-dir', file_id='dir-id')),
546
dict(actions='delete_dir', check='dir_doesnt_exist',
547
# PathConflicts deletion handling requires a special
549
path='<deleted>', file_id='dir-id')),),
550
# Dir renamed/renamed differently
551
(dict(_base_actions='create_dir'),
553
dict(actions='rename_dir', check='dir_renamed',
554
path='new-dir', file_id='dir-id')),
556
dict(actions='rename_dir2', check='dir_renamed2',
557
path='new-dir2', file_id='dir-id')),),
560
def do_create_file(self):
561
return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
563
def do_create_dir(self):
564
return [('add', ('dir', 'dir-id', 'directory', ''))]
566
def do_rename_file(self):
567
return [('rename', ('file', 'new-file'))]
569
def check_file_renamed(self):
570
self.failIfExists('branch/file')
571
self.failUnlessExists('branch/new-file')
573
def do_rename_file2(self):
574
return [('rename', ('file', 'new-file2'))]
576
def check_file_renamed2(self):
577
self.failIfExists('branch/file')
578
self.failUnlessExists('branch/new-file2')
580
def do_rename_dir(self):
581
return [('rename', ('dir', 'new-dir'))]
583
def check_dir_renamed(self):
584
self.failIfExists('branch/dir')
585
self.failUnlessExists('branch/new-dir')
587
def do_rename_dir2(self):
588
return [('rename', ('dir', 'new-dir2'))]
590
def check_dir_renamed2(self):
591
self.failIfExists('branch/dir')
592
self.failUnlessExists('branch/new-dir2')
594
def do_delete_file(self):
595
return [('unversion', 'file-id')]
597
def check_file_doesnt_exist(self):
598
self.failIfExists('branch/file')
600
def do_delete_dir(self):
601
return [('unversion', 'dir-id')]
603
def check_dir_doesnt_exist(self):
604
self.failIfExists('branch/dir')
606
def do_create_file_in_dir(self):
607
return [('add', ('dir', 'dir-id', 'directory', '')),
608
('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
610
def do_rename_file_in_dir(self):
611
return [('rename', ('dir/file', 'dir/new-file'))]
613
def check_file_in_dir_renamed(self):
614
self.failIfExists('branch/dir/file')
615
self.failUnlessExists('branch/dir/new-file')
617
def check_file_in_dir_doesnt_exist(self):
618
self.failIfExists('branch/dir/file')
620
def _get_resolve_path_arg(self, wt, action):
621
tpath = self._this['path']
622
opath = self._other['path']
623
if tpath == '<deleted>':
629
def assertPathConflict(self, wt, c):
630
tpath = self._this['path']
631
tfile_id = self._this['file_id']
632
opath = self._other['path']
633
ofile_id = self._other['file_id']
634
self.assertEqual(tfile_id, ofile_id) # Sanity check
635
self.assertEqual(tfile_id, c.file_id)
636
self.assertEqual(tpath, c.path)
637
self.assertEqual(opath, c.conflict_path)
638
_assert_conflict = assertPathConflict
641
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
642
"""Same as TestResolvePathConflict but a specific conflict object.
645
def assertPathConflict(self, c):
646
# We create a conflict object as it was created before the fix and
647
# inject it into the working tree, the test will exercise the
648
# compatibility code.
649
old_c = conflicts.PathConflict('<deleted>', self._item_path,
651
wt.set_conflicts(conflicts.ConflictList([old_c]))
654
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
656
_conflict_type = conflicts.DuplicateEntry
658
scenarios = mirror_scenarios(
660
# File created with different file-ids
661
(dict(_base_actions='nothing'),
663
dict(actions='create_file_a', check='file_content_a',
664
path='file', file_id='file-a-id')),
666
dict(actions='create_file_b', check='file_content_b',
667
path='file', file_id='file-b-id')),),
670
def do_nothing(self):
673
def do_create_file_a(self):
674
return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
676
def check_file_content_a(self):
677
self.assertFileEqual('file a content\n', 'branch/file')
679
def do_create_file_b(self):
680
return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
682
def check_file_content_b(self):
683
self.assertFileEqual('file b content\n', 'branch/file')
685
def _get_resolve_path_arg(self, wt, action):
686
return self._this['path']
688
def assertDuplicateEntry(self, wt, c):
689
tpath = self._this['path']
690
tfile_id = self._this['file_id']
691
opath = self._other['path']
692
ofile_id = self._other['file_id']
693
self.assertEqual(tpath, opath) # Sanity check
694
self.assertEqual(tfile_id, c.file_id)
695
self.assertEqual(tpath + '.moved', c.path)
696
self.assertEqual(tpath, c.conflict_path)
697
_assert_conflict = assertDuplicateEntry
700
class TestResolveUnversionedParent(TestResolveConflicts):
702
# FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
704
# FIXME: While this *creates* UnversionedParent conflicts, this really only
705
# tests MissingParent resolution :-/
712
$ bzr commit -m 'Create trunk' -q
713
$ echo 'trunk content' >dir/file
714
$ bzr add -q dir/file
715
$ bzr commit -q -m 'Add dir/file in trunk'
716
$ bzr branch -q . -r 1 ../branch
719
$ bzr commit -q -m 'Remove dir in branch'
723
2>Conflict adding files to dir. Created directory.
724
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
725
2>2 conflicts encountered.
728
def test_take_this(self):
730
$ bzr rm -q dir --force
732
2>2 conflict(s) resolved, 0 remaining
733
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
736
def test_take_other(self):
739
2>2 conflict(s) resolved, 0 remaining
740
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
744
class TestResolveMissingParent(TestResolveConflicts):
751
$ echo 'trunk content' >dir/file
753
$ bzr commit -m 'Create trunk' -q
754
$ echo 'trunk content' >dir/file2
755
$ bzr add -q dir/file2
756
$ bzr commit -q -m 'Add dir/file2 in branch'
757
$ bzr branch -q . -r 1 ../branch
759
$ bzr rm -q dir/file --force
761
$ bzr commit -q -m 'Remove dir/file'
765
2>Conflict adding files to dir. Created directory.
766
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
767
2>2 conflicts encountered.
770
def test_keep_them_all(self):
773
2>2 conflict(s) resolved, 0 remaining
774
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
777
def test_adopt_child(self):
779
$ bzr mv -q dir/file2 file2
780
$ bzr rm -q dir --force
782
2>2 conflict(s) resolved, 0 remaining
783
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
786
def test_kill_them_all(self):
788
$ bzr rm -q dir --force
790
2>2 conflict(s) resolved, 0 remaining
791
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
794
def test_resolve_taking_this(self):
796
$ bzr resolve --take-this dir
798
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
801
def test_resolve_taking_other(self):
803
$ bzr resolve --take-other dir
805
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
809
class TestResolveDeletingParent(TestResolveConflicts):
816
$ echo 'trunk content' >dir/file
818
$ bzr commit -m 'Create trunk' -q
819
$ bzr rm -q dir/file --force
820
$ bzr rm -q dir --force
821
$ bzr commit -q -m 'Remove dir/file'
822
$ bzr branch -q . -r 1 ../branch
824
$ echo 'branch content' >dir/file2
825
$ bzr add -q dir/file2
826
$ bzr commit -q -m 'Add dir/file2 in branch'
829
2>Conflict: can't delete dir because it is not empty. Not deleting.
830
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
831
2>2 conflicts encountered.
834
def test_keep_them_all(self):
837
2>2 conflict(s) resolved, 0 remaining
838
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
841
def test_adopt_child(self):
843
$ bzr mv -q dir/file2 file2
844
$ bzr rm -q dir --force
846
2>2 conflict(s) resolved, 0 remaining
847
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
850
def test_kill_them_all(self):
852
$ bzr rm -q dir --force
854
2>2 conflict(s) resolved, 0 remaining
855
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
858
def test_resolve_taking_this(self):
860
$ bzr resolve --take-this dir
861
2>2 conflict(s) resolved, 0 remaining
862
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
865
def test_resolve_taking_other(self):
867
$ bzr resolve --take-other dir
870
2>2 conflict(s) resolved, 0 remaining
871
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
875
class TestResolveParentLoop(TestParametrizedResolveConflicts):
877
_conflict_type = conflicts.ParentLoop
882
# Each side dict additionally defines:
883
# - dir_id: the directory being moved
884
# - target_id: The target directory
885
# - xfail: whether the test is expected to fail if the action is
886
# involved as 'other'
887
scenarios = mirror_scenarios(
889
# Dirs moved into each other
890
(dict(_base_actions='create_dir1_dir2'),
892
dict(actions='move_dir1_into_dir2', check='dir1_moved',
893
dir_id='dir1-id', target_id='dir2-id', xfail=False)),
895
dict(actions='move_dir2_into_dir1', check='dir2_moved',
896
dir_id='dir2-id', target_id='dir1-id', xfail=False))),
897
# Subdirs moved into each other
898
(dict(_base_actions='create_dir1_4'),
900
dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
901
dir_id='dir1-id', target_id='dir4-id', xfail=True)),
903
dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
904
dir_id='dir3-id', target_id='dir2-id', xfail=True))),
907
def do_create_dir1_dir2(self):
908
return [('add', ('dir1', 'dir1-id', 'directory', '')),
909
('add', ('dir2', 'dir2-id', 'directory', '')),]
911
def do_move_dir1_into_dir2(self):
912
return [('rename', ('dir1', 'dir2/dir1'))]
914
def check_dir1_moved(self):
915
self.failIfExists('branch/dir1')
916
self.failUnlessExists('branch/dir2/dir1')
918
def do_move_dir2_into_dir1(self):
919
return [('rename', ('dir2', 'dir1/dir2'))]
921
def check_dir2_moved(self):
922
self.failIfExists('branch/dir2')
923
self.failUnlessExists('branch/dir1/dir2')
925
def do_create_dir1_4(self):
926
return [('add', ('dir1', 'dir1-id', 'directory', '')),
927
('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
928
('add', ('dir3', 'dir3-id', 'directory', '')),
929
('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
931
def do_move_dir1_into_dir4(self):
932
return [('rename', ('dir1', 'dir3/dir4/dir1'))]
934
def check_dir1_2_moved(self):
935
self.failIfExists('branch/dir1')
936
self.failUnlessExists('branch/dir3/dir4/dir1')
937
self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
939
def do_move_dir3_into_dir2(self):
940
return [('rename', ('dir3', 'dir1/dir2/dir3'))]
942
def check_dir3_4_moved(self):
943
self.failIfExists('branch/dir3')
944
self.failUnlessExists('branch/dir1/dir2/dir3')
945
self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
947
def _get_resolve_path_arg(self, wt, action):
948
# ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
949
# But since <path> doesn't exist in the working tree, we need to use
950
# <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
951
return wt.id2path(self._other['dir_id'])
953
def assertParentLoop(self, wt, c):
954
self.assertEqual(self._other['dir_id'], c.file_id)
955
self.assertEqual(self._other['target_id'], c.conflict_file_id)
956
# The conflict paths are irrelevant (they are deterministic but not
957
# worth checking since they don't provide the needed information
959
if self._other['xfail']:
960
# It's a bit hackish to raise from here relying on being called for
961
# both tests but this avoid overriding test_resolve_taking_other
962
raise tests.KnownFailure(
963
"ParentLoop doesn't carry enough info to resolve --take-other")
964
_assert_conflict = assertParentLoop
967
class TestResolveNonDirectoryParent(TestResolveConflicts):
975
$ bzr commit -m 'Create trunk' -q
976
$ echo "Boing" >foo/bar
978
$ bzr commit -q -m 'Add foo/bar'
979
$ bzr branch -q . -r 1 ../branch
983
$ bzr commit -q -m 'foo is now a file'
987
# FIXME: The message is misleading, foo.new *is* a directory when the message
988
# is displayed -- vila 090916
989
2>Conflict: foo.new is not a directory, but has files in it. Created directory.
990
2>1 conflicts encountered.
993
def test_take_this(self):
995
$ bzr rm -q foo.new --force
996
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
997
# aside ? -- vila 090916
999
$ bzr resolve foo.new
1000
2>1 conflict(s) resolved, 0 remaining
1001
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1004
def test_take_other(self):
1006
$ bzr rm -q foo --force
1007
$ bzr mv -q foo.new foo
1009
2>1 conflict(s) resolved, 0 remaining
1010
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1013
def test_resolve_taking_this(self):
1015
$ bzr resolve --take-this foo.new
1017
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1020
def test_resolve_taking_other(self):
1022
$ bzr resolve --take-other foo.new
1024
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1028
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
1030
def test_bug_430129(self):
1031
# This is nearly like TestResolveNonDirectoryParent but with branch and
1032
# trunk switched. As such it should certainly produce the same
1040
$ bzr commit -m 'Create trunk' -q
1043
$ bzr commit -m 'foo is now a file' -q
1044
$ bzr branch -q . -r 1 ../branch -q
1046
$ echo "Boing" >foo/bar
1047
$ bzr add -q foo/bar -q
1048
$ bzr commit -m 'Add foo/bar' -q
1049
$ bzr merge ../trunk
1050
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
1054
class TestResolveActionOption(tests.TestCase):
1057
super(TestResolveActionOption, self).setUp()
1058
self.options = [conflicts.ResolveActionOption()]
1059
self.parser = option.get_optparser(dict((o.name, o)
1060
for o in self.options))
1062
def parse(self, args):
1063
return self.parser.parse_args(args)
1065
def test_unknown_action(self):
1066
self.assertRaises(errors.BadOptionValue,
1067
self.parse, ['--action', 'take-me-to-the-moon'])
1069
def test_done(self):
1070
opts, args = self.parse(['--action', 'done'])
1071
self.assertEqual({'action':'done'}, opts)
1073
def test_take_this(self):
1074
opts, args = self.parse(['--action', 'take-this'])
1075
self.assertEqual({'action': 'take_this'}, opts)
1076
opts, args = self.parse(['--take-this'])
1077
self.assertEqual({'action': 'take_this'}, opts)
1079
def test_take_other(self):
1080
opts, args = self.parse(['--action', 'take-other'])
1081
self.assertEqual({'action': 'take_other'}, opts)
1082
opts, args = self.parse(['--take-other'])
1083
self.assertEqual({'action': 'take_other'}, opts)