~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: John Arbash Meinel
  • Date: 2007-04-12 20:36:40 UTC
  • mfrom: (2413 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2566.
  • Revision ID: john@arbash-meinel.com-20070412203640-z1jld315288moxvy
[merge] bzr.dev 2413

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005 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
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
19
import os
18
20
from cStringIO import StringIO
19
21
 
20
 
from bzrlib.selftest import BzrTestBase, TestCaseInTempDir
21
 
from bzrlib.log import LogFormatter, show_log, LongLogFormatter
 
22
from bzrlib.tests import BzrTestBase, TestCaseWithTransport
 
23
from bzrlib.log import (show_log, 
 
24
                        get_view_revisions, 
 
25
                        LogFormatter, 
 
26
                        LongLogFormatter, 
 
27
                        ShortLogFormatter, 
 
28
                        LineLogFormatter)
22
29
from bzrlib.branch import Branch
23
30
from bzrlib.errors import InvalidRevisionNumber
24
31
 
 
32
 
25
33
class _LogEntry(object):
26
34
    # should probably move into bzrlib.log?
27
35
    pass
39
47
    def __init__(self):
40
48
        super(LogCatcher, self).__init__(to_file=None)
41
49
        self.logs = []
42
 
        
43
 
        
 
50
 
44
51
    def show(self, revno, rev, delta):
45
52
        le = _LogEntry()
46
53
        le.revno = revno
49
56
        self.logs.append(le)
50
57
 
51
58
 
52
 
class SimpleLogTest(TestCaseInTempDir):
 
59
class SimpleLogTest(TestCaseWithTransport):
53
60
 
54
61
    def checkDelta(self, delta, **kw):
55
62
        """Check the filenames touched by a delta are as expected."""
57
64
            expected = kw.get(n, [])
58
65
 
59
66
            # 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]
 
67
            #if os.sep != '/':
 
68
            #    expected = [x.replace('/', os.sep) for x in expected]
62
69
 
63
70
            # strip out only the path components
64
71
            got = [x[0] for x in getattr(delta, n)]
65
72
            self.assertEquals(expected, got)
66
73
 
67
74
    def test_cur_revno(self):
68
 
        b = Branch('.', init=True)
69
 
 
70
 
        lf = LogCatcher()
71
 
        b.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('.')
88
 
 
89
 
        lf = LogCatcher()
90
 
        b.commit('empty commit')
 
75
        wt = self.make_branch_and_tree('.')
 
76
        b = wt.branch
 
77
 
 
78
        lf = LogCatcher()
 
79
        wt.commit('empty commit')
91
80
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
92
81
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
93
82
                          start_revision=2, end_revision=1) 
105
94
    def test_simple_log(self):
106
95
        eq = self.assertEquals
107
96
        
108
 
        b = Branch.initialize('.')
 
97
        wt = self.make_branch_and_tree('.')
 
98
        b = wt.branch
109
99
 
110
100
        lf = LogCatcher()
111
101
        show_log(b, lf)
112
102
        # no entries yet
113
103
        eq(lf.logs, [])
114
104
 
115
 
 
116
 
        b.commit('empty commit')
 
105
        wt.commit('empty commit')
117
106
        lf = LogCatcher()
118
107
        show_log(b, lf, verbose=True)
119
108
        eq(len(lf.logs), 1)
120
 
        eq(lf.logs[0].revno, 1)
 
109
        eq(lf.logs[0].revno, '1')
121
110
        eq(lf.logs[0].rev.message, 'empty commit')
122
111
        d = lf.logs[0].delta
123
112
        self.log('log delta: %r' % d)
124
113
        self.checkDelta(d)
125
114
 
126
 
 
127
115
        self.build_tree(['hello'])
128
 
        b.add('hello')
129
 
        b.commit('add one file')
 
116
        wt.add('hello')
 
117
        wt.commit('add one file')
130
118
 
131
119
        lf = StringIO()
132
120
        # log using regular thing
141
129
        eq(len(lf.logs), 2)
142
130
        self.log('log entries:')
143
131
        for logentry in lf.logs:
144
 
            self.log('%4d %s' % (logentry.revno, logentry.rev.message))
 
132
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
145
133
        
146
134
        # first one is most recent
147
135
        logentry = lf.logs[0]
148
 
        eq(logentry.revno, 2)
 
136
        eq(logentry.revno, '2')
149
137
        eq(logentry.rev.message, 'add one file')
150
138
        d = logentry.delta
151
139
        self.log('log 2 delta: %r' % d)
153
141
        
154
142
        # commit a log message with control characters
155
143
        msg = "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
156
 
        b.commit(msg)
 
144
        self.log("original commit message: %r", msg)
 
145
        wt.commit(msg)
157
146
        lf = LogCatcher()
158
147
        show_log(b, lf, verbose=True)
159
148
        committed_msg = lf.logs[0].rev.message
160
149
        self.log("escaped commit message: %r", committed_msg)
