/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: Robert Collins
  • Date: 2007-09-19 05:14:14 UTC
  • mto: (2835.1.1 ianc-integration)
  • mto: This revision was merged to the branch mainline in revision 2836.
  • Revision ID: robertc@robertcollins.net-20070919051414-2tgjqteg7k3ps4h0
* ``pull``, ``merge`` and ``push`` will no longer silently correct some
  repository index errors that occured as a result of the Weave disk format.
  Instead the ``reconcile`` command needs to be run to correct those
  problems if they exist (and it has been able to fix most such problems
  since bzr 0.8). Some new problems have been identified during this release
  and you should run ``bzr check`` once on every repository to see if you
  need to reconcile. If you cannot ``pull`` or ``merge`` from a remote
  repository due to mismatched parent errors - a symptom of index errors -
  you should simply take a full copy of that remote repository to a clean
  directory outside any local repositories, then run reconcile on it, and
  finally pull from it locally. (And naturally email the repositories owner
  to ask them to upgrade and run reconcile).
  (Robert Collins)

* ``VersionedFile.fix_parents`` has been removed as a harmful API.
  ``VersionedFile.join`` will no longer accept different parents on either
  side of a join - it will either ignore them, or error, depending on the
  implementation. See notes when upgrading for more information.
  (Robert Collins)

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
class TestAnnotate(tests.TestCaseWithTransport):
 
111
 
 
112
    def create_merged_trees(self):
 
113
        """create 2 trees with merges between them.
 
114
 
 
115
        rev-1 --+
 
116
         |      |
 
117
        rev-2  rev-1_1_1
 
118
         |      |
 
119
         +------+
 
120
         |
 
121
        rev-3
 
122
        """
 
123
 
 
124
        tree1 = self.make_branch_and_tree('tree1')
 
125
        self.build_tree_contents([('tree1/a', 'first\n')])
 
126
        tree1.add(['a'], ['a-id'])
 
127
        tree1.commit('a', rev_id='rev-1',
 
128
                     committer="joe@foo.com",
 
129
                     timestamp=1166046000.00, timezone=0)
 
130
 
 
131
        tree2 = tree1.bzrdir.clone('tree2').open_workingtree()
 
132
 
 
133
        self.build_tree_contents([('tree1/a', 'first\nsecond\n')])
 
134
        tree1.commit('b', rev_id='rev-2',
 
135
                     committer='joe@foo.com',
 
136
                     timestamp=1166046001.00, timezone=0)
 
137
 
 
138
        self.build_tree_contents([('tree2/a', 'first\nthird\n')])
 
139
        tree2.commit('c', rev_id='rev-1_1_1',
 
140
                     committer="barry@foo.com",
 
141
                     timestamp=1166046002.00, timezone=0)
 
142
 
 
143
        num_conflicts = tree1.merge_from_branch(tree2.branch)
 
144
        self.assertEqual(1, num_conflicts)
 
145
 
 
146
        self.build_tree_contents([('tree1/a',
 
147
                                 'first\nsecond\nthird\n')])
 
148
        tree1.set_conflicts(conflicts.ConflictList())
 
149
        tree1.commit('merge 2', rev_id='rev-3',
 
150
                     committer='sal@foo.com',
 
151
                     timestamp=1166046003.00, timezone=0)
 
152
        return tree1, tree2
 
153
 
 
154
    def create_deeply_merged_trees(self):
 
155
        """Create some trees with a more complex merge history.
 
156
 
 
157
        rev-1 --+
 
158
         |      |
 
159
        rev-2  rev-1_1_1 --+
 
160
         |      |          |
 
161
         +------+          |
 
162
         |      |          |
 
163
        rev-3  rev-1_1_2  rev-1_1_1_1_1 --+
 
164
         |      |          |              |
 
165
         +------+          |              |
 
166
         |                 |              |
 
167
        rev-4             rev-1_1_1_1_2  rev-1_1_1_1_1_1_1
 
168
         |                 |              |
 
169
         +-----------------+              |
 
170
         |                                |
 
171
        rev-5                             |
 
172
         |                                |
 
173
         +--------------------------------+
 
174
         |
 
175
        rev-6
 
176
        """
 
