~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Aaron Bentley
  • Date: 2005-07-26 14:06:11 UTC
  • mto: (1092.1.41) (1185.3.4) (974.1.47)
  • mto: This revision was merged to the branch mainline in revision 982.
  • Revision ID: abentley@panoramicfeedback.com-20050726140611-403e366f3c79c1f1
Fixed python invocation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
 
18
 
import os
19
 
 
20
 
from bzrlib import (
21
 
    bzrdir,
22
 
    conflicts,
23
 
    errors,
24
 
    option,
25
 
    osutils,
26
 
    tests,
27
 
    )
28
 
from bzrlib.tests import (
29
 
    script,
30
 
    scenarios,
31
 
    )
32
 
 
33
 
 
34
 
load_tests = scenarios.load_tests_apply_scenarios
35
 
 
36
 
 
37
 
# TODO: Test commit with some added, and added-but-missing files
38
 
# RBC 20060124 is that not tested in test_commit.py ?
39
 
 
40
 
# The order of 'path' here is important - do not let it
41
 
# be a sorted list.
42
 
# u'\xe5' == a with circle
43
 
# '\xc3\xae' == u'\xee' == i with hat
44
 
# So these are u'path' and 'id' only with a circle and a hat. (shappo?)
45
 
example_conflicts = conflicts.ConflictList(
46
 
    [conflicts.MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
47
 
     conflicts.ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'),
48
 
     conflicts.TextConflict(u'p\xe5tha'),
49
 
     conflicts.PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
50
 
     conflicts.DuplicateID('Unversioned existing file',
51
 
                           u'p\xe5thc', u'p\xe5thc2',
52
 
                           '\xc3\xaedc', '\xc3\xaedc'),
53
 
    conflicts.DuplicateEntry('Moved existing file to',
54
 
                             u'p\xe5thdd.moved', u'p\xe5thd',
55
 
                             '\xc3\xaedd', None),
56
 
    conflicts.ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
57
 
                         None, '\xc3\xaed2e'),
58
 
    conflicts.UnversionedParent('Versioned directory',
59
 
                                u'p\xe5thf', '\xc3\xaedf'),
60
 
    conflicts.NonDirectoryParent('Created directory',
61
 
                                 u'p\xe5thg', '\xc3\xaedg'),
62
 
])
63
 
 
64
 
 
65
 
def vary_by_conflicts():
66
 
    for conflict in example_conflicts:
67
 
        yield (conflict.__class__.__name__, {"conflict": conflict})
68
 
 
69
 
 
70
 
class TestConflicts(tests.TestCaseWithTransport):
71
 
 
72
 
    def test_resolve_conflict_dir(self):
73
 
        tree = self.make_branch_and_tree('.')
74
 
        self.build_tree_contents([('hello', 'hello world4'),
75
 
                                  ('hello.THIS', 'hello world2'),
76
 
                                  ('hello.BASE', 'hello world1'),
77
 
                                  ])
78
 
        os.mkdir('hello.OTHER')
79
 
        tree.add('hello', 'q')
80
 
        l = conflicts.ConflictList([conflicts.TextConflict('hello')])
81
 
        l.remove_files(tree)
82
 
 
83
 
    def test_select_conflicts(self):
84
 
        tree = self.make_branch_and_tree('.')
85
 
        clist = conflicts.ConflictList
86
 
 
87
 
        def check_select(not_selected, selected, paths, **kwargs):
88
 
            self.assertEqual(
89
 
                (not_selected, selected),
90
 
                tree_conflicts.select_conflicts(tree, paths, **kwargs))
91
 
 
92
 
        foo = conflicts.ContentsConflict('foo')
93
 
        bar = conflicts.ContentsConflict('bar')
94
 
        tree_conflicts = clist([foo, bar])
95
 
 
96
 
        check_select(clist([bar]), clist([foo]), ['foo'])
97
 
        check_select(clist(), tree_conflicts,
98
 
                     [''], ignore_misses=True, recurse=True)
99
 
 
100
 
        foobaz  = conflicts.ContentsConflict('foo/baz')
101
 
        tree_conflicts = clist([foobaz, bar])
102
 
 
103
 
        check_select(clist([bar]), clist([foobaz]),
104
 
                     ['foo'], ignore_misses=True, recurse=True)
