~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-11-26 09:40:14 UTC
  • mfrom: (3855.1.1 bzr.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20081126094014-rr61dd2gkl53qthl
(vila) Fix bug #300055 by handling revision ranges starting with a
        dotted revno for bzr log while --forward is used for a specific file

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
import os
18
18
from cStringIO import StringIO
19
19
 
20
 
from bzrlib import log, registry
21
 
from bzrlib.tests import TestCase, TestCaseWithTransport
22
 
from bzrlib.log import (show_log,
23
 
                        get_view_revisions,
24
 
                        LogRevision,
25
 
                        LogFormatter,
26
 
                        LongLogFormatter,
27
 
                        ShortLogFormatter,
28
 
                        LineLogFormatter)
29
 
from bzrlib.branch import Branch
30
 
from bzrlib.errors import (
31
 
    BzrCommandError,
32
 
    InvalidRevisionNumber,
33
 
    )
34
 
from bzrlib.revision import Revision
35
 
from bzrlib.revisionspec import (
36
 
    RevisionInfo,
37
 
    RevisionSpec,
38
 
    )
39
 
 
40
 
 
41
 
class TestCaseWithoutPropsHandler(TestCaseWithTransport):
 
20
from bzrlib import (
 
21
    errors,
 
22
    log,
 
23
    registry,
 
24
    revision,
 
25
    revisionspec,
 
26
    tests,
 
27
    )
 
28
 
 
29
 
 
30
class TestCaseWithoutPropsHandler(tests.TestCaseWithTransport):
42
31
 
43
32
    def setUp(self):
44
33
        super(TestCaseWithoutPropsHandler, self).setUp()
45
34
        # keep a reference to the "current" custom prop. handler registry
46
 
        self.properties_handler_registry = \
47
 
            log.properties_handler_registry
 
35
        self.properties_handler_registry = log.properties_handler_registry
48
36
        # clean up the registry in log
49
37
        log.properties_handler_registry = registry.Registry()
50
 
        
 
38
 
51
39
    def _cleanup(self):
52
40
        super(TestCaseWithoutPropsHandler, self)._cleanup()
53
41
        # restore the custom properties handler registry
54
 
        log.properties_handler_registry = \
55
 
            self.properties_handler_registry
56
 
 
57
 
 
58
 
class LogCatcher(LogFormatter):
 
42
        log.properties_handler_registry = self.properties_handler_registry
 
43
 
 
44
 
 
45
class LogCatcher(log.LogFormatter):
59
46
    """Pull log messages into list rather than displaying them.
60
47
 
61
48
    For ease of testing we save log messages here rather than actually
75
62
        self.logs.append(revision)
76
63
 
77
64
 
78
 
class TestShowLog(TestCaseWithTransport):
 
65
class TestShowLog(tests.TestCaseWithTransport):
79
66
 
80
67
    def checkDelta(self, delta, **kw):
81
 
        """Check the filenames touched by a delta are as expected."""
 
68
        """Check the filenames touched by a delta are as expected.
 
69
 
 
70
        Caller only have to pass in the list of files for each part, all
 
71
        unspecified parts are considered empty (and checked as such).
 
72
        """
82
73
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
 
74
            # By default we expect an empty list
83
75
            expected = kw.get(n, [])
84
76
            # strip out only the path components
85
77
            got = [x[0] for x in getattr(delta, n)]
86
 
            self.assertEquals(expected, got)
 
78
            self.assertEqual(expected, got)
 
79
 
 
80
    def assertInvalidRevisonNumber(self, br, start, end):
 
81
        lf = LogCatcher()
 
82
        self.assertRaises(errors.InvalidRevisionNumber,
 
83
                          log.show_log, br, lf,
 
84
                          start_revision=start, end_revision=end)
87
85
 
88
86
    def test_cur_revno(self):
89
87
        wt = self.make_branch_and_tree('.')
91
89
 
92
90
        lf = LogCatcher()
93
91
        wt.commit('empty commit')
94
 
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
95
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
96
 
                          start_revision=2, end_revision=1) 
97
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
98
 
                          start_revision=1, end_revision=2) 
99
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
100
 
                          start_revision=0, end_revision=2) 
101
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
102
 
                          start_revision=1, end_revision=0) 
103
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
104
 
                          start_revision=-1, end_revision=1) 
105
 
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
106
 
                          start_revision=1, end_revision=-1) 
107
 
 
108
 
    def test_simple_log(self):
109
 
        eq = self.assertEquals
110
 
        
 
92
        log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
 
93
 
 
94
        # Since there is a single revision in the branch all the combinations
 
95
        # below should fail.
 
96
        self.assertInvalidRevisonNumber(b, 2, 1)
 
97
        self.assertInvalidRevisonNumber(b, 1, 2)
 
98
        self.assertInvalidRevisonNumber(b, 0, 2)
 
99
        self.assertInvalidRevisonNumber(b, 1, 0)
 
100
        self.assertInvalidRevisonNumber(b, -1, 1)
 
101
        self.assertInvalidRevisonNumber(b, 1, -1)
 
102
 
 
103
    def test_empty_branch(self):
111
104
        wt = self.make_branch_and_tree('.')
112
 
        b = wt.branch
113
105
 
114
106
        lf = LogCatcher()
115
 
        show_log(b, lf)
 
107
        log.show_log(wt.branch, lf)
116
108
        # no entries yet
117
 
        eq(lf.logs, [])
 
109
        self.assertEqual([], lf.logs)
 
110
 
 
111
    def test_empty_commit(self):
 
112
        wt = self.make_branch_and_tree('.')
118
113
 
119
114
        wt.commit('empty commit')
120
115
        lf = LogCatcher()
121
 
        show_log(b, lf, verbose=True)
122
 
        eq(len(lf.logs), 1)
123
 
        eq(lf.logs[0].revno, '1')
124
 
        eq(lf.logs[0].rev.message, 'empty commit')
125
 
        d = lf.logs[0].delta
126
 
        self.log('log delta: %r' % d)
127
 
        self.checkDelta(d)
 
116
        log.show_log(wt.branch, lf, verbose=True)
 
117
        self.assertEqual(1, len(lf.logs))
 
118
        self.assertEqual('1', lf.logs[0].revno)
 
119
        self.assertEqual('empty commit', lf.logs[0].rev.message)
 
120
        self.checkDelta(lf.logs[0].delta)
128
121
 
 
122
    def test_simple_commit(self):
 
123
        wt = self.make_branch_and_tree('.')
 
124
        wt.commit('empty commit')
129
125
        self.build_tree(['hello'])
130
126
        wt.add('hello')
131
127
        wt.commit('add one file',
132
128
                  committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
133
129
                            u'<test@example.com>')
134
 
 
135
 
        lf = self.make_utf8_encoded_stringio()
136
 
        # log using regular thing
137
 
        show_log(b, LongLogFormatter(lf))
138
 
        lf.seek(0)
139
 
        for l in lf.readlines():
140
 
            self.log(l)
141
 
 
142
 
        # get log as data structure
143
130
        lf = LogCatcher()
144
 
        show_log(b, lf, verbose=True)
145
 
        eq(len(lf.logs), 2)
146
 
        self.log('log entries:')
147
 
        for logentry in lf.logs:
148
 
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
149
 
 
 
131
        log.show_log(wt.branch, lf, verbose=True)
 
132
        self.assertEqual(2, len(lf.logs))
150
133
        # first one is most recent
151
 
        logentry = lf.logs[0]
152
 
        eq(logentry.revno, '2')
153
 
        eq(logentry.rev.message, 'add one file')
154
 
        d = logentry.delta
155
 
        self.log('log 2 delta: %r' % d)
156
 
        self.checkDelta(d, added=['hello'])
 
134
        log_entry = lf.logs[0]
 
135
        self.assertEqual('2', log_entry.revno)
 
136
        self.assertEqual('add one file', log_entry.rev.message)
 
137
        self.checkDelta(log_entry.delta, added=['hello'])
157
138
 
158
 
        # commit a log message with control characters
 
139
    def test_commit_message_with_control_chars(self):
 
140
        wt = self.make_branch_and_tree('.')
159
141
        msg = u"All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
160
142
        msg = msg.replace(u'\r', u'\n')
161
 
        self.log("original commit message: %r", msg)
162
143
        wt.commit(msg)
163
144
        lf = LogCatcher()
164
 
        show_log(b, lf, verbose=True)
 
145
        log.show_log(wt.branch, lf, verbose=True)
165
146
        committed_msg = lf.logs[0].rev.message
166
 
        self.log("escaped commit message: %r", committed_msg)
167
 
        self.assert_(msg != committed_msg)
168
 
        self.assert_(len(committed_msg) > len(msg))
 
147
        self.assertNotEqual(msg, committed_msg)
 
148
        self.assertTrue(len(committed_msg) > len(msg))
169
149
 
170
 
        # Check that log message with only XML-valid characters isn't
 
150
    def test_commit_message_without_control_chars(self):
 
151
        wt = self.make_branch_and_tree('.')
171
152
        # escaped.  As ElementTree apparently does some kind of
172
153
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
173
154
        # included in the test commit message, even though they are
174
155
        # valid XML 1.0 characters.
175
156
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
176
 
        self.log("original commit message: %r", msg)
177
157
        wt.commit(msg)
178
158
        lf = LogCatcher()
179
 
        show_log(b, lf, verbose=True)
 
159
        log.show_log(wt.branch, lf, verbose=True)
180
160
        committed_msg = lf.logs[0].rev.message
181
 
        self.log("escaped commit message: %r", committed_msg)
182
 
        self.assert_(msg == committed_msg)
 
161
        self.assertEqual(msg, committed_msg)
183
162
 
184
163
    def test_deltas_in_merge_revisions(self):
185
164
        """Check deltas created for both mainline and merge revisions"""
186
 
        eq = self.assertEquals
187
165
        wt = self.make_branch_and_tree('parent')
188
166
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
189
167
        wt.add('file1')
201
179
        b = wt.branch
202
180
        lf = LogCatcher()
203
181
        lf.supports_merge_revisions = True
204
 
        show_log(b, lf, verbose=True)
205
 
        eq(len(lf.logs),3)
 
182
        log.show_log(b, lf, verbose=True)
 
183
 
 
184
        self.assertEqual(3, len(lf.logs))
 
185
 
206
186
        logentry = lf.logs[0]
207
 
        eq(logentry.revno, '2')
208
 
        eq(logentry.rev.message, 'merge child branch')
209
 
        d = logentry.delta
210
 
        self.checkDelta(d, removed=['file1'], modified=['file2'])
 
187
        self.assertEqual('2', logentry.revno)
 
188
        self.assertEqual('merge child branch', logentry.rev.message)
 
189
        self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
 
190
 
211
191
        logentry = lf.logs[1]
212
 
        eq(logentry.revno, '1.1.1')
213
 
        eq(logentry.rev.message, 'remove file1 and modify file2')
214
 
        d = logentry.delta
215
 
        self.checkDelta(d, removed=['file1'], modified=['file2'])
 
192
        self.assertEqual('1.1.1', logentry.revno)
 
193
        self.assertEqual('remove file1 and modify file2', logentry.rev.message)
 
194
        self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
 
195
 
216
196
        logentry = lf.logs[2]
217
 
        eq(logentry.revno, '1')
218
 
        eq(logentry.rev.message, 'add file1 and file2')
219
 
        d = logentry.delta
220
 
        self.checkDelta(d, added=['file1', 'file2'])
 
197
        self.assertEqual('1', logentry.revno)
 
198
        self.assertEqual('add file1 and file2', logentry.rev.message)
 
199
        self.checkDelta(logentry.delta, added=['file1', 'file2'])
221
200
 
222
201
    def test_merges_nonsupporting_formatter(self):
223
202
        """Tests that show_log will raise if the formatter doesn't
224
203
        support merge revisions."""
225
204
        wt = self.make_branch_and_memory_tree('.')
226
205
        wt.lock_write()
227
 
        try:
228
 
            wt.add('')
229
 
            wt.commit('rev-1', rev_id='rev-1',
230
 
                      timestamp=1132586655, timezone=36000,
231
 
                      committer='Joe Foo <joe@foo.com>')
232
 
            wt.commit('rev-merged', rev_id='rev-2a',
233
 
                      timestamp=1132586700, timezone=36000,
234
 
                      committer='Joe Foo <joe@foo.com>')
235
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
236
 
            wt.branch.set_last_revision_info(1, 'rev-1')
237
 
            wt.commit('rev-2', rev_id='rev-2b',
238
 
                      timestamp=1132586800, timezone=36000,
239
 
                      committer='Joe Foo <joe@foo.com>')
240
 
            logfile = self.make_utf8_encoded_stringio()
241
 
            formatter = ShortLogFormatter(to_file=logfile)
242
 
            wtb = wt.branch
243
 
            lf = LogCatcher()
244
 
            revspec = RevisionSpec.from_string('1.1.1')
245
 
            rev = revspec.in_history(wtb)
246
 
            self.assertRaises(BzrCommandError, show_log, wtb, lf,
247
 
                              start_revision=rev, end_revision=rev)
248
 
        finally:
249
 
            wt.unlock()
 
206
        self.addCleanup(wt.unlock)
 
207
        wt.add('')
 
208
        wt.commit('rev-1', rev_id='rev-1',
 
209
                  timestamp=1132586655, timezone=36000,
 
210
                  committer='Joe Foo <joe@foo.com>')
 
211
        wt.commit('rev-merged', rev_id='rev-2a',
 
212
                  timestamp=1132586700, timezone=36000,
 
213
                  committer='Joe Foo <joe@foo.com>')
 
214
        wt.set_parent_ids(['rev-1', 'rev-2a'])
 
215
        wt.branch.set_last_revision_info(1, 'rev-1')
 
216
        wt.commit('rev-2', rev_id='rev-2b',
 
217
                  timestamp=1132586800, timezone=36000,
 
218
                  committer='Joe Foo <joe@foo.com>')
 
219
        logfile = self.make_utf8_encoded_stringio()
 
220
        formatter = log.ShortLogFormatter(to_file=logfile)
 
221
        wtb = wt.branch
 
222
        lf = LogCatcher()
 
223
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
224
        rev = revspec.in_history(wtb)
 
225
        self.assertRaises(errors.BzrCommandError, log.show_log, wtb, lf,
 
226
                          start_revision=rev, end_revision=rev)
250
227
 
251
228
 
252
229
def make_commits_with_trailing_newlines(wt):
253
 
    """Helper method for LogFormatter tests"""    
 
230
    """Helper method for LogFormatter tests"""
254
231
    b = wt.branch
255
232
    b.nick='test'
256
233
    open('a', 'wb').write('hello moto\n')
290
267
    return ''.join(lines)
291
268
 
292
269
 
293
 
class TestShortLogFormatter(TestCaseWithTransport):
 
270
class TestShortLogFormatter(tests.TestCaseWithTransport):
294
271
 
295
272
    def test_trailing_newlines(self):
296
273
        wt = self.make_branch_and_tree('.')
297
274
        b = make_commits_with_trailing_newlines(wt)
298
275
        sio = self.make_utf8_encoded_stringio()
299
 
        lf = ShortLogFormatter(to_file=sio)
300
 
        show_log(b, lf)
301
 
        self.assertEqualDiff(sio.getvalue(), """\
 
276
        lf = log.ShortLogFormatter(to_file=sio)
 
277
        log.show_log(b, lf)
 
278
        self.assertEqualDiff("""\
302
279
    3 Joe Foo\t2005-11-21
303
280
      single line with trailing newline
304
281
 
310
287
    1 Joe Foo\t2005-11-21
311
288
      simple log message
312
289
 
313
 
""")
 
290
""",
 
291
                             sio.getvalue())
314
292
 
315
293
    def test_short_log_with_merges(self):
316
294
        wt = self.make_branch_and_memory_tree('.')
317
295
        wt.lock_write()
318
 
        try:
319
 
            wt.add('')
320
 
            wt.commit('rev-1', rev_id='rev-1',
321
 
                      timestamp=1132586655, timezone=36000,
322
 
                      committer='Joe Foo <joe@foo.com>')
323
 
            wt.commit('rev-merged', rev_id='rev-2a',
324
 
                      timestamp=1132586700, timezone=36000,
325
 
                      committer='Joe Foo <joe@foo.com>')
326
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
327
 
            wt.branch.set_last_revision_info(1, 'rev-1')
328
 
            wt.commit('rev-2', rev_id='rev-2b',
329
 
                      timestamp=1132586800, timezone=36000,
330
 
                      committer='Joe Foo <joe@foo.com>')
331
 
            logfile = self.make_utf8_encoded_stringio()
332
 
            formatter = ShortLogFormatter(to_file=logfile)
333
 
            show_log(wt.branch, formatter)
334
 
            self.assertEqualDiff(logfile.getvalue(), """\
 
296
        self.addCleanup(wt.unlock)
 
297
        wt.add('')
 
298
        wt.commit('rev-1', rev_id='rev-1',
 
299
                  timestamp=1132586655, timezone=36000,
 
300
                  committer='Joe Foo <joe@foo.com>')
 
301
        wt.commit('rev-merged', rev_id='rev-2a',
 
302
                  timestamp=1132586700, timezone=36000,
 
303
                  committer='Joe Foo <joe@foo.com>')
 
304
        wt.set_parent_ids(['rev-1', 'rev-2a'])
 
305
        wt.branch.set_last_revision_info(1, 'rev-1')
 
306
        wt.commit('rev-2', rev_id='rev-2b',
 
307
                  timestamp=1132586800, timezone=36000,
 
308
                  committer='Joe Foo <joe@foo.com>')
 
309
        logfile = self.make_utf8_encoded_stringio()
 
310
        formatter = log.ShortLogFormatter(to_file=logfile)
 
311
        log.show_log(wt.branch, formatter)
 
312
        self.assertEqualDiff("""\
335
313
    2 Joe Foo\t2005-11-22 [merge]
336
314
      rev-2
337
315
 
338
316
    1 Joe Foo\t2005-11-22
339
317
      rev-1
340
318
 
341
 
""")
342
 
        finally:
343
 
            wt.unlock()
 
319
""",
 
320
                             logfile.getvalue())
344
321
 
345
322
    def test_short_log_single_merge_revision(self):
346
323
        wt = self.make_branch_and_memory_tree('.')
347
324
        wt.lock_write()
348
 
        try:
349
 
            wt.add('')
350
 
            wt.commit('rev-1', rev_id='rev-1',
351
 
                      timestamp=1132586655, timezone=36000,
352
 
                      committer='Joe Foo <joe@foo.com>')
353
 
            wt.commit('rev-merged', rev_id='rev-2a',
354
 
                      timestamp=1132586700, timezone=36000,
355
 
                      committer='Joe Foo <joe@foo.com>')
356
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
357
 
            wt.branch.set_last_revision_info(1, 'rev-1')
358
 
            wt.commit('rev-2', rev_id='rev-2b',
359
 
                      timestamp=1132586800, timezone=36000,
360
 
                      committer='Joe Foo <joe@foo.com>')
361
 
            logfile = self.make_utf8_encoded_stringio()
362
 
            formatter = ShortLogFormatter(to_file=logfile)
363
 
            revspec = RevisionSpec.from_string('1.1.1')
364
 
            wtb = wt.branch
365
 
            rev = revspec.in_history(wtb)
366
 
            show_log(wtb, formatter, start_revision=rev, end_revision=rev)
367
 
            self.assertEqualDiff(logfile.getvalue(), """\
 
325
        self.addCleanup(wt.unlock)
 
326
        wt.add('')
 
327
        wt.commit('rev-1', rev_id='rev-1',
 
328
                  timestamp=1132586655, timezone=36000,
 
329
                  committer='Joe Foo <joe@foo.com>')
 
330
        wt.commit('rev-merged', rev_id='rev-2a',
 
331
                  timestamp=1132586700, timezone=36000,
 
332
                  committer='Joe Foo <joe@foo.com>')
 
333
        wt.set_parent_ids(['rev-1', 'rev-2a'])
 
334
        wt.branch.set_last_revision_info(1, 'rev-1')
 
335
        wt.commit('rev-2', rev_id='rev-2b',
 
336
                  timestamp=1132586800, timezone=36000,
 
337
                  committer='Joe Foo <joe@foo.com>')
 
338
        logfile = self.make_utf8_encoded_stringio()
 
339
        formatter = log.ShortLogFormatter(to_file=logfile)
 
340
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
341
        wtb = wt.branch
 
342
        rev = revspec.in_history(wtb)
 
343
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
 
344
        self.assertEqualDiff("""\
368
345
1.1.1 Joe Foo\t2005-11-22
369
346
      rev-merged
370
347
 
371
 
""")
372
 
        finally:
373
 
            wt.unlock()
 
348
""",
 
349
                             logfile.getvalue())
374
350
 
375
351
 
376
352
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
386
362
        wt.add('a')
387
363
        # XXX: why does a longer nick show up?
388
364
        b.nick = 'test_verbose_log'
389
 
        wt.commit(message='add a', 
390
 
                  timestamp=1132711707, 
 
365
        wt.commit(message='add a',
 
366
                  timestamp=1132711707,
391
367
                  timezone=36000,
392
368
                  committer='Lorem Ipsum <test@example.com>')
393
369
        logfile = file('out.tmp', 'w+')
394
 
        formatter = LongLogFormatter(to_file=logfile)
395
 
        show_log(b, formatter, verbose=True)
 
370
        formatter = log.LongLogFormatter(to_file=logfile)
 
371
        log.show_log(b, formatter, verbose=True)
396
372
        logfile.flush()
397
373
        logfile.seek(0)
398
374
        log_contents = logfile.read()
399
 
        self.assertEqualDiff(log_contents, '''\
 
375
        self.assertEqualDiff('''\
400
376
------------------------------------------------------------
401
377
revno: 1
402
378
committer: Lorem Ipsum <test@example.com>
406
382
  add a
407
383
added:
408
384
  a
409
 
''')
 
385
''',
 
386
                             log_contents)
410
387
 
411
388
    def test_merges_are_indented_by_level(self):
412
389
        wt = self.make_branch_and_tree('parent')
424
401
        wt.commit('merge branch 1')
425
402
        b = wt.branch
426
403
        sio = self.make_utf8_encoded_stringio()
427
 
        lf = LongLogFormatter(to_file=sio)
428
 
        show_log(b, lf, verbose=True)
429
 
        log = normalize_log(sio.getvalue())
430
 
        self.assertEqualDiff(log, """\
 
404
        lf = log.LongLogFormatter(to_file=sio)
 
405
        log.show_log(b, lf, verbose=True)
 
406
        the_log = normalize_log(sio.getvalue())
 
407
        self.assertEqualDiff("""\
431
408
------------------------------------------------------------
432
409
revno: 2
433
410
committer: Lorem Ipsum <test@example.com>
463
440
timestamp: Just now
464
441
message:
465
442
  first post
466
 
""")
 
443
""",
 
444
                             the_log)
467
445
 
468
446
    def test_verbose_merge_revisions_contain_deltas(self):
469
447
        wt = self.make_branch_and_tree('parent')
480
458
        wt.commit('merge branch 1')
481
459
        b = wt.branch
482
460
        sio = self.make_utf8_encoded_stringio()
483
 
        lf = LongLogFormatter(to_file=sio)
484
 
        show_log(b, lf, verbose=True)
485
 
        log = normalize_log(sio.getvalue())
486
 
        self.assertEqualDiff(log, """\
 
461
        lf = log.LongLogFormatter(to_file=sio)
 
462
        log.show_log(b, lf, verbose=True)
 
463
        the_log = normalize_log(sio.getvalue())
 
464
        self.assertEqualDiff("""\
487
465
------------------------------------------------------------
488
466
revno: 2
489
467
committer: Lorem Ipsum <test@example.com>
516
494
added:
517
495
  f1
518
496
  f2
519
 
""")
 
497
""",
 
498
                             the_log)
520
499
 
521
500
    def test_trailing_newlines(self):
522
501
        wt = self.make_branch_and_tree('.')
523
502
        b = make_commits_with_trailing_newlines(wt)
524
503
        sio = self.make_utf8_encoded_stringio()
525
 
        lf = LongLogFormatter(to_file=sio)
526
 
        show_log(b, lf)
527
 
        self.assertEqualDiff(sio.getvalue(), """\
 
504
        lf = log.LongLogFormatter(to_file=sio)
 
505
        log.show_log(b, lf)
 
506
        self.assertEqualDiff("""\
528
507
------------------------------------------------------------
529
508
revno: 3
530
509
committer: Joe Foo <joe@foo.com>
549
528
timestamp: Mon 2005-11-21 09:24:15 -0600
550
529
message:
551
530
  simple log message
552
 
""")
 
531
""",
 
532
                             sio.getvalue())
553
533
 
554
534
    def test_author_in_log(self):
555
535
        """Log includes the author name if it's set in
566
546
                  committer='Lorem Ipsum <test@example.com>',
567
547
                  author='John Doe <jdoe@example.com>')