177
        tree1, tree2 = self.create_merged_trees()
 
178
 
 
179
        tree3 = tree2.bzrdir.clone('tree3').open_workingtree()
 
180
 
 
181
        tree2.commit('noop', rev_id='rev-1_1_2')
 
182
        self.assertEqual(0, tree1.merge_from_branch(tree2.branch))
 
183
        tree1.commit('noop merge', rev_id='rev-4')
 
184
 
 
185
        self.build_tree_contents([('tree3/a', 'first\nthird\nfourth\n')])
 
186
        tree3.commit('four', rev_id='rev-1_1_1_1_1',
 
187
                     committer='jerry@foo.com',
 
188
                     timestamp=1166046003.00, timezone=0)
 
189
 
 
190
        tree4 = tree3.bzrdir.clone('tree4').open_workingtree()
 
191
 
 
192
        tree3.commit('noop', rev_id='rev-1_1_1_1_2',
 
193
                     committer='jerry@foo.com',
 
194
                     timestamp=1166046004.00, timezone=0)
 
195
        self.assertEqual(0, tree1.merge_from_branch(tree3.branch))
 
196
        tree1.commit('merge four', rev_id='rev-5')
 
197
 
 
198
        self.build_tree_contents([('tree4/a',
 
199
                                   'first\nthird\nfourth\nfifth\nsixth\n')])
 
200
        tree4.commit('five and six', rev_id='rev-1_1_1_1_1_1_1',
 
201
                     committer='george@foo.com',
 
202
                     timestamp=1166046005.00, timezone=0)
 
203
        self.assertEqual(0, tree1.merge_from_branch(tree4.branch))
 
204
        tree1.commit('merge five and six', rev_id='rev-6')
 
205
        return tree1
 
206
 
 
207
    def test_annotate_shows_dotted_revnos(self):
 
208
        tree1, tree2 = self.create_merged_trees()
 
209
 
 
210
        sio = StringIO()
 
211
        annotate.annotate_file(tree1.branch, 'rev-3', 'a-id',
 
212
                               to_file=sio)
 
213
        self.assertEqualDiff('1     joe@foo | first\n'
 
214
                             '2     joe@foo | second\n'
 
215
                             '1.1.1 barry@f | third\n',
 
216
                             sio.getvalue())
 
217
 
 
218
    def test_annotate_limits_dotted_revnos(self):
 
219
        """Annotate should limit dotted revnos to a depth of 12"""
 
220
        tree1 = self.create_deeply_merged_trees()
 
221
 
 
222
        sio = StringIO()
 
223
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
224
                               to_file=sio, verbose=False, full=False)
 
225
        self.assertEqualDiff('1            joe@foo | first\n'
 
226
                             '2            joe@foo | second\n'
 
227
                             '1.1.1        barry@f | third\n'
 
228
                             '1.1.1.1.1    jerry@f | fourth\n'
 
229
                             '1.1.1.1.1.1> george@ | fifth\n'
 
230
                             '                     | sixth\n',
 
231
                             sio.getvalue())
 
232
 
 
233
        sio = StringIO()
 
234
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
235
                               to_file=sio, verbose=False, full=True)
 
236
        self.assertEqualDiff('1            joe@foo | first\n'
 
237
                             '2            joe@foo | second\n'
 
238
                             '1.1.1        barry@f | third\n'
 
239
                             '1.1.1.1.1    jerry@f | fourth\n'
 
240
                             '1.1.1.1.1.1> george@ | fifth\n'
 
241
                             '1.1.1.1.1.1> george@ | sixth\n',
 
242
                             sio.getvalue())
 
243
 
 
244
        # verbose=True shows everything, the full revno, user id, and date
 
245
        sio = StringIO()
 
246
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
247
                               to_file=sio, verbose=True, full=False)
 
