~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Jelmer Vernooij
  • Date: 2016-04-03 16:32:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6617.
  • Revision ID: jelmer@jelmer.uk-20160403163231-h72bo0uyek2gikw0
Don't put French text in doc/en/user-reference when LANGUAGE=fr_CH.UTF_8.

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
24
24
    registry,
25
25
    revision,
26
26
    revisionspec,
27
 
    symbol_versioning,
28
27
    tests,
 
28
    gpg,
 
29
    trace,
29
30
    )
30
31
 
31
32
 
251
252
        wt.commit(message='add file1 and file2')
252
253
        self.run_bzr('branch parent child')
253
254
        os.unlink('child/file1')
254
 
        file('child/file2', 'wb').write('hello\n')
 
255
        with file('child/file2', 'wb') as f: f.write('hello\n')
255
256
        self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
256
257
            'child'])
257
258
        os.chdir('parent')
282
283
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
283
284
 
284
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
 
285
318
class TestShortLogFormatter(TestCaseForLogFormatter):
286
319
 
287
320
    def test_trailing_newlines(self):
323
356
    1 Joe Foo\t2005-11-22
324
357
      rev-1
325
358
 
326
 
Use --include-merges or -n0 to see merged revisions.
 
359
Use --include-merged or -n0 to see merged revisions.
327
360
""",
328
361
            wt.branch, log.ShortLogFormatter,
329
362
            formatter_kwargs=dict(show_advice=True))
983
1016
            wt.branch, log.GnuChangelogLogFormatter,
984
1017
            show_log_kwargs=dict(verbose=True))
985
1018
 
986
 
class TestGetViewRevisions(tests.TestCaseWithTransport, TestLogMixin):
987
 
 
988
 
    def _get_view_revisions(self, *args, **kwargs):
989
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
990
 
                                    log.get_view_revisions, *args, **kwargs)
991
 
 
992
 
    def make_tree_with_commits(self):
993
 
        """Create a tree with well-known revision ids"""
994
 
        wt = self.make_branch_and_tree('tree1')
995
 
        self.wt_commit(wt, 'commit one', rev_id='1')
996
 
        self.wt_commit(wt, 'commit two', rev_id='2')
997
 
        self.wt_commit(wt, 'commit three', rev_id='3')
998
 
        mainline_revs = [None, '1', '2', '3']
999
 
        rev_nos = {'1': 1, '2': 2, '3': 3}
1000
 
        return mainline_revs, rev_nos, wt
1001
 
 
1002
 
    def make_tree_with_merges(self):
1003
 
        """Create a tree with well-known revision ids and a merge"""
1004
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1005
 
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1006
 
        self.wt_commit(tree2, 'four-a', rev_id='4a')
1007
 
        wt.merge_from_branch(tree2.branch)
1008
 
        self.wt_commit(wt, 'four-b', rev_id='4b')
1009
 
        mainline_revs.append('4b')
1010
 
        rev_nos['4b'] = 4
1011
 
        # 4a: 3.1.1
1012
 
        return mainline_revs, rev_nos, wt
1013
 
 
1014
 
    def make_branch_with_many_merges(self):
1015
 
        """Create a tree with well-known revision ids"""
1016
 
        builder = self.make_branch_builder('tree1')
1017
 
        builder.start_series()
1018
 
        builder.build_snapshot('1', None, [
1019
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),
1020
 
            ('add', ('f', 'f-id', 'file', '1\n'))])
1021
 
        builder.build_snapshot('2', ['1'], [])
1022
 
        builder.build_snapshot('3a', ['2'], [
1023
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
1024
 
        builder.build_snapshot('3b', ['2', '3a'], [
1025
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
1026
 
        builder.build_snapshot('3c', ['2', '3b'], [
1027
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
1028
 
        builder.build_snapshot('4a', ['3b'], [])
1029
 
        builder.build_snapshot('4b', ['3c', '4a'], [])
1030
 
        builder.finish_series()
1031
 
 
1032
 
        # 1
1033
 
        # |
1034
 
        # 2-.
1035
 
        # |\ \
1036
 
        # | | 3a
1037
 
        # | |/
1038
 
        # | 3b
1039
 
        # |/|
1040
 
        # 3c4a
1041
 
        # |/
1042
 
        # 4b
1043
 
 
1044
 
        mainline_revs = [None, '1', '2', '3c', '4b']
1045
 
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
1046
 
        full_rev_nos_for_reference = {
1047
 
            '1': '1',
1048
 
            '2': '2',
1049
 
            '3a': '2.1.1', #first commit tree 3
1050
 
            '3b': '2.2.1', # first commit tree 2
1051
 
            '3c': '3', #merges 3b to main
1052
 
            '4a': '2.2.2', # second commit tree 2
1053
 
            '4b': '4', # merges 4a to main
1054
 
            }
1055
 
        return mainline_revs, rev_nos, builder.get_branch()
1056
 
 
1057
 
    def test_get_view_revisions_forward(self):
1058
 
        """Test the get_view_revisions method"""
1059
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1060
 
        wt.lock_read()
1061
 
        self.addCleanup(wt.unlock)
1062
 
        revisions = list(self._get_view_revisions(
1063
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1064
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
1065
 
                         revisions)
1066
 
        revisions2 = list(self._get_view_revisions(
1067
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1068
 
                include_merges=False))
1069
 
        self.assertEqual(revisions, revisions2)
1070
 
 
1071
 
    def test_get_view_revisions_reverse(self):
1072
 
        """Test the get_view_revisions with reverse"""
1073
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1074
 
        wt.lock_read()
1075
 
        self.addCleanup(wt.unlock)
1076
 
        revisions = list(self._get_view_revisions(
1077
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1078
 
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1079
 
                         revisions)
1080
 
        revisions2 = list(self._get_view_revisions(
1081
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1082
 
                include_merges=False))
1083
 
        self.assertEqual(revisions, revisions2)
1084
 
 
1085
 
    def test_get_view_revisions_merge(self):
1086
 
        """Test get_view_revisions when there are merges"""
1087
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1088
 
        wt.lock_read()
1089
 
        self.addCleanup(wt.unlock)
1090
 
        revisions = list(self._get_view_revisions(
1091
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1092
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1093
 
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
1094
 
                         revisions)
1095
 
        revisions = list(self._get_view_revisions(
1096
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1097
 
                include_merges=False))
1098
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1099
 
                          ('4b', '4', 0)],
1100
 
                         revisions)
1101
 
 
1102
 
    def test_get_view_revisions_merge_reverse(self):
1103
 
        """Test get_view_revisions in reverse when there are merges"""
1104
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1105
 
        wt.lock_read()
1106
 
        self.addCleanup(wt.unlock)
1107
 
        revisions = list(self._get_view_revisions(
1108
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1109
 
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1110
 
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1111
 
                         revisions)
1112
 
        revisions = list(self._get_view_revisions(
1113
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1114
 
                include_merges=False))
1115
 
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1116
 
                          ('1', '1', 0)],
1117
 
                         revisions)
1118
 
 
1119
 
    def test_get_view_revisions_merge2(self):
1120
 
        """Test get_view_revisions when there are merges"""
1121
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1122
 
        b.lock_read()
1123
 
        self.addCleanup(b.unlock)
1124
 
        revisions = list(self._get_view_revisions(
1125
 
                mainline_revs, rev_nos, b, 'forward'))
1126
 
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1127
 
                    ('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
1128
 
                    ('4a', '2.2.2', 1)]
1129
 
        self.assertEqual(expected, revisions)
1130
 
        revisions = list(self._get_view_revisions(
1131
 
                mainline_revs, rev_nos, b, 'forward',
1132
 
                include_merges=False))
1133
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1134
 
                          ('4b', '4', 0)],
1135
 
                         revisions)
1136
 
 
1137
 
    def test_file_id_for_range(self):
1138
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1139
 
        b.lock_read()
1140
 
        self.addCleanup(b.unlock)
1141
 
 
1142
 
        def rev_from_rev_id(revid, branch):
1143
 
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1144
 
            return revspec.in_history(branch)
1145
 
 
1146
 
        def view_revs(start_rev, end_rev, file_id, direction):
1147
 
            revs = self.applyDeprecated(
1148
 
                symbol_versioning.deprecated_in((2, 2, 0)),
1149
 
                log.calculate_view_revisions,
1150
 
                b,
1151
 
                start_rev, # start_revision
1152
 
                end_rev, # end_revision
1153
 
                direction, # direction
1154
 
                file_id, # specific_fileid
1155
 
                True, # generate_merge_revisions
1156
 
                )
1157
 
            return revs
1158
 
 
1159
 
        rev_3a = rev_from_rev_id('3a', b)
1160
 
        rev_4b = rev_from_rev_id('4b', b)
1161
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1162
 
                          ('3a', '2.1.1', 2)],
1163
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1164
 
        # Note: 3c still appears before 3a here because of depth-based sorting
1165
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1166
 
                          ('3a', '2.1.1', 2)],
1167
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1168
 
 
1169
 
 
1170
 
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
1171
 
 
1172
 
    def get_view_revisions(self, *args):
1173
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
1174
 
                                    log.get_view_revisions, *args)
1175
 
 
1176
 
    def create_tree_with_single_merge(self):
1177
 
        """Create a branch with a moderate layout.