568
548
        sio = StringIO()
569
 
        formatter = LongLogFormatter(to_file=sio)
570
 
        show_log(b, formatter)
571
 
        self.assertEqualDiff(sio.getvalue(), '''\
 
549
        formatter = log.LongLogFormatter(to_file=sio)
 
550
        log.show_log(b, formatter)
 
551
        self.assertEqualDiff('''\
572
552
------------------------------------------------------------
573
553
revno: 1
574
554
author: John Doe <jdoe@example.com>
577
557
timestamp: Wed 2005-11-23 12:08:27 +1000
578
558
message:
579
559
  add a
580
 
''')
 
560
''',
 
561
                             sio.getvalue())
581
562
 
582
563
    def test_properties_in_log(self):
583
564
        """Log includes the custom properties returned by the registered 
594
575
                  committer='Lorem Ipsum <test@example.com>',
595
576
                  author='John Doe <jdoe@example.com>')
596
577
        sio = StringIO()
597
 
        formatter = LongLogFormatter(to_file=sio)
 
578
        formatter = log.LongLogFormatter(to_file=sio)
598
579
        try:
599
580
            def trivial_custom_prop_handler(revision):
600
581
                return {'test_prop':'test_value'}
601
 
            
 
582
 
602
583
            log.properties_handler_registry.register(
603
 
                'trivial_custom_prop_handler', 
 
584
                'trivial_custom_prop_handler',
604
585
                trivial_custom_prop_handler)
605
 
            show_log(b, formatter)
 
586
            log.show_log(b, formatter)
606
587
        finally:
607
588
            log.properties_handler_registry.remove(
608
589
                'trivial_custom_prop_handler')
609
 
            self.assertEqualDiff(sio.getvalue(), '''\
 
590
            self.assertEqualDiff('''\
610
591
------------------------------------------------------------
611
592
revno: 1
612
593
test_prop: test_value
616
597
timestamp: Wed 2005-11-23 12:08:27 +1000
617
598
message:
618
599
  add a
619
 
''')
 
600
''',
 
601
                                 sio.getvalue())
620
602
 
621
603
    def test_error_in_properties_handler(self):
622
604
        """Log includes the custom properties returned by the registered 
