~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Jelmer Vernooij
  • Date: 2010-04-30 11:03:59 UTC
  • mto: This revision was merged to the branch mainline in revision 5197.
  • Revision ID: jelmer@samba.org-20100430110359-ow3e3grh7sxy93pa
Remove more unused imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
    branchbuilder,
21
22
    bzrdir,
22
23
    conflicts,
23
24
    errors,
24
25
    option,
25
 
    osutils,
26
26
    tests,
27
 
    )
28
 
from bzrlib.tests import (
29
 
    script,
30
 
    scenarios,
31
 
    )
32
 
 
33
 
 
34
 
load_tests = scenarios.load_tests_apply_scenarios
 
27
    workingtree,
 
28
    )
 
29
from bzrlib.tests import script
 
30
 
 
31
 
 
32
def load_tests(standard_tests, module, loader):
 
33
    result = loader.suiteClass()
 
34
 
 
35
    sp_tests, remaining_tests = tests.split_suite_by_condition(
 
36
        standard_tests, tests.condition_isinstance((
 
37
                TestParametrizedResolveConflicts,
 
38
                )))
 
39
    # Each test class defines its own scenarios. This is needed for
 
40
    # TestResolvePathConflictBefore531967 that verifies that the same tests as
 
41
    # TestResolvePathConflict still pass.
 
42
    for test in tests.iter_suite_tests(sp_tests):
 
43
        tests.apply_scenarios(test, test.scenarios(), result)
 
44
 
 
45
    # No parametrization for the remaining tests
 
46
    result.addTests(remaining_tests)
 
47
 
 
48
    return result
35
49
 
36
50
 
37
51
# TODO: Test commit with some added, and added-but-missing files
64
78
 
65
79
class TestConflicts(tests.TestCaseWithTransport):
66
80
 
 
81
    def test_conflicts(self):
 
82
        """Conflicts are detected properly"""
 
83
        # Use BzrDirFormat6 so we can fake conflicts
 
84
        tree = self.make_branch_and_tree('.', format=bzrdir.BzrDirFormat6())
 
85
        self.build_tree_contents([('hello', 'hello world4'),
 
86
                                  ('hello.THIS', 'hello world2'),
 
87
                                  ('hello.BASE', 'hello world1'),
 
88
                                  ('hello.OTHER', 'hello world3'),
 
89
                                  ('hello.sploo.BASE', 'yellowworld'),
 
90
                                  ('hello.sploo.OTHER', 'yellowworld2'),
 
91
                                  ])
 
92
        tree.lock_read()
 
93
        self.assertLength(6, list(tree.list_files()))
 
94
        tree.unlock()
 
95
        tree_conflicts = tree.conflicts()
 
96
        self.assertLength(2, tree_conflicts)
 
97
        self.assertTrue('hello' in tree_conflicts[0].path)
 
98
        self.assertTrue('hello.sploo' in tree_conflicts[1].path)
 
99
        conflicts.restore('hello')
 
100
        conflicts.restore('hello.sploo')
 
101
        self.assertLength(0, tree.conflicts())
 
102
        self.assertFileEqual('hello world2', 'hello')
 
103
        self.assertFalse(os.path.lexists('hello.sploo'))
 
104
        self.assertRaises(errors.NotConflicted, conflicts.restore, 'hello')
 
105
        self.assertRaises(errors.NotConflicted,
 
106
                          conflicts.restore, 'hello.sploo')
 
107
 
67
108
    def test_resolve_conflict_dir(self):
68
109
        tree = self.make_branch_and_tree('.')
69
110
        self.build_tree_contents([('hello', 'hello world4'),
169
210
        self.run_script(self.preamble)
170
211
 
171
212
 
172
 
def mirror_scenarios(base_scenarios):
173
 
    """Return a list of mirrored scenarios.
174
 
 
175
 
    Each scenario in base_scenarios is duplicated switching the roles of 'this'
176
 
    and 'other'
177
 
    """
178
 
    scenarios = []
179
 
    for common, (lname, ldict), (rname, rdict) in base_scenarios:
180
 
        a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
181
 
                                     [(rname, dict(_other=rdict))])
182
 
        b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
183
 
                                     [(lname, dict(_other=ldict))])
184
 
        # Inject the common parameters in all scenarios
185
 
        for name, d in a + b:
186
 
            d.update(common)
187
 
        scenarios.extend(a + b)
188
 
    return scenarios
 
213
class TestResolveTextConflicts(TestResolveConflicts):
 
214
    # TBC
 
215
    pass
189
216
 
190
217
 
191
218
# FIXME: Get rid of parametrized (in the class name) once we delete
193
220
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
194
221
    """This class provides a base to test single conflict resolution.
195
222
 
196
 
    Since all conflict objects are created with specific semantics for their
197
 
    attributes, each class should implement the necessary functions and
198
 
    attributes described below.
199
 
 
200
 
    Each class should define the scenarios that create the expected (single)
201
 
    conflict.
202
 
 
203
 
    Each scenario describes:
204
 
    * how to create 'base' tree (and revision)
205
 
    * how to create 'left' tree (and revision, parent rev 'base')
206
 
    * how to create 'right' tree (and revision, parent rev 'base')
207
 
    * how to check that changes in 'base'->'left' have been taken
208
 
    * how to check that changes in 'base'->'right' have been taken
209
 
 
210
 
    From each base scenario, we generate two concrete scenarios where:
211
 
    * this=left, other=right
212
 
    * this=right, other=left
213
 
 
214
 
    Then the test case verifies each concrete scenario by:
215
 
    * creating a branch containing the 'base', 'this' and 'other' revisions
216
 
    * creating a working tree for the 'this' revision
217
 
    * performing the merge of 'other' into 'this'
218
 
    * verifying the expected conflict was generated
219
 
    * resolving with --take-this or --take-other, and running the corresponding
220
 
      checks (for either 'base'->'this', or 'base'->'other')
221
 
 
222
 
    :cvar _conflict_type: The expected class of the generated conflict.
223
 
 
224
 
    :cvar _assert_conflict: A method receiving the working tree and the
225
 
        conflict object and checking its attributes.
226
 
 
227
 
    :cvar _base_actions: The branchbuilder actions to create the 'base'
228
 
        revision.
229
 
 
230
 
    :cvar _this: The dict related to 'base' -> 'this'. It contains at least:
231
 
      * 'actions': The branchbuilder actions to create the 'this'
232
 
          revision.
233
 
      * 'check': how to check the changes after resolution with --take-this.
234
 
 
235
 
    :cvar _other: The dict related to 'base' -> 'other'. It contains at least:
236
 
      * 'actions': The branchbuilder actions to create the 'other'
237
 
          revision.
238
 
      * 'check': how to check the changes after resolution with --take-other.
 
223
    The aim is to define scenarios in daughter classes (one for each conflict
 
224
    type) that create a single conflict object when one branch is merged in
 
225
    another (and vice versa). Each class can define as many scenarios as
 
226
    needed. Each scenario should define a couple of actions that will be
 
227
    swapped to define the sibling scenarios.
 
228
 
 
229
    From there, both resolutions are tested (--take-this and --take-other).
 
230
 
 
231
    Each conflict type use its attributes in a specific way, so each class 
 
232
    should define a specific _assert_conflict method.
 
233
 
 
234
    Since the resolution change the working tree state, each action should
 
235
    define an associated check.
239
236
    """