1178
 
 
1179
 
        The revision graph looks like:
1180
 
 
1181
 
           A
1182
 
           |\
1183
 
           B C
1184
 
           |/
1185
 
           D
1186
 
 
1187
 
        In this graph, A introduced files f1 and f2 and f3.
1188
 
        B modifies f1 and f3, and C modifies f2 and f3.
1189
 
        D merges the changes from B and C and resolves the conflict for f3.
1190
 
        """
1191
 
        # TODO: jam 20070218 This seems like it could really be done
1192
 
        #       with make_branch_and_memory_tree() if we could just
1193
 
        #       create the content of those files.
1194
 
        # TODO: jam 20070218 Another alternative is that we would really
1195
 
        #       like to only create this tree 1 time for all tests that
1196
 
        #       use it. Since 'log' only uses the tree in a readonly
1197
 
        #       fashion, it seems a shame to regenerate an identical
1198
 
        #       tree for each test.
1199
 
        # TODO: vila 20100122 One way to address the shame above will be to
1200
 
        #       create a memory tree during test parametrization and give a
1201
 
        #       *copy* of this tree to each test. Copying a memory tree ought
1202
 
        #       to be cheap, at least cheaper than creating them with such
1203
 
        #       complex setups.
1204
 
        tree = self.make_branch_and_tree('tree')
1205
 
        tree.lock_write()
1206
 
        self.addCleanup(tree.unlock)
1207
 
 
1208
 
        self.build_tree_contents([('tree/f1', 'A\n'),
1209
 
                                  ('tree/f2', 'A\n'),
1210
 
                                  ('tree/f3', 'A\n'),
1211
 
                                 ])
1212
 
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
1213
 
        tree.commit('A', rev_id='A')
1214
 
 
1215
 
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
1216
 
                                  ('tree/f3', 'A\nC\n'),
1217
 
                                 ])
1218
 
        tree.commit('C', rev_id='C')
1219
 
        # Revert back to A to build the other history.
1220
 
        tree.set_last_revision('A')
1221
 
        tree.branch.set_last_revision_info(1, 'A')
1222
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1223
 
                                  ('tree/f2', 'A\n'),
1224
 
                                  ('tree/f3', 'A\nB\n'),
1225
 
                                 ])
1226
 
        tree.commit('B', rev_id='B')
1227
 
        tree.set_parent_ids(['B', 'C'])
1228
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1229
 
                                  ('tree/f2', 'A\nC\n'),
1230
 
                                  ('tree/f3', 'A\nB\nC\n'),
1231
 
                                 ])
1232
 
        tree.commit('D', rev_id='D')
1233
 
 
1234
 
        # Switch to a read lock for this tree.
1235
 
        # We still have an addCleanup(tree.unlock) pending
1236
 
        tree.unlock()
1237
 
        tree.lock_read()
1238
 
        return tree
1239
 
 
1240
 
    def check_delta(self, delta, **kw):
1241
 
        """Check the filenames touched by a delta are as expected.
