~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-08 08:15:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5498.
  • Revision ID: andrew.bennetts@canonical.com-20101008081514-dviqzrdfwyzsqbz2
Split NEWS into per-release doc/en/release-notes/bzr-*.txt

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2013, 2016 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
24
24
    registry,
25
25
    revision,
26
26
    revisionspec,
 
27
    symbol_versioning,
27
28
    tests,
28
 
    gpg,
29
 
    trace,
30
29
    )
31
30
 
32
31
 
118
117
            branch.tags.set_tag('v1.0', 'rev-3')
119
118
        return wt
120
119
 
121
 
 
122
120
class LogCatcher(log.LogFormatter):
123
121
    """Pull log messages into a list rather than displaying them.
124
122
 
252
250
        wt.commit(message='add file1 and file2')
253
251
        self.run_bzr('branch parent child')
254
252
        os.unlink('child/file1')
255
 
        with file('child/file2', 'wb') as f: f.write('hello\n')
 
253
        file('child/file2', 'wb').write('hello\n')
256
254
        self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
257
255
            'child'])
258
256
        os.chdir('parent')
283
281
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
284
282
 
285
283
 
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
 
 
318
284
class TestShortLogFormatter(TestCaseForLogFormatter):
319
285
 
320
286
    def test_trailing_newlines(self):
356
322
    1 Joe Foo\t2005-11-22
357
323
      rev-1
358
324
 
359
 
Use --include-merged or -n0 to see merged revisions.
 
325
Use --include-merges or -n0 to see merged revisions.
360
326
""",
361
327
            wt.branch, log.ShortLogFormatter,
362
328
            formatter_kwargs=dict(show_advice=True))
405
371
            wt.branch, log.ShortLogFormatter,
406
372
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
407
373
 
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
 
 
434
374
 
435
375
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
436
376
 
719
659
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
720
660
                             sio.getvalue())
721
661
 
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
 
 
763
662
 
764
663
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
765
664
 
1016
915
            wt.branch, log.GnuChangelogLogFormatter,
1017
916
            show_log_kwargs=dict(verbose=True))
1018
917
 
 
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
 
1019
1261
 
1020
1262
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1021
1263
 
1221
1463
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1222
1464
 
1223
1465
 
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
 
 
1342
1466
 
1343
1467
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1344
1468
 
1345
1469
    def setUp(self):
1346
 
        super(TestLogWithBugs, self).setUp()
 
1470
        TestCaseForLogFormatter.setUp(self)
1347
1471
        log.properties_handler_registry.register(
1348
1472
            'bugs_properties_handler',
1349
1473
            log._bugs_properties_handler)
1368
1492
        self.assertFormatterResult("""\
1369
1493
------------------------------------------------------------
1370
1494
revno: 2
1371
 
fixes bugs: test://bug/id test://bug/2
 
1495
fixes bug(s): test://bug/id test://bug/2
1372
1496
author: Joe Bar <joe@bar.com>
1373
1497
committer: Joe Foo <joe@foo.com>
1374
1498
branch nick: work
1379
1503
  message
1380
1504
------------------------------------------------------------
1381
1505
revno: 1
1382
 
fixes bug: test://bug/id
 
1506
fixes bug(s): test://bug/id
1383
1507
committer: Joe Foo <joe@foo.com>
1384
1508
branch nick: work
1385
1509
timestamp: Tue 2005-11-22 00:00:00 +0000
1392
1516
        tree = self.make_commits_with_bugs()
1393
1517
        self.assertFormatterResult("""\
1394
1518
    2 Joe Bar\t2005-11-22
1395
 
      fixes bugs: test://bug/id test://bug/2
 
1519
      fixes bug(s): test://bug/id test://bug/2
1396
1520
      multiline
1397
1521
      log
1398
1522
      message
1399
1523
 
1400
1524
    1 Joe Foo\t2005-11-22
1401
 
      fixes bug: test://bug/id
 
1525
      fixes bug(s): test://bug/id
1402
1526
      simple log message
1403
1527
 
1404
1528
""",
1423
1547
class TestLogForAuthors(TestCaseForLogFormatter):
1424
1548
 
1425
1549
    def setUp(self):
1426
 
        super(TestLogForAuthors, self).setUp()
 
1550
        TestCaseForLogFormatter.setUp(self)
1427
1551
        self.wt = self.make_standard_commit('nicky',
1428
1552
            authors=['John Doe <jdoe@example.com>',
1429
1553
                     'Jane Rey <jrey@example.com>'])
1564
1688
 
1565
1689
""")
1566
1690
 
1567
 
 
1568
1691
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1569
1692
 
1570
1693
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1571
1694
        # See test_merge_sorted_exclude_ancestry below for the difference with
1572
1695
        # bt.per_branch.test_iter_merge_sorted_revision.
1573
 
        # TestIterMergeSortedRevisionsBushyGraph.
 
1696
        # TestIterMergeSortedRevisionsBushyGraph. 
1574
1697
        # make_branch_with_alternate_ancestries
1575
1698
        # and test_merge_sorted_exclude_ancestry
1576
1699
        # See the FIXME in assertLogRevnos too.
1603
1726
    def assertLogRevnos(self, expected_revnos, b, start, end,
1604
1727
                        exclude_common_ancestry, generate_merge_revisions=True):
1605
1728
        # FIXME: the layering in log makes it hard to test intermediate levels,
1606
 
        # I wish adding filters with their parameters was easier...
 
1729
        # I wish adding filters with their parameters were easier...
1607
1730
        # -- vila 20100413
1608
1731
        iter_revs = log._calc_view_revisions(
1609
1732
            b, start, end, direction='reverse',
1631
1754
                             b, '1', '3', exclude_common_ancestry=True,
1632
1755
                             generate_merge_revisions=True)
1633
1756
 
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.assertEqual(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.assertEqual(len(log_formatter.revisions), 2)
1672