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 (
37
class TestLogMixin(object):
39
def wt_commit(self, wt, message, **kwargs):
40
"""Use some mostly fixed values for commits to simplify tests.
42
Tests can use this function to get some commit attributes. The time
43
stamp is incremented at each commit.
45
if getattr(self, 'timestamp', None) is None:
46
self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
48
self.timestamp += 1 # 1 second between each commit
49
kwargs.setdefault('timestamp', self.timestamp)
50
kwargs.setdefault('timezone', 0) # UTC
51
kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
53
return wt.commit(message, **kwargs)
56
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
59
super(TestCaseForLogFormatter, self).setUp()
60
# keep a reference to the "current" custom prop. handler registry
61
self.properties_handler_registry = log.properties_handler_registry
62
# Use a clean registry for log
63
log.properties_handler_registry = registry.Registry()
66
log.properties_handler_registry = self.properties_handler_registry
67
self.addCleanup(restore)
69
def assertFormatterResult(self, result, branch, formatter_class,
70
formatter_kwargs=None, show_log_kwargs=None):
71
logfile = self.make_utf8_encoded_stringio()
72
if formatter_kwargs is None:
74
formatter = formatter_class(to_file=logfile, **formatter_kwargs)
75
if show_log_kwargs is None:
77
log.show_log(branch, formatter, **show_log_kwargs)
78
self.assertEqualDiff(result, logfile.getvalue())
80
def make_standard_commit(self, branch_nick, **kwargs):
81
wt = self.make_branch_and_tree('.')
83
self.addCleanup(wt.unlock)
84
self.build_tree(['a'])
86
wt.branch.nick = branch_nick
87
kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
88
kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
89
self.wt_commit(wt, 'add a', **kwargs)
92
def make_commits_with_trailing_newlines(self, wt):
93
"""Helper method for LogFormatter tests"""
96
self.build_tree_contents([('a', b'hello moto\n')])
97
self.wt_commit(wt, 'simple log message', rev_id=b'a1')
98
self.build_tree_contents([('b', b'goodbye\n')])
100
self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id=b'a2')
102
self.build_tree_contents([('c', b'just another manic monday\n')])
104
self.wt_commit(wt, 'single line with trailing newline\n', rev_id=b'a3')
107
def _prepare_tree_with_merges(self, with_tags=False):
108
wt = self.make_branch_and_memory_tree('.')
110
self.addCleanup(wt.unlock)
112
self.wt_commit(wt, 'rev-1', rev_id=b'rev-1')
113
self.wt_commit(wt, 'rev-merged', rev_id=b'rev-2a')
114
wt.set_parent_ids([b'rev-1', b'rev-2a'])
115
wt.branch.set_last_revision_info(1, b'rev-1')
116
self.wt_commit(wt, 'rev-2', rev_id=b'rev-2b')
119
branch.tags.set_tag('v0.2', b'rev-2b')
120
self.wt_commit(wt, 'rev-3', rev_id=b'rev-3')
121
branch.tags.set_tag('v1.0rc1', b'rev-3')
122
branch.tags.set_tag('v1.0', b'rev-3')
126
class LogCatcher(log.LogFormatter):
127
"""Pull log messages into a list rather than displaying them.
129
To simplify testing we save logged revisions here rather than actually
130
formatting anything, so that we can precisely check the result without
131
being dependent on the formatting.
134
supports_merge_revisions = True
135
supports_delta = True
139
def __init__(self, *args, **kwargs):
140
kwargs.update(dict(to_file=None))
141
super(LogCatcher, self).__init__(*args, **kwargs)
144
def log_revision(self, revision):
145
self.revisions.append(revision)
148
class TestShowLog(tests.TestCaseWithTransport):
150
def checkDelta(self, delta, **kw):
151
"""Check the filenames touched by a delta are as expected.
153
Caller only have to pass in the list of files for each part, all
154
unspecified parts are considered empty (and checked as such).
156
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
157
# By default we expect an empty list
158
expected = kw.get(n, [])
159
# strip out only the path components
160
got = [x[0] for x in getattr(delta, n)]
161
self.assertEqual(expected, got)
163
def assertInvalidRevisonNumber(self, br, start, end):
165
self.assertRaises(errors.InvalidRevisionNumber,
166
log.show_log, br, lf,
167
start_revision=start, end_revision=end)
169
def test_cur_revno(self):
170
wt = self.make_branch_and_tree('.')
174
wt.commit('empty commit')
175
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
177
# Since there is a single revision in the branch all the combinations
179
self.assertInvalidRevisonNumber(b, 2, 1)
180
self.assertInvalidRevisonNumber(b, 1, 2)
181
self.assertInvalidRevisonNumber(b, 0, 2)
182
self.assertInvalidRevisonNumber(b, -1, 1)
183
self.assertInvalidRevisonNumber(b, 1, -1)
184
self.assertInvalidRevisonNumber(b, 1, 0)
186
def test_empty_branch(self):
187
wt = self.make_branch_and_tree('.')
190
log.show_log(wt.branch, lf)
192
self.assertEqual([], lf.revisions)
194
def test_empty_commit(self):
195
wt = self.make_branch_and_tree('.')
197
wt.commit('empty commit')
199
log.show_log(wt.branch, lf, verbose=True)
201
self.assertEqual(1, len(revs))
202
self.assertEqual('1', revs[0].revno)
203
self.assertEqual('empty commit', revs[0].rev.message)
204
self.checkDelta(revs[0].delta)
206
def test_simple_commit(self):
207
wt = self.make_branch_and_tree('.')
208
wt.commit('empty commit')
209
self.build_tree(['hello'])
211
wt.commit('add one file',
212
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
213
u'<test@example.com>')
215
log.show_log(wt.branch, lf, verbose=True)
216
self.assertEqual(2, len(lf.revisions))
217
# first one is most recent
218
log_entry = lf.revisions[0]
219
self.assertEqual('2', log_entry.revno)
220
self.assertEqual('add one file', log_entry.rev.message)
221
self.checkDelta(log_entry.delta, added=['hello'])
223
def test_commit_message_with_control_chars(self):
224
wt = self.make_branch_and_tree('.')
225
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
226
msg = msg.replace(u'\r', u'\n')
229
log.show_log(wt.branch, lf, verbose=True)
230
committed_msg = lf.revisions[0].rev.message
231
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
232
self.assertNotEqual(msg, committed_msg)
233
self.assertTrue(len(committed_msg) > len(msg))
235
self.assertEqual(msg, committed_msg)
237
def test_commit_message_without_control_chars(self):
238
wt = self.make_branch_and_tree('.')
239
# escaped. As ElementTree apparently does some kind of
240
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
241
# included in the test commit message, even though they are
242
# valid XML 1.0 characters.
243
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
246
log.show_log(wt.branch, lf, verbose=True)
247
committed_msg = lf.revisions[0].rev.message
248
self.assertEqual(msg, committed_msg)
250
def test_deltas_in_merge_revisions(self):
251
"""Check deltas created for both mainline and merge revisions"""
252
wt = self.make_branch_and_tree('parent')
253
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
256
wt.commit(message='add file1 and file2')
257
self.run_bzr('branch parent child')
258
os.unlink('child/file1')
259
with open('child/file2', 'wb') as f:
261
self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
264
self.run_bzr('merge ../child')
265
wt.commit('merge child branch')
269
lf.supports_merge_revisions = True
270
log.show_log(b, lf, verbose=True)
273
self.assertEqual(3, len(revs))
276
self.assertEqual('2', logentry.revno)
277
self.assertEqual('merge child branch', logentry.rev.message)
278
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
281
self.assertEqual('1.1.1', logentry.revno)
282
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
283
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
286
self.assertEqual('1', logentry.revno)
287
self.assertEqual('add file1 and file2', logentry.rev.message)
288
self.checkDelta(logentry.delta, added=['file1', 'file2'])
290
def test_bug_842695_log_restricted_to_dir(self):
291
# Comments here indicate revision numbers in trunk # VVVVV
292
trunk = self.make_branch_and_tree('this')
293
trunk.commit('initial trunk') # 1
294
adder = trunk.controldir.sprout('adder').open_workingtree()
295
merger = trunk.controldir.sprout('merger').open_workingtree()
296
self.build_tree_contents([
298
('adder/dir/file', b'foo'),
300
adder.add(['dir', 'dir/file'])
301
adder.commit('added dir') # 1.1.1
302
trunk.merge_from_branch(adder.branch)
303
trunk.commit('merged adder into trunk') # 2
304
merger.merge_from_branch(trunk.branch)
305
merger.commit('merged trunk into merger') # 1.2.1
306
# Commits are processed in increments of 200 revisions, so
307
# make sure the two merges into trunk are in different chunks.
309
trunk.commit('intermediate commit %d' % i) # 3-202
310
trunk.merge_from_branch(merger.branch)
311
trunk.commit('merged merger into trunk') # 203
312
file_id = trunk.path2id('dir')
314
lf.supports_merge_revisions = True
315
log.show_log(trunk.branch, lf, file_id)
317
self.assertEqual(['2', '1.1.1'], [r.revno for r in lf.revisions])
318
except AssertionError:
319
raise tests.KnownFailure("bug #842695")
322
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
324
def verify_revision_signature(self, revid, gpg_strategy):
325
return (gpg.SIGNATURE_VALID,
326
u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
328
def test_format_signature_validity_utf(self):
329
"""Check that GPG signatures containing UTF-8 names are formatted
331
wt = self.make_branch_and_tree('.')
332
revid = wt.commit('empty commit')
333
repo = wt.branch.repository
334
# Monkey patch out checking if this rev is actually signed, since we
335
# can't sign it without a heavier TestCase and LoopbackGPGStrategy
336
# doesn't care anyways.
337
self.overrideAttr(repo, 'verify_revision_signature',
338
self.verify_revision_signature)
339
out = log.format_signature_validity(revid, wt.branch)
341
u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
345
class TestShortLogFormatter(TestCaseForLogFormatter):
347
def test_trailing_newlines(self):
348
wt = self.make_branch_and_tree('.')
349
b = self.make_commits_with_trailing_newlines(wt)
350
self.assertFormatterResult(b"""\
351
3 Joe Foo\t2005-11-22
352
single line with trailing newline
354
2 Joe Foo\t2005-11-22
359
1 Joe Foo\t2005-11-22
363
b, log.ShortLogFormatter)
365
def test_short_log_with_merges(self):
366
wt = self._prepare_tree_with_merges()
367
self.assertFormatterResult(b"""\
368
2 Joe Foo\t2005-11-22 [merge]
371
1 Joe Foo\t2005-11-22
375
wt.branch, log.ShortLogFormatter)
377
def test_short_log_with_merges_and_advice(self):
378
wt = self._prepare_tree_with_merges()
379
self.assertFormatterResult(b"""\
380
2 Joe Foo\t2005-11-22 [merge]
383
1 Joe Foo\t2005-11-22
386
Use --include-merged or -n0 to see merged revisions.
388
wt.branch, log.ShortLogFormatter,
389
formatter_kwargs=dict(show_advice=True))
391
def test_short_log_with_merges_and_range(self):
392
wt = self._prepare_tree_with_merges()
393
self.wt_commit(wt, 'rev-3a', rev_id=b'rev-3a')
394
wt.branch.set_last_revision_info(2, b'rev-2b')
395
wt.set_parent_ids([b'rev-2b', b'rev-3a'])
396
self.wt_commit(wt, 'rev-3b', rev_id=b'rev-3b')
397
self.assertFormatterResult(b"""\
398
3 Joe Foo\t2005-11-22 [merge]
401
2 Joe Foo\t2005-11-22 [merge]
405
wt.branch, log.ShortLogFormatter,
406
show_log_kwargs=dict(start_revision=2, end_revision=3))
408
def test_short_log_with_tags(self):
409
wt = self._prepare_tree_with_merges(with_tags=True)
410
self.assertFormatterResult(b"""\
411
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
414
2 Joe Foo\t2005-11-22 {v0.2} [merge]
417
1 Joe Foo\t2005-11-22
421
wt.branch, log.ShortLogFormatter)
423
def test_short_log_single_merge_revision(self):
424
wt = self._prepare_tree_with_merges()
425
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
426
rev = revspec.in_history(wt.branch)
427
self.assertFormatterResult(b"""\
428
1.1.1 Joe Foo\t2005-11-22
432
wt.branch, log.ShortLogFormatter,
433
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
435
def test_show_ids(self):
436
wt = self.make_branch_and_tree('parent')
437
self.build_tree(['parent/f1', 'parent/f2'])
439
self.wt_commit(wt, 'first post', rev_id=b'a')
440
child_wt = wt.controldir.sprout('child').open_workingtree()
441
self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
442
wt.merge_from_branch(child_wt.branch)
443
self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
444
self.assertFormatterResult(b"""\
445
2 Joe Foo\t2005-11-22 [merge]
449
1.1.1 Joe Foo\t2005-11-22
453
1 Joe Foo\t2005-11-22
458
wt.branch, log.ShortLogFormatter,
459
formatter_kwargs=dict(levels=0, show_ids=True))
462
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
464
def test_short_merge_revs_log_with_merges(self):
465
wt = self._prepare_tree_with_merges()
466
# Note that the 1.1.1 indenting is in fact correct given that
467
# the revision numbers are right justified within 5 characters
468
# for mainline revnos and 9 characters for dotted revnos.
469
self.assertFormatterResult(b"""\
470
2 Joe Foo\t2005-11-22 [merge]
473
1.1.1 Joe Foo\t2005-11-22
476
1 Joe Foo\t2005-11-22
480
wt.branch, log.ShortLogFormatter,
481
formatter_kwargs=dict(levels=0))
483
def test_short_merge_revs_log_single_merge_revision(self):
484
wt = self._prepare_tree_with_merges()
485
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
486
rev = revspec.in_history(wt.branch)
487
self.assertFormatterResult(b"""\
488
1.1.1 Joe Foo\t2005-11-22
492
wt.branch, log.ShortLogFormatter,
493
formatter_kwargs=dict(levels=0),
494
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
497
class TestLongLogFormatter(TestCaseForLogFormatter):
499
def test_verbose_log(self):
500
"""Verbose log includes changed files
504
wt = self.make_standard_commit('test_verbose_log', authors=[])
505
self.assertFormatterResult(b'''\
506
------------------------------------------------------------
508
committer: Lorem Ipsum <test@example.com>
509
branch nick: test_verbose_log
510
timestamp: Tue 2005-11-22 00:00:00 +0000
516
wt.branch, log.LongLogFormatter,
517
show_log_kwargs=dict(verbose=True))
519
def test_merges_are_indented_by_level(self):
520
wt = self.make_branch_and_tree('parent')
521
self.wt_commit(wt, 'first post')
522
child_wt = wt.controldir.sprout('child').open_workingtree()
523
self.wt_commit(child_wt, 'branch 1')
524
smallerchild_wt = wt.controldir.sprout(
525
'smallerchild').open_workingtree()
526
self.wt_commit(smallerchild_wt, 'branch 2')
527
child_wt.merge_from_branch(smallerchild_wt.branch)
528
self.wt_commit(child_wt, 'merge branch 2')
529
wt.merge_from_branch(child_wt.branch)
530
self.wt_commit(wt, 'merge branch 1')
531
self.assertFormatterResult(b"""\
532
------------------------------------------------------------
534
committer: Joe Foo <joe@foo.com>
536
timestamp: Tue 2005-11-22 00:00:04 +0000
539
------------------------------------------------------------
541
committer: Joe Foo <joe@foo.com>
543
timestamp: Tue 2005-11-22 00:00:03 +0000
546
------------------------------------------------------------
548
committer: Joe Foo <joe@foo.com>
549
branch nick: smallerchild
550
timestamp: Tue 2005-11-22 00:00:02 +0000
553
------------------------------------------------------------
555
committer: Joe Foo <joe@foo.com>
557
timestamp: Tue 2005-11-22 00:00:01 +0000
560
------------------------------------------------------------
562
committer: Joe Foo <joe@foo.com>
564
timestamp: Tue 2005-11-22 00:00:00 +0000
568
wt.branch, log.LongLogFormatter,
569
formatter_kwargs=dict(levels=0),
570
show_log_kwargs=dict(verbose=True))
572
def test_verbose_merge_revisions_contain_deltas(self):
573
wt = self.make_branch_and_tree('parent')
574
self.build_tree(['parent/f1', 'parent/f2'])
576
self.wt_commit(wt, 'first post')
577
child_wt = wt.controldir.sprout('child').open_workingtree()
578
os.unlink('child/f1')
579
self.build_tree_contents([('child/f2', b'hello\n')])
580
self.wt_commit(child_wt, 'removed f1 and modified f2')
581
wt.merge_from_branch(child_wt.branch)
582
self.wt_commit(wt, 'merge branch 1')
583
self.assertFormatterResult(b"""\
584
------------------------------------------------------------
586
committer: Joe Foo <joe@foo.com>
588
timestamp: Tue 2005-11-22 00:00:02 +0000
595
------------------------------------------------------------
597
committer: Joe Foo <joe@foo.com>
599
timestamp: Tue 2005-11-22 00:00:01 +0000
601
removed f1 and modified f2
606
------------------------------------------------------------
608
committer: Joe Foo <joe@foo.com>
610
timestamp: Tue 2005-11-22 00:00:00 +0000
617
wt.branch, log.LongLogFormatter,
618
formatter_kwargs=dict(levels=0),
619
show_log_kwargs=dict(verbose=True))
621
def test_trailing_newlines(self):
622
wt = self.make_branch_and_tree('.')
623
b = self.make_commits_with_trailing_newlines(wt)
624
self.assertFormatterResult(b"""\
625
------------------------------------------------------------
627
committer: Joe Foo <joe@foo.com>
629
timestamp: Tue 2005-11-22 00:00:02 +0000
631
single line with trailing newline
632
------------------------------------------------------------
634
committer: Joe Foo <joe@foo.com>
636
timestamp: Tue 2005-11-22 00:00:01 +0000
641
------------------------------------------------------------
643
committer: Joe Foo <joe@foo.com>
645
timestamp: Tue 2005-11-22 00:00:00 +0000
649
b, log.LongLogFormatter)
651
def test_author_in_log(self):
652
"""Log includes the author name if it's set in
653
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(b"""\
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: Tue 2005-11-22 00:00:00 +0000
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')
676
def trivial_custom_prop_handler(revision):
677
return {'test_prop': 'test_value'}
679
# Cleaned up in setUp()
680
log.properties_handler_registry.register(
681
'trivial_custom_prop_handler',
682
trivial_custom_prop_handler)
683
self.assertFormatterResult(b"""\
684
------------------------------------------------------------
686
test_prop: test_value
687
author: John Doe <jdoe@example.com>
688
committer: Lorem Ipsum <test@example.com>
689
branch nick: test_properties_in_log
690
timestamp: Tue 2005-11-22 00:00:00 +0000
694
wt.branch, log.LongLogFormatter)
696
def test_properties_in_short_log(self):
697
"""Log includes the custom properties returned by the registered
700
wt = self.make_standard_commit('test_properties_in_short_log')
702
def trivial_custom_prop_handler(revision):
703
return {'test_prop': 'test_value'}
705
log.properties_handler_registry.register(
706
'trivial_custom_prop_handler',
707
trivial_custom_prop_handler)
708
self.assertFormatterResult(b"""\
709
1 John Doe\t2005-11-22
710
test_prop: test_value
714
wt.branch, log.ShortLogFormatter)
716
def test_error_in_properties_handler(self):
717
"""Log includes the custom properties returned by the registered
720
wt = self.make_standard_commit('error_in_properties_handler',
721
revprops={u'first_prop': 'first_value'})
722
sio = self.make_utf8_encoded_stringio()
723
formatter = log.LongLogFormatter(to_file=sio)
725
def trivial_custom_prop_handler(revision):
726
raise Exception("a test error")
728
log.properties_handler_registry.register(
729
'trivial_custom_prop_handler',
730
trivial_custom_prop_handler)
731
self.assertRaises(Exception, log.show_log, wt.branch, formatter,)
733
def test_properties_handler_bad_argument(self):
734
wt = self.make_standard_commit('bad_argument',
735
revprops={u'a_prop': 'test_value'})
736
sio = self.make_utf8_encoded_stringio()
737
formatter = log.LongLogFormatter(to_file=sio)
739
def bad_argument_prop_handler(revision):
740
return {'custom_prop_name': revision.properties['a_prop']}
742
log.properties_handler_registry.register(
743
'bad_argument_prop_handler',
744
bad_argument_prop_handler)
746
self.assertRaises(AttributeError, formatter.show_properties,
749
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
750
formatter.show_properties(revision, '')
751
self.assertEqualDiff(b'''custom_prop_name: test_value\n''',
754
def test_show_ids(self):
755
wt = self.make_branch_and_tree('parent')
756
self.build_tree(['parent/f1', 'parent/f2'])
758
self.wt_commit(wt, 'first post', rev_id=b'a')
759
child_wt = wt.controldir.sprout('child').open_workingtree()
760
self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
761
wt.merge_from_branch(child_wt.branch)
762
self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
763
self.assertFormatterResult(b"""\
764
------------------------------------------------------------
769
committer: Joe Foo <joe@foo.com>
771
timestamp: Tue 2005-11-22 00:00:02 +0000
774
------------------------------------------------------------
778
committer: Joe Foo <joe@foo.com>
780
timestamp: Tue 2005-11-22 00:00:01 +0000
783
------------------------------------------------------------
786
committer: Joe Foo <joe@foo.com>
788
timestamp: Tue 2005-11-22 00:00:00 +0000
792
wt.branch, log.LongLogFormatter,
793
formatter_kwargs=dict(levels=0, show_ids=True))
796
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
798
def test_long_verbose_log(self):
799
"""Verbose log includes changed files
803
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
804
self.assertFormatterResult(b"""\
805
------------------------------------------------------------
807
committer: Lorem Ipsum <test@example.com>
808
branch nick: test_long_verbose_log
809
timestamp: Tue 2005-11-22 00:00:00 +0000
815
wt.branch, log.LongLogFormatter,
816
formatter_kwargs=dict(levels=1),
817
show_log_kwargs=dict(verbose=True))
819
def test_long_verbose_contain_deltas(self):
820
wt = self.make_branch_and_tree('parent')
821
self.build_tree(['parent/f1', 'parent/f2'])
823
self.wt_commit(wt, 'first post')
824
child_wt = wt.controldir.sprout('child').open_workingtree()
825
os.unlink('child/f1')
826
self.build_tree_contents([('child/f2', b'hello\n')])
827
self.wt_commit(child_wt, 'removed f1 and modified f2')
828
wt.merge_from_branch(child_wt.branch)
829
self.wt_commit(wt, 'merge branch 1')
830
self.assertFormatterResult(b"""\
831
------------------------------------------------------------
833
committer: Joe Foo <joe@foo.com>
835
timestamp: Tue 2005-11-22 00:00:02 +0000
842
------------------------------------------------------------
844
committer: Joe Foo <joe@foo.com>
846
timestamp: Tue 2005-11-22 00:00:00 +0000
853
wt.branch, log.LongLogFormatter,
854
formatter_kwargs=dict(levels=1),
855
show_log_kwargs=dict(verbose=True))
857
def test_long_trailing_newlines(self):
858
wt = self.make_branch_and_tree('.')
859
b = self.make_commits_with_trailing_newlines(wt)
860
self.assertFormatterResult(b"""\
861
------------------------------------------------------------
863
committer: Joe Foo <joe@foo.com>
865
timestamp: Tue 2005-11-22 00:00:02 +0000
867
single line with trailing newline
868
------------------------------------------------------------
870
committer: Joe Foo <joe@foo.com>
872
timestamp: Tue 2005-11-22 00:00:01 +0000
877
------------------------------------------------------------
879
committer: Joe Foo <joe@foo.com>
881
timestamp: Tue 2005-11-22 00:00:00 +0000
885
b, log.LongLogFormatter,
886
formatter_kwargs=dict(levels=1))
888
def test_long_author_in_log(self):
889
"""Log includes the author name if it's set in
890
the revision properties
892
wt = self.make_standard_commit('test_author_log')
893
self.assertFormatterResult(b"""\
894
------------------------------------------------------------
896
author: John Doe <jdoe@example.com>
897
committer: Lorem Ipsum <test@example.com>
898
branch nick: test_author_log
899
timestamp: Tue 2005-11-22 00:00:00 +0000
903
wt.branch, log.LongLogFormatter,
904
formatter_kwargs=dict(levels=1))
906
def test_long_properties_in_log(self):
907
"""Log includes the custom properties returned by the registered
910
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(b"""\
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(b"""\
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(b"""\
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(b"""\
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(b"""\
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(b"""\
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(b"""\
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(b"""\
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(b'''\
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(b'''\
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(b'''\
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=b'bar-id')
1060
s = self.make_utf8_encoded_stringio()
1061
log.show_changed_revisions(tree.branch, [], [b'bar-id'], s)
1062
self.assertContainsRe(s.getvalue(), b'bar')
1063
self.assertNotContainsRe(s.getvalue(), b'foo')
1066
class TestLogFormatter(tests.TestCase):
1069
super(TestLogFormatter, self).setUp()
1070
self.rev = revision.Revision(b'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))
1129
def test_mainline_revisions(self):
1130
self.assertReversed([('1', 0), ('2', 0)],
1131
[('2', 0), ('1', 0)])
1133
def test_merged_revisions(self):
1134
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1), ],
1135
[('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=b'1a')
1167
tree.commit('2a', rev_id=b'2a')
1168
tree.commit('3a', rev_id=b'3a')
1171
def setup_ab_tree(self):
1172
tree = self.setup_a_tree()
1173
tree.set_last_revision(b'1a')
1174
tree.branch.set_last_revision_info(1, b'1a')
1175
tree.commit('2b', rev_id=b'2b')
1176
tree.commit('3b', rev_id=b'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=b'1c')
1184
tree.commit('2c', rev_id=b'2c')
1185
tree.commit('3c', rev_id=b'3c')
1188
def test_all_new(self):
1189
tree = self.setup_ab_tree()
1190
old, new = log.get_history_change(b'1a', b'3a', tree.branch.repository)
1191
self.assertEqual([], old)
1192
self.assertEqual([b'2a', b'3a'], new)
1194
def test_all_old(self):
1195
tree = self.setup_ab_tree()
1196
old, new = log.get_history_change(b'3a', b'1a', tree.branch.repository)
1197
self.assertEqual([], new)
1198
self.assertEqual([b'2a', b'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
b'3a', tree.branch.repository)
1204
self.assertEqual([], old)
1205
self.assertEqual([b'1a', b'2a', b'3a'], new)
1207
def test_null_new(self):
1208
tree = self.setup_ab_tree()
1209
old, new = log.get_history_change(b'3a', revision.NULL_REVISION,
1210
tree.branch.repository)
1211
self.assertEqual([], new)
1212
self.assertEqual([b'1a', b'2a', b'3a'], old)
1214
def test_diverged(self):
1215
tree = self.setup_ab_tree()
1216
old, new = log.get_history_change(b'3a', b'3b', tree.branch.repository)
1217
self.assertEqual(old, [b'2a', b'3a'])
1218
self.assertEqual(new, [b'2b', b'3b'])
1220
def test_unrelated(self):
1221
tree = self.setup_ac_tree()
1222
old, new = log.get_history_change(b'3a', b'3c', tree.branch.repository)
1223
self.assertEqual(old, [b'1a', b'2a', b'3a'])
1224
self.assertEqual(new, [b'1c', b'2c', b'3c'])
1226
def test_show_branch_change(self):
1227
tree = self.setup_ab_tree()
1229
log.show_branch_change(tree.branch, s, 3, b'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, b'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, b'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, b'2b')
1252
log.show_branch_change(tree.branch, s, 3, b'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=b'1a', **kwargs)
1269
tree.commit('commit 2a', rev_id=b'2a', **kwargs)
1270
tree.commit('commit 3a', rev_id=b'3a', **kwargs)
1273
def setup_ab_tree(self):
1274
tree = self.setup_a_tree()
1275
tree.set_last_revision(b'1a')
1276
tree.branch.set_last_revision_info(1, b'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=b'2b', **kwargs)
1283
tree.commit('commit 3b', rev_id=b'3b', **kwargs)
1286
def test_one_revision(self):
1287
tree = self.setup_ab_tree()
1289
rev = revisionspec.RevisionInfo(tree.branch, None, b'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(b'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, b'1a')
1300
end_rev = revisionspec.RevisionInfo(tree.branch, None, b'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(b'3a', lf.revisions[0].rev.revision_id)
1306
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1307
self.assertEqual(b'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, b'1a')
1313
end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
1314
self.assertFormatterResult(b"""\
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, b'1a')
1344
end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
1345
self.assertFormatterResult(b"""\
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, b'1a')
1365
end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
1366
self.assertFormatterResult(b"""\
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=b'a1',
1390
revprops={u'bugs': 'test://bug/id fixed'})
1392
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id=b'a2',
1393
authors=['Joe Bar <joe@bar.com>'],
1394
revprops={u'bugs': 'test://bug/id fixed\n'
1395
'test://bug/2 fixed'})
1398
def test_long_bugs(self):
1399
tree = self.make_commits_with_bugs()
1400
self.assertFormatterResult(b"""\
1401
------------------------------------------------------------
1403
fixes bugs: test://bug/id test://bug/2
1404
author: Joe Bar <joe@bar.com>
1405
committer: Joe Foo <joe@foo.com>
1407
timestamp: Tue 2005-11-22 00:00:01 +0000
1412
------------------------------------------------------------
1414
fixes bug: test://bug/id
1415
committer: Joe Foo <joe@foo.com>
1417
timestamp: Tue 2005-11-22 00:00:00 +0000
1421
tree.branch, log.LongLogFormatter)
1423
def test_short_bugs(self):
1424
tree = self.make_commits_with_bugs()
1425
self.assertFormatterResult(b"""\
1426
2 Joe Bar\t2005-11-22
1427
fixes bugs: test://bug/id test://bug/2
1432
1 Joe Foo\t2005-11-22
1433
fixes bug: test://bug/id
1437
tree.branch, log.ShortLogFormatter)
1439
def test_wrong_bugs_property(self):
1440
tree = self.make_branch_and_tree(u'.')
1441
self.build_tree(['foo'])
1442
self.wt_commit(tree, 'simple log message', rev_id=b'a1',
1443
revprops={u'bugs': 'test://bug/id invalid_value'})
1444
self.assertFormatterResult(b"""\
1445
1 Joe Foo\t2005-11-22
1449
tree.branch, log.ShortLogFormatter)
1451
def test_bugs_handler_present(self):
1452
self.properties_handler_registry.get('bugs_properties_handler')
1455
class TestLogForAuthors(TestCaseForLogFormatter):
1458
super(TestLogForAuthors, self).setUp()
1459
self.wt = self.make_standard_commit('nicky',
1460
authors=['John Doe <jdoe@example.com>',
1461
'Jane Rey <jrey@example.com>'])
1463
def assertFormatterResult(self, formatter, who, result):
1464
formatter_kwargs = dict()
1466
author_list_handler = log.author_list_registry.get(who)
1467
formatter_kwargs['author_list_handler'] = author_list_handler
1468
TestCaseForLogFormatter.assertFormatterResult(self, result,
1469
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1471
def test_line_default(self):
1472
self.assertFormatterResult(log.LineLogFormatter, None, b"""\
1473
1: John Doe 2005-11-22 add a
1476
def test_line_committer(self):
1477
self.assertFormatterResult(log.LineLogFormatter, 'committer', b"""\
1478
1: Lorem Ipsum 2005-11-22 add a
1481
def test_line_first(self):
1482
self.assertFormatterResult(log.LineLogFormatter, 'first', b"""\
1483
1: John Doe 2005-11-22 add a
1486
def test_line_all(self):
1487
self.assertFormatterResult(log.LineLogFormatter, 'all', b"""\
1488
1: John Doe, Jane Rey 2005-11-22 add a
1491
def test_short_default(self):
1492
self.assertFormatterResult(log.ShortLogFormatter, None, b"""\
1493
1 John Doe\t2005-11-22
1498
def test_short_committer(self):
1499
self.assertFormatterResult(log.ShortLogFormatter, 'committer', b"""\
1500
1 Lorem Ipsum\t2005-11-22
1505
def test_short_first(self):
1506
self.assertFormatterResult(log.ShortLogFormatter, 'first', b"""\
1507
1 John Doe\t2005-11-22
1512
def test_short_all(self):
1513
self.assertFormatterResult(log.ShortLogFormatter, 'all', b"""\
1514
1 John Doe, Jane Rey\t2005-11-22
1519
def test_long_default(self):
1520
self.assertFormatterResult(log.LongLogFormatter, None, b"""\
1521
------------------------------------------------------------
1523
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1524
committer: Lorem Ipsum <test@example.com>
1526
timestamp: Tue 2005-11-22 00:00:00 +0000
1531
def test_long_committer(self):
1532
self.assertFormatterResult(log.LongLogFormatter, 'committer', b"""\
1533
------------------------------------------------------------
1535
committer: Lorem Ipsum <test@example.com>
1537
timestamp: Tue 2005-11-22 00:00:00 +0000
1542
def test_long_first(self):
1543
self.assertFormatterResult(log.LongLogFormatter, 'first', b"""\
1544
------------------------------------------------------------
1546
author: John Doe <jdoe@example.com>
1547
committer: Lorem Ipsum <test@example.com>
1549
timestamp: Tue 2005-11-22 00:00:00 +0000
1554
def test_long_all(self):
1555
self.assertFormatterResult(log.LongLogFormatter, 'all', b"""\
1556
------------------------------------------------------------
1558
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1559
committer: Lorem Ipsum <test@example.com>
1561
timestamp: Tue 2005-11-22 00:00:00 +0000
1566
def test_gnu_changelog_default(self):
1567
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, b"""\
1568
2005-11-22 John Doe <jdoe@example.com>
1574
def test_gnu_changelog_committer(self):
1575
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', b"""\
1576
2005-11-22 Lorem Ipsum <test@example.com>
1582
def test_gnu_changelog_first(self):
1583
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', b"""\
1584
2005-11-22 John Doe <jdoe@example.com>
1590
def test_gnu_changelog_all(self):
1591
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', b"""\
1592
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1599
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1601
def make_branch_with_alternate_ancestries(self, relpath='.'):
1602
# See test_merge_sorted_exclude_ancestry below for the difference with
1603
# bt.per_branch.test_iter_merge_sorted_revision.
1604
# TestIterMergeSortedRevisionsBushyGraph.
1605
# make_branch_with_alternate_ancestries
1606
# and test_merge_sorted_exclude_ancestry
1607
# See the FIXME in assertLogRevnos too.
1608
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1620
builder.start_series()
1621
builder.build_snapshot(None, [
1622
('add', ('', b'TREE_ROOT', 'directory', '')), ],
1624
builder.build_snapshot([b'1'], [], revision_id=b'1.1.1')
1625
builder.build_snapshot([b'1'], [], revision_id=b'2')
1626
builder.build_snapshot([b'1.1.1'], [], revision_id=b'1.2.1')
1627
builder.build_snapshot([b'1.1.1', b'1.2.1'], [], revision_id=b'1.1.2')
1628
builder.build_snapshot([b'2', b'1.1.2'], [], revision_id=b'3')
1629
builder.finish_series()
1630
br = builder.get_branch()
1632
self.addCleanup(br.unlock)
1635
def assertLogRevnos(self, expected_revnos, b, start, end,
1636
exclude_common_ancestry, generate_merge_revisions=True):
1637
# FIXME: the layering in log makes it hard to test intermediate levels,
1638
# I wish adding filters with their parameters was easier...
1640
iter_revs = log._calc_view_revisions(
1641
b, start, end, direction='reverse',
1642
generate_merge_revisions=generate_merge_revisions,
1643
exclude_common_ancestry=exclude_common_ancestry)
1644
self.assertEqual(expected_revnos,
1645
[revid for revid, revno, depth in iter_revs])
1647
def test_merge_sorted_exclude_ancestry(self):
1648
b = self.make_branch_with_alternate_ancestries()
1649
self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'1.1.1', b'2', b'1'],
1650
b, b'1', b'3', exclude_common_ancestry=False)
1651
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1652
# it should be mentioned even if merge_sort order will make it appear
1654
self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'2'],
1655
b, b'1.1.1', b'3', exclude_common_ancestry=True)
1657
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1658
b = self.make_branch_with_alternate_ancestries()
1659
self.assertLogRevnos([b'3', b'2'],
1660
b, b'1', b'3', exclude_common_ancestry=True,
1661
generate_merge_revisions=False)
1662
self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'1.1.1', b'2'],
1663
b, b'1', b'3', exclude_common_ancestry=True,
1664
generate_merge_revisions=True)
1667
class TestLogDefaults(TestCaseForLogFormatter):
1668
def test_default_log_level(self):
1670
Test to ensure that specifying 'levels=1' to make_log_request_dict
1671
doesn't get overwritten when using a LogFormatter that supports more
1675
wt = self._prepare_tree_with_merges()
1678
class CustomLogFormatter(log.LogFormatter):
1679
def __init__(self, *args, **kwargs):
1680
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1683
def get_levels(self):
1684
# 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)