1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
27
from bzrlib.tests import script
30
# TODO: Test commit with some added, and added-but-missing files
31
# RBC 20060124 is that not tested in test_commit.py ?
33
# The order of 'path' here is important - do not let it
35
# u'\xe5' == a with circle
36
# '\xc3\xae' == u'\xee' == i with hat
37
# So these are u'path' and 'id' only with a circle and a hat. (shappo?)
38
example_conflicts = conflicts.ConflictList(
39
[conflicts.MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
40
conflicts.ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
41
conflicts.TextConflict(u'p\xe5tha'),
42
conflicts.PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
43
conflicts.DuplicateID('Unversioned existing file',
44
u'p\xe5thc', u'p\xe5thc2',
45
'\xc3\xaedc', '\xc3\xaedc'),
46
conflicts.DuplicateEntry('Moved existing file to',
47
u'p\xe5thdd.moved', u'p\xe5thd',
49
conflicts.ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
51
conflicts.UnversionedParent('Versioned directory',
52
u'p\xe5thf', '\xc3\xaedf'),
53
conflicts.NonDirectoryParent('Created directory',
54
u'p\xe5thg', '\xc3\xaedg'),
58
class TestConflicts(tests.TestCaseWithTransport):
60
def test_conflicts(self):
61
"""Conflicts are detected properly"""
62
# Use BzrDirFormat6 so we can fake conflicts
63
tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
64
self.build_tree_contents([('hello', 'hello world4'),
65
('hello.THIS', 'hello world2'),
66
('hello.BASE', 'hello world1'),
67
('hello.OTHER', 'hello world3'),
68
('hello.sploo.BASE', 'yellowworld'),
69
('hello.sploo.OTHER', 'yellowworld2'),
72
self.assertEqual(6, len(list(tree.list_files())))
74
tree_conflicts = tree.conflicts()
75
self.assertEqual(2, len(tree_conflicts))
76
self.assertTrue('hello' in tree_conflicts[0].path)
77
self.assertTrue('hello.sploo' in tree_conflicts[1].path)
78
conflicts.restore('hello')
79
conflicts.restore('hello.sploo')
80
self.assertEqual(0, len(tree.conflicts()))
81
self.assertFileEqual('hello world2', 'hello')
82
self.assertFalse(os.path.lexists('hello.sploo'))
83
self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
84
self.assertRaises(errors.NotConflicted,
85
conflicts.restore, 'hello.sploo')
87
def test_resolve_conflict_dir(self):
88
tree = self.make_branch_and_tree('.')
89
self.build_tree_contents([('hello', 'hello world4'),
90
('hello.THIS', 'hello world2'),
91
('hello.BASE', 'hello world1'),
93
os.mkdir('hello.OTHER')
94
tree.add('hello', 'q')
95
l = conflicts.ConflictList([conflicts.TextConflict('hello')])
98
def test_select_conflicts(self):
99
tree = self.make_branch_and_tree('.')
100
clist = conflicts.ConflictList
102
def check_select(not_selected, selected, paths, **kwargs):
104
(not_selected, selected),
105
tree_conflicts.select_conflicts(tree, paths, **kwargs))
107
foo = conflicts.ContentsConflict('foo')
108
bar = conflicts.ContentsConflict('bar')
109
tree_conflicts = clist([foo, bar])
111
check_select(clist([bar]), clist([foo]), ['foo'])
112
check_select(clist(), tree_conflicts,
113
[''], ignore_misses=True, recurse=True)
115
foobaz = conflicts.ContentsConflict('foo/baz')
116
tree_conflicts = clist([foobaz, bar])
118
check_select(clist([bar]), clist([foobaz]),
119
['foo'], ignore_misses=True, recurse=True)
121
qux = conflicts.PathConflict('qux', 'foo/baz')
122
tree_conflicts = clist([qux])
124
check_select(clist(), tree_conflicts,
125
['foo'], ignore_misses=True, recurse=True)
126
check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
128
def test_resolve_conflicts_recursive(self):
129
tree = self.make_branch_and_tree('.')
130
self.build_tree(['dir/', 'dir/hello'])
131
tree.add(['dir', 'dir/hello'])
133
dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
134
tree.set_conflicts(dirhello)
136
conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
137
self.assertEqual(dirhello, tree.conflicts())
139
conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
140
self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
143
class TestConflictStanzas(tests.TestCase):
145
def test_stanza_roundtrip(self):
146
# write and read our example stanza.
147
stanza_iter = example_conflicts.to_stanzas()
148
processed = conflicts.ConflictList.from_stanzas(stanza_iter)
149
for o, p in zip(processed, example_conflicts):
150
self.assertEqual(o, p)
152
self.assertIsInstance(o.path, unicode)
154
if o.file_id is not None:
155
self.assertIsInstance(o.file_id, str)
157
conflict_path = getattr(o, 'conflict_path', None)
158
if conflict_path is not None:
159
self.assertIsInstance(conflict_path, unicode)
161
conflict_file_id = getattr(o, 'conflict_file_id', None)
162
if conflict_file_id is not None:
163
self.assertIsInstance(conflict_file_id, str)
165
def test_stanzification(self):
166
for stanza in example_conflicts.to_stanzas():
167
if 'file_id' in stanza:
168
# In Stanza form, the file_id has to be unicode.
169
self.assertStartsWith(stanza['file_id'], u'\xeed')
170
self.assertStartsWith(stanza['path'], u'p\xe5th')
171
if 'conflict_path' in stanza:
172
self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
173
if 'conflict_file_id' in stanza:
174
self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
177
# FIXME: Tests missing for DuplicateID conflict type
178
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
180
preamble = None # The setup script set by daughter classes
183
super(TestResolveConflicts, self).setUp()
184
self.run_script(self.preamble)
187
class TestResolveTextConflicts(TestResolveConflicts):
192
class TestResolveContentConflicts(TestResolveConflicts):
194
# FIXME: We need to add the reverse case (delete in trunk, modify in
195
# branch) but that could wait until the resolution mechanism is implemented.
200
$ echo 'trunk content' >file
202
$ bzr commit -m 'Create trunk'
204
$ bzr branch . ../branch
207
$ bzr commit -m 'Delete file'
210
$ echo 'more content' >>file
211
$ bzr commit -m 'Modify file'
216
2>Contents conflict in file
217
2>1 conflicts encountered.
220
def test_take_this(self):
222
$ bzr rm file.OTHER --force # a simple rm file.OTHER is valid too
224
$ bzr commit --strict -m 'No more conflicts nor unknown files'
227
def test_take_other(self):
229
$ bzr mv file.OTHER file
231
$ bzr commit --strict -m 'No more conflicts nor unknown files'
234
def test_resolve_taking_this(self):
236
$ bzr resolve --take-this file
237
$ bzr commit --strict -m 'No more conflicts nor unknown files'
240
def test_resolve_taking_other(self):
242
$ bzr resolve --take-other file
243
$ bzr commit --strict -m 'No more conflicts nor unknown files'
247
class TestResolveDuplicateEntry(TestResolveConflicts):
252
$ echo 'trunk content' >file
254
$ bzr commit -m 'Create trunk'
255
$ echo 'trunk content too' >file2
257
$ bzr commit -m 'Add file2 in trunk'
259
$ bzr branch . -r 1 ../branch
261
$ echo 'branch content' >file2
263
$ bzr commit -m 'Add file2 in branch'
267
2>R file2 => file2.moved
268
2>Conflict adding file file2. Moved existing file to file2.moved.
269
2>1 conflicts encountered.
272
def test_keep_this(self):
274
$ bzr rm file2 --force
275
$ bzr mv file2.moved file2
277
$ bzr commit --strict -m 'No more conflicts nor unknown files'
280
def test_keep_other(self):
281
self.failIfExists('branch/file2.moved')
283
$ bzr rm file2.moved --force
285
$ bzr commit --strict -m 'No more conflicts nor unknown files'
287
self.failIfExists('branch/file2.moved')
289
def test_resolve_taking_this(self):
291
$ bzr resolve --take-this file2
292
$ bzr commit --strict -m 'No more conflicts nor unknown files'
295
def test_resolve_taking_other(self):
297
$ bzr resolve --take-other file2
298
$ bzr commit --strict -m 'No more conflicts nor unknown files'
302
class TestResolveUnversionedParent(TestResolveConflicts):
304
# FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
306
# FIXME: While this *creates* UnversionedParent conflicts, this really only
307
# tests MissingParent resolution :-/
313
$ bzr commit -m 'Create trunk'
314
$ echo 'trunk content' >dir/file
316
$ bzr commit -m 'Add dir/file in trunk'
318
$ bzr branch . -r 1 ../branch
321
$ bzr commit -m 'Remove dir in branch'
326
2>Conflict adding files to dir. Created directory.
327
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
328
2>2 conflicts encountered.
331
def test_take_this(self):
335
$ bzr commit --strict -m 'No more conflicts nor unknown files'
338
def test_take_other(self):
341
$ bzr commit --strict -m 'No more conflicts nor unknown files'
345
class TestResolveMissingParent(TestResolveConflicts):
351
$ echo 'trunk content' >dir/file
353
$ bzr commit -m 'Create trunk'
354
$ echo 'trunk content' >dir/file2
356
$ bzr commit -m 'Add dir/file2 in branch'
358
$ bzr branch . -r 1 ../branch
360
$ bzr rm dir/file --force
362
$ bzr commit -m 'Remove dir/file'
367
2>Conflict adding files to dir. Created directory.
368
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
369
2>2 conflicts encountered.
372
def test_keep_them_all(self):
375
$ bzr commit --strict -m 'No more conflicts nor unknown files'
378
def test_adopt_child(self):
380
$ bzr mv dir/file2 file2
383
$ bzr commit --strict -m 'No more conflicts nor unknown files'
386
def test_kill_them_all(self):
390
$ bzr commit --strict -m 'No more conflicts nor unknown files'
393
def test_resolve_taking_this(self):
395
$ bzr resolve --take-this dir
396
$ bzr commit --strict -m 'No more conflicts nor unknown files'
399
def test_resolve_taking_other(self):
401
$ bzr resolve --take-other dir
402
$ bzr commit --strict -m 'No more conflicts nor unknown files'
406
class TestResolveDeletingParent(TestResolveConflicts):
412
$ echo 'trunk content' >dir/file
414
$ bzr commit -m 'Create trunk'
415
$ bzr rm dir/file --force
417
$ bzr commit -m 'Remove dir/file'
419
$ bzr branch . -r 1 ../branch
421
$ echo 'branch content' >dir/file2
423
$ bzr commit -m 'Add dir/file2 in branch'
427
2>Conflict: can't delete dir because it is not empty. Not deleting.
428
2>Conflict because dir is not versioned, but has versioned children. Versioned directory.
429
2>2 conflicts encountered.
432
def test_keep_them_all(self):
435
$ bzr commit --strict -m 'No more conflicts nor unknown files'
438
def test_adopt_child(self):
440
$ bzr mv dir/file2 file2
443
$ bzr commit --strict -m 'No more conflicts nor unknown files'
446
def test_kill_them_all(self):
450
$ bzr commit --strict -m 'No more conflicts nor unknown files'
453
def test_resolve_taking_this(self):
455
$ bzr resolve --take-this dir
456
$ bzr commit --strict -m 'No more conflicts nor unknown files'
459
def test_resolve_taking_other(self):
461
$ bzr resolve --take-other dir
462
$ bzr commit --strict -m 'No more conflicts nor unknown files'
466
class TestResolvePathConflict(TestResolveConflicts):
473
$ bzr commit -m 'Create trunk'
474
$ bzr mv file file-in-trunk
475
$ bzr commit -m 'Renamed to file-in-trunk'
477
$ bzr branch . -r 1 ../branch
479
$ bzr mv file file-in-branch
480
$ bzr commit -m 'Renamed to file-in-branch'
483
2>R file-in-branch => file-in-trunk
484
2>Path conflict: file-in-branch / file-in-trunk
485
2>1 conflicts encountered.
488
def test_keep_source(self):
490
$ bzr resolve file-in-trunk
491
$ bzr commit --strict -m 'No more conflicts nor unknown files'
494
def test_keep_target(self):
496
$ bzr mv file-in-trunk file-in-branch
497
$ bzr resolve file-in-branch
498
$ bzr commit --strict -m 'No more conflicts nor unknown files'
501
def test_resolve_taking_this(self):
503
$ bzr resolve --take-this file-in-branch
504
$ bzr commit --strict -m 'No more conflicts nor unknown files'
507
def test_resolve_taking_other(self):
509
$ bzr resolve --take-other file-in-branch
510
$ bzr commit --strict -m 'No more conflicts nor unknown files'
514
class TestResolveParentLoop(TestResolveConflicts):
521
$ bzr commit -m 'Create trunk'
523
$ bzr commit -m 'Moved dir2 into dir1'
525
$ bzr branch . -r 1 ../branch
528
$ bzr commit -m 'Moved dir1 into dir2'
531
2>Conflict moving dir2/dir1 into dir2. Cancelled move.
532
2>1 conflicts encountered.
535
def test_take_this(self):
538
$ bzr commit --strict -m 'No more conflicts nor unknown files'
541
def test_take_other(self):
543
$ bzr mv dir2/dir1 dir1
546
$ bzr commit --strict -m 'No more conflicts nor unknown files'
549
def test_resolve_taking_this(self):
551
$ bzr resolve --take-this dir2
552
$ bzr commit --strict -m 'No more conflicts nor unknown files'
554
self.failUnlessExists('dir2')
556
def test_resolve_taking_other(self):
558
$ bzr resolve --take-other dir2
559
$ bzr commit --strict -m 'No more conflicts nor unknown files'
561
self.failUnlessExists('dir1')
564
class TestResolveNonDirectoryParent(TestResolveConflicts):
570
$ bzr commit -m 'Create trunk'
571
$ echo "Boing" >foo/bar
573
$ bzr commit -m 'Add foo/bar'
575
$ bzr branch . -r 1 ../branch
579
$ bzr commit -m 'foo is now a file'
584
# FIXME: The message is misleading, foo.new *is* a directory when the message
585
# is displayed -- vila 090916
586
2>Conflict: foo.new is not a directory, but has files in it. Created directory.
587
2>1 conflicts encountered.
590
def test_take_this(self):
592
$ bzr rm foo.new --force
593
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
594
# aside ? -- vila 090916
596
$ bzr resolve foo.new
597
$ bzr commit --strict -m 'No more conflicts nor unknown files'
600
def test_take_other(self):
605
$ bzr commit --strict -m 'No more conflicts nor unknown files'
608
def test_resolve_taking_this(self):
610
$ bzr resolve --take-this foo.new
611
$ bzr commit --strict -m 'No more conflicts nor unknown files'
614
def test_resolve_taking_other(self):
616
$ bzr resolve --take-other foo.new
617
$ bzr commit --strict -m 'No more conflicts nor unknown files'
621
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
623
def test_bug_430129(self):
624
# This is nearly like TestResolveNonDirectoryParent but with branch and
625
# trunk switched. As such it should certainly produce the same
631
$ bzr commit -m 'Create trunk'
634
$ bzr commit -m 'foo is now a file'
636
$ bzr branch . -r 1 ../branch
638
$ echo "Boing" >foo/bar
640
$ bzr commit -m 'Add foo/bar'
643
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
647
class TestResolveActionOption(tests.TestCase):
650
super(TestResolveActionOption, self).setUp()
651
self.options = [conflicts.ResolveActionOption()]
652
self.parser = option.get_optparser(dict((o.name, o)
653
for o in self.options))
655
def parse(self, args):
656
return self.parser.parse_args(args)
658
def test_unknown_action(self):
659
self.assertRaises(errors.BadOptionValue,
660
self.parse, ['--action', 'take-me-to-the-moon'])
663
opts, args = self.parse(['--action', 'done'])
664
self.assertEqual({'action':'done'}, opts)
666
def test_take_this(self):
667
opts, args = self.parse(['--action', 'take-this'])
668
self.assertEqual({'action': 'take_this'}, opts)
669
opts, args = self.parse(['--take-this'])
670
self.assertEqual({'action': 'take_this'}, opts)
672
def test_take_other(self):
673
opts, args = self.parse(['--action', 'take-other'])
674
self.assertEqual({'action': 'take_other'}, opts)
675
opts, args = self.parse(['--take-other'])
676
self.assertEqual({'action': 'take_other'}, opts)