634
616
                  author='John Doe <jdoe@example.com>',
635
617
                  revprops={'first_prop':'first_value'})
636
618
        sio = StringIO()
637
 
        formatter = LongLogFormatter(to_file=sio)
 
619
        formatter = log.LongLogFormatter(to_file=sio)
638
620
        try:
639
621
            def trivial_custom_prop_handler(revision):
640
622
                raise StandardError("a test error")
641
 
            
 
623
 
642
624
            log.properties_handler_registry.register(
643
 
                'trivial_custom_prop_handler', 
 
625
                'trivial_custom_prop_handler',
644
626
                trivial_custom_prop_handler)
645
 
            self.assertRaises(StandardError, show_log, b, formatter,)
 
627
            self.assertRaises(StandardError, log.show_log, b, formatter,)
646
628
        finally:
647
629
            log.properties_handler_registry.remove(
648
630
                'trivial_custom_prop_handler')
649
 
                
 
631
 
650
632
    def test_properties_handler_bad_argument(self):
651
633
        wt = self.make_branch_and_tree('.')
652
634
        b = wt.branch
660
642
                  author='John Doe <jdoe@example.com>',
661
643
                  revprops={'a_prop':'test_value'})
662
644
        sio = StringIO()
663
 
        formatter = LongLogFormatter(to_file=sio)
 