240
237
 
241
238
    # Set by daughter classes
244
241
 
245
242
    # Set by load_tests
246
243
    _base_actions = None
247
 
    _this = None
248
 
    _other = None
249
 
 
250
 
    scenarios = []
251
 
    """The scenario list for the conflict type defined by the class.
252
 
 
253
 
    Each scenario is of the form:
254
 
    (common, (left_name, left_dict), (right_name, right_dict))
255
 
 
256
 
    * common is a dict
257
 
 
258
 
    * left_name and right_name are the scenario names that will be combined
259
 
 
260
 
    * left_dict and right_dict are the attributes specific to each half of
261
 
      the scenario. They should include at least 'actions' and 'check' and
262
 
      will be available as '_this' and '_other' test instance attributes.
263
 
 
264
 
    Daughters classes are free to add their specific attributes as they see
265
 
    fit in any of the three dicts.
266
 
 
267
 
    This is a class method so that load_tests can find it.
268
 
 
269
 
    '_base_actions' in the common dict, 'actions' and 'check' in the left
270
 
    and right dicts use names that map to methods in the test classes. Some
271
 
    prefixes are added to these names to get the correspong methods (see
272
 
    _get_actions() and _get_check()). The motivation here is to avoid
273
 
    collisions in the class namespace.
274
 
    """
 
244
    _this_actions = None
 
245
    _other_actions = None
 
246
    _item_path = None
 
247
    _item_id = None
 
248
 
 
249
    # Set by _this_actions and other_actions
 
250
    # FIXME: rename them this_args and other_args so the tests can use them
 
251
    # more freely
 
252
    _this_path = None
 
253
    _this_id = None
 
254
    _other_path = None
 
255
    _other_id = None
 
256
 
 
257
    @classmethod
 
258
    def mirror_scenarios(klass, base_scenarios):
 
259
        scenarios = []
 
260
        def adapt(d, side):
 
261
            """Modify dict to apply to the given side.
 
262
 
 
263
            'actions' key is turned into '_actions_this' if side is 'this' for
 
264
            example.
 
265
            """
 
266
            t = {}
 
267
            # Turn each key into _side_key
 
268
            for k,v in d.iteritems():
 
269
                t['_%s_%s' % (k, side)] = v
 
270
            return t
 
271
        # Each base scenario is duplicated switching the roles of 'this' and
 
272
        # 'other'
 
273
        left = [l for l, r, c in base_scenarios]
 
274
        right = [r for l, r, c in base_scenarios]
 
275
        common = [c for l, r, c in base_scenarios]
 
276
        for (lname, ldict), (rname, rdict), common in zip(left, right, common):
 
277
            a = tests.multiply_scenarios([(lname, adapt(ldict, 'this'))],
 
278
                                         [(rname, adapt(rdict, 'other'))])
 
279
            b = tests.multiply_scenarios(
 
280
                    [(rname, adapt(rdict, 'this'))],
 
281
                    [(lname, adapt(ldict, 'other'))])
 
282
            # Inject the common parameters in all scenarios
 
283
            for name, d in a + b:
 
284
                d.update(common)
 
285
            scenarios.extend(a + b)
 
286
        return scenarios
 
287
 
 
288
    @classmethod
 
289
    def scenarios(klass):
 
290
        # Only concrete classes return actual scenarios
 
291
        return []
275
292
 
276
293
    def setUp(self):
277
294
        super(TestParametrizedResolveConflicts, self).setUp()
282
299
        builder.build_snapshot('start', None, [
283
300
                ('add', ('', 'root-id', 'directory', ''))])
284
301
        # Add a minimal base content
285
 
        base_actions = self._get_actions(self._base_actions)()
286
 
        builder.build_snapshot('base', ['start'], base_actions)
 
302
        _, _, actions_base = self._get_actions(self._actions_base)()
 
303
        builder.build_snapshot('base', ['start'], actions_base)
287
304
        # Modify the base content in branch
288
 
        actions_other = self._get_actions(self._other['actions'])()
 
305
        (self._other_path, self._other_id,
 
306
         actions_other) = self._get_actions(self._actions_other)()
289
307
        builder.build_snapshot('other', ['base'], actions_other)
290
308
        # Modify the base content in trunk
291
 
        actions_this = self._get_actions(self._this['actions'])()
 
309
        (self._this_path, self._this_id,
 
310
         actions_this) = self._get_actions(self._actions_this)()
292
311
        builder.build_snapshot('this', ['base'], actions_this)
293
312
        # builder.get_branch() tip is now 'this'
294
313
 
301
320
    def _get_check(self, name):
302
321
        return getattr(self, 'check_%s' % name)
303
322
 
 
323
    def do_nothing(self):
 
324
        return (None, None, [])
 
325
 
 
326
    def do_create_file(self):
 
327
        return ('file', 'file-id',
 
328
                [('add', ('file', 'file-id', 'file', 'trunk content\n'))])
 
329
 
 
330
    def do_create_file_a(self):
 
331
        return ('file', 'file-a-id',
 
332
                [('add', ('file', 'file-a-id', 'file', 'file a content\n'))])
 
333
 
 
334
    def check_file_content_a(self):
 
335
        self.assertFileEqual('file a content\n', 'branch/file')
 
336
 
 
337
    def do_create_file_b(self):
 
338
        return ('file', 'file-b-id',
 
339
                [('add', ('file', 'file-b-id', 'file', 'file b content\n'))])
 
340
 
 
341
    def check_file_content_b(self):
 
342
        self.assertFileEqual('file b content\n', 'branch/file')
 
343
 
 
344
    def do_create_dir(self):
 
345
        return ('dir', 'dir-id', [('add', ('dir', 'dir-id', 'directory', ''))])
 
346
 
 
347
    def do_modify_file(self):
 
348
        return ('file', 'file-id',
 
349
                [('modify', ('file-id', 'trunk content\nmore content\n'))])
 
350
 
 
351
    def check_file_has_more_content(self):
 
352
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
 
353
 
 
354
    def do_delete_file(self):
 
355
        return ('file', 'file-id', [('unversion', 'file-id')])
 
356
 
 
357
    def check_file_doesnt_exist(self):
 
358
        self.failIfExists('branch/file')
 
359
 
 
360
    def do_rename_file(self):
 
