~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)
3010.1.1 by Robert Collins
Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
152
        tree1.lock_read()
153
        self.addCleanup(tree1.unlock)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
154
        return tree1, tree2
155
156
    def create_deeply_merged_trees(self):
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
157
        """Create some trees with a more complex merge history.
158
159
        rev-1 --+
160
         |      |
161
        rev-2  rev-1_1_1 --+
162
         |      |          |
163
         +------+          |
164
         |      |          |
165
        rev-3  rev-1_1_2  rev-1_1_1_1_1 --+
166
         |      |          |              |
167
         +------+          |              |
168
         |                 |              |
169
        rev-4             rev-1_1_1_1_2  rev-1_1_1_1_1_1_1
170
         |                 |              |
171
         +-----------------+              |
172
         |                                |
173
        rev-5                             |
174
         |                                |
175
         +--------------------------------+
176
         |
177
        rev-6
178
        """
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
179
        tree1, tree2 = self.create_merged_trees()
3010.1.1 by Robert Collins
Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
180
        tree1.unlock()
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
181
182
        tree3 = tree2.bzrdir.clone('tree3').open_workingtree()
183
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
184
        tree2.commit('noop', rev_id='rev-1_1_2')
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
185
        self.assertEqual(0, tree1.merge_from_branch(tree2.branch))
186
        tree1.commit('noop merge', rev_id='rev-4')
187
188
        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
189
        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.
190
                     committer='jerry@foo.com',
191
                     timestamp=1166046003.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
192
193
        tree4 = tree3.bzrdir.clone('tree4').open_workingtree()
194
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
195
        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.
196
                     committer='jerry@foo.com',
197
                     timestamp=1166046004.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
198
        self.assertEqual(0, tree1.merge_from_branch(tree3.branch))
199
        tree1.commit('merge four', rev_id='rev-5')
200
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
201
        self.build_tree_contents([('tree4/a',
202
                                   'first\nthird\nfourth\nfifth\nsixth\n')])
203
        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.
204
                     committer='george@foo.com',
205
                     timestamp=1166046005.00, timezone=0)
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
206
        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
207
        tree1.commit('merge five and six', rev_id='rev-6')
3010.1.1 by Robert Collins
Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
208
        tree1.lock_read()
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
209
        return tree1
210
211
    def test_annotate_shows_dotted_revnos(self):
212
        tree1, tree2 = self.create_merged_trees()
213
214
        sio = StringIO()
215
        annotate.annotate_file(tree1.branch, 'rev-3', 'a-id',
216
                               to_file=sio)
217
        self.assertEqualDiff('1     joe@foo | first\n'
218
                             '2     joe@foo | second\n'
219
                             '1.1.1 barry@f | third\n',
220
                             sio.getvalue())
221
222
    def test_annotate_limits_dotted_revnos(self):
223
        """Annotate should limit dotted revnos to a depth of 12"""
224
        tree1 = self.create_deeply_merged_trees()
225
226
        sio = StringIO()
227
        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
228
                               to_file=sio, verbose=False, full=False)
229
        self.assertEqualDiff('1            joe@foo | first\n'
230
                             '2            joe@foo | second\n'
231
                             '1.1.1        barry@f | third\n'
232
                             '1.1.1.1.1    jerry@f | fourth\n'
233
                             '1.1.1.1.1.1> george@ | fifth\n'
234
                             '                     | sixth\n',
235
                             sio.getvalue())
236
237
        sio = StringIO()
238
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
239
                               to_file=sio, verbose=False, full=True)
240
        self.assertEqualDiff('1            joe@foo | first\n'
241
                             '2            joe@foo | second\n'
242
                             '1.1.1        barry@f | third\n'
243
                             '1.1.1.1.1    jerry@f | fourth\n'
244
                             '1.1.1.1.1.1> george@ | fifth\n'
245
                             '1.1.1.1.1.1> george@ | sixth\n',
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
246
                             sio.getvalue())
247
248
        # verbose=True shows everything, the full revno, user id, and date
249
        sio = StringIO()
250
        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
251
                               to_file=sio, verbose=True, full=False)
252
        self.assertEqualDiff('1             joe@foo.com    20061213 | first\n'
253
                             '2             joe@foo.com    20061213 | second\n'
254
                             '1.1.1         barry@foo.com  20061213 | third\n'
255
                             '1.1.1.1.1     jerry@foo.com  20061213 | fourth\n'
256
                             '1.1.1.1.1.1.1 george@foo.com 20061213 | fifth\n'
257
                             '                                      | sixth\n',
258
                             sio.getvalue())
