~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_branch/test_tags.py

(vila) Fix bzrlib.tests.test_gpg.TestVerify.test_verify_revoked_signature
 with recent versions of gpg. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
from bzrlib import (
24
24
    branch,
25
 
    bzrdir,
 
25
    controldir,
26
26
    errors,
27
 
    repository,
28
27
    tests,
29
28
    )
30
29
from bzrlib.tests import per_branch
41
40
            raise tests.TestSkipped(
42
41
                "format %s doesn't support tags" % branch._format)
43
42
 
 
43
    def make_branch_with_revisions(self, relpath, revisions):
 
44
        builder = self.make_branch_builder(relpath)
 
45
        builder.start_series()
 
46
        for revid in revisions:
 
47
            builder.build_commit(rev_id=revid)
 
48
        builder.finish_series()
 
49
        return builder.get_branch()
 
50
 
44
51
    def test_tags_initially_empty(self):
45
52
        b = self.make_branch('b')
46
53
        tags = b.tags.get_tag_dict()
47
54
        self.assertEqual(tags, {})
48
55
 
49
56
    def test_make_and_lookup_tag(self):
50
 
        b = self.make_branch('b')
 
57
        b = self.make_branch_with_revisions('b',
 
58
            ['target-revid-1', 'target-revid-2'])
51
59
        b.tags.set_tag('tag-name', 'target-revid-1')
52
60
        b.tags.set_tag('other-name', 'target-revid-2')
53
61
        # then reopen the branch and see they're still there
64
72
        self.assertFalse(b.tags.has_tag('imaginary'))
65
73
 
66
74
    def test_reverse_tag_dict(self):
67
 
        b = self.make_branch('b')
 
75
        b = self.make_branch_with_revisions('b',
 
76
            ['target-revid-1', 'target-revid-2'])
68
77
        b.tags.set_tag('tag-name', 'target-revid-1')
69
78
        b.tags.set_tag('other-name', 'target-revid-2')
70
79
        # then reopen the branch and check reverse map id->tags list
71
80
        b = branch.Branch.open('b')