645
        formatter = log.LongLogFormatter(to_file=sio)
664
646
        try:
665
647
            def bad_argument_prop_handler(revision):
666
648
                return {'custom_prop_name':revision.properties['a_prop']}
667
 
                
 
649
 
668
650
            log.properties_handler_registry.register(
669
 
                'bad_argument_prop_handler', 
 
651
                'bad_argument_prop_handler',
670
652
                bad_argument_prop_handler)
671
 
            
672
 
            self.assertRaises(AttributeError, formatter.show_properties, 
673
 
                'a revision', '')
674
 
            
 
653
 
 
654
            self.assertRaises(AttributeError, formatter.show_properties,
 
655
                              'a revision', '')
 
656
 
675
657
            revision = b.repository.get_revision(b.last_revision())
676
658
            formatter.show_properties(revision, '')
677
 
            self.assertEqualDiff(sio.getvalue(),
678
 
                '''custom_prop_name: test_value\n''')
 
659
            self.assertEqualDiff('''custom_prop_name: test_value\n''',
 
660
                                 sio.getvalue())
679
661
        finally:
680
662
            log.properties_handler_registry.remove(
681
663
                'bad_argument_prop_handler')
682
664
 
683
665
 
684
 
class TestLineLogFormatter(TestCaseWithTransport):
 
