/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 bzrlib/tests/test_log.py

  • Committer: John Arbash Meinel
  • Date: 2006-04-25 15:05:42 UTC
  • mfrom: (1185.85.85 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060425150542-c7b518dca9928691
[merge] the old bzr-encoding changes, reparenting them on bzr.dev

Show diffs side-by-side

added added

removed removed

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