~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Vincent Ladeuil
  • Date: 2016-01-21 09:52:36 UTC
  • mto: This revision was merged to the branch mainline in revision 6611.
  • Revision ID: v.ladeuil+lp@free.fr-20160121095236-iandk0fic0phu9ey
Fix the failing gpg test on wily.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
18
18
import os
19
19
 
20
20
from bzrlib import (
21
 
    bzrdir,
22
21
    conflicts,
23
22
    errors,
24
23
    option,
 
24
    osutils,
25
25
    tests,
26
26
    )
27
 
from bzrlib.tests import script
 
27
from bzrlib.tests import (
 
28
    script,
 
29
    scenarios,
 
30
    )
 
31
 
 
32
 
 
33
load_tests = scenarios.load_tests_apply_scenarios
28
34
 
29
35
 
30
36
# TODO: Test commit with some added, and added-but-missing files
55
61
])
56
62
 
57
63
 
 
64
def vary_by_conflicts():
 
65
    for conflict in example_conflicts:
 
66
        yield (conflict.__class__.__name__, {"conflict": conflict})
 
67
 
 
68
 
58
69
class TestConflicts(tests.TestCaseWithTransport):
59
70
 
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'),
70
 
                                  ])
71
 
        tree.lock_read()
72
 
        self.assertEqual(6, len(list(tree.list_files())))
73
 
        tree.unlock()
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')
86
 
 
87
71
    def test_resolve_conflict_dir(self):
88
72
        tree = self.make_branch_and_tree('.')
89
73
        self.build_tree_contents([('hello', 'hello world4'),
140
124
        self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
141
125
 
142
126
 
143
 
class TestConflictStanzas(tests.TestCase):
 
127
class TestPerConflict(tests.TestCase):
 
128
 
 
129
    scenarios = scenarios.multiply_scenarios(vary_by_conflicts())
 
130
 
 
131
    def test_stringification(self):
 
132
        text = unicode(self.conflict)
 
133
        self.assertContainsString(text, self.conflict.path)
 
134
        self.assertContainsString(text.lower(), "conflict")
 
135
        self.assertContainsString(repr(self.conflict),
 
136
            self.conflict.__class__.__name__)
144
137
 
145
138
    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)
151
 
 
152
 
            self.assertIsInstance(o.path, unicode)
153
 
 
154
 
            if o.file_id is not None:
155
 
                self.assertIsInstance(o.file_id, str)
156
 
 
157
 
            conflict_path = getattr(o, 'conflict_path', None)
158
 
            if conflict_path is not None:
159
 
                self.assertIsInstance(conflict_path, unicode)
160
 
 
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)
 
139
        p = self.conflict
 
140
        o = conflicts.Conflict.factory(**p.as_stanza().as_dict())
 
141
        self.assertEqual(o, p)
 
142
 
 
143
        self.assertIsInstance(o.path, unicode)
 
144
 
 
145
        if o.file_id is not None:
 
146
            self.assertIsInstance(o.file_id, str)
 
147
 
 
148
        conflict_path = getattr(o, 'conflict_path', None)
 
149
        if conflict_path is not None:
 
150
            self.assertIsInstance(conflict_path, unicode)
 
151
 
 
152
        conflict_file_id = getattr(o, 'conflict_file_id', None)
 
153
        if conflict_file_id is not None:
 
154
            self.assertIsInstance(conflict_file_id, str)
164
155
 
165
156
    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')
 
157
        stanza = self.conflict.as_stanza()
 
158
        if 'file_id' in stanza:
 
159
            # In Stanza form, the file_id has to be unicode.
 
160
            self.assertStartsWith(stanza['file_id'], u'\xeed')
 
161
        self.assertStartsWith(stanza['path'], u'p\xe5th')
 
162
        if 'conflict_path' in stanza:
 
163
            self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
 
164
        if 'conflict_file_id' in stanza:
 
165
            self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
 
166
 
 
167
 
 
168
class TestConflictList(tests.TestCase):
 
169
 
 
170
    def test_stanzas_roundtrip(self):
 
171
        stanzas_iter = example_conflicts.to_stanzas()
 
172
        processed = conflicts.ConflictList.from_stanzas(stanzas_iter)
 
173
        self.assertEqual(example_conflicts, processed)
 
174
 
 
175
    def test_stringification(self):
 
176
        for text, o in zip(example_conflicts.to_strings(), example_conflicts):
 
177
            self.assertEqual(text, unicode(o))
175
178
 
176
179
 
177
180
# FIXME: The shell-like tests should be converted to real whitebox tests... or
178
181
# moved to a blackbox module -- vila 20100205
179
182
 
 
183
# FIXME: test missing for multiple conflicts
 
184
 
180
185
# FIXME: Tests missing for DuplicateID conflict type
181
186
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
182
187
 
187
192
        self.run_script(self.preamble)
188
193
 
189
194
 
190
 
class TestResolveTextConflicts(TestResolveConflicts):
191
 
    # TBC
192
 
    pass
193
 
 
194
 
 
195
 
class TestResolveContentConflicts(TestResolveConflicts):
196
 
 
197
 
    # FIXME: We need to add the reverse case (delete in trunk, modify in
198
 
    # branch) but that could wait until the resolution mechanism is implemented.
199
 
 
200
 
    preamble = """
201
 
$ bzr init trunk
202
 
$ cd trunk
203
 
$ echo 'trunk content' >file
204
 
$ bzr add file
205
 
$ bzr commit -m 'Create trunk'
206
 
 
207
 
$ bzr branch . ../branch
208
 
$ cd ../branch
209
 
$ bzr rm file
210
 
$ bzr commit -m 'Delete file'
211
 
 
212
 
$ cd ../trunk
213
 
$ echo 'more content' >>file
214
 
$ bzr commit -m 'Modify file'
215
 
 
216
 
$ cd ../branch
217
 
$ bzr merge ../trunk
218
 
2>+N  file.OTHER
219
 
2>Contents conflict in file
220
 
2>1 conflicts encountered.
221
 
"""
222
 
 
223
 
    def test_take_this(self):
224
 
        self.run_script("""
225
 
$ bzr rm file.OTHER --force # a simple rm file.OTHER is valid too
226
 
$ bzr resolve file
227
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
228
 
""")
229
 
 
230
 
    def test_take_other(self):
231
 
        self.run_script("""
232
 
$ bzr mv file.OTHER file
233
 
$ bzr resolve file
234
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
235
 
""")
236
 
 
237
 
    def test_resolve_taking_this(self):
238
 
        self.run_script("""
239
 
$ bzr resolve --take-this file
240
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
241
 
""")
242
 
 
243
 
    def test_resolve_taking_other(self):
244
 
        self.run_script("""
245
 
$ bzr resolve --take-other file
246
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
247
 
""")
248
 
 
249
 
 
250
 