666
class TestLineLogFormatter(tests.TestCaseWithTransport):
685
667
 
686
668
    def test_line_log(self):
687
669
        """Line log should show revno
698
680
                  timezone=36000,
699
681
                  committer='Line-Log-Formatter Tester <test@line.log>')
700
682
        logfile = file('out.tmp', 'w+')
701
 
        formatter = LineLogFormatter(to_file=logfile)
702
 
        show_log(b, formatter)
 
683
        formatter = log.LineLogFormatter(to_file=logfile)
 
684
        log.show_log(b, formatter)
703
685
        logfile.flush()
704
686
        logfile.seek(0)
705
687
        log_contents = logfile.read()
706
 
        self.assertEqualDiff(log_contents,
707
 
            '1: Line-Log-Formatte... 2005-11-23 add a\n')
 
688
        self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
 
689
                             log_contents)
708
690
 
709
691
    def test_trailing_newlines(self):
710
692
        wt = self.make_branch_and_tree('.')
711
693
        b = make_commits_with_trailing_newlines(wt)
712
694
        sio = self.make_utf8_encoded_stringio()
713
 
        lf = LineLogFormatter(to_file=sio)
714
 
        show_log(b, lf)
715
 
        self.assertEqualDiff(sio.getvalue(), """\
 
695
        lf = log.LineLogFormatter(to_file=sio)
 
696
        log.show_log(b, lf)
 
697
        self.assertEqualDiff("""\
716
698
3: Joe Foo 2005-11-21 single line with trailing newline
717
699
2: Joe Bar 2005-11-21 multiline
718
700
1: Joe Foo 2005-11-21 simple log message
719
 
""")
 
