~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

(vila) Fix test failures blocking package builds. (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

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