/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_log.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-20 00:00:04 UTC
  • mfrom: (6690.5.2 bundle-guess)
  • Revision ID: jelmer@jelmer.uk-20170720000004-wlknc5gthdk3tokn
Merge lp:~jelmer/brz/bundle-guess.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2013, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import os
18
 
from cStringIO import StringIO
19
18
 
20
 
from bzrlib import (
 
19
from .. import (
21
20
    branchbuilder,
22
21
    errors,
23
22
    log,
24
23
    registry,
25
24
    revision,
26
25
    revisionspec,
27
 
    symbol_versioning,
28
26
    tests,
 
27
    gpg,
 
28
    trace,
 
29
    )
 
30
from ..sixish import (
 
31
    BytesIO,
29
32
    )
30
33
 
31
34
 
117
120
            branch.tags.set_tag('v1.0', 'rev-3')
118
121
        return wt
119
122
 
 
123
 
120
124
class LogCatcher(log.LogFormatter):
121
125
    """Pull log messages into a list rather than displaying them.
122
126
 
250
254
        wt.commit(message='add file1 and file2')
251
255
        self.run_bzr('branch parent child')
252
256
        os.unlink('child/file1')
253
 
        file('child/file2', 'wb').write('hello\n')
 
257
        with file('child/file2', 'wb') as f: f.write('hello\n')
254
258
        self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
255
259
            'child'])
256
260
        os.chdir('parent')
281
285
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
282
286
 
283
287
 
 
288
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
 
289
    class UTFLoopbackGPGStrategy(gpg.LoopbackGPGStrategy):
 
290
        def verify(self, content, testament):
 
291
            return (gpg.SIGNATURE_VALID,
 
292
                u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
 
293
 
 
294
    def has_signature_for_revision_id(self, revision_id):
 
295
        return True
 
296
 
 
297
    def get_signature_text(self, revision_id):
 
298
        return ''
 
299
 
 
300
    def test_format_signature_validity_utf(self):
 
301
        """Check that GPG signatures containing UTF-8 names are formatted
 
302
        correctly."""
 
303
        # Monkey patch to use our UTF-8 generating GPGStrategy
 
304
        self.overrideAttr(gpg, 'GPGStrategy', self.UTFLoopbackGPGStrategy)
 
305
        wt = self.make_branch_and_tree('.')
 
306
        revid = wt.commit('empty commit')
 
307
        repo = wt.branch.repository
 
308
        # Monkey patch out checking if this rev is actually signed, since we
 
309
        # can't sign it without a heavier TestCase and LoopbackGPGStrategy
 
310
        # doesn't care anyways.
 
311
        self.overrideAttr(repo, 'has_signature_for_revision_id',
 
312
                self.has_signature_for_revision_id)
 
313
        self.overrideAttr(repo, 'get_signature_text', self.get_signature_text)
 
314
        out = log.format_signature_validity(revid, repo)
 
315
        self.assertEqual(
 
316
u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
 
317
                out)
 
318
 
 
319
 
284
320
class TestShortLogFormatter(TestCaseForLogFormatter):
285
321
 
286
322
    def test_trailing_newlines(self):
322
358
    1 Joe Foo\t2005-11-22
323
359
      rev-1
324
360
 
325
 
Use --include-merges or -n0 to see merged revisions.
 
361
Use --include-merged or -n0 to see merged revisions.
326
362
""",
327
363
            wt.branch, log.ShortLogFormatter,
328
364
            formatter_kwargs=dict(show_advice=True))
371
407
            wt.branch, log.ShortLogFormatter,
372
408
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
373
409
 
 
410
    def test_show_ids(self):
 
411
        wt = self.make_branch_and_tree('parent')
 
412
        self.build_tree(['parent/f1', 'parent/f2'])
 
413
        wt.add(['f1','f2'])
 
414
        self.wt_commit(wt, 'first post', rev_id='a')
 
415
        child_wt = wt.controldir.sprout('child').open_workingtree()
 
416
        self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
 
417
        wt.merge_from_branch(child_wt.branch)
 
418
        self.wt_commit(wt, 'merge branch 1', rev_id='c')
 
419
        self.assertFormatterResult("""\
 
420
    2 Joe Foo\t2005-11-22 [merge]
 
421
      revision-id:c
 
422
      merge branch 1
 
423
 
 
424
          1.1.1 Joe Foo\t2005-11-22
 
425
                revision-id:b
 
426
                branch 1 changes
 
427
 
 
428
    1 Joe Foo\t2005-11-22
 
429
      revision-id:a
 
430
      first post
 
431
 
 
432
""",
 
433
            wt.branch, log.ShortLogFormatter,
 
434
            formatter_kwargs=dict(levels=0,show_ids=True))
 
435
 
374
436
 
375
437
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
376
438
 
432
494
    def test_merges_are_indented_by_level(self):
433
495
        wt = self.make_branch_and_tree('parent')
434
496
        self.wt_commit(wt, 'first post')
435
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
497
        child_wt = wt.controldir.sprout('child').open_workingtree()
436
498
        self.wt_commit(child_wt, 'branch 1')
437
 
        smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
 