701
""",
 
702
                             sio.getvalue())
720
703
 
721
704
    def test_line_log_single_merge_revision(self):
722
705
        wt = self.make_branch_and_memory_tree('.')
723
706
        wt.lock_write()
724
 
        try:
725
 
            wt.add('')
726
 
            wt.commit('rev-1', rev_id='rev-1',
727
 
                      timestamp=1132586655, timezone=36000,
728
 
                      committer='Joe Foo <joe@foo.com>')
729
 
            wt.commit('rev-merged', rev_id='rev-2a',
730
 
                      timestamp=1132586700, timezone=36000,
731
 
                      committer='Joe Foo <joe@foo.com>')
732
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
733
 
            wt.branch.set_last_revision_info(1, 'rev-1')
734
 
            wt.commit('rev-2', rev_id='rev-2b',
735
 
                      timestamp=1132586800, timezone=36000,
736
 
                      committer='Joe Foo <joe@foo.com>')
737
 
            logfile = self.make_utf8_encoded_stringio()
738
 
            formatter = LineLogFormatter(to_file=logfile)
739
 
            revspec = RevisionSpec.from_string('1.1.1')
740
 
            wtb = wt.branch
741
 
            rev = revspec.in_history(wtb)
742
 
            show_log(wtb, formatter, start_revision=rev, end_revision=rev)
743
 
            self.assertEqualDiff(logfile.getvalue(), """\
 
707
        self.addCleanup(wt.unlock)
 
708
        wt.add('')
 
709
        wt.commit('rev-1', rev_id='rev-1',
 
710
                  timestamp=1132586655, timezone=36000,
 
711
                  committer='Joe Foo <joe@foo.com>')
 
712
        wt.commit('rev-merged', rev_id='rev-2a',
 
713
                  timestamp=1132586700, timezone=36000,
 
714
                  committer='Joe Foo <joe@foo.com>')
 
715
        wt.set_parent_ids(['rev-1', 'rev-2a'])
 
716
        wt.branch.set_last_revision_info(1, 'rev-1')
 
717
        wt.commit('rev-2', rev_id='rev-2b',
 
718
                  timestamp=1132586800, timezone=36000,
 
719
                  committer='Joe Foo <joe@foo.com>')
 
720
        logfile = self.make_utf8_encoded_stringio()
 
721
        formatter = log.LineLogFormatter(to_file=logfile)
 
722
        revspec = revisionspec.RevisionSpec.from_string('1.1.1')
 
723
        wtb = wt.branch
 
724
        rev = revspec.in_history(wtb)
 
725
        log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
 
726
        self.assertEqualDiff("""\
744
727
1.1.1: Joe Foo 2005-11-22 rev-merged
745
 
""")
746
 
        finally:
747
 
            wt.unlock()
748
 
 
749
 
 
750
 
 
751
 
class TestGetViewRevisions(TestCaseWithTransport):
 
728
""",
 
729
                             logfile.getvalue())
 
730
 
 
731
 
 
732
 
 
733
class TestGetViewRevisions(tests.TestCaseWithTransport):
752
734
 
753
735
    def make_tree_with_commits(self):
754
736
        """Create a tree with well-known revision ids"""
775
757
    def make_tree_with_many_merges(self):
776
758
        """Create a tree with well-known revision ids"""
777
759
        wt = self.make_branch_and_tree('tree1')
 
760
        self.build_tree_contents([('tree1/f', '1\n')])
 
761
        wt.add(['f'], ['f-id'])
778
762
        wt.commit('commit one', rev_id='1')
779
763
        wt.commit('commit two', rev_id='2')
 
764
 
780
765
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
 
766
        self.build_tree_contents([('tree3/f', '1\n2\n3a\n')])
781
767
        tree3.commit('commit three a', rev_id='3a')
 
768
 
782
769
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
783
770
        tree2.merge_from_branch(tree3.branch)
784
771
        tree2.commit('commit three b', rev_id='3b')
 
772
 
785
773
        wt.merge_from_branch(tree2.branch)
786
774
        wt.commit('commit three c', rev_id='3c')
787
775
        tree2.commit('four-a', rev_id='4a')
 
776
 
788
777
        wt.merge_from_branch(tree2.branch)
789
778
        wt.commit('four-b', rev_id='4b')
 
779
 
790
780
        mainline_revs = [None, '1', '2', '3c', '4b']
791
781
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
792
782
        full_rev_nos_for_reference = {
805
795
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
806
796
        wt.lock_read()
807
797
        self.addCleanup(wt.unlock)
808
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
809
 
                                            'forward'))
 
798
        revisions = list(log.get_view_revisions(
 
799
                mainline_revs, rev_nos, wt.branch, 'forward'))
810
800
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
811
 
            revisions)
812
 
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
813
 
                                             'forward', include_merges=False))
 
801
                         revisions)
 
802
        revisions2 = list(log.get_view_revisions(
 
803
                mainline_revs, rev_nos, wt.branch, 'forward',
 
804
                include_merges=False))
814
805
        self.assertEqual(revisions, revisions2)
815
806
 
816
807
    def test_get_view_revisions_reverse(self):
818
809
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
819
810
        wt.lock_read()
820
811
        self.addCleanup(wt.unlock)
821
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
822
 
                                            'reverse'))
 
812
        revisions = list(log.get_view_revisions(
 
813
                mainline_revs, rev_nos, wt.branch, 'reverse'))
823
814
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
824
 
            revisions)
825
 
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
826
 
                                             'reverse', include_merges=False))
 
815
                         revisions)
 
816
        revisions2 = list(log.get_view_revisions(
 
817
                mainline_revs, rev_nos, wt.branch, 'reverse',
 
818
                include_merges=False))
827
819
        self.assertEqual(revisions, revisions2)
828
820
 
829
821
    def test_get_view_revisions_merge(self):
831
823
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
832
824
        wt.lock_read()
833
825
        self.addCleanup(wt.unlock)
834
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
835
 
                                            'forward'))
836
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
837
 
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
838
 
            revisions)
839
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
840
 
                                             'forward', include_merges=False))
841
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
842
 
            ('4b', '4', 0)],
843
 
            revisions)
 
826
        revisions = list(log.get_view_revisions(
 
827
                mainline_revs, rev_nos, wt.branch, 'forward'))
 
828
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
829
                          ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
830
                         revisions)
 
831
        revisions = list(log.get_view_revisions(
 
832
                mainline_revs, rev_nos, wt.branch, 'forward',
 
833
                include_merges=False))
 
834
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
835
                          ('4b', '4', 0)],
 
836
                         revisions)
844
837
 
845
838
    def test_get_view_revisions_merge_reverse(self):
846
839
        """Test get_view_revisions in reverse when there are merges"""
847
840
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
848
841
        wt.lock_read()
849
842
        self.addCleanup(wt.unlock)
850
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
851
 
                                            'reverse'))
 
843
        revisions = list(log.get_view_revisions(
 
844
                mainline_revs, rev_nos, wt.branch, 'reverse'))
852
845
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
853
 
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
854
 
            revisions)
855
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
856
 
                                             'reverse', include_merges=False))
 
846
                          ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
847
                         revisions)
 
848
        revisions = list(log.get_view_revisions(
 
849
                mainline_revs, rev_nos, wt.branch, 'reverse',
 
850
                include_merges=False))
857
851
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
858
 
            ('1', '1', 0)],
859
 
            revisions)
 
852
                          ('1', '1', 0)],
 
853
                         revisions)
860
854
 
861
855
    def test_get_view_revisions_merge2(self):
862
856
        """Test get_view_revisions when there are merges"""
863
857
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
864
858
        wt.lock_read()
865
859
        self.addCleanup(wt.unlock)
866
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
867
 
                                            'forward'))
 
860
        revisions = list(log.get_view_revisions(
 
861
                mainline_revs, rev_nos, wt.branch, 'forward'))
868
862
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
869
 
            ('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
870
 
            ('4a', '2.2.2', 1)]
 
863
                    ('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
 
864
                    ('4a', '2.2.2', 1)]
871
865
        self.assertEqual(expected, revisions)
872
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
873
 
                                             'forward', include_merges=False))
 
866
        revisions = list(log.get_view_revisions(
 
867
                mainline_revs, rev_nos, wt.branch, 'forward',
 
868
                include_merges=False))
874
869
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
875
 
            ('4b', '4', 0)],
876
 
            revisions)
877
 
 
878
 
 
879
 
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
 
870
                          ('4b', '4', 0)],
 
871
                         revisions)
 
872
 
 
873
 
 
874
    def test_file_id_for_range(self):
 
875
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
 
876
        wt.lock_read()
 
877
        self.addCleanup(wt.unlock)
 
878
 
 
879
        def rev_from_rev_id(revid, branch):
 
880
            revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
 
881
            return revspec.in_history(branch)
 
882
 
 
883
        def view_revs(start_rev, end_rev, file_id, direction):
 
884
            revs = log.calculate_view_revisions(
 
885
                wt.branch,
 
886
                start_rev, # start_revision
 
887
                end_rev, # end_revision
 
888
                direction, # direction
 
889
                file_id, # specific_fileid
 
890
                True, # generate_merge_revisions
 
891
                True, # allow_single_merge_revision
 
892
                )
 
893
            return revs
 
894
 
 
895
        rev_3a = rev_from_rev_id('3a', wt.branch)
 
896
        rev_4b = rev_from_rev_id('4b', wt.branch)
 
897
        self.assertEqual([('3c', '3', 0), ('3a', '2.1.1', 1)],
 
898
                          view_revs(rev_3a, rev_4b, 'f-id', 'reverse'))
 
899
        # Note that the depth is 0 for 3a because depths are normalized, but
 
900
        # there is still a bug somewhere... most probably in
 
901
        # _filter_revision_range and/or get_view_revisions still around a bad
 
902
        # use of reverse_by_depth
 
903
        self.assertEqual([('3a', '2.1.1', 0)],
 
904
                          view_revs(rev_3a, rev_4b, 'f-id', 'forward'))
 
905
 
 
906
 
 
907
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
880
908
 
881
909
    def create_tree_with_single_merge(self):
882
910
        """Create a branch with a moderate layout.
