~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Patch Queue Manager
  • Date: 2011-10-13 14:09:11 UTC
  • mfrom: (6207.2.1 colo-fixes)
  • Revision ID: pqm@pqm.ubuntu.com-20111013140911-c2zv6a9pux62xnr9
(jelmer) Add support for cloning into a colocated branch. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

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