1
# Copyright (C) 2005 by Canonical Ltd
2
# -*- coding: utf-8 -*-
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
from cStringIO import StringIO
22
from bzrlib.tests import BzrTestBase, TestCaseWithTransport
23
from bzrlib.log import (LogFormatter, show_log, LongLogFormatter,
24
ShortLogFormatter, LineLogFormatter)
25
from bzrlib.branch import Branch
26
from bzrlib.errors import InvalidRevisionNumber
29
class _LogEntry(object):
30
# should probably move into bzrlib.log?
34
class LogCatcher(LogFormatter):
35
"""Pull log messages into list rather than displaying them.
37
For ease of testing we save log messages here rather than actually
38
formatting them, so that we can precisely check the result without
39
being too dependent on the exact formatting.
41
We should also test the LogFormatter.
44
super(LogCatcher, self).__init__(to_file=None)
47
def show(self, revno, rev, delta):
55
class SimpleLogTest(TestCaseWithTransport):
57
def checkDelta(self, delta, **kw):
58
"""Check the filenames touched by a delta are as expected."""
59
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
60
expected = kw.get(n, [])
62
# tests are written with unix paths; fix them up for windows
64
# expected = [x.replace('/', os.sep) for x in expected]
66
# strip out only the path components
67
got = [x[0] for x in getattr(delta, n)]
68
self.assertEquals(expected, got)
70
def test_cur_revno(self):
71
wt = self.make_branch_and_tree('.')
75
wt.commit('empty commit')
76
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
77
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
78
start_revision=2, end_revision=1)
79
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
80
start_revision=1, end_revision=2)
81
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
82
start_revision=0, end_revision=2)
83
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
84
start_revision=1, end_revision=0)
85
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
86
start_revision=-1, end_revision=1)
87
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
88
start_revision=1, end_revision=-1)
90
def test_cur_revno(self):
91
wt = self.make_branch_and_tree('.')
95
wt.commit('empty commit')
96
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
97
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
98
start_revision=2, end_revision=1)
99
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
100
start_revision=1, end_revision=2)
101
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
102
start_revision=0, end_revision=2)
103
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
104
start_revision=1, end_revision=0)
105
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
106
start_revision=-1, end_revision=1)
107
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
108
start_revision=1, end_revision=-1)
110
def test_simple_log(self):
111
eq = self.assertEquals
113
wt = self.make_branch_and_tree('.')
121
wt.commit('empty commit')
123
show_log(b, lf, verbose=True)
125
eq(lf.logs[0].revno, 1)
126
eq(lf.logs[0].rev.message, 'empty commit')
128
self.log('log delta: %r' % d)
131
self.build_tree(['hello'])
133
wt.commit('add one file')
136
# log using regular thing
137
show_log(b, LongLogFormatter(lf))
139
for l in lf.readlines():
142
# get log as data structure
144
show_log(b, lf, verbose=True)
146
self.log('log entries:')
147
for logentry in lf.logs:
148
self.log('%4d %s' % (logentry.revno, logentry.rev.message))
150
# first one is most recent
151
logentry = lf.logs[0]
152
eq(logentry.revno, 2)
153
eq(logentry.rev.message, 'add one file')
155
self.log('log 2 delta: %r' % d)
156
# self.checkDelta(d, added=['hello'])
158
# commit a log message with control characters
159
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
160
self.log("original commit message: %r", msg)
163
show_log(b, lf, verbose=True)
164
committed_msg = lf.logs[0].rev.message
165
self.log("escaped commit message: %r", committed_msg)
166
self.assert_(msg != committed_msg)
167
self.assert_(len(committed_msg) > len(msg))
169
# Check that log message with only XML-valid characters isn't
170
# escaped. As ElementTree apparently does some kind of
171
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
172
# included in the test commit message, even though they are
173
# valid XML 1.0 characters.
174
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
175
self.log("original commit message: %r", msg)
178
show_log(b, lf, verbose=True)
179
committed_msg = lf.logs[0].rev.message
180
self.log("escaped commit message: %r", committed_msg)
181
self.assert_(msg == committed_msg)
183
def test_trailing_newlines(self):
184
wt = self.make_branch_and_tree('.')
187
open('a', 'wb').write('hello moto\n')
189
wt.commit('simple log message', rev_id='a1'
190
, timestamp=1132586655.459960938, timezone=-6*3600
191
, committer='Joe Foo <joe@foo.com>')
192
open('b', 'wb').write('goodbye\n')
194
wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
195
, timestamp=1132586842.411175966, timezone=-6*3600
196
, committer='Joe Foo <joe@foo.com>')
198
open('c', 'wb').write('just another manic monday\n')
200
wt.commit('single line with trailing newline\n', rev_id='a3'
201
, timestamp=1132587176.835228920, timezone=-6*3600
202
, committer = 'Joe Foo <joe@foo.com>')
205
lf = ShortLogFormatter(to_file=sio)
207
self.assertEquals(sio.getvalue(), """\
208
3 Joe Foo\t2005-11-21
209
single line with trailing newline
211
2 Joe Foo\t2005-11-21
216
1 Joe Foo\t2005-11-21
222
lf = LongLogFormatter(to_file=sio)
224
self.assertEquals(sio.getvalue(), """\
225
------------------------------------------------------------
227
committer: Joe Foo <joe@foo.com>
229
timestamp: Mon 2005-11-21 09:32:56 -0600
231
single line with trailing newline
232
------------------------------------------------------------
234
committer: Joe Foo <joe@foo.com>
236
timestamp: Mon 2005-11-21 09:27:22 -0600
241
------------------------------------------------------------
243
committer: Joe Foo <joe@foo.com>
245
timestamp: Mon 2005-11-21 09:24:15 -0600
250
def test_verbose_log(self):
251
"""Verbose log includes changed files
255
wt = self.make_branch_and_tree('.')
257
self.build_tree(['a'])
259
# XXX: why does a longer nick show up?
260
b.nick = 'test_verbose_log'
261
wt.commit(message='add a',
262
timestamp=1132711707,
264
committer='Lorem Ipsum <test@example.com>')
265
logfile = file('out.tmp', 'w+')
266
formatter = LongLogFormatter(to_file=logfile)
267
show_log(b, formatter, verbose=True)
270
log_contents = logfile.read()
271
self.assertEqualDiff(log_contents, '''\
272
------------------------------------------------------------
274
committer: Lorem Ipsum <test@example.com>
275
branch nick: test_verbose_log
276
timestamp: Wed 2005-11-23 12:08:27 +1000
283
def test_line_log(self):
284
"""Line log should show revno
288
wt = self.make_branch_and_tree('.')
290
self.build_tree(['a'])
292
b.nick = 'test-line-log'
293
wt.commit(message='add a',
294
timestamp=1132711707,
296
committer='Line-Log-Formatter Tester <test@line.log>')
297
logfile = file('out.tmp', 'w+')
298
formatter = LineLogFormatter(to_file=logfile)
299
show_log(b, formatter)
302
log_contents = logfile.read()
303
self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')