~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Vincent Ladeuil
  • Date: 2016-01-21 09:52:36 UTC
  • mto: This revision was merged to the branch mainline in revision 6611.
  • Revision ID: v.ladeuil+lp@free.fr-20160121095236-iandk0fic0phu9ey
Fix the failing gpg test on wily.

Show diffs side-by-side

added added

removed removed

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