/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_annotate.py

  • Committer: John Arbash Meinel
  • Date: 2008-08-18 22:34:21 UTC
  • mto: (3606.5.6 1.6)
  • mto: This revision was merged to the branch mainline in revision 3641.
  • Revision ID: john@arbash-meinel.com-20080818223421-todjny24vj4faj4t
Add tests for the fetching behavior.

The proper parameter passed is 'unordered' add an assert for it, and
fix callers that were passing 'unsorted' instead.
Add tests that we make the right get_record_stream call based
on the value of _fetch_uses_deltas.
Fix the fetch request for signatures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Whitebox tests for annotate functionality."""
 
18
 
 
19
import codecs
 
20
from cStringIO import StringIO
 
21
 
 
22
from bzrlib import (
 
23
    annotate,
 
24
    conflicts,
 
25
    errors,
 
26
    tests,
 
27
    trace,
 
28
    )
 
29
 
 
30
 
 
31
def annotation(text):
 
32
    return [tuple(l.split(' ', 1)) for l in text.splitlines(True)]
 
33
 
 
34
 
 
35
parent_1 = annotation("""\
 
36
rev1 a
 
37
rev2 b
 
38
rev3 c
 
39
rev4 d
 
40
rev5 e
 
41
""")
 
42
 
 
43
 
 
44
parent_2 = annotation("""\
 
45
rev1 a
 
46
rev3 c
 
47
rev4 d
 
48
rev6 f
 
49
rev7 e
 
50
rev8 h
 
51
""")
 
52
 
 
53
 
 
54
expected_2_1 = annotation("""\
 
55
rev1 a
 
56
blahblah b
 
57
rev3 c
 
58
rev4 d
 
59
rev7 e
 
60
""")
 
61
 
 
62
 
 
63
# a: in both, same value, kept
 
64
# b: in 1, kept
 
65
# c: in both, same value, kept
 
66
# d: in both, same value, kept
 
67
# e: 1 and 2 disagree, so it goes to blahblah
 
68
# f: in 2, but not in new, so ignored
 
69
# g: not in 1 or 2, so it goes to blahblah
 
70
# h: only in parent 2, so 2 gets it
 
71
expected_1_2_2 = annotation("""\
 
72
rev1 a
 
73
rev2 b
 
74
rev3 c
 
75
rev4 d
 
76
blahblah e
 
77
blahblah g
 
78
rev8 h
 
79
""")
 
80
 
 
81
 
 
82
new_1 = """\
 
83
a
 
84
b
 
85
c
 
86
d
 
87
e
 
88
""".splitlines(True)
 
89
 
 
90
expected_1 = annotation("""\
 
91
blahblah a
 
92
blahblah b
 
93
blahblah c
 
94
blahblah d
 
95
blahblah e
 
96
""")
 
97
 
 
98
 
 
99
new_2 = """\
 
100
a
 
101
b
 
102
c
 
103
d
 
104
e
 
105
g
 
106
h
 
107
""".splitlines(True)
 
108
 
 
109
 
 
110
# For the 'duplicate' series, both sides introduce the same change, which then
 
111
# gets merged around. The last-modified should properly reflect this.
 
112
# We always change the fourth line so that the file is properly tracked as
 
113
# being modified in each revision. In reality, this probably would happen over
 
114
# many revisions, and it would be a different line that changes.
 
115
# BASE
 
116
#  |\
 
117
#  A B  # line should be annotated as new for A and B
 
118
#  |\|
 
119
#  C D  # line should 'converge' and say A
 
120
#  |/
 
121
#  E    # D should supersede A and stay as D (not become E because C references
 
122
#         A)
 
123
duplicate_base = annotation("""\
 
124
rev-base first
 
125
rev-base second
 
126
rev-base third
 
127
rev-base fourth-base
 
128
""")
 
129
 
 
130
duplicate_A = annotation("""\
 
131
rev-base first
 
132
rev-A alt-second
 
133
rev-base third
 
134
rev-A fourth-A
 
135
""")
 
136
 
 
137
duplicate_B = annotation("""\
 
138
rev-base first
 
139
rev-B alt-second
 
140
rev-base third
 
141
rev-B fourth-B
 
142
""")
 
143
 
 
144
duplicate_C = annotation("""\
 
145
rev-base first
 
146
rev-A alt-second
 
147
rev-base third
 
148
rev-C fourth-C
 
149
""")
 
150
 
 
151
duplicate_D = annotation("""\
 
152
rev-base first
 
153
rev-A alt-second
 
154
rev-base third
 
155
rev-D fourth-D
 