259
260
        sio = StringIO()
261
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
262
                               to_file=sio, verbose=True, full=True)
263
        self.assertEqualDiff('1             joe@foo.com    20061213 | first\n'
264
                             '2             joe@foo.com    20061213 | second\n'
265
                             '1.1.1         barry@foo.com  20061213 | third\n'
266
                             '1.1.1.1.1     jerry@foo.com  20061213 | fourth\n'
267
                             '1.1.1.1.1.1.1 george@foo.com 20061213 | fifth\n'
268
                             '1.1.1.1.1.1.1 george@foo.com 20061213 | sixth\n',
269
                             sio.getvalue())
2245.3.1 by John Arbash Meinel
bzr annotate should use Branch's dotted revnos.
270
271
    def test_annotate_uses_branch_context(self):
272
        """Dotted revnos should use the Branch context.
273
274
        When annotating a non-mainline revision, the annotation should still
275
        use dotted revnos from the mainline.
276
        """
277
        tree1 = self.create_deeply_merged_trees()
278
279
        sio = StringIO()
280
        annotate.annotate_file(tree1.branch, 'rev-1_1_1_1_1_1_1', 'a-id',
281
                               to_file=sio, verbose=False, full=False)
282
        self.assertEqualDiff('1            joe@foo | first\n'
283
                             '1.1.1        barry@f | third\n'
284
                             '1.1.1.1.1    jerry@f | fourth\n'
285
                             '1.1.1.1.1.1> george@ | fifth\n'
286
                             '                     | sixth\n',
287
                             sio.getvalue())
288
2182.3.4 by John Arbash Meinel
add show-ids and test that nearby areas are collapsed without full
289
    def test_annotate_show_ids(self):
290
        tree1 = self.create_deeply_merged_trees()
291
292
        sio = StringIO()
293
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
294
                               to_file=sio, show_ids=True, full=False)
295
296
        # It looks better with real revision ids :)
297
        self.assertEqualDiff('            rev-1 | first\n'
298
                             '            rev-2 | second\n'
299
                             '        rev-1_1_1 | third\n'
300
                             '    rev-1_1_1_1_1 | fourth\n'
301
                             'rev-1_1_1_1_1_1_1 | fifth\n'
302
                             '                  | sixth\n',
303
                             sio.getvalue())
304
305
        sio = StringIO()
306
        annotate.annotate_file(tree1.branch, 'rev-6', 'a-id',
307
                               to_file=sio, show_ids=True, full=True)
308
309
        self.assertEqualDiff('            rev-1 | first\n'
310
                             '            rev-2 | second\n'
311
                             '        rev-1_1_1 | third\n'
312
                             '    rev-1_1_1_1_1 | fourth\n'
313
                             'rev-1_1_1_1_1_1_1 | fifth\n'
314
                             'rev-1_1_1_1_1_1_1 | sixth\n',
2182.3.3 by John Arbash Meinel
Add tests for annotate with dotted revnos.
315
                             sio.getvalue())
1551.9.19 by Aaron Bentley
Merge from bzr.dev
316
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
317
    def test_annotate_unicode_author(self):
318
        tree1 = self.make_branch_and_tree('tree1')
319
320
        self.build_tree_contents([('tree1/a', 'adi\xc3\xb3s')])
321
        tree1.add(['a'], ['a-id'])
322
        tree1.commit('a', rev_id='rev-1',
323
                     committer=u'Pepe P\xe9rez <pperez@ejemplo.com>',
324
                     timestamp=1166046000.00, timezone=0)
325
326
        self.build_tree_contents([('tree1/b', 'bye')])
327
        tree1.add(['b'], ['b-id'])
328
        tree1.commit('b', rev_id='rev-2',
329
                     committer=u'p\xe9rez',
330
                     timestamp=1166046000.00, timezone=0)
331
3010.1.1 by Robert Collins
Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
332
        tree1.lock_read()
333
        self.addCleanup(tree1.unlock)
2593.1.2 by Adeodato Simó
Improve tests a bit, actually checking for the replace encoding.
334
        # this passes if no exception is raised
335
        to_file = StringIO()
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
336
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
337
2593.1.2 by Adeodato Simó
Improve tests a bit, actually checking for the replace encoding.
338
        sio = StringIO()