248
        self.assertEqualDiff('1             joe@foo.com    20061213 | first\n'
 
249
                             '2             joe@foo.com    20061213 | second\n'
 
250
                             '1.1.1         barry@foo.com  20061213 | third\n'
 
251
                             '1.1.1.1.1     jerry@foo.com  20061213 | fourth\n'
 
252
                             '1.1.1.1.1.1.1 george@foo.com 20061213 | fifth\n'
 
253
                             '                                      | sixth\n',
 
254
                             sio.getvalue())
 
255
 
 
256
        sio = StringIO()
 
257
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
258
                               to_file=sio, verbose=True, full=True)
 
259
        self.assertEqualDiff('1             joe@foo.com    20061213 | first\n'
 
260
                             '2             joe@foo.com    20061213 | second\n'
 
261
                             '1.1.1         barry@foo.com  20061213 | third\n'
 
262
                             '1.1.1.1.1     jerry@foo.com  20061213 | fourth\n'
 
263
                             '1.1.1.1.1.1.1 george@foo.com 20061213 | fifth\n'
 
264
                             '1.1.1.1.1.1.1 george@foo.com 20061213 | sixth\n',
 
265
                             sio.getvalue())
 
266
 
 
267
    def test_annotate_uses_branch_context(self):
 
268
        """Dotted revnos should use the Branch context.
 
269
 
 
270
        When annotating a non-mainline revision, the annotation should still
 
271
        use dotted revnos from the mainline.
 
272
        """
 
273
        tree1 = self.create_deeply_merged_trees()
 
274
 
 
275
        sio = StringIO()
 
276
        annotate.annotate_file(tree1.branch, 'rev-1_1_1_1_1_1_1', 'a-id',
 
277
                               to_file=sio, verbose=False, full=False)
 
278
        self.assertEqualDiff('1            joe@foo | first\n'
 
279
                             '1.1.1        barry@f | third\n'
 
280
                             '1.1.1.1.1    jerry@f | fourth\n'
 
281
                             '1.1.1.1.1.1> george@ | fifth\n'
 
282
                             '                     | sixth\n',
 
283
                             sio.getvalue())
 
284
 
 
285
    def test_annotate_show_ids(self):
 
286
        tree1 = self.create_deeply_merged_trees()
 
287
 
 
288
        sio = StringIO()
 
289
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
290
                               to_file=sio, show_ids=True, full=False)
 
291
 
 
292
        # It looks better with real revision ids :)
 
293
        self.assertEqualDiff('            rev-1 | first\n'
 
294
                             '            rev-2 | second\n'
 
295
                             '        rev-1_1_1 | third\n'
 
296
                             '    rev-1_1_1_1_1 | fourth\n'
 
297
                             'rev-1_1_1_1_1_1_1 | fifth\n'
 
298
                             '                  | sixth\n',
 
299
                             sio.getvalue())
 
300
 
 
301
        sio = StringIO()
 
302
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
 
303
                               to_file=sio, show_ids=True, full=True)
 
304
 
 
305
        self.assertEqualDiff('            rev-1 | first\n'
 
306
                             '            rev-2 | second\n'
 
307
                             '        rev-1_1_1 | third\n'
 
308
                             '    rev-1_1_1_1_1 | fourth\n'
 
309
                             'rev-1_1_1_1_1_1_1 | fifth\n'
 
310
                             'rev-1_1_1_1_1_1_1 | sixth\n',
 
311
                             sio.getvalue())
 
312
 
 
313
    def test_annotate_unicode_author(self):
 
314
        tree1 = self.make_branch_and_tree('tree1')
 
315
 
 
316
        self.build_tree_contents([('tree1/a', 'adi\xc3\xb3s')])
 
317
        tree1.add(['a'], ['a-id'])
 
318
        tree1.commit('a', rev_id='rev-1',
 
319
                     committer=u'Pepe P\xe9rez <pperez@ejemplo.com>',
 
320
                     timestamp=1166046000.00, timezone=0)
 
321
 
 
322
        self.build_tree_contents([('tree1/b', 'bye')])
 
323
        tree1.add(['b'], ['b-id'])
 
