/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: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2020-07-28 02:47:10 UTC
  • mfrom: (7519.1.1 merge-3.1)
  • Revision ID: breezy.the.bot@gmail.com-20200728024710-a2ylds219f1lsl62
Merge lp:brz/3.1.

Merged from https://code.launchpad.net/~jelmer/brz/merge-3.1/+merge/388173

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2013, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from io import (
 
18
    BytesIO,
 
19
    StringIO,
 
20
    )
17
21
import os
18
 
from cStringIO import StringIO
 
22
import re
19
23
 
20
 
from bzrlib import (
 
24
from .. import (
21
25
    branchbuilder,
22
26
    errors,
23
27
    log,
24
28
    registry,
25
29
    revision,
26
30
    revisionspec,
27
 
    symbol_versioning,
28
31
    tests,
 
32
    gpg,
 
33
    trace,
29
34
    )
30
35
 
31
36
 
38
43
        stamp is incremented at each commit.
39
44
        """
40
45
        if getattr(self, 'timestamp', None) is None:
41
 
            self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
 
46
            self.timestamp = 1132617600  # Mon 2005-11-22 00:00:00 +0000
42
47
        else:
43
 
            self.timestamp += 1 # 1 second between each commit
 
48
            self.timestamp += 1  # 1 second between each commit
44
49
        kwargs.setdefault('timestamp', self.timestamp)
45
 
        kwargs.setdefault('timezone', 0) # UTC
 
50
        kwargs.setdefault('timezone', 0)  # UTC
46
51
        kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
47
52
 
48
53
        return wt.commit(message, **kwargs)
88
93
        """Helper method for LogFormatter tests"""
89
94
        b = wt.branch
90
95
        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')])
 
96
        self.build_tree_contents([('a', b'hello moto\n')])
 
97
        self.wt_commit(wt, 'simple log message', rev_id=b'a1')
 
98
        self.build_tree_contents([('b', b'goodbye\n')])
94
99
        wt.add('b')
95
 
        self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
 
100
        self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id=b'a2')
96
101
 
97
 
        self.build_tree_contents([('c', 'just another manic monday\n')])
 
102
        self.build_tree_contents([('c', b'just another manic monday\n')])
98
103
        wt.add('c')
99
 
        self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
 
104
        self.wt_commit(wt, 'single line with trailing newline\n', rev_id=b'a3')
100
105
        return b
101
106
 
102
107
    def _prepare_tree_with_merges(self, with_tags=False):
104
109
        wt.lock_write()
105
110
        self.addCleanup(wt.unlock)
106
111
        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
        self.wt_commit(wt, 'rev-1', rev_id=b'rev-1')
 
113
        self.wt_commit(wt, 'rev-merged', rev_id=b'rev-2a')
 
114
        wt.set_parent_ids([b'rev-1', b'rev-2a'])
 
115
        wt.branch.set_last_revision_info(1, b'rev-1')
 
116
        self.wt_commit(wt, 'rev-2', rev_id=b'rev-2b')
112
117
        if with_tags:
113
118
            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')
 
119
            branch.tags.set_tag('v0.2', b'rev-2b')
 
120
            self.wt_commit(wt, 'rev-3', rev_id=b'rev-3')
 
121
            branch.tags.set_tag('v1.0rc1', b'rev-3')
 
122
            branch.tags.set_tag('v1.0', b'rev-3')
118
123
        return wt
119
124
 
 
125
 
120
126
class LogCatcher(log.LogFormatter):
121
127
    """Pull log messages into a list rather than displaying them.
122
128
 
151
157
            # By default we expect an empty list
152
158
            expected = kw.get(n, [])
153
159
            # strip out only the path components
154
 
            got = [x[0] for x in getattr(delta, n)]
 
160
            got = [x.path[1] or x.path[0] for x in getattr(delta, n)]
155
161
            self.assertEqual(expected, got)
156
162
 
157
163
    def assertInvalidRevisonNumber(self, br, start, end):
173
179
        self.assertInvalidRevisonNumber(b, 2, 1)
174
180
        self.assertInvalidRevisonNumber(b, 1, 2)
175
181
        self.assertInvalidRevisonNumber(b, 0, 2)
176
 
        self.assertInvalidRevisonNumber(b, 1, 0)
177
182
        self.assertInvalidRevisonNumber(b, -1, 1)
178
183
        self.assertInvalidRevisonNumber(b, 1, -1)
 
184
        self.assertInvalidRevisonNumber(b, 1, 0)
179
185
 
180
186
    def test_empty_branch(self):
181
187
        wt = self.make_branch_and_tree('.')
216
222
 
217
223
    def test_commit_message_with_control_chars(self):
218
224
        wt = self.make_branch_and_tree('.')
219
 
        msg = u"All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
 
225
        msg = u"All 8-bit chars: " + ''.join([chr(x) for x in range(256)])
220
226
        msg = msg.replace(u'\r', u'\n')
221
227
        wt.commit(msg)
222
228
        lf = LogCatcher()
234
240
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
235
241
        # included in the test commit message, even though they are
236
242
        # valid XML 1.0 characters.
237
 
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
 
243
        msg = "\x09" + ''.join([chr(x) for x in range(0x20, 256)])
238
244
        wt.commit(msg)
239
245
        lf = LogCatcher()
240
246
        log.show_log(wt.branch, lf, verbose=True)
250
256
        wt.commit(message='add file1 and file2')
251
257
        self.run_bzr('branch parent child')
252
258
        os.unlink('child/file1')
253
 
        file('child/file2', 'wb').write('hello\n')
 
259
        with open('child/file2', 'wb') as f:
 
260
            f.write(b'hello\n')
254
261
        self.run_bzr(['commit', '-m', 'remove file1 and modify file2',
255
 
            'child'])
 
262
                      'child'])
256
263
        os.chdir('parent')
257
264
        self.run_bzr('merge ../child')
258
265
        wt.commit('merge child branch')
280
287
        self.assertEqual('add file1 and file2', logentry.rev.message)
281
288
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
282
289
 
 
290
    def test_bug_842695_log_restricted_to_dir(self):
 
291
        # Comments here indicate revision numbers in trunk  # VVVVV
 
292
        trunk = self.make_branch_and_tree('this')
 
293
        trunk.commit('initial trunk')                       # 1
 
294
        adder = trunk.controldir.sprout('adder').open_workingtree()
 
295
        merger = trunk.controldir.sprout('merger').open_workingtree()
 
296
        self.build_tree_contents([
 
297
            ('adder/dir/',),
 
298
            ('adder/dir/file', b'foo'),
 
299
            ])
 
300
        adder.add(['dir', 'dir/file'])
 
301
        adder.commit('added dir')                           # 1.1.1
 
302
        trunk.merge_from_branch(adder.branch)
 
303
        trunk.commit('merged adder into trunk')             # 2
 
304
        merger.merge_from_branch(trunk.branch)
 
305
        merger.commit('merged trunk into merger')           # 1.2.1
 
306
        # Commits are processed in increments of 200 revisions, so
 
307
        # make sure the two merges into trunk are in different chunks.
 
308
        for i in range(200):
 
309
            trunk.commit('intermediate commit %d' % i)      # 3-202
 
310
        trunk.merge_from_branch(merger.branch)
 
311
        trunk.commit('merged merger into trunk')            # 203
 
312
        file_id = trunk.path2id('dir')
 
313
        lf = LogCatcher()
 
314
        lf.supports_merge_revisions = True
 
315
        log.show_log(trunk.branch, lf, file_id)
 
316
        try:
 
317
            self.assertEqual(['2', '1.1.1'], [r.revno for r in lf.revisions])
 
318
        except AssertionError:
 
319
            raise tests.KnownFailure("bug #842695")
 
320
 
 
321
 
 
322
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
 
323
 
 
324
    def verify_revision_signature(self, revid, gpg_strategy):
 