72
 
        self.assertEqual(b.tags.get_reverse_tag_dict(),
 
81
        self.assertEqual(dict(b.tags.get_reverse_tag_dict()),
73
82
            {'target-revid-1': ['tag-name'],
74
83
             'target-revid-2': ['other-name'],
75
84
            })
76
85
 
 
86
    def test_ghost_tag(self):
 
87
        b = self.make_branch('b')
 
88
        if not b._format.supports_tags_referencing_ghosts():
 
89
            self.assertRaises(errors.GhostTagsNotSupported,
 
90
                b.tags.set_tag, "ghost", "idontexist")
 
91
        else:
 
92
            b.tags.set_tag("ghost", "idontexist")
 
93
            self.assertEquals("idontexist", b.tags.lookup_tag("ghost"))
 
94
 
77
95
    def test_no_such_tag(self):
78
96
        b = self.make_branch('b')
79
97
        try:
85
103
            self.fail("didn't get expected exception")
86
104
 
87
105
    def test_merge_tags(self):
88
 
        b1 = self.make_branch('b1')
89
 
        b2 = self.make_branch('b2')
 
106
        b1 = self.make_branch_with_revisions('b1', ['revid', 'revid-1'])
 
107
        b2 = self.make_branch_with_revisions('b2', ['revid', 'revid-2'])
90
108
        # if there are tags in the source and not the destination, then they
91
109
        # just go across
92
110
        b1.tags.set_tag('tagname', 'revid')
95
113
        # if a tag is in the destination and not in the source, it is not
96
114
        # removed when we merge them
97
115
        b2.tags.set_tag('in-destination', 'revid')
98
 
        result = b1.tags.merge_to(b2.tags)
99
 
        self.assertEquals(result, [])
 
116
        updates, conflicts = b1.tags.merge_to(b2.tags)
 
117
        self.assertEquals(list(conflicts), [])
 
118
        self.assertEquals(updates, {})
100
119
        self.assertEquals(b2.tags.lookup_tag('in-destination'), 'revid')
101
120
        # if there's a conflicting tag, it's reported -- the command line
102
121
        # interface will say "these tags couldn't be copied"
103
122
        b1.tags.set_tag('conflicts', 'revid-1')
104
123
        b2.tags.set_tag('conflicts', 'revid-2')
105
 
        result = b1.tags.merge_to(b2.tags)
106
 
        self.assertEquals(result,
 
124
        updates, conflicts = b1.tags.merge_to(b2.tags)
 
125
        self.assertEquals(list(conflicts),
107
126
            [('conflicts', 'revid-1', 'revid-2')])
108
127
        # and it keeps the same value
 
128
        self.assertEquals(updates, {})
109
129
        self.assertEquals(b2.tags.lookup_tag('conflicts'), 'revid-2')
110
130
 
111
 
 
112
131
    def test_unicode_tag(self):
113
 
        b1 = self.make_branch('b')
114
132
        tag_name = u'\u3070'
115
133
        # in anticipation of the planned change to treating revision ids as
116
134
        # just 8bit strings
117
135
        revid = ('revid' + tag_name).encode('utf-8')
 
136
        b1 = self.make_branch_with_revisions('b', [revid])
118
137
        b1.tags.set_tag(tag_name, revid)
119
138
        self.assertEquals(b1.tags.lookup_tag(tag_name), revid)
120
139
 
121
140
    def test_delete_tag(self):
122
 
        b = self.make_branch('b')
123
141
        tag_name = u'\N{GREEK SMALL LETTER ALPHA}'
124
142
        revid = ('revid' + tag_name).encode('utf-8')
 
143
        b = self.make_branch_with_revisions('b', [revid])
125
144
        b.tags.set_tag(tag_name, revid)
126
145
        # now try to delete it
127
146
        b.tags.delete_tag(tag_name)
143
162
        b2 = self.make_branch('b2')
144
163
        b1.tags.merge_to(b2.tags)
145
164
 
 
165
    def test_read_lock_caches_tags(self):
 
166
        """Tags are read from a branch only once during a read-lock."""
 
167
        # Open the same branch twice.  Read-lock one, and then mutate the tags
 
168
        # in the second.  The read-locked branch never re-reads the tags, so it
 
169
        # never observes the changed/new tags.
 
170
        b1 = self.make_branch_with_revisions('b',
 
171
            ['rev-1', 'rev-1-changed', 'rev-2'])
 
172
        b1.tags.set_tag('one', 'rev-1')
 
173
        b2 = controldir.ControlDir.open('b').open_branch()
 
174
        b1.lock_read()
 
175
        self.assertEqual({'one': 'rev-1'}, b1.tags.get_tag_dict())
 
176
        # Add a tag and modify a tag in b2.  b1 is read-locked and has already
 
177
        # read the tags, so it is unaffected.
 
178
        b2.tags.set_tag('one', 'rev-1-changed')
 
179
        b2.tags.set_tag('two', 'rev-2')
 
180
        self.assertEqual({'one': 'rev-1'}, b1.tags.get_tag_dict())
 
181
        b1.unlock()
 
182
        # Once unlocked the cached value is forgotten, so now the latest tags
 
183
        # will be retrieved.
 
184
        self.assertEqual(
 
185
            {'one': 'rev-1-changed', 'two': 'rev-2'}, b1.tags.get_tag_dict())
 
186
 
 
187
    def test_unlocked_does_not_cache_tags(self):
 
188
        """Unlocked branches do not cache tags."""
 
189
        # Open the same branch twice.
 
190
        b1 = self.make_branch_with_revisions('b',
 
191
            ['rev-1', 'rev-1-changed', 'rev-2'])
 
192
        b1.tags.set_tag('one', 'rev-1')
 
193
        b2 = b1.bzrdir.open_branch()
 
194
        self.assertEqual({'one': 'rev-1'}, b1.tags.get_tag_dict())
 
195
        # Add a tag and modify a tag in b2.  b1 isn't locked, so it will
 
196
        # immediately return the new tags too.
 
197
        b2.tags.set_tag('one', 'rev-1-changed')
 
198
        b2.tags.set_tag('two', 'rev-2')
 
199
        self.assertEqual(
 
200
            {'one': 'rev-1-changed', 'two': 'rev-2'}, b1.tags.get_tag_dict())
 
201
 
 
202
    def test_cached_tag_dict_not_accidentally_mutable(self):
 
203
        """When there's a cached version of the tags, b.tags.get_tag_dict
 
204
        returns a copy of the cached data so that callers cannot accidentally
 
205
        corrupt the cache.
 
206
        """
 
207
        b = self.make_branch_with_revisions('b',
 
208
            ['rev-1', 'rev-2', 'rev-3'])
 
209
        b.tags.set_tag('one', 'rev-1')
 
210
        self.addCleanup(b.lock_read().unlock)
 
211
        # The first time the data returned will not be in the cache
 
212
        tags_dict = b.tags.get_tag_dict()
 
213
        tags_dict['two'] = 'rev-2'
 
214
        # The second time the data comes from the cache
 
215
        tags_dict = b.tags.get_tag_dict()
 
216
        tags_dict['three'] = 'rev-3'
 
217
        # The get_tag_dict() result should still be unchanged, even though we
 
218
        # mutated its earlier return values.
 
219
        self.assertEqual({'one': 'rev-1'}, b.tags.get_tag_dict())
 
220
 
 
221
    def make_write_locked_branch_with_one_tag(self):
 
222
        b = self.make_branch_with_revisions('b',
 
223
            ['rev-1', 'rev-1-changed', 'rev-2'])
 
224
        b.tags.set_tag('one', 'rev-1')
 
225
        self.addCleanup(b.lock_write().unlock)
 
226
        # Populate the cache
 
227
        b.tags.get_tag_dict()
 
228
        return b
 
229
 
 
230
    def test_set_tag_invalides_cache(self):
 
231
        b = self.make_write_locked_branch_with_one_tag()
 
232
        b.tags.set_tag('one', 'rev-1-changed')
 
233
        self.assertEqual({'one': 'rev-1-changed'}, b.tags.get_tag_dict())
 
234
 
 
235
    def test_delete_tag_invalides_cache(self):
 
236
        b = self.make_write_locked_branch_with_one_tag()
 
237
        b.tags.delete_tag('one')
 
238
        self.assertEqual({}, b.tags.get_tag_dict())
 
239
 
 
240
    def test_merge_to_invalides_cache(self):
 
241
        b1 = self.make_write_locked_branch_with_one_tag()
 
242
        b2 = self.make_branch_with_revisions('b2', ['rev-2', 'rev-1'])
 
243
        b2.tags.set_tag('two', 'rev-2')
 
244
        b2.tags.merge_to(b1.tags)
 
245
        self.assertEqual(
 
246
            {'one': 'rev-1', 'two': 'rev-2'}, b1.tags.get_tag_dict())
 
247
 
 
248
    def test_rename_revisions_invalides_cache(self):
 
249
        b = self.make_write_locked_branch_with_one_tag()
 
250
        b.tags.rename_revisions({'rev-1': 'rev-1-changed'})
 
251
        self.assertEqual({'one': 'rev-1-changed'}, b.tags.get_tag_dict())
 
252
 
 
253
 
 
254
class TestTagsMergeToInCheckouts(per_branch.TestCaseWithBranch):
 
255
    """Tests for checkout.branch.tags.merge_to.
 
256
    
 
257
    In particular this exercises variations in tag conflicts in the master
 
258
    branch and/or the checkout (child).  It may seem strange to have different
 
259
    tags in the child and master, but 'bzr merge' intentionally updates the
 
260
    child and not the master (instead the next 'bzr commit', if the user
 
261
    decides to commit, will update the master).  Also, merge_to in bzr < 2.3
 
262
    didn't propagate changes to the master, and current bzr versions may find
 
263
    themselves operating on checkouts touched by older bzrs
 
264
    
 
265
    So we need to make sure bzr copes gracefully with differing tags in the
 
266
    master versus the child.
 
267
 
 
268
    See also <https://bugs.launchpad.net/bzr/+bug/603395>.
 
269
    """
 
270
 
 
271
    def setUp(self):
 
272
        super(TestTagsMergeToInCheckouts, self).setUp()
 
273
        branch1 = self.make_branch('tags-probe')
 
274
        if not branch1._format.supports_tags():
 
275
            raise tests.TestSkipped(
 
276
                "format %s doesn't support tags" % branch1._format)
 
277
        branch2 = self.make_branch('bind-probe')
 
278
        try:
 
279
            branch2.bind(branch1)
 
280
        except errors.UpgradeRequired:
 
281
            raise tests.TestNotApplicable(
 
282
                "format %s doesn't support bound branches" % branch2._format)
 
283
 
 
284
    def test_merge_to_propagates_tags(self):
 
285
        """merge_to(child) also merges tags to the master."""
 
286
        master = self.make_branch('master')
 
287
        other = self.make_branch('other')
 
288
        other.tags.set_tag('foo', 'rev-1')
 
289
        child = self.make_branch('child')
 
290
        child.bind(master)
 
291
        child.update()
 
292
        other.tags.merge_to(child.tags)
 
293
        self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
 
294
        self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
 
295
 
 
296
    def test_ignore_master_disables_tag_propagation(self):
 
297
        """merge_to(child, ignore_master=True) does not merge tags to the
 
298
        master.
 
299
        """
 
300
        master = self.make_branch('master')
 
301
        other = self.make_branch('other')
 
302
        other.tags.set_tag('foo', 'rev-1')
 
303
        child = self.make_branch('child')
 
304
        child.bind(master)
 
305
        child.update()
 
306
        other.tags.merge_to(child.tags, ignore_master=True)
 
307
        self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
 
308
        self.assertRaises(errors.NoSuchTag, master.tags.lookup_tag, 'foo')
 
309
 
 
310
    def test_merge_to_overwrite_conflict_in_master(self):
 
311
        """merge_to(child, overwrite=True) overwrites any conflicting tags in
 
312
        the master.
 
313
        """
 
314
        master = self.make_branch('master')
 
315
        other = self.make_branch('other')
 
316
        other.tags.set_tag('foo', 'rev-1')
 
317
        child = self.make_branch('child')
 
318
        child.bind(master)
 
319
        child.update()
 
320
        master.tags.set_tag('foo', 'rev-2')
 
321
        tag_updates, tag_conflicts = other.tags.merge_to(child.tags, overwrite=True)
 
322
        self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
 
323
        self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
 
324
        self.assertEquals({"foo": "rev-1"}, tag_updates)
 
325
        self.assertLength(0, tag_conflicts)
 
326
 
 
327
    def test_merge_to_overwrite_conflict_in_child_and_master(self):
 
328
        """merge_to(child, overwrite=True) overwrites any conflicting tags in
 
329
        both the child and the master.
 
330
        """
 
331
        master = self.make_branch('master')
 
332
        master.tags.set_tag('foo', 'rev-2')
 
333
        other = self.make_branch('other')
 
334
        other.tags.set_tag('foo', 'rev-1')
 
335
        child = self.make_branch('child')
 
336
        child.bind(master)
 
337
        child.update()
 
338
        tag_updates, tag_conflicts = other.tags.merge_to(
 
339
            child.tags, overwrite=True)
 
340
        self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
 
341
        self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
 
342
        self.assertEquals({u'foo': 'rev-1'}, tag_updates)
 
343
        self.assertLength(0, tag_conflicts)
 
344
 
 
345
    def test_merge_to_conflict_in_child_only(self):
 
346
        """When new_tags.merge_to(child.tags) conflicts with the child but not
 
347
        the master, a conflict is reported and the child receives the new tag.
 
348
        """
 
349
        master = self.make_branch('master')
 
350
        master.tags.set_tag('foo', 'rev-2')
 
351
        other = self.make_branch('other')
 
352
        other.tags.set_tag('foo', 'rev-1')
 
353
        child = self.make_branch('child')
 
354
        child.bind(master)
 
355
        child.update()
 
356
        master.tags.delete_tag('foo')
 
357
        tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
 
358
        # Conflict in child, so it is unchanged.
 
359
        self.assertEquals('rev-2', child.tags.lookup_tag('foo'))
 
360
        # No conflict in the master, so the 'foo' tag equals other's value here.
 
361
        self.assertEquals('rev-1', master.tags.lookup_tag('foo'))
 
362
        # The conflict is reported.
 
363
        self.assertEquals([(u'foo', 'rev-1', 'rev-2')], list(tag_conflicts))
 
364
        self.assertEquals({u'foo': 'rev-1'}, tag_updates)
 
365
 
 
366
    def test_merge_to_conflict_in_master_only(self):
 
367
        """When new_tags.merge_to(child.tags) conflicts with the master but not
 
368
        the child, a conflict is reported and the child receives the new tag.
 
369
        """
 
370
        master = self.make_branch('master')
 
371
        other = self.make_branch('other')
 
372
        other.tags.set_tag('foo', 'rev-1')
 
373
        child = self.make_branch('child')
 
374
        child.bind(master)
 
375
        child.update()
 
376
        master.tags.set_tag('foo', 'rev-2')
 
377
        tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
 
378
        # No conflict in the child, so the 'foo' tag equals other's value here.
 
379
        self.assertEquals('rev-1', child.tags.lookup_tag('foo'))
 
380
        # Conflict in master, so it is unchanged.
 
381
        self.assertEquals('rev-2', master.tags.lookup_tag('foo'))
 
382
        # The conflict is reported.
 
383
        self.assertEquals({u'foo': 'rev-1'}, tag_updates)
 
384
        self.assertEquals([(u'foo', 'rev-1', 'rev-2')], list(tag_conflicts))
 
385
 
 
386
    def test_merge_to_same_conflict_in_master_and_child(self):
 
387
        """When new_tags.merge_to(child.tags) conflicts the same way with the
 
388
        master and the child a single conflict is reported.
 
389
        """
 
390
        master = self.make_branch('master')
 
391
        master.tags.set_tag('foo', 'rev-2')
 
392
        other = self.make_branch('other')
 
393
        other.tags.set_tag('foo', 'rev-1')
 
394
        child = self.make_branch('child')
 
395
        child.bind(master)
 
396
        child.update()
 
397
        tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
 
398
        # Both master and child conflict, so both stay as rev-2
 
399
        self.assertEquals('rev-2', child.tags.lookup_tag('foo'))
 
400
        self.assertEquals('rev-2', master.tags.lookup_tag('foo'))
 
401
        # The conflict is reported exactly once, even though it occurs in both
 
402
        # master and child.
 
403
        self.assertEquals({}, tag_updates)
 
404
        self.assertEquals([(u'foo', 'rev-1', 'rev-2')], list(tag_conflicts))
 
405
 
 
406
    def test_merge_to_different_conflict_in_master_and_child(self):
 
407
        """When new_tags.merge_to(child.tags) conflicts differently in the
 
408
        master and the child both conflicts are reported.
 
409
        """
 
410
        master = self.make_branch('master')
 
411
        master.tags.set_tag('foo', 'rev-2')
 
412
        other = self.make_branch('other')
 
413
        other.tags.set_tag('foo', 'rev-1')
 
414
        child = self.make_branch('child')
 
415
        child.bind(master)
 
416
        child.update()
 
417
        # We use the private method _set_tag_dict because normally bzr tries to
 
418
        # avoid this scenario.
 
419
        child.tags._set_tag_dict({'foo': 'rev-3'})
 
420
        tag_updates, tag_conflicts = other.tags.merge_to(child.tags)
 
421
        # Both master and child conflict, so both stay as they were.
 
422
        self.assertEquals('rev-3', child.tags.lookup_tag('foo'))
 
423
        self.assertEquals('rev-2', master.tags.lookup_tag('foo'))
 
424
        # Both conflicts are reported.
 
425
        self.assertEquals({}, tag_updates)
 
426
        self.assertEquals(
 
427
            [(u'foo', 'rev-1', 'rev-2'), (u'foo', 'rev-1', 'rev-3')],
 
428
            sorted(tag_conflicts))
 
429
 
146
430
 
147
431
class TestUnsupportedTags(per_branch.TestCaseWithBranch):
148
432
    """Formats that don't support tags should give reasonable errors."""
202
486
            get_tag_name, 'get tag name foo')
203
487
        self.assertEquals("foo", self.branch.automatic_tag_name(
204
488
            self.branch.last_revision()))
205
 
    
 
489
 
206
490
    def test_uses_first_return(self):
207
491
        get_tag_name_1 = lambda br, revid: "foo1"
208
492
        get_tag_name_2 = lambda br, revid: "foo2"