324
        tree1.commit('b', rev_id='rev-2',
 
325
                     committer=u'p\xe9rez',
 
326
                     timestamp=1166046000.00, timezone=0)
 
327
 
 
328
        # this passes if no exception is raised
 
329
        to_file = StringIO()
 
330
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
 
331
 
 
332
        sio = StringIO()
 
333
        to_file = codecs.getwriter('ascii')(sio)
 
334
        to_file.encoding = 'ascii' # codecs does not set it
 
335
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
336
        self.assertEqualDiff('2   p?rez   | bye\n', sio.getvalue())
 
337
 
 
338
        # test now with to_file.encoding = None
 
339
        to_file = tests.StringIOWrapper()
 
340
        to_file.encoding = None
 
341
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
342
        self.assertContainsRe('2   p.rez   | bye\n', to_file.getvalue())
 
343
 
 
344
        # and when it does not exist
 
345
        to_file = StringIO()
 
346
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
347
        self.assertContainsRe('2   p.rez   | bye\n', to_file.getvalue())
 
348
 
 
349
    def test_annotate_author_or_committer(self):
 
350
        tree1 = self.make_branch_and_tree('tree1')
 
351
 
 
352
        self.build_tree_contents([('tree1/a', 'hello')])
 
353
        tree1.add(['a'], ['a-id'])
 
354
        tree1.commit('a', rev_id='rev-1',
 
355
                     committer='Committer <committer@example.com>',
 
356
                     timestamp=1166046000.00, timezone=0)
 
357
 
 
358
        self.build_tree_contents([('tree1/b', 'bye')])
 
359
        tree1.add(['b'], ['b-id'])
 
360
        tree1.commit('b', rev_id='rev-2',
 
361
                     committer='Committer <committer@example.com>',
 
362
                     author='Author <author@example.com>',
 
363
                     timestamp=1166046000.00, timezone=0)
 
364
 
 
365
        to_file = StringIO()
 
366
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
 
367
        self.assertEqual('1   committ | hello\n', to_file.getvalue())
 
368
 
 
369
        to_file = StringIO()
 
370
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
 
371
        self.assertEqual('2   author@ | bye\n', to_file.getvalue())
 
372
 
 
373
 
 
374
class TestReannotate(tests.TestCase):
 
375
 
 
376
    def annotateEqual(self, expected, parents, newlines, revision_id,
 
377
                      blocks=None):
 
378
        annotate_list = list(annotate.reannotate(parents, newlines,
 
379
                             revision_id, blocks))
 
380
        self.assertEqual(len(expected), len(annotate_list))
 
381
        for e, a in zip(expected, annotate_list):
 
382
            self.assertEqual(e, a)
 
383
 
 
384
    def test_reannotate(self):
 
385
        self.annotateEqual(parent_1, [parent_1], new_1, 'blahblah')
 
386
        self.annotateEqual(expected_2_1, [parent_2], new_1, 'blahblah')
 
387
        self.annotateEqual(expected_1_2_2, [parent_1, parent_2], new_2, 
 
388
                           'blahblah')
 
389
 
 
390
    def test_reannotate_no_parents(self):
 
391
        self.annotateEqual(expected_1, [], new_1, 'blahblah')
 
392
 
 
393
    def test_reannotate_left_matching_blocks(self):
 
394
        """Ensure that left_matching_blocks has an impact.
 
395
 
 
396
        In this case, the annotation is ambiguous, so the hint isn't actually
 
397
        lying.
 
398
        """
 
399
        parent = [('rev1', 'a\n')]
 
400
        new_text = ['a\n', 'a\n']
 
401
        blocks = [(0, 0, 1), (1, 2, 0)]
 
402
        self.annotateEqual([('rev1', 'a\n'), ('rev2', 'a\n')], [parent],
 
403
                           new_text, 'rev2', blocks)
 
404
        blocks = [(0, 1, 1), (1, 2, 0)]
 
405
        self.annotateEqual([('rev2', 'a\n'), ('rev1', 'a\n')], [parent],
 
406
                           new_text, 'rev2', blocks)