/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/tests/test_log.py

  • Committer: Jelmer Vernooij
  • Date: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2013, 2016 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
import os
 
18
 
 
19
from .. import (
 
20
    branchbuilder,
 
21
    errors,
 
22
    log,
 
23
    registry,
 
24
    revision,
 
25
    revisionspec,
 
26
    tests,
 
27
    gpg,
 
28
    trace,
 
29
    )
 
30
from ..sixish import (
 
31
    BytesIO,
 
32
    )
 
33
 
 
34
 
 
35
class TestLogMixin(object):
 
36
 
 
37
    def wt_commit(self, wt, message, **kwargs):
 
38
        """Use some mostly fixed values for commits to simplify tests.
 
39
 
 
40
        Tests can use this function to get some commit attributes. The time
 
41
        stamp is incremented at each commit.
 
42
        """
 
43
        if getattr(self, 'timestamp', None) is None:
 
44
            self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
 
45
        else:
 
46
            self.timestamp += 1 # 1 second between each commit
 
47
        kwargs.setdefault('timestamp', self.timestamp)
 
48
        kwargs.setdefault('timezone', 0) # UTC
 
49
        kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
 
50
 
 
51
        return wt.commit(message, **kwargs)
 
52
 
 
53
 
 
54
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
 
55
 
 
56
    def setUp(self):
 
57
        super(TestCaseForLogFormatter, self).setUp()
 
58
        # keep a reference to the "current" custom prop. handler registry
 
59
        self.properties_handler_registry = log.properties_handler_registry
 
60
        # Use a clean registry for log
 
61
        log.properties_handler_registry = registry.Registry()
 
62
 
 
63
        def restore():
 
64
            log.properties_handler_registry = self.properties_handler_registry
 
65
        self.addCleanup(restore)
 
66
 
 
67
    def assertFormatterResult(self, result, branch, formatter_class,
 
68
                              formatter_kwargs=None, show_log_kwargs=None):
 
69
        logfile = self.make_utf8_encoded_stringio()
 
70
        if formatter_kwargs is None:
 
71
            formatter_kwargs = {}
 
72
        formatter = formatter_class(to_file=logfile, **formatter_kwargs)
 
73
        if show_log_kwargs is None:
 
74
            show_log_kwargs = {}
 
75
        log.show_log(branch, formatter, **show_log_kwargs)
 
76
        self.assertEqualDiff(result, logfile.getvalue())
 
77
 
 
78
    def make_standard_commit(self, branch_nick, **kwargs):
 
79
        wt = self.make_branch_and_tree('.')
 
80
        wt.lock_write()
 
81
        self.addCleanup(wt.unlock)
 
82
        self.build_tree(['a'])
 
83
        wt.add(['a'])
 
84
        wt.branch.nick = branch_nick
 
85
        kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
 
86
        kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
 
87
        self.wt_commit(wt, 'add a', **kwargs)
 
88
        return wt
 
89
 
 
90
    def make_commits_with_trailing_newlines(self, wt):
 
91
        """Helper method for LogFormatter tests"""
 
92
        b = wt.branch
 
93
        b.nick = 'test'
 
94
        self.build_tree_contents([('a', 'hello moto\n')])
 
95
        self.wt_commit(wt, 'simple log message', rev_id='a1')
 
96
        self.build_tree_contents([('b', 'goodbye\n')])
 
97
        wt.add('b')
 
98
        self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
 
99
 
 
100
        self.build_tree_contents([('c', 'just another manic monday\n')])
 
101
        wt.add('c')
 
102
        self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
 
103
        return b
 
104
 
 
105
    def _prepare_tree_with_merges(self, with_tags=False):
 
106
        wt = self.make_branch_and_memory_tree('.')
 
107
        wt.lock_write()
 
108
        self.addCleanup(wt.unlock)
 
109
        wt.add('')
 
110
        self.wt_commit(wt, 'rev-1', rev_id='rev-1')
 
111
        self.wt_commit(wt, 'rev-merged', rev_id='rev-2a')
 
112
        wt.set_parent_ids(['rev-1', 'rev-2a'])
 
113
        wt.branch.set_last_revision_info(1, 'rev-1')
 
114
        self.wt_commit(wt, 'rev-2', rev_id='rev-2b')
 
115
        if with_tags:
 
116
            branch = wt.branch
 
117
            branch.tags.set_tag('v0.2', 'rev-2b')
 
118
            self.wt_commit(wt, 'rev-3', rev_id='rev-3')
 
119
            branch.tags.set_tag('v1.0rc1', 'rev-3')
 
120
            branch.tags.set_tag('v1.0', 'rev-3')
 
121
        return wt
 
122
 
 
123
 
 
124
class LogCatcher(log.LogFormatter):
 
125
    """Pull log messages into a list rather than displaying them.
 
126
 
 
127
    To simplify testing we save logged revisions here rather than actually
 
128
    formatting anything, so that we can precisely check the result without
 
129
    being dependent on the formatting.
 
130
    """
 
131
 
 
132
    supports_merge_revisions = True
 
133
    supports_delta = True
 
134
    supports_diff = True
 
135
    preferred_levels = 0
 
136
 
 
137
    def __init__(self, *args, **kwargs):
 
138
        kwargs.update(dict(to_file=None))
 
139
        super(LogCatcher, self).__init__(*args, **kwargs)
 
140
        self.revisions = []
 
141
 
 
142
    def log_revision(self, revision):
 
143
        self.revisions.append(revision)
 
144
 
 
145
 
 
146
class TestShowLog(tests.TestCaseWithTransport):
 
147
 
 
148
    def checkDelta(self, delta, **kw):
 
149
        """Check the filenames touched by a delta are as expected.
 
150
 
 
151
        Caller only have to pass in the list of files for each part, all
 
152
        unspecified parts are considered empty (and checked as such).
 
153
        """
 
154
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
 
155
            # By default we expect an empty list
 
156
            expected = kw.get(n, [])
 
157
            # strip out only the path components
 
158
            got = [x[0] for x in getattr(delta, n)]
 
159
            self.assertEqual(expected, got)
 
160
 
 
161
    def assertInvalidRevisonNumber(self, br, start, end):
 
162
        lf = LogCatcher()
 
163
        self.assertRaises(errors.InvalidRevisionNumber,
 
164
                          log.show_log, br, lf,
 
165
                          start_revision=start, end_revision=end)
 
166
 
 
167
    def test_cur_revno(self):
 
168
        wt = self.make_branch_and_tree('.')
 
169
        b = wt.branch
 
170
 
 
171
        lf = LogCatcher()
 
172
        wt.commit('empty commit')
 
173
        log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
 
174
 
 
175
        # Since there is a single revision in the branch all the combinations
 
176
        # below should fail.
 
177
        self.assertInvalidRevisonNumber(b, 2, 1)
 
178
        self.assertInvalidRevisonNumber(b, 1, 2)
 
179
        self.assertInvalidRevisonNumber(b, 0, 2)
 
180
        self.assertInvalidRevisonNumber(b, 1, 0)
 
181
        self.assertInvalidRevisonNumber(b, -1, 1)
 
182
        self.assertInvalidRevisonNumber(b, 1, -1)
 
183
 
 
184
    def test_empty_branch(self):
 
185
        wt = self.make_branch_and_tree('.')
 
186
 
 
187
        lf = LogCatcher()
 
188
        log.show_log(wt.branch, lf)
 
189
        # no entries yet
 
190
        self.assertEqual([], lf.revisions)
 
191
 
 
192
    def test_empty_commit(self):
 
193
        wt = self.make_branch_and_tree('.')
 
194
 
 
195
        wt.commit('empty commit')
 
196
        lf = LogCatcher()
 
197
        log.show_log(wt.branch, lf, verbose=True)
 
198
        revs = lf.revisions
 
199
        self.assertEqual(1, len(revs))
 
200
        self.assertEqual('1', revs[0].revno)
 
201
        self.assertEqual('empty commit', revs[0].rev.message)
 
202
        self.checkDelta(revs[0].delta)
 
203
 
 
204
    def test_simple_commit(self):
 
205
        wt = self.make_branch_and_tree('.')
 
206
        wt.commit('empty commit')
 
207
        self.build_tree(['hello'])
 
208
        wt.add('hello')
 
209
        wt.commit('add one file',
 
210
                  committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
 
211
                            u'<test@example.com>')
 
212
        lf = LogCatcher()
 
213
        log.show_log(wt.branch, lf, verbose=True)
 
214
        self.assertEqual(2, len(lf.revisions))
 
215
        # first one is most recent
 
216
        log_entry = lf.revisions[0]
 
217
        self.assertEqual('2', log_entry.revno)
 