932
960
        tree.commit('D', rev_id='D')
933
961
 
934
962
        # Switch to a read lock for this tree.
935
 
        # We still have addCleanup(unlock)
 
963
        # We still have an addCleanup(tree.unlock) pending
936
964
        tree.unlock()
937
965
        tree.lock_read()
938
966
        return tree
939
967
 
 
968
    def check_delta(self, delta, **kw):
 
969
        """Check the filenames touched by a delta are as expected.
 
970
 
 
971
        Caller only have to pass in the list of files for each part, all
 
972
        unspecified parts are considered empty (and checked as such).
 
973
        """
 
974
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
 
975
            # By default we expect an empty list
 
976
            expected = kw.get(n, [])
 
977
            # strip out only the path components
 
978
            got = [x[0] for x in getattr(delta, n)]
 
979
            self.assertEqual(expected, got)
 
980
 
940
981
    def test_tree_with_single_merge(self):
941
982
        """Make sure the tree layout is correct."""
942
983
        tree = self.create_tree_with_single_merge()
943
984
        rev_A_tree = tree.branch.repository.revision_tree('A')
944
985
        rev_B_tree = tree.branch.repository.revision_tree('B')
945
 
 
946
 
        f1_changed = (u'f1', 'f1-id', 'file', True, False)
947
 
        f2_changed = (u'f2', 'f2-id', 'file', True, False)
948
 
        f3_changed = (u'f3', 'f3-id', 'file', True, False)
949
 
 
950
 
        delta = rev_B_tree.changes_from(rev_A_tree)
951
 
        self.assertEqual([f1_changed, f3_changed], delta.modified)
952
 
        self.assertEqual([], delta.renamed)
953
 
        self.assertEqual([], delta.added)
954
 
        self.assertEqual([], delta.removed)
955
 
 
956
986
        rev_C_tree = tree.branch.repository.revision_tree('C')
957
 
        delta = rev_C_tree.changes_from(rev_A_tree)
958
 
        self.assertEqual([f2_changed, f3_changed], delta.modified)
959
 
        self.assertEqual([], delta.renamed)
960
 
        self.assertEqual([], delta.added)
961
 
        self.assertEqual([], delta.removed)
962
 
 
963
987
        rev_D_tree = tree.branch.repository.revision_tree('D')
964
 
        delta = rev_D_tree.changes_from(rev_B_tree)
965
 
        self.assertEqual([f2_changed, f3_changed], delta.modified)
966
 
        self.assertEqual([], delta.renamed)
967
 
        self.assertEqual([], delta.added)
968
 
        self.assertEqual([], delta.removed)
969
 
 
970
 
        delta = rev_D_tree.changes_from(rev_C_tree)
971
 
        self.assertEqual([f1_changed, f3_changed], delta.modified)
972
 
        self.assertEqual([], delta.renamed)
973
 
        self.assertEqual([], delta.added)
974
 
        self.assertEqual([], delta.removed)
 
988
 
 
989
        self.check_delta(rev_B_tree.changes_from(rev_A_tree),
 
990
                         modified=['f1', 'f3'])
 
