1
# Copyright (C) 2005-2013, 2016 Canonical Ltd
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30
from ..sixish import (
35
class TestLogMixin(object):
37
def wt_commit(self, wt, message, **kwargs):
38
"""Use some mostly fixed values for commits to simplify tests.
40
Tests can use this function to get some commit attributes. The time
41
stamp is incremented at each commit.
43
if getattr(self, 'timestamp', None) is None:
44
self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
46
self.timestamp += 1 # 1 second between each commit
47
kwargs.setdefault('timestamp', self.timestamp)
48
kwargs.setdefault('timezone', 0) # UTC
49
kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
51
return wt.commit(message, **kwargs)
54
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
57
super(TestCaseForLogFormatter, self).setUp()
58
# keep a reference to the "current" custom prop. handler registry
59
self.properties_handler_registry = log.properties_handler_registry
60
# Use a clean registry for log
61
log.properties_handler_registry = registry.Registry()
64
log.properties_handler_registry = self.properties_handler_registry
65
self.addCleanup(restore)
67
def assertFormatterResult(self, result, branch, formatter_class,
68
formatter_kwargs=None, show_log_kwargs=None):
69
logfile = self.make_utf8_encoded_stringio()
70
if formatter_kwargs is None:
72
formatter = formatter_class(to_file=logfile, **formatter_kwargs)
73
if show_log_kwargs is None:
75
log.show_log(branch, formatter, **show_log_kwargs)
76
self.assertEqualDiff(result, logfile.getvalue())
78
def make_standard_commit(self, branch_nick, **kwargs):
79
wt = self.make_branch_and_tree('.')
81
self.addCleanup(wt.unlock)
82
self.build_tree(['a'])
84
wt.branch.nick = branch_nick
85
kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
86
kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
87
self.wt_commit(wt, 'add a', **kwargs)
90
def make_commits_with_trailing_newlines(self, wt):
91
"""Helper method for LogFormatter tests"""
94
self.build_tree_contents([('a', 'hello moto\n')])
95
self.wt_commit(wt, 'simple log message', rev_id='a1')
96
self.build_tree_contents([('b', 'goodbye\n')])
98
self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
100
self.build_tree_contents([('c', 'just another manic monday\n')])
102
self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
105
def _prepare_tree_with_merges(self, with_tags=False):
106
wt = self.make_branch_and_memory_tree('.')
108
self.addCleanup(wt.unlock)
110
self.wt_commit(wt, 'rev-1', rev_id='rev-1')
111
self.wt_commit(wt, 'rev-merged', rev_id='rev-2a')
112
wt.set_parent_ids(['rev-1', 'rev-2a'])
113
wt.branch.set_last_revision_info(1, 'rev-1')
114
self.wt_commit(wt, 'rev-2', rev_id='rev-2b')
117
branch.tags.set_tag('v0.2', 'rev-2b')
118
self.wt_commit(wt, 'rev-3', rev_id='rev-3')
119
branch.tags.set_tag('v1.0rc1', 'rev-3')
120
branch.tags.set_tag('v1.0', 'rev-3')
124
class LogCatcher(log.LogFormatter):
125
"""Pull log messages into a list rather than displaying them.
127
To simplify testing we save logged revisions here rather than actually
128
formatting anything, so that we can precisely check the result without
129
being dependent on the formatting.
132
supports_merge_revisions = True
133
supports_delta = True
137
def __init__(self, *args, **kwargs):
138
kwargs.update(dict(to_file=None))
139
super(LogCatcher, self).__init__(*args, **kwargs)
142
def log_revision(self, revision):
143
self.revisions.append(revision)
146
class TestShowLog(tests.TestCaseWithTransport):
148
def checkDelta(self, delta, **kw):
149
"""Check the filenames touched by a delta are as expected.
151
Caller only have to pass in the list of files for each part, all
152
unspecified parts are considered empty (and checked as such).
154
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
155
# By default we expect an empty list
156
expected = kw.get(n, [])
157
# strip out only the path components
158
got = [x[0] for x in getattr(delta, n)]
159
self.assertEqual(expected, got)
161
def assertInvalidRevisonNumber(self, br, start, end):
163
self.assertRaises(errors.InvalidRevisionNumber,
164
log.show_log, br, lf,
165
start_revision=start, end_revision=end)
167
def test_cur_revno(self):
168
wt = self.make_branch_and_tree('.')
172
wt.commit('empty commit')
173
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
175
# Since there is a single revision in the branch all the combinations
177
self.assertInvalidRevisonNumber(b, 2, 1)
178
self.assertInvalidRevisonNumber(b, 1, 2)
179
self.assertInvalidRevisonNumber(b, 0, 2)
180
self.assertInvalidRevisonNumber(b, 1, 0)
181
self.assertInvalidRevisonNumber(b, -1, 1)
182
self.assertInvalidRevisonNumber(b, 1, -1)
184
def test_empty_branch(self):
185
wt = self.make_branch_and_tree('.')
188
log.show_log(wt.branch, lf)
190
self.assertEqual([], lf.revisions)
192
def test_empty_commit(self):
193
wt = self.make_branch_and_tree('.')
195
wt.commit('empty commit')
197
log.show_log(wt.branch, lf, verbose=True)
199
self.assertEqual(1, len(revs))
200
self.assertEqual('1', revs[0].revno)
201
self.assertEqual('empty commit', revs[0].rev.message)
202
self.checkDelta(revs[0].delta)
204
def test_simple_commit(self):
205
wt = self.make_branch_and_tree('.')
206
wt.commit('empty commit')
207
self.build_tree(['hello'])
209
wt.commit('add one file',
210
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
211
u'<test@example.com>')
213
log.show_log(wt.branch, lf, verbose=True)
214
self.assertEqual(2, len(lf.revisions))
215
# first one is most recent
216
log_entry = lf.revisions[0]
217
self.assertEqual('2', log_entry.revno)
218
self.assertEqual('add one file', log_entry.rev.message)
219
self.checkDelta(log_entry.delta, added=['hello'])
221
def test_commit_message_with_control_chars(self):
222
wt = self.make_branch_and_tree('.')
223
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
224
msg = msg.replace(u'\r', u'\n')
227
log.show_log(wt.branch, lf, verbose=True)
228
committed_msg = lf.revisions[0].rev.message
229
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
230
self.assertNotEqual(msg, committed_msg)
231
self.assertTrue(len(committed_msg) > len(msg))
233
self.assertEqual(msg, committed_msg)
235
def test_commit_message_without_control_chars(self):
236
wt = self.make_branch_and_tree('.')
237
# escaped. As ElementTree apparently does some kind of
238
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
239
# included in the test commit message, even though they are
240
# valid XML 1.0 characters.
241
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
244
log.show_log(wt.branch, lf, verbose=True)
245
committed_msg = lf.revisions[0].rev.message
246
self.assertEqual(msg, committed_msg)
248
def test_deltas_in_merge_revisions(self):
249
"""Check deltas created for both mainline and merge revisions"""
250
wt = self.make_branch_and_tree('parent')
251
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
254
wt.commit(message='add file1 and file2')
255
self.run_bzr('branch parent child')
256
os.unlink('child/file1')
257
with file('child/file2', 'wb') as f: f.write('hello\n')
258
self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
261
self.run_bzr('merge ../child')
262
wt.commit('merge child branch')
266
lf.supports_merge_revisions = True
267
log.show_log(b, lf, verbose=True)
270
self.assertEqual(3, len(revs))
273
self.assertEqual('2', logentry.revno)
274
self.assertEqual('merge child branch', logentry.rev.message)
275
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
278
self.assertEqual('1.1.1', logentry.revno)
279
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
280
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
283
self.assertEqual('1', logentry.revno)
284
self.assertEqual('add file1 and file2', logentry.rev.message)
285
self.checkDelta(logentry.delta, added=['file1', 'file2'])
287
def test_bug_842695_log_restricted_to_dir(self):
288
# Comments here indicate revision numbers in trunk # VVVVV
289
trunk = self.make_branch_and_tree('this')
290
trunk.commit('initial trunk') # 1
291
adder = trunk.controldir.sprout('adder').open_workingtree()
292
merger = trunk.controldir.sprout('merger').open_workingtree()
293
self.build_tree_contents([
295
('adder/dir/file', 'foo'),
297
adder.add(['dir', 'dir/file'])
298
adder.commit('added dir') # 1.1.1
299
trunk.merge_from_branch(adder.branch)
300
trunk.commit('merged adder into trunk') # 2
301
merger.merge_from_branch(trunk.branch)
302
merger.commit('merged trunk into merger') # 1.2.1
303
# Commits are processed in increments of 200 revisions, so
304
# make sure the two merges into trunk are in different chunks.
305
for i in xrange(200):
306
trunk.commit('intermediate commit %d' % i) # 3-202
307
trunk.merge_from_branch(merger.branch)
308
trunk.commit('merged merger into trunk') # 203
309
file_id = trunk.path2id('dir')
311
lf.supports_merge_revisions = True
312
log.show_log(trunk.branch, lf, file_id)
314
self.assertEqual(['2', '1.1.1'], [r.revno for r in lf.revisions])
315
except AssertionError:
316
raise tests.KnownFailure("bug #842695")
319
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
320
class UTFLoopbackGPGStrategy(gpg.LoopbackGPGStrategy):
321
def verify(self, content, testament):
322
return (gpg.SIGNATURE_VALID,
323
u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
325
def has_signature_for_revision_id(self, revision_id):
328
def get_signature_text(self, revision_id):
331
def test_format_signature_validity_utf(self):
332
"""Check that GPG signatures containing UTF-8 names are formatted
334
# Monkey patch to use our UTF-8 generating GPGStrategy
335
self.overrideAttr(gpg, 'GPGStrategy', self.UTFLoopbackGPGStrategy)
336
wt = self.make_branch_and_tree('.')
337
revid = wt.commit('empty commit')
338
repo = wt.branch.repository
339
# Monkey patch out checking if this rev is actually signed, since we
340
# can't sign it without a heavier TestCase and LoopbackGPGStrategy
341
# doesn't care anyways.
342
self.overrideAttr(repo, 'has_signature_for_revision_id',
343
self.has_signature_for_revision_id)
344
self.overrideAttr(repo, 'get_signature_text', self.get_signature_text)
345
out = log.format_signature_validity(revid, wt.branch)
347
u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
351
class TestShortLogFormatter(TestCaseForLogFormatter):
353
def test_trailing_newlines(self):
354
wt = self.make_branch_and_tree('.')
355
b = self.make_commits_with_trailing_newlines(wt)
356
self.assertFormatterResult("""\
357
3 Joe Foo\t2005-11-22
358
single line with trailing newline
360
2 Joe Foo\t2005-11-22
365
1 Joe Foo\t2005-11-22
369
b, log.ShortLogFormatter)
371
def test_short_log_with_merges(self):
372
wt = self._prepare_tree_with_merges()
373
self.assertFormatterResult("""\
374
2 Joe Foo\t2005-11-22 [merge]
377
1 Joe Foo\t2005-11-22
381
wt.branch, log.ShortLogFormatter)
383
def test_short_log_with_merges_and_advice(self):
384
wt = self._prepare_tree_with_merges()
385
self.assertFormatterResult("""\
386
2 Joe Foo\t2005-11-22 [merge]
389
1 Joe Foo\t2005-11-22
392
Use --include-merged or -n0 to see merged revisions.
394
wt.branch, log.ShortLogFormatter,
395
formatter_kwargs=dict(show_advice=True))
397
def test_short_log_with_merges_and_range(self):
398
wt = self._prepare_tree_with_merges()
399
self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
400
wt.branch.set_last_revision_info(2, 'rev-2b')
401
wt.set_parent_ids(['rev-2b', 'rev-3a'])
402
self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
403
self.assertFormatterResult("""\
404
3 Joe Foo\t2005-11-22 [merge]
407
2 Joe Foo\t2005-11-22 [merge]
411
wt.branch, log.ShortLogFormatter,
412
show_log_kwargs=dict(start_revision=2, end_revision=3))
414
def test_short_log_with_tags(self):
415
wt = self._prepare_tree_with_merges(with_tags=True)
416
self.assertFormatterResult("""\
417
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
420
2 Joe Foo\t2005-11-22 {v0.2} [merge]
423
1 Joe Foo\t2005-11-22
427
wt.branch, log.ShortLogFormatter)
429
def test_short_log_single_merge_revision(self):
430
wt = self._prepare_tree_with_merges()
431
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
432
rev = revspec.in_history(wt.branch)
433
self.assertFormatterResult("""\
434
1.1.1 Joe Foo\t2005-11-22
438
wt.branch, log.ShortLogFormatter,
439
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
441
def test_show_ids(self):
442
wt = self.make_branch_and_tree('parent')
443
self.build_tree(['parent/f1', 'parent/f2'])
445
self.wt_commit(wt, 'first post', rev_id='a')
446
child_wt = wt.controldir.sprout('child').open_workingtree()
447
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
448
wt.merge_from_branch(child_wt.branch)
449
self.wt_commit(wt, 'merge branch 1', rev_id='c')
450
self.assertFormatterResult("""\
451
2 Joe Foo\t2005-11-22 [merge]
455
1.1.1 Joe Foo\t2005-11-22
459
1 Joe Foo\t2005-11-22
464
wt.branch, log.ShortLogFormatter,
465
formatter_kwargs=dict(levels=0, show_ids=True))
468
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
470
def test_short_merge_revs_log_with_merges(self):
471
wt = self._prepare_tree_with_merges()
472
# Note that the 1.1.1 indenting is in fact correct given that
473
# the revision numbers are right justified within 5 characters
474
# for mainline revnos and 9 characters for dotted revnos.
475
self.assertFormatterResult("""\
476
2 Joe Foo\t2005-11-22 [merge]
479
1.1.1 Joe Foo\t2005-11-22
482
1 Joe Foo\t2005-11-22
486
wt.branch, log.ShortLogFormatter,
487
formatter_kwargs=dict(levels=0))
489
def test_short_merge_revs_log_single_merge_revision(self):
490
wt = self._prepare_tree_with_merges()
491
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
492
rev = revspec.in_history(wt.branch)
493
self.assertFormatterResult("""\
494
1.1.1 Joe Foo\t2005-11-22
498
wt.branch, log.ShortLogFormatter,
499
formatter_kwargs=dict(levels=0),
500
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
503
class TestLongLogFormatter(TestCaseForLogFormatter):
505
def test_verbose_log(self):
506
"""Verbose log includes changed files
510
wt = self.make_standard_commit('test_verbose_log', authors=[])
511
self.assertFormatterResult('''\
512
------------------------------------------------------------
514
committer: Lorem Ipsum <test@example.com>
515
branch nick: test_verbose_log
516
timestamp: Tue 2005-11-22 00:00:00 +0000
522
wt.branch, log.LongLogFormatter,
523
show_log_kwargs=dict(verbose=True))
525
def test_merges_are_indented_by_level(self):
526
wt = self.make_branch_and_tree('parent')
527
self.wt_commit(wt, 'first post')
528
child_wt = wt.controldir.sprout('child').open_workingtree()
529
self.wt_commit(child_wt, 'branch 1')
530
smallerchild_wt = wt.controldir.sprout('smallerchild').open_workingtree()
531
self.wt_commit(smallerchild_wt, 'branch 2')
532
child_wt.merge_from_branch(smallerchild_wt.branch)
533
self.wt_commit(child_wt, 'merge branch 2')
534
wt.merge_from_branch(child_wt.branch)
535
self.wt_commit(wt, 'merge branch 1')
536
self.assertFormatterResult("""\
537
------------------------------------------------------------
539
committer: Joe Foo <joe@foo.com>
541
timestamp: Tue 2005-11-22 00:00:04 +0000
544
------------------------------------------------------------
546
committer: Joe Foo <joe@foo.com>
548
timestamp: Tue 2005-11-22 00:00:03 +0000
551
------------------------------------------------------------
553
committer: Joe Foo <joe@foo.com>
554
branch nick: smallerchild
555
timestamp: Tue 2005-11-22 00:00:02 +0000
558
------------------------------------------------------------
560
committer: Joe Foo <joe@foo.com>
562
timestamp: Tue 2005-11-22 00:00:01 +0000
565
------------------------------------------------------------
567
committer: Joe Foo <joe@foo.com>
569
timestamp: Tue 2005-11-22 00:00:00 +0000
573
wt.branch, log.LongLogFormatter,
574
formatter_kwargs=dict(levels=0),
575
show_log_kwargs=dict(verbose=True))
577
def test_verbose_merge_revisions_contain_deltas(self):
578
wt = self.make_branch_and_tree('parent')
579
self.build_tree(['parent/f1', 'parent/f2'])
581
self.wt_commit(wt, 'first post')
582
child_wt = wt.controldir.sprout('child').open_workingtree()
583
os.unlink('child/f1')
584
self.build_tree_contents([('child/f2', 'hello\n')])
585
self.wt_commit(child_wt, 'removed f1 and modified f2')
586
wt.merge_from_branch(child_wt.branch)
587
self.wt_commit(wt, 'merge branch 1')
588
self.assertFormatterResult("""\
589
------------------------------------------------------------
591
committer: Joe Foo <joe@foo.com>
593
timestamp: Tue 2005-11-22 00:00:02 +0000
600
------------------------------------------------------------
602
committer: Joe Foo <joe@foo.com>
604
timestamp: Tue 2005-11-22 00:00:01 +0000
606
removed f1 and modified f2
611
------------------------------------------------------------
613
committer: Joe Foo <joe@foo.com>
615
timestamp: Tue 2005-11-22 00:00:00 +0000
622
wt.branch, log.LongLogFormatter,
623
formatter_kwargs=dict(levels=0),
624
show_log_kwargs=dict(verbose=True))
626
def test_trailing_newlines(self):
627
wt = self.make_branch_and_tree('.')
628
b = self.make_commits_with_trailing_newlines(wt)
629
self.assertFormatterResult("""\
630
------------------------------------------------------------
632
committer: Joe Foo <joe@foo.com>
634
timestamp: Tue 2005-11-22 00:00:02 +0000
636
single line with trailing newline
637
------------------------------------------------------------
639
committer: Joe Foo <joe@foo.com>
641
timestamp: Tue 2005-11-22 00:00:01 +0000
646
------------------------------------------------------------
648
committer: Joe Foo <joe@foo.com>
650
timestamp: Tue 2005-11-22 00:00:00 +0000
654
b, log.LongLogFormatter)
656
def test_author_in_log(self):
657
"""Log includes the author name if it's set in
658
the revision properties
660
wt = self.make_standard_commit('test_author_log',
661
authors=['John Doe <jdoe@example.com>',
662
'Jane Rey <jrey@example.com>'])
663
self.assertFormatterResult("""\
664
------------------------------------------------------------
666
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
667
committer: Lorem Ipsum <test@example.com>
668
branch nick: test_author_log
669
timestamp: Tue 2005-11-22 00:00:00 +0000
673
wt.branch, log.LongLogFormatter)
675
def test_properties_in_log(self):
676
"""Log includes the custom properties returned by the registered
679
wt = self.make_standard_commit('test_properties_in_log')
680
def trivial_custom_prop_handler(revision):
681
return {'test_prop':'test_value'}
683
# Cleaned up in setUp()
684
log.properties_handler_registry.register(
685
'trivial_custom_prop_handler',
686
trivial_custom_prop_handler)
687
self.assertFormatterResult("""\
688
------------------------------------------------------------
690
test_prop: test_value
691
author: John Doe <jdoe@example.com>
692
committer: Lorem Ipsum <test@example.com>
693
branch nick: test_properties_in_log
694
timestamp: Tue 2005-11-22 00:00:00 +0000
698
wt.branch, log.LongLogFormatter)
700
def test_properties_in_short_log(self):
701
"""Log includes the custom properties returned by the registered
704
wt = self.make_standard_commit('test_properties_in_short_log')
705
def trivial_custom_prop_handler(revision):
706
return {'test_prop':'test_value'}
708
log.properties_handler_registry.register(
709
'trivial_custom_prop_handler',
710
trivial_custom_prop_handler)
711
self.assertFormatterResult("""\
712
1 John Doe\t2005-11-22
713
test_prop: test_value
717
wt.branch, log.ShortLogFormatter)
719
def test_error_in_properties_handler(self):
720
"""Log includes the custom properties returned by the registered
723
wt = self.make_standard_commit('error_in_properties_handler',
724
revprops={'first_prop':'first_value'})
725
sio = self.make_utf8_encoded_stringio()
726
formatter = log.LongLogFormatter(to_file=sio)
727
def trivial_custom_prop_handler(revision):
728
raise Exception("a test error")
730
log.properties_handler_registry.register(
731
'trivial_custom_prop_handler',
732
trivial_custom_prop_handler)
733
self.assertRaises(Exception, log.show_log, wt.branch, formatter,)
735
def test_properties_handler_bad_argument(self):
736
wt = self.make_standard_commit('bad_argument',
737
revprops={'a_prop':'test_value'})
738
sio = self.make_utf8_encoded_stringio()
739
formatter = log.LongLogFormatter(to_file=sio)
740
def bad_argument_prop_handler(revision):
741
return {'custom_prop_name':revision.properties['a_prop']}
743
log.properties_handler_registry.register(
744
'bad_argument_prop_handler',
745
bad_argument_prop_handler)
747
self.assertRaises(AttributeError, formatter.show_properties,
750
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
751
formatter.show_properties(revision, '')
752
self.assertEqualDiff('''custom_prop_name: test_value\n''',
755
def test_show_ids(self):
756
wt = self.make_branch_and_tree('parent')
757
self.build_tree(['parent/f1', 'parent/f2'])
759
self.wt_commit(wt, 'first post', rev_id='a')
760
child_wt = wt.controldir.sprout('child').open_workingtree()
761
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
762
wt.merge_from_branch(child_wt.branch)
763
self.wt_commit(wt, 'merge branch 1', rev_id='c')
764
self.assertFormatterResult("""\
765
------------------------------------------------------------
770
committer: Joe Foo <joe@foo.com>
772
timestamp: Tue 2005-11-22 00:00:02 +0000
775
------------------------------------------------------------
779
committer: Joe Foo <joe@foo.com>
781
timestamp: Tue 2005-11-22 00:00:01 +0000
784
------------------------------------------------------------
787
committer: Joe Foo <joe@foo.com>
789
timestamp: Tue 2005-11-22 00:00:00 +0000
793
wt.branch, log.LongLogFormatter,
794
formatter_kwargs=dict(levels=0, show_ids=True))
797
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
799
def test_long_verbose_log(self):
800
"""Verbose log includes changed files
804
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
805
self.assertFormatterResult("""\
806
------------------------------------------------------------
808
committer: Lorem Ipsum <test@example.com>
809
branch nick: test_long_verbose_log
810
timestamp: Tue 2005-11-22 00:00:00 +0000
816
wt.branch, log.LongLogFormatter,
817
formatter_kwargs=dict(levels=1),
818
show_log_kwargs=dict(verbose=True))
820
def test_long_verbose_contain_deltas(self):
821
wt = self.make_branch_and_tree('parent')
822
self.build_tree(['parent/f1', 'parent/f2'])
824
self.wt_commit(wt, 'first post')
825
child_wt = wt.controldir.sprout('child').open_workingtree()
826
os.unlink('child/f1')
827
self.build_tree_contents([('child/f2', 'hello\n')])
828
self.wt_commit(child_wt, 'removed f1 and modified f2')
829
wt.merge_from_branch(child_wt.branch)
830
self.wt_commit(wt, 'merge branch 1')
831
self.assertFormatterResult("""\
832
------------------------------------------------------------
834
committer: Joe Foo <joe@foo.com>
836
timestamp: Tue 2005-11-22 00:00:02 +0000
843
------------------------------------------------------------
845
committer: Joe Foo <joe@foo.com>
847
timestamp: Tue 2005-11-22 00:00:00 +0000
854
wt.branch, log.LongLogFormatter,
855
formatter_kwargs=dict(levels=1),
856
show_log_kwargs=dict(verbose=True))
858
def test_long_trailing_newlines(self):
859
wt = self.make_branch_and_tree('.')
860
b = self.make_commits_with_trailing_newlines(wt)
861
self.assertFormatterResult("""\
862
------------------------------------------------------------
864
committer: Joe Foo <joe@foo.com>
866
timestamp: Tue 2005-11-22 00:00:02 +0000
868
single line with trailing newline
869
------------------------------------------------------------
871
committer: Joe Foo <joe@foo.com>
873
timestamp: Tue 2005-11-22 00:00:01 +0000
878
------------------------------------------------------------
880
committer: Joe Foo <joe@foo.com>
882
timestamp: Tue 2005-11-22 00:00:00 +0000
886
b, log.LongLogFormatter,
887
formatter_kwargs=dict(levels=1))
889
def test_long_author_in_log(self):
890
"""Log includes the author name if it's set in
891
the revision properties
893
wt = self.make_standard_commit('test_author_log')
894
self.assertFormatterResult("""\
895
------------------------------------------------------------
897
author: John Doe <jdoe@example.com>
898
committer: Lorem Ipsum <test@example.com>
899
branch nick: test_author_log
900
timestamp: Tue 2005-11-22 00:00:00 +0000
904
wt.branch, log.LongLogFormatter,
905
formatter_kwargs=dict(levels=1))
907
def test_long_properties_in_log(self):
908
"""Log includes the custom properties returned by the registered
911
wt = self.make_standard_commit('test_properties_in_log')
912
def trivial_custom_prop_handler(revision):
913
return {'test_prop':'test_value'}
915
log.properties_handler_registry.register(
916
'trivial_custom_prop_handler',
917
trivial_custom_prop_handler)
918
self.assertFormatterResult("""\
919
------------------------------------------------------------
921
test_prop: test_value
922
author: John Doe <jdoe@example.com>
923
committer: Lorem Ipsum <test@example.com>
924
branch nick: test_properties_in_log
925
timestamp: Tue 2005-11-22 00:00:00 +0000
929
wt.branch, log.LongLogFormatter,
930
formatter_kwargs=dict(levels=1))
933
class TestLineLogFormatter(TestCaseForLogFormatter):
935
def test_line_log(self):
936
"""Line log should show revno
940
wt = self.make_standard_commit('test-line-log',
941
committer='Line-Log-Formatter Tester <test@line.log>',
943
self.assertFormatterResult("""\
944
1: Line-Log-Formatte... 2005-11-22 add a
946
wt.branch, log.LineLogFormatter)
948
def test_trailing_newlines(self):
949
wt = self.make_branch_and_tree('.')
950
b = self.make_commits_with_trailing_newlines(wt)
951
self.assertFormatterResult("""\
952
3: Joe Foo 2005-11-22 single line with trailing newline
953
2: Joe Foo 2005-11-22 multiline
954
1: Joe Foo 2005-11-22 simple log message
956
b, log.LineLogFormatter)
958
def test_line_log_single_merge_revision(self):
959
wt = self._prepare_tree_with_merges()
960
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
961
rev = revspec.in_history(wt.branch)
962
self.assertFormatterResult("""\
963
1.1.1: Joe Foo 2005-11-22 rev-merged
965
wt.branch, log.LineLogFormatter,
966
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
968
def test_line_log_with_tags(self):
969
wt = self._prepare_tree_with_merges(with_tags=True)
970
self.assertFormatterResult("""\
971
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
972
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
973
1: Joe Foo 2005-11-22 rev-1
975
wt.branch, log.LineLogFormatter)
978
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
980
def test_line_merge_revs_log(self):
981
"""Line log should show revno
985
wt = self.make_standard_commit('test-line-log',
986
committer='Line-Log-Formatter Tester <test@line.log>',
988
self.assertFormatterResult("""\
989
1: Line-Log-Formatte... 2005-11-22 add a
991
wt.branch, log.LineLogFormatter)
993
def test_line_merge_revs_log_single_merge_revision(self):
994
wt = self._prepare_tree_with_merges()
995
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
996
rev = revspec.in_history(wt.branch)
997
self.assertFormatterResult("""\
998
1.1.1: Joe Foo 2005-11-22 rev-merged
1000
wt.branch, log.LineLogFormatter,
1001
formatter_kwargs=dict(levels=0),
1002
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
1004
def test_line_merge_revs_log_with_merges(self):
1005
wt = self._prepare_tree_with_merges()
1006
self.assertFormatterResult("""\
1007
2: Joe Foo 2005-11-22 [merge] rev-2
1008
1.1.1: Joe Foo 2005-11-22 rev-merged
1009
1: Joe Foo 2005-11-22 rev-1
1011
wt.branch, log.LineLogFormatter,
1012
formatter_kwargs=dict(levels=0))
1015
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
1017
def test_gnu_changelog(self):
1018
wt = self.make_standard_commit('nicky', authors=[])
1019
self.assertFormatterResult('''\
1020
2005-11-22 Lorem Ipsum <test@example.com>
1025
wt.branch, log.GnuChangelogLogFormatter)
1027
def test_with_authors(self):
1028
wt = self.make_standard_commit('nicky',
1029
authors=['Fooa Fooz <foo@example.com>',
1030
'Bari Baro <bar@example.com>'])
1031
self.assertFormatterResult('''\
1032
2005-11-22 Fooa Fooz <foo@example.com>
1037
wt.branch, log.GnuChangelogLogFormatter)
1039
def test_verbose(self):
1040
wt = self.make_standard_commit('nicky')
1041
self.assertFormatterResult('''\
1042
2005-11-22 John Doe <jdoe@example.com>
1049
wt.branch, log.GnuChangelogLogFormatter,
1050
show_log_kwargs=dict(verbose=True))
1053
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1055
def test_show_changed_revisions_verbose(self):
1056
tree = self.make_branch_and_tree('tree_a')
1057
self.build_tree(['tree_a/foo'])
1059
tree.commit('bar', rev_id='bar-id')
1060
s = self.make_utf8_encoded_stringio()
1061
log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
1062
self.assertContainsRe(s.getvalue(), 'bar')
1063
self.assertNotContainsRe(s.getvalue(), 'foo')
1066
class TestLogFormatter(tests.TestCase):
1069
super(TestLogFormatter, self).setUp()
1070
self.rev = revision.Revision('a-id')
1071
self.lf = log.LogFormatter(None)
1073
def test_short_committer(self):
1074
def assertCommitter(expected, committer):
1075
self.rev.committer = committer
1076
self.assertEqual(expected, self.lf.short_committer(self.rev))
1078
assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
1079
assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
1080
assertCommitter('John Smith', 'John Smith')
1081
assertCommitter('jsmith@example.com', 'jsmith@example.com')
1082
assertCommitter('jsmith@example.com', '<jsmith@example.com>')
1083
assertCommitter('John Smith', 'John Smith jsmith@example.com')
1085
def test_short_author(self):
1086
def assertAuthor(expected, author):
1087
self.rev.properties['author'] = author
1088
self.assertEqual(expected, self.lf.short_author(self.rev))
1090
assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
1091
assertAuthor('John Smith', 'John Smith')
1092
assertAuthor('jsmith@example.com', 'jsmith@example.com')
1093
assertAuthor('jsmith@example.com', '<jsmith@example.com>')
1094
assertAuthor('John Smith', 'John Smith jsmith@example.com')
1096
def test_short_author_from_committer(self):
1097
self.rev.committer = 'John Doe <jdoe@example.com>'
1098
self.assertEqual('John Doe', self.lf.short_author(self.rev))
1100
def test_short_author_from_authors(self):
1101
self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1102
'Jane Rey <jrey@example.com>')
1103
self.assertEqual('John Smith', self.lf.short_author(self.rev))
1106
class TestReverseByDepth(tests.TestCase):
1107
"""Test reverse_by_depth behavior.
1109
This is used to present revisions in forward (oldest first) order in a nice
1112
The tests use lighter revision description to ease reading.
1115
def assertReversed(self, forward, backward):
1116
# Transform the descriptions to suit the API: tests use (revno, depth),
1117
# while the API expects (revid, revno, depth)
1118
def complete_revisions(l):
1119
"""Transform the description to suit the API.
1121
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1122
Since the revid is arbitrary, we just duplicate revno
1124
return [ (r, r, d) for r, d in l]
1125
forward = complete_revisions(forward)
1126
backward= complete_revisions(backward)
1127
self.assertEqual(forward, log.reverse_by_depth(backward))
1130
def test_mainline_revisions(self):
1131
self.assertReversed([( '1', 0), ('2', 0)],
1132
[('2', 0), ('1', 0)])
1134
def test_merged_revisions(self):
1135
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1136
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1137
def test_shifted_merged_revisions(self):
1138
"""Test irregular layout.
1140
Requesting revisions touching a file can produce "holes" in the depths.
1142
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1143
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1145
def test_merged_without_child_revisions(self):
1146
"""Test irregular layout.
1148
Revision ranges can produce "holes" in the depths.
1150
# When a revision of higher depth doesn't follow one of lower depth, we
1151
# assume a lower depth one is virtually there
1152
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1153
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1154
# So we get the same order after reversing below even if the original
1155
# revisions are not in the same order.
1156
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1157
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1160
class TestHistoryChange(tests.TestCaseWithTransport):
1162
def setup_a_tree(self):
1163
tree = self.make_branch_and_tree('tree')
1165
self.addCleanup(tree.unlock)
1166
tree.commit('1a', rev_id='1a')
1167
tree.commit('2a', rev_id='2a')
1168
tree.commit('3a', rev_id='3a')
1171
def setup_ab_tree(self):
1172
tree = self.setup_a_tree()
1173
tree.set_last_revision('1a')
1174
tree.branch.set_last_revision_info(1, '1a')
1175
tree.commit('2b', rev_id='2b')
1176
tree.commit('3b', rev_id='3b')
1179
def setup_ac_tree(self):
1180
tree = self.setup_a_tree()
1181
tree.set_last_revision(revision.NULL_REVISION)
1182
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1183
tree.commit('1c', rev_id='1c')
1184
tree.commit('2c', rev_id='2c')
1185
tree.commit('3c', rev_id='3c')
1188
def test_all_new(self):
1189
tree = self.setup_ab_tree()
1190
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1191
self.assertEqual([], old)
1192
self.assertEqual(['2a', '3a'], new)
1194
def test_all_old(self):
1195
tree = self.setup_ab_tree()
1196
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1197
self.assertEqual([], new)
1198
self.assertEqual(['2a', '3a'], old)
1200
def test_null_old(self):
1201
tree = self.setup_ab_tree()
1202
old, new = log.get_history_change(revision.NULL_REVISION,
1203
'3a', tree.branch.repository)
1204
self.assertEqual([], old)
1205
self.assertEqual(['1a', '2a', '3a'], new)
1207
def test_null_new(self):
1208
tree = self.setup_ab_tree()
1209
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1210
tree.branch.repository)
1211
self.assertEqual([], new)
1212
self.assertEqual(['1a', '2a', '3a'], old)
1214
def test_diverged(self):
1215
tree = self.setup_ab_tree()
1216
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1217
self.assertEqual(old, ['2a', '3a'])
1218
self.assertEqual(new, ['2b', '3b'])
1220
def test_unrelated(self):
1221
tree = self.setup_ac_tree()
1222
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1223
self.assertEqual(old, ['1a', '2a', '3a'])
1224
self.assertEqual(new, ['1c', '2c', '3c'])
1226
def test_show_branch_change(self):
1227
tree = self.setup_ab_tree()
1229
log.show_branch_change(tree.branch, s, 3, '3a')
1230
self.assertContainsRe(s.getvalue(),
1231
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1232
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1234
def test_show_branch_change_no_change(self):
1235
tree = self.setup_ab_tree()
1237
log.show_branch_change(tree.branch, s, 3, '3b')
1238
self.assertEqual(s.getvalue(),
1239
'Nothing seems to have changed\n')
1241
def test_show_branch_change_no_old(self):
1242
tree = self.setup_ab_tree()
1244
log.show_branch_change(tree.branch, s, 2, '2b')
1245
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1246
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1248
def test_show_branch_change_no_new(self):
1249
tree = self.setup_ab_tree()
1250
tree.branch.set_last_revision_info(2, '2b')
1252
log.show_branch_change(tree.branch, s, 3, '3b')
1253
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1254
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1257
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1259
def setup_a_tree(self):
1260
tree = self.make_branch_and_tree('tree')
1262
self.addCleanup(tree.unlock)
1264
'committer': 'Joe Foo <joe@foo.com>',
1265
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1266
'timezone': 0, # UTC
1268
tree.commit('commit 1a', rev_id='1a', **kwargs)
1269
tree.commit('commit 2a', rev_id='2a', **kwargs)
1270
tree.commit('commit 3a', rev_id='3a', **kwargs)
1273
def setup_ab_tree(self):
1274
tree = self.setup_a_tree()
1275
tree.set_last_revision('1a')
1276
tree.branch.set_last_revision_info(1, '1a')
1278
'committer': 'Joe Foo <joe@foo.com>',
1279
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1280
'timezone': 0, # UTC
1282
tree.commit('commit 2b', rev_id='2b', **kwargs)
1283
tree.commit('commit 3b', rev_id='3b', **kwargs)
1286
def test_one_revision(self):
1287
tree = self.setup_ab_tree()
1289
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1290
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1292
self.assertEqual(1, len(lf.revisions))
1293
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1294
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1296
def test_many_revisions(self):
1297
tree = self.setup_ab_tree()
1299
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1300
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1301
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1302
end_revision=end_rev)
1303
self.assertEqual(3, len(lf.revisions))
1304
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1305
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1306
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1307
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1308
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1310
def test_long_format(self):
1311
tree = self.setup_ab_tree()
1312
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1313
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1314
self.assertFormatterResult("""\
1315
------------------------------------------------------------
1317
committer: Joe Foo <joe@foo.com>
1319
timestamp: Tue 2005-11-22 00:00:00 +0000
1322
------------------------------------------------------------
1324
committer: Joe Foo <joe@foo.com>
1326
timestamp: Tue 2005-11-22 00:00:00 +0000
1329
------------------------------------------------------------
1331
committer: Joe Foo <joe@foo.com>
1333
timestamp: Tue 2005-11-22 00:00:00 +0000
1337
tree.branch, log.LongLogFormatter, show_log_kwargs={
1338
'start_revision': start_rev, 'end_revision': end_rev
1341
def test_short_format(self):
1342
tree = self.setup_ab_tree()
1343
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1344
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1345
self.assertFormatterResult("""\
1354
1 Joe Foo\t2005-11-22
1358
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1359
'start_revision': start_rev, 'end_revision': end_rev
1362
def test_line_format(self):
1363
tree = self.setup_ab_tree()
1364
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1365
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1366
self.assertFormatterResult("""\
1367
Joe Foo 2005-11-22 commit 3a
1368
Joe Foo 2005-11-22 commit 2a
1369
1: Joe Foo 2005-11-22 commit 1a
1371
tree.branch, log.LineLogFormatter, show_log_kwargs={
1372
'start_revision': start_rev, 'end_revision': end_rev
1376
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1379
super(TestLogWithBugs, self).setUp()
1380
log.properties_handler_registry.register(
1381
'bugs_properties_handler',
1382
log._bugs_properties_handler)
1384
def make_commits_with_bugs(self):
1385
"""Helper method for LogFormatter tests"""
1386
tree = self.make_branch_and_tree(u'.')
1387
self.build_tree(['a', 'b'])
1389
self.wt_commit(tree, 'simple log message', rev_id='a1',
1390
revprops={'bugs': 'test://bug/id fixed'})
1392
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
1393
authors=['Joe Bar <joe@bar.com>'],
1394
revprops={'bugs': 'test://bug/id fixed\n'
1395
'test://bug/2 fixed'})
1399
def test_long_bugs(self):
1400
tree = self.make_commits_with_bugs()
1401
self.assertFormatterResult("""\
1402
------------------------------------------------------------
1404
fixes bugs: test://bug/id test://bug/2
1405
author: Joe Bar <joe@bar.com>
1406
committer: Joe Foo <joe@foo.com>
1408
timestamp: Tue 2005-11-22 00:00:01 +0000
1413
------------------------------------------------------------
1415
fixes bug: test://bug/id
1416
committer: Joe Foo <joe@foo.com>
1418
timestamp: Tue 2005-11-22 00:00:00 +0000
1422
tree.branch, log.LongLogFormatter)
1424
def test_short_bugs(self):
1425
tree = self.make_commits_with_bugs()
1426
self.assertFormatterResult("""\
1427
2 Joe Bar\t2005-11-22
1428
fixes bugs: test://bug/id test://bug/2
1433
1 Joe Foo\t2005-11-22
1434
fixes bug: test://bug/id
1438
tree.branch, log.ShortLogFormatter)
1440
def test_wrong_bugs_property(self):
1441
tree = self.make_branch_and_tree(u'.')
1442
self.build_tree(['foo'])
1443
self.wt_commit(tree, 'simple log message', rev_id='a1',
1444
revprops={'bugs': 'test://bug/id invalid_value'})
1445
self.assertFormatterResult("""\
1446
1 Joe Foo\t2005-11-22
1450
tree.branch, log.ShortLogFormatter)
1452
def test_bugs_handler_present(self):
1453
self.properties_handler_registry.get('bugs_properties_handler')
1456
class TestLogForAuthors(TestCaseForLogFormatter):
1459
super(TestLogForAuthors, self).setUp()
1460
self.wt = self.make_standard_commit('nicky',
1461
authors=['John Doe <jdoe@example.com>',
1462
'Jane Rey <jrey@example.com>'])
1464
def assertFormatterResult(self, formatter, who, result):
1465
formatter_kwargs = dict()
1467
author_list_handler = log.author_list_registry.get(who)
1468
formatter_kwargs['author_list_handler'] = author_list_handler
1469
TestCaseForLogFormatter.assertFormatterResult(self, result,
1470
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1472
def test_line_default(self):
1473
self.assertFormatterResult(log.LineLogFormatter, None, """\
1474
1: John Doe 2005-11-22 add a
1477
def test_line_committer(self):
1478
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1479
1: Lorem Ipsum 2005-11-22 add a
1482
def test_line_first(self):
1483
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1484
1: John Doe 2005-11-22 add a
1487
def test_line_all(self):
1488
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1489
1: John Doe, Jane Rey 2005-11-22 add a
1493
def test_short_default(self):
1494
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1495
1 John Doe\t2005-11-22
1500
def test_short_committer(self):
1501
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1502
1 Lorem Ipsum\t2005-11-22
1507
def test_short_first(self):
1508
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1509
1 John Doe\t2005-11-22
1514
def test_short_all(self):
1515
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1516
1 John Doe, Jane Rey\t2005-11-22
1521
def test_long_default(self):
1522
self.assertFormatterResult(log.LongLogFormatter, None, """\
1523
------------------------------------------------------------
1525
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1526
committer: Lorem Ipsum <test@example.com>
1528
timestamp: Tue 2005-11-22 00:00:00 +0000
1533
def test_long_committer(self):
1534
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1535
------------------------------------------------------------
1537
committer: Lorem Ipsum <test@example.com>
1539
timestamp: Tue 2005-11-22 00:00:00 +0000
1544
def test_long_first(self):
1545
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1546
------------------------------------------------------------
1548
author: John Doe <jdoe@example.com>
1549
committer: Lorem Ipsum <test@example.com>
1551
timestamp: Tue 2005-11-22 00:00:00 +0000
1556
def test_long_all(self):
1557
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1558
------------------------------------------------------------
1560
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1561
committer: Lorem Ipsum <test@example.com>
1563
timestamp: Tue 2005-11-22 00:00:00 +0000
1568
def test_gnu_changelog_default(self):
1569
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1570
2005-11-22 John Doe <jdoe@example.com>
1576
def test_gnu_changelog_committer(self):
1577
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1578
2005-11-22 Lorem Ipsum <test@example.com>
1584
def test_gnu_changelog_first(self):
1585
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1586
2005-11-22 John Doe <jdoe@example.com>
1592
def test_gnu_changelog_all(self):
1593
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1594
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1601
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1603
def make_branch_with_alternate_ancestries(self, relpath='.'):
1604
# See test_merge_sorted_exclude_ancestry below for the difference with
1605
# bt.per_branch.test_iter_merge_sorted_revision.
1606
# TestIterMergeSortedRevisionsBushyGraph.
1607
# make_branch_with_alternate_ancestries
1608
# and test_merge_sorted_exclude_ancestry
1609
# See the FIXME in assertLogRevnos too.
1610
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1622
builder.start_series()
1623
builder.build_snapshot(None, [
1624
('add', ('', 'TREE_ROOT', 'directory', '')),],
1626
builder.build_snapshot(['1'], [], revision_id='1.1.1')
1627
builder.build_snapshot(['1'], [], revision_id='2')
1628
builder.build_snapshot(['1.1.1'], [], revision_id='1.2.1')
1629
builder.build_snapshot(['1.1.1', '1.2.1'], [], revision_id='1.1.2')
1630
builder.build_snapshot(['2', '1.1.2'], [], revision_id='3')
1631
builder.finish_series()
1632
br = builder.get_branch()
1634
self.addCleanup(br.unlock)
1637
def assertLogRevnos(self, expected_revnos, b, start, end,
1638
exclude_common_ancestry, generate_merge_revisions=True):
1639
# FIXME: the layering in log makes it hard to test intermediate levels,
1640
# I wish adding filters with their parameters was easier...
1642
iter_revs = log._calc_view_revisions(
1643
b, start, end, direction='reverse',
1644
generate_merge_revisions=generate_merge_revisions,
1645
exclude_common_ancestry=exclude_common_ancestry)
1646
self.assertEqual(expected_revnos,
1647
[revid for revid, revno, depth in iter_revs])
1649
def test_merge_sorted_exclude_ancestry(self):
1650
b = self.make_branch_with_alternate_ancestries()
1651
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1652
b, '1', '3', exclude_common_ancestry=False)
1653
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1654
# it should be mentioned even if merge_sort order will make it appear
1656
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1657
b, '1.1.1', '3', exclude_common_ancestry=True)
1659
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1660
b = self.make_branch_with_alternate_ancestries()
1661
self.assertLogRevnos(['3', '2'],
1662
b, '1', '3', exclude_common_ancestry=True,
1663
generate_merge_revisions=False)
1664
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1665
b, '1', '3', exclude_common_ancestry=True,
1666
generate_merge_revisions=True)
1669
class TestLogDefaults(TestCaseForLogFormatter):
1670
def test_default_log_level(self):
1672
Test to ensure that specifying 'levels=1' to make_log_request_dict
1673
doesn't get overwritten when using a LogFormatter that supports more
1677
wt = self._prepare_tree_with_merges()
1680
class CustomLogFormatter(log.LogFormatter):
1681
def __init__(self, *args, **kwargs):
1682
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1684
def get_levels(self):
1685
# log formatter supports all levels:
1687
def log_revision(self, revision):
1688
self.revisions.append(revision)
1690
log_formatter = LogCatcher()
1691
# First request we don't specify number of levels, we should get a
1692
# sensible default (whatever the LogFormatter handles - which in this
1693
# case is 0/everything):
1694
request = log.make_log_request_dict(limit=10)
1695
log.Logger(b, request).show(log_formatter)
1696
# should have all three revisions:
1697
self.assertEqual(len(log_formatter.revisions), 3)
1700
log_formatter = LogCatcher()
1701
# now explicitly request mainline revisions only:
1702
request = log.make_log_request_dict(limit=10, levels=1)
1703
log.Logger(b, request).show(log_formatter)
1704
# should now only have 2 revisions:
1705
self.assertEqual(len(log_formatter.revisions), 2)