~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-14 12:15:44 UTC
  • mto: This revision was merged to the branch mainline in revision 6365.
  • Revision ID: jelmer@samba.org-20111214121544-v07cbvmi30re6q7w
s/NoVfsCalls/ContainsNoVfsCalls/.

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