class TestResolveDuplicateEntry(TestResolveConflicts):
251
 
 
252
 
    preamble = """
253
 
$ bzr init trunk
254
 
$ cd trunk
255
 
$ echo 'trunk content' >file
256
 
$ bzr add file
257
 
$ bzr commit -m 'Create trunk'
258
 
$ echo 'trunk content too' >file2
259
 
$ bzr add file2
260
 
$ bzr commit -m 'Add file2 in trunk'
261
 
 
262
 
$ bzr branch . -r 1 ../branch
263
 
$ cd ../branch
264
 
$ echo 'branch content' >file2
265
 
$ bzr add file2
266
 
$ bzr commit -m 'Add file2 in branch'
267
 
 
268
 
$ bzr merge ../trunk
269
 
2>+N  file2
270
 
2>R   file2 => file2.moved
271
 
2>Conflict adding file file2.  Moved existing file to file2.moved.
272
 
2>1 conflicts encountered.
273
 
"""
274
 
 
275
 
    def test_keep_this(self):
276
 
        self.run_script("""
277
 
$ bzr rm file2  --force
278
 
$ bzr mv file2.moved file2
279
 
$ bzr resolve file2
280
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
281
 
""")
282
 
 
283
 
    def test_keep_other(self):
284
 
        self.failIfExists('branch/file2.moved')
285
 
        self.run_script("""
286
 
$ bzr rm file2.moved --force
287
 
$ bzr resolve file2
288
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
289
 
""")
290
 
        self.failIfExists('branch/file2.moved')
291
 
 
292
 
    def test_resolve_taking_this(self):
293
 
        self.run_script("""
294
 
$ bzr resolve --take-this file2
295
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
296
 
""")
297
 
 
298
 
    def test_resolve_taking_other(self):
299
 
        self.run_script("""
300
 
$ bzr resolve --take-other file2
301
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
302
 
""")
 
195
def mirror_scenarios(base_scenarios):
 
196
    """Return a list of mirrored scenarios.
 
197
 
 
198
    Each scenario in base_scenarios is duplicated switching the roles of 'this'
 
199
    and 'other'
 
200
    """
 
201
    scenarios = []
 
202
    for common, (lname, ldict), (rname, rdict) in base_scenarios:
 
203
        a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
 
204
                                     [(rname, dict(_other=rdict))])
 
205
        b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
 
206
                                     [(lname, dict(_other=ldict))])
 
207
        # Inject the common parameters in all scenarios
 
208
        for name, d in a + b:
 
209
            d.update(common)
 
210
        scenarios.extend(a + b)
 
211
    return scenarios
 
212
 
 
213
 
 
214
# FIXME: Get rid of parametrized (in the class name) once we delete
 
215
# TestResolveConflicts -- vila 20100308
 
216
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
 
217
    """This class provides a base to test single conflict resolution.
 
218
 
 
219
    Since all conflict objects are created with specific semantics for their
 
220
    attributes, each class should implement the necessary functions and
 
221
    attributes described below.
 
222
 
 
223
    Each class should define the scenarios that create the expected (single)
 
224
    conflict.
 
225
 
 
226
    Each scenario describes:
 
227
    * how to create 'base' tree (and revision)
 
228
    * how to create 'left' tree (and revision, parent rev 'base')
 
229
    * how to create 'right' tree (and revision, parent rev 'base')
 
230
    * how to check that changes in 'base'->'left' have been taken
 
231
    * how to check that changes in 'base'->'right' have been taken
 
232
 
 
233
    From each base scenario, we generate two concrete scenarios where:
 
234
    * this=left, other=right
 
235
    * this=right, other=left
 
236
 
 
237
    Then the test case verifies each concrete scenario by:
 
238
    * creating a branch containing the 'base', 'this' and 'other' revisions
 
239
    * creating a working tree for the 'this' revision
 
240
    * performing the merge of 'other' into 'this'
 
241
    * verifying the expected conflict was generated
 
242
    * resolving with --take-this or --take-other, and running the corresponding
 
243
      checks (for either 'base'->'this', or 'base'->'other')
 
244
 
 
245
    :cvar _conflict_type: The expected class of the generated conflict.
 
246
 
 
247
    :cvar _assert_conflict: A method receiving the working tree and the
 
248
        conflict object and checking its attributes.
 
249
 
 
250
    :cvar _base_actions: The branchbuilder actions to create the 'base'
 
251
        revision.
 
252
 
 
253
    :cvar _this: The dict related to 'base' -> 'this'. It contains at least:
 
254
      * 'actions': The branchbuilder actions to create the 'this'
 
255
          revision.
 
256
      * 'check': how to check the changes after resolution with --take-this.
 
257
 
 
258
    :cvar _other: The dict related to 'base' -> 'other'. It contains at least:
 
259
      * 'actions': The branchbuilder actions to create the 'other'
 
260
          revision.
 
261
      * 'check': how to check the changes after resolution with --take-other.
 
262
    """
 
263
 
 
264
    # Set by daughter classes
 
265
    _conflict_type = None
 
266
    _assert_conflict = None
 
267
 
 
268
    # Set by load_tests
 
269
    _base_actions = None
 
270
    _this = None
 
271
    _other = None
 
272
 
 
273
    scenarios = []
 
274
    """The scenario list for the conflict type defined by the class.
 
275
 
 
276
    Each scenario is of the form:
 
277
    (common, (left_name, left_dict), (right_name, right_dict))
 
278
 
 
279
    * common is a dict
 
280
 
 
281
    * left_name and right_name are the scenario names that will be combined
 
282
 
 
283
    * left_dict and right_dict are the attributes specific to each half of
 
284
      the scenario. They should include at least 'actions' and 'check' and
 
285
      will be available as '_this' and '_other' test instance attributes.
 
286
 
 
287
    Daughters classes are free to add their specific attributes as they see
 
288
    fit in any of the three dicts.
 
289
 
 
290
    This is a class method so that load_tests can find it.
 
291
 
 
292
    '_base_actions' in the common dict, 'actions' and 'check' in the left
 
293
    and right dicts use names that map to methods in the test classes. Some
 
294
    prefixes are added to these names to get the correspong methods (see
 
295
    _get_actions() and _get_check()). The motivation here is to avoid
 
296
    collisions in the class namespace.
 
297
    """
 
298
 
 
299
    def setUp(self):
 
300
        super(TestParametrizedResolveConflicts, self).setUp()
 
301
        builder = self.make_branch_builder('trunk')
 
302
        builder.start_series()
 
303
 
 
304
        # Create an empty trunk
 
305
        builder.build_snapshot('start', None, [
 
306
                ('add', ('', 'root-id', 'directory', ''))])
 
307
        # Add a minimal base content
 
308
        base_actions = self._get_actions(self._base_actions)()
 
