/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

merge bzr.dev@3883

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'])
157
 
        
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)
 
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'])
 
138
 
 
139
    def test_commit_message_with_control_chars(self):
 
140
        wt = self.make_branch_and_tree('.')
 
141
        msg = u"All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
 
142
        msg = msg.replace(u'\r', u'\n')
161
143
        wt.commit(msg)
162
144
        lf = LogCatcher()
163
 
        show_log(b, lf, verbose=True)
 
145
        log.show_log(wt.branch, lf, verbose=True)
164
146
        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))
 
147
        self.assertNotEqual(msg, committed_msg)
 
148
        self.assertTrue(len(committed_msg) > len(msg))
168
149
 
169
 
        # 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('.')
170
152
        # escaped.  As ElementTree apparently does some kind of
171
153
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
172
154
        # included in the test commit message, even though they are
173
155
        # valid XML 1.0 characters.
174
156
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
175
 
        self.log("original commit message: %r", msg)
176
157
        wt.commit(msg)
177
158
        lf = LogCatcher()
178
 
        show_log(b, lf, verbose=True)
 
159
        log.show_log(wt.branch, lf, verbose=True)
179
160
        committed_msg = lf.logs[0].rev.message
180
 
        self.log("escaped commit message: %r", committed_msg)
181
 
        self.assert_(msg == committed_msg)
 
161
        self.assertEqual(msg, committed_msg)
182
162
 
183
163
    def test_deltas_in_merge_revisions(self):
184
164
        """Check deltas created for both mainline and merge revisions"""
185
 
        eq = self.assertEquals
186
165
        wt = self.make_branch_and_tree('parent')
187
166
        self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
188
167
        wt.add('file1')
200
179
        b = wt.branch
201
180
        lf = LogCatcher()
202
181
        lf.supports_merge_revisions = True
203
 
        show_log(b, lf, verbose=True)
204
 
        eq(len(lf.logs),3)
 
182
        log.show_log(b, lf, verbose=True)
 
183
 
 
184
        self.assertEqual(3, len(lf.logs))
 
185
 
205
186
        logentry = lf.logs[0]
206
 
        eq(logentry.revno, '2')
207
 
        eq(logentry.rev.message, 'merge child branch')
208
 
        d = logentry.delta
209
 
        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
 
210
191
        logentry = lf.logs[1]
211
 
        eq(logentry.revno, '1.1.1')
212
 
        eq(logentry.rev.message, 'remove file1 and modify file2')
213
 
        d = logentry.delta
214
 
        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
 
215
196
        logentry = lf.logs[2]
216
 
        eq(logentry.revno, '1')
217
 
        eq(logentry.rev.message, 'add file1 and file2')
218
 
        d = logentry.delta
219
 
        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'])
220
200
 
221
201
    def test_merges_nonsupporting_formatter(self):
222
202
        """Tests that show_log will raise if the formatter doesn't
223
203
        support merge revisions."""
224
204
        wt = self.make_branch_and_memory_tree('.')
225
205
        wt.lock_write()
226
 
        try:
227
 
            wt.add('')
228
 
            wt.commit('rev-1', rev_id='rev-1',
229
 
                      timestamp=1132586655, timezone=36000,
230
 
                      committer='Joe Foo <joe@foo.com>')
231
 
            wt.commit('rev-merged', rev_id='rev-2a',
232
 
                      timestamp=1132586700, timezone=36000,
233
 
                      committer='Joe Foo <joe@foo.com>')
234
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
235
 
            wt.branch.set_last_revision_info(1, 'rev-1')
236
 
            wt.commit('rev-2', rev_id='rev-2b',
237
 
                      timestamp=1132586800, timezone=36000,
238
 
                      committer='Joe Foo <joe@foo.com>')
239
 
            logfile = self.make_utf8_encoded_stringio()
240
 
            formatter = ShortLogFormatter(to_file=logfile)
241
 
            wtb = wt.branch
