~greatmay12/+junk/test1

« back to all changes in this revision

Viewing changes to build/lib.linux-x86_64-2.6/bzrlib/tests/test_conflicts.py

  • Committer: thitipong at ndrsolution
  • Date: 2011-11-14 06:31:02 UTC
  • Revision ID: thitipong@ndrsolution.com-20111114063102-9obte3yfi2azku7d
ndr redirect version

Show diffs side-by-side

added added

removed removed

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