/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

  • Committer: John Arbash Meinel
  • Date: 2006-04-25 15:05:42 UTC
  • mfrom: (1185.85.85 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060425150542-c7b518dca9928691
[merge] the old bzr-encoding changes, reparenting them on bzr.dev

Show diffs side-by-side

added added

removed removed

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