309
        builder.build_snapshot('base', ['start'], base_actions)
 
310
        # Modify the base content in branch
 
311
        actions_other = self._get_actions(self._other['actions'])()
 
312
        builder.build_snapshot('other', ['base'], actions_other)
 
313
        # Modify the base content in trunk
 
314
        actions_this = self._get_actions(self._this['actions'])()
 
315
        builder.build_snapshot('this', ['base'], actions_this)
 
316
        # builder.get_branch() tip is now 'this'
 
317
 
 
318
        builder.finish_series()
 
319
        self.builder = builder
 
320
 
 
321
    def _get_actions(self, name):
 
322
        return getattr(self, 'do_%s' % name)
 
323
 
 
324
    def _get_check(self, name):
 
325
        return getattr(self, 'check_%s' % name)
 
326
 
 
327
    def _merge_other_into_this(self):
 
328
        b = self.builder.get_branch()
 
329
        wt = b.bzrdir.sprout('branch').open_workingtree()
 
330
        wt.merge_from_branch(b, 'other')
 
331
        return wt
 
332
 
 
333
    def assertConflict(self, wt):
 
334
        confs = wt.conflicts()
 
335
        self.assertLength(1, confs)
 
336
        c = confs[0]
 
337
        self.assertIsInstance(c, self._conflict_type)
 
338
        self._assert_conflict(wt, c)
 
339
 
 
340
    def _get_resolve_path_arg(self, wt, action):
 
341
        raise NotImplementedError(self._get_resolve_path_arg)
 
342
 
 
343
    def check_resolved(self, wt, action):
 
344
        path = self._get_resolve_path_arg(wt, action)
 
345
        conflicts.resolve(wt, [path], action=action)
 
346
        # Check that we don't have any conflicts nor unknown left
 
347
        self.assertLength(0, wt.conflicts())
 
348
        self.assertLength(0, list(wt.unknowns()))
 
349
 
 
350
    def test_resolve_taking_this(self):
 
351
        wt = self._merge_other_into_this()
 
352
        self.assertConflict(wt)
 
353
        self.check_resolved(wt, 'take_this')
 
354
        check_this = self._get_check(self._this['check'])
 
355
        check_this()
 
356
 
 
357
    def test_resolve_taking_other(self):
 
358
        wt = self._merge_other_into_this()
 
359
        self.assertConflict(wt)
 
360
        self.check_resolved(wt, 'take_other')
 
361
        check_other = self._get_check(self._other['check'])
 
362
        check_other()
 
363
 
 
364
 
 
365
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
 
366
 
 
367
    _conflict_type = conflicts.TextConflict
 
368
 
 
369
    # Set by the scenarios
 
370
    # path and file-id for the file involved in the conflict
 
371
    _path = None
 
372
    _file_id = None
 
373
 
 
374
    scenarios = mirror_scenarios(
 
375
        [
 
376
            # File modified on both sides
 
377
            (dict(_base_actions='create_file',
 
378
                  _path='file', _file_id='file-id'),
 
379
             ('filed_modified_A',
 
380
              dict(actions='modify_file_A', check='file_has_content_A')),
 
381
             ('file_modified_B',
 
382
              dict(actions='modify_file_B', check='file_has_content_B')),),
 
383
            # File modified on both sides in dir
 
384
            (dict(_base_actions='create_file_in_dir',
 
385
                  _path='dir/file', _file_id='file-id'),
 
386
             ('filed_modified_A_in_dir',
 
387
              dict(actions='modify_file_A',
 
388
                   check='file_in_dir_has_content_A')),
 
389
             ('file_modified_B',
 
390
              dict(actions='modify_file_B',
 
391
                   check='file_in_dir_has_content_B')),),
 
392
            ])
 
393
 
 
394
    def do_create_file(self, path='file'):
 
395
        return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
 
396
 
 
397
    def do_modify_file_A(self):
 
398
        return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
 
399
 
 
400
    def do_modify_file_B(self):
 
401
        return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
 
402
 
 
403
    def check_file_has_content_A(self, path='file'):
 
404
        self.assertFileEqual('trunk content\nfeature A\n',
 
405
                             osutils.pathjoin('branch', path))
 
406
 
 
407
    def check_file_has_content_B(self, path='file'):
 
408
        self.assertFileEqual('trunk content\nfeature B\n',
 
409
                             osutils.pathjoin('branch', path))
 
410
 
 
411
    def do_create_file_in_dir(self):
 
412
        return [('add', ('dir', 'dir-id', 'directory', '')),
 
413
            ] + self.do_create_file('dir/file')
 
414
 
 
415
    def check_file_in_dir_has_content_A(self):
 
416
        self.check_file_has_content_A('dir/file')
 
417
 
 
418
    def check_file_in_dir_has_content_B(self):
 
419
        self.check_file_has_content_B('dir/file')
 
420
 
 
421
    def _get_resolve_path_arg(self, wt, action):
 
422
        return self._path
 
423
 
 
424
    def assertTextConflict(self, wt, c):
 
425
        self.assertEqual(self._file_id, c.file_id)
 
426
        self.assertEqual(self._path, c.path)
 
427
    _assert_conflict = assertTextConflict
 
428
 
 
429
 
 
430
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
 
431
 
 
432
    _conflict_type = conflicts.ContentsConflict
 
433
 
 
434
    # Set by the scenarios
 
435
    # path and file-id for the file involved in the conflict
 
436
    _path = None
 
437
    _file_id = None
 
438
 
 
439
    scenarios = mirror_scenarios(
 
440
        [
 
441
            # File modified/deleted
 
442
            (dict(_base_actions='create_file',
 
443
                  _path='file', _file_id='file-id'),
 
444
             ('file_modified',
 
445
              dict(actions='modify_file', check='file_has_more_content')),
 
446
             ('file_deleted',
 
447
              dict(actions='delete_file', check='file_doesnt_exist')),),
 
448
            # File renamed-modified/deleted
 
449
            (dict(_base_actions='create_file',
 
450
                  _path='new-file', _file_id='file-id'),
 
451
             ('file_renamed_and_modified',
 
452
              dict(actions='modify_and_rename_file',
 
453
                   check='file_renamed_and_more_content')),
 
454
             ('file_deleted',
 
455
              dict(actions='delete_file', check='file_doesnt_exist')),),
 
456
            # File modified/deleted in dir
 
457
            (dict(_base_actions='create_file_in_dir',
 
458
                  _path='dir/file', _file_id='file-id'),
 
459
             ('file_modified_in_dir',
 
460
              dict(actions='modify_file_in_dir',
 
461
                   check='file_in_dir_has_more_content')),
 
462
             ('file_deleted_in_dir',
 
463
              dict(actions='delete_file',
 
464
                   check='file_in_dir_doesnt_exist')),),
 
465
            ])
 