242
 
            lf = LogCatcher()
243
 
            revspec = RevisionSpec.from_string('1.1.1')
244
 
            rev = revspec.in_history(wtb)
245
 
            self.assertRaises(BzrCommandError, show_log, wtb, lf,
246
 
                              start_revision=rev, end_revision=rev)
247
 
        finally:
248
 
            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)
249
227
 
250
228
 
251
229
def make_commits_with_trailing_newlines(wt):
252
 
    """Helper method for LogFormatter tests"""    
 
230
    """Helper method for LogFormatter tests"""
253
231
    b = wt.branch
254
232
    b.nick='test'
255
233
    open('a', 'wb').write('hello moto\n')
289
267
    return ''.join(lines)
290
268
 
291
269
 
292
 
class TestShortLogFormatter(TestCaseWithTransport):
 
270
class TestShortLogFormatter(tests.TestCaseWithTransport):
293
271
 
294
272
    def test_trailing_newlines(self):
295
273
        wt = self.make_branch_and_tree('.')
296
274
        b = make_commits_with_trailing_newlines(wt)
297
275
        sio = self.make_utf8_encoded_stringio()
298
 
        lf = ShortLogFormatter(to_file=sio)
299
 
        show_log(b, lf)
300
 
        self.assertEqualDiff(sio.getvalue(), """\
 
276
        lf = log.ShortLogFormatter(to_file=sio)
 
277
        log.show_log(b, lf)
 
278
        self.assertEqualDiff("""\
301
279
    3 Joe Foo\t2005-11-21
302
280
      single line with trailing newline
303
281
 
309
287
    1 Joe Foo\t2005-11-21
310
288
      simple log message
311
289
 
312
 
""")
 
290
""",
 
291
                             sio.getvalue())
313
292
 
314
293
    def test_short_log_with_merges(self):
315
294
        wt = self.make_branch_and_memory_tree('.')
316
295
        wt.lock_write()
317
 
        try:
318
 
            wt.add('')
319
 
            wt.commit('rev-1', rev_id='rev-1',
320
 
                      timestamp=1132586655, timezone=36000,
321
 
                      committer='Joe Foo <joe@foo.com>')
322
 
            wt.commit('rev-merged', rev_id='rev-2a',
323
 
                      timestamp=1132586700, timezone=36000,
324
 
                      committer='Joe Foo <joe@foo.com>')
325
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
326
 
            wt.branch.set_last_revision_info(1, 'rev-1')
327
 
            wt.commit('rev-2', rev_id='rev-2b',
328
 
                      timestamp=1132586800, timezone=36000,
329
 
                      committer='Joe Foo <joe@foo.com>')
330
 
            logfile = self.make_utf8_encoded_stringio()
331
 
            formatter = ShortLogFormatter(to_file=logfile)
332
 
            show_log(wt.branch, formatter)
333
 
            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("""\
334
313
    2 Joe Foo\t2005-11-22 [merge]
335
314
      rev-2
336
315
 
337
316
    1 Joe Foo\t2005-11-22
338
317
      rev-1
339
318
 
340
 
""")
341
 
        finally:
342
 
            wt.unlock()
 
319
""",
 
320
                             logfile.getvalue())
343
321
 
344
322
    def test_short_log_single_merge_revision(self):
345
323
        wt = self.make_branch_and_memory_tree('.')
346
324
        wt.lock_write()
347
 
        try:
348
 
            wt.add('')
349
 
            wt.commit('rev-1', rev_id='rev-1',
350
 
                      timestamp=1132586655, timezone=36000,
351
 
                      committer='Joe Foo <joe@foo.com>')
352
 
            wt.commit('rev-merged', rev_id='rev-2a',
353
 
                      timestamp=1132586700, timezone=36000,
354
 
                      committer='Joe Foo <joe@foo.com>')
355
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
356
 
            wt.branch.set_last_revision_info(1, 'rev-1')
357
 
            wt.commit('rev-2', rev_id='rev-2b',
358
 
                      timestamp=1132586800, timezone=36000,
359
 
                      committer='Joe Foo <joe@foo.com>')
360
 
            logfile = self.make_utf8_encoded_stringio()
361
 
            formatter = ShortLogFormatter(to_file=logfile)
362
 
            revspec = RevisionSpec.from_string('1.1.1')
363
 
            wtb = wt.branch
364
 
            rev = revspec.in_history(wtb)
365
 
            show_log(wtb, formatter, start_revision=rev, end_revision=rev)
366
 
            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("""\