218
        self.assertEqual('add one file', log_entry.rev.message)
 
219
        self.checkDelta(log_entry.delta, added=['hello'])
 
220
 
 
221
    def test_commit_message_with_control_chars(self):
 
222
        wt = self.make_branch_and_tree('.')
 
223
        msg = u"All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
 
224
        msg = msg.replace(u'\r', u'\n')
 
225
        wt.commit(msg)
 
226
        lf = LogCatcher()
 
227
        log.show_log(wt.branch, lf, verbose=True)
 
228
        committed_msg = lf.revisions[0].rev.message
 
229
        if wt.branch.repository._serializer.squashes_xml_invalid_characters:
 
230
            self.assertNotEqual(msg, committed_msg)
 
231
            self.assertTrue(len(committed_msg) > len(msg))
 
232
        else:
 
233
            self.assertEqual(msg, committed_msg)
 
234
 
 
235
    def test_commit_message_without_control_chars(self):
 
236
        wt = self.make_branch_and_tree('.')
 
237
        # escaped.  As ElementTree apparently does some kind of
 
238
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
 
239
        # included in the test commit message, even though they are
 
240
        # valid XML 1.0 characters.
 
241
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
 
242
        wt.commit(msg)
 
243
        lf = LogCatcher()
 
244
        log.show_log(wt.branch, lf, verbose=True)
 
245
        committed_msg = lf.revisions[0].rev.message
 
246
        self.assertEqual(msg, committed_msg)
 
247
 
 
248
    def test_deltas_in_merge_revisions(self):
 
249
        """Check deltas created for both mainline and merge revisions"""
 
250
        wt = self.make_branch_and_tree('parent')
 
251
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
 
252
        wt.add('file1')
 
253
        wt.add('file2')
 
254
        wt.commit(message='add file1 and file2')
 
255
        self.run_bzr('branch parent child')
 
256
        os.unlink('child/file1')
 
257
        with file('child/file2', 'wb') as f: f.write('hello\n')
 
258
        self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
 
259
            'child'])
 
260
        os.chdir('parent')
 
261
        self.run_bzr('merge ../child')
 
262
        wt.commit('merge child branch')
 
263
        os.chdir('..')
 
264
        b = wt.branch
 
265
        lf = LogCatcher()
 
266
        lf.supports_merge_revisions = True
 
267
        log.show_log(b, lf, verbose=True)
 
268
 
 
269
        revs = lf.revisions
 
270
        self.assertEqual(3, len(revs))
 
271
 
 
272
        logentry = revs[0]
 
273
        self.assertEqual('2', logentry.revno)
 
274
        self.assertEqual('merge child branch', logentry.rev.message)
 
275
        self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
 
276
 
 
277
        logentry = revs[1]
 
278
        self.assertEqual('1.1.1', logentry.revno)
 
279
        self.assertEqual('remove file1 and modify file2', logentry.rev.message)
 
280
        self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
 
281
 
 
282
        logentry = revs[2]
 
283
        self.assertEqual('1', logentry.revno)
 
284
        self.assertEqual('add file1 and file2', logentry.rev.message)
 
285
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
 
286
 
 
287
 
 
288
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
 
289
    class UTFLoopbackGPGStrategy(gpg.LoopbackGPGStrategy):
 
290
        def verify(self, content, testament):
 