161
150
        self.assert_(msg != committed_msg)
162
151
        self.assert_(len(committed_msg) > len(msg))
 
152
 
 
153
        # Check that log message with only XML-valid characters isn't
 
154
        # escaped.  As ElementTree apparently does some kind of
 
155
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
 
156
        # included in the test commit message, even though they are
 
157
        # valid XML 1.0 characters.
 
158
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
 
159
        self.log("original commit message: %r", msg)
 
160
        wt.commit(msg)
 
161
        lf = LogCatcher()
 
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
 
 
167
    def test_trailing_newlines(self):
 
168
        wt = self.make_branch_and_tree('.')
 
169
        b = wt.branch
 
170
        b.nick='test'
 
171
        open('a', 'wb').write('hello moto\n')
 
172
        wt.add('a')
 
173
        wt.commit('simple log message', rev_id='a1'
 
174
                , timestamp=1132586655.459960938, timezone=-6*3600
 
175
                , committer='Joe Foo <joe@foo.com>')
 
176
        open('b', 'wb').write('goodbye\n')
 
177
        wt.add('b')
 
178
        wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
 
179
                , timestamp=1132586842.411175966, timezone=-6*3600
 
180
                , committer='Joe Foo <joe@foo.com>')
 
181
 
 
182
        open('c', 'wb').write('just another manic monday\n')
 
183
        wt.add('c')
 
184
        wt.commit('single line with trailing newline\n', rev_id='a3'
 
185
                , timestamp=1132587176.835228920, timezone=-6*3600
 
186
                , committer = 'Joe Foo <joe@foo.com>')
 
187
 
 
188
        sio = StringIO()
 
189
        lf = ShortLogFormatter(to_file=sio)
 
190
        show_log(b, lf)
 
191
        self.assertEquals(sio.getvalue(), """\
 
192
    3 Joe Foo\t2005-11-21
 
193
      single line with trailing newline
 
194
 
 
195
    2 Joe Foo\t2005-11-21
 
196
      multiline
 
197
      log
 
198
      message
 
199
 
 
200
    1 Joe Foo\t2005-11-21
 
201
      simple log message
 
202
 
 
203
""")
 
204
 
 
205
        sio = StringIO()
 
206
        lf = LongLogFormatter(to_file=sio)
 
207
        show_log(b, lf)
 
208
        self.assertEquals(sio.getvalue(), """\
 
209
------------------------------------------------------------
 
210
revno: 3
 
211
committer: Joe Foo <joe@foo.com>
 
212
branch nick: test
 
213
timestamp: Mon 2005-11-21 09:32:56 -0600
 
214
message:
 
215
  single line with trailing newline
 
216
------------------------------------------------------------
 
217
revno: 2
 
218
committer: Joe Foo <joe@foo.com>
 
219
branch nick: test
 
220
timestamp: Mon 2005-11-21 09:27:22 -0600
 
221
message:
 
222
  multiline
 
223
  log
 
224
  message
 
225
------------------------------------------------------------
 
226
revno: 1
 
227
committer: Joe Foo <joe@foo.com>
 
228
branch nick: test
 
229
timestamp: Mon 2005-11-21 09:24:15 -0600
 
230
message:
 
231
  simple log message
 
232
""")
 
233
        
 
234
    def test_verbose_log(self):
 
235
        """Verbose log includes changed files
 
236
        
 
237
        bug #4676
 
238
        """
 
239
        wt = self.make_branch_and_tree('.')
 
240
        b = wt.branch
 
241
        self.build_tree(['a'])
 
242
        wt.add('a')
 
243
        # XXX: why does a longer nick show up?
 
244
        b.nick = 'test_verbose_log'
 
245
        wt.commit(message='add a', 
 
246
                  timestamp=1132711707, 
 
247
                  timezone=36000,
 
248
                  committer='Lorem Ipsum <test@example.com>')
 
249
        logfile = file('out.tmp', 'w+')
 
250
        formatter = LongLogFormatter(to_file=logfile)
 
251
        show_log(b, formatter, verbose=True)
 
252
        logfile.flush()
 
253
        logfile.seek(0)
 
254
        log_contents = logfile.read()
 
255
        self.assertEqualDiff(log_contents, '''\
 
256
------------------------------------------------------------
 
257
revno: 1
 
258
committer: Lorem Ipsum <test@example.com>
 
259
branch nick: test_verbose_log
 
260
timestamp: Wed 2005-11-23 12:08:27 +1000
 
261
message:
 
262
  add a
 
263
added:
 
264
  a
 
265
''')
 
266
 
 
267
    def test_line_log(self):
 
268
        """Line log should show revno
 
269
        
 
270
        bug #5162
 
271
        """
 
272
        wt = self.make_branch_and_tree('.')
 
273
        b = wt.branch
 
274
        self.build_tree(['a'])
 
275
        wt.add('a')
 