156
""")
 
157
 
 
158
duplicate_E = annotation("""\
 
159
rev-base first
 
160
rev-A alt-second
 
161
rev-base third
 
162
rev-E fourth-E
 
163
""")
 
164
 
 
165
 
 
166
class TestAnnotate(tests.TestCaseWithTransport):
 
167
 
 
168
    def create_merged_trees(self):
 
169
        """create 2 trees with merges between them.
 
170
 
 
171
        rev-1 --+
 
172
         |      |
 
173
        rev-2  rev-1_1_1
 
174
         |      |
 
175
         +------+
 
176
         |
 
177
        rev-3
 
178
        """
 
179
 
 
180
        tree1 = self.make_branch_and_tree('tree1')
 
181
        self.build_tree_contents([('tree1/a', 'first\n')])
 
182
        tree1.add(['a'], ['a-id'])
 
183
        tree1.commit('a', rev_id='rev-1',
 
184
                     committer="joe@foo.com",
 
185
                     timestamp=1166046000.00, timezone=0)
 
186
 
 
187
        tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
 
188
 
 
189
        self.build_tree_contents([('tree1/a', 'first\nsecond\n')])
 
190
        tree1.commit('b', rev_id='rev-2',
 
191
                     committer='joe@foo.com',
 
192
                     timestamp=1166046001.00, timezone=0)
 
193
 
 
194
        self.build_tree_contents([('tree2/a', 'first\nthird\n')])
 
195
        tree2.commit('c', rev_id='rev-1_1_1',
 
196
                     committer="barry@foo.com",
 
197
                     timestamp=1166046002.00, timezone=0)
 
198
 
 
199
        num_conflicts = tree1.merge_from_branch(tree2.branch)
 
200
        self.assertEqual(1, num_conflicts)
 
201
 
 
202
        self.build_tree_contents([('tree1/a',
 
203
                                 'first\nsecond\nthird\n')])
 
204
        tree1.set_conflicts(conflicts.ConflictList())
 
205
        tree1.commit('merge 2', rev_id='rev-3',
 
206
                     committer='sal@foo.com',
 
207
                     timestamp=1166046003.00, timezone=0)
 
208
        tree1.lock_read()
 
209
        self.addCleanup(tree1.unlock)
 
210
        return tree1, tree2
 
211
 
 
212
    def create_deeply_merged_trees(self):
 
213
        """Create some trees with a more complex merge history.
 
214
 
 
215
        rev-1 --+
 
216
         |      |
 
217
        rev-2  rev-1_1_1 --+
 
218
         |      |          |
 
219
         +------+          |
 
220
         |      |          |
 
221
        rev-3  rev-1_1_2  rev-1_2_1 ------+
 
222
         |      |          |              |
 
223
         +------+          |              |
 
224
         |                 |              |
 
225
        rev-4             rev-1_2_2  rev-1_3_1
 
226
         |                 |              |
 
227
         +-----------------+              |
 
228
         |                                |
 
229
        rev-5                             |
 
230
         |                                |
 
231
         +--------------------------------+
 
232
         |
 
233
        rev-6
 