466
 
 
467
    def do_create_file(self):
 
468
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
 
469
 
 
470
    def do_modify_file(self):
 
471
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
 
472
 
 
473
    def do_modify_and_rename_file(self):
 
474
        return [('modify', ('file-id', 'trunk content\nmore content\n')),
 
475
                ('rename', ('file', 'new-file'))]
 
476
 
 
477
    def check_file_has_more_content(self):
 
478
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
 
479
 
 
480
    def check_file_renamed_and_more_content(self):
 
481
        self.assertFileEqual('trunk content\nmore content\n', 'branch/new-file')
 
482
 
 
483
    def do_delete_file(self):
 
484
        return [('unversion', 'file-id')]
 
485
 
 
486
    def check_file_doesnt_exist(self):
 
487
        self.assertPathDoesNotExist('branch/file')
 
488
 
 
489
    def do_create_file_in_dir(self):
 
490
        return [('add', ('dir', 'dir-id', 'directory', '')),
 
491
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
 
492
 
 
493
    def do_modify_file_in_dir(self):
 
494
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
 
495
 
 
496
    def check_file_in_dir_has_more_content(self):
 
497
        self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
 
498
 
 
499
    def check_file_in_dir_doesnt_exist(self):
 
500
        self.assertPathDoesNotExist('branch/dir/file')
 
501
 
 
502
    def _get_resolve_path_arg(self, wt, action):
 
503
        return self._path
 
504
 
 
505
    def assertContentsConflict(self, wt, c):
 
506
        self.assertEqual(self._file_id, c.file_id)
 
507
        self.assertEqual(self._path, c.path)
 
508
    _assert_conflict = assertContentsConflict
 
509
 
 
510
 
 
511
class TestResolvePathConflict(TestParametrizedResolveConflicts):
 
512
 
 
513
    _conflict_type = conflicts.PathConflict
 
514
 
 
515
    def do_nothing(self):
 
516
        return []
 
517
 
 
518
    # Each side dict additionally defines:
 
519
    # - path path involved (can be '<deleted>')
 
520
    # - file-id involved
 
521
    scenarios = mirror_scenarios(
 
522
        [
 
523
            # File renamed/deleted
 
524
            (dict(_base_actions='create_file'),
 
525
             ('file_renamed',
 
526
              dict(actions='rename_file', check='file_renamed',
 
527
                   path='new-file', file_id='file-id')),
 
528
             ('file_deleted',
 
529
              dict(actions='delete_file', check='file_doesnt_exist',
 
530
                   # PathConflicts deletion handling requires a special
 
531
                   # hard-coded value
 
532
                   path='<deleted>', file_id='file-id')),),
 
533
            # File renamed/deleted in dir
 
534
            (dict(_base_actions='create_file_in_dir'),
 
535
             ('file_renamed_in_dir',
 
536
              dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
 
537
                   path='dir/new-file', file_id='file-id')),
 
538
             ('file_deleted',
 
539
              dict(actions='delete_file', check='file_in_dir_doesnt_exist',
 
540
                   # PathConflicts deletion handling requires a special
 
541
                   # hard-coded value
 
542
                   path='<deleted>', file_id='file-id')),),
 
543
            # File renamed/renamed differently
 
544
            (dict(_base_actions='create_file'),
 
545
             ('file_renamed',
 
546
              dict(actions='rename_file', check='file_renamed',
 
547
                   path='new-file', file_id='file-id')),
 
548
             ('file_renamed2',
 
549
              dict(actions='rename_file2', check='file_renamed2',
 
550
                   path='new-file2', file_id='file-id')),),
 
551
            # Dir renamed/deleted
 
552
            (dict(_base_actions='create_dir'),
 
553
             ('dir_renamed',
 
554
              dict(actions='rename_dir', check='dir_renamed',
 
555
                   path='new-dir', file_id='dir-id')),
 
556
             ('dir_deleted',
 
557
              dict(actions='delete_dir', check='dir_doesnt_exist',
 
558
                   # PathConflicts deletion handling requires a special
 
559
                   # hard-coded value
 
560
                   path='<deleted>', file_id='dir-id')),),
 
561
            # Dir renamed/renamed differently
 
562
            (dict(_base_actions='create_dir'),
 
563
             ('dir_renamed',
 
564
              dict(actions='rename_dir', check='dir_renamed',
 
565
                   path='new-dir', file_id='dir-id')),
 
566
             ('dir_renamed2',
 
567
              dict(actions='rename_dir2', check='dir_renamed2',
 
568
                   path='new-dir2', file_id='dir-id')),),
 
569
            ])
 
570
 
 
571
    def do_create_file(self):
 
572
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
 
573
 
 
574
    def do_create_dir(self):
 
575
        return [('add', ('dir', 'dir-id', 'directory', ''))]
 
576
 
 
577
    def do_rename_file(self):
 
578
        return [('rename', ('file', 'new-file'))]
 
579
 
 
580
    def check_file_renamed(self):
 
581
        self.assertPathDoesNotExist('branch/file')
 
582
        self.assertPathExists('branch/new-file')
 
583
 
 
584
    def do_rename_file2(self):
 
585
        return [('rename', ('file', 'new-file2'))]
 
586
 
 
587
    def check_file_renamed2(self):
 
588
        self.assertPathDoesNotExist('branch/file')
 
589
        self.assertPathExists('branch/new-file2')
 
590
 
 
591
    def do_rename_dir(self):
 
592
        return [('rename', ('dir', 'new-dir'))]
 
593
 
 
594
    def check_dir_renamed(self):
 
595
        self.assertPathDoesNotExist('branch/dir')
 
596
        self.assertPathExists('branch/new-dir')
 
597
 
 
598
    def do_rename_dir2(self):
 
599
        return [('rename', ('dir', 'new-dir2'))]
 
600
 
 
601
    def check_dir_renamed2(self):
 
602
        self.assertPathDoesNotExist('branch/dir')
 
603
        self.assertPathExists('branch/new-dir2')
 
604
 
 
605
    def do_delete_file(self):
 
606
        return [('unversion', 'file-id')]
 
607
 
 
608
    def check_file_doesnt_exist(self):
 
609
        self.assertPathDoesNotExist('branch/file')
 
610
 
 
611
    def do_delete_dir(self):
 
612
        return [('unversion', 'dir-id')]
 
613
 
 
614
    def check_dir_doesnt_exist(self):
 
615
        self.assertPathDoesNotExist('branch/dir')
 
616
 
 
617
    def do_create_file_in_dir(self):
 