291
            return (gpg.SIGNATURE_VALID,
 
292
                u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
 
293
 
 
294
    def has_signature_for_revision_id(self, revision_id):
 
295
        return True
 
296
 
 
297
    def get_signature_text(self, revision_id):
 
298
        return ''
 
299
 
 
300
    def test_format_signature_validity_utf(self):
 
301
        """Check that GPG signatures containing UTF-8 names are formatted
 
302
        correctly."""
 
303
        # Monkey patch to use our UTF-8 generating GPGStrategy
 
304
        self.overrideAttr(gpg, 'GPGStrategy', self.UTFLoopbackGPGStrategy)
 
305
        wt = self.make_branch_and_tree('.')
 
306
        revid = wt.commit('empty commit')
 
307
        repo = wt.branch.repository
 
308
        # Monkey patch out checking if this rev is actually signed, since we
 
309
        # can't sign it without a heavier TestCase and LoopbackGPGStrategy
 
310
        # doesn't care anyways.
 
311
        self.overrideAttr(repo, 'has_signature_for_revision_id',
 
312
                self.has_signature_for_revision_id)
 
313
        self.overrideAttr(repo, 'get_signature_text', self.get_signature_text)
 
314
        out = log.format_signature_validity(revid, repo)
 
315
        self.assertEqual(
 
316
u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
 
317
                out)
 
318
 
 
319
 
 
320
class TestShortLogFormatter(TestCaseForLogFormatter):
 
321
 
 
322
    def test_trailing_newlines(self):
 
323
        wt = self.make_branch_and_tree('.')
 
324
        b = self.make_commits_with_trailing_newlines(wt)
 
325
        self.assertFormatterResult("""\
 
326
    3 Joe Foo\t2005-11-22
 
327
      single line with trailing newline
 
328
 
 
329
    2 Joe Foo\t2005-11-22
 
330
      multiline
 
331
      log
 
332
      message
 
333
 
 
334
    1 Joe Foo\t2005-11-22
 
335
      simple log message
 
336
 
 
337
""",
 
338
            b, log.ShortLogFormatter)
 
339
 
 
340
    def test_short_log_with_merges(self):
 
341
        wt = self._prepare_tree_with_merges()
 
342
        self.assertFormatterResult("""\
 
343
    2 Joe Foo\t2005-11-22 [merge]
 
344
      rev-2
 
345
 
 
346
    1 Joe Foo\t2005-11-22
 
347
      rev-1
 
348
 
 
349
""",
 
350
            wt.branch, log.ShortLogFormatter)
 
351
 
 
352
    def test_short_log_with_merges_and_advice(self):
 
353
        wt = self._prepare_tree_with_merges()
 
354
        self.assertFormatterResult("""\
 
355
    2 Joe Foo\t2005-11-22 [merge]
 
356
      rev-2
 
357
 
 
358
    1 Joe Foo\t2005-11-22
 
359
      rev-1
 
360
 
 
361
Use --include-merged or -n0 to see merged revisions.
 
362
""",
 
363
            wt.branch, log.ShortLogFormatter,
 
364
            formatter_kwargs=dict(show_advice=True))
 
365
 
 
366
    def test_short_log_with_merges_and_range(self):
 
367
        wt = self._prepare_tree_with_merges()
 
368
        self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
 
369
        wt.branch.set_last_revision_info(2, 'rev-2b')
 
370
        wt.set_parent_ids(['rev-2b', 'rev-3a'])
 
371
        self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
 
372
        self.assertFormatterResult("""\
 
373
    3 Joe Foo\t2005-11-22 [merge]
 
374
      rev-3b
 
375
 
 
376
    2 Joe Foo\t2005-11-22 [merge]
 
377
      rev-2
 
378
 
 
379
""",
 
380
            wt.branch, log.ShortLogFormatter,
 
381
            show_log_kwargs=dict(start_revision=2, end_revision=3))
 
382
 
 
383
    def test_short_log_with_tags(self):
 
384
        wt = self._prepare_tree_with_merges(with_tags=True)
 
385
        self.assertFormatterResult("""\
 
386
    3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
 
387
      rev-3
 
388
 
 
389
    2 Joe Foo\t2005-11-22 {v0.2} [merge]
 
390
      rev-2
 
391
 
 
392
    1 Joe Foo\t2005-11-22
 
393
      rev-1
 
394
 
 
395
""",
 
396
            wt.branch, log.ShortLogFormatter)
 
397
 
 
398
    def test_short_log_single_merge_revision(self):
 
399
        wt = self._prepare_tree_with_merges()
 
400
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
401
        rev = revspec.in_history(wt.branch)
 
402
        self.assertFormatterResult("""\
 
403
      1.1.1 Joe Foo\t2005-11-22
 
404
            rev-merged
 
405
 
 
406
""",
 
407
            wt.branch, log.ShortLogFormatter,
 
408
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
409
 
 
410
    def test_show_ids(self):
 
411
        wt = self.make_branch_and_tree('parent')
 
412
        self.build_tree(['parent/f1', 'parent/f2'])
 
413
        wt.add(['f1','f2'])
 
414
        self.wt_commit(wt, 'first post', rev_id='a')
 
415
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
416
        self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
 
417
        wt.merge_from_branch(child_wt.branch)
 
418
        self.wt_commit(wt, 'merge branch 1', rev_id='c')
 
419
        self.assertFormatterResult("""\
 
420
    2 Joe Foo\t2005-11-22 [merge]
 
421
      revision-id:c
 
422
      merge branch 1
 
423
 
 
424
          1.1.1 Joe Foo\t2005-11-22
 
425
                revision-id:b
 
426
                branch 1 changes
 
427
 
 
428
    1 Joe Foo\t2005-11-22
 
429
      revision-id:a
 
430
      first post
 
431
 
 
432
""",
 
433
            wt.branch, log.ShortLogFormatter,
 
434
            formatter_kwargs=dict(levels=0,show_ids=True))
 
435
 
 
436
 
 
437
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
 
438
 
 
439
    def test_short_merge_revs_log_with_merges(self):
 
440
        wt = self._prepare_tree_with_merges()
 
441
        # Note that the 1.1.1 indenting is in fact correct given that
 
442
        # the revision numbers are right justified within 5 characters
 
443
        # for mainline revnos and 9 characters for dotted revnos.
 
444
        self.assertFormatterResult("""\
 
445
    2 Joe Foo\t2005-11-22 [merge]
 
446
      rev-2
 
447
 
 
448
          1.1.1 Joe Foo\t2005-11-22
 
449
                rev-merged
 
450
 
 
451
    1 Joe Foo\t2005-11-22
 
452
      rev-1
 
453
 
 
454
""",
 
455
            wt.branch, log.ShortLogFormatter,
 
456
            formatter_kwargs=dict(levels=0))
 
457
 
 
458
    def test_short_merge_revs_log_single_merge_revision(self):
 
459
        wt = self._prepare_tree_with_merges()
 
460
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
461
        rev = revspec.in_history(wt.branch)
 
462
        self.assertFormatterResult("""\
 
463
      1.1.1 Joe Foo\t2005-11-22
 
464
            rev-merged
 
465
 
 
466
""",
 
467
            wt.branch, log.ShortLogFormatter,
 
468
            formatter_kwargs=dict(levels=0),
 
469
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
470
 
 
471
 
 
472
class TestLongLogFormatter(TestCaseForLogFormatter):
 
473
 
 
474
    def test_verbose_log(self):
 
475
        """Verbose log includes changed files
 
476
 
 
477
        bug #4676
 
478
        """
 
479
        wt = self.make_standard_commit('test_verbose_log', authors=[])
 
480
        self.assertFormatterResult('''\
 
481
------------------------------------------------------------
 
482
revno: 1
 
483
committer: Lorem Ipsum <test@example.com>
 
484
branch nick: test_verbose_log
 
485
timestamp: Tue 2005-11-22 00:00:00 +0000
 
486
message:
 
487
  add a
 
488
added:
 
489
  a
 
490
''',
 
491
            wt.branch, log.LongLogFormatter,
 
492
            show_log_kwargs=dict(verbose=True))
 
493
 
 
494
    def test_merges_are_indented_by_level(self):
 
495
        wt = self.make_branch_and_tree('parent')
 
496
        self.wt_commit(wt, 'first post')
 
497
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
498
        self.wt_commit(child_wt, 'branch 1')
 
499
        smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
 
500
        self.wt_commit(smallerchild_wt, 'branch 2')
 
501
        child_wt.merge_from_branch(smallerchild_wt.branch)
 
502
        self.wt_commit(child_wt, 'merge branch 2')
 
503
        wt.merge_from_branch(child_wt.branch)
 
504
        self.wt_commit(wt, 'merge branch 1')
 
505
        self.assertFormatterResult("""\
 
506
------------------------------------------------------------
 
507
revno: 2 [merge]
 
508
committer: Joe Foo <joe@foo.com>
 
509
branch nick: parent
 
510
timestamp: Tue 2005-11-22 00:00:04 +0000
 
511
message:
 
512
  merge branch 1
 
513
    ------------------------------------------------------------
 
514
    revno: 1.1.2 [merge]
 
515
    committer: Joe Foo <joe@foo.com>
 
516
    branch nick: child
 
517
    timestamp: Tue 2005-11-22 00:00:03 +0000
 
518
    message:
 
519
      merge branch 2
 
520
        ------------------------------------------------------------
 
521
        revno: 1.2.1
 
522
        committer: Joe Foo <joe@foo.com>
 
523
        branch nick: smallerchild
 
524
        timestamp: Tue 2005-11-22 00:00:02 +0000
 
525
        message:
 
526
          branch 2
 
527
    ------------------------------------------------------------
 
528
    revno: 1.1.1
 
529
    committer: Joe Foo <joe@foo.com>
 
530
    branch nick: child
 
531
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
532
    message:
 
533
      branch 1
 
534
------------------------------------------------------------
 
535
revno: 1
 
536
committer: Joe Foo <joe@foo.com>
 
537
branch nick: parent
 
538
timestamp: Tue 2005-11-22 00:00:00 +0000
 
539
message:
 
540
  first post
 
541
""",
 
542
            wt.branch, log.LongLogFormatter,
 
543
            formatter_kwargs=dict(levels=0),
 
544
            show_log_kwargs=dict(verbose=True))
 
545
 
 
546
    def test_verbose_merge_revisions_contain_deltas(self):
 
547
        wt = self.make_branch_and_tree('parent')
 
548
        self.build_tree(['parent/f1', 'parent/f2'])
 
549
        wt.add(['f1','f2'])
 
550
        self.wt_commit(wt, 'first post')
 
551
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
552
        os.unlink('child/f1')
 
553
        self.build_tree_contents([('child/f2', 'hello\n')])
 
554
        self.wt_commit(child_wt, 'removed f1 and modified f2')
 
555
        wt.merge_from_branch(child_wt.branch)
 
556
        self.wt_commit(wt, 'merge branch 1')
 
557
        self.assertFormatterResult("""\
 
558
------------------------------------------------------------
 
559
revno: 2 [merge]
 
560
committer: Joe Foo <joe@foo.com>
 
561
branch nick: parent
 
562
timestamp: Tue 2005-11-22 00:00:02 +0000
 
563
message:
 
564
  merge branch 1
 
565
removed:
 
566
  f1
 
567
modified:
 
568
  f2
 
569
    ------------------------------------------------------------
 
570
    revno: 1.1.1
 
571
    committer: Joe Foo <joe@foo.com>
 
572
    branch nick: child
 
573
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
574
    message:
 
575
      removed f1 and modified f2
 
576
    removed:
 
577
      f1
 
578
    modified:
 
579
      f2
 
580
------------------------------------------------------------
 
581
revno: 1
 
582
committer: Joe Foo <joe@foo.com>
 
583
branch nick: parent
 
584
timestamp: Tue 2005-11-22 00:00:00 +0000
 
585
message:
 
586
  first post
 
587
added:
 
588
  f1
 
589
  f2
 
590
""",
 
591
            wt.branch, log.LongLogFormatter,
 
592
            formatter_kwargs=dict(levels=0),
 
593
            show_log_kwargs=dict(verbose=True))
 
594
 
 
595
    def test_trailing_newlines(self):
 
596
        wt = self.make_branch_and_tree('.')
 
597
        b = self.make_commits_with_trailing_newlines(wt)
 
598
        self.assertFormatterResult("""\
 
599
------------------------------------------------------------
 
600
revno: 3
 
601
committer: Joe Foo <joe@foo.com>
 
602
branch nick: test
 
603
timestamp: Tue 2005-11-22 00:00:02 +0000
 
604
message:
 
605
  single line with trailing newline
 
606
------------------------------------------------------------
 
607
revno: 2
 
608
committer: Joe Foo <joe@foo.com>
 
609
branch nick: test
 
610
timestamp: Tue 2005-11-22 00:00:01 +0000
 
611
message:
 
612
  multiline
 
613
  log
 
614
  message
 
615
------------------------------------------------------------
 
616
revno: 1
 
617
committer: Joe Foo <joe@foo.com>
 
618
branch nick: test
 
619
timestamp: Tue 2005-11-22 00:00:00 +0000
 
620
message:
 
621
  simple log message
 
622
""",
 
623
        b, log.LongLogFormatter)
 
624
 
 
625
    def test_author_in_log(self):
 
626
        """Log includes the author name if it's set in
 
627
        the revision properties
 
628
        """
 
629
        wt = self.make_standard_commit('test_author_log',
 
630
            authors=['John Doe <jdoe@example.com>',
 
631
                     'Jane Rey <jrey@example.com>'])
 
632
        self.assertFormatterResult("""\
 
633
------------------------------------------------------------
 
634
revno: 1
 
635
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
636
committer: Lorem Ipsum <test@example.com>
 
637
branch nick: test_author_log
 
638
timestamp: Tue 2005-11-22 00:00:00 +0000
 
639
message:
 
640
  add a
 
641
""",
 
642
        wt.branch, log.LongLogFormatter)
 
643
 
 
644
    def test_properties_in_log(self):
 
645
        """Log includes the custom properties returned by the registered
 
646
        handlers.
 
647
        """
 
648
        wt = self.make_standard_commit('test_properties_in_log')
 
649
        def trivial_custom_prop_handler(revision):
 
650
            return {'test_prop':'test_value'}
 
651
 
 
652
        # Cleaned up in setUp()
 
653
        log.properties_handler_registry.register(
 
654
            'trivial_custom_prop_handler',
 
655
            trivial_custom_prop_handler)
 
656
        self.assertFormatterResult("""\
 
657
------------------------------------------------------------
 
658
revno: 1
 
659
test_prop: test_value
 
660
author: John Doe <jdoe@example.com>
 
661
committer: Lorem Ipsum <test@example.com>
 
662
branch nick: test_properties_in_log
 
663
timestamp: Tue 2005-11-22 00:00:00 +0000
 
664
message:
 
665
  add a
 
666
""",
 
667
            wt.branch, log.LongLogFormatter)
 
668
 
 
669
    def test_properties_in_short_log(self):
 
670
        """Log includes the custom properties returned by the registered
 
671
        handlers.
 
672
        """
 
673
        wt = self.make_standard_commit('test_properties_in_short_log')
 
674
        def trivial_custom_prop_handler(revision):
 
675
            return {'test_prop':'test_value'}
 
676
 
 
677
        log.properties_handler_registry.register(
 
678
            'trivial_custom_prop_handler',
 
679
            trivial_custom_prop_handler)
 
680
        self.assertFormatterResult("""\
 
681
    1 John Doe\t2005-11-22
 
682
      test_prop: test_value
 
683
      add a
 
684
 
 
685
""",
 
686
            wt.branch, log.ShortLogFormatter)
 
687
 
 
688
    def test_error_in_properties_handler(self):
 
689
        """Log includes the custom properties returned by the registered
 
690
        handlers.
 
691
        """
 
692
        wt = self.make_standard_commit('error_in_properties_handler',
 
693
            revprops={'first_prop':'first_value'})
 
694
        sio = self.make_utf8_encoded_stringio()
 
695
        formatter = log.LongLogFormatter(to_file=sio)
 
696
        def trivial_custom_prop_handler(revision):
 
697
            raise Exception("a test error")
 
698
 
 
699
        log.properties_handler_registry.register(
 
700
            'trivial_custom_prop_handler',
 
701
            trivial_custom_prop_handler)
 
702
        self.assertRaises(Exception, log.show_log, wt.branch, formatter,)
 
703
 
 
704
    def test_properties_handler_bad_argument(self):
 
705
        wt = self.make_standard_commit('bad_argument',
 
706
              revprops={'a_prop':'test_value'})
 
707
        sio = self.make_utf8_encoded_stringio()
 
708
        formatter = log.LongLogFormatter(to_file=sio)
 
709
        def bad_argument_prop_handler(revision):
 
710
            return {'custom_prop_name':revision.properties['a_prop']}
 
711
 
 
712
        log.properties_handler_registry.register(
 
713
            'bad_argument_prop_handler',
 
714
            bad_argument_prop_handler)
 
715
 
 
716
        self.assertRaises(AttributeError, formatter.show_properties,
 
717
                          'a revision', '')
 
718
 
 
719
        revision = wt.branch.repository.get_revision(wt.branch.last_revision())
 
720
        formatter.show_properties(revision, '')
 
721
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
 
722
                             sio.getvalue())
 
723
 
 
724
    def test_show_ids(self):
 
725
        wt = self.make_branch_and_tree('parent')
 
726
        self.build_tree(['parent/f1', 'parent/f2'])
 
727
        wt.add(['f1','f2'])
 
728
        self.wt_commit(wt, 'first post', rev_id='a')
 
729
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
730
        self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
 
731
        wt.merge_from_branch(child_wt.branch)
 
732
        self.wt_commit(wt, 'merge branch 1', rev_id='c')
 
733
        self.assertFormatterResult("""\
 
734
------------------------------------------------------------
 
735
revno: 2 [merge]
 
736
revision-id: c
 
737
parent: a
 
738
parent: b
 
739
committer: Joe Foo <joe@foo.com>
 
740
branch nick: parent
 
741
timestamp: Tue 2005-11-22 00:00:02 +0000
 
742
message:
 
743
  merge branch 1
 
744
    ------------------------------------------------------------
 
745
    revno: 1.1.1
 
746
    revision-id: b
 
747
    parent: a
 
748
    committer: Joe Foo <joe@foo.com>
 
749
    branch nick: child
 
750
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
751
    message:
 
752
      branch 1 changes
 
753
------------------------------------------------------------
 
754
revno: 1
 
755
revision-id: a
 
756
committer: Joe Foo <joe@foo.com>
 
757
branch nick: parent
 
758
timestamp: Tue 2005-11-22 00:00:00 +0000
 
759
message:
 
760
  first post
 
761
""",
 
762
            wt.branch, log.LongLogFormatter,
 
763
            formatter_kwargs=dict(levels=0,show_ids=True))
 
764
 
 
765
 
 
766
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
 
767
 
 
768
    def test_long_verbose_log(self):
 
769
        """Verbose log includes changed files
 
770
 
 
771
        bug #4676
 
772
        """
 
773
        wt = self.make_standard_commit('test_long_verbose_log', authors=[])
 
774
        self.assertFormatterResult("""\
 
775
------------------------------------------------------------
 
776
revno: 1
 
777
committer: Lorem Ipsum <test@example.com>
 
778
branch nick: test_long_verbose_log
 
779
timestamp: Tue 2005-11-22 00:00:00 +0000
 
780
message:
 
781
  add a
 
782
added:
 
783
  a
 
784
""",
 
785
            wt.branch, log.LongLogFormatter,
 
786
            formatter_kwargs=dict(levels=1),
 
787
            show_log_kwargs=dict(verbose=True))
 
788
 
 
789
    def test_long_verbose_contain_deltas(self):
 
790
        wt = self.make_branch_and_tree('parent')
 
791
        self.build_tree(['parent/f1', 'parent/f2'])
 
792
        wt.add(['f1','f2'])
 
793
        self.wt_commit(wt, 'first post')
 
794
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
795
        os.unlink('child/f1')
 
796
        self.build_tree_contents([('child/f2', 'hello\n')])
 
797
        self.wt_commit(child_wt, 'removed f1 and modified f2')
 
798
        wt.merge_from_branch(child_wt.branch)
 
799
        self.wt_commit(wt, 'merge branch 1')
 
800
        self.assertFormatterResult("""\
 
801
------------------------------------------------------------
 
802
revno: 2 [merge]
 
803
committer: Joe Foo <joe@foo.com>
 
804
branch nick: parent
 
805
timestamp: Tue 2005-11-22 00:00:02 +0000
 
806
message:
 
807
  merge branch 1
 
808
removed:
 
809
  f1
 
810
modified:
 
811
  f2
 
812
------------------------------------------------------------
 
813
revno: 1
 
814
committer: Joe Foo <joe@foo.com>
 
815
branch nick: parent
 
816
timestamp: Tue 2005-11-22 00:00:00 +0000
 
817
message:
 
818
  first post
 
819
added:
 
820
  f1
 
821
  f2
 
822
""",
 
823
            wt.branch, log.LongLogFormatter,
 
824
            formatter_kwargs=dict(levels=1),
 
825
            show_log_kwargs=dict(verbose=True))
 
826
 
 
827
    def test_long_trailing_newlines(self):
 
828
        wt = self.make_branch_and_tree('.')
 
829
        b = self.make_commits_with_trailing_newlines(wt)
 
830
        self.assertFormatterResult("""\
 
831
------------------------------------------------------------
 
832
revno: 3
 
833
committer: Joe Foo <joe@foo.com>
 
834
branch nick: test
 
835
timestamp: Tue 2005-11-22 00:00:02 +0000
 
836
message:
 
837
  single line with trailing newline
 
838
------------------------------------------------------------
 
839
revno: 2
 
840
committer: Joe Foo <joe@foo.com>
 
841
branch nick: test
 
842
timestamp: Tue 2005-11-22 00:00:01 +0000
 
843
message:
 
844
  multiline
 
845
  log
 
846
  message
 
847
------------------------------------------------------------
 
848
revno: 1
 
849
committer: Joe Foo <joe@foo.com>
 
850
branch nick: test
 
851
timestamp: Tue 2005-11-22 00:00:00 +0000
 
852
message:
 
853
  simple log message
 
854
""",
 
855
        b, log.LongLogFormatter,
 
856
        formatter_kwargs=dict(levels=1))
 
857
 
 
858
    def test_long_author_in_log(self):
 
859
        """Log includes the author name if it's set in
 
860
        the revision properties
 
861
        """
 
862
        wt = self.make_standard_commit('test_author_log')
 
863
        self.assertFormatterResult("""\
 
864
------------------------------------------------------------
 
865
revno: 1
 
866
author: John Doe <jdoe@example.com>
 
867
committer: Lorem Ipsum <test@example.com>
 
868
branch nick: test_author_log
 
869
timestamp: Tue 2005-11-22 00:00:00 +0000
 
870
message:
 
871
  add a
 
872
""",
 
873
            wt.branch, log.LongLogFormatter,
 
874
            formatter_kwargs=dict(levels=1))
 
875
 
 
876
    def test_long_properties_in_log(self):
 
877
        """Log includes the custom properties returned by the registered
 
878
        handlers.
 
879
        """
 
880
        wt = self.make_standard_commit('test_properties_in_log')
 
881
        def trivial_custom_prop_handler(revision):
 
882
            return {'test_prop':'test_value'}
 
883
 
 
884
        log.properties_handler_registry.register(
 
885
            'trivial_custom_prop_handler',
 
886
            trivial_custom_prop_handler)
 
887
        self.assertFormatterResult("""\
 
888
------------------------------------------------------------
 
889
revno: 1
 
890
test_prop: test_value
 
891
author: John Doe <jdoe@example.com>
 
892
committer: Lorem Ipsum <test@example.com>
 
893
branch nick: test_properties_in_log
 
894
timestamp: Tue 2005-11-22 00:00:00 +0000
 
895
message:
 
896
  add a
 
897
""",
 
898
            wt.branch, log.LongLogFormatter,
 
899
            formatter_kwargs=dict(levels=1))
 
900
 
 
901
 
 
902
class TestLineLogFormatter(TestCaseForLogFormatter):
 
903
 
 
904
    def test_line_log(self):
 
905
        """Line log should show revno
 
906
 
 
907
        bug #5162
 
908
        """
 
909
        wt = self.make_standard_commit('test-line-log',
 
910
                committer='Line-Log-Formatter Tester <test@line.log>',
 
911
                authors=[])
 
912
        self.assertFormatterResult("""\
 
913
1: Line-Log-Formatte... 2005-11-22 add a
 
914
""",
 
915
            wt.branch, log.LineLogFormatter)
 
916
 
 
917
    def test_trailing_newlines(self):
 
918
        wt = self.make_branch_and_tree('.')
 
919
        b = self.make_commits_with_trailing_newlines(wt)
 
920
        self.assertFormatterResult("""\
 
921
3: Joe Foo 2005-11-22 single line with trailing newline
 
922
2: Joe Foo 2005-11-22 multiline
 
923
1: Joe Foo 2005-11-22 simple log message
 
924
""",
 
925
            b, log.LineLogFormatter)
 
926
 
 
927
    def test_line_log_single_merge_revision(self):
 
928
        wt = self._prepare_tree_with_merges()
 
929
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
930
        rev = revspec.in_history(wt.branch)
 
931
        self.assertFormatterResult("""\
 
932
1.1.1: Joe Foo 2005-11-22 rev-merged
 
933
""",
 
934
            wt.branch, log.LineLogFormatter,
 
935
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
936
 
 
937
    def test_line_log_with_tags(self):
 
938
        wt = self._prepare_tree_with_merges(with_tags=True)
 
939
        self.assertFormatterResult("""\
 
940
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
 
941
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
 
942
1: Joe Foo 2005-11-22 rev-1
 
943
""",
 
944
            wt.branch, log.LineLogFormatter)
 
945
 
 
946
 
 
947
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
 
948
 
 
949
    def test_line_merge_revs_log(self):
 
950
        """Line log should show revno
 
951
 
 
952
        bug #5162
 
953
        """
 
954
        wt = self.make_standard_commit('test-line-log',
 
955
                committer='Line-Log-Formatter Tester <test@line.log>',
 
956
                authors=[])
 
957
        self.assertFormatterResult("""\
 
958
1: Line-Log-Formatte... 2005-11-22 add a
 
959
""",
 
960
            wt.branch, log.LineLogFormatter)
 
961
 
 
962
    def test_line_merge_revs_log_single_merge_revision(self):
 
963
        wt = self._prepare_tree_with_merges()
 
964
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
965
        rev = revspec.in_history(wt.branch)
 
966
        self.assertFormatterResult("""\
 
967
1.1.1: Joe Foo 2005-11-22 rev-merged
 
968
""",
 
969
            wt.branch, log.LineLogFormatter,
 
970
            formatter_kwargs=dict(levels=0),
 
971
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
972
 
 
973
    def test_line_merge_revs_log_with_merges(self):
 
974
        wt = self._prepare_tree_with_merges()
 
975
        self.assertFormatterResult("""\
 
976
2: Joe Foo 2005-11-22 [merge] rev-2
 
977
  1.1.1: Joe Foo 2005-11-22 rev-merged
 
978
1: Joe Foo 2005-11-22 rev-1
 
979
""",
 
980
            wt.branch, log.LineLogFormatter,
 
981
            formatter_kwargs=dict(levels=0))
 
982
 
 
983
 
 
984
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
 
985
 
 
986
    def test_gnu_changelog(self):
 
987
        wt = self.make_standard_commit('nicky', authors=[])
 
988
        self.assertFormatterResult('''\
 
989
2005-11-22  Lorem Ipsum  <test@example.com>
 
990
 
 
991
\tadd a
 
992
 
 
993
''',
 
994
            wt.branch, log.GnuChangelogLogFormatter)
 
995
 
 
996
    def test_with_authors(self):
 
997
        wt = self.make_standard_commit('nicky',
 
998
            authors=['Fooa Fooz <foo@example.com>',
 
999
                     'Bari Baro <bar@example.com>'])
 
1000
        self.assertFormatterResult('''\
 
1001
2005-11-22  Fooa Fooz  <foo@example.com>
 
1002
 
 
1003
\tadd a
 
1004
 
 
1005
''',
 
1006
            wt.branch, log.GnuChangelogLogFormatter)
 
1007
 
 
1008
    def test_verbose(self):
 
1009
        wt = self.make_standard_commit('nicky')
 
1010
        self.assertFormatterResult('''\
 
1011
2005-11-22  John Doe  <jdoe@example.com>
 
1012
 
 
1013
\t* a:
 
1014
 
 
1015
\tadd a
 
1016
 
 
1017
''',
 
1018
            wt.branch, log.GnuChangelogLogFormatter,
 
1019
            show_log_kwargs=dict(verbose=True))
 
1020
 
 
1021
 
 
1022
class TestShowChangedRevisions(tests.TestCaseWithTransport):
 
1023
 
 
1024
    def test_show_changed_revisions_verbose(self):
 
1025
        tree = self.make_branch_and_tree('tree_a')
 
1026
        self.build_tree(['tree_a/foo'])
 
1027
        tree.add('foo')
 
1028
        tree.commit('bar', rev_id='bar-id')
 
1029
        s = self.make_utf8_encoded_stringio()
 
1030
        log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
 
1031
        self.assertContainsRe(s.getvalue(), 'bar')
 
1032
        self.assertNotContainsRe(s.getvalue(), 'foo')
 
1033
 
 
1034
 
 
1035
class TestLogFormatter(tests.TestCase):
 
1036
 
 
1037
    def setUp(self):
 
1038
        super(TestLogFormatter, self).setUp()
 
1039
        self.rev = revision.Revision('a-id')
 
1040
        self.lf = log.LogFormatter(None)
 
1041
 
 
1042
    def test_short_committer(self):
 
1043
        def assertCommitter(expected, committer):
 
1044
            self.rev.committer = committer
 
1045
            self.assertEqual(expected, self.lf.short_committer(self.rev))
 
1046
 
 
1047
        assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
 
1048
        assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
 
1049
        assertCommitter('John Smith', 'John Smith')
 
1050
        assertCommitter('jsmith@example.com', 'jsmith@example.com')
 
1051
        assertCommitter('jsmith@example.com', '<jsmith@example.com>')
 
1052
        assertCommitter('John Smith', 'John Smith jsmith@example.com')
 
1053
 
 
1054
    def test_short_author(self):
 
1055
        def assertAuthor(expected, author):
 
1056
            self.rev.properties['author'] = author
 
1057
            self.assertEqual(expected, self.lf.short_author(self.rev))
 
1058
 
 
1059
        assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
 
1060
        assertAuthor('John Smith', 'John Smith')
 
1061
        assertAuthor('jsmith@example.com', 'jsmith@example.com')
 
1062
        assertAuthor('jsmith@example.com', '<jsmith@example.com>')
 
1063
        assertAuthor('John Smith', 'John Smith jsmith@example.com')
 
1064
 
 
1065
    def test_short_author_from_committer(self):
 
1066
        self.rev.committer = 'John Doe <jdoe@example.com>'
 
1067
        self.assertEqual('John Doe', self.lf.short_author(self.rev))
 
1068
 
 
1069
    def test_short_author_from_authors(self):
 
1070
        self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
 
1071
                                          'Jane Rey <jrey@example.com>')
 
1072
        self.assertEqual('John Smith', self.lf.short_author(self.rev))
 
1073
 
 
1074
 
 
1075
class TestReverseByDepth(tests.TestCase):
 
1076
    """Test reverse_by_depth behavior.
 
1077
 
 
1078
    This is used to present revisions in forward (oldest first) order in a nice
 
1079
    layout.
 
1080
 
 
1081
    The tests use lighter revision description to ease reading.
 
1082
    """
 
1083
 
 
1084
    def assertReversed(self, forward, backward):
 
1085
        # Transform the descriptions to suit the API: tests use (revno, depth),
 
1086
        # while the API expects (revid, revno, depth)
 
1087
        def complete_revisions(l):
 
1088
            """Transform the description to suit the API.
 
1089
 
 
1090
            Tests use (revno, depth) whil the API expects (revid, revno, depth).
 
1091
            Since the revid is arbitrary, we just duplicate revno
 
1092
            """
 
1093
            return [ (r, r, d) for r, d in l]
 
1094
        forward = complete_revisions(forward)
 
1095
        backward= complete_revisions(backward)
 
1096
        self.assertEqual(forward, log.reverse_by_depth(backward))
 
1097
 
 
1098
 
 
1099
    def test_mainline_revisions(self):
 
1100
        self.assertReversed([( '1', 0), ('2', 0)],
 
1101
                            [('2', 0), ('1', 0)])
 
1102
 
 
1103
    def test_merged_revisions(self):
 
1104
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
 
1105
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
 
1106
    def test_shifted_merged_revisions(self):
 
1107
        """Test irregular layout.
 
1108
 
 
1109
        Requesting revisions touching a file can produce "holes" in the depths.
 
1110
        """
 
1111
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
 
1112
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
 
1113
 
 
1114
    def test_merged_without_child_revisions(self):
 
1115
        """Test irregular layout.
 
1116
 
 
1117
        Revision ranges can produce "holes" in the depths.
 
1118
        """
 
1119
        # When a revision of higher depth doesn't follow one of lower depth, we
 
1120
        # assume a lower depth one is virtually there
 
1121
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
 
1122
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2),])
 