367
345
1.1.1 Joe Foo\t2005-11-22
368
346
      rev-merged
369
347
 
370
 
""")
371
 
        finally:
372
 
            wt.unlock()
 
348
""",
 
349
                             logfile.getvalue())
373
350
 
374
351
 
375
352
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
385
362
        wt.add('a')
386
363
        # XXX: why does a longer nick show up?
387
364
        b.nick = 'test_verbose_log'
388
 
        wt.commit(message='add a', 
389
 
                  timestamp=1132711707, 
 
365
        wt.commit(message='add a',
 
366
                  timestamp=1132711707,
390
367
                  timezone=36000,
391
368
                  committer='Lorem Ipsum <test@example.com>')
392
369
        logfile = file('out.tmp', 'w+')
393
 
        formatter = LongLogFormatter(to_file=logfile)
394
 
        show_log(b, formatter, verbose=True)
 
370
        formatter = log.LongLogFormatter(to_file=logfile)
 
371
        log.show_log(b, formatter, verbose=True)
395
372
        logfile.flush()
396
373
        logfile.seek(0)
397
374
        log_contents = logfile.read()
398
 
        self.assertEqualDiff(log_contents, '''\
 
375
        self.assertEqualDiff('''\
399
376
------------------------------------------------------------
400
377
revno: 1
401
378
committer: Lorem Ipsum <test@example.com>
405
382
  add a
406
383
added:
407
384
  a
408
 
''')
 
385
''',
 
386
                             log_contents)
409
387
 
410
388
    def test_merges_are_indented_by_level(self):
411
389
        wt = self.make_branch_and_tree('parent')
423
401
        wt.commit('merge branch 1')
424
402
        b = wt.branch
425
403
        sio = self.make_utf8_encoded_stringio()
426
 
        lf = LongLogFormatter(to_file=sio)
427
 
        show_log(b, lf, verbose=True)
428
 
        log = normalize_log(sio.getvalue())
429
 
        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("""\
430
408
------------------------------------------------------------
431
409
revno: 2
432
410
committer: Lorem Ipsum <test@example.com>
462
440
timestamp: Just now
463
441
message:
464
442
  first post
465
 
""")
 
443
""",
 
444
                             the_log)
466
445
 
467
446
    def test_verbose_merge_revisions_contain_deltas(self):
468
447
        wt = self.make_branch_and_tree('parent')
479
458
        wt.commit('merge branch 1')
480
459
        b = wt.branch
481
460
        sio = self.make_utf8_encoded_stringio()
482
 
        lf = LongLogFormatter(to_file=sio)
483
 
        show_log(b, lf, verbose=True)
484
 
        log = normalize_log(sio.getvalue())
485
 
        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("""\
486
465
------------------------------------------------------------
487
466
revno: 2
488
467
committer: Lorem Ipsum <test@example.com>
515
494
added:
516
495
  f1
517
496
  f2
518
 
""")
 
497
""",
 
498
                             the_log)
519
499
 
520
500
    def test_trailing_newlines(self):
521
501
        wt = self.make_branch_and_tree('.')
522
502
        b = make_commits_with_trailing_newlines(wt)
523
503
        sio = self.make_utf8_encoded_stringio()
524
 
        lf = LongLogFormatter(to_file=sio)
525
 
        show_log(b, lf)
