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
TestResolveContentConflicts,
39
tests.multiply_tests(sp_tests, content_conflict_scenarios(), result)
41
# No parametrization for the remaining tests
42
result.addTests(remaining_tests)
47
# TODO: Test commit with some added, and added-but-missing files
48
# RBC 20060124 is that not tested in test_commit.py ?
50
# The order of 'path' here is important - do not let it
52
# u'\xe5' == a with circle
53
# '\xc3\xae' == u'\xee' == i with hat
54
# So these are u'path' and 'id' only with a circle and a hat. (shappo?)
55
example_conflicts = conflicts.ConflictList(
56
[conflicts.MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
57
conflicts.ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
58
conflicts.TextConflict(u'p\xe5tha'),
59
conflicts.PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
60
conflicts.DuplicateID('Unversioned existing file',
61
u'p\xe5thc', u'p\xe5thc2',
62
'\xc3\xaedc', '\xc3\xaedc'),
63
conflicts.DuplicateEntry('Moved existing file to',
64
u'p\xe5thdd.moved', u'p\xe5thd',
66
conflicts.ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
68
conflicts.UnversionedParent('Versioned directory',
69
u'p\xe5thf', '\xc3\xaedf'),
70
conflicts.NonDirectoryParent('Created directory',
71
u'p\xe5thg', '\xc3\xaedg'),
75
class TestConflicts(tests.TestCaseWithTransport):
77
def test_conflicts(self):
78
"""Conflicts are detected properly"""
79
# Use BzrDirFormat6 so we can fake conflicts
80
tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
81
self.build_tree_contents([('hello', 'hello world4'),
82
('hello.THIS', 'hello world2'),
83
('hello.BASE', 'hello world1'),
84
('hello.OTHER', 'hello world3'),
85
('hello.sploo.BASE', 'yellowworld'),
86
('hello.sploo.OTHER', 'yellowworld2'),
89
self.assertLength(6, list(tree.list_files()))
91
tree_conflicts = tree.conflicts()
92
self.assertLength(2, tree_conflicts)
93
self.assertTrue('hello' in tree_conflicts[0].path)
94
self.assertTrue('hello.sploo' in tree_conflicts[1].path)
95
conflicts.restore('hello')
96
conflicts.restore('hello.sploo')
97
self.assertLength(0, tree.conflicts())
98
self.assertFileEqual('hello world2', 'hello')
99
self.assertFalse(os.path.lexists('hello.sploo'))
100
self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
101
self.assertRaises(errors.NotConflicted,
102
conflicts.restore, 'hello.sploo')
104
def test_resolve_conflict_dir(self):
105
tree = self.make_branch_and_tree('.')
106
self.build_tree_contents([('hello', 'hello world4'),
107
('hello.THIS', 'hello world2'),
108
('hello.BASE', 'hello world1'),
110
os.mkdir('hello.OTHER')
111
tree.add('hello', 'q')
112
l = conflicts.ConflictList([conflicts.TextConflict('hello')])
115
def test_select_conflicts(self):
116
tree = self.make_branch_and_tree('.')
117
clist = conflicts.ConflictList
119
def check_select(not_selected, selected, paths, **kwargs):
121
(not_selected, selected),
122
tree_conflicts.select_conflicts(tree, paths, **kwargs))
124
foo = conflicts.ContentsConflict('foo')
125
bar = conflicts.ContentsConflict('bar')
126
tree_conflicts = clist([foo, bar])
128
check_select(clist([bar]), clist([foo]), ['foo'])
129
check_select(clist(), tree_conflicts,
130
[''], ignore_misses=True, recurse=True)
132
foobaz = conflicts.ContentsConflict('foo/baz')
133
tree_conflicts = clist([foobaz, bar])
135
check_select(clist([bar]), clist([foobaz]),
136
['foo'], ignore_misses=True, recurse=True)
138
qux = conflicts.PathConflict('qux', 'foo/baz')
139
tree_conflicts = clist([qux])
141
check_select(clist(), tree_conflicts,
142
['foo'], ignore_misses=True, recurse=True)
143
check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
145
def test_resolve_conflicts_recursive(self):
146
tree = self.make_branch_and_tree('.')
147
self.build_tree(['dir/', 'dir/hello'])
148
tree.add(['dir', 'dir/hello'])
150
dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
151
tree.set_conflicts(dirhello)
153
conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
154
self.assertEqual(dirhello, tree.conflicts())
156
conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
157
self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
160
class TestConflictStanzas(tests.TestCase):
162
def test_stanza_roundtrip(self):
163
# write and read our example stanza.
164
stanza_iter = example_conflicts.to_stanzas()
165
processed = conflicts.ConflictList.from_stanzas(stanza_iter)
166
for o, p in zip(processed, example_conflicts):
167
self.assertEqual(o, p)
169
self.assertIsInstance(o.path, unicode)
171
if o.file_id is not None:
172
self.assertIsInstance(o.file_id, str)
174
conflict_path = getattr(o, 'conflict_path', None)
175
if conflict_path is not None:
176
self.assertIsInstance(conflict_path, unicode)
178
conflict_file_id = getattr(o, 'conflict_file_id', None)
179
if conflict_file_id is not None:
180
self.assertIsInstance(conflict_file_id, str)
182
def test_stanzification(self):
183
for stanza in example_conflicts.to_stanzas():
184
if 'file_id' in stanza:
185
# In Stanza form, the file_id has to be unicode.
186
self.assertStartsWith(stanza['file_id'], u'\xeed')
187
self.assertStartsWith(stanza['path'], u'p\xe5th')
188
if 'conflict_path' in stanza:
189
self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
190
if 'conflict_file_id' in stanza:
191
self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
194
# FIXME: The shell-like tests should be converted to real whitebox tests... or
195
# moved to a blackbox module -- vila 20100205
197
# FIXME: Tests missing for DuplicateID conflict type
198
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
200
preamble = None # The setup script set by daughter classes
203
super(TestResolveConflicts, self).setUp()
204
self.run_script(self.preamble)
207
class TestResolveTextConflicts(TestResolveConflicts):
212
def content_conflict_scenarios():
213
return [('file,None', dict(_this_actions='modify_file',
214
_check_this='file_has_more_content',
215
_other_actions='delete_file',
216
_check_other='file_doesnt_exist',
218
('None,file', dict(_this_actions='delete_file',
219
_check_this='file_doesnt_exist',
220
_other_actions='modify_file',
221
_check_other='file_has_more_content',
226
class TestResolveContentConflicts(tests.TestCaseWithTransport):
233
super(TestResolveContentConflicts, self).setUp()
234
builder = self.make_branch_builder('trunk')
235
builder.start_series()
236
# Create an empty trunk
237
builder.build_snapshot('start', None, [
238
('add', ('', 'root-id', 'directory', ''))])
239
# Add a minimal base content
240
builder.build_snapshot('base', ['start'], [
241
('add', ('file', 'file-id', 'file', 'trunk content\n'))])
242
# Modify the base content in branch
243
other_actions = self._get_actions(self._other_actions)
244
builder.build_snapshot('other', ['base'], other_actions())
245
# Modify the base content in trunk
246
this_actions = self._get_actions(self._this_actions)
247
builder.build_snapshot('this', ['base'], this_actions())
248
builder.finish_series()
249
self.builder = builder
251
def _get_actions(self, name):
252
return getattr(self, 'do_%s' % name)
254
def _get_check(self, name):
255
return getattr(self, 'check_%s' % name)
257
def do_modify_file(self):
258
return [('modify', ('file-id', 'trunk content\nmore content\n'))]
260
def check_file_has_more_content(self):
261
self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
263
def do_delete_file(self):
264
return [('unversion', 'file-id')]
266
def check_file_doesnt_exist(self):
267
self.failIfExists('branch/file')
269
def _merge_other_into_this(self):
270
b = self.builder.get_branch()
271
wt = b.bzrdir.sprout('branch').open_workingtree()
272
wt.merge_from_branch(b, 'other')
275
def assertConflict(self, wt, ctype, **kwargs):
276
confs = wt.conflicts()
277
self.assertLength(1, confs)
279
self.assertIsInstance(c, ctype)
280
sentinel = object() # An impossible value
281
for k, v in kwargs.iteritems():
282
self.assertEqual(v, getattr(c, k, sentinel))
284
def check_resolved(self, wt, item, action):
285
conflicts.resolve(wt, [item], action=action)
286
# Check that we don't have any conflicts nor unknown left
287
self.assertLength(0, wt.conflicts())
288
self.assertLength(0, list(wt.unknowns()))
290
def test_resolve_taking_this(self):
291
wt = self._merge_other_into_this()
292
self.assertConflict(wt, conflicts.ContentsConflict,
293
path='file', file_id='file-id',)
294
self.check_resolved(wt, 'file', 'take_this')
295
check_this = self._get_check(self._check_this)
298
def test_resolve_taking_other(self):
299
wt = self._merge_other_into_this()
300
self.assertConflict(wt, conflicts.ContentsConflict,
301
path='file', file_id='file-id',)
302
self.check_resolved(wt, 'file', 'take_other')
303
check_other = self._get_check(self._check_other)
307
class TestResolveDuplicateEntry(TestResolveConflicts):
312
$ echo 'trunk content' >file
314
$ bzr commit -m 'Create trunk'
316
$ echo 'trunk content too' >file2
318
$ bzr commit -m 'Add file2 in trunk'
320
$ bzr branch . -r 1 ../branch
322
$ echo 'branch content' >file2
324
$ bzr commit -m 'Add file2 in branch'
328
2>R file2 => file2.moved
329
2>Conflict adding file file2. Moved existing file to file2.moved.
330
2>1 conflicts encountered.
333
def test_keep_this(self):
335
$ bzr rm file2 --force
336
$ bzr mv file2.moved file2
338
$ bzr commit --strict -m 'No more conflicts nor unknown files'
341
def test_keep_other(self):
342
self.failIfExists('branch/file2.moved')
344
$ bzr rm file2.moved --force
346
$ bzr commit --strict -m 'No more conflicts nor unknown files'
348
self.failIfExists('branch/file2.moved')
350
def test_resolve_taking_this(self):
352
$ bzr resolve --take-this file2
353
$ bzr commit --strict -m 'No more conflicts nor unknown files'
356
def test_resolve_taking_other(self):
358
$ bzr resolve --take-other file2
359
$ bzr commit --strict -m 'No more conflicts nor unknown files'
363
class TestResolveUnversionedParent(TestResolveConflicts):
365
# FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
367
# FIXME: While this *creates* UnversionedParent conflicts, this really only
368
# tests MissingParent resolution :-/
374
$ bzr commit -m 'Create trunk'
376
$ echo 'trunk content' >dir/file
378
$ bzr commit -m 'Add dir/file in trunk'
380
$ bzr branch . -r 1 ../branch
383
$ bzr commit -m 'Remove dir in branch'
388
2>Conflict adding files to dir. Created directory.
389
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
390
2>2 conflicts encountered.
393
def test_take_this(self):
397
$ bzr commit --strict -m 'No more conflicts nor unknown files'
400
def test_take_other(self):
403
$ bzr commit --strict -m 'No more conflicts nor unknown files'
407
class TestResolveMissingParent(TestResolveConflicts):
413
$ echo 'trunk content' >dir/file
415
$ bzr commit -m 'Create trunk'
417
$ echo 'trunk content' >dir/file2
419
$ bzr commit -m 'Add dir/file2 in branch'
421
$ bzr branch . -r 1 ../branch
423
$ bzr rm dir/file --force
425
$ bzr commit -m 'Remove dir/file'
430
2>Conflict adding files to dir. Created directory.
431
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
432
2>2 conflicts encountered.
435
def test_keep_them_all(self):
438
$ bzr commit --strict -m 'No more conflicts nor unknown files'
441
def test_adopt_child(self):
443
$ bzr mv dir/file2 file2
446
$ bzr commit --strict -m 'No more conflicts nor unknown files'
449
def test_kill_them_all(self):
453
$ bzr commit --strict -m 'No more conflicts nor unknown files'
456
def test_resolve_taking_this(self):
458
$ bzr resolve --take-this dir
459
$ bzr commit --strict -m 'No more conflicts nor unknown files'
462
def test_resolve_taking_other(self):
464
$ bzr resolve --take-other dir
465
$ bzr commit --strict -m 'No more conflicts nor unknown files'
469
class TestResolveDeletingParent(TestResolveConflicts):
475
$ echo 'trunk content' >dir/file
477
$ bzr commit -m 'Create trunk'
479
$ bzr rm dir/file --force
481
$ bzr commit -m 'Remove dir/file'
483
$ bzr branch . -r 1 ../branch
485
$ echo 'branch content' >dir/file2
487
$ bzr commit -m 'Add dir/file2 in branch'
491
2>Conflict: can't delete dir because it is not empty. Not deleting.
492
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
493
2>2 conflicts encountered.
496
def test_keep_them_all(self):
499
$ bzr commit --strict -m 'No more conflicts nor unknown files'
502
def test_adopt_child(self):
504
$ bzr mv dir/file2 file2
507
$ bzr commit --strict -m 'No more conflicts nor unknown files'
510
def test_kill_them_all(self):
514
$ bzr commit --strict -m 'No more conflicts nor unknown files'
517
def test_resolve_taking_this(self):
519
$ bzr resolve --take-this dir
520
$ bzr commit --strict -m 'No more conflicts nor unknown files'
523
def test_resolve_taking_other(self):
525
$ bzr resolve --take-other dir
526
$ bzr commit --strict -m 'No more conflicts nor unknown files'
530
class TestResolvePathConflict(TestResolveConflicts):
537
$ bzr commit -m 'Create trunk'
539
$ bzr mv file file-in-trunk
540
$ bzr commit -m 'Renamed to file-in-trunk'
542
$ bzr branch . -r 1 ../branch
544
$ bzr mv file file-in-branch
545
$ bzr commit -m 'Renamed to file-in-branch'
548
2>R file-in-branch => file-in-trunk
549
2>Path conflict: file-in-branch / file-in-trunk
550
2>1 conflicts encountered.
553
def test_keep_source(self):
555
$ bzr resolve file-in-trunk
556
$ bzr commit --strict -m 'No more conflicts nor unknown files'
559
def test_keep_target(self):
561
$ bzr mv file-in-trunk file-in-branch
562
$ bzr resolve file-in-branch
563
$ bzr commit --strict -m 'No more conflicts nor unknown files'
566
def test_resolve_taking_this(self):
568
$ bzr resolve --take-this file-in-branch
569
$ bzr commit --strict -m 'No more conflicts nor unknown files'
572
def test_resolve_taking_other(self):
574
$ bzr resolve --take-other file-in-branch
575
$ bzr commit --strict -m 'No more conflicts nor unknown files'
579
class TestResolveParentLoop(TestResolveConflicts):
586
$ bzr commit -m 'Create trunk'
589
$ bzr commit -m 'Moved dir2 into dir1'
591
$ bzr branch . -r 1 ../branch
594
$ bzr commit -m 'Moved dir1 into dir2'
597
2>Conflict moving dir2/dir1 into dir2. Cancelled move.
598
2>1 conflicts encountered.
601
def test_take_this(self):
604
$ bzr commit --strict -m 'No more conflicts nor unknown files'
607
def test_take_other(self):
609
$ bzr mv dir2/dir1 dir1
612
$ bzr commit --strict -m 'No more conflicts nor unknown files'
615
def test_resolve_taking_this(self):
617
$ bzr resolve --take-this dir2
618
$ bzr commit --strict -m 'No more conflicts nor unknown files'
620
self.failUnlessExists('dir2')
622
def test_resolve_taking_other(self):
624
$ bzr resolve --take-other dir2
625
$ bzr commit --strict -m 'No more conflicts nor unknown files'
627
self.failUnlessExists('dir1')
630
class TestResolveNonDirectoryParent(TestResolveConflicts):
636
$ bzr commit -m 'Create trunk'
637
$ echo "Boing" >foo/bar
639
$ bzr commit -m 'Add foo/bar'
641
$ bzr branch . -r 1 ../branch
645
$ bzr commit -m 'foo is now a file'
650
# FIXME: The message is misleading, foo.new *is* a directory when the message
651
# is displayed -- vila 090916
652
2>Conflict: foo.new is not a directory, but has files in it. Created directory.
653
2>1 conflicts encountered.
656
def test_take_this(self):
658
$ bzr rm foo.new --force
659
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
660
# aside ? -- vila 090916
662
$ bzr resolve foo.new
663
$ bzr commit --strict -m 'No more conflicts nor unknown files'
666
def test_take_other(self):
671
$ bzr commit --strict -m 'No more conflicts nor unknown files'
674
def test_resolve_taking_this(self):
676
$ bzr resolve --take-this foo.new
677
$ bzr commit --strict -m 'No more conflicts nor unknown files'
680
def test_resolve_taking_other(self):
682
$ bzr resolve --take-other foo.new
683
$ bzr commit --strict -m 'No more conflicts nor unknown files'
687
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
689
def test_bug_430129(self):
690
# This is nearly like TestResolveNonDirectoryParent but with branch and
691
# trunk switched. As such it should certainly produce the same
697
$ bzr commit -m 'Create trunk'
700
$ bzr commit -m 'foo is now a file'
702
$ bzr branch . -r 1 ../branch
704
$ echo "Boing" >foo/bar
706
$ bzr commit -m 'Add foo/bar'
709
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
713
class TestResolveActionOption(tests.TestCase):
716
super(TestResolveActionOption, self).setUp()
717
self.options = [conflicts.ResolveActionOption()]
718
self.parser = option.get_optparser(dict((o.name, o)
719
for o in self.options))
721
def parse(self, args):
722
return self.parser.parse_args(args)
724
def test_unknown_action(self):
725
self.assertRaises(errors.BadOptionValue,
726
self.parse, ['--action', 'take-me-to-the-moon'])
729
opts, args = self.parse(['--action', 'done'])
730
self.assertEqual({'action':'done'}, opts)
732
def test_take_this(self):
733
opts, args = self.parse(['--action', 'take-this'])
734
self.assertEqual({'action': 'take_this'}, opts)
735
opts, args = self.parse(['--take-this'])
736
self.assertEqual({'action': 'take_this'}, opts)
738
def test_take_other(self):
739
opts, args = self.parse(['--action', 'take-other'])
740
self.assertEqual({'action': 'take_other'}, opts)
741
opts, args = self.parse(['--take-other'])
742
self.assertEqual({'action': 'take_other'}, opts)