499
        smallerchild_wt = wt.controldir.sprout('smallerchild').open_workingtree()
438
500
        self.wt_commit(smallerchild_wt, 'branch 2')
439
501
        child_wt.merge_from_branch(smallerchild_wt.branch)
440
502
        self.wt_commit(child_wt, 'merge branch 2')
486
548
        self.build_tree(['parent/f1', 'parent/f2'])
487
549
        wt.add(['f1','f2'])
488
550
        self.wt_commit(wt, 'first post')
489
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
551
        child_wt = wt.controldir.sprout('child').open_workingtree()
490
552
        os.unlink('child/f1')
491
553
        self.build_tree_contents([('child/f2', 'hello\n')])
492
554
        self.wt_commit(child_wt, 'removed f1 and modified f2')
632
694
        sio = self.make_utf8_encoded_stringio()
633
695
        formatter = log.LongLogFormatter(to_file=sio)
634
696
        def trivial_custom_prop_handler(revision):
635
 
            raise StandardError("a test error")
 
697
            raise Exception("a test error")
636
698
 
637
699
        log.properties_handler_registry.register(
638
700
            'trivial_custom_prop_handler',
639
701
            trivial_custom_prop_handler)
640
 
        self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
 
702
        self.assertRaises(Exception, log.show_log, wt.branch, formatter,)
641
703
 
642
704
    def test_properties_handler_bad_argument(self):
643
705
        wt = self.make_standard_commit('bad_argument',
659
721
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
660
722
                             sio.getvalue())
661
723
 
 
724
    def test_show_ids(self):
 
725
        wt = self.make_branch_and_tree('parent')
 
726
        self.build_tree(['parent/f1', 'parent/f2'])
 
727
        wt.add(['f1','f2'])
 
728
        self.wt_commit(wt, 'first post', rev_id='a')
 
729
        child_wt = wt.controldir.sprout('child').open_workingtree()
 
730
        self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
 
731
        wt.merge_from_branch(child_wt.branch)
 
732
        self.wt_commit(wt, 'merge branch 1', rev_id='c')
 
733
        self.assertFormatterResult("""\
 
734
------------------------------------------------------------
 
735
revno: 2 [merge]
 
736
revision-id: c
 
737
parent: a
 
738
parent: b
 
739
committer: Joe Foo <joe@foo.com>
 
740
branch nick: parent
 
741
timestamp: Tue 2005-11-22 00:00:02 +0000
 
742
message:
 
743
  merge branch 1
 
744
    ------------------------------------------------------------
 
745
    revno: 1.1.1
 
746
    revision-id: b
 
747
    parent: a
 
748
    committer: Joe Foo <joe@foo.com>
 
749
    branch nick: child
 
750
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
751
    message:
 
752
      branch 1 changes
 
753
------------------------------------------------------------
 
754
revno: 1
 
755
revision-id: a
 
756
committer: Joe Foo <joe@foo.com>
 
757
branch nick: parent
 
758
timestamp: Tue 2005-11-22 00:00:00 +0000
 
759
message:
 
760
  first post
 
761
""",
 
762
            wt.branch, log.LongLogFormatter,
 
763
            formatter_kwargs=dict(levels=0,show_ids=True))
 
764
 
662
765
 
663
766
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
664
767
 
688
791
        self.build_tree(['parent/f1', 'parent/f2'])
689
792
        wt.add(['f1','f2'])
690
793
        self.wt_commit(wt, 'first post')
691
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
794
        child_wt = wt.controldir.sprout('child').open_workingtree()
692
795
        os.unlink('child/f1')
693
796
        self.build_tree_contents([('child/f2', 'hello\n')])
694
797
        self.wt_commit(child_wt, 'removed f1 and modified f2')
915
1018
            wt.branch, log.GnuChangelogLogFormatter,
916
1019
            show_log_kwargs=dict(verbose=True))
917
1020
 
918
 
class TestGetViewRevisions(tests.TestCaseWithTransport, TestLogMixin):
919
 
 
920
 
    def _get_view_revisions(self, *args, **kwargs):
921
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
922
 
                                    log.get_view_revisions, *args, **kwargs)
923
 
 
924
 
    def make_tree_with_commits(self):
925
 
        """Create a tree with well-known revision ids"""
926
 
        wt = self.make_branch_and_tree('tree1')
927
 
        self.wt_commit(wt, 'commit one', rev_id='1')
928
 
        self.wt_commit(wt, 'commit two', rev_id='2')
929
 
        self.wt_commit(wt, 'commit three', rev_id='3')
930
 
        mainline_revs = [None, '1', '2', '3']
931
 
        rev_nos = {'1': 1, '2': 2, '3': 3}
932
 
        return mainline_revs, rev_nos, wt
933
 
 
934
 
    def make_tree_with_merges(self):
935
 
        """Create a tree with well-known revision ids and a merge"""
936
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
937
 
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
938
 
        self.wt_commit(tree2, 'four-a', rev_id='4a')
939
 
        wt.merge_from_branch(tree2.branch)
940
 
        self.wt_commit(wt, 'four-b', rev_id='4b')
941
 
        mainline_revs.append('4b')
942
 
        rev_nos['4b'] = 4
