~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_conflicts.py

merge fix for 531967

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
        standard_tests, tests.condition_isinstance((
37
37
                TestParametrizedResolveConflicts,
38
38
                )))
39
 
    tests.multiply_tests(sp_tests, resolve_conflict_scenarios(), result)
 
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)
40
44
 
41
45
    # No parametrization for the remaining tests
42
46
    result.addTests(remaining_tests)
194
198
# FIXME: The shell-like tests should be converted to real whitebox tests... or
195
199
# moved to a blackbox module -- vila 20100205
196
200
 
 
201
# FIXME: test missing for multiple conflicts
 
202
 
197
203
# FIXME: Tests missing for DuplicateID conflict type
198
204
class TestResolveConflicts(script.TestCaseWithTransportAndScript):
199
205
 
209
215
    pass
210
216
 
211
217
 
212
 
def resolve_conflict_scenarios():
213
 
    base_scenarios = [
214
 
        (dict(_conflict_type=conflicts.ContentsConflict,
215
 
              _item_path='file', _item_id='file-id',),
216
 
         ('file_modified', dict(actions='modify_file',
217
 
                                check='file_has_more_content')),
218
 
         ('file_deleted', dict(actions='delete_file',
219
 
                                check='file_doesnt_exist'))),
220
 
        (dict(_conflict_type=conflicts.PathConflict,
221
 
              _item_path='new-dir', _item_id='dir-id',),
222
 
         ('dir_renamed', dict(actions='rename_dir', check='dir_renamed')),
223
 
         ('dir_deleted', dict(actions='delete_dir', check='dir_doesnt_exist'))),
224
 
        ]
225
 
    # Each base scenario is duplicated switching the roles of this and other
226
 
    scenarios = []
227
 
    for common, (tname, tdict), (oname, odict) in base_scenarios:
228
 
        d = common.copy()
229
 
        d.update(_this_actions=tdict['actions'], _check_this=tdict['check'],
230
 
                 _other_actions=odict['actions'], _check_other=odict['check'])
231
 
        scenarios.append(('%s,%s' % (tname, oname), d))
232
 
        d = common.copy()
233
 
        d.update(_this_actions=odict['actions'], _check_this=odict['check'],
234
 
                 _other_actions=tdict['actions'], _check_other=tdict['check'])
235
 
        scenarios.append(('%s,%s' % (oname, tname), d))
236
 
    return scenarios
237
 
 
238
 
# FIXME: Get rid of parametrized once we delete TestResolveConflicts
 
218
# FIXME: Get rid of parametrized (in the class name) once we delete
 
219
# TestResolveConflicts -- vila 20100308
239
220
class TestParametrizedResolveConflicts(tests.TestCaseWithTransport):
240
221
 
 
222
    # Set by daughter classes
 
223
    _conflict_type = None
 
224
    _assert_conflict = None
 
225
 
241
226
    # Set by load_tests
 
227
    _base_actions = None
242
228
    _this_actions = None
243
229
    _other_actions = None
244
 
    _conflict_type = None
245
230
    _item_path = None
246
231
    _item_id = None
247
232
 
 
233
    # Set by _this_actions and other_actions
 
234
    _this_path = None
 
235
    _this_id = None
 
236
    _other_path = None
 
237
    _other_id = None
 
238
 
 
239
    @classmethod
 
240
    def mirror_scenarios(klass, base_scenarios):
 
241
        scenarios = []
 
242
        def adapt(d, side):
 
243
            """Modify dict to apply to the given side.
 
244
 
 
245
            'actions' key is turned into '_actions_this' if side is 'this' for
 
246
            example.
 
247
            """
 
248
            t = {}
 
249
            # Turn each key into _side_key
 
250
            for k,v in d.iteritems():
 
251
                t['_%s_%s' % (k, side)] = v
 
252
            return t
 
253
        # Each base scenario is duplicated switching the roles of 'this' and
 
254
        # 'other'
 
255
        left = [l for l, r, c in base_scenarios]
 
256
        right = [r for l, r, c in base_scenarios]
 
257
        common = [c for l, r, c in base_scenarios]
 
258
        for (lname, ldict), (rname, rdict), common in zip(left, right, common):
 
259
            a = tests.multiply_scenarios([(lname, adapt(ldict, 'this'))],
 
260
                                         [(rname, adapt(rdict, 'other'))])
 
261
            b = tests.multiply_scenarios(
 
262
                    [(rname, adapt(rdict, 'this'))],
 
263
                    [(lname, adapt(ldict, 'other'))])
 
264
            # Inject the common parameters in all scenarios
 
265
            for name, d in a + b:
 
266
                d.update(common)
 
267
            scenarios.extend(a + b)
 
268
        return scenarios
 
269
 
 
270
    @classmethod
 
271
    def scenarios(klass):
 
272
        # Only concrete classes return actual scenarios
 
273
        return []
 
274
 
248
275
    def setUp(self):
249
276
        super(TestParametrizedResolveConflicts, self).setUp()