1242
 
 
1243
 
        Caller only have to pass in the list of files for each part, all
1244
 
        unspecified parts are considered empty (and checked as such).
1245
 
        """
1246
 
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1247
 
            # By default we expect an empty list
1248
 
            expected = kw.get(n, [])
1249
 
            # strip out only the path components
1250
 
            got = [x[0] for x in getattr(delta, n)]
1251
 
            self.assertEqual(expected, got)
1252
 
 
1253
 
    def test_tree_with_single_merge(self):
1254
 
        """Make sure the tree layout is correct."""
1255
 
        tree = self.create_tree_with_single_merge()
1256
 
        rev_A_tree = tree.branch.repository.revision_tree('A')
1257
 
        rev_B_tree = tree.branch.repository.revision_tree('B')
1258
 
        rev_C_tree = tree.branch.repository.revision_tree('C')
1259
 
        rev_D_tree = tree.branch.repository.revision_tree('D')
1260
 
 
1261
 
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1262
 
                         modified=['f1', 'f3'])
1263
 
 
1264
 
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1265
 
                         modified=['f2', 'f3'])
1266
 
 
1267
 
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1268
 
                         modified=['f2', 'f3'])
1269
 
 
1270
 
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1271
 
                         modified=['f1', 'f3'])
1272
 
 
1273
 
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1274
 
        """Ensure _filter_revisions_touching_file_id returns the right values.