105
 
 
106
 
        qux = conflicts.PathConflict('qux', 'foo/baz')
107
 
        tree_conflicts = clist([qux])
108
 
 
109
 
        check_select(clist(), tree_conflicts,
110
 
                     ['foo'], ignore_misses=True, recurse=True)
111
 
        check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
112
 
 
113
 
    def test_resolve_conflicts_recursive(self):
114
 
        tree = self.make_branch_and_tree('.')
115
 
        self.build_tree(['dir/', 'dir/hello'])
116
 
        tree.add(['dir', 'dir/hello'])
117
 
 
118
 
        dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
119
 
        tree.set_conflicts(dirhello)
120
 
 
121
 
        conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
122
 
        self.assertEqual(dirhello, tree.conflicts())
123
 
 
124
 
        conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
125
 
        self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
126
 
 
127
 
 
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__)
138
 
 
139
 
    def test_stanza_roundtrip(self):
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)
156
 
 
157
 
    def test_stanzification(self):
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))
179
 
 
180
 
 
181
 
# FIXME: The shell-like tests should be converted to real whitebox tests... or
182
 
# moved to a blackbox module -- vila 20100205
183
 
 
184
 
# FIXME: test missing for multiple conflicts
185
 
 
186
 
# FIXME: Tests missing for DuplicateID conflict type
187
 
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
188
 
 
189
 
    preamble = None # The setup script set by daughter classes
190
 
 
191
 
    def setUp(self):
192
 
        super(TestResolveConflicts, self).setUp()
193
 
        self.run_script(self.preamble)
194
 
 
195
 
 
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
728
 
 
729
 
 
730
 
class TestResolveUnversionedParent(TestResolveConflicts):
731
 
 
732
 
    # FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
733
 
 
734
 
    # FIXME: While this *creates* UnversionedParent conflicts, this really only
735
 
    # tests MissingParent resolution :-/
736
 
    preamble = """
737
 
$ bzr init trunk
738
 
...
739
 
$ cd trunk
740
 
$ mkdir dir
741
 
$ bzr add -q dir
742
 
$ bzr commit -m 'Create trunk' -q
743
 
$ echo 'trunk content' >dir/file
744
 
$ bzr add -q dir/file
745
 
$ bzr commit -q -m 'Add dir/file in trunk'
746
 
$ bzr branch -q . -r 1 ../branch
747
 
$ cd ../branch
748
 
$ bzr rm dir -q
749
 
$ bzr commit -q -m 'Remove dir in branch'
750
 
$ bzr merge ../trunk
751
 
2>+N  dir/
752
 
2>+N  dir/file
753
 
2>Conflict adding files to dir.  Created directory.
754
 
2>Conflict because dir is not versioned, but has versioned children.  Versioned directory.
755
 
2>2 conflicts encountered.
756
 
"""
757
 
 
758
 
    def test_take_this(self):
759
 
        self.run_script("""
760
 
$ bzr rm -q dir  --force
761
 
$ bzr resolve dir
762
 
2>2 conflicts resolved, 0 remaining
763
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
764
 
""")
765
 
 
766
 
    def test_take_other(self):
767
 
        self.run_script("""
768
 
$ bzr resolve dir
769
 
2>2 conflicts resolved, 0 remaining
770
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
771
 
""")
772
 
 
773
 
 
774
 
class TestResolveMissingParent(TestResolveConflicts):
775
 
 
776
 
    preamble = """
777
 
$ bzr init trunk
778
 
...
779
 
$ cd trunk
780
 
$ mkdir dir
781
 
$ echo 'trunk content' >dir/file
782
 
$ bzr add -q
783
 
$ bzr commit -m 'Create trunk' -q
784
 
$ echo 'trunk content' >dir/file2
785
 
$ bzr add -q dir/file2
786
 
$ bzr commit -q -m 'Add dir/file2 in branch'
787
 
$ bzr branch -q . -r 1 ../branch
788
 
$ cd ../branch
789
 
$ bzr rm -q dir/file --force
790
 
$ bzr rm -q dir
791
 
$ bzr commit -q -m 'Remove dir/file'
792
 
$ bzr merge ../trunk
793
 
2>+N  dir/
794
 
2>+N  dir/file2
795
 
2>Conflict adding files to dir.  Created directory.
796
 
2>Conflict because dir is not versioned, but has versioned children.  Versioned directory.
797
 
2>2 conflicts encountered.
798
 
"""
799
 
 
800
 
    def test_keep_them_all(self):