526
 
        self.assertEqualDiff(sio.getvalue(), """\
 
504
        lf = log.LongLogFormatter(to_file=sio)
 
505
        log.show_log(b, lf)
 
506
        self.assertEqualDiff("""\
527
507
------------------------------------------------------------
528
508
revno: 3
529
509
committer: Joe Foo <joe@foo.com>
548
528
timestamp: Mon 2005-11-21 09:24:15 -0600
549
529
message:
550
530
  simple log message
551
 
""")
 
531
""",
 
532
                             sio.getvalue())
552
533
 
553
534
    def test_author_in_log(self):
554
535
        """Log includes the author name if it's set in
565
546
                  committer='Lorem Ipsum <test@example.com>',
566
547
                  author='John Doe <jdoe@example.com>')
567
548
        sio = StringIO()
568
 
        formatter = LongLogFormatter(to_file=sio)
569
 
        show_log(b, formatter)
570
 
        self.assertEqualDiff(sio.getvalue(), '''\
 
549
        formatter = log.LongLogFormatter(to_file=sio)
 
550
        log.show_log(b, formatter)
 
551
        self.assertEqualDiff('''\
571
552
------------------------------------------------------------
572
553
revno: 1
573
554
author: John Doe <jdoe@example.com>
576
557
timestamp: Wed 2005-11-23 12:08:27 +1000
577
558
message:
578
559
  add a
579
 
''')
 
560
''',
 
561
                             sio.getvalue())
580
562
 
581
563
    def test_properties_in_log(self):
582
564
        """Log includes the custom properties returned by the registered 
593
575
                  committer='Lorem Ipsum <test@example.com>',
594
576
                  author='John Doe <jdoe@example.com>')
595
577
        sio = StringIO()
596
 
        formatter = LongLogFormatter(to_file=sio)
 
578
        formatter = log.LongLogFormatter(to_file=sio)
597
579
        try:
598
580
            def trivial_custom_prop_handler(revision):
599
581
                return {'test_prop':'test_value'}
600
 
            
 
582
 
601
583
            log.properties_handler_registry.register(
602
 
                'trivial_custom_prop_handler', 
 
584
                'trivial_custom_prop_handler',
603
585
                trivial_custom_prop_handler)
604
 
            show_log(b, formatter)
 
586
            log.show_log(b, formatter)
605
587
        finally:
606
588
            log.properties_handler_registry.remove(
607
589
                'trivial_custom_prop_handler')
608
 
            self.assertEqualDiff(sio.getvalue(), '''\
 
590
            self.assertEqualDiff('''\
609
591
------------------------------------------------------------
610
592
revno: 1
611
593
test_prop: test_value
615
597
timestamp: Wed 2005-11-23 12:08:27 +1000
616
598
message:
617
599
  add a
618
 
''')
 
600
''',
 
601
                                 sio.getvalue())
619
602
 
620
603
    def test_error_in_properties_handler(self):