1275
 
 
1276
 
        Get the return value from _filter_revisions_touching_file_id and make
1277
 
        sure they are correct.
1278
 
        """
1279
 
        # The api for _filter_revisions_touching_file_id is a little crazy.
1280
 
        # So we do the setup here.
1281
 
        mainline = tree.branch.revision_history()
1282
 
        mainline.insert(0, None)
1283
 
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
1284
 
        view_revs_iter = self.get_view_revisions(
1285
 
            mainline, revnos, tree.branch, 'reverse', True)
1286
 
        actual_revs = log._filter_revisions_touching_file_id(
1287
 
            tree.branch, file_id, list(view_revs_iter))
1288
 
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1289
 
 
1290
 
    def test_file_id_f1(self):
1291
 
        tree = self.create_tree_with_single_merge()
1292
 
        # f1 should be marked as modified by revisions A and B
1293
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1294
 
 
1295
 
    def test_file_id_f2(self):
1296
 
        tree = self.create_tree_with_single_merge()
1297
 
        # f2 should be marked as modified by revisions A, C, and D
1298
 
        # because D merged the changes from C.
1299
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1300
 
 
1301
 
    def test_file_id_f3(self):
1302
 
        tree = self.create_tree_with_single_merge()
1303
 
        # f3 should be marked as modified by revisions A, B, C, and D
1304
 
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1305
 
 
1306
 
    def test_file_id_with_ghosts(self):
1307
 
        # This is testing bug #209948, where having a ghost would cause
1308
 
        # _filter_revisions_touching_file_id() to fail.
1309
 
        tree = self.create_tree_with_single_merge()
1310
 
        # We need to add a revision, so switch back to a write-locked tree
1311
 
        # (still a single addCleanup(tree.unlock) pending).
1312
 
        tree.unlock()
1313
 
        tree.lock_write()
1314
 
        first_parent = tree.last_revision()
1315
 
        tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1316
 
        self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1317
 
        tree.commit('commit with a ghost', rev_id='XX')
1318
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1319
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1320
 
 
1321
 
    def test_unknown_file_id(self):
1322
 
        tree = self.create_tree_with_single_merge()
1323
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1324
 
 
1325
 
    def test_empty_branch_unknown_file_id(self):
1326
 
        tree = self.make_branch_and_tree('tree')
1327
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1328
 
 
1329
1019
 
1330
1020
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1331
1021
 
1653
1343
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1654
1344
 
1655
1345
    def setUp(self):
1656
 
        TestCaseForLogFormatter.setUp(self)
 
1346
        super(TestLogWithBugs, self).setUp()
1657
1347
        log.properties_handler_registry.register(
1658
1348
            'bugs_properties_handler',
1659
1349
            log._bugs_properties_handler)
1678
1368
        self.assertFormatterResult("""\