250
277
        builder = self.make_branch_builder('trunk')
251
278
        builder.start_series()
 
279
 
252
280
        # Create an empty trunk
253
281
        builder.build_snapshot('start', None, [
254
282
                ('add', ('', 'root-id', 'directory', ''))])
255
283
        # Add a minimal base content
256
 
        builder.build_snapshot(
257
 
            'base', ['start'], [
258
 
                ('add', ('file', 'file-id', 'file', 'trunk content\n')),
259
 
                ('add', ('dir', 'dir-id', 'directory', '')),
260
 
                ])
 
284
        _, _, actions_base = self._get_actions(self._actions_base)()
 
285
        builder.build_snapshot('base', ['start'], actions_base)
261
286
        # Modify the base content in branch
262
 
        other_actions = self._get_actions(self._other_actions)
263
 
        builder.build_snapshot('other', ['base'], other_actions())
 
287
        (self._other_path, self._other_id,
 
288
         actions_other) = self._get_actions(self._actions_other)()
 
289
        builder.build_snapshot('other', ['base'], actions_other)
264
290
        # Modify the base content in trunk
265
 
        this_actions = self._get_actions(self._this_actions)
266
 
        builder.build_snapshot('this', ['base'], this_actions())
 
291
        (self._this_path, self._this_id,
 
292
         actions_this) = self._get_actions(self._actions_this)()
 
293
        builder.build_snapshot('this', ['base'], actions_this)
 
294
        # builder.get_branch() tip is now 'this'
 
295
 
267
296
        builder.finish_series()
268
297
        self.builder = builder
269
298
 
273
302
    def _get_check(self, name):
274
303
        return getattr(self, 'check_%s' % name)
275
304
 
276
 
    def assertConflict(self, wt, **kwargs):
 
305
    def assertConflict(self, wt):
277
306
        confs = wt.conflicts()
278
307
        self.assertLength(1, confs)
279
308
        c = confs[0]
280
309
        self.assertIsInstance(c, self._conflict_type)
281
 
        sentinel = object() # An impossible value
282
 
        for k, v in kwargs.iteritems():
283
 
            self.assertEqual(v, getattr(c, k, sentinel), "for key '%s'" % k)
 
310
        self._assert_conflict(wt, c)
284
311
 
285
 
    def check_resolved(self, wt, item, action):
286
 
        conflicts.resolve(wt, [item], action=action)
 
312
    def check_resolved(self, wt, path, action):
 
313
        conflicts.resolve(wt, [path], action=action)
287
314
        # Check that we don't have any conflicts nor unknown left
288
315
        self.assertLength(0, wt.conflicts())
289
316
        self.assertLength(0, list(wt.unknowns()))
290
317
 
 
318
    def do_create_file(self):
 
319
        return ('file', 'file-id',
 
320
                [('add', ('file', 'file-id', 'file', 'trunk content\n'))])
 
321
 
 
322
    def do_create_dir(self):
 
323
        return ('dir', 'dir-id', [('add', ('dir', 'dir-id', 'directory', ''))])
 
324
 
291
325
    def do_modify_file(self):
292
 
        return [('modify', ('file-id', 'trunk content\nmore content\n'))]
 
326
        return ('file', 'file-id',
 
327
                [('modify', ('file-id', 'trunk content\nmore content\n'))])
293
328
 
294
329
    def check_file_has_more_content(self):
295
330
        self.assertFileEqual('trunk content\nmore content\n', 'branch/file')
296
331
 
297
332
    def do_delete_file(self):
298
 
        return [('unversion', 'file-id')]
 
333
        return ('file', 'file-id', [('unversion', 'file-id')])
299
334
 
300
335
    def check_file_doesnt_exist(self):
301
336
        self.failIfExists('branch/file')
302
337
 
 
338
    def do_rename_file(self):
 
339
        return ('new-file', 'file-id', [('rename', ('file', 'new-file'))])
 
340
 
 
341
    def check_file_renamed(self):
 
342
        self.failIfExists('branch/file')
 
343
        self.failUnlessExists('branch/new-file')
 
344
 
303
345
    def do_rename_dir(self):
304
 
        return [('rename', ('dir', 'new-dir'))]
 
346
        return ('new-dir', 'dir-id', [('rename', ('dir', 'new-dir'))])
305
347
 
306
348
    def check_dir_renamed(self):
307
349
        self.failIfExists('branch/dir')
308
350
        self.failUnlessExists('branch/new-dir')
309
351
 
 
352
    def do_rename_dir2(self):
 
353
        return ('new-dir2', 'dir-id', [('rename', ('dir', 'new-dir2'))])
 
354
 
 
355
    def check_dir_renamed2(self):
 
356
        self.failIfExists('branch/dir')
 
357
        self.failUnlessExists('branch/new-dir2')
 
358
 
310
359
    def do_delete_dir(self):
311
 
        return [('unversion', 'dir-id')]
 
360
        return ('<deleted>', 'dir-id', [('unversion', 'dir-id')])
312
361
 