943
 
        # 4a: 3.1.1
944
 
        return mainline_revs, rev_nos, wt
945
 
 
946
 
    def make_branch_with_many_merges(self):
947
 
        """Create a tree with well-known revision ids"""
948
 
        builder = self.make_branch_builder('tree1')
949
 
        builder.start_series()
950
 
        builder.build_snapshot('1', None, [
951
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),
952
 
            ('add', ('f', 'f-id', 'file', '1\n'))])
953
 
        builder.build_snapshot('2', ['1'], [])
954
 
        builder.build_snapshot('3a', ['2'], [
955
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
956
 
        builder.build_snapshot('3b', ['2', '3a'], [
957
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
958
 
        builder.build_snapshot('3c', ['2', '3b'], [
959
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
960
 
        builder.build_snapshot('4a', ['3b'], [])
961
 
        builder.build_snapshot('4b', ['3c', '4a'], [])
962
 
        builder.finish_series()
963
 
 
964
 
        # 1
965
 
        # |
966
 
        # 2-.
967
 
        # |\ \
968
 
        # | | 3a
969
 
        # | |/
970
 
        # | 3b
971
 
        # |/|
972
 
        # 3c4a
973
 
        # |/
974
 
        # 4b
975
 
 
976
 
        mainline_revs = [None, '1', '2', '3c', '4b']
977
 
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
978
 
        full_rev_nos_for_reference = {
979
 
            '1': '1',
980
 
            '2': '2',
981
 
            '3a': '2.1.1', #first commit tree 3
982
 
            '3b': '2.2.1', # first commit tree 2
983
 
            '3c': '3', #merges 3b to main
984
 
            '4a': '2.2.2', # second commit tree 2
985
 
            '4b': '4', # merges 4a to main
986
 
            }
987
 
        return mainline_revs, rev_nos, builder.get_branch()
988
 
 
989
 
    def test_get_view_revisions_forward(self):
990
 
        """Test the get_view_revisions method"""
991
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
992
 
        wt.lock_read()
993
 
        self.addCleanup(wt.unlock)
994
 
        revisions = list(self._get_view_revisions(
995
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
996
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
997
 
                         revisions)
998
 
        revisions2 = list(self._get_view_revisions(
999
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1000
 
                include_merges=False))
1001
 
        self.assertEqual(revisions, revisions2)
1002
 
 
1003
 
    def test_get_view_revisions_reverse(self):
1004
 
        """Test the get_view_revisions with reverse"""
1005
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1006
 
        wt.lock_read()
1007
 
        self.addCleanup(wt.unlock)
1008
 
        revisions = list(self._get_view_revisions(
1009
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1010
 
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1011
 
                         revisions)
1012
 
        revisions2 = list(self._get_view_revisions(
1013
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1014
 
                include_merges=False))
1015
 
        self.assertEqual(revisions, revisions2)
1016
 
 
1017
 
    def test_get_view_revisions_merge(self):
1018
 
        """Test get_view_revisions when there are merges"""
1019
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1020
 
        wt.lock_read()
1021
 
        self.addCleanup(wt.unlock)
1022
 
        revisions = list(self._get_view_revisions(
1023
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1024
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1025
 
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
1026
 
                         revisions)
1027
 
        revisions = list(self._get_view_revisions(
1028
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1029
 
                include_merges=False))
1030
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1031
 
                          ('4b', '4', 0)],
1032
 
                         revisions)
1033
 
 
1034
 
    def test_get_view_revisions_merge_reverse(self):
1035
 
        """Test get_view_revisions in reverse when there are merges"""
1036
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1037
 
        wt.lock_read()
1038
 
        self.addCleanup(wt.unlock)
1039
 
        revisions = list(self._get_view_revisions(
1040
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1041
 
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1042
 
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1043
 
                         revisions)
1044
 
        revisions = list(self._get_view_revisions(
1045
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1046
 
                include_merges=False))
1047
 
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1048
 
                          ('1', '1', 0)],
1049
 
                         revisions)
1050
 
 
1051
 
    def test_get_view_revisions_merge2(self):
1052
 
        """Test get_view_revisions when there are merges"""
1053
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1054
 
        b.lock_read()
1055
 
        self.addCleanup(b.unlock)
1056
 
        revisions = list(self._get_view_revisions(
1057
 
                mainline_revs, rev_nos, b, 'forward'))
1058
 
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1059
 
                    ('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
1060
 
                    ('4a', '2.2.2', 1)]
1061
 
        self.assertEqual(expected, revisions)
1062
 
        revisions = list(self._get_view_revisions(
1063
 
                mainline_revs, rev_nos, b, 'forward',
1064
 
                include_merges=False))
1065
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1066
 
                          ('4b', '4', 0)],
1067
 
                         revisions)
1068
 
 
1069
 
    def test_file_id_for_range(self):
1070
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1071
 
        b.lock_read()
1072
 
        self.addCleanup(b.unlock)
1073
 
 
1074
 
        def rev_from_rev_id(revid, branch):
1075
 
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1076
 
            return revspec.in_history(branch)
1077
 
 
1078
 
        def view_revs(start_rev, end_rev, file_id, direction):
1079
 
            revs = self.applyDeprecated(
1080
 
                symbol_versioning.deprecated_in((2, 2, 0)),
1081
 
                log.calculate_view_revisions,
1082
 
                b,
1083
 
                start_rev, # start_revision
1084
 
                end_rev, # end_revision
1085
 
                direction, # direction
1086
 
                file_id, # specific_fileid
1087
 
                True, # generate_merge_revisions
1088
 
                )
1089
 
            return revs
1090
 
 
1091
 
        rev_3a = rev_from_rev_id('3a', b)
1092
 
        rev_4b = rev_from_rev_id('4b', b)
1093
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1094
 
                          ('3a', '2.1.1', 2)],
1095
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1096
 
        # Note: 3c still appears before 3a here because of depth-based sorting
1097
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1098
 
                          ('3a', '2.1.1', 2)],
1099
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1100
 
 
1101
 
 
1102
 
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
1103
 
 
1104
 
    def get_view_revisions(self, *args):
1105
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
1106
 
                                    log.get_view_revisions, *args)
1107
 
 
1108
 
    def create_tree_with_single_merge(self):
1109
 
        """Create a branch with a moderate layout.
1110
 
 
1111
 
        The revision graph looks like:
1112
 
 
1113
 
           A
1114
 
           |\
1115
 
           B C
1116
 
           |/
1117
 
           D
1118
 
 
1119
 
        In this graph, A introduced files f1 and f2 and f3.
1120
 
        B modifies f1 and f3, and C modifies f2 and f3.
1121
 
        D merges the changes from B and C and resolves the conflict for f3.
1122
 
        """
1123
 
        # TODO: jam 20070218 This seems like it could really be done
1124
 
        #       with make_branch_and_memory_tree() if we could just
1125
 
        #       create the content of those files.
1126
 
        # TODO: jam 20070218 Another alternative is that we would really
1127
 
        #       like to only create this tree 1 time for all tests that
1128
 
        #       use it. Since 'log' only uses the tree in a readonly
1129
 
        #       fashion, it seems a shame to regenerate an identical
1130
 
        #       tree for each test.
1131
 
        # TODO: vila 20100122 One way to address the shame above will be to
1132
 
        #       create a memory tree during test parametrization and give a
1133
 
        #       *copy* of this tree to each test. Copying a memory tree ought
1134
 
        #       to be cheap, at least cheaper than creating them with such
1135
 
        #       complex setups.
1136
 
        tree = self.make_branch_and_tree('tree')
1137
 
        tree.lock_write()
1138
 
        self.addCleanup(tree.unlock)
1139
 
 
1140
 
        self.build_tree_contents([('tree/f1', 'A\n'),
1141
 
                                  ('tree/f2', 'A\n'),
1142
 
                                  ('tree/f3', 'A\n'),
1143
 
                                 ])
1144
 
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
1145
 
        tree.commit('A', rev_id='A')
1146
 
 
1147
 
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
1148
 
                                  ('tree/f3', 'A\nC\n'),
1149
 
                                 ])