234
        """
 
235
        tree1, tree2 = self.create_merged_trees()
 
236
        tree1.unlock()
 
237
 
 
238
        tree3 = tree2.bzrdir.sprout('tree3').open_workingtree()
 
239
 
 
240
        tree2.commit('noop', rev_id='rev-1_1_2')
 
241
        self.assertEqual(0, tree1.merge_from_branch(tree2.branch))
 
242
        tree1.commit('noop merge', rev_id='rev-4')
 
243
 
 
244
        self.build_tree_contents([('tree3/a', 'first\nthird\nfourth\n')])
 
245
        tree3.commit('four', rev_id='rev-1_2_1',
 
246
                     committer='jerry@foo.com',
 
247
                     timestamp=1166046003.00, timezone=0)
 
248
 
 
249
        tree4 = tree3.bzrdir.sprout('tree4').open_workingtree()
 
250
 
 
251
        tree3.commit('noop', rev_id='rev-1_2_2',
 
252
                     committer='jerry@foo.com',
 
253
                     timestamp=1166046004.00, timezone=0)
 
254
        self.assertEqual(0, tree1.merge_from_branch(tree3.branch))
 
255
        tree1.commit('merge four', rev_id='rev-5')
 
256
 
 
257
        self.build_tree_contents([('tree4/a',
 
258
                                   'first\nthird\nfourth\nfifth\nsixth\n')])
 
259
        tree4.commit('five and six', rev_id='rev-1_3_1',
 
260
                     committer='george@foo.com',
 
261
                     timestamp=1166046005.00, timezone=0)
 
262
        self.assertEqual(0, tree1.merge_from_branch(tree4.branch))
 
263
        tree1.commit('merge five and six', rev_id='rev-6')
 
264
        tree1.lock_read()
 
265
        return tree1
 
266
 
 
267
    def create_duplicate_lines_tree(self):
 
268
        tree1 = self.make_branch_and_tree('tree1')
 
269
        base_text = ''.join(l for r, l in duplicate_base)
 
270
        a_text = ''.join(l for r, l in duplicate_A)
 
271
        b_text = ''.join(l for r, l in duplicate_B)
 
272
        c_text = ''.join(l for r, l in duplicate_C)
 
273
        d_text = ''.join(l for r, l in duplicate_D)
 
274
        e_text = ''.join(l for r, l in duplicate_E)
 
275
        self.build_tree_contents([('tree1/file', base_text)])
 
276
        tree1.add(['file'], ['file-id'])
 
277
        tree1.commit('base', rev_id='rev-base')
 
278
        tree2 = tree1.bzrdir.sprout('tree2').open_workingtree()
 
279
 
 
280
        self.build_tree_contents([('tree1/file', a_text),
 
281
                                  ('tree2/file', b_text)])
 
282
        tree1.commit('A', rev_id='rev-A')
 
283
        tree2.commit('B', rev_id='rev-B')
 
284
 
 
285
        tree2.merge_from_branch(tree1.branch)
 
286
        conflicts.resolve(tree2, None) # Resolve the conflicts
 
287
        self.build_tree_contents([('tree2/file', d_text)])
 
288
        tree2.commit('D', rev_id='rev-D')
 
289
 
 
290
        self.build_tree_contents([('tree1/file', c_text)])
 
291
        tree1.commit('C', rev_id='rev-C')
 
292
 
 
293
        tree1.merge_from_branch(tree2.branch)
 
294
        conflicts.resolve(tree1, None) # Resolve the conflicts
 
295
        self.build_tree_contents([('tree1/file', e_text)])
 
296
        tree1.commit('E', rev_id='rev-E')
 
297
        return tree1
 
298
 
 
299
    def assertRepoAnnotate(self, expected, repo, file_id, revision_id):
 
300
        """Assert that the revision is properly annotated."""
 
301
        actual = list(repo.revision_tree(revision_id).annotate_iter(file_id))
 
302
        if actual != expected:
 
303
            # Create an easier to understand diff when the lines don't actually
 
304
            # match
 
305
            self.assertEqualDiff(''.join('\t'.join(l) for l in expected),
 
306
                                 ''.join('\t'.join(l) for l in actual))
 
307
 
 
308
    def test_annotate_duplicate_lines(self):
 
309
        # XXX: Should this be a repository_implementations test?
 
310
        tree1 = self.create_duplicate_lines_tree()
 
311
        repo = tree1.branch.repository
 
312
        repo.lock_read()
 
313
        self.addCleanup(repo.unlock)
 
314
        self.assertRepoAnnotate(duplicate_base, repo, 'file-id', 'rev-base')
 
315
        self.assertRepoAnnotate(duplicate_A, repo, 'file-id', 'rev-A')
 
316
        self.assertRepoAnnotate(duplicate_B, repo, 'file-id', 'rev-B')
 
317
        self.assertRepoAnnotate(duplicate_C, repo, 'file-id', 'rev-C')
 
318
        self.assertRepoAnnotate(duplicate_D, repo, 'file-id', 'rev-D')
 
319
        self.assertRepoAnnotate(duplicate_E, repo, 'file-id', 'rev-E')
 
320
 
 
321
    def test_annotate_shows_dotted_revnos(self):
 
322
        tree1, tree2 = self.create_merged_trees()
 
323
 
 
324
        sio = StringIO()
 
325
        annotate.annotate_file(tree1.branch, 'rev-3', 'a-id',
 
326
                               to_file=sio)
 
327
        self.assertEqualDiff('1     joe@foo | first\n'
 
328
                             '2     joe@foo | second\n'
 
329
                             '1.1.1 barry@f | third\n',
 
330
                             sio.getvalue())
 
331
 
 
332
    def test_annotate_limits_dotted_revnos(self):
 
333
        """Annotate should limit dotted revnos to a depth of 12"""
 
334
        tree1 = self.create_deeply_merged_trees()
 
335
 
 
336
        sio = StringIO()
 
337
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
338
                               to_file=sio, verbose=False, full=False)
 
339
        self.assertEqualDiff('1     joe@foo | first\n'
 
340
                             '2     joe@foo | second\n'
 
341
                             '1.1.1 barry@f | third\n'
 
342
                             '1.2.1 jerry@f | fourth\n'
 
343
                             '1.3.1 george@ | fifth\n'
 
344
                             '              | sixth\n',
 
345
                             sio.getvalue())
 
346
 
 
347
        sio = StringIO()
 
348
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
349
                               to_file=sio, verbose=False, full=True)
 
350
        self.assertEqualDiff('1     joe@foo | first\n'
 
351
                             '2     joe@foo | second\n'
 
352
                             '1.1.1 barry@f | third\n'
 
353
                             '1.2.1 jerry@f | fourth\n'
 
354
                             '1.3.1 george@ | fifth\n'
 
355
                             '1.3.1 george@ | sixth\n',
 
356
                             sio.getvalue())
 
357
 
 
358
        # verbose=True shows everything, the full revno, user id, and date
 
359
        sio = StringIO()
 
360
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
361
                               to_file=sio, verbose=True, full=False)
 
362
        self.assertEqualDiff('1     joe@foo.com    20061213 | first\n'
 
363
                             '2     joe@foo.com    20061213 | second\n'
 
364
                             '1.1.1 barry@foo.com  20061213 | third\n'
 
365
                             '1.2.1 jerry@foo.com  20061213 | fourth\n'
 
366
                             '1.3.1 george@foo.com 20061213 | fifth\n'
 
367
                             '                              | sixth\n',
 
368
                             sio.getvalue())
 
369
 
 
370
        sio = StringIO()
 
371
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
372
                               to_file=sio, verbose=True, full=True)
 
373
        self.assertEqualDiff('1     joe@foo.com    20061213 | first\n'
 
374
                             '2     joe@foo.com    20061213 | second\n'
 
375
                             '1.1.1 barry@foo.com  20061213 | third\n'
 
376
                             '1.2.1 jerry@foo.com  20061213 | fourth\n'
 
377
                             '1.3.1 george@foo.com 20061213 | fifth\n'
 
378
                             '1.3.1 george@foo.com 20061213 | sixth\n',
 
379
                             sio.getvalue())
 
380
 
 
381
    def test_annotate_uses_branch_context(self):
 
382
        """Dotted revnos should use the Branch context.
 