618
        return [('add', ('dir', 'dir-id', 'directory', '')),
 
619
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
 
620
 
 
621
    def do_rename_file_in_dir(self):
 
622
        return [('rename', ('dir/file', 'dir/new-file'))]
 
623
 
 
624
    def check_file_in_dir_renamed(self):
 
625
        self.assertPathDoesNotExist('branch/dir/file')
 
626
        self.assertPathExists('branch/dir/new-file')
 
627
 
 
628
    def check_file_in_dir_doesnt_exist(self):
 
629
        self.assertPathDoesNotExist('branch/dir/file')
 
630
 
 
631
    def _get_resolve_path_arg(self, wt, action):
 
632
        tpath = self._this['path']
 
633
        opath = self._other['path']
 
634
        if tpath == '<deleted>':
 
635
            path = opath
 
636
        else:
 
637
            path = tpath
 
638
        return path
 
639
 
 
640
    def assertPathConflict(self, wt, c):
 
641
        tpath = self._this['path']
 
642
        tfile_id = self._this['file_id']
 
643
        opath = self._other['path']
 
644
        ofile_id = self._other['file_id']
 
645
        self.assertEqual(tfile_id, ofile_id) # Sanity check
 
646
        self.assertEqual(tfile_id, c.file_id)
 
647
        self.assertEqual(tpath, c.path)
 
648
        self.assertEqual(opath, c.conflict_path)
 
649
    _assert_conflict = assertPathConflict
 
650
 
 
651
 
 
652
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
 
653
    """Same as TestResolvePathConflict but a specific conflict object.
 
654
    """
 
655
 
 
656
    def assertPathConflict(self, c):
 
657
        # We create a conflict object as it was created before the fix and
 
658
        # inject it into the working tree, the test will exercise the
 
659
        # compatibility code.
 
660
        old_c = conflicts.PathConflict('<deleted>', self._item_path,
 
661
                                       file_id=None)
 
662
        wt.set_conflicts(conflicts.ConflictList([old_c]))
 
663
 
 
664
 
 
665
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
 
666
 
 
667
    _conflict_type = conflicts.DuplicateEntry
 
668
 
 
669
    scenarios = mirror_scenarios(
 
670
        [
 
671
            # File created with different file-ids
 
672
            (dict(_base_actions='nothing'),
 
673
             ('filea_created',
 
674
              dict(actions='create_file_a', check='file_content_a',
 
675
                   path='file', file_id='file-a-id')),
 
676
             ('fileb_created',
 
677
              dict(actions='create_file_b', check='file_content_b',
 
678
                   path='file', file_id='file-b-id')),),
 
679
            # File created with different file-ids but deleted on one side
 
680
            (dict(_base_actions='create_file_a'),
 
681
             ('filea_replaced',
 
682
              dict(actions='replace_file_a_by_b', check='file_content_b',
 
683
                   path='file', file_id='file-b-id')),
 
684
             ('filea_modified',
 
685
              dict(actions='modify_file_a', check='file_new_content',
 
686
                   path='file', file_id='file-a-id')),),
 
687
            ])
 
688
 
 
689
    def do_nothing(self):
 
690
        return []
 
691
 
 
692
    def do_create_file_a(self):
 
693
        return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
 
694
 
 
695
    def check_file_content_a(self):
 
696
        self.assertFileEqual('file a content\n', 'branch/file')
 
697
 
 
698
    def do_create_file_b(self):
 
699
        return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
 
700
 
 
701
    def check_file_content_b(self):
 
702
        self.assertFileEqual('file b content\n', 'branch/file')
 
703
 
 
704
    def do_replace_file_a_by_b(self):
 
705
        return [('unversion', 'file-a-id'),
 
706
                ('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
 
707
 
 
708
    def do_modify_file_a(self):
 
709
        return [('modify', ('file-a-id', 'new content\n'))]
 
710
 
 
711
    def check_file_new_content(self):
 
712
        self.assertFileEqual('new content\n', 'branch/file')
 
713
 
 
714
    def _get_resolve_path_arg(self, wt, action):
 
715
        return self._this['path']
 
716
 
 
717
    def assertDuplicateEntry(self, wt, c):
 
718
        tpath = self._this['path']
 
719
        tfile_id = self._this['file_id']
 
720
        opath = self._other['path']
 
721
        ofile_id = self._other['file_id']
 
722
        self.assertEqual(tpath, opath) # Sanity check
 
723
        self.assertEqual(tfile_id, c.file_id)
 
724
        self.assertEqual(tpath + '.moved', c.path)
 
725
        self.assertEqual(tpath, c.conflict_path)
 
726
    _assert_conflict = assertDuplicateEntry
303
727
 
304
728
 
305
729
class TestResolveUnversionedParent(TestResolveConflicts):
310
734
    # tests MissingParent resolution :-/
311
735
    preamble = """
312
736
$ bzr init trunk
 
737
...
313
738
$ cd trunk
314
739
$ mkdir dir
315
 
$ bzr add dir
316
 
$ bzr commit -m 'Create trunk'
 
740
$ bzr add -q dir
 
741
$ bzr commit -m 'Create trunk' -q
317
742
$ echo 'trunk content' >dir/file
318
 
$ bzr add dir/file
319
 
$ bzr commit -m 'Add dir/file in trunk'
320
 
 
321
 
$ bzr branch . -r 1 ../branch
 
743
$ bzr add -q dir/file
 
744
$ bzr commit -q -m 'Add dir/file in trunk'
 
745
$ bzr branch -q . -r 1 ../branch
322
746
$ cd ../branch
323
 
$ bzr rm dir
324
 
$ bzr commit -m 'Remove dir in branch'
325
 
 
 
747
$ bzr rm dir -q
 
748
$ bzr commit -q -m 'Remove dir in branch'
326
749
$ bzr merge ../trunk
327
750
2>+N  dir/
328
751
2>+N  dir/file
333
756
 
334
757
    def test_take_this(self):
335
758
        self.run_script("""
336
 
$ bzr rm dir  --force
 
759
$ bzr rm -q dir --no-backup
337
760
$ bzr resolve dir
338
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
761
2>2 conflicts resolved, 0 remaining
 
762
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
339
763
""")
340
764
 
341
765
    def test_take_other(self):
342
766
        self.run_script("""
343
767
$ bzr resolve dir
344
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
768
2>2 conflicts resolved, 0 remaining
 
769
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
345
770
""")
346
771
 
347
772
 
349
774
 
350
775
    preamble = """
351
776
$ bzr init trunk
 
777
...
352
778
$ cd trunk
353
779
$ mkdir dir
354
780
$ echo 'trunk content' >dir/file
355
 
$ bzr add
356
 
$ bzr commit -m 'Create trunk'
 
781
$ bzr add -q
 
782
$ bzr commit -m 'Create trunk' -q
357
783
$ echo 'trunk content' >dir/file2
358
 
$ bzr add dir/file2
359
 
$ bzr commit -m 'Add dir/file2 in branch'
360
 
 
361
 
$ bzr branch . -r 1 ../branch
 
784
$ bzr add -q dir/file2
 
785
$ bzr commit -q -m 'Add dir/file2 in branch'
 
786
$ bzr branch -q . -r 1 ../branch
362
787
$ cd ../branch
363
 
$ bzr rm dir/file --force
364
 
$ bzr rm dir
365
 
$ bzr commit -m 'Remove dir/file'
366
 
 
 
788
$ bzr rm -q dir/file --no-backup
 
789
$ bzr rm -q dir
 
790
$ bzr commit -q -m 'Remove dir/file'
367
791
$ bzr merge ../trunk
368
792
2>+N  dir/
369
793
2>+N  dir/file2
375
799
    def test_keep_them_all(self):
376
800
        self.run_script("""
377
801
$ bzr resolve dir
378
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
802
2>2 conflicts resolved, 0 remaining
 
803
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
379
804
""")
380
805
 
381
806
    def test_adopt_child(self):
382
807
        self.run_script("""
383
 
$ bzr mv dir/file2 file2
384
 
$ bzr rm dir --force
 
808
$ bzr mv -q dir/file2 file2
 
809
$ bzr rm -q dir --no-backup
385
810
$ bzr resolve dir
386
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
811
2>2 conflicts resolved, 0 remaining
 
812
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
387
813
""")
388
814
 
389
815
    def test_kill_them_all(self):
390
816
        self.run_script("""
391
 
$ bzr rm dir --force
 
817
$ bzr rm -q dir --no-backup
392
818
$ bzr resolve dir
393
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
819
2>2 conflicts resolved, 0 remaining
 
820
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
394
821
""")
395
822
 
396
823
    def test_resolve_taking_this(self):
397
824
        self.run_script("""
398
825
$ bzr resolve --take-this dir
399
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
826
2>...
 
827
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
400
828
""")
401
829
 
402
830
    def test_resolve_taking_other(self):
403
831
        self.run_script("""
404
832
$ bzr resolve --take-other dir
405
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
833
2>...
 
834
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
406
835
""")
407
836
 
408
837
 
410
839
 
411
840
    preamble = """
412
841
$ bzr init trunk
 
842
...
413
843
$ cd trunk
414
844
$ mkdir dir
415
845
$ echo 'trunk content' >dir/file
416
 
$ bzr add
417
 
$ bzr commit -m 'Create trunk'
418
 
$ bzr rm dir/file --force
419
 
$ bzr rm dir --force
420
 
$ bzr commit -m 'Remove dir/file'
421
 
 
422
 
$ bzr branch . -r 1 ../branch
 
846
$ bzr add -q
 
847
$ bzr commit -m 'Create trunk' -q
 
848
$ bzr rm -q dir/file --no-backup
 
849
$ bzr rm -q dir --no-backup
 
850
$ bzr commit -q -m 'Remove dir/file'
 
851
$ bzr branch -q . -r 1 ../branch
423
852
$ cd ../branch
424
853
$ echo 'branch content' >dir/file2
425
 
$ bzr add dir/file2
426
 
$ bzr commit -m 'Add dir/file2 in branch'
427
 
 
 
854
$ bzr add -q dir/file2
 
855
$ bzr commit -q -m 'Add dir/file2 in branch'
428
856
$ bzr merge ../trunk
429
857
2>-D  dir/file
430
858
2>Conflict: can't delete dir because it is not empty.  Not deleting.
435
863
    def test_keep_them_all(self):
436
864
        self.run_script("""
437
865
$ bzr resolve dir
438
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
866
2>2 conflicts resolved, 0 remaining
 
867
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
439
868
""")
440
869
 
441
870
    def test_adopt_child(self):
442
871
        self.run_script("""
443
 
$ bzr mv dir/file2 file2
444
 
$ bzr rm dir --force
 
872
$ bzr mv -q dir/file2 file2
 
873
$ bzr rm -q dir --no-backup
445
874
$ bzr resolve dir
446
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
875
2>2 conflicts resolved, 0 remaining
 
876
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
447
877
""")
448
878
 
449
879
    def test_kill_them_all(self):
450
880
        self.run_script("""
451
 
$ bzr rm dir --force
 
881
$ bzr rm -q dir --no-backup
452
882
$ bzr resolve dir
453
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
883
2>2 conflicts resolved, 0 remaining
 
884
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
454
885
""")
455
886
 
456
887
    def test_resolve_taking_this(self):
457
888
        self.run_script("""
458
889
$ bzr resolve --take-this dir
459
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
890
2>2 conflicts resolved, 0 remaining
 
891
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
460
892
""")
461
893
 
462
894
    def test_resolve_taking_other(self):
463
895
        self.run_script("""
464
896
$ bzr resolve --take-other dir
465
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
466
 
""")
467
 
 
468
 
 
469
 
class TestResolvePathConflict(TestResolveConflicts):
470
 
 
471
 
    preamble = """
472
 
$ bzr init trunk
473
 
$ cd trunk
474
 
$ echo 'Boo!' >file
475
 
$ bzr add
476
 
$ bzr commit -m 'Create trunk'
477
 
$ bzr mv file file-in-trunk
478
 
$ bzr commit -m 'Renamed to file-in-trunk'
479
 
 
480
 
$ bzr branch . -r 1 ../branch
481
 
$ cd ../branch
482
 
$ bzr mv file file-in-branch
483
 
$ bzr commit -m 'Renamed to file-in-branch'
484
 
 
485
 
$ bzr merge ../trunk
486
 
2>R   file-in-branch => file-in-trunk
487
 
2>Path conflict: file-in-branch / file-in-trunk
488
 
2>1 conflicts encountered.
489
 
"""
490
 
 
491
 
    def test_keep_source(self):
492
 
        self.run_script("""
493
 
$ bzr resolve file-in-trunk
494
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
495
 
""")
496
 
 
497
 
    def test_keep_target(self):
498
 
        self.run_script("""
499
 
$ bzr mv file-in-trunk file-in-branch
500
 
$ bzr resolve file-in-branch
501
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
502
 
""")
503
 
 
504
 
    def test_resolve_taking_this(self):
505
 
        self.run_script("""
506
 
$ bzr resolve --take-this file-in-branch
507
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
508
 
""")
509
 
 
510
 
    def test_resolve_taking_other(self):
511
 
        self.run_script("""
512
 
$ bzr resolve --take-other file-in-branch
513
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
514
 
""")
515
 
 
516
 
 
517
 
class TestResolveParentLoop(TestResolveConflicts):
518
 
 
519
 
    preamble = """
520
 
$ bzr init trunk
521
 
$ cd trunk
522
 
$ bzr mkdir dir1
523
 
$ bzr mkdir dir2
524
 
$ bzr commit -m 'Create trunk'
525
 
$ bzr mv dir2 dir1
526
 
$ bzr commit -m 'Moved dir2 into dir1'
527
 
 
528
 
$ bzr branch . -r 1 ../branch
529
 
$ cd ../branch
530
 
$ bzr mv dir1 dir2
531
 
$ bzr commit -m 'Moved dir1 into dir2'
532
 
 
533
 
$ bzr merge ../trunk
534
 
2>Conflict moving dir2/dir1 into dir2.  Cancelled move.
535
 
2>1 conflicts encountered.
536
 
"""
537
 
 
538
 
    def test_take_this(self):
539
 
        self.run_script("""
540
 
$ bzr resolve dir2
541
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
542
 
""")
543
 
 
544
 
    def test_take_other(self):
545
 
        self.run_script("""
546
 
$ bzr mv dir2/dir1 dir1
547
 
$ bzr mv dir2 dir1
548
 
$ bzr resolve dir2
549
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
550
 
""")
551
 
 
552
 
    def test_resolve_taking_this(self):
553
 
        self.run_script("""
554
 
$ bzr resolve --take-this dir2
555
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
556
 
""")
557
 
        self.failUnlessExists('dir2')
558
 
 
559
 
    def test_resolve_taking_other(self):
560
 
        self.run_script("""
561
 
$ bzr resolve --take-other dir2
562
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
563
 
""")
564
 
        self.failUnlessExists('dir1')
 
897
2>deleted dir/file2
 
898
2>deleted dir
 
899
2>2 conflicts resolved, 0 remaining
 
900
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
901
""")
 
902
 
 
903
 
 
904
class TestResolveParentLoop(TestParametrizedResolveConflicts):
 
905
 
 
906
    _conflict_type = conflicts.ParentLoop
 
907
 
 
908
    _this_args = None
 
909
    _other_args = None
 
910
 
 
911
    # Each side dict additionally defines:
 
912
    # - dir_id: the directory being moved
 
913
    # - target_id: The target directory
 
914
    # - xfail: whether the test is expected to fail if the action is
 
915
    #   involved as 'other'
 
916
    scenarios = mirror_scenarios(
 
917
        [
 
918
            # Dirs moved into each other
 
919
            (dict(_base_actions='create_dir1_dir2'),
 
920
             ('dir1_into_dir2',
 
921
              dict(actions='move_dir1_into_dir2', check='dir1_moved',
 
922
                   dir_id='dir1-id', target_id='dir2-id', xfail=False)),
 
923
             ('dir2_into_dir1',
 
924
              dict(actions='move_dir2_into_dir1', check='dir2_moved',
 
925
                   dir_id='dir2-id', target_id='dir1-id', xfail=False))),
 
926
            # Subdirs moved into each other
 
927
            (dict(_base_actions='create_dir1_4'),
 
928
             ('dir1_into_dir4',
 
929
              dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
 
930
                   dir_id='dir1-id', target_id='dir4-id', xfail=True)),
 
931
             ('dir3_into_dir2',
 
932
              dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
 
933
                   dir_id='dir3-id', target_id='dir2-id', xfail=True))),
 
934
            ])
 
