~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: Ian Clatworthy
  • Date: 2007-12-11 02:07:30 UTC
  • mto: (3119.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 3120.
  • Revision ID: ian.clatworthy@internode.on.net-20071211020730-sdj4kj794dw0628e
make help topics more discoverable

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
import os
19
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
 
20
from bzrlib import bzrdir
 
21
from bzrlib.tests import TestCaseWithTransport, TestCase
 
22
from bzrlib.branch import Branch
 
23
from bzrlib.conflicts import (
 
24
    ConflictList,
 
25
    ContentsConflict,
 
26
    DuplicateID,
 
27
    DuplicateEntry,
 
28
    MissingParent,
 
29
    ParentLoop,
 
30
    PathConflict,
 
31
    TextConflict,
 
32
    UnversionedParent,
 
33
    resolve,
 
34
    restore,
 
35
    )
 
36
from bzrlib.errors import NotConflicted
35
37
 
36
38
 
37
39
# TODO: Test commit with some added, and added-but-missing files
41
43
# be a sorted list.
42
44
# u'\xe5' == a with circle
43
45
# '\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'),
 
46
# So these are u'pathg' and 'idg' only with a circle and a hat. (shappo?)
 
47
example_conflicts = ConflictList([ 
 
48
    MissingParent('Not deleting', u'p\xe5thg', '\xc3\xaedg'),
 
49
    ContentsConflict(u'p\xe5tha', None, '\xc3\xaeda'), 
 
50
    TextConflict(u'p\xe5tha'),
 
51
    PathConflict(u'p\xe5thb', u'p\xe5thc', '\xc3\xaedb'),
 
52
    DuplicateID('Unversioned existing file', u'p\xe5thc', u'p\xe5thc2',
 
53
                '\xc3\xaedc', '\xc3\xaedc'),
 
54
    DuplicateEntry('Moved existing file to',  u'p\xe5thdd.moved', u'p\xe5thd',
 
55
                   '\xc3\xaedd', None),
 
56
    ParentLoop('Cancelled move', u'p\xe5the', u'p\xe5th2e',
 
57
               None, '\xc3\xaed2e'),
 
58
    UnversionedParent('Versioned directory', u'p\xe5thf', '\xc3\xaedf'),
62
59
])
63
60
 
64
61
 
65
 
class TestConflicts(tests.TestCaseWithTransport):
 
62
class TestConflicts(TestCaseWithTransport):
 
63
 
 
64
    def test_conflicts(self):
 
65
        """Conflicts are detected properly"""
 
66
        tree = self.make_branch_and_tree('.',
 
67
            format=bzrdir.BzrDirFormat6())
 
68
        b = tree.branch
 
69
        file('hello', 'w').write('hello world4')
 
70
        file('hello.THIS', 'w').write('hello world2')
 
71
        file('hello.BASE', 'w').write('hello world1')
 
72
        file('hello.OTHER', 'w').write('hello world3')
 
73
        file('hello.sploo.BASE', 'w').write('yellow world')
 
74
        file('hello.sploo.OTHER', 'w').write('yellow world2')
 
75
        tree.lock_read()
 
76
        self.assertEqual(len(list(tree.list_files())), 6)
 
77
        tree.unlock()
 
78
        conflicts = tree.conflicts()
 
79
        self.assertEqual(len(conflicts), 2)
 
80
        self.assert_('hello' in conflicts[0].path)
 
81
        self.assert_('hello.sploo' in conflicts[1].path)
 
82
        restore('hello')
 
83
        restore('hello.sploo')
 
84
        self.assertEqual(len(tree.conflicts()), 0)
 
85
        self.assertFileEqual('hello world2', 'hello')
 
86
        assert not os.path.lexists('hello.sploo')
 
87
        self.assertRaises(NotConflicted, restore, 'hello')
 
88
        self.assertRaises(NotConflicted, restore, 'hello.sploo')
66
89
 
67
90
    def test_resolve_conflict_dir(self):
68
91
        tree = self.make_branch_and_tree('.')
69
 
        self.build_tree_contents([('hello', 'hello world4'),
70
 
                                  ('hello.THIS', 'hello world2'),
71
 
                                  ('hello.BASE', 'hello world1'),
72
 
                                  ])
 
92
        b = tree.branch
 
93
        file('hello', 'w').write('hello world4')
 
94
        tree.add('hello', 'q')
 
95
        file('hello.THIS', 'w').write('hello world2')
 
96
        file('hello.BASE', 'w').write('hello world1')
73
97
        os.mkdir('hello.OTHER')
74
 
        tree.add('hello', 'q')
75
 
        l = conflicts.ConflictList([conflicts.TextConflict('hello')])
 
98
        l = ConflictList([TextConflict('hello')])
76
99
        l.remove_files(tree)
77
100
 
78
101
    def test_select_conflicts(self):
79
102
        tree = self.make_branch_and_tree('.')
80
 
        clist = conflicts.ConflictList
81
 
 
82
 
        def check_select(not_selected, selected, paths, **kwargs):
83
 
            self.assertEqual(
84
 
                (not_selected, selected),
85
 
                tree_conflicts.select_conflicts(tree, paths, **kwargs))
86
 
 
87
 
        foo = conflicts.ContentsConflict('foo')
88
 
        bar = conflicts.ContentsConflict('bar')
89
 
        tree_conflicts = clist([foo, bar])
90
 
 
91
 
        check_select(clist([bar]), clist([foo]), ['foo'])
92
 
        check_select(clist(), tree_conflicts,
93
 
                     [''], ignore_misses=True, recurse=True)
94
 
 
95
 
        foobaz  = conflicts.ContentsConflict('foo/baz')
96
 
        tree_conflicts = clist([foobaz, bar])
97
 
 
98
 
        check_select(clist([bar]), clist([foobaz]),
99
 
                     ['foo'], ignore_misses=True, recurse=True)
100
 
 
101
 
        qux = conflicts.PathConflict('qux', 'foo/baz')
102
 
        tree_conflicts = clist([qux])
103
 
 
104
 
        check_select(clist(), tree_conflicts,
105
 
                     ['foo'], ignore_misses=True, recurse=True)
106
 
        check_select (tree_conflicts, clist(), ['foo'], ignore_misses=True)
 
103
        tree_conflicts = ConflictList([ContentsConflict('foo'),
 
104
                                       ContentsConflict('bar')])
 
105
        self.assertEqual((ConflictList([ContentsConflict('bar')]),
 
106
                          ConflictList([ContentsConflict('foo')])),
 
107
                         tree_conflicts.select_conflicts(tree, ['foo']))
 
108
        self.assertEqual((ConflictList(), tree_conflicts),
 
109
                         tree_conflicts.select_conflicts(tree, [''],
 
110
                         ignore_misses=True, recurse=True))
 
111
        tree_conflicts = ConflictList([ContentsConflict('foo/baz'),
 
112
                                       ContentsConflict('bar')])
 
113
        self.assertEqual((ConflictList([ContentsConflict('bar')]),
 
114
                          ConflictList([ContentsConflict('foo/baz')])),
 
115
                         tree_conflicts.select_conflicts(tree, ['foo'],
 
116
                                                         recurse=True,
 
117
                                                         ignore_misses=True))
 
118
        tree_conflicts = ConflictList([PathConflict('qux', 'foo/baz')])
 
119
        self.assertEqual((ConflictList(), tree_conflicts),
 
120
                         tree_conflicts.select_conflicts(tree, ['foo'],
 
121
                                                         recurse=True,
 
122
                                                         ignore_misses=True))
 
123
        self.assertEqual((tree_conflicts, ConflictList()),
 
124
                         tree_conflicts.select_conflicts(tree, ['foo'],
 
125
                                                         ignore_misses=True))
107
126
 
108
127
    def test_resolve_conflicts_recursive(self):
109
128
        tree = self.make_branch_and_tree('.')
110
129
        self.build_tree(['dir/', 'dir/hello'])
111
130
        tree.add(['dir', 'dir/hello'])
112
 
 
113
 
        dirhello = conflicts.ConflictList([conflicts.TextConflict('dir/hello')])
114
 
        tree.set_conflicts(dirhello)
115
 
 
116
 
        conflicts.resolve(tree, ['dir'], recursive=False, ignore_misses=True)
117
 
        self.assertEqual(dirhello, tree.conflicts())
118
 
 
119
 
        conflicts.resolve(tree, ['dir'], recursive=True, ignore_misses=True)
120
 
        self.assertEqual(conflicts.ConflictList([]), tree.conflicts())
121
 
 
122
 
 
123
 
class TestConflictStanzas(tests.TestCase):
 
131
        tree.set_conflicts(ConflictList([TextConflict('dir/hello')]))
 
132
        resolve(tree, ['dir'], recursive=False, ignore_misses=True)
 
133
        self.assertEqual(ConflictList([TextConflict('dir/hello')]),
 
134
                         tree.conflicts())
 
135
        resolve(tree, ['dir'], recursive=True, ignore_misses=True)
 
136
        self.assertEqual(ConflictList([]),
 
137
                         tree.conflicts())
 
138
 
 
139
 
 
140
class TestConflictStanzas(TestCase):
124
141
 
125
142
    def test_stanza_roundtrip(self):
126
143
        # write and read our example stanza.
127
144
        stanza_iter = example_conflicts.to_stanzas()
128
 
        processed = conflicts.ConflictList.from_stanzas(stanza_iter)
 
145
        processed = ConflictList.from_stanzas(stanza_iter)
129
146
        for o, p in zip(processed, example_conflicts):
130
147
            self.assertEqual(o, p)
131
148
 
152
169
                self.assertStartsWith(stanza['conflict_path'], u'p\xe5th')
153
170
            if 'conflict_file_id' in stanza:
154
171
                self.assertStartsWith(stanza['conflict_file_id'], u'\xeed')
155
 
 
156
 
 
157
 
# FIXME: The shell-like tests should be converted to real whitebox tests... or
158
 
# moved to a blackbox module -- vila 20100205
159
 
 
160
 
# FIXME: test missing for multiple conflicts
161
 
 
162
 
# FIXME: Tests missing for DuplicateID conflict type
163
 
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
164
 
 
165
 
    preamble = None # The setup script set by daughter classes
166
 
 
167
 
    def setUp(self):
168
 
        super(TestResolveConflicts, self).setUp()
169
 
        self.run_script(self.preamble)
170
 
 
171
 
 
172
 
def mirror_scenarios(base_scenarios):
173
 
    """Return a list of mirrored scenarios.
174
 
 
175
 
    Each scenario in base_scenarios is duplicated switching the roles of 'this'
176
 
    and 'other'
177
 
    """
178
 
    scenarios = []
179
 
    for common, (lname, ldict), (rname, rdict) in base_scenarios:
180
 
        a = tests.multiply_scenarios([(lname, dict(_this=ldict))],
181
 
                                     [(rname, dict(_other=rdict))])
182
 
        b = tests.multiply_scenarios([(rname, dict(_this=rdict))],
183
 
                                     [(lname, dict(_other=ldict))])
184
 
        # Inject the common parameters in all scenarios
185
 
        for name, d in a + b:
186
 
            d.update(common)
187
 
        scenarios.extend(a + b)
188
 
    return scenarios
189
 
 
190
 
 
191
 
# FIXME: Get rid of parametrized (in the class name) once we delete
192
 
# TestResolveConflicts -- vila 20100308
193
 
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
194
 
    """This class provides a base to test single conflict resolution.
195
 
 
196
 
    Since all conflict objects are created with specific semantics for their
197
 
    attributes, each class should implement the necessary functions and
198
 
    attributes described below.
199
 
 
200
 
    Each class should define the scenarios that create the expected (single)
201
 
    conflict.
202
 
 
203
 
    Each scenario describes:
204
 
    * how to create 'base' tree (and revision)
205
 
    * how to create 'left' tree (and revision, parent rev 'base')
206
 
    * how to create 'right' tree (and revision, parent rev 'base')
207
 
    * how to check that changes in 'base'->'left' have been taken
208
 
    * how to check that changes in 'base'->'right' have been taken
209
 
 
210
 
    From each base scenario, we generate two concrete scenarios where:
211
 
    * this=left, other=right
212
 
    * this=right, other=left
213
 
 
214
 
    Then the test case verifies each concrete scenario by:
215
 
    * creating a branch containing the 'base', 'this' and 'other' revisions
216
 
    * creating a working tree for the 'this' revision
217
 
    * performing the merge of 'other' into 'this'
218
 
    * verifying the expected conflict was generated
219
 
    * resolving with --take-this or --take-other, and running the corresponding
220
 
      checks (for either 'base'->'this', or 'base'->'other')
221
 
 
222
 
    :cvar _conflict_type: The expected class of the generated conflict.
223
 
 
224
 
    :cvar _assert_conflict: A method receiving the working tree and the
225
 
        conflict object and checking its attributes.
226
 
 
227
 
    :cvar _base_actions: The branchbuilder actions to create the 'base'
228
 
        revision.
229
 
 
230
 
    :cvar _this: The dict related to 'base' -> 'this'. It contains at least:
231
 
      * 'actions': The branchbuilder actions to create the 'this'
232
 
          revision.
233
 
      * 'check': how to check the changes after resolution with --take-this.
234
 
 
235
 
    :cvar _other: The dict related to 'base' -> 'other'. It contains at least:
236
 
      * 'actions': The branchbuilder actions to create the 'other'
237
 
          revision.
238
 
      * 'check': how to check the changes after resolution with --take-other.
239
 
    """
240
 
 
241
 
    # Set by daughter classes
242
 
    _conflict_type = None
243
 
    _assert_conflict = None
244
 
 
245
 
    # Set by load_tests
246
 
    _base_actions = None
247
 
    _this = None
248
 
    _other = None
249
 
 
250
 
    scenarios = []
251
 
    """The scenario list for the conflict type defined by the class.
252
 
 
253
 
    Each scenario is of the form:
254
 
    (common, (left_name, left_dict), (right_name, right_dict))
255
 
 
256
 
    * common is a dict
257
 
 
258
 
    * left_name and right_name are the scenario names that will be combined
259
 
 
260
 
    * left_dict and right_dict are the attributes specific to each half of
261
 
      the scenario. They should include at least 'actions' and 'check' and
262
 
      will be available as '_this' and '_other' test instance attributes.
263
 
 
264
 
    Daughters classes are free to add their specific attributes as they see
265
 
    fit in any of the three dicts.
266
 
 
267
 
    This is a class method so that load_tests can find it.
268
 
 
269
 
    '_base_actions' in the common dict, 'actions' and 'check' in the left
270
 
    and right dicts use names that map to methods in the test classes. Some
271
 
    prefixes are added to these names to get the correspong methods (see
272
 
    _get_actions() and _get_check()). The motivation here is to avoid
273
 
    collisions in the class namespace.
274
 
    """
275
 
 
276
 
    def setUp(self):
277
 
        super(TestParametrizedResolveConflicts, self).setUp()
278
 
        builder = self.make_branch_builder('trunk')
279
 
        builder.start_series()
280
 
 
281
 
        # Create an empty trunk
282
 
        builder.build_snapshot('start', None, [
283
 
                ('add', ('', 'root-id', 'directory', ''))])
284
 
        # Add a minimal base content
285
 
        base_actions = self._get_actions(self._base_actions)()
286
 
        builder.build_snapshot('base', ['start'], base_actions)
287
 
        # Modify the base content in branch
288
 
        actions_other = self._get_actions(self._other['actions'])()
289
 
        builder.build_snapshot('other', ['base'], actions_other)
290
 
        # Modify the base content in trunk
291
 
        actions_this = self._get_actions(self._this['actions'])()
292
 
        builder.build_snapshot('this', ['base'], actions_this)
293
 
        # builder.get_branch() tip is now 'this'
294
 
 
295
 
        builder.finish_series()
296
 
        self.builder = builder
297
 
 
298
 
    def _get_actions(self, name):
299
 
        return getattr(self, 'do_%s' % name)
300
 
 
301
 
    def _get_check(self, name):
302
 
        return getattr(self, 'check_%s' % name)
303
 
 
304
 
    def _merge_other_into_this(self):
305
 
        b = self.builder.get_branch()
306
 
        wt = b.bzrdir.sprout('branch').open_workingtree()
307
 
        wt.merge_from_branch(b, 'other')
308
 
        return wt
309
 
 
310
 
    def assertConflict(self, wt):
311
 
        confs = wt.conflicts()
312
 
        self.assertLength(1, confs)
313
 
        c = confs[0]
314
 
        self.assertIsInstance(c, self._conflict_type)
315
 
        self._assert_conflict(wt, c)
316
 
 
317
 
    def _get_resolve_path_arg(self, wt, action):
318
 
        raise NotImplementedError(self._get_resolve_path_arg)
319
 
 
320
 
    def check_resolved(self, wt, action):
321
 
        path = self._get_resolve_path_arg(wt, action)
322
 
        conflicts.resolve(wt, [path], action=action)
323
 
        # Check that we don't have any conflicts nor unknown left
324
 
        self.assertLength(0, wt.conflicts())
325
 
        self.assertLength(0, list(wt.unknowns()))
326
 
 
327
 
    def test_resolve_taking_this(self):
328
 
        wt = self._merge_other_into_this()
329
 
        self.assertConflict(wt)
330
 
        self.check_resolved(wt, 'take_this')
331
 
        check_this = self._get_check(self._this['check'])
332
 
        check_this()
333
 
 
334
 
    def test_resolve_taking_other(self):
335
 
        wt = self._merge_other_into_this()
336
 
        self.assertConflict(wt)
337
 
        self.check_resolved(wt, 'take_other')
338
 
        check_other = self._get_check(self._other['check'])
339
 
        check_other()
340
 
 
341
 
 
342
 
class TestResolveTextConflicts(TestParametrizedResolveConflicts):
343
 
 
344
 
    _conflict_type = conflicts.TextConflict
345
 
 
346
 
    # Set by the scenarios
347
 
    # path and file-id for the file involved in the conflict
348
 
    _path = None
349
 
    _file_id = None
350
 
 
351
 
    scenarios = mirror_scenarios(
352
 
        [
353
 
            # File modified on both sides
354
 
            (dict(_base_actions='create_file',
355
 
                  _path='file', _file_id='file-id'),
356
 
             ('filed_modified_A',
357
 
              dict(actions='modify_file_A', check='file_has_content_A')),
358
 
             ('file_modified_B',
359
 
              dict(actions='modify_file_B', check='file_has_content_B')),),
360
 
            # File modified on both sides in dir
361
 
            (dict(_base_actions='create_file_in_dir',
362
 
                  _path='dir/file', _file_id='file-id'),
363
 
             ('filed_modified_A_in_dir',
364
 
              dict(actions='modify_file_A',
365
 
                   check='file_in_dir_has_content_A')),
366
 
             ('file_modified_B',
367
 
              dict(actions='modify_file_B',
368
 
                   check='file_in_dir_has_content_B')),),
369
 
            ])
370
 
 
371
 
    def do_create_file(self, path='file'):
372
 
        return [('add', (path, 'file-id', 'file', 'trunk content\n'))]
373
 
 
374
 
    def do_modify_file_A(self):
375
 
        return [('modify', ('file-id', 'trunk content\nfeature A\n'))]
376
 
 
377
 
    def do_modify_file_B(self):
378
 
        return [('modify', ('file-id', 'trunk content\nfeature B\n'))]
379
 
 
380
 
    def check_file_has_content_A(self, path='file'):
381
 
        self.assertFileEqual('trunk content\nfeature A\n',
382
 
                             osutils.pathjoin('branch', path))
383
 
 
384
 
    def check_file_has_content_B(self, path='file'):
385
 
        self.assertFileEqual('trunk content\nfeature B\n',
386
 
                             osutils.pathjoin('branch', path))
387
 
 
388
 
    def do_create_file_in_dir(self):
389
 
        return [('add', ('dir', 'dir-id', 'directory', '')),
390
 
            ] + self.do_create_file('dir/file')
391
 
 
392
 
    def check_file_in_dir_has_content_A(self):
393
 
        self.check_file_has_content_A('dir/file')
394
 
 
395
 
    def check_file_in_dir_has_content_B(self):
396
 
        self.check_file_has_content_B('dir/file')
397
 
 
398
 
    def _get_resolve_path_arg(self, wt, action):
399
 
        return self._path
400
 
 
401
 
    def assertTextConflict(self, wt, c):
402
 
        self.assertEqual(self._file_id, c.file_id)
403
 
        self.assertEqual(self._path, c.path)
404
 
    _assert_conflict = assertTextConflict
405
 
 
406
 
 
407
 
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
408
 
 
409
 
    _conflict_type = conflicts.ContentsConflict
410
 
 
411
 
    # Set by the scenarios
412
 
    # path and file-id for the file involved in the conflict
413
 
    _path = None
414
 
    _file_id = None
415
 
 
416
 
    scenarios = mirror_scenarios(
417
 
        [
418
 
            # File modified/deleted
419
 
            (dict(_base_actions='create_file',
420
 
                  _path='file', _file_id='file-id'),
421
 
             ('file_modified',
422
 
              dict(actions='modify_file', check='file_has_more_content')),
423
 
             ('file_deleted',
424
 
              dict(actions='delete_file', check='file_doesnt_exist')),),
425
 
            # File modified/deleted in dir
426
 
            (dict(_base_actions='create_file_in_dir',
427
 
                  _path='dir/file', _file_id='file-id'),
428
 
             ('file_modified_in_dir',
429
 
              dict(actions='modify_file_in_dir',
430
 
                   check='file_in_dir_has_more_content')),
431
 
             ('file_deleted_in_dir',
432
 
              dict(actions='delete_file',
433
 
                   check='file_in_dir_doesnt_exist')),),
434
 
            ])
435
 
 
436
 
    def do_create_file(self):
437
 
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
438
 
 
439
 
    def do_modify_file(self):
440
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
441
 
 
442
 
    def check_file_has_more_content(self):
443
 
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
444
 
 
445
 
    def do_delete_file(self):
446
 
        return [('unversion', 'file-id')]
447
 
 
448
 
    def check_file_doesnt_exist(self):
449
 
        self.assertPathDoesNotExist('branch/file')
450
 
 
451
 
    def do_create_file_in_dir(self):
452
 
        return [('add', ('dir', 'dir-id', 'directory', '')),
453
 
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
454
 
 
455
 
    def do_modify_file_in_dir(self):
456
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
457
 
 
458
 
    def check_file_in_dir_has_more_content(self):
459
 
        self.assertFileEqual('trunk content\nmore content\n', 'branch/dir/file')
460
 
 
461
 
    def check_file_in_dir_doesnt_exist(self):
462
 
        self.assertPathDoesNotExist('branch/dir/file')
463
 
 
464
 
    def _get_resolve_path_arg(self, wt, action):
465
 
        return self._path
466
 
 
467
 
    def assertContentsConflict(self, wt, c):
468
 
        self.assertEqual(self._file_id, c.file_id)
469
 
        self.assertEqual(self._path, c.path)
470
 
    _assert_conflict = assertContentsConflict
471
 
 
472
 
 
473
 
class TestResolvePathConflict(TestParametrizedResolveConflicts):
474
 
 
475
 
    _conflict_type = conflicts.PathConflict
476
 
 
477
 
    def do_nothing(self):
478
 
        return []
479
 
 
480
 
    # Each side dict additionally defines:
481
 
    # - path path involved (can be '<deleted>')
482
 
    # - file-id involved
483
 
    scenarios = mirror_scenarios(
484
 
        [
485
 
            # File renamed/deleted
486
 
            (dict(_base_actions='create_file'),
487
 
             ('file_renamed',
488
 
              dict(actions='rename_file', check='file_renamed',
489
 
                   path='new-file', file_id='file-id')),
490
 
             ('file_deleted',
491
 
              dict(actions='delete_file', check='file_doesnt_exist',
492
 
                   # PathConflicts deletion handling requires a special
493
 
                   # hard-coded value
494
 
                   path='<deleted>', file_id='file-id')),),
495
 
            # File renamed/deleted in dir
496
 
            (dict(_base_actions='create_file_in_dir'),
497
 
             ('file_renamed_in_dir',
498
 
              dict(actions='rename_file_in_dir', check='file_in_dir_renamed',
499
 
                   path='dir/new-file', file_id='file-id')),
500
 
             ('file_deleted',
501
 
              dict(actions='delete_file', check='file_in_dir_doesnt_exist',
502
 
                   # PathConflicts deletion handling requires a special
503
 
                   # hard-coded value
504
 
                   path='<deleted>', file_id='file-id')),),
505
 
            # File renamed/renamed differently
506
 
            (dict(_base_actions='create_file'),
507
 
             ('file_renamed',
508
 
              dict(actions='rename_file', check='file_renamed',
509
 
                   path='new-file', file_id='file-id')),
510
 
             ('file_renamed2',
511
 
              dict(actions='rename_file2', check='file_renamed2',
512
 
                   path='new-file2', file_id='file-id')),),
513
 
            # Dir renamed/deleted
514
 
            (dict(_base_actions='create_dir'),
515
 
             ('dir_renamed',
516
 
              dict(actions='rename_dir', check='dir_renamed',
517
 
                   path='new-dir', file_id='dir-id')),
518
 
             ('dir_deleted',
519
 
              dict(actions='delete_dir', check='dir_doesnt_exist',
520
 
                   # PathConflicts deletion handling requires a special
521
 
                   # hard-coded value
522
 
                   path='<deleted>', file_id='dir-id')),),
523
 
            # Dir renamed/renamed differently
524
 
            (dict(_base_actions='create_dir'),
525
 
             ('dir_renamed',
526
 
              dict(actions='rename_dir', check='dir_renamed',
527
 
                   path='new-dir', file_id='dir-id')),
528
 
             ('dir_renamed2',
529
 
              dict(actions='rename_dir2', check='dir_renamed2',
530
 
                   path='new-dir2', file_id='dir-id')),),
531
 
            ])
532
 
 
533
 
    def do_create_file(self):
534
 
        return [('add', ('file', 'file-id', 'file', 'trunk content\n'))]
535
 
 
536
 
    def do_create_dir(self):
537
 
        return [('add', ('dir', 'dir-id', 'directory', ''))]
538
 
 
539
 
    def do_rename_file(self):
540
 
        return [('rename', ('file', 'new-file'))]
541
 
 
542
 
    def check_file_renamed(self):
543
 
        self.assertPathDoesNotExist('branch/file')
544
 
        self.assertPathExists('branch/new-file')
545
 
 
546
 
    def do_rename_file2(self):
547
 
        return [('rename', ('file', 'new-file2'))]
548
 
 
549
 
    def check_file_renamed2(self):
550
 
        self.assertPathDoesNotExist('branch/file')
551
 
        self.assertPathExists('branch/new-file2')
552
 
 
553
 
    def do_rename_dir(self):
554
 
        return [('rename', ('dir', 'new-dir'))]
555
 
 
556
 
    def check_dir_renamed(self):
557
 
        self.assertPathDoesNotExist('branch/dir')
558
 
        self.assertPathExists('branch/new-dir')
559
 
 
560
 
    def do_rename_dir2(self):
561
 
        return [('rename', ('dir', 'new-dir2'))]
562
 
 
563
 
    def check_dir_renamed2(self):
564
 
        self.assertPathDoesNotExist('branch/dir')
565
 
        self.assertPathExists('branch/new-dir2')
566
 
 
567
 
    def do_delete_file(self):
568
 
        return [('unversion', 'file-id')]
569
 
 
570
 
    def check_file_doesnt_exist(self):
571
 
        self.assertPathDoesNotExist('branch/file')
572
 
 
573
 
    def do_delete_dir(self):
574
 
        return [('unversion', 'dir-id')]
575
 
 
576
 
    def check_dir_doesnt_exist(self):
577
 
        self.assertPathDoesNotExist('branch/dir')
578
 
 
579
 
    def do_create_file_in_dir(self):
580
 
        return [('add', ('dir', 'dir-id', 'directory', '')),
581
 
                ('add', ('dir/file', 'file-id', 'file', 'trunk content\n'))]
582
 
 
583
 
    def do_rename_file_in_dir(self):
584
 
        return [('rename', ('dir/file', 'dir/new-file'))]
585
 
 
586
 
    def check_file_in_dir_renamed(self):
587
 
        self.assertPathDoesNotExist('branch/dir/file')
588
 
        self.assertPathExists('branch/dir/new-file')
589
 
 
590
 
    def check_file_in_dir_doesnt_exist(self):
591
 
        self.assertPathDoesNotExist('branch/dir/file')
592
 
 
593
 
    def _get_resolve_path_arg(self, wt, action):
594
 
        tpath = self._this['path']
595
 
        opath = self._other['path']
596
 
        if tpath == '<deleted>':
597
 
            path = opath
598
 
        else:
599
 
            path = tpath
600
 
        return path
601
 
 
602
 
    def assertPathConflict(self, wt, c):
603
 
        tpath = self._this['path']
604
 
        tfile_id = self._this['file_id']
605
 
        opath = self._other['path']
606
 
        ofile_id = self._other['file_id']
607
 
        self.assertEqual(tfile_id, ofile_id) # Sanity check
608
 
        self.assertEqual(tfile_id, c.file_id)
609
 
        self.assertEqual(tpath, c.path)
610
 
        self.assertEqual(opath, c.conflict_path)
611
 
    _assert_conflict = assertPathConflict
612
 
 
613
 
 
614
 
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
615
 
    """Same as TestResolvePathConflict but a specific conflict object.
616
 
    """
617
 
 
618
 
    def assertPathConflict(self, c):
619
 
        # We create a conflict object as it was created before the fix and
620
 
        # inject it into the working tree, the test will exercise the
621
 
        # compatibility code.
622
 
        old_c = conflicts.PathConflict('<deleted>', self._item_path,
623
 
                                       file_id=None)
624
 
        wt.set_conflicts(conflicts.ConflictList([old_c]))
625
 
 
626
 
 
627
 
class TestResolveDuplicateEntry(TestParametrizedResolveConflicts):
628
 
 
629
 
    _conflict_type = conflicts.DuplicateEntry
630
 
 
631
 
    scenarios = mirror_scenarios(
632
 
        [
633
 
            # File created with different file-ids
634
 
            (dict(_base_actions='nothing'),
635
 
             ('filea_created',
636
 
              dict(actions='create_file_a', check='file_content_a',
637
 
                   path='file', file_id='file-a-id')),
638
 
             ('fileb_created',
639
 
              dict(actions='create_file_b', check='file_content_b',
640
 
                   path='file', file_id='file-b-id')),),
641
 
            ])
642
 
 
643
 
    def do_nothing(self):
644
 
        return []
645
 
 
646
 
    def do_create_file_a(self):
647
 
        return [('add', ('file', 'file-a-id', 'file', 'file a content\n'))]
648
 
 
649
 
    def check_file_content_a(self):
650
 
        self.assertFileEqual('file a content\n', 'branch/file')
651
 
 
652
 
    def do_create_file_b(self):
653
 
        return [('add', ('file', 'file-b-id', 'file', 'file b content\n'))]
654
 
 
655
 
    def check_file_content_b(self):
656
 
        self.assertFileEqual('file b content\n', 'branch/file')
657
 
 
658
 
    def _get_resolve_path_arg(self, wt, action):
659
 
        return self._this['path']
660
 
 
661
 
    def assertDuplicateEntry(self, wt, c):
662
 
        tpath = self._this['path']
663
 
        tfile_id = self._this['file_id']
664
 
        opath = self._other['path']
665
 
        ofile_id = self._other['file_id']
666
 
        self.assertEqual(tpath, opath) # Sanity check
667
 
        self.assertEqual(tfile_id, c.file_id)
668
 
        self.assertEqual(tpath + '.moved', c.path)
669
 
        self.assertEqual(tpath, c.conflict_path)
670
 
    _assert_conflict = assertDuplicateEntry
671
 
 
672
 
 
673
 
class TestResolveUnversionedParent(TestResolveConflicts):
674
 
 
675
 
    # FIXME: Add the reverse tests: dir deleted in trunk, file added in branch
676
 
 
677
 
    # FIXME: While this *creates* UnversionedParent conflicts, this really only
678
 
    # tests MissingParent resolution :-/
679
 
    preamble = """
680
 
$ bzr init trunk
681
 
...
682
 
$ cd trunk
683
 
$ mkdir dir
684
 
$ bzr add -q dir
685
 
$ bzr commit -m 'Create trunk' -q
686
 
$ echo 'trunk content' >dir/file
687
 
$ bzr add -q dir/file
688
 
$ bzr commit -q -m 'Add dir/file in trunk'
689
 
$ bzr branch -q . -r 1 ../branch
690
 
$ cd ../branch
691
 
$ bzr rm dir -q
692
 
$ bzr commit -q -m 'Remove dir in branch'
693
 
$ bzr merge ../trunk
694
 
2>+N  dir/
695
 
2>+N  dir/file
696
 
2>Conflict adding files to dir.  Created directory.
697
 
2>Conflict because dir is not versioned, but has versioned children.  Versioned directory.
698
 
2>2 conflicts encountered.
699
 
"""
700
 
 
701
 
    def test_take_this(self):
702
 
        self.run_script("""
703
 
$ bzr rm -q dir  --force
704
 
$ bzr resolve dir
705
 
2>2 conflict(s) resolved, 0 remaining
706
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
707
 
""")
708
 
 
709
 
    def test_take_other(self):
710
 
        self.run_script("""
711
 
$ bzr resolve dir
712
 
2>2 conflict(s) resolved, 0 remaining
713
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
714
 
""")
715
 
 
716
 
 
717
 
class TestResolveMissingParent(TestResolveConflicts):
718
 
 
719
 
    preamble = """
720
 
$ bzr init trunk
721
 
...
722
 
$ cd trunk
723
 
$ mkdir dir
724
 
$ echo 'trunk content' >dir/file
725
 
$ bzr add -q
726
 
$ bzr commit -m 'Create trunk' -q
727
 
$ echo 'trunk content' >dir/file2
728
 
$ bzr add -q dir/file2
729
 
$ bzr commit -q -m 'Add dir/file2 in branch'
730
 
$ bzr branch -q . -r 1 ../branch
731
 
$ cd ../branch
732
 
$ bzr rm -q dir/file --force
733
 
$ bzr rm -q dir
734
 
$ bzr commit -q -m 'Remove dir/file'
735
 
$ bzr merge ../trunk
736
 
2>+N  dir/
737
 
2>+N  dir/file2
738
 
2>Conflict adding files to dir.  Created directory.
739
 
2>Conflict because dir is not versioned, but has versioned children.  Versioned directory.
740
 
2>2 conflicts encountered.
741
 
"""
742
 
 
743
 
    def test_keep_them_all(self):
744
 
        self.run_script("""
745
 
$ bzr resolve dir
746
 
2>2 conflict(s) resolved, 0 remaining
747
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
748
 
""")
749
 
 
750
 
    def test_adopt_child(self):
751
 
        self.run_script("""
752
 
$ bzr mv -q dir/file2 file2
753
 
$ bzr rm -q dir --force
754
 
$ bzr resolve dir
755
 
2>2 conflict(s) resolved, 0 remaining
756
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
757
 
""")
758
 
 
759
 
    def test_kill_them_all(self):
760
 
        self.run_script("""
761
 
$ bzr rm -q dir --force
762
 
$ bzr resolve dir
763
 
2>2 conflict(s) resolved, 0 remaining
764
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
765
 
""")
766
 
 
767
 
    def test_resolve_taking_this(self):
768
 
        self.run_script("""
769
 
$ bzr resolve --take-this dir
770
 
2>...
771
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
772
 
""")
773
 
 
774
 
    def test_resolve_taking_other(self):
775
 
        self.run_script("""
776
 
$ bzr resolve --take-other dir
777
 
2>...
778
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
779
 
""")
780
 
 
781
 
 
782
 
class TestResolveDeletingParent(TestResolveConflicts):
783
 
 
784
 
    preamble = """
785
 
$ bzr init trunk
786
 
...
787
 
$ cd trunk
788
 
$ mkdir dir
789
 
$ echo 'trunk content' >dir/file
790
 
$ bzr add -q
791
 
$ bzr commit -m 'Create trunk' -q
792
 
$ bzr rm -q dir/file --force
793
 
$ bzr rm -q dir --force
794
 
$ bzr commit -q -m 'Remove dir/file'
795
 
$ bzr branch -q . -r 1 ../branch
796
 
$ cd ../branch
797
 
$ echo 'branch content' >dir/file2
798
 
$ bzr add -q dir/file2
799
 
$ bzr commit -q -m 'Add dir/file2 in branch'
800
 
$ bzr merge ../trunk
801
 
2>-D  dir/file
802
 
2>Conflict: can't delete dir because it is not empty.  Not deleting.
803
 
2>Conflict because dir is not versioned, but has versioned children.  Versioned directory.
804
 
2>2 conflicts encountered.
805
 
"""
806
 
 
807
 
    def test_keep_them_all(self):
808
 
        self.run_script("""
809
 
$ bzr resolve dir
810
 
2>2 conflict(s) resolved, 0 remaining
811
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
812
 
""")
813
 
 
814
 
    def test_adopt_child(self):
815
 
        self.run_script("""
816
 
$ bzr mv -q dir/file2 file2
817
 
$ bzr rm -q dir --force
818
 
$ bzr resolve dir
819
 
2>2 conflict(s) resolved, 0 remaining
820
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
821
 
""")
822
 
 
823
 
    def test_kill_them_all(self):
824
 
        self.run_script("""
825
 
$ bzr rm -q dir --force
826
 
$ bzr resolve dir
827
 
2>2 conflict(s) resolved, 0 remaining
828
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
829
 
""")
830
 
 
831
 
    def test_resolve_taking_this(self):
832
 
        self.run_script("""
833
 
$ bzr resolve --take-this dir
834
 
2>2 conflict(s) resolved, 0 remaining
835
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
836
 
""")
837
 
 
838
 
    def test_resolve_taking_other(self):
839
 
        self.run_script("""
840
 
$ bzr resolve --take-other dir
841
 
2>deleted dir/file2
842
 
2>deleted dir
843
 
2>2 conflict(s) resolved, 0 remaining
844
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
845
 
""")
846
 
 
847
 
 
848
 
class TestResolveParentLoop(TestParametrizedResolveConflicts):
849
 
 
850
 
    _conflict_type = conflicts.ParentLoop
851
 
 
852
 
    _this_args = None
853
 
    _other_args = None
854
 
 
855
 
    # Each side dict additionally defines:
856
 
    # - dir_id: the directory being moved
857
 
    # - target_id: The target directory
858
 
    # - xfail: whether the test is expected to fail if the action is
859
 
    #   involved as 'other'
860
 
    scenarios = mirror_scenarios(
861
 
        [
862
 
            # Dirs moved into each other
863
 
            (dict(_base_actions='create_dir1_dir2'),
864
 
             ('dir1_into_dir2',
865
 
              dict(actions='move_dir1_into_dir2', check='dir1_moved',
866
 
                   dir_id='dir1-id', target_id='dir2-id', xfail=False)),
867
 
             ('dir2_into_dir1',
868
 
              dict(actions='move_dir2_into_dir1', check='dir2_moved',
869
 
                   dir_id='dir2-id', target_id='dir1-id', xfail=False))),
870
 
            # Subdirs moved into each other
871
 
            (dict(_base_actions='create_dir1_4'),
872
 
             ('dir1_into_dir4',
873
 
              dict(actions='move_dir1_into_dir4', check='dir1_2_moved',
874
 
                   dir_id='dir1-id', target_id='dir4-id', xfail=True)),
875
 
             ('dir3_into_dir2',
876
 
              dict(actions='move_dir3_into_dir2', check='dir3_4_moved',
877
 
                   dir_id='dir3-id', target_id='dir2-id', xfail=True))),
878
 
            ])
879
 
 
880
 
    def do_create_dir1_dir2(self):
881
 
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
882
 
                ('add', ('dir2', 'dir2-id', 'directory', '')),]
883
 
 
884
 
    def do_move_dir1_into_dir2(self):
885
 
        return [('rename', ('dir1', 'dir2/dir1'))]
886
 
 
887
 
    def check_dir1_moved(self):
888
 
        self.assertPathDoesNotExist('branch/dir1')
889
 
        self.assertPathExists('branch/dir2/dir1')
890
 
 
891
 
    def do_move_dir2_into_dir1(self):
892
 
        return [('rename', ('dir2', 'dir1/dir2'))]
893
 
 
894
 
    def check_dir2_moved(self):
895
 
        self.assertPathDoesNotExist('branch/dir2')
896
 
        self.assertPathExists('branch/dir1/dir2')
897
 
 
898
 
    def do_create_dir1_4(self):
899
 
        return [('add', ('dir1', 'dir1-id', 'directory', '')),
900
 
                ('add', ('dir1/dir2', 'dir2-id', 'directory', '')),
901
 
                ('add', ('dir3', 'dir3-id', 'directory', '')),
902
 
                ('add', ('dir3/dir4', 'dir4-id', 'directory', '')),]
903
 
 
904
 
    def do_move_dir1_into_dir4(self):
905
 
        return [('rename', ('dir1', 'dir3/dir4/dir1'))]
906
 
 
907
 
    def check_dir1_2_moved(self):
908
 
        self.assertPathDoesNotExist('branch/dir1')
909
 
        self.assertPathExists('branch/dir3/dir4/dir1')
910
 
        self.assertPathExists('branch/dir3/dir4/dir1/dir2')
911
 
 
912
 
    def do_move_dir3_into_dir2(self):
913
 
        return [('rename', ('dir3', 'dir1/dir2/dir3'))]
914
 
 
915
 
    def check_dir3_4_moved(self):
916
 
        self.assertPathDoesNotExist('branch/dir3')
917
 
        self.assertPathExists('branch/dir1/dir2/dir3')
918
 
        self.assertPathExists('branch/dir1/dir2/dir3/dir4')
919
 
 
920
 
    def _get_resolve_path_arg(self, wt, action):
921
 
        # ParentLoop says: moving <conflict_path> into <path>. Cancelled move.
922
 
        # But since <path> doesn't exist in the working tree, we need to use
923
 
        # <conflict_path> instead, and that, in turn, is given by dir_id. Pfew.
924
 
        return wt.id2path(self._other['dir_id'])
925
 
 
926
 
    def assertParentLoop(self, wt, c):
927
 
        self.assertEqual(self._other['dir_id'], c.file_id)
928
 
        self.assertEqual(self._other['target_id'], c.conflict_file_id)
929
 
        # The conflict paths are irrelevant (they are deterministic but not
930
 
        # worth checking since they don't provide the needed information
931
 
        # anyway)
932
 
        if self._other['xfail']:
933
 
            # It's a bit hackish to raise from here relying on being called for
934
 
            # both tests but this avoid overriding test_resolve_taking_other
935
 
            raise tests.KnownFailure(
936
 
                "ParentLoop doesn't carry enough info to resolve --take-other")
937
 
    _assert_conflict = assertParentLoop
938
 
 
939
 
 
940
 
class TestResolveNonDirectoryParent(TestResolveConflicts):
941
 
 
942
 
    preamble = """
943
 
$ bzr init trunk
944
 
...
945
 
$ cd trunk
946
 
$ bzr mkdir foo
947
 
...
948
 
$ bzr commit -m 'Create trunk' -q
949
 
$ echo "Boing" >foo/bar
950
 
$ bzr add -q foo/bar
951
 
$ bzr commit -q -m 'Add foo/bar'
952
 
$ bzr branch -q . -r 1 ../branch
953
 
$ cd ../branch
954
 
$ rm -r foo
955
 
$ echo "Boo!" >foo
956
 
$ bzr commit -q -m 'foo is now a file'
957
 
$ bzr merge ../trunk
958
 
2>+N  foo.new/bar
959
 
2>RK  foo => foo.new/
960
 
# FIXME: The message is misleading, foo.new *is* a directory when the message
961
 
# is displayed -- vila 090916
962
 
2>Conflict: foo.new is not a directory, but has files in it.  Created directory.
963
 
2>1 conflicts encountered.
964
 
"""
965
 
 
966
 
    def test_take_this(self):
967
 
        self.run_script("""
968
 
$ bzr rm -q foo.new --force
969
 
# FIXME: Isn't it weird that foo is now unkown even if foo.new has been put
970
 
# aside ? -- vila 090916
971
 
$ bzr add -q foo
972
 
$ bzr resolve foo.new
973
 
2>1 conflict(s) resolved, 0 remaining
974
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
975
 
""")
976
 
 
977
 
    def test_take_other(self):
978
 
        self.run_script("""
979
 
$ bzr rm -q foo --force
980
 
$ bzr mv -q foo.new foo
981
 
$ bzr resolve foo
982
 
2>1 conflict(s) resolved, 0 remaining
983
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
984
 
""")
985
 
 
986
 
    def test_resolve_taking_this(self):
987
 
        self.run_script("""
988
 
$ bzr resolve --take-this foo.new
989
 
2>...
990
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
991
 
""")
992
 
 
993
 
    def test_resolve_taking_other(self):
994
 
        self.run_script("""
995
 
$ bzr resolve --take-other foo.new
996
 
2>...
997
 
$ bzr commit -q --strict -m 'No more conflicts nor unknown files'
998
 
""")
999
 
 
1000
 
 
1001
 
class TestMalformedTransform(script.TestCaseWithTransportAndScript):
1002
 
 
1003
 
    def test_bug_430129(self):
1004
 
        # This is nearly like TestResolveNonDirectoryParent but with branch and
1005
 
        # trunk switched. As such it should certainly produce the same
1006
 
        # conflict.
1007
 
        self.run_script("""
1008
 
$ bzr init trunk
1009
 
...
1010
 
$ cd trunk
1011
 
$ bzr mkdir foo
1012
 
...
1013
 
$ bzr commit -m 'Create trunk' -q
1014
 
$ rm -r foo
1015
 
$ echo "Boo!" >foo
1016
 
$ bzr commit -m 'foo is now a file' -q
1017
 
$ bzr branch -q . -r 1 ../branch -q
1018
 
$ cd ../branch
1019
 
$ echo "Boing" >foo/bar
1020
 
$ bzr add -q foo/bar -q
1021
 
$ bzr commit -m 'Add foo/bar' -q
1022
 
$ bzr merge ../trunk
1023
 
2>bzr: ERROR: Tree transform is malformed [('unversioned executability', 'new-1')]
1024
 
""")
1025
 
 
1026
 
 
1027
 
class TestResolveActionOption(tests.TestCase):
1028
 
 
1029
 
    def setUp(self):
1030
 
        super(TestResolveActionOption, self).setUp()
1031
 
        self.options = [conflicts.ResolveActionOption()]
1032
 
        self.parser = option.get_optparser(dict((o.name, o)
1033
 
                                                for o in self.options))
1034
 
 
1035
 
    def parse(self, args):
1036
 
        return self.parser.parse_args(args)
1037
 
 
1038
 
    def test_unknown_action(self):
1039
 
        self.assertRaises(errors.BadOptionValue,
1040
 
                          self.parse, ['--action', 'take-me-to-the-moon'])
1041
 
 
1042
 
    def test_done(self):
1043
 
        opts, args = self.parse(['--action', 'done'])
1044
 
        self.assertEqual({'action':'done'}, opts)
1045
 
 
1046
 
    def test_take_this(self):
1047
 
        opts, args = self.parse(['--action', 'take-this'])
1048
 
        self.assertEqual({'action': 'take_this'}, opts)
1049
 
        opts, args = self.parse(['--take-this'])
1050
 
        self.assertEqual({'action': 'take_this'}, opts)
1051
 
 
1052
 
    def test_take_other(self):
1053
 
        opts, args = self.parse(['--action', 'take-other'])
1054
 
        self.assertEqual({'action': 'take_other'}, opts)
1055
 
        opts, args = self.parse(['--take-other'])
1056
 
        self.assertEqual({'action': 'take_other'}, opts)