383
 
 
384
        When annotating a non-mainline revision, the annotation should still
 
385
        use dotted revnos from the mainline.
 
386
        """
 
387
        tree1 = self.create_deeply_merged_trees()
 
388
 
 
389
        sio = StringIO()
 
390
        annotate.annotate_file(tree1.branch, 'rev-1_3_1', 'a-id',
 
391
                               to_file=sio, verbose=False, full=False)
 
392
        self.assertEqualDiff('1     joe@foo | first\n'
 
393
                             '1.1.1 barry@f | third\n'
 
394
                             '1.2.1 jerry@f | fourth\n'
 
395
                             '1.3.1 george@ | fifth\n'
 
396
                             '              | sixth\n',
 
397
                             sio.getvalue())
 
398
 
 
399
    def test_annotate_show_ids(self):
 
400
        tree1 = self.create_deeply_merged_trees()
 
401
 
 
402
        sio = StringIO()
 
403
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
404
                               to_file=sio, show_ids=True, full=False)
 
405
 
 
406
        # It looks better with real revision ids :)
 
407
        self.assertEqualDiff('    rev-1 | first\n'
 
408
                             '    rev-2 | second\n'
 
409
                             'rev-1_1_1 | third\n'
 
410
                             'rev-1_2_1 | fourth\n'
 
411
                             'rev-1_3_1 | fifth\n'
 
412
                             '          | sixth\n',
 
413
                             sio.getvalue())
 
414
 
 
415
        sio = StringIO()
 
416
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
417
                               to_file=sio, show_ids=True, full=True)
 
418
 
 
419
        self.assertEqualDiff('    rev-1 | first\n'
 
420
                             '    rev-2 | second\n'
 
421
                             'rev-1_1_1 | third\n'
 
422
                             'rev-1_2_1 | fourth\n'
 
423
                             'rev-1_3_1 | fifth\n'
 
424
                             'rev-1_3_1 | sixth\n',
 
425
                             sio.getvalue())
 
426
 
 
427
    def test_annotate_unicode_author(self):
 
428
        tree1 = self.make_branch_and_tree('tree1')
 
429
 
 
430
        self.build_tree_contents([('tree1/a', 'adi\xc3\xb3s')])
 
431
        tree1.add(['a'], ['a-id'])
 
432
        tree1.commit('a', rev_id='rev-1',
 
433
                     committer=u'Pepe P\xe9rez <pperez@ejemplo.com>',
 
434
                     timestamp=1166046000.00, timezone=0)
 
435
 
 
436
        self.build_tree_contents([('tree1/b', 'bye')])
 
437
        tree1.add(['b'], ['b-id'])
 
438
        tree1.commit('b', rev_id='rev-2',
 
439
                     committer=u'p\xe9rez',
 
440
                     timestamp=1166046000.00, timezone=0)
 
441
 
 
442
        tree1.lock_read()
 
443
        self.addCleanup(tree1.unlock)
 
444
        # this passes if no exception is raised
 
445
        to_file = StringIO()
 
446
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
 
447
 
 
448
        sio = StringIO()
 
449
        to_file = codecs.getwriter('ascii')(sio)
 
450
        to_file.encoding = 'ascii' # codecs does not set it
 
451
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
452
        self.assertEqualDiff('2   p?rez   | bye\n', sio.getvalue())
 
453
 
 
454
        # test now with to_file.encoding = None
 
455
        to_file = tests.StringIOWrapper()
 
456
        to_file.encoding = None
 
457
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
458
        self.assertContainsRe('2   p.rez   | bye\n', to_file.getvalue())
 
459
 
 
460
        # and when it does not exist
 
461
        to_file = StringIO()
 
462
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
463
        self.assertContainsRe('2   p.rez   | bye\n', to_file.getvalue())
 
464
 
 
465
    def test_annotate_author_or_committer(self):
 
466
        tree1 = self.make_branch_and_tree('tree1')
 
467
 
 
468
        self.build_tree_contents([('tree1/a', 'hello')])
 
469
        tree1.add(['a'], ['a-id'])
 
470
        tree1.commit('a', rev_id='rev-1',
 
471
                     committer='Committer <committer@example.com>',
 
472
                     timestamp=1166046000.00, timezone=0)
 
473
 
 
474
        self.build_tree_contents([('tree1/b', 'bye')])
 
475
        tree1.add(['b'], ['b-id'])
 
476
        tree1.commit('b', rev_id='rev-2',
 
477
                     committer='Committer <committer@example.com>',
 
478
                     author='Author <author@example.com>',
 
479
                     timestamp=1166046000.00, timezone=0)
 
480
 
 
481
        tree1.lock_read()
 
482
        self.addCleanup(tree1.unlock)
 
483
        to_file = StringIO()
 
484
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
 
485
        self.assertEqual('1   committ | hello\n', to_file.getvalue())
 
486
 
 
487
        to_file = StringIO()
 
488
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
489
        self.assertEqual('2   author@ | bye\n', to_file.getvalue())
 
490
 
 
491
 
 
492
class TestReannotate(tests.TestCase):
 
493
 
 
494
    def annotateEqual(self, expected, parents, newlines, revision_id,
 
495
                      blocks=None):
 
496
        annotate_list = list(annotate.reannotate(parents, newlines,
 
497
                             revision_id, blocks))
 
498
        self.assertEqual(len(expected), len(annotate_list))
 
499
        for e, a in zip(expected, annotate_list):
 
500
            self.assertEqual(e, a)
 
501
 
 
502
    def test_reannotate(self):
 
503
        self.annotateEqual(parent_1, [parent_1], new_1, 'blahblah')
 
504
        self.annotateEqual(expected_2_1, [parent_2], new_1, 'blahblah')
 
505
        self.annotateEqual(expected_1_2_2, [parent_1, parent_2], new_2, 
 
506
                           'blahblah')
 
507
 
 
508
    def test_reannotate_no_parents(self):
 
509
        self.annotateEqual(expected_1, [], new_1, 'blahblah')
 
510
 
 
511
    def test_reannotate_left_matching_blocks(self):
 
512
        """Ensure that left_matching_blocks has an impact.
 
513
 
 
514
        In this case, the annotation is ambiguous, so the hint isn't actually
 
515
        lying.
 
516
        """
 
517
        parent = [('rev1', 'a\n')]
 
518
        new_text = ['a\n', 'a\n']
 
519
        blocks = [(0, 0, 1), (1, 2, 0)]
 
520
        self.annotateEqual([('rev1', 'a\n'), ('rev2', 'a\n')], [parent],
 
521
                           new_text, 'rev2', blocks)
 
522
        blocks = [(0, 1, 1), (1, 2, 0)]
 
523
        self.annotateEqual([('rev2', 'a\n'), ('rev1', 'a\n')], [parent],
 
524
                           new_text, 'rev2', blocks)