1123
        # So we get the same order after reversing below even if the original
 
1124
        # revisions are not in the same order.
 
1125
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
 
1126
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2),])
 
1127
 
 
1128
 
 
1129
class TestHistoryChange(tests.TestCaseWithTransport):
 
1130
 
 
1131
    def setup_a_tree(self):
 
1132
        tree = self.make_branch_and_tree('tree')
 
1133
        tree.lock_write()
 
1134
        self.addCleanup(tree.unlock)
 
1135
        tree.commit('1a', rev_id='1a')
 
1136
        tree.commit('2a', rev_id='2a')
 
1137
        tree.commit('3a', rev_id='3a')
 
1138
        return tree
 
1139
 
 
1140
    def setup_ab_tree(self):
 
1141
        tree = self.setup_a_tree()
 
1142
        tree.set_last_revision('1a')
 
1143
        tree.branch.set_last_revision_info(1, '1a')
 
1144
        tree.commit('2b', rev_id='2b')
 
1145
        tree.commit('3b', rev_id='3b')
 
1146
        return tree
 
1147
 
 
1148
    def setup_ac_tree(self):
 
1149
        tree = self.setup_a_tree()
 
1150
        tree.set_last_revision(revision.NULL_REVISION)
 
1151
        tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
 
1152
        tree.commit('1c', rev_id='1c')
 
