~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

Deprecate compare_trees and move its body to InterTree.changes_from.

Show diffs side-by-side

added added

removed removed

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