361
        return ('new-file', 'file-id', [('rename', ('file', 'new-file'))])
 
362
 
 
363
    def check_file_renamed(self):
 
364
        self.failIfExists('branch/file')
 
365
        self.failUnlessExists('branch/new-file')
 
366
 
 
367
    def do_rename_file2(self):
 
368
        return ('new-file2', 'file-id', [('rename', ('file', 'new-file2'))])
 
369
 
 
370
    def check_file_renamed2(self):
 
371
        self.failIfExists('branch/file')
 
372
        self.failUnlessExists('branch/new-file2')
 
373
 
 
374
    def do_rename_dir(self):
 
375
        return ('new-dir', 'dir-id', [('rename', ('dir', 'new-dir'))])
 
376
 
 
377
    def check_dir_renamed(self):
 
378
        self.failIfExists('branch/dir')
 
379
        self.failUnlessExists('branch/new-dir')
 
380
 
 
381
    def do_rename_dir2(self):
 
382
        return ('new-dir2', 'dir-id', [('rename', ('dir', 'new-dir2'))])
 
383
 
 
384
    def check_dir_renamed2(self):
 
385
        self.failIfExists('branch/dir')
 
386
        self.failUnlessExists('branch/new-dir2')
 
387
 
 
388
    def do_delete_dir(self):
 
389
        return ('<deleted>', 'dir-id', [('unversion', 'dir-id')])
 
390
 
 
391
    def check_dir_doesnt_exist(self):
 
392
        self.failIfExists('branch/dir')
 
393
 
304
394
    def _merge_other_into_this(self):
305
395
        b = self.builder.get_branch()
306
396
        wt = b.bzrdir.sprout('branch').open_workingtree()
315
405
        self._assert_conflict(wt, c)
316
406
 
317
407
    def _get_resolve_path_arg(self, wt, action):
318
 
        raise NotImplementedError(self._get_resolve_path_arg)
 
408
        return self._item_path
319
409
 
320
410
    def check_resolved(self, wt, action):
321
411
        path = self._get_resolve_path_arg(wt, action)
328
418
        wt = self._merge_other_into_this()
329
419
        self.assertConflict(wt)
330
420
        self.check_resolved(wt, 'take_this')
331
 
        check_this = self._get_check(self._this['check'])
 
421
        check_this = self._get_check(self._check_this)
332
422
        check_this()
333
423
 
334
424
    def test_resolve_taking_other(self):
335
425
        wt = self._merge_other_into_this()
336
426
        self.assertConflict(wt)
337
427
        self.check_resolved(wt, 'take_other')
338
 
        check_other = self._get_check(self._other['check'])
 
428
        check_other = self._get_check(self._check_other)
339
429
        check_other()
340
430
 
341
431
 
342
 
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
343
 
 
344
 
    _conflict_type = conflicts.TextConflict
345
 
 
346
 
    # Set by the scenarios
347
 
    # path and file-id for the file involved in the conflict
348
 
    _path = None
349
 
    _file_id = None
350
 
 
351
 
    scenarios = mirror_scenarios(
352
 
        [
353
 
            # File modified on both sides
354
 
            (dict(_base_actions='create_file',
355
 
                  _path='file', _file_id='file-id'),
356
 
             ('filed_modified_A',
357
 
              dict(actions='modify_file_A', check='file_has_content_A')),
358
 
             ('file_modified_B',
359
 
              dict(actions='modify_file_B', check='file_has_content_B')),),
360
 
            # File modified on both sides in dir
361
 
            (dict(_base_actions='create_file_in_dir',
362
 
                  _path='dir/file', _file_id='file-id'),
363
 
             ('filed_modified_A_in_dir',
364
 
              dict(actions='modify_file_A',
365
 
                   check='file_in_dir_has_content_A')),
366
 
             ('file_modified_B',
367
 
              dict(actions='modify_file_B',
368
 
                   check='file_in_dir_has_content_B')),),
369
 
            ])
370
 
 
371
 
    def do_create_file(self, path='file'):
372
 
        return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
373
 
 
374
 
    def do_modify_file_A(self):
375
 
        return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
376
 
 
377
 
    def do_modify_file_B(self):
378
 
        return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
379
 
 
380
 
    def check_file_has_content_A(self, path='file'):
381
 
        self.assertFileEqual('trunk content\nfeature A\n',
382
 
                             osutils.pathjoin('branch', path))
383
 
 
384
 
    def check_file_has_content_B(self, path='file'):
385
 
        self.assertFileEqual('trunk content\nfeature B\n',
386
 
                             osutils.pathjoin('branch', path))
387
 
 
388
 
    def do_create_file_in_dir(self):
389
 
        return [('add', ('dir', 'dir-id', 'directory', '')),
390
 
            ] + self.do_create_file('dir/file')
391
 
 
392
 
    def check_file_in_dir_has_content_A(self):
393
 
        self.check_file_has_content_A('dir/file')
394
 
 
395
 
    def check_file_in_dir_has_content_B(self):
396
 
        self.check_file_has_content_B('dir/file')
397
 
 
398
 
    def _get_resolve_path_arg(self, wt, action):
399
 
        return self._path
400
 
 
401
 
    def assertTextConflict(self, wt, c):
402
 
        self.assertEqual(self._file_id, c.file_id)
403
 
        self.assertEqual(self._path, c.path)
404
 
    _assert_conflict = assertTextConflict
405
 
 
406
 
 
407
432
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
408
433
 
409
 
    _conflict_type = conflicts.ContentsConflict
410
 
 
411
 
    # Set by the scenarios
412
 
    # path and file-id for the file involved in the conflict
413
 
    _path = None
414
 
    _file_id = None
415
 
 
416
 
    scenarios = mirror_scenarios(
417
 
        [
418
 
            # File modified/deleted
419
 
            (dict(_base_actions='create_file',
420
 
                  _path='file', _file_id='file-id'),
421
 
             ('file_modified',
422
 
              dict(actions='modify_file', check='file_has_more_content')),
423
 
             ('file_deleted',
424
 
              dict(actions='delete_file', check='file_doesnt_exist')),),
425
 
            # File modified/deleted in dir
426
 
            (dict(_base_actions='create_file_in_dir',
427
 
                  _path='dir/file', _file_id='file-id'),
428
 
             ('file_modified_in_dir',
429
 
              dict(actions='modify_file_in_dir',
430
 
                   check='file_in_dir_has_more_content')),
431
 
             ('file_deleted_in_dir',
432
 
              dict(actions='delete_file',
433
 
                   check='file_in_dir_doesnt_exist')),),
434
 
            ])
435
 
 
436
 
    def do_create_file(self):
437
 
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
438
 
 
439
 
    def do_modify_file(self):
440
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
441
 
 
442
 
    def check_file_has_more_content(self):
443
 
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
444
 
 
445
 
    def do_delete_file(self):