1150
 
        tree.commit('C', rev_id='C')
1151
 
        # Revert back to A to build the other history.
1152
 
        tree.set_last_revision('A')
1153
 
        tree.branch.set_last_revision_info(1, 'A')
1154
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1155
 
                                  ('tree/f2', 'A\n'),
1156
 
                                  ('tree/f3', 'A\nB\n'),
1157
 
                                 ])
1158
 
        tree.commit('B', rev_id='B')
1159
 
        tree.set_parent_ids(['B', 'C'])
1160
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1161
 
                                  ('tree/f2', 'A\nC\n'),
1162
 
                                  ('tree/f3', 'A\nB\nC\n'),
1163
 
                                 ])
1164
 
        tree.commit('D', rev_id='D')
1165
 
 
1166
 
        # Switch to a read lock for this tree.
1167
 
        # We still have an addCleanup(tree.unlock) pending
1168
 
        tree.unlock()
1169
 
        tree.lock_read()
1170
 
        return tree
1171
 
 
1172
 
    def check_delta(self, delta, **kw):
1173
 
        """Check the filenames touched by a delta are as expected.
1174
 
 
1175
 
        Caller only have to pass in the list of files for each part, all
1176
 
        unspecified parts are considered empty (and checked as such).
1177
 
        """
1178
 
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1179
 
            # By default we expect an empty list
1180
 
            expected = kw.get(n, [])
1181
 
            # strip out only the path components
1182
 
            got = [x[0] for x in getattr(delta, n)]
1183
 
            self.assertEqual(expected, got)
1184
 
 
1185
 
    def test_tree_with_single_merge(self):
1186
 
        """Make sure the tree layout is correct."""
1187
 
        tree = self.create_tree_with_single_merge()
1188
 
        rev_A_tree = tree.branch.repository.revision_tree('A')
1189
 
        rev_B_tree = tree.branch.repository.revision_tree('B')
1190
 
        rev_C_tree = tree.branch.repository.revision_tree('C')
1191
 
        rev_D_tree = tree.branch.repository.revision_tree('D')
1192
 
 
1193
 
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1194
 
                         modified=['f1', 'f3'])