313
362
    def check_dir_doesnt_exist(self):
314
363
        self.failIfExists('branch/dir')
321
370
 
322
371
    def test_resolve_taking_this(self):
323
372
        wt = self._merge_other_into_this()
324
 
        self.assertConflict(wt, path=self._item_path, file_id=self._item_id)
 
373
        self.assertConflict(wt)
325
374
        self.check_resolved(wt, self._item_path, 'take_this')
326
375
        check_this = self._get_check(self._check_this)
327
376
        check_this()
328
377
 
329
378
    def test_resolve_taking_other(self):
330
379
        wt = self._merge_other_into_this()
331
 
        self.assertConflict(wt, path=self._item_path, file_id=self._item_id)
 
380
        self.assertConflict(wt)
332
381
        self.check_resolved(wt, self._item_path, 'take_other')
333
382
        check_other = self._get_check(self._check_other)
334
383
        check_other()
335
384
 
336
385
 
 
386
class TestResolveContentsConflict(TestParametrizedResolveConflicts):
 
387
 
 
388
    _conflict_type = conflicts.ContentsConflict,
 
389
    @classmethod
 
390
    def scenarios(klass):
 
391
        common = dict(_actions_base='create_file',
 
392
                      _item_path='file', item_id='file-id',
 
393
                      )
 
394
        base_scenarios = [
 
395
            (('file_modified', dict(actions='modify_file',
 
396
                                   check='file_has_more_content')),
 
397
             ('file_deleted', dict(actions='delete_file',
 
398
                                   check='file_doesnt_exist')),
 
399
             dict(_actions_base='create_file',
 
400
                  _item_path='file', item_id='file-id',)),
 
401
            ]
 
402
        return klass.mirror_scenarios(base_scenarios)
 
403
 
 
404
    def assertContentsConflict(self, wt, c):
 
405
        self.assertEqual(self._other_id, c.file_id)
 
406
        self.assertEqual(self._other_path, c.path)
 
407
    _assert_conflict = assertContentsConflict
 
408
 
 
409
 
 
410
 
 
411
class TestResolvePathConflict(TestParametrizedResolveConflicts):
 
412
 
 
413
    _conflict_type = conflicts.PathConflict,
 
414
 
 
415
    @classmethod
 
416
    def scenarios(klass):
 
417
        for_dirs = dict(_actions_base='create_dir',
 
418
                        _item_path='new-dir', _item_id='dir-id',)
 
419
        base_scenarios = [
 
420
            (('file_renamed',
 
421
              dict(actions='rename_file', check='file_renamed')),
 
422
             ('file_deleted',
 
423
              dict(actions='delete_file', check='file_doesnt_exist')),
 
424
             dict(_actions_base='create_file',
 
425
                  _item_path='new-file', _item_id='file-id',)),
 
426
            (('dir_renamed',
 
427
              dict(actions='rename_dir', check='dir_renamed')),
 
428
             ('dir_deleted',
 
429
              dict(actions='delete_dir', check='dir_doesnt_exist')),
 
430
             for_dirs),
 
431
            (('dir_renamed',
 
432
              dict(actions='rename_dir', check='dir_renamed')),
 
433
             ('dir_renamed2',
 
434
              dict(actions='rename_dir2', check='dir_renamed2')),
 
435
             for_dirs),
 
436
        ]
 
437
        return klass.mirror_scenarios(base_scenarios)
 
438
 
 
439
    def do_delete_file(self):
 
440
        sup = super(TestResolvePathConflict, self).do_delete_file()
 
441
        # PathConflicts handle deletion differently and requires a special
 
442
        # hard-coded value
 
443
        return ('<deleted>',) + sup[1:]
 
444
 
 
445
    def assertPathConflict(self, wt, c):
 
446
        self.assertEqual(self._item_id, c.file_id)
 
447
        self.assertEqual(self._this_path, c.path)
 
448
        self.assertEqual(self._other_path, c.conflict_path)
 
449
    _assert_conflict = assertPathConflict
 
450
 
 
451
 
 
452
class TestResolvePathConflictBefore531967(TestResolvePathConflict):
 
453
    """Same as TestResolvePathConflict but a specific conflict object.
 
454
    """
 
455
 
 
456
    def assertPathConflict(self, c):
 
457
        # We create a conflict object as it was created before the fix and
 
458
        # inject it into the working tree, the test will exercise the
 
459
        # compatibility code.
 
460
        old_c = conflicts.PathConflict('<deleted>', self._item_path,
 
461
                                       file_id=None)
 
462
        wt.set_conflicts(conflicts.ConflictList([c]))
 
463
 
 
464
 
337
465
class TestResolveDuplicateEntry(TestResolveConflicts):
338
466
 
339
467
    preamble = """
557
685
""")
558
686
 
559
687
 
560
 
class TestResolvePathConflict(TestResolveConflicts):
 
688
class OldTestResolvePathConflict(TestResolveConflicts):
561
689
 
562
690
    preamble = """
563
691
$ bzr init trunk