446
 
        return [('unversion', 'file-id')]
447
 
 
448
 
    def check_file_doesnt_exist(self):
449
 
        self.assertPathDoesNotExist('branch/file')
450
 
 
451
 
    def do_create_file_in_dir(self):
452
 
        return [('add', ('dir', 'dir-id', 'directory', '')),
453
 
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
454
 
 
455
 
    def do_modify_file_in_dir(self):
456
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
457
 
 
458
 
    def check_file_in_dir_has_more_content(self):
459
 
        self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
460
 
 
461
 
    def check_file_in_dir_doesnt_exist(self):
462
 
        self.assertPathDoesNotExist('branch/dir/file')
463
 
 
464
 
    def _get_resolve_path_arg(self, wt, action):
465
 
        return self._path
 
434
    _conflict_type = conflicts.ContentsConflict,
 
435
    @classmethod
 
436
    def scenarios(klass):
 
437
        base_scenarios = [
 
438
            (('file_modified', dict(actions='modify_file',
 
439
                                   check='file_has_more_content')),
 
440
             ('file_deleted', dict(actions='delete_file',
 
441
                                   check='file_doesnt_exist')),
 
442
             dict(_actions_base='create_file', _item_path='file')),
 
443
            ]
 
444
        return klass.mirror_scenarios(base_scenarios)
466
445
 
467
446
    def assertContentsConflict(self, wt, c):
468
 
        self.assertEqual(self._file_id, c.file_id)
469
 
        self.assertEqual(self._path, c.path)
 
447
        self.assertEqual(self._other_id, c.file_id)
 
448
        self.assertEqual(self._other_path, c.path)
470
449
    _assert_conflict = assertContentsConflict
471
450
 
472
451
 
 
452
 
473
453
class TestResolvePathConflict(TestParametrizedResolveConflicts):
474
454
 
475
 
    _conflict_type = conflicts.PathConflict
476
 
 
477
 
    def do_nothing(self):
478
 
        return []
479
 
 
480
 
    # Each side dict additionally defines:
481
 
    # - path path involved (can be '<deleted>')
482
 
    # - file-id involved
483
 
    scenarios = mirror_scenarios(
484
 
        [
485
 
            # File renamed/deleted
486
 
            (dict(_base_actions='create_file'),
487
 
             ('file_renamed',
488
 
              dict(actions='rename_file', check='file_renamed',
489
 
                   path='new-file', file_id='file-id')),
490
 
             ('file_deleted',
491
 
              dict(actions='delete_file', check='file_doesnt_exist',
492
 
                   # PathConflicts deletion handling requires a special
493
 
                   # hard-coded value
494
 
                   path='<deleted>', file_id='file-id')),),
495
 
            # File renamed/deleted in dir
496
 
            (dict(_base_actions='create_file_in_dir'),
497
 
             ('file_renamed_in_dir',
498
 
              dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
499
 
                   path='dir/new-file', file_id='file-id')),
500
 
             ('file_deleted',
501
 
              dict(actions='delete_file', check='file_in_dir_doesnt_exist',
502
 
                   # PathConflicts deletion handling requires a special
503
 
                   # hard-coded value
504
 
                   path='<deleted>', file_id='file-id')),),
505
 
            # File renamed/renamed differently
506
 
            (dict(_base_actions='create_file'),
507
 
             ('file_renamed',
508
 
              dict(actions='rename_file', check='file_renamed',
509
 
                   path='new-file', file_id='file-id')),
 
455
    _conflict_type = conflicts.PathConflict,
 
456
 
 
457
    @classmethod
 
458
    def scenarios(klass):
 
459
        for_file = dict(_actions_base='create_file',
 
460
                  _item_path='new-file', _item_id='file-id',)
 
461
        for_dir = dict(_actions_base='create_dir',
 
462
                        _item_path='new-dir', _item_id='dir-id',)
 
463
        base_scenarios = [
 
464
            (('file_renamed',
 
465
              dict(actions='rename_file', check='file_renamed')),
 
466
             ('file_deleted',
 
467
              dict(actions='delete_file', check='file_doesnt_exist')),
 
468
             for_file),
 
469
            (('file_renamed',
 
470
              dict(actions='rename_file', check='file_renamed')),
510
471
             ('file_renamed2',
511
 
              dict(actions='rename_file2', check='file_renamed2',
512
 
                   path='new-file2', file_id='file-id')),),
513
 
            # Dir renamed/deleted
514
 
            (dict(_base_actions='create_dir'),
515
 
             ('dir_renamed',
516
 
              dict(actions='rename_dir', check='dir_renamed',
517
 
                   path='new-dir', file_id='dir-id')),
 
472
              dict(actions='rename_file2', check='file_renamed2')),
 
473
             for_file),
 
474
            (('dir_renamed',
 
475
              dict(actions='rename_dir', check='dir_renamed')),
518
476
             ('dir_deleted',
519
 
              dict(actions='delete_dir', check='dir_doesnt_exist',
520
 
                   # PathConflicts deletion handling requires a special
521
 
                   # hard-coded value
522
 
                   path='<deleted>', file_id='dir-id')),),
523
 
            # Dir renamed/renamed differently
524
 
            (dict(_base_actions='create_dir'),
525
 
             ('dir_renamed',
526
 
              dict(actions='rename_dir', check='dir_renamed',
527
 
                   path='new-dir', file_id='dir-id')),
 
477
              dict(actions='delete_dir', check='dir_doesnt_exist')),
 
478
             for_dir),
 
479
            (('dir_renamed',
 
480
              dict(actions='rename_dir', check='dir_renamed')),
528
481
             ('dir_renamed2',
529
 
              dict(actions='rename_dir2', check='dir_renamed2',
530
 
                   path='new-dir2', file_id='dir-id')),),
531
 
            ])
532
 
 
533
 
    def do_create_file(self):
534
 
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
535
 
 
536
 
    def do_create_dir(self):
537
 
        return [('add', ('dir', 'dir-id', 'directory', ''))]
538
 
 
539
 
    def do_rename_file(self):
540
 
        return [('rename', ('file', 'new-file'))]
541
 
 
542
 
    def check_file_renamed(self):
543
 
        self.assertPathDoesNotExist('branch/file')
544
 
        self.assertPathExists('branch/new-file')
545
 
 
546
 
    def do_rename_file2(self):
547
 
        return [('rename', ('file', 'new-file2'))]
548
 
 
549
 
    def check_file_renamed2(self):
550
 
        self.assertPathDoesNotExist('branch/file')
551
 
        self.assertPathExists('branch/new-file2')
552
 
 
553
 
    def do_rename_dir(self):
554
 
        return [('rename', ('dir', 'new-dir'))]
555
 
 
556
 
    def check_dir_renamed(self):
557
 
        self.assertPathDoesNotExist('branch/dir')
558
 
        self.assertPathExists('branch/new-dir')