1195
 
 
1196
 
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1197
 
                         modified=['f2', 'f3'])
1198
 
 
1199
 
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1200
 
                         modified=['f2', 'f3'])
1201
 
 
1202
 
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1203
 
                         modified=['f1', 'f3'])
1204
 
 
1205
 
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1206
 
        """Ensure _filter_revisions_touching_file_id returns the right values.
1207
 
 
1208
 
        Get the return value from _filter_revisions_touching_file_id and make
1209
 
        sure they are correct.
1210
 
        """
1211
 
        # The api for _filter_revisions_touching_file_id is a little crazy.
1212
 
        # So we do the setup here.
1213
 
        mainline = tree.branch.revision_history()
1214
 
        mainline.insert(0, None)
1215
 
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
1216
 
        view_revs_iter = self.get_view_revisions(
1217
 
            mainline, revnos, tree.branch, 'reverse', True)
1218
 
        actual_revs = log._filter_revisions_touching_file_id(
1219
 
            tree.branch, file_id, list(view_revs_iter))
1220
 
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1221
 
 
1222
 
    def test_file_id_f1(self):
1223
 
        tree = self.create_tree_with_single_merge()
1224
 
        # f1 should be marked as modified by revisions A and B
1225
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1226
 
 
1227
 
    def test_file_id_f2(self):
1228
 
        tree = self.create_tree_with_single_merge()
1229
 
        # f2 should be marked as modified by revisions A, C, and D
1230
 
        # because D merged the changes from C.
1231
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1232
 
 
1233
 
    def test_file_id_f3(self):
1234
 
        tree = self.create_tree_with_single_merge()
1235
 
        # f3 should be marked as modified by revisions A, B, C, and D
1236
 
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1237
 
 
1238
 
    def test_file_id_with_ghosts(self):
1239
 
        # This is testing bug #209948, where having a ghost would cause
1240
 
        # _filter_revisions_touching_file_id() to fail.
1241
 
        tree = self.create_tree_with_single_merge()
1242
 
        # We need to add a revision, so switch back to a write-locked tree
1243
 
        # (still a single addCleanup(tree.unlock) pending).
1244
 
        tree.unlock()
1245
 
        tree.lock_write()
1246
 
        first_parent = tree.last_revision()
1247
 
        tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1248
 
        self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1249
 
        tree.commit('commit with a ghost', rev_id='XX')
1250
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1251
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1252
 
 
1253
 
    def test_unknown_file_id(self):
1254
 
        tree = self.create_tree_with_single_merge()
1255
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1256
 
 
1257
 
    def test_empty_branch_unknown_file_id(self):
1258
 
        tree = self.make_branch_and_tree('tree')
1259
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1260
 
 
1261
1021
 
1262
1022
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1263
1023
 
1434
1194
 
1435
1195
    def test_show_branch_change(self):
1436
1196
        tree = self.setup_ab_tree()
1437
 
        s = StringIO()
 
1197
        s = BytesIO()
1438
1198
        log.show_branch_change(tree.branch, s, 3, '3a')
1439
1199
        self.assertContainsRe(s.getvalue(),
1440
1200
            '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1442
1202
 
1443
1203
    def test_show_branch_change_no_change(self):
1444
1204
        tree = self.setup_ab_tree()
1445
 
        s = StringIO()
 
1205
        s = BytesIO()
1446
1206
        log.show_branch_change(tree.branch, s, 3, '3b')
1447
1207
        self.assertEqual(s.getvalue(),
1448
1208
            'Nothing seems to have changed\n')
1449
1209
 
1450
1210
    def test_show_branch_change_no_old(self):
1451
1211
        tree = self.setup_ab_tree()
1452
 
        s = StringIO()
 
1212
        s = BytesIO()
1453
1213
        log.show_branch_change(tree.branch, s, 2, '2b')
1454
1214
        self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1455
1215
        self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1457
1217
    def test_show_branch_change_no_new(self):
1458
1218
        tree = self.setup_ab_tree()
1459
1219
        tree.branch.set_last_revision_info(2, '2b')
1460
 
        s = StringIO()
 
1220
        s = BytesIO()
1461
1221
        log.show_branch_change(tree.branch, s, 3, '3b')
1462
1222
        self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1463
1223
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1464
1224
 
1465
1225
 
 
1226
class TestRevisionNotInBranch(TestCaseForLogFormatter):
 
1227
 
 
1228
    def setup_a_tree(self):
 
1229
        tree = self.make_branch_and_tree('tree')
 
1230
        tree.lock_write()
 
1231
        self.addCleanup(tree.unlock)
 
1232
        kwargs = {
 
1233
            'committer': 'Joe Foo <joe@foo.com>',
 
1234
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
 
1235
            'timezone': 0, # UTC
 
1236
        }
 
1237
        tree.commit('commit 1a', rev_id='1a', **kwargs)
 
1238
        tree.commit('commit 2a', rev_id='2a', **kwargs)
 
1239
        tree.commit('commit 3a', rev_id='3a', **kwargs)
 
1240
        return tree
 
1241
 
 
1242
    def setup_ab_tree(self):
 
1243
        tree = self.setup_a_tree()
 
1244
        tree.set_last_revision('1a')
 
1245
        tree.branch.set_last_revision_info(1, '1a')
 
1246
        kwargs = {
 
1247
            'committer': 'Joe Foo <joe@foo.com>',
 
1248
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
 
1249
            'timezone': 0, # UTC
 
1250
        }
 
1251
        tree.commit('commit 2b', rev_id='2b', **kwargs)
 
1252
        tree.commit('commit 3b', rev_id='3b', **kwargs)
 
1253
        return tree
 
1254
 
 
1255
    def test_one_revision(self):
 
1256
        tree = self.setup_ab_tree()
 
1257
        lf = LogCatcher()
 
1258
        rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1259
        log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
 
1260
                     end_revision=rev)
 