276
        b.nick = 'test-line-log'
 
277
        wt.commit(message='add a', 
 
278
                  timestamp=1132711707, 
 
279
                  timezone=36000,
 
280
                  committer='Line-Log-Formatter Tester <test@line.log>')
 
281
        logfile = file('out.tmp', 'w+')
 
282
        formatter = LineLogFormatter(to_file=logfile)
 
283
        show_log(b, formatter)
 
284
        logfile.flush()
 
285
        logfile.seek(0)
 
286
        log_contents = logfile.read()
 
287
        self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
 
288
 
 
289
    def make_tree_with_commits(self):
 
290
        """Create a tree with well-known revision ids"""
 
291
        wt = self.make_branch_and_tree('tree1')
 
292
        wt.commit('commit one', rev_id='1')
 
293
        wt.commit('commit two', rev_id='2')
 
294
        wt.commit('commit three', rev_id='3')
 
295
        mainline_revs = [None, '1', '2', '3']
 
296
        rev_nos = {'1': 1, '2': 2, '3': 3}
 
297
        return mainline_revs, rev_nos, wt
 
298
 
 
299
    def make_tree_with_merges(self):
 
300
        """Create a tree with well-known revision ids and a merge"""
 
301
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
302
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
303
        tree2.commit('four-a', rev_id='4a')
 
304
        wt.merge_from_branch(tree2.branch)
 
305
        wt.commit('four-b', rev_id='4b')
 
306
        mainline_revs.append('4b')
 
307
        rev_nos['4b'] = 4
 
308
        # 4a: 3.1.1
 
309
        return mainline_revs, rev_nos, wt
 
310
 
 
311
    def make_tree_with_many_merges(self):
 
312
        """Create a tree with well-known revision ids"""
 
313
        wt = self.make_branch_and_tree('tree1')
 
314
        wt.commit('commit one', rev_id='1')
 
315
        wt.commit('commit two', rev_id='2')
 
316
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
 
317
        tree3.commit('commit three a', rev_id='3a')
 
318
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
319
        tree2.merge_from_branch(tree3.branch)
 
320
        tree2.commit('commit three b', rev_id='3b')
 
321
        wt.merge_from_branch(tree2.branch)
 
322
        wt.commit('commit three c', rev_id='3c')
 
323
        tree2.commit('four-a', rev_id='4a')
 
324
        wt.merge_from_branch(tree2.branch)
 
325
        wt.commit('four-b', rev_id='4b')
 
326
        mainline_revs = [None, '1', '2', '3c', '4b']
 
327
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
 
328
        full_rev_nos_for_reference = {
 
329
            '1': '1',
 
330
            '2': '2',
 
331
            '3a': '2.2.1', #first commit tree 3
 
332
            '3b': '2.1.1', # first commit tree 2
 
333
            '3c': '3', #merges 3b to main
 
334
            '4a': '2.1.2', # second commit tree 2
 
335
            '4b': '4', # merges 4a to main
 
336
            }
 
337
        return mainline_revs, rev_nos, wt
 
338
 
 
339
    def test_get_view_revisions_forward(self):
 
340
        """Test the get_view_revisions method"""
 
341
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
342
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
343
                                            'forward'))
 
344
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
 
345
            revisions)
 
346
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
347
                                             'forward', include_merges=False))
 
348
        self.assertEqual(revisions, revisions2)
 
349
 
 
350
    def test_get_view_revisions_reverse(self):
 
351
        """Test the get_view_revisions with reverse"""
 
352
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
353
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
354
                                            'reverse'))
 
355
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
 
356
            revisions)
 
357
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
358
                                             'reverse', include_merges=False))
 
359
        self.assertEqual(revisions, revisions2)
 
360
 
 
361
    def test_get_view_revisions_merge(self):
 
362
        """Test get_view_revisions when there are merges"""
 
363
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
364
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
365
                                            'forward'))
 
366
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
367
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
368
            revisions)
 
369
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
370
                                             'forward', include_merges=False))
 
371
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
372
            ('4b', '4', 0)],
 
373
            revisions)
 
374
 
 
375
    def test_get_view_revisions_merge_reverse(self):
 
376
        """Test get_view_revisions in reverse when there are merges"""
 
377
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
378
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
379
                                            'reverse'))
 
380
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
 
381
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
382
            revisions)
 
383
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
384
                                             'reverse', include_merges=False))
 
385
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
 
386
            ('1', '1', 0)],
 
387
            revisions)
 
388
 
 
389
    def test_get_view_revisions_merge2(self):
 
390
        """Test get_view_revisions when there are merges"""
 
391
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
 
392
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
393
                                            'forward'))
 
394
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
395
            ('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
 
396
            ('4a', '2.1.2', 1)]
 
397
        self.assertEqual(expected, revisions)
 
398
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
399
                                             'forward', include_merges=False))
 
400
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
401
            ('4b', '4', 0)],
 
402
            revisions)