559
 
 
560
 
    def do_rename_dir2(self):
561
 
        return [('rename', ('dir', 'new-dir2'))]
562
 
 
563
 
    def check_dir_renamed2(self):
564
 
        self.assertPathDoesNotExist('branch/dir')
565
 
        self.assertPathExists('branch/new-dir2')
 
482
              dict(actions='rename_dir2', check='dir_renamed2')),
 
483
             for_dir),
 
484
        ]
 
485
        return klass.mirror_scenarios(base_scenarios)
566
486
 
567
487
    def do_delete_file(self):
568
 
        return [('unversion', 'file-id')]
569
 
 
570
 
    def check_file_doesnt_exist(self):
571
 
        self.assertPathDoesNotExist('branch/file')
572
 
 
573
 
    def do_delete_dir(self):
574
 
        return [('unversion', 'dir-id')]
575
 
 
576
 
    def check_dir_doesnt_exist(self):
577
 
        self.assertPathDoesNotExist('branch/dir')
578
 
 
579
 
    def do_create_file_in_dir(self):
580
 
        return [('add', ('dir', 'dir-id', 'directory', '')),
581
 
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
582
 
 
583
 
    def do_rename_file_in_dir(self):
584
 
        return [('rename', ('dir/file', 'dir/new-file'))]
585
 
 
586
 
    def check_file_in_dir_renamed(self):
587
 
        self.assertPathDoesNotExist('branch/dir/file')
588
 
        self.assertPathExists('branch/dir/new-file')
589
 
 
590
 
    def check_file_in_dir_doesnt_exist(self):
591
 
        self.assertPathDoesNotExist('branch/dir/file')
592
 
 
593
 
    def _get_resolve_path_arg(self, wt, action):
594
 
        tpath = self._this['path']
595
 
        opath = self._other['path']
596
 
        if tpath == '<deleted>':
597
 
            path = opath
598
 
        else:
599
 
            path = tpath
600
 
        return path
 
488
        sup = super(TestResolvePathConflict, self).do_delete_file()
 
489
        # PathConflicts handle deletion differently and requires a special
 
490
        # hard-coded value
 
491
        return ('<deleted>',) + sup[1:]
601
492
 
602
493
    def assertPathConflict(self, wt, c):
603
 
        tpath = self._this['path']
604
 
        tfile_id = self._this['file_id']
605
 
        opath = self._other['path']
606
 
        ofile_id = self._other['file_id']
607
 
        self.assertEqual(tfile_id, ofile_id) # Sanity check
608
 
        self.assertEqual(tfile_id, c.file_id)
609
 
        self.assertEqual(tpath, c.path)
610
 
        self.assertEqual(opath, c.conflict_path)
 
494
        self.assertEqual(self._item_id, c.file_id)
 
495
        self.assertEqual(self._this_path, c.path)
 
496
        self.assertEqual(self._other_path, c.conflict_path)
611
497
    _assert_conflict = assertPathConflict
612
498
 
613
499
 
626
512
 
627
513
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
628
514
 
629
 
    _conflict_type = conflicts.DuplicateEntry
630
 
 
631
 
    scenarios = mirror_scenarios(
632
 
        [
633
 
            # File created with different file-ids
634
 
            (dict(_base_actions='nothing'),
635
 
             ('filea_created',
636
 
              dict(actions='create_file_a', check='file_content_a',
637
 
                   path='file', file_id='file-a-id')),
638
 
             ('fileb_created',
639
 
              dict(actions='create_file_b', check='file_content_b',
640
 
                   path='file', file_id='file-b-id')),),
641
 
            ])
642
 
 
643
 
    def do_nothing(self):
644
 
        return []
645
 
 
646
 
    def do_create_file_a(self):
647
 
        return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
648
 
 
649
 
    def check_file_content_a(self):
650
 
        self.assertFileEqual('file a content\n', 'branch/file')
651
 
 
652
 
    def do_create_file_b(self):
653
 
        return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
654
 
 
655
 
    def check_file_content_b(self):
656
 
        self.assertFileEqual('file b content\n', 'branch/file')
657
 
 
658
 
    def _get_resolve_path_arg(self, wt, action):
659
 
        return self._this['path']
 
515
    _conflict_type = conflicts.DuplicateEntry,
 
516
    @classmethod
 
517
    def scenarios(klass):
 
518
        base_scenarios = [
 
519
            (('filea_created', dict(actions='create_file_a',
 
520
                                    check='file_content_a')),
 
521
             ('fileb_created', dict(actions='create_file_b',
 
522
                                   check='file_content_b')),
 
523
             dict(_actions_base='nothing', _item_path='file')),
 
524
            ]
 
525
        return klass.mirror_scenarios(base_scenarios)
660
526
 
661
527
    def assertDuplicateEntry(self, wt, c):
662
 
        tpath = self._this['path']
663
 
        tfile_id = self._this['file_id']
664
 
        opath = self._other['path']
665
 
        ofile_id = self._other['file_id']
666
 
        self.assertEqual(tpath, opath) # Sanity check
667
 
        self.assertEqual(tfile_id, c.file_id)
668
 
        self.assertEqual(tpath + '.moved', c.path)
669
 
        self.assertEqual(tpath, c.conflict_path)
 
528
        self.assertEqual(self._this_id, c.file_id)
 
529
        self.assertEqual(self._item_path + '.moved', c.path)
 
530
        self.assertEqual(self._item_path, c.conflict_path)
670
531
    _assert_conflict = assertDuplicateEntry
671
532
 
672
533
 
678
539
    # tests MissingParent resolution :-/
679
540
    preamble = """
680
541
$ bzr init trunk
681
 
...
682
542
$ cd trunk
683
543
$ mkdir dir
684
 
$ bzr add -q dir
685
 
$ bzr commit -m 'Create trunk' -q
 
544
$ bzr add dir
 
545
$ bzr commit -m 'Create trunk'
 
546
 
686
547
$ echo 'trunk content' >dir/file
687
 
$ bzr add -q dir/file
688
 
$ bzr commit -q -m 'Add dir/file in trunk'
689
 
$ bzr branch -q . -r 1 ../branch
 
548
$ bzr add dir/file
 
549
$ bzr commit -m 'Add dir/file in trunk'
 
550
 
 
551
$ bzr branch . -r 1 ../branch
690
552
$ cd ../branch
691
 
$ bzr rm dir -q
692
 
$ bzr commit -q -m 'Remove dir in branch'
 
553
$ bzr rm dir
 
554
$ bzr commit -m 'Remove dir in branch'
 
555
 
693
556
$ bzr merge ../trunk
694
557
2>+N  dir/
695
558
2>+N  dir/file
700
563
 
701
564
    def test_take_this(self):
702
565
        self.run_script("""
703
 
$ bzr rm -q dir  --force
 
566
$ bzr rm dir  --force
704
567
$ bzr resolve dir
705
 
2>2 conflict(s) resolved, 0 remaining
706
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
568
$ bzr commit --strict -m 'No more conflicts nor unknown files'
707
569
""")
708
570
 
709
571
    def test_take_other(self):
710
572
        self.run_script("""
711
573
$ bzr resolve dir
712
 
2>2 conflict(s) resolved, 0 remaining
713
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
574
$ bzr commit --strict -m 'No more conflicts nor unknown files'
714
575
""")
715
576
 
716
577
 
718
579
 
719
580
    preamble = """
720
581
$ bzr init trunk
721
 
...
722
582
$ cd trunk
723
583
$ mkdir dir
724
584
$ echo 'trunk content' >dir/file
725
 
$ bzr add -q
726
 
$ bzr commit -m 'Create trunk' -q
 
585
$ bzr add
 
586
$ bzr commit -m 'Create trunk'
 
587
 
727
588
$ echo 'trunk content' >dir/file2
728
 
$ bzr add -q dir/file2
729
 
$ bzr commit -q -m 'Add dir/file2 in branch'
730
 
$ bzr branch -q . -r 1 ../branch
 
589
$ bzr add dir/file2
 
590
$ bzr commit -m 'Add dir/file2 in branch'
 
591
 
 
592
$ bzr branch . -r 1 ../branch
731
593
$ cd ../branch
732
 
$ bzr rm -q dir/file --force
733
 
$ bzr rm -q dir
734
 
$ bzr commit -q -m 'Remove dir/file'
 
594
$ bzr rm dir/file --force
 
595
$ bzr rm dir
 
596
$ bzr commit -m 'Remove dir/file'
 
597
 
735
598
$ bzr merge ../trunk
736
599
2>+N  dir/
737
600
2>+N  dir/file2
743
606
    def test_keep_them_all(self):
744
607
        self.run_script("""
745
608
$ bzr resolve dir
746
 
2>2 conflict(s) resolved, 0 remaining
747
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
609
$ bzr commit --strict -m 'No more conflicts nor unknown files'
748
610
""")
749
611
 
750
612
    def test_adopt_child(self):
751
613
        self.run_script("""
752
 
$ bzr mv -q dir/file2 file2
753
 
$ bzr rm -q dir --force
 
614
$ bzr mv dir/file2 file2
 
615
$ bzr rm dir --force
754
616
$ bzr resolve dir
755
 
2>2 conflict(s) resolved, 0 remaining
756
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
617
$ bzr commit --strict -m 'No more conflicts nor unknown files'
757
618
""")
758
619
 
759
620
    def test_kill_them_all(self):
760
621
        self.run_script("""
761
 
$ bzr rm -q dir --force
 
622
$ bzr rm dir --force
762
623
$ bzr resolve dir
763
 
2>2 conflict(s) resolved, 0 remaining
764
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
624
$ bzr commit --strict -m 'No more conflicts nor unknown files'
765
625
""")
766
626
 
767
627
    def test_resolve_taking_this(self):
768
628
        self.run_script("""
769
629
$ bzr resolve --take-this dir
770
 
2>...
771
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
630
$ bzr commit --strict -m 'No more conflicts nor unknown files'
772
631
""")
773
632
 
774
633
    def test_resolve_taking_other(self):
775
634
        self.run_script("""
776
635
$ bzr resolve --take-other dir
777
 
2>...
778
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
636
$ bzr commit --strict -m 'No more conflicts nor unknown files'
779
637
""")
780
638
 
781
639
 
783
641
 
784
642
    preamble = """
785
643
$ bzr init trunk
786
 
...
787
644
$ cd trunk
788
645
$ mkdir dir
789
646
$ echo 'trunk content' >dir/file
790
 
$ bzr add -q
791
 
$ bzr commit -m 'Create trunk' -q
792
 
$ bzr rm -q dir/file --force
793
 
$ bzr rm -q dir --force
794
 
$ bzr commit -q -m 'Remove dir/file'
795
 
$ bzr branch -q . -r 1 ../branch
 
647
$ bzr add
 
648
$ bzr commit -m 'Create trunk'
 
649
 
 
650
$ bzr rm dir/file --force
 
651
$ bzr rm dir --force
 
652
$ bzr commit -m 'Remove dir/file'
 
653
 
 
654
$ bzr branch . -r 1 ../branch
796
655
$ cd ../branch
797
656
$ echo 'branch content' >dir/file2
798
 
$ bzr add -q dir/file2
799
 
$ bzr commit -q -m 'Add dir/file2 in branch'
 
657
$ bzr add dir/file2
 
658
$ bzr commit -m 'Add dir/file2 in branch'
 
659
 
800
660
$ bzr merge ../trunk
801
661
2>-D  dir/file
802
662
2>Conflict: can't delete dir because it is not empty.  Not deleting.
807
667
    def test_keep_them_all(self):
808
668
        self.run_script("""
809
669
$ bzr resolve dir
810
 
2>2 conflict(s) resolved, 0 remaining
811
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
670
$ bzr commit --strict -m 'No more conflicts nor unknown files'
812
671
""")
813
672
 
814
673
    def test_adopt_child(self):
815
674
        self.run_script("""
816
 
$ bzr mv -q dir/file2 file2
817
 
$ bzr rm -q dir --force
 
675
$ bzr mv dir/file2 file2
 
676
$ bzr rm dir --force
818
677
$ bzr resolve dir
819
 
2>2 conflict(s) resolved, 0 remaining
820
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
678
$ bzr commit --strict -m 'No more conflicts nor unknown files'
821
679
""")
822
680
 
823
681
    def test_kill_them_all(self):
824
682
        self.run_script("""
825
 
$ bzr rm -q dir --force
 
683
$ bzr rm dir --force
826
684
$ bzr resolve dir
827
 
2>2 conflict(s) resolved, 0 remaining
828
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
685
$ bzr commit --strict -m 'No more conflicts nor unknown files'
829
686
""")
830
687
 
831
688
    def test_resolve_taking_this(self):
832
689
        self.run_script("""
833
690
$ bzr resolve --take-this dir
834
 
2>2 conflict(s) resolved, 0 remaining
835
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
691
$ bzr commit --strict -m 'No more conflicts nor unknown files'
836
692
""")
837
693
 
838
694
    def test_resolve_taking_other(self):
839
695
        self.run_script("""
840
696
$ bzr resolve --take-other dir
841
 
2>deleted dir/file2
842
 
2>deleted dir
843
 
2>2 conflict(s) resolved, 0 remaining
844
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
697
$ bzr commit --strict -m 'No more conflicts nor unknown files'
845
698
""")
846
699
 
847
700
 
848
701
class TestResolveParentLoop(TestParametrizedResolveConflicts):
849
702
 
850
 
    _conflict_type = conflicts.ParentLoop
851
 
 
852
 
    _this_args = None
853
 
    _other_args = None
854
 
 
855
 
    # Each side dict additionally defines:
856
 
    # - dir_id: the directory being moved
857
 
    # - target_id: The target directory
858
 
    # - xfail: whether the test is expected to fail if the action is
859
 
    #   involved as 'other'
860
 
    scenarios = mirror_scenarios(
861
 
        [
862
 
            # Dirs moved into each other
863
 
            (dict(_base_actions='create_dir1_dir2'),
864
 
             ('dir1_into_dir2',
865
 
              dict(actions='move_dir1_into_dir2', check='dir1_moved',
866
 
                   dir_id='dir1-id', target_id='dir2-id', xfail=False)),
867
 
             ('dir2_into_dir1',
868
 
              dict(actions='move_dir2_into_dir1', check='dir2_moved',
869
 
                   dir_id='dir2-id', target_id='dir1-id', xfail=False))),
870
 
            # Subdirs moved into each other
871
 
            (dict(_base_actions='create_dir1_4'),
872
 
             ('dir1_into_dir4',
873
 
              dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
874
 
                   dir_id='dir1-id', target_id='dir4-id', xfail=True)),
875
 
             ('dir3_into_dir2',
876
 
              dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
877
 
                   dir_id='dir3-id', target_id='dir2-id', xfail=True))),
878
 
            ])
 
703
    _conflict_type = conflicts.ParentLoop,
 
704
    @classmethod
 
705
    def scenarios(klass):
 
706
        base_scenarios = [
 
707
            (('dir1_into_dir2', dict(actions='move_dir1_into_dir2',
 
708
                                      check='dir1_moved')),
 
709
             ('dir2_into_dir1', dict(actions='move_dir2_into_dir1',
 
710
                                      check='dir2_moved')),
 
711
             dict(_actions_base='create_dir1_dir2')),
 
712
            (('dir1_into_dir4', dict(actions='move_dir1_into_dir4',
 
713
                                      check='dir1_2_moved')),
 
714
             ('dir3_into_dir2', dict(actions='move_dir3_into_dir2',
 
715
                                      check='dir3_4_moved')),
 
716
             dict(_actions_base='create_dir1_4')),
 
717
            ]
 
718
        return klass.mirror_scenarios(base_scenarios)
879
719
 
880
720
    def do_create_dir1_dir2(self):
881
 
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
882
 
                ('add', ('dir2', 'dir2-id', 'directory', '')),]
 
721
        return (None, None,
 
722
                [('add', ('dir1', 'dir1-id', 'directory', '')),
 
723
                 ('add', ('dir2', 'dir2-id', 'directory', '')),
 
724
                 ])
883
725
 
884
726
    def do_move_dir1_into_dir2(self):
885
 
        return [('rename', ('dir1', 'dir2/dir1'))]
 
727
        # The arguments are the file-id to move and the targeted file-id dir.
 
728
        return ('dir1-id', 'dir2-id', [('rename', ('dir1', 'dir2/dir1'))])
886
729
 
887
730
    def check_dir1_moved(self):
888
 
        self.assertPathDoesNotExist('branch/dir1')
889
 
        self.assertPathExists('branch/dir2/dir1')
 
731
        self.failIfExists('branch/dir1')
 
732
        self.failUnlessExists('branch/dir2/dir1')
890
733
 
891
734
    def do_move_dir2_into_dir1(self):
892
 
        return [('rename', ('dir2', 'dir1/dir2'))]
 
735
        # The arguments are the file-id to move and the targeted file-id dir.
 
736
        return ('dir2-id', 'dir1-id', [('rename', ('dir2', 'dir1/dir2'))])
893
737
 
894
738
    def check_dir2_moved(self):
895
 
        self.assertPathDoesNotExist('branch/dir2')
896
 
        self.assertPathExists('branch/dir1/dir2')
 
739
        self.failIfExists('branch/dir2')
 
740
        self.failUnlessExists('branch/dir1/dir2')
897
741
 
898
742
    def do_create_dir1_4(self):
899
 
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
900
 
                ('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
901
 
                ('add', ('dir3', 'dir3-id', 'directory', '')),
902
 
                ('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
 
743
        return (None, None,
 
744
                [('add', ('dir1', 'dir1-id', 'directory', '')),
 
745
                 ('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
 
746
                 ('add', ('dir3', 'dir3-id', 'directory', '')),
 
747
                 ('add', ('dir3/dir4', 'dir4-id', 'directory', '')),
 
748
                 ])
903
749
 
904
750
    def do_move_dir1_into_dir4(self):
905
 
        return [('rename', ('dir1', 'dir3/dir4/dir1'))]
 
751
        # The arguments are the file-id to move and the targeted file-id dir.
 
752
        return ('dir1-id', 'dir4-id',
 
753
                [('rename', ('dir1', 'dir3/dir4/dir1'))])
906
754
 
907
755
    def check_dir1_2_moved(self):
908
 
        self.assertPathDoesNotExist('branch/dir1')
909
 
        self.assertPathExists('branch/dir3/dir4/dir1')
910
 
        self.assertPathExists('branch/dir3/dir4/dir1/dir2')
 
756
        self.failIfExists('branch/dir1')
 
757
        self.failUnlessExists('branch/dir3/dir4/dir1')
 
758
        self.failUnlessExists('branch/dir3/dir4/dir1/dir2')
911
759
 
912
760
    def do_move_dir3_into_dir2(self):
913
 
        return [('rename', ('dir3', 'dir1/dir2/dir3'))]
 
761
        # The arguments are the file-id to move and the targeted file-id dir.
 
762
        return ('dir3-id', 'dir2-id',
 
763
                [('rename', ('dir3', 'dir1/dir2/dir3'))])
914
764
 
915
765
    def check_dir3_4_moved(self):
916
 
        self.assertPathDoesNotExist('branch/dir3')
917
 
        self.assertPathExists('branch/dir1/dir2/dir3')
918
 
        self.assertPathExists('branch/dir1/dir2/dir3/dir4')
 
766
        self.failIfExists('branch/dir3')
 
767
        self.failUnlessExists('branch/dir1/dir2/dir3')
 
768
        self.failUnlessExists('branch/dir1/dir2/dir3/dir4')
919
769
 
920
770
    def _get_resolve_path_arg(self, wt, action):
921
 
        # ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
 
771
        # ParentLoop is unsual as it says: 
 
772
        # moving <conflict_path> into <path>.  Cancelled move.
922
773
        # But since <path> doesn't exist in the working tree, we need to use
923
 
        # <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
924
 
        return wt.id2path(self._other['dir_id'])
 
774
        # <conflict_path> instead
 
775
        path = wt.id2path(self._other_id)
 
776
        return path
925
777
 
926
778
    def assertParentLoop(self, wt, c):
927
 
        self.assertEqual(self._other['dir_id'], c.file_id)
928
 
        self.assertEqual(self._other['target_id'], c.conflict_file_id)
 
779
        if 'taking_other(' in self.id() and 'dir4' in self.id():
 
780
            raise tests.KnownFailure(
 
781
                "ParentLoop doesn't carry enough info to resolve")
 
782
        # The relevant file-ids are other_args swapped (which is the main
 
783
        # reason why they should be renamed other_args instead of Other_path
 
784
        # and other_id). In the conflict object, they represent:
 
785
        # c.file_id: the directory being moved
 
786
        # c.conflict_id_id: The target directory
 
787
        self.assertEqual(self._other_path, c.file_id)
 
788
        self.assertEqual(self._other_id, c.conflict_file_id)
929
789
        # The conflict paths are irrelevant (they are deterministic but not
930
790
        # worth checking since they don't provide the needed information
931
791
        # anyway)
932
 
        if self._other['xfail']:
933
 
            # It's a bit hackish to raise from here relying on being called for
934
 
            # both tests but this avoid overriding test_resolve_taking_other
935
 
            raise tests.KnownFailure(
936
 
                "ParentLoop doesn't carry enough info to resolve --take-other")
937
792
    _assert_conflict = assertParentLoop
938
793
 
939
794
 
 
795
class OldTestResolveParentLoop(TestResolveConflicts):
 
796
 
 
797
    preamble = """
 
798
$ bzr init trunk
 
799
$ cd trunk
 
800
$ bzr mkdir dir1
 
801
$ bzr mkdir dir2
 
802
$ bzr commit -m 'Create trunk'
 
803
 
 
804
$ bzr mv dir2 dir1
 
805
$ bzr commit -m 'Moved dir2 into dir1'
 
806
 
 
807
$ bzr branch . -r 1 ../branch
 
808
$ cd ../branch
 
809
$ bzr mv dir1 dir2
 
810
$ bzr commit -m 'Moved dir1 into dir2'
 
811
 
 
812
$ bzr merge ../trunk
 
813
2>Conflict moving dir2 into dir2/dir1. Cancelled move.
 
814
2>1 conflicts encountered.
 
815
"""
 
816
 
 
817
    def test_take_this(self):
 
818
        self.run_script("""
 
819
$ bzr resolve dir2
 
820
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
821
""")
 
822
 
 
823
    def test_take_other(self):
 
824
        self.run_script("""
 
825
$ bzr mv dir2/dir1 dir1
 
826
$ bzr mv dir2 dir1
 
827
$ bzr resolve dir2
 
828
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
829
""")
 
830
 
 
831
    def test_resolve_taking_this(self):
 
832
        self.run_script("""
 
833
$ bzr resolve --take-this dir2
 
834
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
835
""")
 
836
        self.failUnlessExists('dir2')
 
837
 
 
838
    def test_resolve_taking_other(self):
 
839
        self.run_script("""
 
840
$ bzr resolve --take-other dir2
 
841
$ bzr commit --strict -m 'No more conflicts nor unknown files'
 
842
""")
 
843
        self.failUnlessExists('dir1')
 
844
 
 
845
 
940
846
class TestResolveNonDirectoryParent(TestResolveConflicts):
941
847
 
942
848
    preamble = """
943
849
$ bzr init trunk
944
 
...
945
850
$ cd trunk
946
851
$ bzr mkdir foo
947
 
...
948
 
$ bzr commit -m 'Create trunk' -q
 
852
$ bzr commit -m 'Create trunk'
949
853
$ echo "Boing" >foo/bar
950
 
$ bzr add -q foo/bar
951
 
$ bzr commit -q -m 'Add foo/bar'
952
 
$ bzr branch -q . -r 1 ../branch
 
854
$ bzr add foo/bar
 
855
$ bzr commit -m 'Add foo/bar'
 
856
 
 
857
$ bzr branch . -r 1 ../branch
953
858
$ cd ../branch
954
859
$ rm -r foo
955
860
$ echo "Boo!" >foo
956
 
$ bzr commit -q -m 'foo is now a file'
 
861
$ bzr commit -m 'foo is now a file'
 
862
 
957
863
$ bzr merge ../trunk
958
864
2>+N  foo.new/bar
959
865
2>RK  foo => foo.new/
965
871
 
966
872
    def test_take_this(self):
967
873
        self.run_script("""
968
 
$ bzr rm -q foo.new --force
 
874
$ bzr rm foo.new --force
969
875
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
970
876
# aside ? -- vila 090916
971
 
$ bzr add -q foo
 
877
$ bzr add foo
972
878
$ bzr resolve foo.new
973
 
2>1 conflict(s) resolved, 0 remaining
974
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
879
$ bzr commit --strict -m 'No more conflicts nor unknown files'
975
880
""")
976
881
 
977
882
    def test_take_other(self):
978
883
        self.run_script("""
979
 
$ bzr rm -q foo --force
980
 
$ bzr mv -q foo.new foo
 
884
$ bzr rm foo --force
 
885
$ bzr mv foo.new foo
981
886
$ bzr resolve foo
982
 
2>1 conflict(s) resolved, 0 remaining
983
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
887
$ bzr commit --strict -m 'No more conflicts nor unknown files'
984
888
""")
985
889
 
986
890
    def test_resolve_taking_this(self):
987
891
        self.run_script("""
988
892
$ bzr resolve --take-this foo.new
989
 
2>...
990
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
893
$ bzr commit --strict -m 'No more conflicts nor unknown files'
991
894
""")
992
895
 
993
896
    def test_resolve_taking_other(self):
994
897
        self.run_script("""
995
898
$ bzr resolve --take-other foo.new
996
 
2>...
997
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
 
899
$ bzr commit --strict -m 'No more conflicts nor unknown files'
998
900
""")
999
901
 
1000
902
 
1006
908
        # conflict.
1007
909
        self.run_script("""
1008
910
$ bzr init trunk
1009
 
...
1010
911
$ cd trunk
1011
912
$ bzr mkdir foo
1012
 
...
1013
 
$ bzr commit -m 'Create trunk' -q
 
913
$ bzr commit -m 'Create trunk'
1014
914
$ rm -r foo
1015
915
$ echo "Boo!" >foo
1016
 
$ bzr commit -m 'foo is now a file' -q
1017
 
$ bzr branch -q . -r 1 ../branch -q
 
916
$ bzr commit -m 'foo is now a file'
 
917
 
 
918
$ bzr branch . -r 1 ../branch
1018
919
$ cd ../branch
1019
920
$ echo "Boing" >foo/bar
1020
 
$ bzr add -q foo/bar -q
1021
 
$ bzr commit -m 'Add foo/bar' -q
 
921
$ bzr add foo/bar
 
922
$ bzr commit -m 'Add foo/bar'
 
923
 
1022
924
$ bzr merge ../trunk
1023
925
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
1024
926
""")