1261
        self.assertEqual(1, len(lf.revisions))
 
1262
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1263
        self.assertEqual('3a', lf.revisions[0].rev.revision_id)
 
1264
 
 
1265
    def test_many_revisions(self):
 
1266
        tree = self.setup_ab_tree()
 
1267
        lf = LogCatcher()
 
1268
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1269
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1270
        log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
 
1271
                     end_revision=end_rev)
 
1272
        self.assertEqual(3, len(lf.revisions))
 
1273
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1274
        self.assertEqual('3a', lf.revisions[0].rev.revision_id)
 
1275
        self.assertEqual(None, lf.revisions[1].revno)   # Out-of-branch
 
1276
        self.assertEqual('2a', lf.revisions[1].rev.revision_id)
 
1277
        self.assertEqual('1', lf.revisions[2].revno)    # In-branch
 
1278
 
 
1279
    def test_long_format(self):
 
1280
        tree = self.setup_ab_tree()
 
1281
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1282
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1283
        self.assertFormatterResult("""\
 
1284
------------------------------------------------------------
 
1285
revision-id: 3a
 
1286
committer: Joe Foo <joe@foo.com>
 
1287
branch nick: tree
 
1288
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1289
message:
 
1290
  commit 3a
 
1291
------------------------------------------------------------
 
1292
revision-id: 2a
 
1293
committer: Joe Foo <joe@foo.com>
 
1294
branch nick: tree
 
1295
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1296
message:
 
1297
  commit 2a
 
1298
------------------------------------------------------------
 
1299
revno: 1
 
1300
committer: Joe Foo <joe@foo.com>
 
1301
branch nick: tree
 
1302
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1303
message:
 
1304
  commit 1a
 
1305
""",
 
1306
            tree.branch, log.LongLogFormatter, show_log_kwargs={
 
1307
                'start_revision': start_rev, 'end_revision': end_rev
 
1308
            })
 
1309
 
 
1310
    def test_short_format(self):
 
1311
        tree = self.setup_ab_tree()
 
1312
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1313
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1314
        self.assertFormatterResult("""\
 
1315
      Joe Foo\t2005-11-22
 
1316
      revision-id:3a
 
1317
      commit 3a
 
1318
 
 
1319
      Joe Foo\t2005-11-22
 
1320
      revision-id:2a
 
1321
      commit 2a
 
1322
 
 
1323
    1 Joe Foo\t2005-11-22
 
1324
      commit 1a
 
1325
 
 
1326
""",
 
1327
            tree.branch, log.ShortLogFormatter, show_log_kwargs={
 
1328
                'start_revision': start_rev, 'end_revision': end_rev
 
1329
            })
 
1330
 
 
1331
    def test_line_format(self):
 
1332
        tree = self.setup_ab_tree()
 
1333
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1334
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1335
        self.assertFormatterResult("""\
 
1336
Joe Foo 2005-11-22 commit 3a
 
1337
Joe Foo 2005-11-22 commit 2a
 
1338
1: Joe Foo 2005-11-22 commit 1a
 
1339
""",
 
1340
            tree.branch, log.LineLogFormatter, show_log_kwargs={
 
1341
                'start_revision': start_rev, 'end_revision': end_rev
 
1342
            })
 
1343
 
1466
1344
 
1467
1345
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1468
1346
 
1469
1347
    def setUp(self):
1470
 
        TestCaseForLogFormatter.setUp(self)
 
1348
        super(TestLogWithBugs, self).setUp()
1471
1349
        log.properties_handler_registry.register(
1472
1350
            'bugs_properties_handler',
1473
1351
            log._bugs_properties_handler)