991
 
 
992
        self.check_delta(rev_C_tree.changes_from(rev_A_tree),
 
993
                         modified=['f2', 'f3'])
 
994
 
 
995
        self.check_delta(rev_D_tree.changes_from(rev_B_tree),
 
996
                         modified=['f2', 'f3'])
 
997
 
 
998
        self.check_delta(rev_D_tree.changes_from(rev_C_tree),
 
999
                         modified=['f1', 'f3'])
975
1000
 
976
1001
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
977
 
        """Make sure _filter_revisions_touching_file_id returns the right values.
 
1002
        """Ensure _filter_revisions_touching_file_id returns the right values.
978
1003
 
979
1004
        Get the return value from _filter_revisions_touching_file_id and make
980
1005
        sure they are correct.
981
1006
        """
982
 
        # The api for _get_revisions_touching_file_id is a little crazy,
 
1007
        # The api for _filter_revisions_touching_file_id is a little crazy.
983
1008
        # So we do the setup here.
984
1009
        mainline = tree.branch.revision_history()
985
1010
        mainline.insert(0, None)
989
1014
        actual_revs = log._filter_revisions_touching_file_id(
990
1015
                            tree.branch,
991
1016
                            file_id,
992
 
                            list(view_revs_iter),
993
 
                            'reverse')
 
1017
                            list(view_revs_iter))
994
1018
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
995
1019
 
996
1020
    def test_file_id_f1(self):
1007
1031
    def test_file_id_f3(self):
1008
1032
        tree = self.create_tree_with_single_merge()
1009
1033
        # f3 should be marked as modified by revisions A, B, C, and D
1010
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
1034
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1011
1035
 
1012
1036
    def test_file_id_with_ghosts(self):
1013
1037
        # This is testing bug #209948, where having a ghost would cause
1014
1038
        # _filter_revisions_touching_file_id() to fail.
1015
1039
        tree = self.create_tree_with_single_merge()
1016
1040
        # We need to add a revision, so switch back to a write-locked tree
 
1041
        # (still a single addCleanup(tree.unlock) pending).
1017
1042
        tree.unlock()
1018
1043
        tree.lock_write()
1019
1044
        first_parent = tree.last_revision()
1024
1049
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1025
1050
 
1026
1051
 
1027
 
class TestShowChangedRevisions(TestCaseWithTransport):
 
1052
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1028
1053
 
1029
1054
    def test_show_changed_revisions_verbose(self):
1030
1055
        tree = self.make_branch_and_tree('tree_a')
1037
1062
        self.assertNotContainsRe(s.getvalue(), 'foo')
1038
1063
 
1039
1064
 
1040
 
class TestLogFormatter(TestCase):
 
1065
class TestLogFormatter(tests.TestCase):
1041
1066
 
1042
1067
    def test_short_committer(self):
1043
 
        rev = Revision('a-id')
 
1068
        rev = revision.Revision('a-id')
1044
1069
        rev.committer = 'John Doe <jdoe@example.com>'
1045
 
        lf = LogFormatter(None)
 
1070
        lf = log.LogFormatter(None)
1046
1071
        self.assertEqual('John Doe', lf.short_committer(rev))
1047
1072
        rev.committer = 'John Smith <jsmith@example.com>'
1048
1073
        self.assertEqual('John Smith', lf.short_committer(rev))
1056
1081
        self.assertEqual('John Smith', lf.short_committer(rev))
1057
1082
 
1058
1083
    def test_short_author(self):
1059
 
        rev = Revision('a-id')
 
1084
        rev = revision.Revision('a-id')
1060
1085
        rev.committer = 'John Doe <jdoe@example.com>'
1061
 
        lf = LogFormatter(None)
 
1086
        lf = log.LogFormatter(None)
1062
1087
        self.assertEqual('John Doe', lf.short_author(rev))
1063
1088
        rev.properties['author'] = 'John Smith <jsmith@example.com>'
1064
1089
        self.assertEqual('John Smith', lf.short_author(rev))
1070
1095
        self.assertEqual('jsmith@example.com', lf.short_author(rev))
1071
1096
        rev.properties['author'] = 'John Smith jsmith@example.com'
1072
1097
        self.assertEqual('John Smith', lf.short_author(rev))
 
1098
 
 
1099
 
 
1100
class TestReverseByDepth(tests.TestCase):
 
1101
    """Test reverse_by_depth behavior.
 
1102
 
 
1103
    This is used to present revisions in forward (oldest first) order in a nice
 
1104
    layout.
 
1105
 
 
1106
    The tests use lighter revision description to ease reading.
 
1107
    """
 
1108
 
 
1109
    def assertReversed(self, forward, backward):
 
1110
        # Transform the descriptions to suit the API: tests use (revno, depth),
 
1111
        # while the API expects (revid, revno, depth)
 
1112
        def complete_revisions(l):
 
1113
            """Transform the description to suit the API.
 
1114
 
 
1115
            Tests use (revno, depth) whil the API expects (revid, revno, depth).
 
1116
            Since the revid is arbitrary, we just duplicate revno
 
1117
            """
 
1118
            return [ (r, r, d) for r, d in l]
 
1119
        forward = complete_revisions(forward)
 
1120
        backward= complete_revisions(backward)
 
1121
        self.assertEqual(forward, log.reverse_by_depth(backward))
 
1122
 
 
1123
 
 
1124
    def test_mainline_revisions(self):
 
1125
        self.assertReversed([( '1', 0), ('2', 0)],
 
1126
                            [('2', 0), ('1', 0)])
 
1127
 
 
1128
    def test_merged_revisions(self):
 
1129
        self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
 
1130
                            [('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
 
1131
    def test_shifted_merged_revisions(self):
 
1132
        """Test irregular layout.
 
1133
 
 
1134
        Requesting revisions touching a file can produce "holes" in the depths.
 
1135
        """
 
1136
        self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
 
1137
                            [('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
 
1138
 
 
1139
    def test_merged_without_child_revisions(self):
 
1140
        """Test irregular layout.
 
1141
 
 
1142
        Revision ranges can produce "holes" in the depths.
 
1143
        """
 
1144
        # When a revision of higher depth doesn't follow one of lower depth, we
 
1145
        # assume a lower depth one is virtually there
 
1146
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
 
1147
                            [('4', 4), ('3', 3), ('2', 2), ('1', 2),])
 
1148
        # So we get the same order after reversing below even if the original
 
1149
        # revisions are not in the same order.
 
1150
        self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
 
1151
                            [('3', 3), ('4', 4), ('2', 2), ('1', 2),])