1153
        tree.commit('2c', rev_id='2c')
 
1154
        tree.commit('3c', rev_id='3c')
 
1155
        return tree
 
1156
 
 
1157
    def test_all_new(self):
 
1158
        tree = self.setup_ab_tree()
 
1159
        old, new = log.get_history_change('1a', '3a', tree.branch.repository)
 
1160
        self.assertEqual([], old)
 
1161
        self.assertEqual(['2a', '3a'], new)
 
1162
 
 
1163
    def test_all_old(self):
 
1164
        tree = self.setup_ab_tree()
 
1165
        old, new = log.get_history_change('3a', '1a', tree.branch.repository)
 
1166
        self.assertEqual([], new)
 
1167
        self.assertEqual(['2a', '3a'], old)
 
1168
 
 
1169
    def test_null_old(self):
 
1170
        tree = self.setup_ab_tree()
 
1171
        old, new = log.get_history_change(revision.NULL_REVISION,
 
1172
                                          '3a', tree.branch.repository)
 
1173
        self.assertEqual([], old)
 
1174
        self.assertEqual(['1a', '2a', '3a'], new)
 
1175
 
 
1176
    def test_null_new(self):
 
1177
        tree = self.setup_ab_tree()
 
1178
        old, new = log.get_history_change('3a', revision.NULL_REVISION,
 
1179
                                          tree.branch.repository)
 
