~bzr-pqm/bzr/bzr.dev

2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
19
import codecs
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
20
from cStringIO import StringIO
21
22
from bzrlib import (
23
    annotate,
24
    conflicts,
25
    errors,
26
    tests,
27
    trace,
28
    )
29
30
1551.9.19 by Aaron Bentley
Merge from bzr.dev
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
2770.1.1 by Aaron Bentley
Initial implmentation of plain knit annotation
90
expected_1 = annotation("""\
91
blahblah a
92
blahblah b
93
blahblah c
94
blahblah d
95
blahblah e
96
""")
97
1551.9.19 by Aaron Bentley
Merge from bzr.dev
98
99
new_2 = """\
100
a
101
b
102
c
103
d
104
e
105
g
106
h
107
""".splitlines(True)
108
109
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
110
class TestAnnotate(tests.TestCaseWithTransport):
111
112
    def create_merged_trees(self):
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
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
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
128
                     committer="joe@foo.com",
129
                     timestamp=1166046000.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
135
                     committer='joe@foo.com',
136
                     timestamp=1166046001.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
137
138
        self.build_tree_contents([('tree2/a', 'first\nthird\n')])
139
        tree2.commit('c', rev_id='rev-1_1_1',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
140
                     committer="barry@foo.com",
141
                     timestamp=1166046002.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
150
                     committer='sal@foo.com',
151
                     timestamp=1166046003.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
152
        return tree1, tree2
153
154
    def create_deeply_merged_trees(self):
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
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
        """
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
177
        tree1, tree2 = self.create_merged_trees()
178
179
        tree3 = tree2.bzrdir.clone('tree3').open_workingtree()
180
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
181
        tree2.commit('noop', rev_id='rev-1_1_2')
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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')])
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
186
        tree3.commit('four', rev_id='rev-1_1_1_1_1',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
187
                     committer='jerry@foo.com',
188
                     timestamp=1166046003.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
189
190
        tree4 = tree3.bzrdir.clone('tree4').open_workingtree()
191
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
192
        tree3.commit('noop', rev_id='rev-1_1_1_1_2',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
193
                     committer='jerry@foo.com',
194
                     timestamp=1166046004.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
195
        self.assertEqual(0, tree1.merge_from_branch(tree3.branch))
196
        tree1.commit('merge four', rev_id='rev-5')
197
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
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',
2182.3.12 by John Arbash Meinel
Force the timezone properly during tests which look at dates.
201
                     committer='george@foo.com',
202
                     timestamp=1166046005.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
203
        self.assertEqual(0, tree1.merge_from_branch(tree4.branch))
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
204
        tree1.commit('merge five and six', rev_id='rev-6')
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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',
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
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',
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
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',
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
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())
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
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
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
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',
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
311
                             sio.getvalue())
1551.9.19 by Aaron Bentley
Merge from bzr.dev
312
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
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
2593.1.2 by Adeodato Simó
Improve tests a bit, actually checking for the replace encoding.
328
        # this passes if no exception is raised
329
        to_file = StringIO()
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
330
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
331
2593.1.2 by Adeodato Simó
Improve tests a bit, actually checking for the replace encoding.
332
        sio = StringIO()
333
        to_file = codecs.getwriter('ascii')(sio)
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
334
        to_file.encoding = 'ascii' # codecs does not set it
335
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
2593.1.2 by Adeodato Simó
Improve tests a bit, actually checking for the replace encoding.
336
        self.assertEqualDiff('2   p?rez   | bye\n', sio.getvalue())
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
337
2593.1.3 by Adeodato Simó
Cope with to_file.encoding being None or not present.
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)
2593.1.5 by Adeodato Simó
Fix copy&paste bug in test, catched by John.
342
        self.assertContainsRe('2   p.rez   | bye\n', to_file.getvalue())
2593.1.3 by Adeodato Simó
Cope with to_file.encoding being None or not present.
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)
2593.1.5 by Adeodato Simó
Fix copy&paste bug in test, catched by John.
347
        self.assertContainsRe('2   p.rez   | bye\n', to_file.getvalue())
2593.1.3 by Adeodato Simó
Cope with to_file.encoding being None or not present.
348
2671.5.3 by Lukáš Lalinsky
Use the author name in annotate.
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
1551.9.19 by Aaron Bentley
Merge from bzr.dev
373
374
class TestReannotate(tests.TestCase):
375
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
376
    def annotateEqual(self, expected, parents, newlines, revision_id,
377
                      blocks=None):
1551.9.19 by Aaron Bentley
Merge from bzr.dev
378
        annotate_list = list(annotate.reannotate(parents, newlines,
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
379
                             revision_id, blocks))
1551.9.19 by Aaron Bentley
Merge from bzr.dev
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')
2770.1.1 by Aaron Bentley
Initial implmentation of plain knit annotation
389
390
    def test_reannotate_no_parents(self):
391
        self.annotateEqual(expected_1, [], new_1, 'blahblah')
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
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)