325
        return (gpg.SIGNATURE_VALID,
 
326
                u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
 
327
 
 
328
    def test_format_signature_validity_utf(self):
 
329
        """Check that GPG signatures containing UTF-8 names are formatted
 
330
        correctly."""
 
331
        wt = self.make_branch_and_tree('.')
 
332
        revid = wt.commit('empty commit')
 
333
        repo = wt.branch.repository
 
334
        # Monkey patch out checking if this rev is actually signed, since we
 
335
        # can't sign it without a heavier TestCase and LoopbackGPGStrategy
 
336
        # doesn't care anyways.
 
337
        self.overrideAttr(repo, 'verify_revision_signature',
 
338
                          self.verify_revision_signature)
 
339
        out = log.format_signature_validity(revid, wt.branch)
 
340
        self.assertEqual(
 
341
            u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
 
342
            out)
 
343
 
283
344
 
284
345
class TestShortLogFormatter(TestCaseForLogFormatter):
285
346
 
286
347
    def test_trailing_newlines(self):
287
348
        wt = self.make_branch_and_tree('.')
288
349
        b = self.make_commits_with_trailing_newlines(wt)
289
 
        self.assertFormatterResult("""\
 
350
        self.assertFormatterResult(b"""\
290
351
    3 Joe Foo\t2005-11-22
291
352
      single line with trailing newline
292
353
 
299
360
      simple log message
300
361
 
301
362
""",
302
 
            b, log.ShortLogFormatter)
 
363
                                   b, log.ShortLogFormatter)
303
364
 
304
365
    def test_short_log_with_merges(self):
305
366
        wt = self._prepare_tree_with_merges()
306
 
        self.assertFormatterResult("""\
 
367
        self.assertFormatterResult(b"""\
307
368
    2 Joe Foo\t2005-11-22 [merge]
308
369
      rev-2
309
370
 
311
372
      rev-1
312
373
 
313
374
""",
314
 
            wt.branch, log.ShortLogFormatter)
 
375
                                   wt.branch, log.ShortLogFormatter)
315
376
 
316
377
    def test_short_log_with_merges_and_advice(self):
317
378
        wt = self._prepare_tree_with_merges()
318
 
        self.assertFormatterResult("""\
 
379
        self.assertFormatterResult(b"""\
319
380
    2 Joe Foo\t2005-11-22 [merge]
320
381
      rev-2
321
382
 
322
383
    1 Joe Foo\t2005-11-22
323
384
      rev-1
324
385
 
325
 
Use --include-merges or -n0 to see merged revisions.
 
386
Use --include-merged or -n0 to see merged revisions.
326
387
""",
327
 
            wt.branch, log.ShortLogFormatter,
328
 
            formatter_kwargs=dict(show_advice=True))
 
388
                                   wt.branch, log.ShortLogFormatter,
 
389
                                   formatter_kwargs=dict(show_advice=True))
329
390
 
330
391
    def test_short_log_with_merges_and_range(self):
331
392
        wt = self._prepare_tree_with_merges()
332
 
        self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
333
 
        wt.branch.set_last_revision_info(2, 'rev-2b')
334
 
        wt.set_parent_ids(['rev-2b', 'rev-3a'])
335
 
        self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
336
 
        self.assertFormatterResult("""\
 
393
        self.wt_commit(wt, 'rev-3a', rev_id=b'rev-3a')
 
394
        wt.branch.set_last_revision_info(2, b'rev-2b')
 
395
        wt.set_parent_ids([b'rev-2b', b'rev-3a'])
 
396
        self.wt_commit(wt, 'rev-3b', rev_id=b'rev-3b')
 
397
        self.assertFormatterResult(b"""\
337
398
    3 Joe Foo\t2005-11-22 [merge]
338
399
      rev-3b
339
400
 
341
402
      rev-2
342
403
 
343
404
""",
344
 
            wt.branch, log.ShortLogFormatter,
345
 
            show_log_kwargs=dict(start_revision=2, end_revision=3))
 
405
                                   wt.branch, log.ShortLogFormatter,
 
406
                                   show_log_kwargs=dict(start_revision=2, end_revision=3))
346
407
 
347
408
    def test_short_log_with_tags(self):
348
409
        wt = self._prepare_tree_with_merges(with_tags=True)
349
 
        self.assertFormatterResult("""\
 
410
        self.assertFormatterResult(b"""\
350
411
    3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
351
412
      rev-3
352
413
 
357
418
      rev-1
358
419
 
359
420
""",
360
 
            wt.branch, log.ShortLogFormatter)
 
421
                                   wt.branch, log.ShortLogFormatter)
361
422
 
362
423
    def test_short_log_single_merge_revision(self):
363
424
        wt = self._prepare_tree_with_merges()
364
425
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
365
426
        rev = revspec.in_history(wt.branch)
366
 
        self.assertFormatterResult("""\
 
427
        self.assertFormatterResult(b"""\
367
428
      1.1.1 Joe Foo\t2005-11-22
368
429
            rev-merged
369
430
 
370
431
""",
371
 
            wt.branch, log.ShortLogFormatter,
372
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
432
                                   wt.branch, log.ShortLogFormatter,
 
433
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
434
 
 
435
    def test_show_ids(self):
 
436
        wt = self.make_branch_and_tree('parent')
 
437
        self.build_tree(['parent/f1', 'parent/f2'])
 
438
        wt.add(['f1', 'f2'])
 
439
        self.wt_commit(wt, 'first post', rev_id=b'a')
 
440
        child_wt = wt.controldir.sprout('child').open_workingtree()
 
441
        self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
 
442
        wt.merge_from_branch(child_wt.branch)
 
443
        self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
 
444
        self.assertFormatterResult(b"""\
 
445
    2 Joe Foo\t2005-11-22 [merge]
 
446
      revision-id:c
 
447
      merge branch 1
 
448
 
 
449
          1.1.1 Joe Foo\t2005-11-22
 
450
                revision-id:b
 
451
                branch 1 changes
 
452
 
 
453
    1 Joe Foo\t2005-11-22
 
454
      revision-id:a
 
455
      first post
 
456
 
 
457
""",
 
458
                                   wt.branch, log.ShortLogFormatter,
 
459
                                   formatter_kwargs=dict(levels=0, show_ids=True))
373
460
 
374
461
 
375
462
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
379
466
        # Note that the 1.1.1 indenting is in fact correct given that
380
467
        # the revision numbers are right justified within 5 characters
381
468
        # for mainline revnos and 9 characters for dotted revnos.
382
 
        self.assertFormatterResult("""\
 
469
        self.assertFormatterResult(b"""\
383
470
    2 Joe Foo\t2005-11-22 [merge]
384
471
      rev-2
385
472
 
390
477
      rev-1
391
478
 
392
479
""",
393
 
            wt.branch, log.ShortLogFormatter,
394
 
            formatter_kwargs=dict(levels=0))
 
480
                                   wt.branch, log.ShortLogFormatter,
 
481
                                   formatter_kwargs=dict(levels=0))
395
482
 
396
483
    def test_short_merge_revs_log_single_merge_revision(self):
397
484
        wt = self._prepare_tree_with_merges()
398
485
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
399
486
        rev = revspec.in_history(wt.branch)
400
 
        self.assertFormatterResult("""\
 
487
        self.assertFormatterResult(b"""\
401
488
      1.1.1 Joe Foo\t2005-11-22
402
489
            rev-merged
403
490
 
404
491
""",
405
 
            wt.branch, log.ShortLogFormatter,
406
 
            formatter_kwargs=dict(levels=0),
407
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
492
                                   wt.branch, log.ShortLogFormatter,
 
493
                                   formatter_kwargs=dict(levels=0),
 
494
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
408
495
 
409
496
 
410
497
class TestLongLogFormatter(TestCaseForLogFormatter):
415
502
        bug #4676
416
503
        """
417
504
        wt = self.make_standard_commit('test_verbose_log', authors=[])
418
 
        self.assertFormatterResult('''\
 
505
        self.assertFormatterResult(b'''\
419
506
------------------------------------------------------------
420
507
revno: 1
421
508
committer: Lorem Ipsum <test@example.com>
426
513
added:
427
514
  a
428
515
''',
429
 
            wt.branch, log.LongLogFormatter,
430
 
            show_log_kwargs=dict(verbose=True))
 
516
                                   wt.branch, log.LongLogFormatter,
 
517
                                   show_log_kwargs=dict(verbose=True))
431
518
 
432
519
    def test_merges_are_indented_by_level(self):
433
520
        wt = self.make_branch_and_tree('parent')
434
521
        self.wt_commit(wt, 'first post')
435
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
522
        child_wt = wt.controldir.sprout('child').open_workingtree()
436
523
        self.wt_commit(child_wt, 'branch 1')
437
 
        smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
 
524
        smallerchild_wt = wt.controldir.sprout(
 
525
            'smallerchild').open_workingtree()
438
526
        self.wt_commit(smallerchild_wt, 'branch 2')
439
527
        child_wt.merge_from_branch(smallerchild_wt.branch)
440
528
        self.wt_commit(child_wt, 'merge branch 2')
441
529
        wt.merge_from_branch(child_wt.branch)
442
530
        self.wt_commit(wt, 'merge branch 1')
443
 
        self.assertFormatterResult("""\
 
531
        self.assertFormatterResult(b"""\
444
532
------------------------------------------------------------
445
533
revno: 2 [merge]
446
534
committer: Joe Foo <joe@foo.com>
477
565
message:
478
566
  first post
479
567
""",
480
 
            wt.branch, log.LongLogFormatter,
481
 
            formatter_kwargs=dict(levels=0),
482
 
            show_log_kwargs=dict(verbose=True))
 
568
                                   wt.branch, log.LongLogFormatter,
 
569
                                   formatter_kwargs=dict(levels=0),
 
570
                                   show_log_kwargs=dict(verbose=True))
483
571
 
484
572
    def test_verbose_merge_revisions_contain_deltas(self):
485
573
        wt = self.make_branch_and_tree('parent')
486
574
        self.build_tree(['parent/f1', 'parent/f2'])
487
 
        wt.add(['f1','f2'])
 
575
        wt.add(['f1', 'f2'])
488
576
        self.wt_commit(wt, 'first post')
489
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
577
        child_wt = wt.controldir.sprout('child').open_workingtree()
490
578
        os.unlink('child/f1')
491
 
        self.build_tree_contents([('child/f2', 'hello\n')])
 
579
        self.build_tree_contents([('child/f2', b'hello\n')])
492
580
        self.wt_commit(child_wt, 'removed f1 and modified f2')
493
581
        wt.merge_from_branch(child_wt.branch)
494
582
        self.wt_commit(wt, 'merge branch 1')
495
 
        self.assertFormatterResult("""\
 
583
        self.assertFormatterResult(b"""\
496
584
------------------------------------------------------------
497
585
revno: 2 [merge]
498
586
committer: Joe Foo <joe@foo.com>
526
614
  f1
527
615
  f2
528
616
""",
529
 
            wt.branch, log.LongLogFormatter,
530
 
            formatter_kwargs=dict(levels=0),
531
 
            show_log_kwargs=dict(verbose=True))
 
617
                                   wt.branch, log.LongLogFormatter,
 
618
                                   formatter_kwargs=dict(levels=0),
 
619
                                   show_log_kwargs=dict(verbose=True))
532
620
 
533
621
    def test_trailing_newlines(self):
534
622
        wt = self.make_branch_and_tree('.')
535
623
        b = self.make_commits_with_trailing_newlines(wt)
536
 
        self.assertFormatterResult("""\
 
624
        self.assertFormatterResult(b"""\
537
625
------------------------------------------------------------
538
626
revno: 3
539
627
committer: Joe Foo <joe@foo.com>
558
646
message:
559
647
  simple log message
560
648
""",
561
 
        b, log.LongLogFormatter)
 
649
                                   b, log.LongLogFormatter)
562
650
 
563
651
    def test_author_in_log(self):
564
652
        """Log includes the author name if it's set in
565
653
        the revision properties
566
654
        """
567
655
        wt = self.make_standard_commit('test_author_log',
568
 
            authors=['John Doe <jdoe@example.com>',
569
 
                     'Jane Rey <jrey@example.com>'])
570
 
        self.assertFormatterResult("""\
 
656
                                       authors=['John Doe <jdoe@example.com>',
 
657
                                                'Jane Rey <jrey@example.com>'])
 
658
        self.assertFormatterResult(b"""\
571
659
------------------------------------------------------------
572
660
revno: 1
573
661
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
577
665
message:
578
666
  add a
579
667
""",
580
 
        wt.branch, log.LongLogFormatter)
 
668
                                   wt.branch, log.LongLogFormatter)
581
669
 
582
670
    def test_properties_in_log(self):
583
671
        """Log includes the custom properties returned by the registered
584
672
        handlers.
585
673
        """
586
674
        wt = self.make_standard_commit('test_properties_in_log')
 
675
 
587
676
        def trivial_custom_prop_handler(revision):
588
 
            return {'test_prop':'test_value'}
 
677
            return {'test_prop': 'test_value'}
589
678
 
590
679
        # Cleaned up in setUp()
591
680
        log.properties_handler_registry.register(
592
681
            'trivial_custom_prop_handler',
593
682
            trivial_custom_prop_handler)
594
 
        self.assertFormatterResult("""\
 
683
        self.assertFormatterResult(b"""\
595
684
------------------------------------------------------------
596
685
revno: 1
597
686
test_prop: test_value
602
691
message:
603
692
  add a
604
693
""",
605
 
            wt.branch, log.LongLogFormatter)
 
694
                                   wt.branch, log.LongLogFormatter)
606
695
 
607
696
    def test_properties_in_short_log(self):
608
697
        """Log includes the custom properties returned by the registered
609
698
        handlers.
610
699
        """
611
700
        wt = self.make_standard_commit('test_properties_in_short_log')
 
701
 
612
702
        def trivial_custom_prop_handler(revision):
613
 
            return {'test_prop':'test_value'}
 
703
            return {'test_prop': 'test_value'}
614
704
 
615
705
        log.properties_handler_registry.register(
616
706
            'trivial_custom_prop_handler',
617
707
            trivial_custom_prop_handler)
618
 
        self.assertFormatterResult("""\
 
708
        self.assertFormatterResult(b"""\
619
709
    1 John Doe\t2005-11-22
620
710
      test_prop: test_value
621
711
      add a
622
712
 
623
713
""",
624
 
            wt.branch, log.ShortLogFormatter)
 
714
                                   wt.branch, log.ShortLogFormatter)
625
715
 
626
716
    def test_error_in_properties_handler(self):
627
717
        """Log includes the custom properties returned by the registered
628
718
        handlers.
629
719
        """
630
720
        wt = self.make_standard_commit('error_in_properties_handler',
631
 
            revprops={'first_prop':'first_value'})
 
721
                                       revprops={u'first_prop': 'first_value'})
632
722
        sio = self.make_utf8_encoded_stringio()
633
723
        formatter = log.LongLogFormatter(to_file=sio)
 
724
 
634
725
        def trivial_custom_prop_handler(revision):
635
 
            raise StandardError("a test error")
 
726
            raise Exception("a test error")
636
727
 
637
728
        log.properties_handler_registry.register(
638
729
            'trivial_custom_prop_handler',
639
730
            trivial_custom_prop_handler)
640
 
        self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
 
731
        log.show_log(wt.branch, formatter)
 
732
        self.assertContainsRe(
 
733
            sio.getvalue(), b'brz: ERROR: Exception: a test error')
641
734
 
642
735
    def test_properties_handler_bad_argument(self):
643
736
        wt = self.make_standard_commit('bad_argument',
644
 
              revprops={'a_prop':'test_value'})
 
737
                                       revprops={u'a_prop': 'test_value'})
645
738
        sio = self.make_utf8_encoded_stringio()
646
739
        formatter = log.LongLogFormatter(to_file=sio)
 
740
 
647
741
        def bad_argument_prop_handler(revision):
648
 
            return {'custom_prop_name':revision.properties['a_prop']}
 
742
            return {'custom_prop_name': revision.properties['a_prop']}
649
743
 
650
744
        log.properties_handler_registry.register(
651
745
            'bad_argument_prop_handler',
656
750
 
657
751
        revision = wt.branch.repository.get_revision(wt.branch.last_revision())
658
752
        formatter.show_properties(revision, '')
659
 
        self.assertEqualDiff('''custom_prop_name: test_value\n''',
 
753
        self.assertEqualDiff(b'custom_prop_name: test_value\n',
660
754
                             sio.getvalue())
661
755
 
 
756
    def test_show_ids(self):
 
757
        wt = self.make_branch_and_tree('parent')
 
758
        self.build_tree(['parent/f1', 'parent/f2'])
 
759
        wt.add(['f1', 'f2'])
 
760
        self.wt_commit(wt, 'first post', rev_id=b'a')
 
761
        child_wt = wt.controldir.sprout('child').open_workingtree()
 
762
        self.wt_commit(child_wt, 'branch 1 changes', rev_id=b'b')
 
763
        wt.merge_from_branch(child_wt.branch)
 
764
        self.wt_commit(wt, 'merge branch 1', rev_id=b'c')
 
765
        self.assertFormatterResult(b"""\
 
766
------------------------------------------------------------
 
767
revno: 2 [merge]
 
768
revision-id: c
 
769
parent: a
 
770
parent: b
 
771
committer: Joe Foo <joe@foo.com>
 
772
branch nick: parent
 
773
timestamp: Tue 2005-11-22 00:00:02 +0000
 
774
message:
 
775
  merge branch 1
 
776
    ------------------------------------------------------------
 
777
    revno: 1.1.1
 
778
    revision-id: b
 
779
    parent: a
 
780
    committer: Joe Foo <joe@foo.com>
 
781
    branch nick: child
 
782
    timestamp: Tue 2005-11-22 00:00:01 +0000
 
783
    message:
 
784
      branch 1 changes
 
785
------------------------------------------------------------
 
786
revno: 1
 
787
revision-id: a
 
788
committer: Joe Foo <joe@foo.com>
 
789
branch nick: parent
 
790
timestamp: Tue 2005-11-22 00:00:00 +0000
 
791
message:
 
792
  first post
 
793
""",
 
794
                                   wt.branch, log.LongLogFormatter,
 
795
                                   formatter_kwargs=dict(levels=0, show_ids=True))
 
796
 
662
797
 
663
798
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
664
799
 
668
803
        bug #4676
669
804
        """
670
805
        wt = self.make_standard_commit('test_long_verbose_log', authors=[])
671
 
        self.assertFormatterResult("""\
 
806
        self.assertFormatterResult(b"""\
672
807
------------------------------------------------------------
673
808
revno: 1
674
809
committer: Lorem Ipsum <test@example.com>
679
814
added:
680
815
  a
681
816
""",
682
 
            wt.branch, log.LongLogFormatter,
683
 
            formatter_kwargs=dict(levels=1),
684
 
            show_log_kwargs=dict(verbose=True))
 
817
                                   wt.branch, log.LongLogFormatter,
 
818
                                   formatter_kwargs=dict(levels=1),
 
819
                                   show_log_kwargs=dict(verbose=True))
685
820
 
686
821
    def test_long_verbose_contain_deltas(self):
687
822
        wt = self.make_branch_and_tree('parent')
688
823
        self.build_tree(['parent/f1', 'parent/f2'])
689
 
        wt.add(['f1','f2'])
 
824
        wt.add(['f1', 'f2'])
690
825
        self.wt_commit(wt, 'first post')
691
 
        child_wt = wt.bzrdir.sprout('child').open_workingtree()
 
826
        child_wt = wt.controldir.sprout('child').open_workingtree()
692
827
        os.unlink('child/f1')
693
 
        self.build_tree_contents([('child/f2', 'hello\n')])
 
828
        self.build_tree_contents([('child/f2', b'hello\n')])
694
829
        self.wt_commit(child_wt, 'removed f1 and modified f2')
695
830
        wt.merge_from_branch(child_wt.branch)
696
831
        self.wt_commit(wt, 'merge branch 1')
697
 
        self.assertFormatterResult("""\
 
832
        self.assertFormatterResult(b"""\
698
833
------------------------------------------------------------
699
834
revno: 2 [merge]
700
835
committer: Joe Foo <joe@foo.com>
717
852
  f1
718
853
  f2
719
854
""",
720
 
            wt.branch, log.LongLogFormatter,
721
 
            formatter_kwargs=dict(levels=1),
722
 
            show_log_kwargs=dict(verbose=True))
 
855
                                   wt.branch, log.LongLogFormatter,
 
856
                                   formatter_kwargs=dict(levels=1),
 
857
                                   show_log_kwargs=dict(verbose=True))
723
858
 
724
859
    def test_long_trailing_newlines(self):
725
860
        wt = self.make_branch_and_tree('.')
726
861
        b = self.make_commits_with_trailing_newlines(wt)
727
 
        self.assertFormatterResult("""\
 
862
        self.assertFormatterResult(b"""\
728
863
------------------------------------------------------------
729
864
revno: 3
730
865
committer: Joe Foo <joe@foo.com>
749
884
message:
750
885
  simple log message
751
886
""",
752
 
        b, log.LongLogFormatter,
753
 
        formatter_kwargs=dict(levels=1))
 
887
                                   b, log.LongLogFormatter,
 
888
                                   formatter_kwargs=dict(levels=1))
754
889
 
755
890
    def test_long_author_in_log(self):
756
891
        """Log includes the author name if it's set in
757
892
        the revision properties
758
893
        """
759
894
        wt = self.make_standard_commit('test_author_log')
760
 
        self.assertFormatterResult("""\
 
895
        self.assertFormatterResult(b"""\
761
896
------------------------------------------------------------
762
897
revno: 1
763
898
author: John Doe <jdoe@example.com>
767
902
message:
768
903
  add a
769
904
""",
770
 
            wt.branch, log.LongLogFormatter,
771
 
            formatter_kwargs=dict(levels=1))
 
905
                                   wt.branch, log.LongLogFormatter,
 
906
                                   formatter_kwargs=dict(levels=1))
772
907
 
773
908
    def test_long_properties_in_log(self):
774
909
        """Log includes the custom properties returned by the registered
775
910
        handlers.
776
911
        """
777
912
        wt = self.make_standard_commit('test_properties_in_log')
 
913
 
778
914
        def trivial_custom_prop_handler(revision):
779
 
            return {'test_prop':'test_value'}
 
915
            return {'test_prop': 'test_value'}
780
916
 
781
917
        log.properties_handler_registry.register(
782
918
            'trivial_custom_prop_handler',
783
919
            trivial_custom_prop_handler)
784
 
        self.assertFormatterResult("""\
 
920
        self.assertFormatterResult(b"""\
785
921
------------------------------------------------------------
786
922
revno: 1
787
923
test_prop: test_value
792
928
message:
793
929
  add a
794
930
""",
795
 
            wt.branch, log.LongLogFormatter,
796
 
            formatter_kwargs=dict(levels=1))
 
931
                                   wt.branch, log.LongLogFormatter,
 
932
                                   formatter_kwargs=dict(levels=1))
797
933
 
798
934
 
799
935
class TestLineLogFormatter(TestCaseForLogFormatter):
804
940
        bug #5162
805
941
        """
806
942
        wt = self.make_standard_commit('test-line-log',
807
 
                committer='Line-Log-Formatter Tester <test@line.log>',
808
 
                authors=[])
809
 
        self.assertFormatterResult("""\
 
943
                                       committer='Line-Log-Formatter Tester <test@line.log>',
 
944
                                       authors=[])
 
945
        self.assertFormatterResult(b"""\
810
946
1: Line-Log-Formatte... 2005-11-22 add a
811
947
""",
812
 
            wt.branch, log.LineLogFormatter)
 
948
                                   wt.branch, log.LineLogFormatter)
813
949
 
814
950
    def test_trailing_newlines(self):
815
951
        wt = self.make_branch_and_tree('.')
816
952
        b = self.make_commits_with_trailing_newlines(wt)
817
 
        self.assertFormatterResult("""\
 
953
        self.assertFormatterResult(b"""\
818
954
3: Joe Foo 2005-11-22 single line with trailing newline
819
955
2: Joe Foo 2005-11-22 multiline
820
956
1: Joe Foo 2005-11-22 simple log message
821
957
""",
822
 
            b, log.LineLogFormatter)
 
958
                                   b, log.LineLogFormatter)
823
959
 
824
960
    def test_line_log_single_merge_revision(self):
825
961
        wt = self._prepare_tree_with_merges()
826
962
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
827
963
        rev = revspec.in_history(wt.branch)
828
 
        self.assertFormatterResult("""\
 
964
        self.assertFormatterResult(b"""\
829
965
1.1.1: Joe Foo 2005-11-22 rev-merged
830
966
""",
831
 
            wt.branch, log.LineLogFormatter,
832
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
967
                                   wt.branch, log.LineLogFormatter,
 
968
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
833
969
 
834
970
    def test_line_log_with_tags(self):
835
971
        wt = self._prepare_tree_with_merges(with_tags=True)
836
 
        self.assertFormatterResult("""\
 
972
        self.assertFormatterResult(b"""\
837
973
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
838
974
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
839
975
1: Joe Foo 2005-11-22 rev-1
840
976
""",
841
 
            wt.branch, log.LineLogFormatter)
 
977
                                   wt.branch, log.LineLogFormatter)
842
978
 
843
979
 
844
980
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
849
985
        bug #5162
850
986
        """
851
987
        wt = self.make_standard_commit('test-line-log',
852
 
                committer='Line-Log-Formatter Tester <test@line.log>',
853
 
                authors=[])
854
 
        self.assertFormatterResult("""\
 
988
                                       committer='Line-Log-Formatter Tester <test@line.log>',
 
989
                                       authors=[])
 
990
        self.assertFormatterResult(b"""\
855
991
1: Line-Log-Formatte... 2005-11-22 add a
856
992
""",
857
 
            wt.branch, log.LineLogFormatter)
 
993
                                   wt.branch, log.LineLogFormatter)
858
994
 
859
995
    def test_line_merge_revs_log_single_merge_revision(self):
860
996
        wt = self._prepare_tree_with_merges()
861
997
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
862
998
        rev = revspec.in_history(wt.branch)
863
 
        self.assertFormatterResult("""\
 
999
        self.assertFormatterResult(b"""\
864
1000
1.1.1: Joe Foo 2005-11-22 rev-merged
865
1001
""",
866
 
            wt.branch, log.LineLogFormatter,
867
 
            formatter_kwargs=dict(levels=0),
868
 
            show_log_kwargs=dict(start_revision=rev, end_revision=rev))
 
1002
                                   wt.branch, log.LineLogFormatter,
 
1003
                                   formatter_kwargs=dict(levels=0),
 
1004
                                   show_log_kwargs=dict(start_revision=rev, end_revision=rev))
869
1005
 
870
1006
    def test_line_merge_revs_log_with_merges(self):
871
1007
        wt = self._prepare_tree_with_merges()
872
 
        self.assertFormatterResult("""\
 
1008
        self.assertFormatterResult(b"""\
873
1009
2: Joe Foo 2005-11-22 [merge] rev-2
874
1010
  1.1.1: Joe Foo 2005-11-22 rev-merged
875
1011
1: Joe Foo 2005-11-22 rev-1
876
1012
""",
877
 
            wt.branch, log.LineLogFormatter,
878
 
            formatter_kwargs=dict(levels=0))
 
1013
                                   wt.branch, log.LineLogFormatter,
 
1014
                                   formatter_kwargs=dict(levels=0))
879
1015
 
880
1016
 
881
1017
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
882
1018
 
883
1019
    def test_gnu_changelog(self):
884
1020
        wt = self.make_standard_commit('nicky', authors=[])
885
 
        self.assertFormatterResult('''\
 
1021
        self.assertFormatterResult(b'''\
886
1022
2005-11-22  Lorem Ipsum  <test@example.com>
887
1023
 
888
1024
\tadd a
889
1025
 
890
1026
''',
891
 
            wt.branch, log.GnuChangelogLogFormatter)
 
1027
                                   wt.branch, log.GnuChangelogLogFormatter)
892
1028
 
893
1029
    def test_with_authors(self):
894
1030
        wt = self.make_standard_commit('nicky',
895
 
            authors=['Fooa Fooz <foo@example.com>',
896
 
                     'Bari Baro <bar@example.com>'])
897
 
        self.assertFormatterResult('''\
 
1031
                                       authors=['Fooa Fooz <foo@example.com>',
 
1032
                                                'Bari Baro <bar@example.com>'])
 
1033
        self.assertFormatterResult(b'''\
898
1034
2005-11-22  Fooa Fooz  <foo@example.com>
899
1035
 
900
1036
\tadd a
901
1037
 
902
1038
''',
903
 
            wt.branch, log.GnuChangelogLogFormatter)
 
1039
                                   wt.branch, log.GnuChangelogLogFormatter)
904
1040
 
905
1041
    def test_verbose(self):
906
1042
        wt = self.make_standard_commit('nicky')
907
 
        self.assertFormatterResult('''\
 
1043
        self.assertFormatterResult(b'''\
908
1044
2005-11-22  John Doe  <jdoe@example.com>
909
1045
 
910
1046
\t* a:
912
1048
\tadd a
913
1049
 
914
1050
''',
915
 
            wt.branch, log.GnuChangelogLogFormatter,
916
 
            show_log_kwargs=dict(verbose=True))
917
 
 
918
 
class TestGetViewRevisions(tests.TestCaseWithTransport, TestLogMixin):
919
 
 
920
 
    def _get_view_revisions(self, *args, **kwargs):
921
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
922
 
                                    log.get_view_revisions, *args, **kwargs)
923
 
 
924
 
    def make_tree_with_commits(self):
925
 
        """Create a tree with well-known revision ids"""
926
 
        wt = self.make_branch_and_tree('tree1')
927
 
        self.wt_commit(wt, 'commit one', rev_id='1')
928
 
        self.wt_commit(wt, 'commit two', rev_id='2')
929
 
        self.wt_commit(wt, 'commit three', rev_id='3')
930
 
        mainline_revs = [None, '1', '2', '3']
931
 
        rev_nos = {'1': 1, '2': 2, '3': 3}
932
 
        return mainline_revs, rev_nos, wt
933
 
 
934
 
    def make_tree_with_merges(self):
935
 
        """Create a tree with well-known revision ids and a merge"""
936
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
937
 
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
938
 
        self.wt_commit(tree2, 'four-a', rev_id='4a')
939
 
        wt.merge_from_branch(tree2.branch)
940
 
        self.wt_commit(wt, 'four-b', rev_id='4b')
941
 
        mainline_revs.append('4b')
942
 
        rev_nos['4b'] = 4
943
 
        # 4a: 3.1.1
944
 
        return mainline_revs, rev_nos, wt
945
 
 
946
 
    def make_branch_with_many_merges(self):
947
 
        """Create a tree with well-known revision ids"""
948
 
        builder = self.make_branch_builder('tree1')
949
 
        builder.start_series()
950
 
        builder.build_snapshot('1', None, [
951
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),
952
 
            ('add', ('f', 'f-id', 'file', '1\n'))])
953
 
        builder.build_snapshot('2', ['1'], [])
954
 
        builder.build_snapshot('3a', ['2'], [
955
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
956
 
        builder.build_snapshot('3b', ['2', '3a'], [
957
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
958
 
        builder.build_snapshot('3c', ['2', '3b'], [
959
 
            ('modify', ('f-id', '1\n2\n3a\n'))])
960
 
        builder.build_snapshot('4a', ['3b'], [])
961
 
        builder.build_snapshot('4b', ['3c', '4a'], [])
962
 
        builder.finish_series()
963
 
 
964
 
        # 1
965
 
        # |
966
 
        # 2-.
967
 
        # |\ \
968
 
        # | | 3a
969
 
        # | |/
970
 
        # | 3b
971
 
        # |/|
972
 
        # 3c4a
973
 
        # |/
974
 
        # 4b
975
 
 
976
 
        mainline_revs = [None, '1', '2', '3c', '4b']
977
 
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
978
 
        full_rev_nos_for_reference = {
979
 
            '1': '1',
980
 
            '2': '2',
981
 
            '3a': '2.1.1', #first commit tree 3
982
 
            '3b': '2.2.1', # first commit tree 2
983
 
            '3c': '3', #merges 3b to main
984
 
            '4a': '2.2.2', # second commit tree 2
985
 
            '4b': '4', # merges 4a to main
986
 
            }
987
 
        return mainline_revs, rev_nos, builder.get_branch()
988
 
 
989
 
    def test_get_view_revisions_forward(self):
990
 
        """Test the get_view_revisions method"""
991
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
992
 
        wt.lock_read()
993
 
        self.addCleanup(wt.unlock)
994
 
        revisions = list(self._get_view_revisions(
995
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
996
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
997
 
                         revisions)
998
 
        revisions2 = list(self._get_view_revisions(
999
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1000
 
                include_merges=False))
1001
 
        self.assertEqual(revisions, revisions2)
1002
 
 
1003
 
    def test_get_view_revisions_reverse(self):
1004
 
        """Test the get_view_revisions with reverse"""
1005
 
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
1006
 
        wt.lock_read()
1007
 
        self.addCleanup(wt.unlock)
1008
 
        revisions = list(self._get_view_revisions(
1009
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1010
 
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
1011
 
                         revisions)
1012
 
        revisions2 = list(self._get_view_revisions(
1013
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1014
 
                include_merges=False))
1015
 
        self.assertEqual(revisions, revisions2)
1016
 
 
1017
 
    def test_get_view_revisions_merge(self):
1018
 
        """Test get_view_revisions when there are merges"""
1019
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1020
 
        wt.lock_read()
1021
 
        self.addCleanup(wt.unlock)
1022
 
        revisions = list(self._get_view_revisions(
1023
 
                mainline_revs, rev_nos, wt.branch, 'forward'))
1024
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1025
 
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
1026
 
                         revisions)
1027
 
        revisions = list(self._get_view_revisions(
1028
 
                mainline_revs, rev_nos, wt.branch, 'forward',
1029
 
                include_merges=False))
1030
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
1031
 
                          ('4b', '4', 0)],
1032
 
                         revisions)
1033
 
 
1034
 
    def test_get_view_revisions_merge_reverse(self):
1035
 
        """Test get_view_revisions in reverse when there are merges"""
1036
 
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
1037
 
        wt.lock_read()
1038
 
        self.addCleanup(wt.unlock)
1039
 
        revisions = list(self._get_view_revisions(
1040
 
                mainline_revs, rev_nos, wt.branch, 'reverse'))
1041
 
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
1042
 
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
1043
 
                         revisions)
1044
 
        revisions = list(self._get_view_revisions(
1045
 
                mainline_revs, rev_nos, wt.branch, 'reverse',
1046
 
                include_merges=False))
1047
 
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
1048
 
                          ('1', '1', 0)],
1049
 
                         revisions)
1050
 
 
1051
 
    def test_get_view_revisions_merge2(self):
1052
 
        """Test get_view_revisions when there are merges"""
1053
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1054
 
        b.lock_read()
1055
 
        self.addCleanup(b.unlock)
1056
 
        revisions = list(self._get_view_revisions(
1057
 
                mainline_revs, rev_nos, b, 'forward'))
1058
 
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1059
 
                    ('3b', '2.2.1', 1), ('3a', '2.1.1', 2), ('4b', '4', 0),
1060
 
                    ('4a', '2.2.2', 1)]
1061
 
        self.assertEqual(expected, revisions)
1062
 
        revisions = list(self._get_view_revisions(
1063
 
                mainline_revs, rev_nos, b, 'forward',
1064
 
                include_merges=False))
1065
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
1066
 
                          ('4b', '4', 0)],
1067
 
                         revisions)
1068
 
 
1069
 
    def test_file_id_for_range(self):
1070
 
        mainline_revs, rev_nos, b = self.make_branch_with_many_merges()
1071
 
        b.lock_read()
1072
 
        self.addCleanup(b.unlock)
1073
 
 
1074
 
        def rev_from_rev_id(revid, branch):
1075
 
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
1076
 
            return revspec.in_history(branch)
1077
 
 
1078
 
        def view_revs(start_rev, end_rev, file_id, direction):
1079
 
            revs = self.applyDeprecated(
1080
 
                symbol_versioning.deprecated_in((2, 2, 0)),
1081
 
                log.calculate_view_revisions,
1082
 
                b,
1083
 
                start_rev, # start_revision
1084
 
                end_rev, # end_revision
1085
 
                direction, # direction
1086
 
                file_id, # specific_fileid
1087
 
                True, # generate_merge_revisions
1088
 
                )
1089
 
            return revs
1090
 
 
1091
 
        rev_3a = rev_from_rev_id('3a', b)
1092
 
        rev_4b = rev_from_rev_id('4b', b)
1093
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1094
 
                          ('3a', '2.1.1', 2)],
1095
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
1096
 
        # Note: 3c still appears before 3a here because of depth-based sorting
1097
 
        self.assertEqual([('3c', '3', 0), ('3b', '2.2.1', 1),
1098
 
                          ('3a', '2.1.1', 2)],
1099
 
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
1100
 
 
1101
 
 
1102
 
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
1103
 
 
1104
 
    def get_view_revisions(self, *args):
1105
 
        return self.applyDeprecated(symbol_versioning.deprecated_in((2, 2, 0)),
1106
 
                                    log.get_view_revisions, *args)
1107
 
 
1108
 
    def create_tree_with_single_merge(self):
1109
 
        """Create a branch with a moderate layout.
1110
 
 
1111
 
        The revision graph looks like:
1112
 
 
1113
 
           A
1114
 
           |\
1115
 
           B C
1116
 
           |/
1117
 
           D
1118
 
 
1119
 
        In this graph, A introduced files f1 and f2 and f3.
1120
 
        B modifies f1 and f3, and C modifies f2 and f3.
1121
 
        D merges the changes from B and C and resolves the conflict for f3.
1122
 
        """
1123
 
        # TODO: jam 20070218 This seems like it could really be done
1124
 
        #       with make_branch_and_memory_tree() if we could just
1125
 
        #       create the content of those files.
1126
 
        # TODO: jam 20070218 Another alternative is that we would really
1127
 
        #       like to only create this tree 1 time for all tests that
1128
 
        #       use it. Since 'log' only uses the tree in a readonly
1129
 
        #       fashion, it seems a shame to regenerate an identical
1130
 
        #       tree for each test.
1131
 
        # TODO: vila 20100122 One way to address the shame above will be to
1132
 
        #       create a memory tree during test parametrization and give a
1133
 
        #       *copy* of this tree to each test. Copying a memory tree ought
1134
 
        #       to be cheap, at least cheaper than creating them with such
1135
 
        #       complex setups.
1136
 
        tree = self.make_branch_and_tree('tree')
1137
 
        tree.lock_write()
1138
 
        self.addCleanup(tree.unlock)
1139
 
 
1140
 
        self.build_tree_contents([('tree/f1', 'A\n'),
1141
 
                                  ('tree/f2', 'A\n'),
1142
 
                                  ('tree/f3', 'A\n'),
1143
 
                                 ])
1144
 
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
1145
 
        tree.commit('A', rev_id='A')
1146
 
 
1147
 
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
1148
 
                                  ('tree/f3', 'A\nC\n'),
1149
 
                                 ])
1150
 
        tree.commit('C', rev_id='C')
1151
 
        # Revert back to A to build the other history.
1152
 
        tree.set_last_revision('A')
1153
 
        tree.branch.set_last_revision_info(1, 'A')
1154
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1155
 
                                  ('tree/f2', 'A\n'),
1156
 
                                  ('tree/f3', 'A\nB\n'),
1157
 
                                 ])
1158
 
        tree.commit('B', rev_id='B')
1159
 
        tree.set_parent_ids(['B', 'C'])
1160
 
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
1161
 
                                  ('tree/f2', 'A\nC\n'),
1162
 
                                  ('tree/f3', 'A\nB\nC\n'),
1163
 
                                 ])
1164
 
        tree.commit('D', rev_id='D')
1165
 
 
1166
 
        # Switch to a read lock for this tree.
1167
 
        # We still have an addCleanup(tree.unlock) pending
1168
 
        tree.unlock()
1169
 
        tree.lock_read()
1170
 
        return tree
1171
 
 
1172
 
    def check_delta(self, delta, **kw):
1173
 
        """Check the filenames touched by a delta are as expected.
1174
 
 
1175
 
        Caller only have to pass in the list of files for each part, all
1176
 
        unspecified parts are considered empty (and checked as such).
1177
 
        """
1178
 
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
1179
 
            # By default we expect an empty list
1180
 
            expected = kw.get(n, [])
1181
 
            # strip out only the path components
1182
 
            got = [x[0] for x in getattr(delta, n)]
1183
 
            self.assertEqual(expected, got)
1184
 
 
1185
 
    def test_tree_with_single_merge(self):
1186
 
        """Make sure the tree layout is correct."""
1187
 
        tree = self.create_tree_with_single_merge()
1188
 
        rev_A_tree = tree.branch.repository.revision_tree('A')
1189
 
        rev_B_tree = tree.branch.repository.revision_tree('B')
1190
 
        rev_C_tree = tree.branch.repository.revision_tree('C')
1191
 
        rev_D_tree = tree.branch.repository.revision_tree('D')
1192
 
 
1193
 
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
1194
 
                         modified=['f1', 'f3'])
1195
 
 
1196
 
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
1197
 
                         modified=['f2', 'f3'])
1198
 
 
1199
 
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
1200
 
                         modified=['f2', 'f3'])
1201
 
 
1202
 
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
1203
 
                         modified=['f1', 'f3'])
1204
 
 
1205
 
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
1206
 
        """Ensure _filter_revisions_touching_file_id returns the right values.
1207
 
 
1208
 
        Get the return value from _filter_revisions_touching_file_id and make
1209
 
        sure they are correct.
1210
 
        """
1211
 
        # The api for _filter_revisions_touching_file_id is a little crazy.
1212
 
        # So we do the setup here.
1213
 
        mainline = tree.branch.revision_history()
1214
 
        mainline.insert(0, None)
1215
 
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
1216
 
        view_revs_iter = self.get_view_revisions(
1217
 
            mainline, revnos, tree.branch, 'reverse', True)
1218
 
        actual_revs = log._filter_revisions_touching_file_id(
1219
 
            tree.branch, file_id, list(view_revs_iter))
1220
 
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
1221
 
 
1222
 
    def test_file_id_f1(self):
1223
 
        tree = self.create_tree_with_single_merge()
1224
 
        # f1 should be marked as modified by revisions A and B
1225
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
1226
 
 
1227
 
    def test_file_id_f2(self):
1228
 
        tree = self.create_tree_with_single_merge()
1229
 
        # f2 should be marked as modified by revisions A, C, and D
1230
 
        # because D merged the changes from C.
1231
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1232
 
 
1233
 
    def test_file_id_f3(self):
1234
 
        tree = self.create_tree_with_single_merge()
1235
 
        # f3 should be marked as modified by revisions A, B, C, and D
1236
 
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1237
 
 
1238
 
    def test_file_id_with_ghosts(self):
1239
 
        # This is testing bug #209948, where having a ghost would cause
1240
 
        # _filter_revisions_touching_file_id() to fail.
1241
 
        tree = self.create_tree_with_single_merge()
1242
 
        # We need to add a revision, so switch back to a write-locked tree
1243
 
        # (still a single addCleanup(tree.unlock) pending).
1244
 
        tree.unlock()
1245
 
        tree.lock_write()
1246
 
        first_parent = tree.last_revision()
1247
 
        tree.set_parent_ids([first_parent, 'ghost-revision-id'])
1248
 
        self.build_tree_contents([('tree/f1', 'A\nB\nXX\n')])
1249
 
        tree.commit('commit with a ghost', rev_id='XX')
1250
 
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['XX', 'B', 'A'])
1251
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1252
 
 
1253
 
    def test_unknown_file_id(self):
1254
 
        tree = self.create_tree_with_single_merge()
1255
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
1256
 
 
1257
 
    def test_empty_branch_unknown_file_id(self):
1258
 
        tree = self.make_branch_and_tree('tree')
1259
 
        self.assertAllRevisionsForFileID(tree, 'unknown', [])
 
1051
                                   wt.branch, log.GnuChangelogLogFormatter,
 
1052
                                   show_log_kwargs=dict(verbose=True))
1260
1053
 
1261
1054
 
1262
1055
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1265
1058
        tree = self.make_branch_and_tree('tree_a')
1266
1059
        self.build_tree(['tree_a/foo'])
1267
1060
        tree.add('foo')
1268
 
        tree.commit('bar', rev_id='bar-id')
 
1061
        tree.commit('bar', rev_id=b'bar-id')
1269
1062
        s = self.make_utf8_encoded_stringio()
1270
 
        log.show_changed_revisions(tree.branch, [], ['bar-id'], s)
1271
 
        self.assertContainsRe(s.getvalue(), 'bar')
1272
 
        self.assertNotContainsRe(s.getvalue(), 'foo')
 
1063
        log.show_changed_revisions(tree.branch, [], [b'bar-id'], s)
 
1064
        self.assertContainsRe(s.getvalue(), b'bar')
 
1065
        self.assertNotContainsRe(s.getvalue(), b'foo')
1273
1066
 
1274
1067
 
1275
1068
class TestLogFormatter(tests.TestCase):
1276
1069
 
1277
1070
    def setUp(self):
1278
1071
        super(TestLogFormatter, self).setUp()
1279
 
        self.rev = revision.Revision('a-id')
 
1072
        self.rev = revision.Revision(b'a-id')
1280
1073
        self.lf = log.LogFormatter(None)
1281
1074
 
1282
1075
    def test_short_committer(self):
1330
1123
            Tests use (revno, depth) whil the API expects (revid, revno, depth).
1331
1124
            Since the revid is arbitrary, we just duplicate revno
1332
1125
            """
1333
 
            return [ (r, r, d) for r, d in l]
 
1126
            return [(r, r, d) for r, d in l]
1334
1127
        forward = complete_revisions(forward)
1335
 
        backward= complete_revisions(backward)
 
1128
        backward = complete_revisions(backward)
1336
1129
        self.assertEqual(forward, log.reverse_by_depth(backward))
1337
1130
 
1338
 
 
1339
1131
    def test_mainline_revisions(self):
1340
 
        self.assertReversed([( '1', 0), ('2', 0)],
 
1132
        self.assertReversed([('1', 0), ('2', 0)],
1341
1133
                            [('2', 0), ('1', 0)])
1342
1134
 
1343
1135
    def test_merged_revisions(self):
1344
 
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1345
 
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
 
1136
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1), ],
 
1137
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0), ])
 
1138
 
1346
1139
    def test_shifted_merged_revisions(self):
1347
1140
        """Test irregular layout.
1348
1141
 
1349
1142
        Requesting revisions touching a file can produce "holes" in the depths.
1350
1143
        """
1351
 
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1352
 
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
 
1144
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2), ],
 
1145
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0), ])
1353
1146
 
1354
1147
    def test_merged_without_child_revisions(self):
1355
1148
        """Test irregular layout.
1359
1152
        # When a revision of higher depth doesn't follow one of lower depth, we
1360
1153
        # assume a lower depth one is virtually there
1361
1154
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1362
 
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2),])
 
1155
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2), ])
1363
1156
        # So we get the same order after reversing below even if the original
1364
1157
        # revisions are not in the same order.
1365
1158
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1366
 
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2),])
 
1159
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2), ])
1367
1160
 
1368
1161
 
1369
1162
class TestHistoryChange(tests.TestCaseWithTransport):
1372
1165
        tree = self.make_branch_and_tree('tree')
1373
1166
        tree.lock_write()
1374
1167
        self.addCleanup(tree.unlock)
1375
 
        tree.commit('1a', rev_id='1a')
1376
 
        tree.commit('2a', rev_id='2a')
1377
 
        tree.commit('3a', rev_id='3a')
 
1168
        tree.commit('1a', rev_id=b'1a')
 
1169
        tree.commit('2a', rev_id=b'2a')
 
1170
        tree.commit('3a', rev_id=b'3a')
1378
1171
        return tree
1379
1172
 
1380
1173
    def setup_ab_tree(self):
1381
1174
        tree = self.setup_a_tree()
1382
 
        tree.set_last_revision('1a')
1383
 
        tree.branch.set_last_revision_info(1, '1a')
1384
 
        tree.commit('2b', rev_id='2b')
1385
 
        tree.commit('3b', rev_id='3b')
 
1175
        tree.set_last_revision(b'1a')
 
1176
        tree.branch.set_last_revision_info(1, b'1a')
 
1177
        tree.commit('2b', rev_id=b'2b')
 
1178
        tree.commit('3b', rev_id=b'3b')
1386
1179
        return tree
1387
1180
 
1388
1181
    def setup_ac_tree(self):
1389
1182
        tree = self.setup_a_tree()
1390
1183
        tree.set_last_revision(revision.NULL_REVISION)
1391
1184
        tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1392
 
        tree.commit('1c', rev_id='1c')
1393
 
        tree.commit('2c', rev_id='2c')
1394
 
        tree.commit('3c', rev_id='3c')
 
1185
        tree.commit('1c', rev_id=b'1c')
 
1186
        tree.commit('2c', rev_id=b'2c')
 
1187
        tree.commit('3c', rev_id=b'3c')
1395
1188
        return tree
1396
1189
 
1397
1190
    def test_all_new(self):
1398
1191
        tree = self.setup_ab_tree()
1399
 
        old, new = log.get_history_change('1a', '3a', tree.branch.repository)
 
1192
        old, new = log.get_history_change(b'1a', b'3a', tree.branch.repository)
1400
1193
        self.assertEqual([], old)
1401
 
        self.assertEqual(['2a', '3a'], new)
 
1194
        self.assertEqual([b'2a', b'3a'], new)
1402
1195
 
1403
1196
    def test_all_old(self):
1404
1197
        tree = self.setup_ab_tree()
1405
 
        old, new = log.get_history_change('3a', '1a', tree.branch.repository)
 
1198
        old, new = log.get_history_change(b'3a', b'1a', tree.branch.repository)
1406
1199
        self.assertEqual([], new)
1407
 
        self.assertEqual(['2a', '3a'], old)
 
1200
        self.assertEqual([b'2a', b'3a'], old)
1408
1201
 
1409
1202
    def test_null_old(self):
1410
1203
        tree = self.setup_ab_tree()
1411
1204
        old, new = log.get_history_change(revision.NULL_REVISION,
1412
 
                                          '3a', tree.branch.repository)
 
1205
                                          b'3a', tree.branch.repository)
1413
1206
        self.assertEqual([], old)
1414
 
        self.assertEqual(['1a', '2a', '3a'], new)
 
1207
        self.assertEqual([b'1a', b'2a', b'3a'], new)
1415
1208
 
1416
1209
    def test_null_new(self):
1417
1210
        tree = self.setup_ab_tree()
1418
 
        old, new = log.get_history_change('3a', revision.NULL_REVISION,
 
1211
        old, new = log.get_history_change(b'3a', revision.NULL_REVISION,
1419
1212
                                          tree.branch.repository)
1420
1213
        self.assertEqual([], new)
1421
 
        self.assertEqual(['1a', '2a', '3a'], old)
 
1214
        self.assertEqual([b'1a', b'2a', b'3a'], old)
1422
1215
 
1423
1216
    def test_diverged(self):
1424
1217
        tree = self.setup_ab_tree()
1425
 
        old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1426
 
        self.assertEqual(old, ['2a', '3a'])
1427
 
        self.assertEqual(new, ['2b', '3b'])
 
1218
        old, new = log.get_history_change(b'3a', b'3b', tree.branch.repository)
 
1219
        self.assertEqual(old, [b'2a', b'3a'])
 
1220
        self.assertEqual(new, [b'2b', b'3b'])
1428
1221
 
1429
1222
    def test_unrelated(self):
1430
1223
        tree = self.setup_ac_tree()
1431
 
        old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1432
 
        self.assertEqual(old, ['1a', '2a', '3a'])
1433
 
        self.assertEqual(new, ['1c', '2c', '3c'])
 
1224
        old, new = log.get_history_change(b'3a', b'3c', tree.branch.repository)
 
1225
        self.assertEqual(old, [b'1a', b'2a', b'3a'])
 
1226
        self.assertEqual(new, [b'1c', b'2c', b'3c'])
1434
1227
 
1435
1228
    def test_show_branch_change(self):
1436
1229
        tree = self.setup_ab_tree()
1437
1230
        s = StringIO()
1438
 
        log.show_branch_change(tree.branch, s, 3, '3a')
 
1231
        log.show_branch_change(tree.branch, s, 3, b'3a')
1439
1232
        self.assertContainsRe(s.getvalue(),
1440
 
            '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1441
 
            '[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
 
1233
                              '[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
 
1234
                              '[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1442
1235
 
1443
1236
    def test_show_branch_change_no_change(self):
1444
1237
        tree = self.setup_ab_tree()
1445
1238
        s = StringIO()
1446
 
        log.show_branch_change(tree.branch, s, 3, '3b')
 
1239
        log.show_branch_change(tree.branch, s, 3, b'3b')
1447
1240
        self.assertEqual(s.getvalue(),
1448
 
            'Nothing seems to have changed\n')
 
1241
                         'Nothing seems to have changed\n')
1449
1242
 
1450
1243
    def test_show_branch_change_no_old(self):
1451
1244
        tree = self.setup_ab_tree()
1452
1245
        s = StringIO()
1453
 
        log.show_branch_change(tree.branch, s, 2, '2b')
 
1246
        log.show_branch_change(tree.branch, s, 2, b'2b')
1454
1247
        self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1455
1248
        self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1456
1249
 
1457
1250
    def test_show_branch_change_no_new(self):
1458
1251
        tree = self.setup_ab_tree()
1459
 
        tree.branch.set_last_revision_info(2, '2b')
 
1252
        tree.branch.set_last_revision_info(2, b'2b')
1460
1253
        s = StringIO()
1461
 
        log.show_branch_change(tree.branch, s, 3, '3b')
 
1254
        log.show_branch_change(tree.branch, s, 3, b'3b')
1462
1255
        self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1463
1256
        self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1464
1257
 
1465
1258
 
 
1259
class TestRevisionNotInBranch(TestCaseForLogFormatter):
 
1260
 
 
1261
    def setup_a_tree(self):
 
1262
        tree = self.make_branch_and_tree('tree')
 
1263
        tree.lock_write()
 
1264
        self.addCleanup(tree.unlock)
 
1265
        kwargs = {
 
1266
            'committer': 'Joe Foo <joe@foo.com>',
 
1267
            'timestamp': 1132617600,  # Mon 2005-11-22 00:00:00 +0000
 
1268
            'timezone': 0,  # UTC
 
1269
        }
 
1270
        tree.commit('commit 1a', rev_id=b'1a', **kwargs)
 
1271
        tree.commit('commit 2a', rev_id=b'2a', **kwargs)
 
1272
        tree.commit('commit 3a', rev_id=b'3a', **kwargs)
 
1273
        return tree
 
1274
 
 
1275
    def setup_ab_tree(self):
 
1276
        tree = self.setup_a_tree()
 
1277
        tree.set_last_revision(b'1a')
 
1278
        tree.branch.set_last_revision_info(1, b'1a')
 
1279
        kwargs = {
 
1280
            'committer': 'Joe Foo <joe@foo.com>',
 
1281
            'timestamp': 1132617600,  # Mon 2005-11-22 00:00:00 +0000
 
1282
            'timezone': 0,  # UTC
 
1283
        }
 
1284
        tree.commit('commit 2b', rev_id=b'2b', **kwargs)
 
1285
        tree.commit('commit 3b', rev_id=b'3b', **kwargs)
 
1286
        return tree
 
1287
 
 
1288
    def test_one_revision(self):
 
1289
        tree = self.setup_ab_tree()
 
1290
        lf = LogCatcher()
 
1291
        rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1292
        log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
 
1293
                     end_revision=rev)
 
1294
        self.assertEqual(1, len(lf.revisions))
 
1295
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1296
        self.assertEqual(b'3a', lf.revisions[0].rev.revision_id)
 
1297
 
 
1298
    def test_many_revisions(self):
 
1299
        tree = self.setup_ab_tree()
 
1300
        lf = LogCatcher()
 
1301
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1302
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1303
        log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
 
1304
                     end_revision=end_rev)
 
1305
        self.assertEqual(3, len(lf.revisions))
 
1306
        self.assertEqual(None, lf.revisions[0].revno)   # Out-of-branch
 
1307
        self.assertEqual(b'3a', lf.revisions[0].rev.revision_id)
 
1308
        self.assertEqual(None, lf.revisions[1].revno)   # Out-of-branch
 
1309
        self.assertEqual(b'2a', lf.revisions[1].rev.revision_id)
 
1310
        self.assertEqual('1', lf.revisions[2].revno)    # In-branch
 
1311
 
 
1312
    def test_long_format(self):
 
1313
        tree = self.setup_ab_tree()
 
1314
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1315
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1316
        self.assertFormatterResult(b"""\
 
1317
------------------------------------------------------------
 
1318
revision-id: 3a
 
1319
committer: Joe Foo <joe@foo.com>
 
1320
branch nick: tree
 
1321
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1322
message:
 
1323
  commit 3a
 
1324
------------------------------------------------------------
 
1325
revision-id: 2a
 
1326
committer: Joe Foo <joe@foo.com>
 
1327
branch nick: tree
 
1328
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1329
message:
 
1330
  commit 2a
 
1331
------------------------------------------------------------
 
1332
revno: 1
 
1333
committer: Joe Foo <joe@foo.com>
 
1334
branch nick: tree
 
1335
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1336
message:
 
1337
  commit 1a
 
1338
""",
 
1339
                                   tree.branch, log.LongLogFormatter, show_log_kwargs={
 
1340
                                       'start_revision': start_rev, 'end_revision': end_rev
 
1341
                                       })
 
1342
 
 
1343
    def test_short_format(self):
 
1344
        tree = self.setup_ab_tree()
 
1345
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1346
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1347
        self.assertFormatterResult(b"""\
 
1348
      Joe Foo\t2005-11-22
 
1349
      revision-id:3a
 
1350
      commit 3a
 
1351
 
 
1352
      Joe Foo\t2005-11-22
 
1353
      revision-id:2a
 
1354
      commit 2a
 
1355
 
 
1356
    1 Joe Foo\t2005-11-22
 
1357
      commit 1a
 
1358
 
 
1359
""",
 
1360
                                   tree.branch, log.ShortLogFormatter, show_log_kwargs={
 
1361
                                       'start_revision': start_rev, 'end_revision': end_rev
 
1362
                                       })
 
1363
 
 
1364
    def test_line_format(self):
 
1365
        tree = self.setup_ab_tree()
 
1366
        start_rev = revisionspec.RevisionInfo(tree.branch, None, b'1a')
 
1367
        end_rev = revisionspec.RevisionInfo(tree.branch, None, b'3a')
 
1368
        self.assertFormatterResult(b"""\
 
1369
Joe Foo 2005-11-22 commit 3a
 
1370
Joe Foo 2005-11-22 commit 2a
 
1371
1: Joe Foo 2005-11-22 commit 1a
 
1372
""",
 
1373
                                   tree.branch, log.LineLogFormatter, show_log_kwargs={
 
1374
                                       'start_revision': start_rev, 'end_revision': end_rev
 
1375
                                       })
 
1376
 
1466
1377
 
1467
1378
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1468
1379
 
1469
1380
    def setUp(self):
1470
 
        TestCaseForLogFormatter.setUp(self)
 
1381
        super(TestLogWithBugs, self).setUp()
1471
1382
        log.properties_handler_registry.register(
1472
1383
            'bugs_properties_handler',
1473
1384
            log._bugs_properties_handler)
1477
1388
        tree = self.make_branch_and_tree(u'.')
1478
1389
        self.build_tree(['a', 'b'])
1479
1390
        tree.add('a')
1480
 
        self.wt_commit(tree, 'simple log message', rev_id='a1',
1481
 
                       revprops={'bugs': 'test://bug/id fixed'})
 
1391
        self.wt_commit(tree, 'simple log message', rev_id=b'a1',
 
1392
                       revprops={u'bugs': 'test://bug/id fixed'})
1482
1393
        tree.add('b')
1483
 
        self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
 
1394
        self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id=b'a2',
1484
1395
                       authors=['Joe Bar <joe@bar.com>'],
1485
 
                       revprops={'bugs': 'test://bug/id fixed\n'
 
1396
                       revprops={u'bugs': 'test://bug/id fixed\n'
1486
1397
                                 'test://bug/2 fixed'})
1487
1398
        return tree
1488
1399
 
 
1400
    def test_bug_broken(self):
 
1401
        tree = self.make_branch_and_tree(u'.')
 
1402
        self.build_tree(['a', 'b'])
 
1403
        tree.add('a')
 
1404
        self.wt_commit(tree, 'simple log message', rev_id=b'a1',
 
1405
                       revprops={u'bugs': 'test://bua g/id fixed'})
 
1406
 
 
1407
        logfile = self.make_utf8_encoded_stringio()
 
1408
        formatter = log.LongLogFormatter(to_file=logfile)
 
1409
        log.show_log(tree.branch, formatter)
 
1410
 
 
1411
        self.assertContainsRe(
 
1412
            logfile.getvalue(),
 
1413
            b'brz: ERROR: breezy.bugtracker.InvalidLineInBugsProperty: '
 
1414
            b'Invalid line in bugs property: \'test://bua g/id fixed\'')
 
1415
 
 
1416
        text = logfile.getvalue()
 
1417
        self.assertEqualDiff(
 
1418
            text[text.index(b'-' * 60):],
 
1419
            b"""\
 
1420
------------------------------------------------------------
 
1421
revno: 1
 
1422
committer: Joe Foo <joe@foo.com>
 
1423
branch nick: work
 
1424
timestamp: Tue 2005-11-22 00:00:00 +0000
 
1425
message:
 
1426
  simple log message
 
1427
""")
1489
1428
 
1490
1429
    def test_long_bugs(self):
1491
1430
        tree = self.make_commits_with_bugs()
1492
 
        self.assertFormatterResult("""\
 
1431
        self.assertFormatterResult(b"""\
1493
1432
------------------------------------------------------------
1494
1433
revno: 2
1495
 
fixes bug(s): test://bug/id test://bug/2
 
1434
fixes bugs: test://bug/id test://bug/2
1496
1435
author: Joe Bar <joe@bar.com>
1497
1436
committer: Joe Foo <joe@foo.com>
1498
1437
branch nick: work
1503
1442
  message
1504
1443
------------------------------------------------------------
1505
1444
revno: 1
1506
 
fixes bug(s): test://bug/id
 
1445
fixes bug: test://bug/id
1507
1446
committer: Joe Foo <joe@foo.com>
1508
1447
branch nick: work
1509
1448
timestamp: Tue 2005-11-22 00:00:00 +0000
1510
1449
message:
1511
1450
  simple log message
1512
1451
""",
1513
 
            tree.branch, log.LongLogFormatter)
 
1452
                                   tree.branch, log.LongLogFormatter)
1514
1453
 
1515
1454
    def test_short_bugs(self):
1516
1455
        tree = self.make_commits_with_bugs()
1517
 
        self.assertFormatterResult("""\
 
1456
        self.assertFormatterResult(b"""\
1518
1457
    2 Joe Bar\t2005-11-22
1519
 
      fixes bug(s): test://bug/id test://bug/2
 
1458
      fixes bugs: test://bug/id test://bug/2
1520
1459
      multiline
1521
1460
      log
1522
1461
      message
1523
1462
 
1524
1463
    1 Joe Foo\t2005-11-22
1525
 
      fixes bug(s): test://bug/id
 
1464
      fixes bug: test://bug/id
1526
1465
      simple log message
1527
1466
 
1528
1467
""",
1529
 
            tree.branch, log.ShortLogFormatter)
 
1468
                                   tree.branch, log.ShortLogFormatter)
1530
1469
 
1531
1470
    def test_wrong_bugs_property(self):
1532
1471
        tree = self.make_branch_and_tree(u'.')
1533
1472
        self.build_tree(['foo'])
1534
 
        self.wt_commit(tree, 'simple log message', rev_id='a1',
1535
 
                       revprops={'bugs': 'test://bug/id invalid_value'})
1536
 
        self.assertFormatterResult("""\
1537
 
    1 Joe Foo\t2005-11-22
1538
 
      simple log message
1539
 
 
1540
 
""",
1541
 
            tree.branch, log.ShortLogFormatter)
 
1473
        self.wt_commit(tree, 'simple log message', rev_id=b'a1',
 
1474
                       revprops={u'bugs': 'test://bug/id invalid_value'})
 
1475
 
 
1476
        logfile = self.make_utf8_encoded_stringio()
 
1477
        formatter = log.ShortLogFormatter(to_file=logfile)
 
1478
        log.show_log(tree.branch, formatter)
 
1479
 
 
1480
        lines = logfile.getvalue().splitlines()
 
1481
 
 
1482
        self.assertEqual(
 
1483
            lines[0], b'    1 Joe Foo\t2005-11-22')
 
1484
 
 
1485
        self.assertEqual(
 
1486
            lines[1],
 
1487
            b'brz: ERROR: breezy.bugtracker.InvalidBugStatus: Invalid '
 
1488
            b'bug status: \'invalid_value\'')
 
1489
 
 
1490
        self.assertEqual(lines[-2], b"      simple log message")
1542
1491
 
1543
1492
    def test_bugs_handler_present(self):
1544
1493
        self.properties_handler_registry.get('bugs_properties_handler')
1547
1496
class TestLogForAuthors(TestCaseForLogFormatter):
1548
1497
 
1549
1498
    def setUp(self):
1550
 
        TestCaseForLogFormatter.setUp(self)
 
1499
        super(TestLogForAuthors, self).setUp()
1551
1500
        self.wt = self.make_standard_commit('nicky',
1552
 
            authors=['John Doe <jdoe@example.com>',
1553
 
                     'Jane Rey <jrey@example.com>'])
 
1501
                                            authors=['John Doe <jdoe@example.com>',
 
1502
                                                     'Jane Rey <jrey@example.com>'])
1554
1503
 
1555
1504
    def assertFormatterResult(self, formatter, who, result):
1556
1505
        formatter_kwargs = dict()
1558
1507
            author_list_handler = log.author_list_registry.get(who)
1559
1508
            formatter_kwargs['author_list_handler'] = author_list_handler
1560
1509
        TestCaseForLogFormatter.assertFormatterResult(self, result,
1561
 
            self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
 
1510
                                                      self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1562
1511
 
1563
1512
    def test_line_default(self):
1564
 
        self.assertFormatterResult(log.LineLogFormatter, None, """\
 
1513
        self.assertFormatterResult(log.LineLogFormatter, None, b"""\
1565
1514
1: John Doe 2005-11-22 add a
1566
1515
""")
1567
1516
 
1568
1517
    def test_line_committer(self):
1569
 
        self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
 
1518
        self.assertFormatterResult(log.LineLogFormatter, 'committer', b"""\
1570
1519
1: Lorem Ipsum 2005-11-22 add a
1571
1520
""")
1572
1521
 
1573
1522
    def test_line_first(self):
1574
 
        self.assertFormatterResult(log.LineLogFormatter, 'first', """\
 
1523
        self.assertFormatterResult(log.LineLogFormatter, 'first', b"""\
1575
1524
1: John Doe 2005-11-22 add a
1576
1525
""")
1577
1526
 
1578
1527
    def test_line_all(self):
1579
 
        self.assertFormatterResult(log.LineLogFormatter, 'all', """\
 
1528
        self.assertFormatterResult(log.LineLogFormatter, 'all', b"""\
1580
1529
1: John Doe, Jane Rey 2005-11-22 add a
1581
1530
""")
1582
1531
 
1583
 
 
1584
1532
    def test_short_default(self):
1585
 
        self.assertFormatterResult(log.ShortLogFormatter, None, """\
 
1533
        self.assertFormatterResult(log.ShortLogFormatter, None, b"""\
1586
1534
    1 John Doe\t2005-11-22
1587
1535
      add a
1588
1536
 
1589
1537
""")
1590
1538
 
1591
1539
    def test_short_committer(self):
1592
 
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
 
1540
        self.assertFormatterResult(log.ShortLogFormatter, 'committer', b"""\
1593
1541
    1 Lorem Ipsum\t2005-11-22
1594
1542
      add a
1595
1543
 
1596
1544
""")
1597
1545
 
1598
1546
    def test_short_first(self):
1599
 
        self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
 
1547
        self.assertFormatterResult(log.ShortLogFormatter, 'first', b"""\
1600
1548
    1 John Doe\t2005-11-22
1601
1549
      add a
1602
1550
 
1603
1551
""")
1604
1552
 
1605
1553
    def test_short_all(self):
1606
 
        self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
 
1554
        self.assertFormatterResult(log.ShortLogFormatter, 'all', b"""\
1607
1555
    1 John Doe, Jane Rey\t2005-11-22
1608
1556
      add a
1609
1557
 
1610
1558
""")
1611
1559
 
1612
1560
    def test_long_default(self):
1613
 
        self.assertFormatterResult(log.LongLogFormatter, None, """\
 
1561
        self.assertFormatterResult(log.LongLogFormatter, None, b"""\
1614
1562
------------------------------------------------------------
1615
1563
revno: 1
1616
1564
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1622
1570
""")
1623
1571
 
1624
1572
    def test_long_committer(self):
1625
 
        self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
 
1573
        self.assertFormatterResult(log.LongLogFormatter, 'committer', b"""\
1626
1574
------------------------------------------------------------
1627
1575
revno: 1
1628
1576
committer: Lorem Ipsum <test@example.com>
1633
1581
""")
1634
1582
 
1635
1583
    def test_long_first(self):
1636
 
        self.assertFormatterResult(log.LongLogFormatter, 'first', """\
 
1584
        self.assertFormatterResult(log.LongLogFormatter, 'first', b"""\
1637
1585
------------------------------------------------------------
1638
1586
revno: 1
1639
1587
author: John Doe <jdoe@example.com>
1645
1593
""")
1646
1594
 
1647
1595
    def test_long_all(self):
1648
 
        self.assertFormatterResult(log.LongLogFormatter, 'all', """\
 
1596
        self.assertFormatterResult(log.LongLogFormatter, 'all', b"""\
1649
1597
------------------------------------------------------------
1650
1598
revno: 1
1651
1599
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1657
1605
""")
1658
1606
 
1659
1607
    def test_gnu_changelog_default(self):
1660
 
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
 
1608
        self.assertFormatterResult(log.GnuChangelogLogFormatter, None, b"""\
1661
1609
2005-11-22  John Doe  <jdoe@example.com>
1662
1610
 
1663
1611
\tadd a
1665
1613
""")
1666
1614
 
1667
1615
    def test_gnu_changelog_committer(self):
1668
 
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
 
1616
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', b"""\
1669
1617
2005-11-22  Lorem Ipsum  <test@example.com>
1670
1618
 
1671
1619
\tadd a
1673
1621
""")
1674
1622
 
1675
1623
    def test_gnu_changelog_first(self):
1676
 
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
 
1624
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', b"""\
1677
1625
2005-11-22  John Doe  <jdoe@example.com>
1678
1626
 
1679
1627
\tadd a
1681
1629
""")
1682
1630
 
1683
1631
    def test_gnu_changelog_all(self):
1684
 
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
 
1632
        self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', b"""\
1685
1633
2005-11-22  John Doe  <jdoe@example.com>, Jane Rey  <jrey@example.com>
1686
1634
 
1687
1635
\tadd a
1688
1636
 
1689
1637
""")
1690
1638
 
 
1639
 
1691
1640
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1692
1641
 
1693
1642
    def make_branch_with_alternate_ancestries(self, relpath='.'):
1694
1643
        # See test_merge_sorted_exclude_ancestry below for the difference with
1695
1644
        # bt.per_branch.test_iter_merge_sorted_revision.
1696
 
        # TestIterMergeSortedRevisionsBushyGraph. 
 
1645
        # TestIterMergeSortedRevisionsBushyGraph.
1697
1646
        # make_branch_with_alternate_ancestries
1698
1647
        # and test_merge_sorted_exclude_ancestry
1699
1648
        # See the FIXME in assertLogRevnos too.
1710
1659
        # | /
1711
1660
        # 3
1712
1661
        builder.start_series()
1713
 
        builder.build_snapshot('1', None, [
1714
 
            ('add', ('', 'TREE_ROOT', 'directory', '')),])
1715
 
        builder.build_snapshot('1.1.1', ['1'], [])
1716
 
        builder.build_snapshot('2', ['1'], [])
1717
 
        builder.build_snapshot('1.2.1', ['1.1.1'], [])
1718
 
        builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1719
 
        builder.build_snapshot('3', ['2', '1.1.2'], [])
 
1662
        builder.build_snapshot(None, [
 
1663
            ('add', ('', b'TREE_ROOT', 'directory', '')), ],
 
1664
            revision_id=b'1')
 
1665
        builder.build_snapshot([b'1'], [], revision_id=b'1.1.1')
 
1666
        builder.build_snapshot([b'1'], [], revision_id=b'2')
 
1667
        builder.build_snapshot([b'1.1.1'], [], revision_id=b'1.2.1')
 
1668
        builder.build_snapshot([b'1.1.1', b'1.2.1'], [], revision_id=b'1.1.2')
 
1669
        builder.build_snapshot([b'2', b'1.1.2'], [], revision_id=b'3')
1720
1670
        builder.finish_series()
1721
1671
        br = builder.get_branch()
1722
1672
        br.lock_read()
1724
1674
        return br
1725
1675
 
1726
1676
    def assertLogRevnos(self, expected_revnos, b, start, end,
1727
 
                        exclude_common_ancestry):
 
1677
                        exclude_common_ancestry, generate_merge_revisions=True):
1728
1678
        # FIXME: the layering in log makes it hard to test intermediate levels,
1729
 
        # I wish adding filters with their parameters were easier...
 
1679
        # I wish adding filters with their parameters was easier...
1730
1680
        # -- vila 20100413
1731
1681
        iter_revs = log._calc_view_revisions(
1732
1682
            b, start, end, direction='reverse',
1733
 
            generate_merge_revisions=True,
 
1683
            generate_merge_revisions=generate_merge_revisions,
1734
1684
            exclude_common_ancestry=exclude_common_ancestry)
1735
1685
        self.assertEqual(expected_revnos,
1736
1686
                         [revid for revid, revno, depth in iter_revs])
1737
1687
 
1738
1688
    def test_merge_sorted_exclude_ancestry(self):
1739
1689
        b = self.make_branch_with_alternate_ancestries()
1740
 
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1741
 
                             b, '1', '3', False)
 
1690
        self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'1.1.1', b'2', b'1'],
 
1691
                             b, b'1', b'3', exclude_common_ancestry=False)
1742
1692
        # '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1743
1693
        # it should be mentioned even if merge_sort order will make it appear
1744
1694
        # after 1.1.1
1745
 
        self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1746
 
                             b, '1.1.1', '3', True)
1747
 
 
1748
 
 
 
1695
        self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'2'],
 
1696
                             b, b'1.1.1', b'3', exclude_common_ancestry=True)
 
1697
 
 
1698
    def test_merge_sorted_simple_revnos_exclude_ancestry(self):
 
1699
        b = self.make_branch_with_alternate_ancestries()
 
1700
        self.assertLogRevnos([b'3', b'2'],
 
1701
                             b, b'1', b'3', exclude_common_ancestry=True,
 
1702
                             generate_merge_revisions=False)
 
1703
        self.assertLogRevnos([b'3', b'1.1.2', b'1.2.1', b'1.1.1', b'2'],
 
1704
                             b, b'1', b'3', exclude_common_ancestry=True,
 
1705
                             generate_merge_revisions=True)
 
1706
 
 
1707
 
 
1708
class TestLogDefaults(TestCaseForLogFormatter):
 
1709
    def test_default_log_level(self):
 
1710
        """
 
1711
        Test to ensure that specifying 'levels=1' to make_log_request_dict
 
1712
        doesn't get overwritten when using a LogFormatter that supports more
 
1713
        detail.
 
1714
        Fixes bug #747958.
 
1715
        """
 
1716
        wt = self._prepare_tree_with_merges()
 
1717
        b = wt.branch
 
1718
 
 
1719
        class CustomLogFormatter(log.LogFormatter):
 
1720
            def __init__(self, *args, **kwargs):
 
1721
                super(CustomLogFormatter, self).__init__(*args, **kwargs)
 
1722
                self.revisions = []
 
1723
 
 
1724
            def get_levels(self):
 
1725
                # log formatter supports all levels:
 
1726
                return 0
 
1727
 
 
1728
            def log_revision(self, revision):
 
1729
                self.revisions.append(revision)
 
1730
 
 
1731
        log_formatter = LogCatcher()
 
1732
        # First request we don't specify number of levels, we should get a
 
1733
        # sensible default (whatever the LogFormatter handles - which in this
 
1734
        # case is 0/everything):
 
1735
        request = log.make_log_request_dict(limit=10)
 
1736
        log.Logger(b, request).show(log_formatter)
 
1737
        # should have all three revisions:
 
1738
        self.assertEqual(len(log_formatter.revisions), 3)
 
1739
 
 
1740
        del log_formatter
 
1741
        log_formatter = LogCatcher()
 
1742
        # now explicitly request mainline revisions only:
 
1743
        request = log.make_log_request_dict(limit=10, levels=1)
 
1744
        log.Logger(b, request).show(log_formatter)
 
1745
        # should now only have 2 revisions:
 
1746
        self.assertEqual(len(log_formatter.revisions), 2)