801
 
        self.run_script("""
802
 
$ bzr resolve dir
803
 
2>2 conflicts resolved, 0 remaining
804
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
805
 
""")
806
 
 
807
 
    def test_adopt_child(self):
808
 
        self.run_script("""
809
 
$ bzr mv -q dir/file2 file2
810
 
$ bzr rm -q dir --force
811
 
$ bzr resolve dir
812
 
2>2 conflicts resolved, 0 remaining
813
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
814
 
""")
815
 
 
816
 
    def test_kill_them_all(self):
817
 
        self.run_script("""
818
 
$ bzr rm -q dir --force
819
 
$ bzr resolve dir
820
 
2>2 conflicts resolved, 0 remaining
821
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
822
 
""")
823
 
 
824
 
    def test_resolve_taking_this(self):
825
 
        self.run_script("""
826
 
$ bzr resolve --take-this dir
827
 
2>...
828
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
829
 
""")
830
 
 
831
 
    def test_resolve_taking_other(self):
832
 
        self.run_script("""
833
 
$ bzr resolve --take-other dir
834
 
2>...
835
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
836
 
""")
837
 
 
838
 
 
839
 
class TestResolveDeletingParent(TestResolveConflicts):
840
 
 
841
 
    preamble = """
842
 
$ bzr init trunk
843
 
...
844
 
$ cd trunk
845
 
$ mkdir dir
846
 
$ echo 'trunk content' >dir/file
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
853
 
$ cd ../branch
854
 
$ echo 'branch content' >dir/file2
855
 
$ bzr add -q dir/file2
856
 
$ bzr commit -q -m 'Add dir/file2 in branch'
857
 
$ bzr merge ../trunk
858
 
2>-D  dir/file
859
 
2>Conflict: can't delete dir because it is not empty.  Not deleting.
860
 
2>Conflict because dir is not versioned, but has versioned children.  Versioned directory.
861
 
2>2 conflicts encountered.
862
 
"""
863
 
 
864
 
    def test_keep_them_all(self):
865
 
        self.run_script("""
866
 
$ bzr resolve dir
867
 
2>2 conflicts resolved, 0 remaining
868
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
869
 
""")
870
 
 
871
 
    def test_adopt_child(self):
872
 
        self.run_script("""
873
 
$ bzr mv -q dir/file2 file2
874
 
$ bzr rm -q dir --force
875
 
$ bzr resolve dir
876
 
2>2 conflicts resolved, 0 remaining
877
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
878
 
""")
879
 
 
880
 
    def test_kill_them_all(self):
881
 
        self.run_script("""
882
 
$ bzr rm -q dir --force
883
 
$ bzr resolve dir
884
 
2>2 conflicts resolved, 0 remaining
885
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
886
 
""")
887
 
 
888
 
    def test_resolve_taking_this(self):
889
 
        self.run_script("""
890
 
$ bzr resolve --take-this dir
891
 
2>2 conflicts resolved, 0 remaining
892
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
893
 
""")
894
 
 
895
 
    def test_resolve_taking_other(self):
