13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
from cStringIO import StringIO
30
class TestCaseForLogFormatter(tests.TestCaseWithTransport):
33
super(TestCaseForLogFormatter, self).setUp()
34
# keep a reference to the "current" custom prop. handler registry
35
self.properties_handler_registry = log.properties_handler_registry
36
# Use a clean registry for log
37
log.properties_handler_registry = registry.Registry()
40
log.properties_handler_registry = self.properties_handler_registry
41
self.addCleanup(restore)
43
def assertFormatterResult(self, result, branch, formatter_class,
44
formatter_kwargs=None, show_log_kwargs=None,
46
logfile = self.make_utf8_encoded_stringio()
47
if formatter_kwargs is None:
49
formatter = formatter_class(to_file=logfile, **formatter_kwargs)
50
if show_log_kwargs is None:
52
log.show_log(branch, formatter, **show_log_kwargs)
53
log_content = logfile.getvalue()
55
log_content = normalize_log(log_content)
56
self.assertEqualDiff(result, log_content)
58
def make_standard_commit(self, branch_nick, **kwargs):
59
wt = self.make_branch_and_tree('.')
61
self.addCleanup(wt.unlock)
62
self.build_tree(['a'])
64
wt.branch.nick = branch_nick
66
kwargs.setdefault('message', 'add a')
67
kwargs.setdefault('timestamp', 1132711707)
68
kwargs.setdefault('timezone', 36000)
69
kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
70
kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
74
def _prepare_tree_with_merges(self, with_tags=False):
75
wt = self.make_branch_and_memory_tree('.')
77
self.addCleanup(wt.unlock)
79
wt.commit('rev-1', rev_id='rev-1',
80
timestamp=1132586655, timezone=36000,
81
committer='Joe Foo <joe@foo.com>')
82
wt.commit('rev-merged', rev_id='rev-2a',
83
timestamp=1132586700, timezone=36000,
84
committer='Joe Foo <joe@foo.com>')
85
wt.set_parent_ids(['rev-1', 'rev-2a'])
86
wt.branch.set_last_revision_info(1, 'rev-1')
87
wt.commit('rev-2', rev_id='rev-2b',
88
timestamp=1132586800, timezone=36000,
89
committer='Joe Foo <joe@foo.com>')
92
branch.tags.set_tag('v0.2', 'rev-2b')
93
wt.commit('rev-3', rev_id='rev-3',
94
timestamp=1132586900, timezone=36000,
95
committer='Jane Foo <jane@foo.com>')
96
branch.tags.set_tag('v1.0rc1', 'rev-3')
97
branch.tags.set_tag('v1.0', 'rev-3')
103
class LogCatcher(log.LogFormatter):
104
"""Pull log messages into a list rather than displaying them.
106
To simplify testing we save logged revisions here rather than actually
107
formatting anything, so that we can precisely check the result without
108
being dependent on the formatting.
20
from bzrlib import log
21
from bzrlib.tests import TestCase, TestCaseWithTransport
22
from bzrlib.log import (show_log,
29
from bzrlib.branch import Branch
30
from bzrlib.errors import (
32
InvalidRevisionNumber,
34
from bzrlib.revision import Revision
35
from bzrlib.revisionspec import (
41
class LogCatcher(LogFormatter):
42
"""Pull log messages into list rather than displaying them.
44
For ease of testing we save log messages here rather than actually
45
formatting them, so that we can precisely check the result without
46
being too dependent on the exact formatting.
48
We should also test the LogFormatter.
111
51
supports_delta = True
113
53
def __init__(self):
114
54
super(LogCatcher, self).__init__(to_file=None)
117
57
def log_revision(self, revision):
118
self.revisions.append(revision)
121
class TestShowLog(tests.TestCaseWithTransport):
58
self.logs.append(revision)
61
class TestShowLog(TestCaseWithTransport):
123
63
def checkDelta(self, delta, **kw):
124
"""Check the filenames touched by a delta are as expected.
126
Caller only have to pass in the list of files for each part, all
127
unspecified parts are considered empty (and checked as such).
64
"""Check the filenames touched by a delta are as expected."""
129
65
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
130
# By default we expect an empty list
131
66
expected = kw.get(n, [])
132
67
# strip out only the path components
133
68
got = [x[0] for x in getattr(delta, n)]
134
self.assertEqual(expected, got)
136
def assertInvalidRevisonNumber(self, br, start, end):
138
self.assertRaises(errors.InvalidRevisionNumber,
139
log.show_log, br, lf,
140
start_revision=start, end_revision=end)
69
self.assertEquals(expected, got)
142
71
def test_cur_revno(self):
143
72
wt = self.make_branch_and_tree('.')
147
76
wt.commit('empty commit')
148
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
150
# Since there is a single revision in the branch all the combinations
152
self.assertInvalidRevisonNumber(b, 2, 1)
153
self.assertInvalidRevisonNumber(b, 1, 2)
154
self.assertInvalidRevisonNumber(b, 0, 2)
155
self.assertInvalidRevisonNumber(b, 1, 0)
156
self.assertInvalidRevisonNumber(b, -1, 1)
157
self.assertInvalidRevisonNumber(b, 1, -1)
159
def test_empty_branch(self):
77
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
78
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
79
start_revision=2, end_revision=1)
80
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
81
start_revision=1, end_revision=2)
82
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
83
start_revision=0, end_revision=2)
84
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
85
start_revision=1, end_revision=0)
86
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
87
start_revision=-1, end_revision=1)
88
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
89
start_revision=1, end_revision=-1)
91
def test_simple_log(self):
92
eq = self.assertEquals
160
94
wt = self.make_branch_and_tree('.')
163
log.show_log(wt.branch, lf)
165
self.assertEqual([], lf.revisions)
167
def test_empty_commit(self):
168
wt = self.make_branch_and_tree('.')
170
102
wt.commit('empty commit')
171
103
lf = LogCatcher()
172
log.show_log(wt.branch, lf, verbose=True)
174
self.assertEqual(1, len(revs))
175
self.assertEqual('1', revs[0].revno)
176
self.assertEqual('empty commit', revs[0].rev.message)
177
self.checkDelta(revs[0].delta)
104
show_log(b, lf, verbose=True)
106
eq(lf.logs[0].revno, '1')
107
eq(lf.logs[0].rev.message, 'empty commit')
109
self.log('log delta: %r' % d)
179
def test_simple_commit(self):
180
wt = self.make_branch_and_tree('.')
181
wt.commit('empty commit')
182
112
self.build_tree(['hello'])
184
114
wt.commit('add one file',
185
115
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
186
116
u'<test@example.com>')
118
lf = self.make_utf8_encoded_stringio()
119
# log using regular thing
120
show_log(b, LongLogFormatter(lf))
122
for l in lf.readlines():
125
# get log as data structure
187
126
lf = LogCatcher()
188
log.show_log(wt.branch, lf, verbose=True)
189
self.assertEqual(2, len(lf.revisions))
127
show_log(b, lf, verbose=True)
129
self.log('log entries:')
130
for logentry in lf.logs:
131
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
190
133
# first one is most recent
191
log_entry = lf.revisions[0]
192
self.assertEqual('2', log_entry.revno)
193
self.assertEqual('add one file', log_entry.rev.message)
194
self.checkDelta(log_entry.delta, added=['hello'])
196
def test_commit_message_with_control_chars(self):
197
wt = self.make_branch_and_tree('.')
198
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
199
msg = msg.replace(u'\r', u'\n')
134
logentry = lf.logs[0]
135
eq(logentry.revno, '2')
136
eq(logentry.rev.message, 'add one file')
138
self.log('log 2 delta: %r' % d)
139
self.checkDelta(d, added=['hello'])
141
# commit a log message with control characters
142
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
143
self.log("original commit message: %r", msg)
201
145
lf = LogCatcher()
202
log.show_log(wt.branch, lf, verbose=True)
203
committed_msg = lf.revisions[0].rev.message
204
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
205
self.assertNotEqual(msg, committed_msg)
206
self.assertTrue(len(committed_msg) > len(msg))
208
self.assertEqual(msg, committed_msg)
146
show_log(b, lf, verbose=True)
147
committed_msg = lf.logs[0].rev.message
148
self.log("escaped commit message: %r", committed_msg)
149
self.assert_(msg != committed_msg)
150
self.assert_(len(committed_msg) > len(msg))
210
def test_commit_message_without_control_chars(self):
211
wt = self.make_branch_and_tree('.')
152
# Check that log message with only XML-valid characters isn't
212
153
# escaped. As ElementTree apparently does some kind of
213
154
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
214
155
# included in the test commit message, even though they are
215
156
# valid XML 1.0 characters.
216
157
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
158
self.log("original commit message: %r", msg)
218
160
lf = LogCatcher()
219
log.show_log(wt.branch, lf, verbose=True)
220
committed_msg = lf.revisions[0].rev.message
221
self.assertEqual(msg, committed_msg)
161
show_log(b, lf, verbose=True)
162
committed_msg = lf.logs[0].rev.message
163
self.log("escaped commit message: %r", committed_msg)
164
self.assert_(msg == committed_msg)
223
166
def test_deltas_in_merge_revisions(self):
224
167
"""Check deltas created for both mainline and merge revisions"""
168
eq = self.assertEquals
225
169
wt = self.make_branch_and_tree('parent')
226
170
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
240
184
lf = LogCatcher()
241
185
lf.supports_merge_revisions = True
242
log.show_log(b, lf, verbose=True)
245
self.assertEqual(3, len(revs))
248
self.assertEqual('2', logentry.revno)
249
self.assertEqual('merge child branch', logentry.rev.message)
250
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
253
self.assertEqual('1.1.1', logentry.revno)
254
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
255
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
258
self.assertEqual('1', logentry.revno)
259
self.assertEqual('add file1 and file2', logentry.rev.message)
260
self.checkDelta(logentry.delta, added=['file1', 'file2'])
186
show_log(b, lf, verbose=True)
188
logentry = lf.logs[0]
189
eq(logentry.revno, '2')
190
eq(logentry.rev.message, 'merge child branch')
192
self.checkDelta(d, removed=['file1'], modified=['file2'])
193
logentry = lf.logs[1]
194
eq(logentry.revno, '1.1.1')
195
eq(logentry.rev.message, 'remove file1 and modify file2')
197
self.checkDelta(d, removed=['file1'], modified=['file2'])
198
logentry = lf.logs[2]
199
eq(logentry.revno, '1')
200
eq(logentry.rev.message, 'add file1 and file2')
202
self.checkDelta(d, added=['file1', 'file2'])
204
def test_merges_nonsupporting_formatter(self):
205
"""Tests that show_log will raise if the formatter doesn't
206
support merge revisions."""
207
wt = self.make_branch_and_memory_tree('.')
211
wt.commit('rev-1', rev_id='rev-1',
212
timestamp=1132586655, timezone=36000,
213
committer='Joe Foo <joe@foo.com>')
214
wt.commit('rev-merged', rev_id='rev-2a',
215
timestamp=1132586700, timezone=36000,
216
committer='Joe Foo <joe@foo.com>')
217
wt.set_parent_ids(['rev-1', 'rev-2a'])
218
wt.branch.set_last_revision_info(1, 'rev-1')
219
wt.commit('rev-2', rev_id='rev-2b',
220
timestamp=1132586800, timezone=36000,
221
committer='Joe Foo <joe@foo.com>')
222
logfile = self.make_utf8_encoded_stringio()
223
formatter = ShortLogFormatter(to_file=logfile)
226
revspec = RevisionSpec.from_string('1.1.1')
227
rev = revspec.in_history(wtb)
228
self.assertRaises(BzrCommandError, show_log, wtb, lf,
229
start_revision=rev, end_revision=rev)
263
234
def make_commits_with_trailing_newlines(wt):
264
"""Helper method for LogFormatter tests"""
235
"""Helper method for LogFormatter tests"""
267
238
open('a', 'wb').write('hello moto\n')
318
292
1 Joe Foo\t2005-11-21
319
293
simple log message
322
b, log.ShortLogFormatter)
324
297
def test_short_log_with_merges(self):
325
wt = self._prepare_tree_with_merges()
326
self.assertFormatterResult("""\
327
2 Joe Foo\t2005-11-22 [merge]
330
1 Joe Foo\t2005-11-22
334
wt.branch, log.ShortLogFormatter)
336
def test_short_log_with_merges_and_advice(self):
337
wt = self._prepare_tree_with_merges()
338
self.assertFormatterResult("""\
339
2 Joe Foo\t2005-11-22 [merge]
342
1 Joe Foo\t2005-11-22
345
Use --include-merges or -n0 to see merged revisions.
347
wt.branch, log.ShortLogFormatter,
348
formatter_kwargs=dict(show_advice=True))
350
def test_short_log_with_merges_and_range(self):
351
298
wt = self.make_branch_and_memory_tree('.')
353
self.addCleanup(wt.unlock)
355
wt.commit('rev-1', rev_id='rev-1',
356
timestamp=1132586655, timezone=36000,
357
committer='Joe Foo <joe@foo.com>')
358
wt.commit('rev-merged', rev_id='rev-2a',
359
timestamp=1132586700, timezone=36000,
360
committer='Joe Foo <joe@foo.com>')
361
wt.branch.set_last_revision_info(1, 'rev-1')
362
wt.set_parent_ids(['rev-1', 'rev-2a'])
363
wt.commit('rev-2b', rev_id='rev-2b',
364
timestamp=1132586800, timezone=36000,
365
committer='Joe Foo <joe@foo.com>')
366
wt.commit('rev-3a', rev_id='rev-3a',
367
timestamp=1132586800, timezone=36000,
368
committer='Joe Foo <joe@foo.com>')
369
wt.branch.set_last_revision_info(2, 'rev-2b')
370
wt.set_parent_ids(['rev-2b', 'rev-3a'])
371
wt.commit('rev-3b', rev_id='rev-3b',
372
timestamp=1132586800, timezone=36000,
373
committer='Joe Foo <joe@foo.com>')
374
self.assertFormatterResult("""\
375
3 Joe Foo\t2005-11-22 [merge]
302
wt.commit('rev-1', rev_id='rev-1',
303
timestamp=1132586655, timezone=36000,
304
committer='Joe Foo <joe@foo.com>')
305
wt.commit('rev-merged', rev_id='rev-2a',
306
timestamp=1132586700, timezone=36000,
307
committer='Joe Foo <joe@foo.com>')
308
wt.set_parent_ids(['rev-1', 'rev-2a'])
309
wt.branch.set_last_revision_info(1, 'rev-1')
310
wt.commit('rev-2', rev_id='rev-2b',
311
timestamp=1132586800, timezone=36000,
312
committer='Joe Foo <joe@foo.com>')
313
logfile = self.make_utf8_encoded_stringio()
314
formatter = ShortLogFormatter(to_file=logfile)
315
show_log(wt.branch, formatter)
316
self.assertEqualDiff(logfile.getvalue(), """\
378
317
2 Joe Foo\t2005-11-22 [merge]
382
wt.branch, log.ShortLogFormatter,
383
show_log_kwargs=dict(start_revision=2, end_revision=3))
385
def test_short_log_with_tags(self):
386
wt = self._prepare_tree_with_merges(with_tags=True)
387
self.assertFormatterResult("""\
388
3 Jane Foo\t2005-11-22 {v1.0, v1.0rc1}
391
2 Joe Foo\t2005-11-22 {v0.2} [merge]
394
320
1 Joe Foo\t2005-11-22
398
wt.branch, log.ShortLogFormatter)
400
327
def test_short_log_single_merge_revision(self):
401
328
wt = self.make_branch_and_memory_tree('.')
403
self.addCleanup(wt.unlock)
405
wt.commit('rev-1', rev_id='rev-1',
406
timestamp=1132586655, timezone=36000,
407
committer='Joe Foo <joe@foo.com>')
408
wt.commit('rev-merged', rev_id='rev-2a',
409
timestamp=1132586700, timezone=36000,
410
committer='Joe Foo <joe@foo.com>')
411
wt.set_parent_ids(['rev-1', 'rev-2a'])
412
wt.branch.set_last_revision_info(1, 'rev-1')
413
wt.commit('rev-2', rev_id='rev-2b',
414
timestamp=1132586800, timezone=36000,
415
committer='Joe Foo <joe@foo.com>')
416
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
417
rev = revspec.in_history(wt.branch)
418
self.assertFormatterResult("""\
419
1.1.1 Joe Foo\t2005-11-22
423
wt.branch, log.ShortLogFormatter,
424
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
427
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
429
def test_short_merge_revs_log_with_merges(self):
430
wt = self.make_branch_and_memory_tree('.')
432
self.addCleanup(wt.unlock)
434
wt.commit('rev-1', rev_id='rev-1',
435
timestamp=1132586655, timezone=36000,
436
committer='Joe Foo <joe@foo.com>')
437
wt.commit('rev-merged', rev_id='rev-2a',
438
timestamp=1132586700, timezone=36000,
439
committer='Joe Foo <joe@foo.com>')
440
wt.set_parent_ids(['rev-1', 'rev-2a'])
441
wt.branch.set_last_revision_info(1, 'rev-1')
442
wt.commit('rev-2', rev_id='rev-2b',
443
timestamp=1132586800, timezone=36000,
444
committer='Joe Foo <joe@foo.com>')
445
# Note that the 1.1.1 indenting is in fact correct given that
446
# the revision numbers are right justified within 5 characters
447
# for mainline revnos and 9 characters for dotted revnos.
448
self.assertFormatterResult("""\
449
2 Joe Foo\t2005-11-22 [merge]
452
1.1.1 Joe Foo\t2005-11-22
455
1 Joe Foo\t2005-11-22
459
wt.branch, log.ShortLogFormatter,
460
formatter_kwargs=dict(levels=0))
462
def test_short_merge_revs_log_single_merge_revision(self):
463
wt = self.make_branch_and_memory_tree('.')
465
self.addCleanup(wt.unlock)
467
wt.commit('rev-1', rev_id='rev-1',
468
timestamp=1132586655, timezone=36000,
469
committer='Joe Foo <joe@foo.com>')
470
wt.commit('rev-merged', rev_id='rev-2a',
471
timestamp=1132586700, timezone=36000,
472
committer='Joe Foo <joe@foo.com>')
473
wt.set_parent_ids(['rev-1', 'rev-2a'])
474
wt.branch.set_last_revision_info(1, 'rev-1')
475
wt.commit('rev-2', rev_id='rev-2b',
476
timestamp=1132586800, timezone=36000,
477
committer='Joe Foo <joe@foo.com>')
478
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
479
rev = revspec.in_history(wt.branch)
480
self.assertFormatterResult("""\
481
1.1.1 Joe Foo\t2005-11-22
485
wt.branch, log.ShortLogFormatter,
486
formatter_kwargs=dict(levels=0),
487
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
490
class TestLongLogFormatter(TestCaseForLogFormatter):
332
wt.commit('rev-1', rev_id='rev-1',
333
timestamp=1132586655, timezone=36000,
334
committer='Joe Foo <joe@foo.com>')
335
wt.commit('rev-merged', rev_id='rev-2a',
336
timestamp=1132586700, timezone=36000,
337
committer='Joe Foo <joe@foo.com>')
338
wt.set_parent_ids(['rev-1', 'rev-2a'])
339
wt.branch.set_last_revision_info(1, 'rev-1')
340
wt.commit('rev-2', rev_id='rev-2b',
341
timestamp=1132586800, timezone=36000,
342
committer='Joe Foo <joe@foo.com>')
343
logfile = self.make_utf8_encoded_stringio()
344
formatter = ShortLogFormatter(to_file=logfile)
345
revspec = RevisionSpec.from_string('1.1.1')
347
rev = revspec.in_history(wtb)
348
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
349
self.assertEqualDiff(logfile.getvalue(), """\
350
1.1.1 Joe Foo\t2005-11-22
358
class TestLongLogFormatter(TestCaseWithTransport):
492
360
def test_verbose_log(self):
493
361
"""Verbose log includes changed files
497
wt = self.make_standard_commit('test_verbose_log', authors=[])
498
self.assertFormatterResult('''\
365
wt = self.make_branch_and_tree('.')
367
self.build_tree(['a'])
369
# XXX: why does a longer nick show up?
370
b.nick = 'test_verbose_log'
371
wt.commit(message='add a',
372
timestamp=1132711707,
374
committer='Lorem Ipsum <test@example.com>')
375
logfile = file('out.tmp', 'w+')
376
formatter = LongLogFormatter(to_file=logfile)
377
show_log(b, formatter, verbose=True)
380
log_contents = logfile.read()
381
self.assertEqualDiff(log_contents, '''\
499
382
------------------------------------------------------------
501
384
committer: Lorem Ipsum <test@example.com>
645
531
timestamp: Mon 2005-11-21 09:24:15 -0600
647
533
simple log message
649
b, log.LongLogFormatter)
651
536
def test_author_in_log(self):
652
537
"""Log includes the author name if it's set in
653
538
the revision properties
655
wt = self.make_standard_commit('test_author_log',
656
authors=['John Doe <jdoe@example.com>',
657
'Jane Rey <jrey@example.com>'])
658
self.assertFormatterResult("""\
659
------------------------------------------------------------
661
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
662
committer: Lorem Ipsum <test@example.com>
663
branch nick: test_author_log
664
timestamp: Wed 2005-11-23 12:08:27 +1000
668
wt.branch, log.LongLogFormatter)
670
def test_properties_in_log(self):
671
"""Log includes the custom properties returned by the registered
674
wt = self.make_standard_commit('test_properties_in_log')
675
def trivial_custom_prop_handler(revision):
676
return {'test_prop':'test_value'}
678
# Cleaned up in setUp()
679
log.properties_handler_registry.register(
680
'trivial_custom_prop_handler',
681
trivial_custom_prop_handler)
682
self.assertFormatterResult("""\
683
------------------------------------------------------------
685
test_prop: test_value
686
author: John Doe <jdoe@example.com>
687
committer: Lorem Ipsum <test@example.com>
688
branch nick: test_properties_in_log
689
timestamp: Wed 2005-11-23 12:08:27 +1000
693
wt.branch, log.LongLogFormatter)
695
def test_properties_in_short_log(self):
696
"""Log includes the custom properties returned by the registered
699
wt = self.make_standard_commit('test_properties_in_short_log')
700
def trivial_custom_prop_handler(revision):
701
return {'test_prop':'test_value'}
703
log.properties_handler_registry.register(
704
'trivial_custom_prop_handler',
705
trivial_custom_prop_handler)
706
self.assertFormatterResult("""\
707
1 John Doe\t2005-11-23
708
test_prop: test_value
712
wt.branch, log.ShortLogFormatter)
714
def test_error_in_properties_handler(self):
715
"""Log includes the custom properties returned by the registered
718
wt = self.make_standard_commit('error_in_properties_handler',
719
revprops={'first_prop':'first_value'})
720
sio = self.make_utf8_encoded_stringio()
721
formatter = log.LongLogFormatter(to_file=sio)
722
def trivial_custom_prop_handler(revision):
723
raise StandardError("a test error")
725
log.properties_handler_registry.register(
726
'trivial_custom_prop_handler',
727
trivial_custom_prop_handler)
728
self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
730
def test_properties_handler_bad_argument(self):
731
wt = self.make_standard_commit('bad_argument',
732
revprops={'a_prop':'test_value'})
733
sio = self.make_utf8_encoded_stringio()
734
formatter = log.LongLogFormatter(to_file=sio)
735
def bad_argument_prop_handler(revision):
736
return {'custom_prop_name':revision.properties['a_prop']}
738
log.properties_handler_registry.register(
739
'bad_argument_prop_handler',
740
bad_argument_prop_handler)
742
self.assertRaises(AttributeError, formatter.show_properties,
745
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
746
formatter.show_properties(revision, '')
747
self.assertEqualDiff('''custom_prop_name: test_value\n''',
751
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
753
def test_long_verbose_log(self):
754
"""Verbose log includes changed files
758
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
759
self.assertFormatterResult("""\
760
------------------------------------------------------------
762
committer: Lorem Ipsum <test@example.com>
763
branch nick: test_long_verbose_log
764
timestamp: Wed 2005-11-23 12:08:27 +1000
770
wt.branch, log.LongLogFormatter,
771
formatter_kwargs=dict(levels=1),
772
show_log_kwargs=dict(verbose=True))
774
def test_long_verbose_contain_deltas(self):
775
wt = self.make_branch_and_tree('parent')
776
self.build_tree(['parent/f1', 'parent/f2'])
778
wt.commit('first post')
779
child_wt = wt.bzrdir.sprout('child').open_workingtree()
780
os.unlink('child/f1')
781
self.build_tree_contents([('child/f2', 'hello\n')])
782
child_wt.commit('removed f1 and modified f2')
783
wt.merge_from_branch(child_wt.branch)
784
wt.commit('merge branch 1')
785
self.assertFormatterResult("""\
786
------------------------------------------------------------
788
committer: Lorem Ipsum <test@example.com>
797
------------------------------------------------------------
799
committer: Lorem Ipsum <test@example.com>
808
wt.branch, log.LongLogFormatter,
809
formatter_kwargs=dict(levels=1),
810
show_log_kwargs=dict(verbose=True),
813
def test_long_trailing_newlines(self):
814
540
wt = self.make_branch_and_tree('.')
815
b = make_commits_with_trailing_newlines(wt)
816
self.assertFormatterResult("""\
817
------------------------------------------------------------
819
committer: Joe Foo <joe@foo.com>
821
timestamp: Mon 2005-11-21 09:32:56 -0600
823
single line with trailing newline
824
------------------------------------------------------------
826
author: Joe Bar <joe@bar.com>
827
committer: Joe Foo <joe@foo.com>
829
timestamp: Mon 2005-11-21 09:27:22 -0600
834
------------------------------------------------------------
836
committer: Joe Foo <joe@foo.com>
838
timestamp: Mon 2005-11-21 09:24:15 -0600
842
b, log.LongLogFormatter,
843
formatter_kwargs=dict(levels=1))
845
def test_long_author_in_log(self):
846
"""Log includes the author name if it's set in
847
the revision properties
849
wt = self.make_standard_commit('test_author_log')
850
self.assertFormatterResult("""\
542
self.build_tree(['a'])
544
b.nick = 'test_author_log'
545
wt.commit(message='add a',
546
timestamp=1132711707,
548
committer='Lorem Ipsum <test@example.com>',
549
author='John Doe <jdoe@example.com>')
551
formatter = LongLogFormatter(to_file=sio)
552
show_log(b, formatter)
553
self.assertEqualDiff(sio.getvalue(), '''\
851
554
------------------------------------------------------------
853
556
author: John Doe <jdoe@example.com>
856
559
timestamp: Wed 2005-11-23 12:08:27 +1000
860
wt.branch, log.LongLogFormatter,
861
formatter_kwargs=dict(levels=1))
863
def test_long_properties_in_log(self):
864
"""Log includes the custom properties returned by the registered
867
wt = self.make_standard_commit('test_properties_in_log')
868
def trivial_custom_prop_handler(revision):
869
return {'test_prop':'test_value'}
871
log.properties_handler_registry.register(
872
'trivial_custom_prop_handler',
873
trivial_custom_prop_handler)
874
self.assertFormatterResult("""\
875
------------------------------------------------------------
877
test_prop: test_value
878
author: John Doe <jdoe@example.com>
879
committer: Lorem Ipsum <test@example.com>
880
branch nick: test_properties_in_log
881
timestamp: Wed 2005-11-23 12:08:27 +1000
885
wt.branch, log.LongLogFormatter,
886
formatter_kwargs=dict(levels=1))
889
class TestLineLogFormatter(TestCaseForLogFormatter):
566
class TestLineLogFormatter(TestCaseWithTransport):
891
568
def test_line_log(self):
892
569
"""Line log should show revno
896
wt = self.make_standard_commit('test-line-log',
897
committer='Line-Log-Formatter Tester <test@line.log>',
899
self.assertFormatterResult("""\
900
1: Line-Log-Formatte... 2005-11-23 add a
902
wt.branch, log.LineLogFormatter)
573
wt = self.make_branch_and_tree('.')
575
self.build_tree(['a'])
577
b.nick = 'test-line-log'
578
wt.commit(message='add a',
579
timestamp=1132711707,
581
committer='Line-Log-Formatter Tester <test@line.log>')
582
logfile = file('out.tmp', 'w+')
583
formatter = LineLogFormatter(to_file=logfile)
584
show_log(b, formatter)
587
log_contents = logfile.read()
588
self.assertEqualDiff(log_contents,
589
'1: Line-Log-Formatte... 2005-11-23 add a\n')
904
591
def test_trailing_newlines(self):
905
592
wt = self.make_branch_and_tree('.')
906
593
b = make_commits_with_trailing_newlines(wt)
907
self.assertFormatterResult("""\
594
sio = self.make_utf8_encoded_stringio()
595
lf = LineLogFormatter(to_file=sio)
597
self.assertEqualDiff(sio.getvalue(), """\
908
598
3: Joe Foo 2005-11-21 single line with trailing newline
909
599
2: Joe Bar 2005-11-21 multiline
910
600
1: Joe Foo 2005-11-21 simple log message
912
b, log.LineLogFormatter)
914
603
def test_line_log_single_merge_revision(self):
915
wt = self._prepare_tree_with_merges()
916
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
917
rev = revspec.in_history(wt.branch)
918
self.assertFormatterResult("""\
919
1.1.1: Joe Foo 2005-11-22 rev-merged
921
wt.branch, log.LineLogFormatter,
922
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
924
def test_line_log_with_tags(self):
925
wt = self._prepare_tree_with_merges(with_tags=True)
926
self.assertFormatterResult("""\
927
3: Jane Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
928
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
929
1: Joe Foo 2005-11-22 rev-1
931
wt.branch, log.LineLogFormatter)
934
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
936
def test_line_merge_revs_log(self):
937
"""Line log should show revno
941
wt = self.make_standard_commit('test-line-log',
942
committer='Line-Log-Formatter Tester <test@line.log>',
944
self.assertFormatterResult("""\
945
1: Line-Log-Formatte... 2005-11-23 add a
947
wt.branch, log.LineLogFormatter)
949
def test_line_merge_revs_log_single_merge_revision(self):
950
wt = self.make_branch_and_memory_tree('.')
952
self.addCleanup(wt.unlock)
954
wt.commit('rev-1', rev_id='rev-1',
955
timestamp=1132586655, timezone=36000,
956
committer='Joe Foo <joe@foo.com>')
957
wt.commit('rev-merged', rev_id='rev-2a',
958
timestamp=1132586700, timezone=36000,
959
committer='Joe Foo <joe@foo.com>')
960
wt.set_parent_ids(['rev-1', 'rev-2a'])
961
wt.branch.set_last_revision_info(1, 'rev-1')
962
wt.commit('rev-2', rev_id='rev-2b',
963
timestamp=1132586800, timezone=36000,
964
committer='Joe Foo <joe@foo.com>')
965
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
966
rev = revspec.in_history(wt.branch)
967
self.assertFormatterResult("""\
968
1.1.1: Joe Foo 2005-11-22 rev-merged
970
wt.branch, log.LineLogFormatter,
971
formatter_kwargs=dict(levels=0),
972
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
974
def test_line_merge_revs_log_with_merges(self):
975
wt = self.make_branch_and_memory_tree('.')
977
self.addCleanup(wt.unlock)
979
wt.commit('rev-1', rev_id='rev-1',
980
timestamp=1132586655, timezone=36000,
981
committer='Joe Foo <joe@foo.com>')
982
wt.commit('rev-merged', rev_id='rev-2a',
983
timestamp=1132586700, timezone=36000,
984
committer='Joe Foo <joe@foo.com>')
985
wt.set_parent_ids(['rev-1', 'rev-2a'])
986
wt.branch.set_last_revision_info(1, 'rev-1')
987
wt.commit('rev-2', rev_id='rev-2b',
988
timestamp=1132586800, timezone=36000,
989
committer='Joe Foo <joe@foo.com>')
990
self.assertFormatterResult("""\
991
2: Joe Foo 2005-11-22 [merge] rev-2
992
1.1.1: Joe Foo 2005-11-22 rev-merged
993
1: Joe Foo 2005-11-22 rev-1
995
wt.branch, log.LineLogFormatter,
996
formatter_kwargs=dict(levels=0))
999
class TestGetViewRevisions(tests.TestCaseWithTransport):
604
wt = self.make_branch_and_memory_tree('.')
608
wt.commit('rev-1', rev_id='rev-1',
609
timestamp=1132586655, timezone=36000,
610
committer='Joe Foo <joe@foo.com>')
611
wt.commit('rev-merged', rev_id='rev-2a',
612
timestamp=1132586700, timezone=36000,
613
committer='Joe Foo <joe@foo.com>')
614
wt.set_parent_ids(['rev-1', 'rev-2a'])
615
wt.branch.set_last_revision_info(1, 'rev-1')
616
wt.commit('rev-2', rev_id='rev-2b',
617
timestamp=1132586800, timezone=36000,
618
committer='Joe Foo <joe@foo.com>')
619
logfile = self.make_utf8_encoded_stringio()
620
formatter = LineLogFormatter(to_file=logfile)
621
revspec = RevisionSpec.from_string('1.1.1')
623
rev = revspec.in_history(wtb)
624
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
625
self.assertEqualDiff(logfile.getvalue(), """\
626
1.1.1: Joe Foo 2005-11-22 rev-merged
633
class TestGetViewRevisions(TestCaseWithTransport):
1001
635
def make_tree_with_commits(self):
1002
636
"""Create a tree with well-known revision ids"""
1021
655
return mainline_revs, rev_nos, wt
1023
def make_branch_with_many_merges(self):
657
def make_tree_with_many_merges(self):
1024
658
"""Create a tree with well-known revision ids"""
1025
builder = self.make_branch_builder('tree1')
1026
builder.start_series()
1027
builder.build_snapshot('1', None, [
1028
('add', ('', 'TREE_ROOT', 'directory', '')),
1029
('add', ('f', 'f-id', 'file', '1\n'))])
1030
builder.build_snapshot('2', ['1'], [])
1031
builder.build_snapshot('3a', ['2'], [
1032
('modify', ('f-id', '1\n2\n3a\n'))])
1033
builder.build_snapshot('3b', ['2', '3a'], [
1034
('modify', ('f-id', '1\n2\n3a\n'))])
1035
builder.build_snapshot('3c', ['2', '3b'], [
1036
('modify', ('f-id', '1\n2\n3a\n'))])
1037
builder.build_snapshot('4a', ['3b'], [])
1038
builder.build_snapshot('4b', ['3c', '4a'], [])
1039
builder.finish_series()
659
wt = self.make_branch_and_tree('tree1')
660
wt.commit('commit one', rev_id='1')
661
wt.commit('commit two', rev_id='2')
662
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
663
tree3.commit('commit three a', rev_id='3a')
664
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
665
tree2.merge_from_branch(tree3.branch)
666
tree2.commit('commit three b', rev_id='3b')
667
wt.merge_from_branch(tree2.branch)
668
wt.commit('commit three c', rev_id='3c')
669
tree2.commit('four-a', rev_id='4a')
670
wt.merge_from_branch(tree2.branch)
671
wt.commit('four-b', rev_id='4b')
1053
672
mainline_revs = [None, '1', '2', '3c', '4b']
1054
673
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
1055
674
full_rev_nos_for_reference = {
1096
713
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1098
715
self.addCleanup(wt.unlock)
1099
revisions = list(log.get_view_revisions(
1100
mainline_revs, rev_nos, wt.branch, 'forward'))
1101
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1102
('4b', '4', 0), ('4a', '3.1.1', 1)],
1104
revisions = list(log.get_view_revisions(
1105
mainline_revs, rev_nos, wt.branch, 'forward',
1106
include_merges=False))
1107
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
716
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
718
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
719
('4b', '4', 0), ('4a', '3.1.1', 1)],
721
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
722
'forward', include_merges=False))
723
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1111
727
def test_get_view_revisions_merge_reverse(self):
1112
728
"""Test get_view_revisions in reverse when there are merges"""
1113
729
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1115
731
self.addCleanup(wt.unlock)
1116
revisions = list(log.get_view_revisions(
1117
mainline_revs, rev_nos, wt.branch, 'reverse'))
732
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1118
734
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1119
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1121
revisions = list(log.get_view_revisions(
1122
mainline_revs, rev_nos, wt.branch, 'reverse',
1123
include_merges=False))
735
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
737
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
738
'reverse', include_merges=False))
1124
739
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1128
743
def test_get_view_revisions_merge2(self):
1129
744
"""Test get_view_revisions when there are merges"""
1130
mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1132
self.addCleanup(b.unlock)
1133
revisions = list(log.get_view_revisions(
1134
mainline_revs, rev_nos, b, 'forward'))
745
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
747
self.addCleanup(wt.unlock)
748
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1135
750
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1136
('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
751
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
1138
753
self.assertEqual(expected, revisions)
1139
revisions = list(log.get_view_revisions(
1140
mainline_revs, rev_nos, b, 'forward',
1141
include_merges=False))
754
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
755
'forward', include_merges=False))
1142
756
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1147
def test_file_id_for_range(self):
1148
mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1150
self.addCleanup(b.unlock)
1152
def rev_from_rev_id(revid, branch):
1153
revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1154
return revspec.in_history(branch)
1156
def view_revs(start_rev, end_rev, file_id, direction):
1157
revs = log.calculate_view_revisions(
1159
start_rev, # start_revision
1160
end_rev, # end_revision
1161
direction, # direction
1162
file_id, # specific_fileid
1163
True, # generate_merge_revisions
1167
rev_3a = rev_from_rev_id('3a', b)
1168
rev_4b = rev_from_rev_id('4b', b)
1169
self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1), ('3a', '2.1.1', 2)],
1170
view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1171
# Note: 3c still appears before 3a here because of depth-based sorting
1172
self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1), ('3a', '2.1.1', 2)],
1173
view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1176
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
761
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
1178
763
def create_tree_with_single_merge(self):
1179
764
"""Create a branch with a moderate layout.
1229
814
tree.commit('D', rev_id='D')
1231
816
# Switch to a read lock for this tree.
1232
# We still have an addCleanup(tree.unlock) pending
817
# We still have addCleanup(unlock)
1234
819
tree.lock_read()
1237
def check_delta(self, delta, **kw):
1238
"""Check the filenames touched by a delta are as expected.
1240
Caller only have to pass in the list of files for each part, all
1241
unspecified parts are considered empty (and checked as such).
1243
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1244
# By default we expect an empty list
1245
expected = kw.get(n, [])
1246
# strip out only the path components
1247
got = [x[0] for x in getattr(delta, n)]
1248
self.assertEqual(expected, got)
1250
822
def test_tree_with_single_merge(self):
1251
823
"""Make sure the tree layout is correct."""
1252
824
tree = self.create_tree_with_single_merge()
1253
825
rev_A_tree = tree.branch.repository.revision_tree('A')
1254
826
rev_B_tree = tree.branch.repository.revision_tree('B')
828
f1_changed = (u'f1', 'f1-id', 'file', True, False)
829
f2_changed = (u'f2', 'f2-id', 'file', True, False)
830
f3_changed = (u'f3', 'f3-id', 'file', True, False)
832
delta = rev_B_tree.changes_from(rev_A_tree)
833
self.assertEqual([f1_changed, f3_changed], delta.modified)
834
self.assertEqual([], delta.renamed)
835
self.assertEqual([], delta.added)
836
self.assertEqual([], delta.removed)
1255
838
rev_C_tree = tree.branch.repository.revision_tree('C')
839
delta = rev_C_tree.changes_from(rev_A_tree)
840
self.assertEqual([f2_changed, f3_changed], delta.modified)
841
self.assertEqual([], delta.renamed)
842
self.assertEqual([], delta.added)
843
self.assertEqual([], delta.removed)
1256
845
rev_D_tree = tree.branch.repository.revision_tree('D')
1258
self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1259
modified=['f1', 'f3'])
1261
self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1262
modified=['f2', 'f3'])
1264
self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1265
modified=['f2', 'f3'])
1267
self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1268
modified=['f1', 'f3'])
846
delta = rev_D_tree.changes_from(rev_B_tree)
847
self.assertEqual([f2_changed, f3_changed], delta.modified)
848
self.assertEqual([], delta.renamed)
849
self.assertEqual([], delta.added)
850
self.assertEqual([], delta.removed)
852
delta = rev_D_tree.changes_from(rev_C_tree)
853
self.assertEqual([f1_changed, f3_changed], delta.modified)
854
self.assertEqual([], delta.renamed)
855
self.assertEqual([], delta.added)
856
self.assertEqual([], delta.removed)
1270
858
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1271
"""Ensure _filter_revisions_touching_file_id returns the right values.
859
"""Make sure _filter_revisions_touching_file_id returns the right values.
1273
861
Get the return value from _filter_revisions_touching_file_id and make
1274
862
sure they are correct.
1276
# The api for _filter_revisions_touching_file_id is a little crazy.
864
# The api for _get_revisions_touching_file_id is a little crazy,
1277
865
# So we do the setup here.
1278
866
mainline = tree.branch.revision_history()
1279
867
mainline.insert(0, None)
1372
938
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1373
939
rev.properties['author'] = 'John Smith jsmith@example.com'
1374
940
self.assertEqual('John Smith', lf.short_author(rev))
1375
del rev.properties['author']
1376
rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1377
'Jane Rey <jrey@example.com>')
1378
self.assertEqual('John Smith', lf.short_author(rev))
1381
class TestReverseByDepth(tests.TestCase):
1382
"""Test reverse_by_depth behavior.
1384
This is used to present revisions in forward (oldest first) order in a nice
1387
The tests use lighter revision description to ease reading.
1390
def assertReversed(self, forward, backward):
1391
# Transform the descriptions to suit the API: tests use (revno, depth),
1392
# while the API expects (revid, revno, depth)
1393
def complete_revisions(l):
1394
"""Transform the description to suit the API.
1396
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1397
Since the revid is arbitrary, we just duplicate revno
1399
return [ (r, r, d) for r, d in l]
1400
forward = complete_revisions(forward)
1401
backward= complete_revisions(backward)
1402
self.assertEqual(forward, log.reverse_by_depth(backward))
1405
def test_mainline_revisions(self):
1406
self.assertReversed([( '1', 0), ('2', 0)],
1407
[('2', 0), ('1', 0)])
1409
def test_merged_revisions(self):
1410
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1411
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1412
def test_shifted_merged_revisions(self):
1413
"""Test irregular layout.
1415
Requesting revisions touching a file can produce "holes" in the depths.
1417
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1418
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1420
def test_merged_without_child_revisions(self):
1421
"""Test irregular layout.
1423
Revision ranges can produce "holes" in the depths.
1425
# When a revision of higher depth doesn't follow one of lower depth, we
1426
# assume a lower depth one is virtually there
1427
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1428
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1429
# So we get the same order after reversing below even if the original
1430
# revisions are not in the same order.
1431
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1432
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1435
class TestHistoryChange(tests.TestCaseWithTransport):
1437
def setup_a_tree(self):
1438
tree = self.make_branch_and_tree('tree')
1440
self.addCleanup(tree.unlock)
1441
tree.commit('1a', rev_id='1a')
1442
tree.commit('2a', rev_id='2a')
1443
tree.commit('3a', rev_id='3a')
1446
def setup_ab_tree(self):
1447
tree = self.setup_a_tree()
1448
tree.set_last_revision('1a')
1449
tree.branch.set_last_revision_info(1, '1a')
1450
tree.commit('2b', rev_id='2b')
1451
tree.commit('3b', rev_id='3b')
1454
def setup_ac_tree(self):
1455
tree = self.setup_a_tree()
1456
tree.set_last_revision(revision.NULL_REVISION)
1457
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1458
tree.commit('1c', rev_id='1c')
1459
tree.commit('2c', rev_id='2c')
1460
tree.commit('3c', rev_id='3c')
1463
def test_all_new(self):
1464
tree = self.setup_ab_tree()
1465
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1466
self.assertEqual([], old)
1467
self.assertEqual(['2a', '3a'], new)
1469
def test_all_old(self):
1470
tree = self.setup_ab_tree()
1471
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1472
self.assertEqual([], new)
1473
self.assertEqual(['2a', '3a'], old)
1475
def test_null_old(self):
1476
tree = self.setup_ab_tree()
1477
old, new = log.get_history_change(revision.NULL_REVISION,
1478
'3a', tree.branch.repository)
1479
self.assertEqual([], old)
1480
self.assertEqual(['1a', '2a', '3a'], new)
1482
def test_null_new(self):
1483
tree = self.setup_ab_tree()
1484
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1485
tree.branch.repository)
1486
self.assertEqual([], new)
1487
self.assertEqual(['1a', '2a', '3a'], old)
1489
def test_diverged(self):
1490
tree = self.setup_ab_tree()
1491
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1492
self.assertEqual(old, ['2a', '3a'])
1493
self.assertEqual(new, ['2b', '3b'])
1495
def test_unrelated(self):
1496
tree = self.setup_ac_tree()
1497
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1498
self.assertEqual(old, ['1a', '2a', '3a'])
1499
self.assertEqual(new, ['1c', '2c', '3c'])
1501
def test_show_branch_change(self):
1502
tree = self.setup_ab_tree()
1504
log.show_branch_change(tree.branch, s, 3, '3a')
1505
self.assertContainsRe(s.getvalue(),
1506
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1507
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1509
def test_show_branch_change_no_change(self):
1510
tree = self.setup_ab_tree()
1512
log.show_branch_change(tree.branch, s, 3, '3b')
1513
self.assertEqual(s.getvalue(),
1514
'Nothing seems to have changed\n')
1516
def test_show_branch_change_no_old(self):
1517
tree = self.setup_ab_tree()
1519
log.show_branch_change(tree.branch, s, 2, '2b')
1520
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1521
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1523
def test_show_branch_change_no_new(self):
1524
tree = self.setup_ab_tree()
1525
tree.branch.set_last_revision_info(2, '2b')
1527
log.show_branch_change(tree.branch, s, 3, '3b')
1528
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1529
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1533
class TestLogWithBugs(TestCaseForLogFormatter):
1536
TestCaseForLogFormatter.setUp(self)
1537
log.properties_handler_registry.register(
1538
'bugs_properties_handler',
1539
log._bugs_properties_handler)
1541
def make_commits_with_bugs(self):
1542
"""Helper method for LogFormatter tests"""
1543
tree = self.make_branch_and_tree(u'.')
1544
self.build_tree(['a', 'b'])
1546
tree.commit('simple log message', rev_id='a1',
1547
timestamp=1132586655.459960938, timezone=-6*3600,
1548
committer='Joe Foo <joe@foo.com>',
1549
revprops={'bugs': 'test://bug/id fixed'})
1551
tree.commit('multiline\nlog\nmessage\n', rev_id='a2',
1552
timestamp=1132586842.411175966, timezone=-6*3600,
1553
committer='Joe Foo <joe@foo.com>',
1554
authors=['Joe Bar <joe@bar.com>'],
1555
revprops={'bugs': 'test://bug/id fixed\n'
1556
'test://bug/2 fixed'})
1560
def test_long_bugs(self):
1561
tree = self.make_commits_with_bugs()
1562
self.assertFormatterResult("""\
1563
------------------------------------------------------------
1565
fixes bug(s): test://bug/id test://bug/2
1566
author: Joe Bar <joe@bar.com>
1567
committer: Joe Foo <joe@foo.com>
1569
timestamp: Mon 2005-11-21 09:27:22 -0600
1574
------------------------------------------------------------
1576
fixes bug(s): test://bug/id
1577
committer: Joe Foo <joe@foo.com>
1579
timestamp: Mon 2005-11-21 09:24:15 -0600
1583
tree.branch, log.LongLogFormatter)
1585
def test_short_bugs(self):
1586
tree = self.make_commits_with_bugs()
1587
self.assertFormatterResult("""\
1588
2 Joe Bar\t2005-11-21
1589
fixes bug(s): test://bug/id test://bug/2
1594
1 Joe Foo\t2005-11-21
1595
fixes bug(s): test://bug/id
1599
tree.branch, log.ShortLogFormatter)
1601
def test_wrong_bugs_property(self):
1602
tree = self.make_branch_and_tree(u'.')
1603
self.build_tree(['foo'])
1604
tree.commit('simple log message', rev_id='a1',
1605
timestamp=1132586655.459960938, timezone=-6*3600,
1606
committer='Joe Foo <joe@foo.com>',
1607
revprops={'bugs': 'test://bug/id invalid_value'})
1608
self.assertFormatterResult("""\
1609
1 Joe Foo\t2005-11-21
1613
tree.branch, log.ShortLogFormatter)
1615
def test_bugs_handler_present(self):
1616
self.properties_handler_registry.get('bugs_properties_handler')