621
604
        """Log includes the custom properties returned by the registered 
633
616
                  author='John Doe <jdoe@example.com>',
634
617
                  revprops={'first_prop':'first_value'})
635
618
        sio = StringIO()
636
 
        formatter = LongLogFormatter(to_file=sio)
 
619
        formatter = log.LongLogFormatter(to_file=sio)
637
620
        try:
638
621
            def trivial_custom_prop_handler(revision):
639
622
                raise StandardError("a test error")
640
 
            
 
623
 
641
624
            log.properties_handler_registry.register(
642
 
                'trivial_custom_prop_handler', 
 
625
                'trivial_custom_prop_handler',
643
626
                trivial_custom_prop_handler)
644
 
            self.assertRaises(StandardError, show_log, b, formatter,)
 
627
            self.assertRaises(StandardError, log.show_log, b, formatter,)
645
628
        finally:
646
629
            log.properties_handler_registry.remove(
647
630
                'trivial_custom_prop_handler')
648
 
                
 
631
 
649
632
    def test_properties_handler_bad_argument(self):
650
633
        wt = self.make_branch_and_tree('.')
651
634
        b = wt.branch
659
642
                  author='John Doe <jdoe@example.com>',
660
643
                  revprops={'a_prop':'test_value'})
661
644
        sio = StringIO()
662
 
        formatter = LongLogFormatter(to_file=sio)
 
645
        formatter = log.LongLogFormatter(to_file=sio)
663
646
        try:
664
647
            def bad_argument_prop_handler(revision):
665
648
                return {'custom_prop_name':revision.properties['a_prop']}
666
 
                
 
649
 
667
650
            log.properties_handler_registry.register(
668
 
                'bad_argument_prop_handler', 
 
651
                'bad_argument_prop_handler',
669
652
                bad_argument_prop_handler)
670
 
            
671
 
            self.assertRaises(AttributeError, formatter.show_properties, 
672
 
                'a revision', '')
673
 
            
 
653
 
 
654
            self.assertRaises(AttributeError, formatter.show_properties,
 
655
                              'a revision', '')
 
656
 
674
657
            revision = b.repository.get_revision(b.last_revision())
675
658
            formatter.show_properties(revision, '')
676
 
            self.assertEqualDiff(sio.getvalue(),
677
 
                '''custom_prop_name: test_value\n''')
 
659
            self.assertEqualDiff('''custom_prop_name: test_value\n''',
 
660
                                 sio.getvalue())
678
661
        finally:
679
662
            log.properties_handler_registry.remove(
680
663
                'bad_argument_prop_handler')
681
664
 
682
665
 
683
 
class TestLineLogFormatter(TestCaseWithTransport):
 
666
class TestLineLogFormatter(tests.TestCaseWithTransport):
684
667
 
685
668
    def test_line_log(self):
686
669
        """Line log should show revno
697
680
                  timezone=36000,
698
681
                  committer='Line-Log-Formatter Tester <test@line.log>')
699
682
        logfile = file('out.tmp', 'w+')
700
 
        formatter = LineLogFormatter(to_file=logfile)
701
 
        show_log(b, formatter)
 
683
        formatter = log.LineLogFormatter(to_file=logfile)
 
684
        log.show_log(b, formatter)
702
685
        logfile.flush()
703
686
        logfile.seek(0)
704
687
        log_contents = logfile.read()