339
        to_file = codecs.getwriter('ascii')(sio)
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
340
        to_file.encoding = 'ascii' # codecs does not set it
341
        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.
342
        self.assertEqualDiff('2   p?rez   | bye\n', sio.getvalue())
2593.1.1 by Adeodato Simó
Improve annotate to prevent unicode exceptions in certain situations.
343
2593.1.3 by Adeodato Simó
Cope with to_file.encoding being None or not present.
344
        # test now with to_file.encoding = None
345
        to_file = tests.StringIOWrapper()
346
        to_file.encoding = None
347
        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.
348
        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.
349
350
        # and when it does not exist
351
        to_file = StringIO()
352
        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.
353
        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.
354
2671.5.3 by Lukáš Lalinsky
Use the author name in annotate.
355
    def test_annotate_author_or_committer(self):
356
        tree1 = self.make_branch_and_tree('tree1')
357
358
        self.build_tree_contents([('tree1/a', 'hello')])
359
        tree1.add(['a'], ['a-id'])
360
        tree1.commit('a', rev_id='rev-1',
361
                     committer='Committer <committer@example.com>',
362
                     timestamp=1166046000.00, timezone=0)
363
364
        self.build_tree_contents([('tree1/b', 'bye')])
365
        tree1.add(['b'], ['b-id'])
366
        tree1.commit('b', rev_id='rev-2',
367
                     committer='Committer <committer@example.com>',
368
                     author='Author <author@example.com>',
369
                     timestamp=1166046000.00, timezone=0)
370
3010.1.1 by Robert Collins
Lock the tree's used to test annotate_file, and add a docstring for annotate_file explaining its needs.
371
        tree1.lock_read()
372
        self.addCleanup(tree1.unlock)
2671.5.3 by Lukáš Lalinsky
Use the author name in annotate.
373
        to_file = StringIO()
374
        annotate.annotate_file(tree1.branch, 'rev-1', 'a-id', to_file=to_file)
375
        self.assertEqual('1   committ | hello\n', to_file.getvalue())
376
377
        to_file = StringIO()
378
        annotate.annotate_file(tree1.branch, 'rev-2', 'b-id', to_file=to_file)
379
        self.assertEqual('2   author@ | bye\n', to_file.getvalue())
380
1551.9.19 by Aaron Bentley
Merge from bzr.dev
381
382
class TestReannotate(tests.TestCase):
383
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
384
    def annotateEqual(self, expected, parents, newlines, revision_id,
385
                      blocks=None):
1551.9.19 by Aaron Bentley
Merge from bzr.dev
386
        annotate_list = list(annotate.reannotate(parents, newlines,
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
387
                             revision_id, blocks))
1551.9.19 by Aaron Bentley
Merge from bzr.dev
388
        self.assertEqual(len(expected), len(annotate_list))
389
        for e, a in zip(expected, annotate_list):
390
            self.assertEqual(e, a)
391
392
    def test_reannotate(self):
393
        self.annotateEqual(parent_1, [parent_1], new_1, 'blahblah')
394
        self.annotateEqual(expected_2_1, [parent_2], new_1, 'blahblah')
395
        self.annotateEqual(expected_1_2_2, [parent_1, parent_2], new_2, 
396
                           'blahblah')
2770.1.1 by Aaron Bentley
Initial implmentation of plain knit annotation
397
398
    def test_reannotate_no_parents(self):
399
        self.annotateEqual(expected_1, [], new_1, 'blahblah')
2770.1.5 by Aaron Bentley
Clean up docs, test matching blocks for reannotate
400
401
    def test_reannotate_left_matching_blocks(self):
402
        """Ensure that left_matching_blocks has an impact.
403
404
        In this case, the annotation is ambiguous, so the hint isn't actually
405
        lying.
406
        """
407
        parent = [('rev1', 'a\n')]
408
        new_text = ['a\n', 'a\n']
409
        blocks = [(0, 0, 1), (1, 2, 0)]
410
        self.annotateEqual([('rev1', 'a\n'), ('rev2', 'a\n')], [parent],
411
                           new_text, 'rev2', blocks)
412
        blocks = [(0, 1, 1), (1, 2, 0)]
413
        self.annotateEqual([('rev2', 'a\n'), ('rev1', 'a\n')], [parent],
414
                           new_text, 'rev2', blocks)