1180
        self.assertEqual([], new)
 
1181
        self.assertEqual(['1a', '2a', '3a'], old)
 
1182
 
 
1183
    def test_diverged(self):
 
1184
        tree = self.setup_ab_tree()
 
1185
        old, new = log.get_history_change('3a', '3b', tree.branch.repository)
 
1186
        self.assertEqual(old, ['2a', '3a'])
 
1187
        self.assertEqual(new, ['2b', '3b'])
 
1188
 
 
1189
    def test_unrelated(self):
 
1190
        tree = self.setup_ac_tree()
 
1191
        old, new = log.get_history_change('3a', '3c', tree.branch.repository)
 
1192
        self.assertEqual(old, ['1a', '2a', '3a'])
 
1193
        self.assertEqual(new, ['1c', '2c', '3c'])
 
1194
 
 
1195
    def test_show_branch_change(self):
 
1196
        tree = self.setup_ab_tree()
 
1197
        s = BytesIO()
 
1198
        log.show_branch_change(tree.branch, s, 3, '3a')
 
1199
        self.assertContainsRe(s.getvalue(),
 
1200
            '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
 
1201
            '[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
 
1202
 
 
1203
    def test_show_branch_change_no_change(self):
 
1204
        tree = self.setup_ab_tree()
 
1205
        s = BytesIO()
 
1206
        log.show_branch_change(tree.branch, s, 3, '3b')
 
1207
        self.assertEqual(s.getvalue(),
 
1208
            'Nothing seems to have changed\n')
 
1209
 
 
1210
    def test_show_branch_change_no_old(self):
 
1211
        tree = self.setup_ab_tree()
 
1212
        s = BytesIO()
 
1213
        log.show_branch_change(tree.branch, s, 2, '2b')
 
1214
        self.assertContainsRe(s.getvalue(), 'Added Revisions:')
 
1215
        self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
 
1216
 
 
1217
    def test_show_branch_change_no_new(self):
 
1218
        tree = self.setup_ab_tree()
 
1219
        tree.branch.set_last_revision_info(2, '2b')
 
1220
        s = BytesIO()
 
1221
        log.show_branch_change(tree.branch, s, 3, '3b')
 
1222
        self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
 
1223
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
 
1224
 
 
1225
 
 
1226
class TestRevisionNotInBranch(TestCaseForLogFormatter):
 
1227
 
 
1228
    def setup_a_tree(self):
 
1229
        tree = self.make_branch_and_tree('tree')
 
1230
        tree.lock_write()
 
1231
        self.addCleanup(tree.unlock)
 
1232
        kwargs = {
 
1233
            'committer': 'Joe Foo <joe@foo.com>',
 
1234
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
 
1235
            'timezone': 0, # UTC
 
1236
        }
 
1237
        tree.commit('commit 1a', rev_id='1a', **kwargs)
 
1238
        tree.commit('commit 2a', rev_id='2a', **kwargs)
 
1239
        tree.commit('commit 3a', rev_id='3a', **kwargs)
 
1240
        return tree
 
1241
 
 
1242
    def setup_ab_tree(self):
 
1243
        tree = self.setup_a_tree()
 
1244
        tree.set_last_revision('1a')
 
1245
        tree.branch.set_last_revision_info(1, '1a')
 
1246
        kwargs = {
 
1247
            'committer': 'Joe Foo <joe@foo.com>',
 
1248
            'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
 
1249
            'timezone': 0, # UTC
 
1250
        }
 
1251
        tree.commit('commit 2b', rev_id='2b', **kwargs)
 
1252
        tree.commit('commit 3b', rev_id='3b', **kwargs)
 
1253
        return tree
 
1254
 
 
1255
    def test_one_revision(self):
 
1256
        tree = self.setup_ab_tree()
 
1257
        lf = LogCatcher()
 
1258
        rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1259
        log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
 
1260
                     end_revision=rev)
 