1492
1370
        self.assertFormatterResult("""\
1493
1371
------------------------------------------------------------
1494
1372
revno: 2
1495
 
fixes bug(s): test://bug/id test://bug/2
 
1373
fixes bugs: test://bug/id test://bug/2
1496
1374
author: Joe Bar <joe@bar.com>
1497
1375
committer: Joe Foo <joe@foo.com>
1498
1376
branch nick: work
1503
1381
  message
1504
1382
------------------------------------------------------------
1505
1383
revno: 1
1506
 
fixes bug(s): test://bug/id
 
1384
fixes bug: test://bug/id
1507
1385
committer: Joe Foo <joe@foo.com>
1508
1386
branch nick: work
1509
1387
timestamp: Tue 2005-11-22 00:00:00 +0000
1516
1394
        tree = self.make_commits_with_bugs()
1517
1395
        self.assertFormatterResult("""\
1518
1396
    2 Joe Bar\t2005-11-22
1519
 
      fixes bug(s): test://bug/id test://bug/2
 
1397
      fixes bugs: test://bug/id test://bug/2
1520
1398
      multiline
1521
1399
      log
1522
1400
      message
1523
1401
 
1524
1402
    1 Joe Foo\t2005-11-22
1525
 
      fixes bug(s): test://bug/id
 
1403
      fixes bug: test://bug/id
1526
1404
      simple log message
1527
1405
 
1528
1406
""",
1543
1421
    def test_bugs_handler_present(self):
1544
1422
        self.properties_handler_registry.get('bugs_properties_handler')
1545
1423
 
 
1424
 
 
1425
class TestLogForAuthors(TestCaseForLogFormatter):
 
1426
 
 
1427
    def setUp(self):
 
1428
        super(TestLogForAuthors, self).setUp()
 
1429
        self.wt = self.make_standard_commit('nicky',
 
1430
            authors=['John Doe <jdoe@example.com>',
 
1431
                     'Jane Rey <jrey@example.com>'])
 
1432
 
 
1433
    def assertFormatterResult(self, formatter, who, result):
 
1434
        formatter_kwargs = dict()
 
1435
        if who is not None:
 
1436
            author_list_handler = log.author_list_registry.get(who)
 
1437
            formatter_kwargs['author_list_handler'] = author_list_handler
 
1438
        TestCaseForLogFormatter.assertFormatterResult(self, result,
 
1439
            self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
 
1440
 
 
1441
    def test_line_default(self):
 
1442
        self.assertFormatterResult(log.LineLogFormatter, None, """\
 
1443
1: John Doe 2005-11-22 add a
 
1444
""")
 
1445
 
 
1446
    def test_line_committer(self):
 
1447
        self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
 
1448
1: Lorem Ipsum 2005-11-22 add a
 
1449
""")
 
1450
 
 
1451
    def test_line_first(self):
 
1452
        self.assertFormatterResult(log.LineLogFormatter, 'first', """\
 
1453
1: John Doe 2005-11-22 add a
 
1454
""")
 
1455
 
 
1456
    def test_line_all(self):
 
1457
        self.assertFormatterResult(log.LineLogFormatter, 'all', """\
 
1458
1: John Doe, Jane Rey 2005-11-22 add a
 
1459
""")
 
1460
 
 
1461
 
 
1462
    def test_short_default(self):
 
1463
        self.assertFormatterResult(log.ShortLogFormatter, None, """\
 
1464
    1 John Doe\t2005-11-22
 
1465
      add a
 
1466
 
 
1467
""")
 
1468
 
 
1469
    def test_short_committer(self):
 
1470
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
 
1471
    1 Lorem Ipsum\t2005-11-22
 
1472
      add a
 
1473
 
 
1474
""")
 
1475
 
 
1476
    def test_short_first(self):
 
1477
        self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
 
1478
    1 John Doe\t2005-11-22
 
1479
      add a
 
1480
 
 
1481
""")
 
1482
 
 
1483
    def test_short_all(self):
 
1484
        self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
 
1485
    1 John Doe, Jane Rey\t2005-11-22
 
1486
      add a
 
1487
 
 
1488
""")
 
1489
 
 
1490
    def test_long_default(self):
 
1491
        self.assertFormatterResult(log.LongLogFormatter, None, """\
 
1492
------------------------------------------------------------
 
1493
revno: 1
 
1494
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1495
committer: Lorem Ipsum <test@example.com>
 
1496
branch nick: nicky
 
1497
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1498
message:
 
1499
  add a
 
1500
""")
 
1501
 
 
1502
    def test_long_committer(self):
 
1503
        self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
 
1504
------------------------------------------------------------
 
1505
revno: 1
 
1506
committer: Lorem Ipsum <test@example.com>
 
1507
branch nick: nicky
 
1508
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1509
message:
 
1510
  add a
 
1511
""")
 
1512
 
 
1513
    def test_long_first(self):
 
1514
        self.assertFormatterResult(log.LongLogFormatter, 'first', """\
 
1515
------------------------------------------------------------
 
1516
revno: 1
 
1517
author: John Doe <jdoe@example.com>
 
1518
committer: Lorem Ipsum <test@example.com>
 
1519
branch nick: nicky
 
1520
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1521
message:
 
1522
  add a
 
1523
""")
 
1524
 
 
1525
    def test_long_all(self):
 
1526
        self.assertFormatterResult(log.LongLogFormatter, 'all', """\
 
1527
------------------------------------------------------------
 
1528
revno: 1
 
1529
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1530
committer: Lorem Ipsum <test@example.com>
 
1531
branch nick: nicky
 
1532
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1533
message:
 
1534
  add a
 
1535
""")
 
1536
 
 
1537
    def test_gnu_changelog_default(self):
 
1538
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
 
1539
2005-11-22  John Doe  <jdoe@example.com>
 
1540
 
 
1541
\tadd a
 
1542
 
 
1543
""")
 
1544
 
 
1545
    def test_gnu_changelog_committer(self):
 
1546
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
 
1547
2005-11-22  Lorem Ipsum  <test@example.com>
 
1548
 
 
1549
\tadd a
 
1550
 
 
1551
""")
 