896
 
        self.run_script("""
897
 
$ bzr resolve --take-other dir
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
995
 
 
996
 
 
997
 
class TestResolveNonDirectoryParent(TestResolveConflicts):
998
 
 
999
 
    preamble = """
1000
 
$ bzr init trunk
1001
 
...
1002
 
$ cd trunk
1003
 
$ bzr mkdir foo
1004
 
...
1005
 
$ bzr commit -m 'Create trunk' -q
1006
 
$ echo "Boing" >foo/bar
1007
 
$ bzr add -q foo/bar
1008
 
$ bzr commit -q -m 'Add foo/bar'
1009
 
$ bzr branch -q . -r 1 ../branch
1010
 
$ cd ../branch
1011
 
$ rm -r foo
1012
 
$ echo "Boo!" >foo
1013
 
$ bzr commit -q -m 'foo is now a file'
1014
 
$ bzr merge ../trunk
1015
 
2>+N  foo.new/bar
1016
 
2>RK  foo => foo.new/
1017
 
# FIXME: The message is misleading, foo.new *is* a directory when the message
1018
 
# is displayed -- vila 090916
1019
 
2>Conflict: foo.new is not a directory, but has files in it.  Created directory.
1020
 
2>1 conflicts encountered.
1021
 
"""
1022
 
 
1023
 
    def test_take_this(self):
1024
 
        self.run_script("""
1025
 
$ bzr rm -q foo.new --force
1026
 
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
1027
 
# aside ? -- vila 090916
1028
 
$ bzr add -q foo
1029
 
$ bzr resolve foo.new
1030
 
2>1 conflict resolved, 0 remaining
1031
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1032
 
""")
1033
 
 
1034
 
    def test_take_other(self):
1035
 
        self.run_script("""
1036
 
$ bzr rm -q foo --force
1037
 
$ bzr mv -q foo.new foo
1038
 
$ bzr resolve foo
1039
 
2>1 conflict resolved, 0 remaining
1040
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1041
 
""")
1042
 
 
1043
 
    def test_resolve_taking_this(self):
1044
 
        self.run_script("""
1045
 
$ bzr resolve --take-this foo.new
1046
 
2>...
1047
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1048
 
""")
1049
 
 
1050
 
    def test_resolve_taking_other(self):
1051
 
        self.run_script("""
1052
 
$ bzr resolve --take-other foo.new
1053
 
2>...
1054
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
1055
 
""")
1056
 
 
1057
 
 
1058
 
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
1059
 
 
1060
 
    def test_bug_430129(self):
1061
 
        # This is nearly like TestResolveNonDirectoryParent but with branch and
1062
 
        # trunk switched. As such it should certainly produce the same
1063
 
        # conflict.
1064
 
        self.assertRaises(errors.MalformedTransform,
1065
 
                          self.run_script,"""
1066
 
$ bzr init trunk
1067
 
...
1068
 
$ cd trunk
1069
 
$ bzr mkdir foo
1070
 
...
1071
 
$ bzr commit -m 'Create trunk' -q
1072
 
$ rm -r foo
1073
 
$ echo "Boo!" >foo
1074
 
$ bzr commit -m 'foo is now a file' -q
1075
 
$ bzr branch -q . -r 1 ../branch -q
1076
 
$ cd ../branch
1077
 
$ echo "Boing" >foo/bar
1078
 
$ bzr add -q foo/bar -q
1079
 
$ bzr commit -m 'Add foo/bar' -q
1080
 
$ bzr merge ../trunk
1081
 
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
1082
 
""")
1083
 
 
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
 
 
1170
 
class TestResolveActionOption(tests.TestCase):
1171
 
 
1172
 
    def setUp(self):
1173
 
        super(TestResolveActionOption, self).setUp()
1174
 
        self.options = [conflicts.ResolveActionOption()]
1175
 
        self.parser = option.get_optparser(dict((o.name, o)
1176
 
                                                for o in self.options))
1177
 
 
1178
 
    def parse(self, args):
1179
 
        return self.parser.parse_args(args)
1180
 
 
1181
 
    def test_unknown_action(self):
1182
 
        self.assertRaises(errors.BadOptionValue,
1183
 
                          self.parse, ['--action', 'take-me-to-the-moon'])
1184
 
 
1185
 
    def test_done(self):
1186
 
        opts, args = self.parse(['--action', 'done'])
1187
 
        self.assertEqual({'action':'done'}, opts)
1188
 
 
1189
 
    def test_take_this(self):
1190
 
        opts, args = self.parse(['--action', 'take-this'])
1191
 
        self.assertEqual({'action': 'take_this'}, opts)
1192
 
        opts, args = self.parse(['--take-this'])
1193
 
        self.assertEqual({'action': 'take_this'}, opts)
1194
 
 
1195
 
    def test_take_other(self):
1196
 
        opts, args = self.parse(['--action', 'take-other'])
1197
 
        self.assertEqual({'action': 'take_other'}, opts)
1198
 
        opts, args = self.parse(['--take-other'])
1199
 
        self.assertEqual({'action': 'take_other'}, opts)