1679
1369
------------------------------------------------------------
1680
1370
revno: 2
1681
 
fixes bug(s): test://bug/id test://bug/2
 
1371
fixes bugs: test://bug/id test://bug/2
1682
1372
author: Joe Bar <joe@bar.com>
1683
1373
committer: Joe Foo <joe@foo.com>
1684
1374
branch nick: work
1689
1379
  message
1690
1380
------------------------------------------------------------
1691
1381
revno: 1
1692
 
fixes bug(s): test://bug/id
 
1382
fixes bug: test://bug/id
1693
1383
committer: Joe Foo <joe@foo.com>
1694
1384
branch nick: work
1695
1385
timestamp: Tue 2005-11-22 00:00:00 +0000
1702
1392
        tree = self.make_commits_with_bugs()
1703
1393
        self.assertFormatterResult("""\
1704
1394
    2 Joe Bar\t2005-11-22
1705
 
      fixes bug(s): test://bug/id test://bug/2
 
1395
      fixes bugs: test://bug/id test://bug/2
1706
1396
      multiline
1707
1397
      log
1708
1398
      message
1709
1399
 
1710
1400
    1 Joe Foo\t2005-11-22
1711
 
      fixes bug(s): test://bug/id
 
1401
      fixes bug: test://bug/id
1712
1402
      simple log message
1713
1403
 
1714
1404
""",
1733
1423
class TestLogForAuthors(TestCaseForLogFormatter):
1734
1424
 
1735
1425
    def setUp(self):
1736
 
        TestCaseForLogFormatter.setUp(self)
 
1426
        super(TestLogForAuthors, self).setUp()
1737
1427
        self.wt = self.make_standard_commit('nicky',
1738
1428
            authors=['John Doe <jdoe@example.com>',
1739
1429
                     'Jane Rey <jrey@example.com>'])
1880
1570
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1881
1571
        # See test_merge_sorted_exclude_ancestry below for the difference with
1882
1572
        # bt.per_branch.test_iter_merge_sorted_revision.
1883
 
        # TestIterMergeSortedRevisionsBushyGraph. 
 
1573
        # TestIterMergeSortedRevisionsBushyGraph.
1884
1574
        # make_branch_with_alternate_ancestries
1885
1575
        # and test_merge_sorted_exclude_ancestry
1886
1576
        # See the FIXME in assertLogRevnos too.
1913
1603
    def assertLogRevnos(self, expected_revnos, b, start, end,
1914
1604
                        exclude_common_ancestry, generate_merge_revisions=True):
1915
1605
        # FIXME: the layering in log makes it hard to test intermediate levels,
1916
 
        # I wish adding filters with their parameters were easier...
 
1606
        # I wish adding filters with their parameters was easier...
1917
1607
        # -- vila 20100413
1918
1608
        iter_revs = log._calc_view_revisions(
1919
1609
            b, start, end, direction='reverse',
1941
1631
                             b, '1', '3', exclude_common_ancestry=True,
1942
1632
                             generate_merge_revisions=True)
1943
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.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