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', b'hello moto\n')])
95
self.wt_commit(wt, 'simple log message', rev_id=b'a1')
96
self.build_tree_contents([('b', b'goodbye\n')])
98
self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id=b'a2')
100
self.build_tree_contents([('c', b'just another manic monday\n')])
102
self.wt_commit(wt, 'single line with trailing newline\n', rev_id=b'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=b'rev-1')
111
self.wt_commit(wt, 'rev-merged', rev_id=b'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=b'rev-2b')
117
branch.tags.set_tag('v0.2', 'rev-2b')
118
self.wt_commit(wt, 'rev-3', rev_id=b'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, 1)
181
self.assertInvalidRevisonNumber(b, 1, -1)
182
self.assertInvalidRevisonNumber(b, 1, 0)
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', b'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):
321
def verify_revision_signature(self, revid, gpg_strategy):
322
return (gpg.SIGNATURE_VALID,
323
u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
325
def test_format_signature_validity_utf(self):
326
"""Check that GPG signatures containing UTF-8 names are formatted
328
wt = self.make_branch_and_tree('.')
329
revid = wt.commit('empty commit')
330
repo = wt.branch.repository
331
# Monkey patch out checking if this rev is actually signed, since we
332
# can't sign it without a heavier TestCase and LoopbackGPGStrategy
333
# doesn't care anyways.
334
self.overrideAttr(repo, 'verify_revision_signature',
335
self.verify_revision_signature)
336
out = log.format_signature_validity(revid, wt.branch)
338
u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
342
class TestShortLogFormatter(TestCaseForLogFormatter):
344
def test_trailing_newlines(self):
345
wt = self.make_branch_and_tree('.')
346
b = self.make_commits_with_trailing_newlines(wt)
347
self.assertFormatterResult("""\
348
3 Joe Foo\t2005-11-22
349
single line with trailing newline
351
2 Joe Foo\t2005-11-22
356
1 Joe Foo\t2005-11-22
360
b, log.ShortLogFormatter)
362
def test_short_log_with_merges(self):
363
wt = self._prepare_tree_with_merges()
364
self.assertFormatterResult("""\
365
2 Joe Foo\t2005-11-22 [merge]
368
1 Joe Foo\t2005-11-22
372
wt.branch, log.ShortLogFormatter)
374
def test_short_log_with_merges_and_advice(self):
375
wt = self._prepare_tree_with_merges()
376
self.assertFormatterResult("""\
377
2 Joe Foo\t2005-11-22 [merge]
380
1 Joe Foo\t2005-11-22
383
Use --include-merged or -n0 to see merged revisions.
385
wt.branch, log.ShortLogFormatter,
386
formatter_kwargs=dict(show_advice=True))
388
def test_short_log_with_merges_and_range(self):
389
wt = self._prepare_tree_with_merges()
390
self.wt_commit(wt, 'rev-3a', rev_id=b'rev-3a')
391
wt.branch.set_last_revision_info(2, 'rev-2b')
392
wt.set_parent_ids(['rev-2b', 'rev-3a'])
393
self.wt_commit(wt, 'rev-3b', rev_id=b'rev-3b')
394
self.assertFormatterResult("""\
395
3 Joe Foo\t2005-11-22 [merge]
398
2 Joe Foo\t2005-11-22 [merge]
402
wt.branch, log.ShortLogFormatter,
403
show_log_kwargs=dict(start_revision=2, end_revision=3))
405
def test_short_log_with_tags(self):
406
wt = self._prepare_tree_with_merges(with_tags=True)
407
self.assertFormatterResult("""\
408
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
411
2 Joe Foo\t2005-11-22 {v0.2} [merge]
414
1 Joe Foo\t2005-11-22
418
wt.branch, log.ShortLogFormatter)
420
def test_short_log_single_merge_revision(self):
421
wt = self._prepare_tree_with_merges()
422
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
423
rev = revspec.in_history(wt.branch)
424
self.assertFormatterResult("""\
425
1.1.1 Joe Foo\t2005-11-22
429
wt.branch, log.ShortLogFormatter,
430
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
432
def test_show_ids(self):
433
wt = self.make_branch_and_tree('parent')
434
self.build_tree(['parent/f1', 'parent/f2'])
436
self.wt_commit(wt, 'first post', rev_id=b'a')
437
child_wt = wt.controldir.sprout('child').open_workingtree()
438
self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
439
wt.merge_from_branch(child_wt.branch)
440
self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
441
self.assertFormatterResult("""\
442
2 Joe Foo\t2005-11-22 [merge]
446
1.1.1 Joe Foo\t2005-11-22
450
1 Joe Foo\t2005-11-22
455
wt.branch, log.ShortLogFormatter,
456
formatter_kwargs=dict(levels=0, show_ids=True))
459
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
461
def test_short_merge_revs_log_with_merges(self):
462
wt = self._prepare_tree_with_merges()
463
# Note that the 1.1.1 indenting is in fact correct given that
464
# the revision numbers are right justified within 5 characters
465
# for mainline revnos and 9 characters for dotted revnos.
466
self.assertFormatterResult("""\
467
2 Joe Foo\t2005-11-22 [merge]
470
1.1.1 Joe Foo\t2005-11-22
473
1 Joe Foo\t2005-11-22
477
wt.branch, log.ShortLogFormatter,
478
formatter_kwargs=dict(levels=0))
480
def test_short_merge_revs_log_single_merge_revision(self):
481
wt = self._prepare_tree_with_merges()
482
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
483
rev = revspec.in_history(wt.branch)
484
self.assertFormatterResult("""\
485
1.1.1 Joe Foo\t2005-11-22
489
wt.branch, log.ShortLogFormatter,
490
formatter_kwargs=dict(levels=0),
491
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
494
class TestLongLogFormatter(TestCaseForLogFormatter):
496
def test_verbose_log(self):
497
"""Verbose log includes changed files
501
wt = self.make_standard_commit('test_verbose_log', authors=[])
502
self.assertFormatterResult('''\
503
------------------------------------------------------------
505
committer: Lorem Ipsum <test@example.com>
506
branch nick: test_verbose_log
507
timestamp: Tue 2005-11-22 00:00:00 +0000
513
wt.branch, log.LongLogFormatter,
514
show_log_kwargs=dict(verbose=True))
516
def test_merges_are_indented_by_level(self):
517
wt = self.make_branch_and_tree('parent')
518
self.wt_commit(wt, 'first post')
519
child_wt = wt.controldir.sprout('child').open_workingtree()
520
self.wt_commit(child_wt, 'branch 1')
521
smallerchild_wt = wt.controldir.sprout('smallerchild').open_workingtree()
522
self.wt_commit(smallerchild_wt, 'branch 2')
523
child_wt.merge_from_branch(smallerchild_wt.branch)
524
self.wt_commit(child_wt, 'merge branch 2')
525
wt.merge_from_branch(child_wt.branch)
526
self.wt_commit(wt, 'merge branch 1')
527
self.assertFormatterResult("""\
528
------------------------------------------------------------
530
committer: Joe Foo <joe@foo.com>
532
timestamp: Tue 2005-11-22 00:00:04 +0000
535
------------------------------------------------------------
537
committer: Joe Foo <joe@foo.com>
539
timestamp: Tue 2005-11-22 00:00:03 +0000
542
------------------------------------------------------------
544
committer: Joe Foo <joe@foo.com>
545
branch nick: smallerchild
546
timestamp: Tue 2005-11-22 00:00:02 +0000
549
------------------------------------------------------------
551
committer: Joe Foo <joe@foo.com>
553
timestamp: Tue 2005-11-22 00:00:01 +0000
556
------------------------------------------------------------
558
committer: Joe Foo <joe@foo.com>
560
timestamp: Tue 2005-11-22 00:00:00 +0000
564
wt.branch, log.LongLogFormatter,
565
formatter_kwargs=dict(levels=0),
566
show_log_kwargs=dict(verbose=True))
568
def test_verbose_merge_revisions_contain_deltas(self):
569
wt = self.make_branch_and_tree('parent')
570
self.build_tree(['parent/f1', 'parent/f2'])
572
self.wt_commit(wt, 'first post')
573
child_wt = wt.controldir.sprout('child').open_workingtree()
574
os.unlink('child/f1')
575
self.build_tree_contents([('child/f2', b'hello\n')])
576
self.wt_commit(child_wt, 'removed f1 and modified f2')
577
wt.merge_from_branch(child_wt.branch)
578
self.wt_commit(wt, 'merge branch 1')
579
self.assertFormatterResult("""\
580
------------------------------------------------------------
582
committer: Joe Foo <joe@foo.com>
584
timestamp: Tue 2005-11-22 00:00:02 +0000
591
------------------------------------------------------------
593
committer: Joe Foo <joe@foo.com>
595
timestamp: Tue 2005-11-22 00:00:01 +0000
597
removed f1 and modified f2
602
------------------------------------------------------------
604
committer: Joe Foo <joe@foo.com>
606
timestamp: Tue 2005-11-22 00:00:00 +0000
613
wt.branch, log.LongLogFormatter,
614
formatter_kwargs=dict(levels=0),
615
show_log_kwargs=dict(verbose=True))
617
def test_trailing_newlines(self):
618
wt = self.make_branch_and_tree('.')
619
b = self.make_commits_with_trailing_newlines(wt)
620
self.assertFormatterResult("""\
621
------------------------------------------------------------
623
committer: Joe Foo <joe@foo.com>
625
timestamp: Tue 2005-11-22 00:00:02 +0000
627
single line with trailing newline
628
------------------------------------------------------------
630
committer: Joe Foo <joe@foo.com>
632
timestamp: Tue 2005-11-22 00:00:01 +0000
637
------------------------------------------------------------
639
committer: Joe Foo <joe@foo.com>
641
timestamp: Tue 2005-11-22 00:00:00 +0000
645
b, log.LongLogFormatter)
647
def test_author_in_log(self):
648
"""Log includes the author name if it's set in
649
the revision properties
651
wt = self.make_standard_commit('test_author_log',
652
authors=['John Doe <jdoe@example.com>',
653
'Jane Rey <jrey@example.com>'])
654
self.assertFormatterResult("""\
655
------------------------------------------------------------
657
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
658
committer: Lorem Ipsum <test@example.com>
659
branch nick: test_author_log
660
timestamp: Tue 2005-11-22 00:00:00 +0000
664
wt.branch, log.LongLogFormatter)
666
def test_properties_in_log(self):
667
"""Log includes the custom properties returned by the registered
670
wt = self.make_standard_commit('test_properties_in_log')
671
def trivial_custom_prop_handler(revision):
672
return {'test_prop':'test_value'}
674
# Cleaned up in setUp()
675
log.properties_handler_registry.register(
676
'trivial_custom_prop_handler',
677
trivial_custom_prop_handler)
678
self.assertFormatterResult("""\
679
------------------------------------------------------------
681
test_prop: test_value
682
author: John Doe <jdoe@example.com>
683
committer: Lorem Ipsum <test@example.com>
684
branch nick: test_properties_in_log
685
timestamp: Tue 2005-11-22 00:00:00 +0000
689
wt.branch, log.LongLogFormatter)
691
def test_properties_in_short_log(self):
692
"""Log includes the custom properties returned by the registered
695
wt = self.make_standard_commit('test_properties_in_short_log')
696
def trivial_custom_prop_handler(revision):
697
return {'test_prop':'test_value'}
699
log.properties_handler_registry.register(
700
'trivial_custom_prop_handler',
701
trivial_custom_prop_handler)
702
self.assertFormatterResult("""\
703
1 John Doe\t2005-11-22
704
test_prop: test_value
708
wt.branch, log.ShortLogFormatter)
710
def test_error_in_properties_handler(self):
711
"""Log includes the custom properties returned by the registered
714
wt = self.make_standard_commit('error_in_properties_handler',
715
revprops={'first_prop':'first_value'})
716
sio = self.make_utf8_encoded_stringio()
717
formatter = log.LongLogFormatter(to_file=sio)
718
def trivial_custom_prop_handler(revision):
719
raise Exception("a test error")
721
log.properties_handler_registry.register(
722
'trivial_custom_prop_handler',
723
trivial_custom_prop_handler)
724
self.assertRaises(Exception, log.show_log, wt.branch, formatter,)
726
def test_properties_handler_bad_argument(self):
727
wt = self.make_standard_commit('bad_argument',
728
revprops={'a_prop':'test_value'})
729
sio = self.make_utf8_encoded_stringio()
730
formatter = log.LongLogFormatter(to_file=sio)
731
def bad_argument_prop_handler(revision):
732
return {'custom_prop_name':revision.properties['a_prop']}
734
log.properties_handler_registry.register(
735
'bad_argument_prop_handler',
736
bad_argument_prop_handler)
738
self.assertRaises(AttributeError, formatter.show_properties,
741
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
742
formatter.show_properties(revision, '')
743
self.assertEqualDiff('''custom_prop_name: test_value\n''',
746
def test_show_ids(self):
747
wt = self.make_branch_and_tree('parent')
748
self.build_tree(['parent/f1', 'parent/f2'])
750
self.wt_commit(wt, 'first post', rev_id=b'a')
751
child_wt = wt.controldir.sprout('child').open_workingtree()
752
self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
753
wt.merge_from_branch(child_wt.branch)
754
self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
755
self.assertFormatterResult("""\
756
------------------------------------------------------------
761
committer: Joe Foo <joe@foo.com>
763
timestamp: Tue 2005-11-22 00:00:02 +0000
766
------------------------------------------------------------
770
committer: Joe Foo <joe@foo.com>
772
timestamp: Tue 2005-11-22 00:00:01 +0000
775
------------------------------------------------------------
778
committer: Joe Foo <joe@foo.com>
780
timestamp: Tue 2005-11-22 00:00:00 +0000
784
wt.branch, log.LongLogFormatter,
785
formatter_kwargs=dict(levels=0, show_ids=True))
788
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
790
def test_long_verbose_log(self):
791
"""Verbose log includes changed files
795
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
796
self.assertFormatterResult("""\
797
------------------------------------------------------------
799
committer: Lorem Ipsum <test@example.com>
800
branch nick: test_long_verbose_log
801
timestamp: Tue 2005-11-22 00:00:00 +0000
807
wt.branch, log.LongLogFormatter,
808
formatter_kwargs=dict(levels=1),
809
show_log_kwargs=dict(verbose=True))
811
def test_long_verbose_contain_deltas(self):
812
wt = self.make_branch_and_tree('parent')
813
self.build_tree(['parent/f1', 'parent/f2'])
815
self.wt_commit(wt, 'first post')
816
child_wt = wt.controldir.sprout('child').open_workingtree()
817
os.unlink('child/f1')
818
self.build_tree_contents([('child/f2', b'hello\n')])
819
self.wt_commit(child_wt, 'removed f1 and modified f2')
820
wt.merge_from_branch(child_wt.branch)
821
self.wt_commit(wt, 'merge branch 1')
822
self.assertFormatterResult("""\
823
------------------------------------------------------------
825
committer: Joe Foo <joe@foo.com>
827
timestamp: Tue 2005-11-22 00:00:02 +0000
834
------------------------------------------------------------
836
committer: Joe Foo <joe@foo.com>
838
timestamp: Tue 2005-11-22 00:00:00 +0000
845
wt.branch, log.LongLogFormatter,
846
formatter_kwargs=dict(levels=1),
847
show_log_kwargs=dict(verbose=True))
849
def test_long_trailing_newlines(self):
850
wt = self.make_branch_and_tree('.')
851
b = self.make_commits_with_trailing_newlines(wt)
852
self.assertFormatterResult("""\
853
------------------------------------------------------------
855
committer: Joe Foo <joe@foo.com>
857
timestamp: Tue 2005-11-22 00:00:02 +0000
859
single line with trailing newline
860
------------------------------------------------------------
862
committer: Joe Foo <joe@foo.com>
864
timestamp: Tue 2005-11-22 00:00:01 +0000
869
------------------------------------------------------------
871
committer: Joe Foo <joe@foo.com>
873
timestamp: Tue 2005-11-22 00:00:00 +0000
877
b, log.LongLogFormatter,
878
formatter_kwargs=dict(levels=1))
880
def test_long_author_in_log(self):
881
"""Log includes the author name if it's set in
882
the revision properties
884
wt = self.make_standard_commit('test_author_log')
885
self.assertFormatterResult("""\
886
------------------------------------------------------------
888
author: John Doe <jdoe@example.com>
889
committer: Lorem Ipsum <test@example.com>
890
branch nick: test_author_log
891
timestamp: Tue 2005-11-22 00:00:00 +0000
895
wt.branch, log.LongLogFormatter,
896
formatter_kwargs=dict(levels=1))
898
def test_long_properties_in_log(self):
899
"""Log includes the custom properties returned by the registered
902
wt = self.make_standard_commit('test_properties_in_log')
903
def trivial_custom_prop_handler(revision):
904
return {'test_prop':'test_value'}
906
log.properties_handler_registry.register(
907
'trivial_custom_prop_handler',
908
trivial_custom_prop_handler)
909
self.assertFormatterResult("""\
910
------------------------------------------------------------
912
test_prop: test_value
913
author: John Doe <jdoe@example.com>
914
committer: Lorem Ipsum <test@example.com>
915
branch nick: test_properties_in_log
916
timestamp: Tue 2005-11-22 00:00:00 +0000
920
wt.branch, log.LongLogFormatter,
921
formatter_kwargs=dict(levels=1))
924
class TestLineLogFormatter(TestCaseForLogFormatter):
926
def test_line_log(self):
927
"""Line log should show revno
931
wt = self.make_standard_commit('test-line-log',
932
committer='Line-Log-Formatter Tester <test@line.log>',
934
self.assertFormatterResult("""\
935
1: Line-Log-Formatte... 2005-11-22 add a
937
wt.branch, log.LineLogFormatter)
939
def test_trailing_newlines(self):
940
wt = self.make_branch_and_tree('.')
941
b = self.make_commits_with_trailing_newlines(wt)
942
self.assertFormatterResult("""\
943
3: Joe Foo 2005-11-22 single line with trailing newline
944
2: Joe Foo 2005-11-22 multiline
945
1: Joe Foo 2005-11-22 simple log message
947
b, log.LineLogFormatter)
949
def test_line_log_single_merge_revision(self):
950
wt = self._prepare_tree_with_merges()
951
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
952
rev = revspec.in_history(wt.branch)
953
self.assertFormatterResult("""\
954
1.1.1: Joe Foo 2005-11-22 rev-merged
956
wt.branch, log.LineLogFormatter,
957
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
959
def test_line_log_with_tags(self):
960
wt = self._prepare_tree_with_merges(with_tags=True)
961
self.assertFormatterResult("""\
962
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
963
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
964
1: Joe Foo 2005-11-22 rev-1
966
wt.branch, log.LineLogFormatter)
969
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
971
def test_line_merge_revs_log(self):
972
"""Line log should show revno
976
wt = self.make_standard_commit('test-line-log',
977
committer='Line-Log-Formatter Tester <test@line.log>',
979
self.assertFormatterResult("""\
980
1: Line-Log-Formatte... 2005-11-22 add a
982
wt.branch, log.LineLogFormatter)
984
def test_line_merge_revs_log_single_merge_revision(self):
985
wt = self._prepare_tree_with_merges()
986
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
987
rev = revspec.in_history(wt.branch)
988
self.assertFormatterResult("""\
989
1.1.1: Joe Foo 2005-11-22 rev-merged
991
wt.branch, log.LineLogFormatter,
992
formatter_kwargs=dict(levels=0),
993
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
995
def test_line_merge_revs_log_with_merges(self):
996
wt = self._prepare_tree_with_merges()
997
self.assertFormatterResult("""\
998
2: Joe Foo 2005-11-22 [merge] rev-2
999
1.1.1: Joe Foo 2005-11-22 rev-merged
1000
1: Joe Foo 2005-11-22 rev-1
1002
wt.branch, log.LineLogFormatter,
1003
formatter_kwargs=dict(levels=0))
1006
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
1008
def test_gnu_changelog(self):
1009
wt = self.make_standard_commit('nicky', authors=[])
1010
self.assertFormatterResult('''\
1011
2005-11-22 Lorem Ipsum <test@example.com>
1016
wt.branch, log.GnuChangelogLogFormatter)
1018
def test_with_authors(self):
1019
wt = self.make_standard_commit('nicky',
1020
authors=['Fooa Fooz <foo@example.com>',
1021
'Bari Baro <bar@example.com>'])
1022
self.assertFormatterResult('''\
1023
2005-11-22 Fooa Fooz <foo@example.com>
1028
wt.branch, log.GnuChangelogLogFormatter)
1030
def test_verbose(self):
1031
wt = self.make_standard_commit('nicky')
1032
self.assertFormatterResult('''\
1033
2005-11-22 John Doe <jdoe@example.com>
1040
wt.branch, log.GnuChangelogLogFormatter,
1041
show_log_kwargs=dict(verbose=True))
1044
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1046
def test_show_changed_revisions_verbose(self):
1047
tree = self.make_branch_and_tree('tree_a')
1048
self.build_tree(['tree_a/foo'])
1050
tree.commit('bar', rev_id=b'bar-id')
1051
s = self.make_utf8_encoded_stringio()
1052
log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
1053
self.assertContainsRe(s.getvalue(), 'bar')
1054
self.assertNotContainsRe(s.getvalue(), 'foo')
1057
class TestLogFormatter(tests.TestCase):
1060
super(TestLogFormatter, self).setUp()
1061
self.rev = revision.Revision('a-id')
1062
self.lf = log.LogFormatter(None)
1064
def test_short_committer(self):
1065
def assertCommitter(expected, committer):
1066
self.rev.committer = committer
1067
self.assertEqual(expected, self.lf.short_committer(self.rev))
1069
assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
1070
assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
1071
assertCommitter('John Smith', 'John Smith')
1072
assertCommitter('jsmith@example.com', 'jsmith@example.com')
1073
assertCommitter('jsmith@example.com', '<jsmith@example.com>')
1074
assertCommitter('John Smith', 'John Smith jsmith@example.com')
1076
def test_short_author(self):
1077
def assertAuthor(expected, author):
1078
self.rev.properties['author'] = author
1079
self.assertEqual(expected, self.lf.short_author(self.rev))
1081
assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
1082
assertAuthor('John Smith', 'John Smith')
1083
assertAuthor('jsmith@example.com', 'jsmith@example.com')
1084
assertAuthor('jsmith@example.com', '<jsmith@example.com>')
1085
assertAuthor('John Smith', 'John Smith jsmith@example.com')
1087
def test_short_author_from_committer(self):
1088
self.rev.committer = 'John Doe <jdoe@example.com>'
1089
self.assertEqual('John Doe', self.lf.short_author(self.rev))
1091
def test_short_author_from_authors(self):
1092
self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1093
'Jane Rey <jrey@example.com>')
1094
self.assertEqual('John Smith', self.lf.short_author(self.rev))
1097
class TestReverseByDepth(tests.TestCase):
1098
"""Test reverse_by_depth behavior.
1100
This is used to present revisions in forward (oldest first) order in a nice
1103
The tests use lighter revision description to ease reading.
1106
def assertReversed(self, forward, backward):
1107
# Transform the descriptions to suit the API: tests use (revno, depth),
1108
# while the API expects (revid, revno, depth)
1109
def complete_revisions(l):
1110
"""Transform the description to suit the API.
1112
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1113
Since the revid is arbitrary, we just duplicate revno
1115
return [ (r, r, d) for r, d in l]
1116
forward = complete_revisions(forward)
1117
backward= complete_revisions(backward)
1118
self.assertEqual(forward, log.reverse_by_depth(backward))
1121
def test_mainline_revisions(self):
1122
self.assertReversed([( '1', 0), ('2', 0)],
1123
[('2', 0), ('1', 0)])
1125
def test_merged_revisions(self):
1126
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1127
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1128
def test_shifted_merged_revisions(self):
1129
"""Test irregular layout.
1131
Requesting revisions touching a file can produce "holes" in the depths.
1133
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1134
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1136
def test_merged_without_child_revisions(self):
1137
"""Test irregular layout.
1139
Revision ranges can produce "holes" in the depths.
1141
# When a revision of higher depth doesn't follow one of lower depth, we
1142
# assume a lower depth one is virtually there
1143
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1144
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1145
# So we get the same order after reversing below even if the original
1146
# revisions are not in the same order.
1147
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1148
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1151
class TestHistoryChange(tests.TestCaseWithTransport):
1153
def setup_a_tree(self):
1154
tree = self.make_branch_and_tree('tree')
1156
self.addCleanup(tree.unlock)
1157
tree.commit('1a', rev_id=b'1a')
1158
tree.commit('2a', rev_id=b'2a')
1159
tree.commit('3a', rev_id=b'3a')
1162
def setup_ab_tree(self):
1163
tree = self.setup_a_tree()
1164
tree.set_last_revision('1a')
1165
tree.branch.set_last_revision_info(1, '1a')
1166
tree.commit('2b', rev_id=b'2b')
1167
tree.commit('3b', rev_id=b'3b')
1170
def setup_ac_tree(self):
1171
tree = self.setup_a_tree()
1172
tree.set_last_revision(revision.NULL_REVISION)
1173
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1174
tree.commit('1c', rev_id=b'1c')
1175
tree.commit('2c', rev_id=b'2c')
1176
tree.commit('3c', rev_id=b'3c')
1179
def test_all_new(self):
1180
tree = self.setup_ab_tree()
1181
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1182
self.assertEqual([], old)
1183
self.assertEqual(['2a', '3a'], new)
1185
def test_all_old(self):
1186
tree = self.setup_ab_tree()
1187
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1188
self.assertEqual([], new)
1189
self.assertEqual(['2a', '3a'], old)
1191
def test_null_old(self):
1192
tree = self.setup_ab_tree()
1193
old, new = log.get_history_change(revision.NULL_REVISION,
1194
'3a', tree.branch.repository)
1195
self.assertEqual([], old)
1196
self.assertEqual(['1a', '2a', '3a'], new)
1198
def test_null_new(self):
1199
tree = self.setup_ab_tree()
1200
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1201
tree.branch.repository)
1202
self.assertEqual([], new)
1203
self.assertEqual(['1a', '2a', '3a'], old)
1205
def test_diverged(self):
1206
tree = self.setup_ab_tree()
1207
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1208
self.assertEqual(old, ['2a', '3a'])
1209
self.assertEqual(new, ['2b', '3b'])
1211
def test_unrelated(self):
1212
tree = self.setup_ac_tree()
1213
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1214
self.assertEqual(old, ['1a', '2a', '3a'])
1215
self.assertEqual(new, ['1c', '2c', '3c'])
1217
def test_show_branch_change(self):
1218
tree = self.setup_ab_tree()
1220
log.show_branch_change(tree.branch, s, 3, '3a')
1221
self.assertContainsRe(s.getvalue(),
1222
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1223
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1225
def test_show_branch_change_no_change(self):
1226
tree = self.setup_ab_tree()
1228
log.show_branch_change(tree.branch, s, 3, '3b')
1229
self.assertEqual(s.getvalue(),
1230
'Nothing seems to have changed\n')
1232
def test_show_branch_change_no_old(self):
1233
tree = self.setup_ab_tree()
1235
log.show_branch_change(tree.branch, s, 2, '2b')
1236
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1237
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1239
def test_show_branch_change_no_new(self):
1240
tree = self.setup_ab_tree()
1241
tree.branch.set_last_revision_info(2, '2b')
1243
log.show_branch_change(tree.branch, s, 3, '3b')
1244
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1245
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1248
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1250
def setup_a_tree(self):
1251
tree = self.make_branch_and_tree('tree')
1253
self.addCleanup(tree.unlock)
1255
'committer': 'Joe Foo <joe@foo.com>',
1256
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1257
'timezone': 0, # UTC
1259
tree.commit('commit 1a', rev_id=b'1a', **kwargs)
1260
tree.commit('commit 2a', rev_id=b'2a', **kwargs)
1261
tree.commit('commit 3a', rev_id=b'3a', **kwargs)
1264
def setup_ab_tree(self):
1265
tree = self.setup_a_tree()
1266
tree.set_last_revision('1a')
1267
tree.branch.set_last_revision_info(1, '1a')
1269
'committer': 'Joe Foo <joe@foo.com>',
1270
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1271
'timezone': 0, # UTC
1273
tree.commit('commit 2b', rev_id=b'2b', **kwargs)
1274
tree.commit('commit 3b', rev_id=b'3b', **kwargs)
1277
def test_one_revision(self):
1278
tree = self.setup_ab_tree()
1280
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1281
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1283
self.assertEqual(1, len(lf.revisions))
1284
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1285
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1287
def test_many_revisions(self):
1288
tree = self.setup_ab_tree()
1290
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1291
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1292
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1293
end_revision=end_rev)
1294
self.assertEqual(3, len(lf.revisions))
1295
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1296
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1297
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1298
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1299
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1301
def test_long_format(self):
1302
tree = self.setup_ab_tree()
1303
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1304
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1305
self.assertFormatterResult("""\
1306
------------------------------------------------------------
1308
committer: Joe Foo <joe@foo.com>
1310
timestamp: Tue 2005-11-22 00:00:00 +0000
1313
------------------------------------------------------------
1315
committer: Joe Foo <joe@foo.com>
1317
timestamp: Tue 2005-11-22 00:00:00 +0000
1320
------------------------------------------------------------
1322
committer: Joe Foo <joe@foo.com>
1324
timestamp: Tue 2005-11-22 00:00:00 +0000
1328
tree.branch, log.LongLogFormatter, show_log_kwargs={
1329
'start_revision': start_rev, 'end_revision': end_rev
1332
def test_short_format(self):
1333
tree = self.setup_ab_tree()
1334
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1335
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1336
self.assertFormatterResult("""\
1345
1 Joe Foo\t2005-11-22
1349
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1350
'start_revision': start_rev, 'end_revision': end_rev
1353
def test_line_format(self):
1354
tree = self.setup_ab_tree()
1355
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1356
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1357
self.assertFormatterResult("""\
1358
Joe Foo 2005-11-22 commit 3a
1359
Joe Foo 2005-11-22 commit 2a
1360
1: Joe Foo 2005-11-22 commit 1a
1362
tree.branch, log.LineLogFormatter, show_log_kwargs={
1363
'start_revision': start_rev, 'end_revision': end_rev
1367
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1370
super(TestLogWithBugs, self).setUp()
1371
log.properties_handler_registry.register(
1372
'bugs_properties_handler',
1373
log._bugs_properties_handler)
1375
def make_commits_with_bugs(self):
1376
"""Helper method for LogFormatter tests"""
1377
tree = self.make_branch_and_tree(u'.')
1378
self.build_tree(['a', 'b'])
1380
self.wt_commit(tree, 'simple log message', rev_id=b'a1',
1381
revprops={'bugs': 'test://bug/id fixed'})
1383
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id=b'a2',
1384
authors=['Joe Bar <joe@bar.com>'],
1385
revprops={'bugs': 'test://bug/id fixed\n'
1386
'test://bug/2 fixed'})
1390
def test_long_bugs(self):
1391
tree = self.make_commits_with_bugs()
1392
self.assertFormatterResult("""\
1393
------------------------------------------------------------
1395
fixes bugs: test://bug/id test://bug/2
1396
author: Joe Bar <joe@bar.com>
1397
committer: Joe Foo <joe@foo.com>
1399
timestamp: Tue 2005-11-22 00:00:01 +0000
1404
------------------------------------------------------------
1406
fixes bug: test://bug/id
1407
committer: Joe Foo <joe@foo.com>
1409
timestamp: Tue 2005-11-22 00:00:00 +0000
1413
tree.branch, log.LongLogFormatter)
1415
def test_short_bugs(self):
1416
tree = self.make_commits_with_bugs()
1417
self.assertFormatterResult("""\
1418
2 Joe Bar\t2005-11-22
1419
fixes bugs: test://bug/id test://bug/2
1424
1 Joe Foo\t2005-11-22
1425
fixes bug: test://bug/id
1429
tree.branch, log.ShortLogFormatter)
1431
def test_wrong_bugs_property(self):
1432
tree = self.make_branch_and_tree(u'.')
1433
self.build_tree(['foo'])
1434
self.wt_commit(tree, 'simple log message', rev_id=b'a1',
1435
revprops={'bugs': 'test://bug/id invalid_value'})
1436
self.assertFormatterResult("""\
1437
1 Joe Foo\t2005-11-22
1441
tree.branch, log.ShortLogFormatter)
1443
def test_bugs_handler_present(self):
1444
self.properties_handler_registry.get('bugs_properties_handler')
1447
class TestLogForAuthors(TestCaseForLogFormatter):
1450
super(TestLogForAuthors, self).setUp()
1451
self.wt = self.make_standard_commit('nicky',
1452
authors=['John Doe <jdoe@example.com>',
1453
'Jane Rey <jrey@example.com>'])
1455
def assertFormatterResult(self, formatter, who, result):
1456
formatter_kwargs = dict()
1458
author_list_handler = log.author_list_registry.get(who)
1459
formatter_kwargs['author_list_handler'] = author_list_handler
1460
TestCaseForLogFormatter.assertFormatterResult(self, result,
1461
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1463
def test_line_default(self):
1464
self.assertFormatterResult(log.LineLogFormatter, None, """\
1465
1: John Doe 2005-11-22 add a
1468
def test_line_committer(self):
1469
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1470
1: Lorem Ipsum 2005-11-22 add a
1473
def test_line_first(self):
1474
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1475
1: John Doe 2005-11-22 add a
1478
def test_line_all(self):
1479
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1480
1: John Doe, Jane Rey 2005-11-22 add a
1484
def test_short_default(self):
1485
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1486
1 John Doe\t2005-11-22
1491
def test_short_committer(self):
1492
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1493
1 Lorem Ipsum\t2005-11-22
1498
def test_short_first(self):
1499
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1500
1 John Doe\t2005-11-22
1505
def test_short_all(self):
1506
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1507
1 John Doe, Jane Rey\t2005-11-22
1512
def test_long_default(self):
1513
self.assertFormatterResult(log.LongLogFormatter, None, """\
1514
------------------------------------------------------------
1516
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1517
committer: Lorem Ipsum <test@example.com>
1519
timestamp: Tue 2005-11-22 00:00:00 +0000
1524
def test_long_committer(self):
1525
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1526
------------------------------------------------------------
1528
committer: Lorem Ipsum <test@example.com>
1530
timestamp: Tue 2005-11-22 00:00:00 +0000
1535
def test_long_first(self):
1536
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1537
------------------------------------------------------------
1539
author: John Doe <jdoe@example.com>
1540
committer: Lorem Ipsum <test@example.com>
1542
timestamp: Tue 2005-11-22 00:00:00 +0000
1547
def test_long_all(self):
1548
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1549
------------------------------------------------------------
1551
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1552
committer: Lorem Ipsum <test@example.com>
1554
timestamp: Tue 2005-11-22 00:00:00 +0000
1559
def test_gnu_changelog_default(self):
1560
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1561
2005-11-22 John Doe <jdoe@example.com>
1567
def test_gnu_changelog_committer(self):
1568
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1569
2005-11-22 Lorem Ipsum <test@example.com>
1575
def test_gnu_changelog_first(self):
1576
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1577
2005-11-22 John Doe <jdoe@example.com>
1583
def test_gnu_changelog_all(self):
1584
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1585
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1592
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1594
def make_branch_with_alternate_ancestries(self, relpath='.'):
1595
# See test_merge_sorted_exclude_ancestry below for the difference with
1596
# bt.per_branch.test_iter_merge_sorted_revision.
1597
# TestIterMergeSortedRevisionsBushyGraph.
1598
# make_branch_with_alternate_ancestries
1599
# and test_merge_sorted_exclude_ancestry
1600
# See the FIXME in assertLogRevnos too.
1601
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1613
builder.start_series()
1614
builder.build_snapshot(None, [
1615
('add', ('', 'TREE_ROOT', 'directory', '')),],
1617
builder.build_snapshot(['1'], [], revision_id='1.1.1')
1618
builder.build_snapshot(['1'], [], revision_id='2')
1619
builder.build_snapshot(['1.1.1'], [], revision_id='1.2.1')
1620
builder.build_snapshot(['1.1.1', '1.2.1'], [], revision_id='1.1.2')
1621
builder.build_snapshot(['2', '1.1.2'], [], revision_id='3')
1622
builder.finish_series()
1623
br = builder.get_branch()
1625
self.addCleanup(br.unlock)
1628
def assertLogRevnos(self, expected_revnos, b, start, end,
1629
exclude_common_ancestry, generate_merge_revisions=True):
1630
# FIXME: the layering in log makes it hard to test intermediate levels,
1631
# I wish adding filters with their parameters was easier...
1633
iter_revs = log._calc_view_revisions(
1634
b, start, end, direction='reverse',
1635
generate_merge_revisions=generate_merge_revisions,
1636
exclude_common_ancestry=exclude_common_ancestry)
1637
self.assertEqual(expected_revnos,
1638
[revid for revid, revno, depth in iter_revs])
1640
def test_merge_sorted_exclude_ancestry(self):
1641
b = self.make_branch_with_alternate_ancestries()
1642
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1643
b, '1', '3', exclude_common_ancestry=False)
1644
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1645
# it should be mentioned even if merge_sort order will make it appear
1647
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1648
b, '1.1.1', '3', exclude_common_ancestry=True)
1650
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1651
b = self.make_branch_with_alternate_ancestries()
1652
self.assertLogRevnos(['3', '2'],
1653
b, '1', '3', exclude_common_ancestry=True,
1654
generate_merge_revisions=False)
1655
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1656
b, '1', '3', exclude_common_ancestry=True,
1657
generate_merge_revisions=True)
1660
class TestLogDefaults(TestCaseForLogFormatter):
1661
def test_default_log_level(self):
1663
Test to ensure that specifying 'levels=1' to make_log_request_dict
1664
doesn't get overwritten when using a LogFormatter that supports more
1668
wt = self._prepare_tree_with_merges()
1671
class CustomLogFormatter(log.LogFormatter):
1672
def __init__(self, *args, **kwargs):
1673
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1675
def get_levels(self):
1676
# log formatter supports all levels:
1678
def log_revision(self, revision):
1679
self.revisions.append(revision)
1681
log_formatter = LogCatcher()
1682
# First request we don't specify number of levels, we should get a
1683
# sensible default (whatever the LogFormatter handles - which in this
1684
# case is 0/everything):
1685
request = log.make_log_request_dict(limit=10)
1686
log.Logger(b, request).show(log_formatter)
1687
# should have all three revisions:
1688
self.assertEqual(len(log_formatter.revisions), 3)
1691
log_formatter = LogCatcher()
1692
# now explicitly request mainline revisions only:
1693
request = log.make_log_request_dict(limit=10, levels=1)
1694
log.Logger(b, request).show(log_formatter)
1695
# should now only have 2 revisions:
1696
self.assertEqual(len(log_formatter.revisions), 2)