935
 
 
936
    def do_create_dir1_dir2(self):
 
937
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
 
938
                ('add', ('dir2', 'dir2-id', 'directory', '')),]
 
939
 
 
940
    def do_move_dir1_into_dir2(self):
 
941
        return [('rename', ('dir1', 'dir2/dir1'))]
 
942
 
 
943
    def check_dir1_moved(self):
 
944
        self.assertPathDoesNotExist('branch/dir1')
 
945
        self.assertPathExists('branch/dir2/dir1')
 
946
 
 
947
    def do_move_dir2_into_dir1(self):
 
948
        return [('rename', ('dir2', 'dir1/dir2'))]
 
949
 
 
950
    def check_dir2_moved(self):
 
951
        self.assertPathDoesNotExist('branch/dir2')
 
952
        self.assertPathExists('branch/dir1/dir2')
 
953
 
 
954
    def do_create_dir1_4(self):
 
955
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
 
956
                ('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
 
957
                ('add', ('dir3', 'dir3-id', 'directory', '')),
 
958
                ('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
 
959
 
 
960
    def do_move_dir1_into_dir4(self):
 
961
        return [('rename', ('dir1', 'dir3/dir4/dir1'))]
 
962
 
 
963
    def check_dir1_2_moved(self):
 
964
        self.assertPathDoesNotExist('branch/dir1')
 
965
        self.assertPathExists('branch/dir3/dir4/dir1')
 
966
        self.assertPathExists('branch/dir3/dir4/dir1/dir2')
 
967
 
 
968
    def do_move_dir3_into_dir2(self):
 
969
        return [('rename', ('dir3', 'dir1/dir2/dir3'))]
 
970
 
 
971
    def check_dir3_4_moved(self):
 
972
        self.assertPathDoesNotExist('branch/dir3')
 
973
        self.assertPathExists('branch/dir1/dir2/dir3')
 
974
        self.assertPathExists('branch/dir1/dir2/dir3/dir4')
 
975
 
 
976
    def _get_resolve_path_arg(self, wt, action):
 
977
        # ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
 
978
        # But since <path> doesn't exist in the working tree, we need to use
 
979
        # <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
 
980
        return wt.id2path(self._other['dir_id'])
 
981
 
 
982
    def assertParentLoop(self, wt, c):
 
983
        self.assertEqual(self._other['dir_id'], c.file_id)
 
984
        self.assertEqual(self._other['target_id'], c.conflict_file_id)
 
985
        # The conflict paths are irrelevant (they are deterministic but not
 
986
        # worth checking since they don't provide the needed information
 
987
        # anyway)
 
988
        if self._other['xfail']:
 
989
            # It's a bit hackish to raise from here relying on being called for
 
990
            # both tests but this avoid overriding test_resolve_taking_other
 
991
            self.knownFailure(
 
992
                "ParentLoop doesn't carry enough info to resolve --take-other")
 
993
    _assert_conflict = assertParentLoop
565
994
 
566
995
 
567
996
class TestResolveNonDirectoryParent(TestResolveConflicts):
568
997
 
569
998
    preamble = """
570
999
$ bzr init trunk
 
1000
...
571
1001
$ cd trunk
572
1002
$ bzr mkdir foo
573
 
$ bzr commit -m 'Create trunk'
 
1003
...
 
1004
$ bzr commit -m 'Create trunk' -q
574
1005
$ echo "Boing" >foo/bar
575
 
$ bzr add foo/bar
576
 
$ bzr commit -m 'Add foo/bar'
577
 
 
578
 
$ bzr branch . -r 1 ../branch
 
1006
$ bzr add -q foo/bar
 
1007
$ bzr commit -q -m 'Add foo/bar'
 
1008
$ bzr branch -q . -r 1 ../branch
579
1009
$ cd ../branch
580
1010
$ rm -r foo
581
1011
$ echo "Boo!" >foo
582
 
$ bzr commit -m 'foo is now a file'
583
 
 
 
1012
$ bzr commit -q -m 'foo is now a file'
584
1013
$ bzr merge ../trunk
585
1014
2>+N  foo.new/bar
586
1015
2>RK  foo => foo.new/
592
1021
 
593
1022
    def test_take_this(self):
594
1023
        self.run_script("""
595
 
$ bzr rm foo.new --force
 
1024
$ bzr rm -q foo.new --no-backup
596
1025
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
597
1026
# aside ? -- vila 090916
598
 
$ bzr add foo
 
1027
$ bzr add -q foo
599
1028
$ bzr resolve foo.new
600
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
1029
2>1 conflict resolved, 0 remaining
 
1030
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
601
1031
""")
602
1032
 
603
1033
    def test_take_other(self):
604
1034
        self.run_script("""
605
 
$ bzr rm foo --force
606
 
$ bzr mv foo.new foo
 
1035
$ bzr rm -q foo --no-backup
 
1036
$ bzr mv -q foo.new foo
607
1037
$ bzr resolve foo
608
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
1038
2>1 conflict resolved, 0 remaining
 
1039
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
609
1040
""")
610
1041
 
611
1042
    def test_resolve_taking_this(self):
612
1043
        self.run_script("""
613
1044
$ bzr resolve --take-this foo.new
614
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
1045
2>...
 
1046
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
615
1047
""")
616
1048
 
617
1049
    def test_resolve_taking_other(self):
618
1050
        self.run_script("""
619
1051
$ bzr resolve --take-other foo.new
620
 
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
1052
2>...
 
1053
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
621
1054
""")
622
1055
 
623
1056
 
627
1060
        # This is nearly like TestResolveNonDirectoryParent but with branch and
628
1061
        # trunk switched. As such it should certainly produce the same
629
1062
        # conflict.
630
 
        self.run_script("""
 
1063
        self.assertRaises(errors.MalformedTransform,
 
1064
                          self.run_script,"""
631
1065
$ bzr init trunk
 
1066
...
632
1067
$ cd trunk
633
1068
$ bzr mkdir foo
634
 
$ bzr commit -m 'Create trunk'
 
1069
...
 
1070
$ bzr commit -m 'Create trunk' -q
635
1071
$ rm -r foo
636
1072
$ echo "Boo!" >foo
637
 
$ bzr commit -m 'foo is now a file'
638
 
 
639
 
$ bzr branch . -r 1 ../branch
 
1073
$ bzr commit -m 'foo is now a file' -q
 
1074
$ bzr branch -q . -r 1 ../branch -q
640
1075
$ cd ../branch
641
1076
$ echo "Boing" >foo/bar
642
 
$ bzr add foo/bar
643
 
$ bzr commit -m 'Add foo/bar'
644
 
 
 
1077
$ bzr add -q foo/bar -q
 
1078
$ bzr commit -m 'Add foo/bar' -q
645
1079
$ bzr merge ../trunk
646
1080
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
647
1081
""")
648
1082
 
649
1083
 
 
1084
class TestNoFinalPath(script.TestCaseWithTransportAndScript):
 
1085
 
 
1086
    def test_bug_805809(self):
 
1087
        self.run_script("""
 
1088
$ bzr init trunk
 
1089
Created a standalone tree (format: 2a)
 
1090
$ cd trunk
 
1091
$ echo trunk >file
 
1092
$ bzr add
 
1093
adding file
 
1094
$ bzr commit -m 'create file on trunk'
 
1095
2>Committing to: .../trunk/
 
1096
2>added file
 
1097
2>Committed revision 1.
 
1098
# Create a debian branch based on trunk
 
1099
$ cd ..
 
1100
$ bzr branch trunk -r 1 debian
 
1101
2>Branched 1 revision.
 
1102
$ cd debian
 
1103
$ mkdir dir
 
1104
$ bzr add
 
1105
adding dir
 
1106
$ bzr mv file dir
 
1107
file => dir/file
 
1108
$ bzr commit -m 'rename file to dir/file for debian'
 
1109
2>Committing to: .../debian/
 
1110
2>added dir
 
1111
2>renamed file => dir/file
 
1112
2>Committed revision 2.
 
1113
# Create an experimental branch with a new root-id
 
1114
$ cd ..
 
1115
$ bzr init experimental
 
1116
Created a standalone tree (format: 2a)
 
1117
$ cd experimental
 
1118
# Work around merging into empty branch not being supported
 
1119
# (http://pad.lv/308562)
 
1120
$ echo something >not-empty
 
1121
$ bzr add
 
1122
adding not-empty
 
1123
$ bzr commit -m 'Add some content in experimental'
 
1124
2>Committing to: .../experimental/
 
1125
2>added not-empty
 
1126
2>Committed revision 1.
 
1127
# merge debian even without a common ancestor
 
1128
$ bzr merge ../debian -r0..2
 
1129
2>+N  dir/
 
1130
2>+N  dir/file
 
1131
2>All changes applied successfully.
 
1132
$ bzr commit -m 'merging debian into experimental'
 
1133
2>Committing to: .../experimental/
 
1134
2>added dir
 
1135
2>added dir/file
 
1136
2>Committed revision 2.
 
1137
# Create an ubuntu branch with yet another root-id
 
1138
$ cd ..
 
1139
$ bzr init ubuntu
 
1140
Created a standalone tree (format: 2a)
 
1141
$ cd ubuntu
 
1142
# Work around merging into empty branch not being supported
 
1143
# (http://pad.lv/308562)
 
1144
$ echo something >not-empty-ubuntu
 
1145
$ bzr add
 
1146
adding not-empty-ubuntu
 
1147
$ bzr commit -m 'Add some content in experimental'
 
1148
2>Committing to: .../ubuntu/
 
1149
2>added not-empty-ubuntu
 
1150
2>Committed revision 1.
 
1151
# Also merge debian
 
1152
$ bzr merge ../debian -r0..2
 
1153
2>+N  dir/
 
1154
2>+N  dir/file
 
1155
2>All changes applied successfully.
 
1156
$ bzr commit -m 'merging debian'
 
1157
2>Committing to: .../ubuntu/
 
1158
2>added dir
 
1159
2>added dir/file
 
1160
2>Committed revision 2.
 
1161
# Now try to merge experimental
 
1162
$ bzr merge ../experimental
 
1163
2>+N  not-empty
 
1164
2>Path conflict: dir / dir
 
1165
2>1 conflicts encountered.
 
1166
""")
 
1167
 
 
1168
 
650
1169
class TestResolveActionOption(tests.TestCase):
651
1170
 
652
1171
    def setUp(self):