1261
        self.assertEqual(1, len(lf.revisions))
 
1262
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1263
        self.assertEqual('3a', lf.revisions[0].rev.revision_id)
 
1264
 
 
1265
    def test_many_revisions(self):
 
1266
        tree = self.setup_ab_tree()
 
1267
        lf = LogCatcher()
 
1268
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1269
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1270
        log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
 
1271
                     end_revision=end_rev)
 
1272
        self.assertEqual(3, len(lf.revisions))
 
1273
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1274
        self.assertEqual('3a', lf.revisions[0].rev.revision_id)
 
1275
        self.assertEqual(None, lf.revisions[1].revno)   # Out-of-branch
 
1276
        self.assertEqual('2a', lf.revisions[1].rev.revision_id)
 
1277
        self.assertEqual('1', lf.revisions[2].revno)    # In-branch
 
1278
 
 
1279
    def test_long_format(self):
 
1280
        tree = self.setup_ab_tree()
 
1281
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1282
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1283
        self.assertFormatterResult("""\
 
1284
------------------------------------------------------------
 
1285
revision-id: 3a
 
1286
committer: Joe Foo <joe@foo.com>
 
1287
branch nick: tree
 
1288
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1289
message:
 
1290
  commit 3a
 
1291
------------------------------------------------------------
 
1292
revision-id: 2a
 
1293
committer: Joe Foo <joe@foo.com>
 
1294
branch nick: tree
 
1295
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1296
message:
 
1297
  commit 2a
 
1298
------------------------------------------------------------
 
1299
revno: 1
 
1300
committer: Joe Foo <joe@foo.com>
 
1301
branch nick: tree
 
1302
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1303
message:
 
1304
  commit 1a
 
1305
""",
 
1306
            tree.branch, log.LongLogFormatter, show_log_kwargs={
 
1307
                'start_revision': start_rev, 'end_revision': end_rev
 
1308
            })
 
1309
 
 
1310
    def test_short_format(self):
 
1311
        tree = self.setup_ab_tree()
 
1312
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1313
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1314
        self.assertFormatterResult("""\
 
1315
      Joe Foo\t2005-11-22
 
1316
      revision-id:3a
 
1317
      commit 3a
 
1318
 
 
1319
      Joe Foo\t2005-11-22
 
1320
      revision-id:2a
 
1321
      commit 2a
 
1322
 
 
1323
    1 Joe Foo\t2005-11-22
 
1324
      commit 1a
 
1325
 
 
1326
""",
 
1327
            tree.branch, log.ShortLogFormatter, show_log_kwargs={
 
1328
                'start_revision': start_rev, 'end_revision': end_rev
 
1329
            })
 
1330
 
 
1331
    def test_line_format(self):
 
1332
        tree = self.setup_ab_tree()
 
1333
        start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
 
1334
        end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
 
1335
        self.assertFormatterResult("""\
 
1336
Joe Foo 2005-11-22 commit 3a
 
1337
Joe Foo 2005-11-22 commit 2a
 
1338
1: Joe Foo 2005-11-22 commit 1a
 
1339
""",
 
1340
            tree.branch, log.LineLogFormatter, show_log_kwargs={
 
1341
                'start_revision': start_rev, 'end_revision': end_rev
 
1342
            })
 
1343
 
 
1344
 
 
1345
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
 
1346
 
 
1347
    def setUp(self):
 
1348
        super(TestLogWithBugs, self).setUp()
 
1349
        log.properties_handler_registry.register(
 
1350
            'bugs_properties_handler',
 
1351
            log._bugs_properties_handler)
 
1352
 
 
1353
    def make_commits_with_bugs(self):
 
1354
        """Helper method for LogFormatter tests"""
 
1355
        tree = self.make_branch_and_tree(u'.')
 
1356
        self.build_tree(['a', 'b'])
 
1357
        tree.add('a')
 
1358
        self.wt_commit(tree, 'simple log message', rev_id='a1',
 
1359
                       revprops={'bugs': 'test://bug/id fixed'})
 
1360
        tree.add('b')
 
1361
        self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
 
1362
                       authors=['Joe Bar <joe@bar.com>'],
 
1363
                       revprops={'bugs': 'test://bug/id fixed\n'
 
1364
                                 'test://bug/2 fixed'})
 
1365
        return tree
 
1366
 
 
1367
 
 
1368
    def test_long_bugs(self):
 
1369
        tree = self.make_commits_with_bugs()
 
1370
        self.assertFormatterResult("""\
 
1371
------------------------------------------------------------
 
1372
revno: 2
 
1373
fixes bugs: test://bug/id test://bug/2
 
1374
author: Joe Bar <joe@bar.com>
 
1375
committer: Joe Foo <joe@foo.com>
 
1376
branch nick: work
 
1377
timestamp: Tue 2005-11-22 00:00:01 +0000
 
1378
message:
 
1379
  multiline
 
1380
  log
 
1381
  message
 
1382
------------------------------------------------------------
 
1383
revno: 1
 
1384
fixes bug: test://bug/id
 
1385
committer: Joe Foo <joe@foo.com>
 
1386
branch nick: work
 
1387
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1388
message:
 
1389
  simple log message
 
1390
""",
 
1391
            tree.branch, log.LongLogFormatter)
 
1392
 
 
1393
    def test_short_bugs(self):
 
1394
        tree = self.make_commits_with_bugs()
 
1395
        self.assertFormatterResult("""\
 
1396
    2 Joe Bar\t2005-11-22
 
1397
      fixes bugs: test://bug/id test://bug/2
 
1398
      multiline
 
1399
      log
 
1400
      message
 
1401
 
 
1402
    1 Joe Foo\t2005-11-22
 
1403
      fixes bug: test://bug/id
 
1404
      simple log message
 
1405
 
 
1406
""",
 
1407
            tree.branch, log.ShortLogFormatter)
 
1408
 
 
1409
    def test_wrong_bugs_property(self):
 
1410
        tree = self.make_branch_and_tree(u'.')
 
1411
        self.build_tree(['foo'])
 
1412
        self.wt_commit(tree, 'simple log message', rev_id='a1',
 
1413
                       revprops={'bugs': 'test://bug/id invalid_value'})
 
1414
        self.assertFormatterResult("""\
 
1415
    1 Joe Foo\t2005-11-22
 
1416
      simple log message
 
1417
 
 
1418
""",
 
1419
            tree.branch, log.ShortLogFormatter)
 
1420
 
 
1421
    def test_bugs_handler_present(self):
 
1422
        self.properties_handler_registry.get('bugs_properties_handler')
 
1423
 
 
1424
 
 
1425
class TestLogForAuthors(TestCaseForLogFormatter):
 
1426
 
 
1427
    def setUp(self):
 
1428
        super(TestLogForAuthors, self).setUp()
 
1429
        self.wt = self.make_standard_commit('nicky',
 
1430
            authors=['John Doe <jdoe@example.com>',
 
1431
                     'Jane Rey <jrey@example.com>'])
 
1432
 
 
1433
    def assertFormatterResult(self, formatter, who, result):
 
1434
        formatter_kwargs = dict()
 
1435
        if who is not None:
 
1436
            author_list_handler = log.author_list_registry.get(who)
 
1437
            formatter_kwargs['author_list_handler'] = author_list_handler
 
1438
        TestCaseForLogFormatter.assertFormatterResult(self, result,
 
1439
            self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
 
1440
 
 
1441
    def test_line_default(self):
 
1442
        self.assertFormatterResult(log.LineLogFormatter, None, """\
 
1443
1: John Doe 2005-11-22 add a
 
1444
""")
 
1445
 
 
1446
    def test_line_committer(self):
 
1447
        self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
 
1448
1: Lorem Ipsum 2005-11-22 add a
 
1449
""")
 
1450
 
 
1451
    def test_line_first(self):
 
1452
        self.assertFormatterResult(log.LineLogFormatter, 'first', """\
 
1453
1: John Doe 2005-11-22 add a
 
1454
""")
 
1455
 
 
1456
    def test_line_all(self):
 
1457
        self.assertFormatterResult(log.LineLogFormatter, 'all', """\
 
1458
1: John Doe, Jane Rey 2005-11-22 add a
 
1459
""")
 
1460
 
 
1461
 
 
1462
    def test_short_default(self):
 
1463
        self.assertFormatterResult(log.ShortLogFormatter, None, """\
 
1464
    1 John Doe\t2005-11-22
 
1465
      add a
 
1466
 
 
1467
""")
 
1468
 
 
1469
    def test_short_committer(self):
 
1470
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
 
1471
    1 Lorem Ipsum\t2005-11-22
 
1472
      add a
 
1473
 
 
1474
""")
 