1552
 
 
1553
    def test_gnu_changelog_first(self):
 
1554
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
 
1555
2005-11-22  John Doe  <jdoe@example.com>
 
1556
 
 
1557
\tadd a
 
1558
 
 
1559
""")
 
1560
 
 
1561
    def test_gnu_changelog_all(self):
 
1562
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
 
1563
2005-11-22  John Doe  <jdoe@example.com>, Jane Rey  <jrey@example.com>
 
1564
 
 
1565
\tadd a
 
1566
 
 
1567
""")
 
1568
 
 
1569
 
1546
1570
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1547
1571
 
1548
1572
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1549
1573
        # See test_merge_sorted_exclude_ancestry below for the difference with
1550
1574
        # bt.per_branch.test_iter_merge_sorted_revision.
1551
 
        # TestIterMergeSortedRevisionsBushyGraph. 
 
1575
        # TestIterMergeSortedRevisionsBushyGraph.
1552
1576
        # make_branch_with_alternate_ancestries
1553
1577
        # and test_merge_sorted_exclude_ancestry
1554
1578
        # See the FIXME in assertLogRevnos too.
1579
1603
        return br
1580
1604
 
1581
1605
    def assertLogRevnos(self, expected_revnos, b, start, end,
1582
 
                        exclude_common_ancestry):
 
1606
                        exclude_common_ancestry, generate_merge_revisions=True):
1583
1607
        # FIXME: the layering in log makes it hard to test intermediate levels,
1584
 
        # I wish adding filters with their parameters were easier...
 
1608
        # I wish adding filters with their parameters was easier...
1585
1609
        # -- vila 20100413
1586
1610
        iter_revs = log._calc_view_revisions(
1587
1611
            b, start, end, direction='reverse',
1588
 
            generate_merge_revisions=True,
 
1612
            generate_merge_revisions=generate_merge_revisions,
1589
1613
            exclude_common_ancestry=exclude_common_ancestry)
1590
1614
        self.assertEqual(expected_revnos,
1591
1615
                         [revid for revid, revno, depth in iter_revs])
1593
1617
    def test_merge_sorted_exclude_ancestry(self):
1594
1618
        b = self.make_branch_with_alternate_ancestries()
1595
1619
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1596
 
                             b, '1', '3', False)
 
1620
                             b, '1', '3', exclude_common_ancestry=False)
1597
1621
        # '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1598
1622
        # it should be mentioned even if merge_sort order will make it appear
1599
1623
        # after 1.1.1
1600
1624
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1601
 
                             b, '1.1.1', '3', True)
1602
 
 
 
1625
                             b, '1.1.1', '3', exclude_common_ancestry=True)
 
1626
 
 
1627
    def test_merge_sorted_simple_revnos_exclude_ancestry(self):
 
1628
        b = self.make_branch_with_alternate_ancestries()
 
1629
        self.assertLogRevnos(['3', '2'],
 
1630
                             b, '1', '3', exclude_common_ancestry=True,
 
1631
                             generate_merge_revisions=False)
 
1632
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
 
1633
                             b, '1', '3', exclude_common_ancestry=True,
 
1634
                             generate_merge_revisions=True)
 
1635
 
 
1636
 
 
1637
class TestLogDefaults(TestCaseForLogFormatter):
 
1638
    def test_default_log_level(self):
 
1639
        """
 
1640
        Test to ensure that specifying 'levels=1' to make_log_request_dict
 
1641
        doesn't get overwritten when using a LogFormatter that supports more
 
1642
        detail.
 
1643
        Fixes bug #747958.
 
1644
        """
 
1645
        wt = self._prepare_tree_with_merges()
 
1646
        b = wt.branch
 
1647
 
 
1648
        class CustomLogFormatter(log.LogFormatter):
 
1649
            def __init__(self, *args, **kwargs):
 
1650
                super(CustomLogFormatter, self).__init__(*args, **kwargs)
 
1651
                self.revisions = []
 
1652
            def get_levels(self):
 
1653
                # log formatter supports all levels:
 
1654
                return 0
 
1655
            def log_revision(self, revision):
 
1656
                self.revisions.append(revision)
 
1657
 
 
1658
        log_formatter = LogCatcher()
 
1659
        # First request we don't specify number of levels, we should get a
 
1660
        # sensible default (whatever the LogFormatter handles - which in this
 
1661
        # case is 0/everything):
 
1662
        request = log.make_log_request_dict(limit=10)
 
1663
        log.Logger(b, request).show(log_formatter)
 
1664
        # should have all three revisions:
 
1665
        self.assertEqual(len(log_formatter.revisions), 3)
 
1666
 
 
1667
        del log_formatter
 
1668
        log_formatter = LogCatcher()
 
1669
        # now explicitly request mainline revisions only:
 
1670
        request = log.make_log_request_dict(limit=10, levels=1)
 
1671
        log.Logger(b, request).show(log_formatter)
 
1672
        # should now only have 2 revisions:
 
1673
        self.assertEqual(len(log_formatter.revisions), 2)
1603
1674