705
 
        self.assertEqualDiff(log_contents,
706
 
            '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)
707
690
 
708
691
    def test_trailing_newlines(self):
709
692
        wt = self.make_branch_and_tree('.')
710
693
        b = make_commits_with_trailing_newlines(wt)
711
694
        sio = self.make_utf8_encoded_stringio()
712
 
        lf = LineLogFormatter(to_file=sio)
713
 
        show_log(b, lf)
714
 
        self.assertEqualDiff(sio.getvalue(), """\
 
695
        lf = log.LineLogFormatter(to_file=sio)
 
696
        log.show_log(b, lf)
 
697
        self.assertEqualDiff("""\
715
698
3: Joe Foo 2005-11-21 single line with trailing newline
716
699
2: Joe Bar 2005-11-21 multiline
717
700
1: Joe Foo 2005-11-21 simple log message
718
 
""")
 
701
""",
 
702
                             sio.getvalue())
719
703
 
720
704
    def test_line_log_single_merge_revision(self):
721
705
        wt = self.make_branch_and_memory_tree('.')
722
706
        wt.lock_write()
723
 
        try:
724
 
            wt.add('')
725
 
            wt.commit('rev-1', rev_id='rev-1',
726
 
                      timestamp=1132586655, timezone=36000,
727
 
                      committer='Joe Foo <joe@foo.com>')
728
 
            wt.commit('rev-merged', rev_id='rev-2a',
729
 
                      timestamp=1132586700, timezone=36000,
730
 
                      committer='Joe Foo <joe@foo.com>')
731
 
            wt.set_parent_ids(['rev-1', 'rev-2a'])
732
 
            wt.branch.set_last_revision_info(1, 'rev-1')
733
 
            wt.commit('rev-2', rev_id='rev-2b',
734
 
                      timestamp=1132586800, timezone=36000,
735
 
                      committer='Joe Foo <joe@foo.com>')
736
 
            logfile = self.make_utf8_encoded_stringio()
737
 
            formatter = LineLogFormatter(to_file=logfile)
738
 
            revspec = RevisionSpec.from_string('1.1.1')
739
 
            wtb = wt.branch
740
 
            rev = revspec.in_history(wtb)
741
 
            show_log(wtb, formatter, start_revision=rev, end_revision=rev)
742
 
            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("""\
743
727
1.1.1: Joe Foo 2005-11-22 rev-merged
744
 
""")
745
 
        finally:
746
 
            wt.unlock()
747
 
 
748
 
 
749
 
 
750
 
class TestGetViewRevisions(TestCaseWithTransport):
 
728
""",
 
729
                             logfile.getvalue())
 
730
 
 
731
 
 
732
 
 
733
class TestGetViewRevisions(tests.TestCaseWithTransport):
751
734
 
752
735
    def make_tree_with_commits(self):
753
736
        """Create a tree with well-known revision ids"""
774
757
    def make_tree_with_many_merges(self):
775
758
        """Create a tree with well-known revision ids"""
776
759
        wt = self.make_branch_and_tree('tree1')
 
760
        self.build_tree_contents([('tree1/f', '1\n')])
 
761
        wt.add(['f'], ['f-id'])
777
762
        wt.commit('commit one', rev_id='1')
778
763
        wt.commit('commit two', rev_id='2')
 
764
 
779
765
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
 
766
        self.build_tree_contents([('tree3/f', '1\n2\n3a\n')])
780
767
        tree3.commit('commit three a', rev_id='3a')
 
768
 
781
769
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
782
770
        tree2.merge_from_branch(tree3.branch)
783
771
        tree2.commit('commit three b', rev_id='3b')
 
772
 
784
773
        wt.merge_from_branch(tree2.branch)
785
774
        wt.commit('commit three c', rev_id='3c')
786
775
        tree2.commit('four-a', rev_id='4a')
 
776
 
787
777
        wt.merge_from_branch(tree2.branch)
788
778
        wt.commit('four-b', rev_id='4b')
 
779
 
789
780
        mainline_revs = [None, '1', '2', '3c', '4b']
790
781
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
791
782
        full_rev_nos_for_reference = {
804
795
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
805
796
        wt.lock_read()
806
797
        self.addCleanup(wt.unlock)
807
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
808
 
                                            'forward'))
 
798
        revisions = list(log.get_view_revisions(
 
799
                mainline_revs, rev_nos, wt.branch, 'forward'))
809
800
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
810
 
            revisions)
811
 
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
812
 
                                             '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))
813
805
        self.assertEqual(revisions, revisions2)
814
806
 
815
807
    def test_get_view_revisions_reverse(self):
817
809
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
818
810
        wt.lock_read()
819
811
        self.addCleanup(wt.unlock)
820
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
821
 
                                            'reverse'))
 
812
        revisions = list(log.get_view_revisions(
 
813
                mainline_revs, rev_nos, wt.branch, 'reverse'))
822
814
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
823
 
            revisions)
824
 
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
825
 
                                             '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))
826
819
        self.assertEqual(revisions, revisions2)
827
820
 
828
821
    def test_get_view_revisions_merge(self):
830
823
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
831
824
        wt.lock_read()
832
825
        self.addCleanup(wt.unlock)
833
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
834
 
                                            'forward'))
835
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
836
 
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
837
 
            revisions)
838
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
839
 
                                             'forward', include_merges=False))
840
 
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
841
 
            ('4b', '4', 0)],
842
 
            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)
843
837
 
844
838
    def test_get_view_revisions_merge_reverse(self):
845
839
        """Test get_view_revisions in reverse when there are merges"""
846
840
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
847
841
        wt.lock_read()
848
842
        self.addCleanup(wt.unlock)
849
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
850
 
                                            'reverse'))
 
843
        revisions = list(log.get_view_revisions(
 
844
                mainline_revs, rev_nos, wt.branch, 'reverse'))
851
845
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
852
 
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
853
 
            revisions)
854
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
855
 
                                             '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))
856
851
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
857
 
            ('1', '1', 0)],
858
 
            revisions)
 
852
                          ('1', '1', 0)],
 
853
                         revisions)
859
854
 
860
855
    def test_get_view_revisions_merge2(self):
861
856
        """Test get_view_revisions when there are merges"""
862
857
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
863
858
        wt.lock_read()
864
859
        self.addCleanup(wt.unlock)
865
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
866
 
                                            'forward'))
 
860
        revisions = list(log.get_view_revisions(
 
861
                mainline_revs, rev_nos, wt.branch, 'forward'))
867
862
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
868
 
            ('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
869
 
            ('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)]
870
865
        self.assertEqual(expected, revisions)
871
 
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
872
 
                                             'forward', include_merges=False))
 
866
        revisions = list(log.get_view_revisions(
 
867
                mainline_revs, rev_nos, wt.branch, 'forward',
 
868
                include_merges=False))
873
869
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
874
 
            ('4b', '4', 0)],
875
 
            revisions)
876
 
 
877
 
 
878
 
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):
879
908
 
880
909
    def create_tree_with_single_merge(self):
881
910
        """Create a branch with a moderate layout.
931
960
        tree.commit('D', rev_id='D')
932
961
 
933
962
        # Switch to a read lock for this tree.
934
 
        # We still have addCleanup(unlock)
 
963
        # We still have an addCleanup(tree.unlock) pending
935
964
        tree.unlock()
936
965
        tree.lock_read()
937
966
        return tree
938
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
 
939
981
    def test_tree_with_single_merge(self):
940
982
        """Make sure the tree layout is correct."""
941
983
        tree = self.create_tree_with_single_merge()
942
984
        rev_A_tree = tree.branch.repository.revision_tree('A')
943
985
        rev_B_tree = tree.branch.repository.revision_tree('B')
944
 
 
945
 
        f1_changed = (u'f1', 'f1-id', 'file', True, False)
946
 
        f2_changed = (u'f2', 'f2-id', 'file', True, False)
947
 
        f3_changed = (u'f3', 'f3-id', 'file', True, False)
948
 
 
949
 
        delta = rev_B_tree.changes_from(rev_A_tree)
950
 
        self.assertEqual([f1_changed, f3_changed], delta.modified)
951
 
        self.assertEqual([], delta.renamed)
952
 
        self.assertEqual([], delta.added)
953
 
        self.assertEqual([], delta.removed)
954
 
 
955
986
        rev_C_tree = tree.branch.repository.revision_tree('C')
956
 
        delta = rev_C_tree.changes_from(rev_A_tree)
957
 
        self.assertEqual([f2_changed, f3_changed], delta.modified)
958
 
        self.assertEqual([], delta.renamed)
959
 
        self.assertEqual([], delta.added)
960
 
        self.assertEqual([], delta.removed)
961
 
 
962
987
        rev_D_tree = tree.branch.repository.revision_tree('D')
963
 
        delta = rev_D_tree.changes_from(rev_B_tree)
964
 
        self.assertEqual([f2_changed, f3_changed], delta.modified)
965
 
        self.assertEqual([], delta.renamed)
966
 
        self.assertEqual([], delta.added)
967
 
        self.assertEqual([], delta.removed)
968
 
 
969
 
        delta = rev_D_tree.changes_from(rev_C_tree)
970
 
        self.assertEqual([f1_changed, f3_changed], delta.modified)
971
 
        self.assertEqual([], delta.renamed)
972
 
        self.assertEqual([], delta.added)
973
 
        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'])
974
1000
 
975
1001
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
976
 
        """Make sure _filter_revisions_touching_file_id returns the right values.
 
1002
        """Ensure _filter_revisions_touching_file_id returns the right values.
977
1003
 
978
1004
        Get the return value from _filter_revisions_touching_file_id and make
979
1005
        sure they are correct.
980
1006
        """
981
 
        # 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.
982
1008
        # So we do the setup here.
983
1009
        mainline = tree.branch.revision_history()
984
1010
        mainline.insert(0, None)
988
1014
        actual_revs = log._filter_revisions_touching_file_id(
989
1015
                            tree.branch,
990
1016
                            file_id,
991
 
                            list(view_revs_iter),
992
 
                            'reverse')
 
1017
                            list(view_revs_iter))
993
1018
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
994
1019
 
995
1020
    def test_file_id_f1(self):
1006
1031
    def test_file_id_f3(self):
1007
1032
        tree = self.create_tree_with_single_merge()
1008
1033
        # f3 should be marked as modified by revisions A, B, C, and D
1009
 
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
1034
        self.assertAllRevisionsForFileID(tree, 'f3-id', ['D', 'C', 'B', 'A'])
1010
1035
 
1011
1036
    def test_file_id_with_ghosts(self):
1012
1037
        # This is testing bug #209948, where having a ghost would cause
1013
1038
        # _filter_revisions_touching_file_id() to fail.
1014
1039
        tree = self.create_tree_with_single_merge()
1015
1040
        # We need to add a revision, so switch back to a write-locked tree
 
1041
        # (still a single addCleanup(tree.unlock) pending).
1016
1042
        tree.unlock()
1017
1043
        tree.lock_write()
1018
1044
        first_parent = tree.last_revision()
1023
1049
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
1024
1050
 
1025
1051
 
1026
 
class TestShowChangedRevisions(TestCaseWithTransport):
 
1052
class TestShowChangedRevisions(tests.TestCaseWithTransport):
1027
1053
 
1028
1054
    def test_show_changed_revisions_verbose(self):
1029
1055
        tree = self.make_branch_and_tree('tree_a')
1036
1062
        self.assertNotContainsRe(s.getvalue(), 'foo')
1037
1063
 
1038
1064
 
1039
 
class TestLogFormatter(TestCase):
 
1065
class TestLogFormatter(tests.TestCase):
1040
1066
 
1041
1067
    def test_short_committer(self):
1042
 
        rev = Revision('a-id')
 
1068
        rev = revision.Revision('a-id')
1043
1069
        rev.committer = 'John Doe <jdoe@example.com>'
1044
 
        lf = LogFormatter(None)
 
1070
        lf = log.LogFormatter(None)
1045
1071
        self.assertEqual('John Doe', lf.short_committer(rev))
1046
1072
        rev.committer = 'John Smith <jsmith@example.com>'
1047
1073
        self.assertEqual('John Smith', lf.short_committer(rev))
1055
1081
        self.assertEqual('John Smith', lf.short_committer(rev))
1056
1082
 
1057
1083
    def test_short_author(self):
1058
 
        rev = Revision('a-id')
 
1084
        rev = revision.Revision('a-id')
1059
1085
        rev.committer = 'John Doe <jdoe@example.com>'
1060
 
        lf = LogFormatter(None)
 
1086
        lf = log.LogFormatter(None)
1061
1087
        self.assertEqual('John Doe', lf.short_author(rev))
1062
1088
        rev.properties['author'] = 'John Smith <jsmith@example.com>'
1063
1089
        self.assertEqual('John Smith', lf.short_author(rev))
1069
1095
        self.assertEqual('jsmith@example.com', lf.short_author(rev))
1070
1096
        rev.properties['author'] = 'John Smith jsmith@example.com'
1071
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),])