1475
 
 
1476
    def test_short_first(self):
 
1477
        self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
 
1478
    1 John Doe\t2005-11-22
 
1479
      add a
 
1480
 
 
1481
""")
 
1482
 
 
1483
    def test_short_all(self):
 
1484
        self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
 
1485
    1 John Doe, Jane Rey\t2005-11-22
 
1486
      add a
 
1487
 
 
1488
""")
 
1489
 
 
1490
    def test_long_default(self):
 
1491
        self.assertFormatterResult(log.LongLogFormatter, None, """\
 
1492
------------------------------------------------------------
 
1493
revno: 1
 
1494
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1495
committer: Lorem Ipsum <test@example.com>
 
1496
branch nick: nicky
 
1497
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1498
message:
 
1499
  add a
 
1500
""")
 
1501
 
 
1502
    def test_long_committer(self):
 
1503
        self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
 
1504
------------------------------------------------------------
 
1505
revno: 1
 
1506
committer: Lorem Ipsum <test@example.com>
 
1507
branch nick: nicky
 
1508
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1509
message:
 
1510
  add a
 
1511
""")
 
1512
 
 
1513
    def test_long_first(self):
 
1514
        self.assertFormatterResult(log.LongLogFormatter, 'first', """\
 
1515
------------------------------------------------------------
 
1516
revno: 1
 
1517
author: John Doe <jdoe@example.com>
 
1518
committer: Lorem Ipsum <test@example.com>
 
1519
branch nick: nicky
 
1520
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1521
message:
 
1522
  add a
 
1523
""")
 
1524
 
 
1525
    def test_long_all(self):
 
1526
        self.assertFormatterResult(log.LongLogFormatter, 'all', """\
 
1527
------------------------------------------------------------
 
1528
revno: 1
 
1529
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
 
1530
committer: Lorem Ipsum <test@example.com>
 
1531
branch nick: nicky
 
1532
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1533
message:
 
1534
  add a
 
1535
""")
 
1536
 
 
1537
    def test_gnu_changelog_default(self):
 
1538
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
 
1539
2005-11-22  John Doe  <jdoe@example.com>
 
1540
 
 
1541
\tadd a
 
1542
 
 
1543
""")
 
1544
 
 
1545
    def test_gnu_changelog_committer(self):
 
1546
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
 
1547
2005-11-22  Lorem Ipsum  <test@example.com>
 
1548
 
 
1549
\tadd a
 
1550
 
 
1551
""")
 
1552
 
 
1553
    def test_gnu_changelog_first(self):
 
1554
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
 
1555
2005-11-22  John Doe  <jdoe@example.com>
 
1556
 
 
1557
\tadd a
 
1558
 
 
1559
""")
 
1560
 
 
1561
    def test_gnu_changelog_all(self):
 
1562
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
 
1563
2005-11-22  John Doe  <jdoe@example.com>, Jane Rey  <jrey@example.com>
 
1564
 
 
1565
\tadd a
 
1566
 
 
1567
""")
 
1568
 
 
1569
 
 
1570
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
 
1571
 
 
1572
    def make_branch_with_alternate_ancestries(self, relpath='.'):
 
1573
        # See test_merge_sorted_exclude_ancestry below for the difference with
 
1574
        # bt.per_branch.test_iter_merge_sorted_revision.
 
1575
        # TestIterMergeSortedRevisionsBushyGraph.
 
1576
        # make_branch_with_alternate_ancestries
 
1577
        # and test_merge_sorted_exclude_ancestry
 
1578
        # See the FIXME in assertLogRevnos too.
 
1579
        builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
 
1580
        # 1
 
1581
        # |\
 
1582
        # 2 \
 
1583
        # |  |
 
1584
        # |  1.1.1
 
1585
        # |  | \
 
1586
        # |  |  1.2.1
 
1587
        # |  | /
 
1588
        # |  1.1.2
 
1589
        # | /
 
1590
        # 3
 
1591
        builder.start_series()
 
1592
        builder.build_snapshot('1', None, [
 
1593
            ('add', ('', 'TREE_ROOT', 'directory', '')),])
 
1594
        builder.build_snapshot('1.1.1', ['1'], [])
 
1595
        builder.build_snapshot('2', ['1'], [])
 
1596
        builder.build_snapshot('1.2.1', ['1.1.1'], [])
 
1597
        builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
 
1598
        builder.build_snapshot('3', ['2', '1.1.2'], [])
 
1599
        builder.finish_series()
 
1600
        br = builder.get_branch()
 
1601
        br.lock_read()
 
1602
        self.addCleanup(br.unlock)
 
1603
        return br
 
1604
 
 
1605
    def assertLogRevnos(self, expected_revnos, b, start, end,
 
1606
                        exclude_common_ancestry, generate_merge_revisions=True):
 
1607
        # FIXME: the layering in log makes it hard to test intermediate levels,
 
1608
        # I wish adding filters with their parameters was easier...
 
1609
        # -- vila 20100413
 
1610
        iter_revs = log._calc_view_revisions(
 
1611
            b, start, end, direction='reverse',
 
1612
            generate_merge_revisions=generate_merge_revisions,
 
1613
            exclude_common_ancestry=exclude_common_ancestry)
 
1614
        self.assertEqual(expected_revnos,
 
1615
                         [revid for revid, revno, depth in iter_revs])
 
1616
 
 
1617
    def test_merge_sorted_exclude_ancestry(self):
 
1618
        b = self.make_branch_with_alternate_ancestries()
 
1619
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
 
1620
                             b, '1', '3', exclude_common_ancestry=False)
 
1621
        # '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
 
1622
        # it should be mentioned even if merge_sort order will make it appear
 
1623
        # after 1.1.1
 
1624
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
 
1625
                             b, '1.1.1', '3', exclude_common_ancestry=True)
 
1626
 
 
1627
    def test_merge_sorted_simple_revnos_exclude_ancestry(self):
 
1628
        b = self.make_branch_with_alternate_ancestries()
 
1629
        self.assertLogRevnos(['3', '2'],
 
1630
                             b, '1', '3', exclude_common_ancestry=True,
 
1631
                             generate_merge_revisions=False)
 
1632
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
 
1633
                             b, '1', '3', exclude_common_ancestry=True,
 
1634
                             generate_merge_revisions=True)
 
1635
 
 
1636
 
 
1637
class TestLogDefaults(TestCaseForLogFormatter):
 
1638
    def test_default_log_level(self):
 
1639
        """
 
1640
        Test to ensure that specifying 'levels=1' to make_log_request_dict
 
1641
        doesn't get overwritten when using a LogFormatter that supports more
 
1642
        detail.
 
1643
        Fixes bug #747958.
 
1644
        """
 
1645
        wt = self._prepare_tree_with_merges()
 
1646
        b = wt.branch
 
1647
 
 
1648
        class CustomLogFormatter(log.LogFormatter):
 
1649
            def __init__(self, *args, **kwargs):
 
1650
                super(CustomLogFormatter, self).__init__(*args, **kwargs)
 
1651
                self.revisions = []
 
1652
            def get_levels(self):
 
1653
                # log formatter supports all levels:
 
1654
                return 0
 
1655
            def log_revision(self, revision):
 
1656
                self.revisions.append(revision)
 
1657
 
 
1658
        log_formatter = LogCatcher()
 
1659
        # First request we don't specify number of levels, we should get a
 
1660
        # sensible default (whatever the LogFormatter handles - which in this
 
1661
        # case is 0/everything):
 
1662
        request = log.make_log_request_dict(limit=10)
 
1663
        log.Logger(b, request).show(log_formatter)
 
1664
        # should have all three revisions:
 
1665
        self.assertEqual(len(log_formatter.revisions), 3)
 
1666
 
 
1667
        del log_formatter
 
1668
        log_formatter = LogCatcher()
 
1669
        # now explicitly request mainline revisions only:
 
1670
        request = log.make_log_request_dict(limit=10, levels=1)
 
1671
        log.Logger(b, request).show(log_formatter)
 
1672
        # should now only have 2 revisions:
 
1673
        self.assertEqual(len(log_formatter.revisions), 2)
 
1674