13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
from cStringIO import StringIO
20
from bzrlib import log
21
from bzrlib.tests import TestCase, TestCaseWithTransport
22
from bzrlib.log import (show_log,
29
from bzrlib.branch import Branch
30
from bzrlib.errors import InvalidRevisionNumber
31
from bzrlib.revision import Revision
34
class LogCatcher(LogFormatter):
35
"""Pull log messages into list rather than displaying them.
37
For ease of testing we save log messages here rather than actually
38
formatting them, so that we can precisely check the result without
39
being too dependent on the exact formatting.
41
We should also test the LogFormatter.
33
class TestLogMixin(object):
35
def wt_commit(self, wt, message, **kwargs):
36
"""Use some mostly fixed values for commits to simplify tests.
38
Tests can use this function to get some commit attributes. The time
39
stamp is incremented at each commit.
41
if getattr(self, 'timestamp', None) is None:
42
self.timestamp = 1132617600 # Mon 2005-11-22 00:00:00 +0000
44
self.timestamp += 1 # 1 second between each commit
45
kwargs.setdefault('timestamp', self.timestamp)
46
kwargs.setdefault('timezone', 0) # UTC
47
kwargs.setdefault('committer', 'Joe Foo <joe@foo.com>')
49
return wt.commit(message, **kwargs)
52
class TestCaseForLogFormatter(tests.TestCaseWithTransport, TestLogMixin):
55
super(TestCaseForLogFormatter, self).setUp()
56
# keep a reference to the "current" custom prop. handler registry
57
self.properties_handler_registry = log.properties_handler_registry
58
# Use a clean registry for log
59
log.properties_handler_registry = registry.Registry()
62
log.properties_handler_registry = self.properties_handler_registry
63
self.addCleanup(restore)
65
def assertFormatterResult(self, result, branch, formatter_class,
66
formatter_kwargs=None, show_log_kwargs=None):
67
logfile = self.make_utf8_encoded_stringio()
68
if formatter_kwargs is None:
70
formatter = formatter_class(to_file=logfile, **formatter_kwargs)
71
if show_log_kwargs is None:
73
log.show_log(branch, formatter, **show_log_kwargs)
74
self.assertEqualDiff(result, logfile.getvalue())
76
def make_standard_commit(self, branch_nick, **kwargs):
77
wt = self.make_branch_and_tree('.')
79
self.addCleanup(wt.unlock)
80
self.build_tree(['a'])
82
wt.branch.nick = branch_nick
83
kwargs.setdefault('committer', 'Lorem Ipsum <test@example.com>')
84
kwargs.setdefault('authors', ['John Doe <jdoe@example.com>'])
85
self.wt_commit(wt, 'add a', **kwargs)
88
def make_commits_with_trailing_newlines(self, wt):
89
"""Helper method for LogFormatter tests"""
92
self.build_tree_contents([('a', 'hello moto\n')])
93
self.wt_commit(wt, 'simple log message', rev_id='a1')
94
self.build_tree_contents([('b', 'goodbye\n')])
96
self.wt_commit(wt, 'multiline\nlog\nmessage\n', rev_id='a2')
98
self.build_tree_contents([('c', 'just another manic monday\n')])
100
self.wt_commit(wt, 'single line with trailing newline\n', rev_id='a3')
103
def _prepare_tree_with_merges(self, with_tags=False):
104
wt = self.make_branch_and_memory_tree('.')
106
self.addCleanup(wt.unlock)
108
self.wt_commit(wt, 'rev-1', rev_id='rev-1')
109
self.wt_commit(wt, 'rev-merged', rev_id='rev-2a')
110
wt.set_parent_ids(['rev-1', 'rev-2a'])
111
wt.branch.set_last_revision_info(1, 'rev-1')
112
self.wt_commit(wt, 'rev-2', rev_id='rev-2b')
115
branch.tags.set_tag('v0.2', 'rev-2b')
116
self.wt_commit(wt, 'rev-3', rev_id='rev-3')
117
branch.tags.set_tag('v1.0rc1', 'rev-3')
118
branch.tags.set_tag('v1.0', 'rev-3')
122
class LogCatcher(log.LogFormatter):
123
"""Pull log messages into a list rather than displaying them.
125
To simplify testing we save logged revisions here rather than actually
126
formatting anything, so that we can precisely check the result without
127
being dependent on the formatting.
130
supports_merge_revisions = True
44
131
supports_delta = True
47
super(LogCatcher, self).__init__(to_file=None)
135
def __init__(self, *args, **kwargs):
136
kwargs.update(dict(to_file=None))
137
super(LogCatcher, self).__init__(*args, **kwargs)
50
140
def log_revision(self, revision):
51
self.logs.append(revision)
54
class TestShowLog(TestCaseWithTransport):
141
self.revisions.append(revision)
144
class TestShowLog(tests.TestCaseWithTransport):
56
146
def checkDelta(self, delta, **kw):
57
"""Check the filenames touched by a delta are as expected."""
147
"""Check the filenames touched by a delta are as expected.
149
Caller only have to pass in the list of files for each part, all
150
unspecified parts are considered empty (and checked as such).
58
152
for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
153
# By default we expect an empty list
59
154
expected = kw.get(n, [])
60
155
# strip out only the path components
61
156
got = [x[0] for x in getattr(delta, n)]
62
self.assertEquals(expected, got)
157
self.assertEqual(expected, got)
159
def assertInvalidRevisonNumber(self, br, start, end):
161
self.assertRaises(errors.InvalidRevisionNumber,
162
log.show_log, br, lf,
163
start_revision=start, end_revision=end)
64
165
def test_cur_revno(self):
65
166
wt = self.make_branch_and_tree('.')
69
170
wt.commit('empty commit')
70
show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
71
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
72
start_revision=2, end_revision=1)
73
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
74
start_revision=1, end_revision=2)
75
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
76
start_revision=0, end_revision=2)
77
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
78
start_revision=1, end_revision=0)
79
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
80
start_revision=-1, end_revision=1)
81
self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
82
start_revision=1, end_revision=-1)
84
def test_simple_log(self):
85
eq = self.assertEquals
171
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
173
# Since there is a single revision in the branch all the combinations
175
self.assertInvalidRevisonNumber(b, 2, 1)
176
self.assertInvalidRevisonNumber(b, 1, 2)
177
self.assertInvalidRevisonNumber(b, 0, 2)
178
self.assertInvalidRevisonNumber(b, 1, 0)
179
self.assertInvalidRevisonNumber(b, -1, 1)
180
self.assertInvalidRevisonNumber(b, 1, -1)
182
def test_empty_branch(self):
87
183
wt = self.make_branch_and_tree('.')
186
log.show_log(wt.branch, lf)
188
self.assertEqual([], lf.revisions)
190
def test_empty_commit(self):
191
wt = self.make_branch_and_tree('.')
95
193
wt.commit('empty commit')
97
show_log(b, lf, verbose=True)
99
eq(lf.logs[0].revno, '1')
100
eq(lf.logs[0].rev.message, 'empty commit')
102
self.log('log delta: %r' % d)
195
log.show_log(wt.branch, lf, verbose=True)
197
self.assertEqual(1, len(revs))
198
self.assertEqual('1', revs[0].revno)
199
self.assertEqual('empty commit', revs[0].rev.message)
200
self.checkDelta(revs[0].delta)
202
def test_simple_commit(self):
203
wt = self.make_branch_and_tree('.')
204
wt.commit('empty commit')
105
205
self.build_tree(['hello'])
107
207
wt.commit('add one file',
108
208
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
109
209
u'<test@example.com>')
111
lf = self.make_utf8_encoded_stringio()
112
# log using regular thing
113
show_log(b, LongLogFormatter(lf))
115
for l in lf.readlines():
118
# get log as data structure
119
210
lf = LogCatcher()
120
show_log(b, lf, verbose=True)
122
self.log('log entries:')
123
for logentry in lf.logs:
124
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
211
log.show_log(wt.branch, lf, verbose=True)
212
self.assertEqual(2, len(lf.revisions))
126
213
# first one is most recent
127
logentry = lf.logs[0]
128
eq(logentry.revno, '2')
129
eq(logentry.rev.message, 'add one file')
131
self.log('log 2 delta: %r' % d)
132
self.checkDelta(d, added=['hello'])
134
# commit a log message with control characters
135
msg = "All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
136
self.log("original commit message: %r", msg)
214
log_entry = lf.revisions[0]
215
self.assertEqual('2', log_entry.revno)
216
self.assertEqual('add one file', log_entry.rev.message)
217
self.checkDelta(log_entry.delta, added=['hello'])
219
def test_commit_message_with_control_chars(self):
220
wt = self.make_branch_and_tree('.')
221
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
222
msg = msg.replace(u'\r', u'\n')
138
224
lf = LogCatcher()
139
show_log(b, lf, verbose=True)
140
committed_msg = lf.logs[0].rev.message
141
self.log("escaped commit message: %r", committed_msg)
142
self.assert_(msg != committed_msg)
143
self.assert_(len(committed_msg) > len(msg))
225
log.show_log(wt.branch, lf, verbose=True)
226
committed_msg = lf.revisions[0].rev.message
227
if wt.branch.repository._serializer.squashes_xml_invalid_characters:
228
self.assertNotEqual(msg, committed_msg)
229
self.assertTrue(len(committed_msg) > len(msg))
231
self.assertEqual(msg, committed_msg)
145
# Check that log message with only XML-valid characters isn't
233
def test_commit_message_without_control_chars(self):
234
wt = self.make_branch_and_tree('.')
146
235
# escaped. As ElementTree apparently does some kind of
147
236
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
148
237
# included in the test commit message, even though they are
149
238
# valid XML 1.0 characters.
150
239
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
151
self.log("original commit message: %r", msg)
153
241
lf = LogCatcher()
154
show_log(b, lf, verbose=True)
155
committed_msg = lf.logs[0].rev.message
156
self.log("escaped commit message: %r", committed_msg)
157
self.assert_(msg == committed_msg)
242
log.show_log(wt.branch, lf, verbose=True)
243
committed_msg = lf.revisions[0].rev.message
244
self.assertEqual(msg, committed_msg)
159
246
def test_deltas_in_merge_revisions(self):
160
247
"""Check deltas created for both mainline and merge revisions"""
161
eq = self.assertEquals
162
248
wt = self.make_branch_and_tree('parent')
163
249
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
177
263
lf = LogCatcher()
178
264
lf.supports_merge_revisions = True
179
show_log(b, lf, verbose=True)
181
logentry = lf.logs[0]
182
eq(logentry.revno, '2')
183
eq(logentry.rev.message, 'merge child branch')
185
self.checkDelta(d, removed=['file1'], modified=['file2'])
186
logentry = lf.logs[1]
187
eq(logentry.revno, '1.1.1')
188
eq(logentry.rev.message, 'remove file1 and modify file2')
190
self.checkDelta(d, removed=['file1'], modified=['file2'])
191
logentry = lf.logs[2]
192
eq(logentry.revno, '1')
193
eq(logentry.rev.message, 'add file1 and file2')
195
self.checkDelta(d, added=['file1', 'file2'])
198
def make_commits_with_trailing_newlines(wt):
199
"""Helper method for LogFormatter tests"""
202
open('a', 'wb').write('hello moto\n')
204
wt.commit('simple log message', rev_id='a1',
205
timestamp=1132586655.459960938, timezone=-6*3600,
206
committer='Joe Foo <joe@foo.com>')
207
open('b', 'wb').write('goodbye\n')
209
wt.commit('multiline\nlog\nmessage\n', rev_id='a2',
210
timestamp=1132586842.411175966, timezone=-6*3600,
211
committer='Joe Foo <joe@foo.com>',
212
author='Joe Bar <joe@bar.com>')
214
open('c', 'wb').write('just another manic monday\n')
216
wt.commit('single line with trailing newline\n', rev_id='a3',
217
timestamp=1132587176.835228920, timezone=-6*3600,
218
committer = 'Joe Foo <joe@foo.com>')
222
def normalize_log(log):
223
"""Replaces the variable lines of logs with fixed lines"""
224
author = 'author: Dolor Sit <test@example.com>'
225
committer = 'committer: Lorem Ipsum <test@example.com>'
226
lines = log.splitlines(True)
227
for idx,line in enumerate(lines):
228
stripped_line = line.lstrip()
229
indent = ' ' * (len(line) - len(stripped_line))
230
if stripped_line.startswith('author:'):
231
lines[idx] = indent + author + '\n'
232
elif stripped_line.startswith('committer:'):
233
lines[idx] = indent + committer + '\n'
234
elif stripped_line.startswith('timestamp:'):
235
lines[idx] = indent + 'timestamp: Just now\n'
236
return ''.join(lines)
239
class TestShortLogFormatter(TestCaseWithTransport):
265
log.show_log(b, lf, verbose=True)
268
self.assertEqual(3, len(revs))
271
self.assertEqual('2', logentry.revno)
272
self.assertEqual('merge child branch', logentry.rev.message)
273
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
276
self.assertEqual('1.1.1', logentry.revno)
277
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
278
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
281
self.assertEqual('1', logentry.revno)
282
self.assertEqual('add file1 and file2', logentry.rev.message)
283
self.checkDelta(logentry.delta, added=['file1', 'file2'])
286
class TestFormatSignatureValidity(tests.TestCaseWithTransport):
287
class UTFLoopbackGPGStrategy(gpg.LoopbackGPGStrategy):
288
def verify(self, content, testament):
289
return (gpg.SIGNATURE_VALID,
290
u'UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>')
292
def has_signature_for_revision_id(self, revision_id):
295
def get_signature_text(self, revision_id):
298
def test_format_signature_validity_utf(self):
299
"""Check that GPG signatures containing UTF-8 names are formatted
301
# Monkey patch to use our UTF-8 generating GPGStrategy
302
self.overrideAttr(gpg, 'GPGStrategy', self.UTFLoopbackGPGStrategy)
303
wt = self.make_branch_and_tree('.')
304
revid = wt.commit('empty commit')
305
repo = wt.branch.repository
306
# Monkey patch out checking if this rev is actually signed, since we
307
# can't sign it without a heavier TestCase and LoopbackGPGStrategy
308
# doesn't care anyways.
309
self.overrideAttr(repo, 'has_signature_for_revision_id',
310
self.has_signature_for_revision_id)
311
self.overrideAttr(repo, 'get_signature_text', self.get_signature_text)
312
out = log.format_signature_validity(revid, repo)
314
u'valid signature from UTF8 Test \xa1\xb1\xc1\xd1\xe1\xf1 <jrandom@example.com>',
318
class TestShortLogFormatter(TestCaseForLogFormatter):
241
320
def test_trailing_newlines(self):
242
321
wt = self.make_branch_and_tree('.')
243
b = make_commits_with_trailing_newlines(wt)
244
sio = self.make_utf8_encoded_stringio()
245
lf = ShortLogFormatter(to_file=sio)
247
self.assertEqualDiff(sio.getvalue(), """\
248
3 Joe Foo\t2005-11-21
322
b = self.make_commits_with_trailing_newlines(wt)
323
self.assertFormatterResult("""\
324
3 Joe Foo\t2005-11-22
249
325
single line with trailing newline
251
2 Joe Bar\t2005-11-21
327
2 Joe Foo\t2005-11-22
256
1 Joe Foo\t2005-11-21
332
1 Joe Foo\t2005-11-22
257
333
simple log message
262
class TestLongLogFormatter(TestCaseWithTransport):
336
b, log.ShortLogFormatter)
338
def test_short_log_with_merges(self):
339
wt = self._prepare_tree_with_merges()
340
self.assertFormatterResult("""\
341
2 Joe Foo\t2005-11-22 [merge]
344
1 Joe Foo\t2005-11-22
348
wt.branch, log.ShortLogFormatter)
350
def test_short_log_with_merges_and_advice(self):
351
wt = self._prepare_tree_with_merges()
352
self.assertFormatterResult("""\
353
2 Joe Foo\t2005-11-22 [merge]
356
1 Joe Foo\t2005-11-22
359
Use --include-merged or -n0 to see merged revisions.
361
wt.branch, log.ShortLogFormatter,
362
formatter_kwargs=dict(show_advice=True))
364
def test_short_log_with_merges_and_range(self):
365
wt = self._prepare_tree_with_merges()
366
self.wt_commit(wt, 'rev-3a', rev_id='rev-3a')
367
wt.branch.set_last_revision_info(2, 'rev-2b')
368
wt.set_parent_ids(['rev-2b', 'rev-3a'])
369
self.wt_commit(wt, 'rev-3b', rev_id='rev-3b')
370
self.assertFormatterResult("""\
371
3 Joe Foo\t2005-11-22 [merge]
374
2 Joe Foo\t2005-11-22 [merge]
378
wt.branch, log.ShortLogFormatter,
379
show_log_kwargs=dict(start_revision=2, end_revision=3))
381
def test_short_log_with_tags(self):
382
wt = self._prepare_tree_with_merges(with_tags=True)
383
self.assertFormatterResult("""\
384
3 Joe Foo\t2005-11-22 {v1.0, v1.0rc1}
387
2 Joe Foo\t2005-11-22 {v0.2} [merge]
390
1 Joe Foo\t2005-11-22
394
wt.branch, log.ShortLogFormatter)
396
def test_short_log_single_merge_revision(self):
397
wt = self._prepare_tree_with_merges()
398
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
399
rev = revspec.in_history(wt.branch)
400
self.assertFormatterResult("""\
401
1.1.1 Joe Foo\t2005-11-22
405
wt.branch, log.ShortLogFormatter,
406
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
408
def test_show_ids(self):
409
wt = self.make_branch_and_tree('parent')
410
self.build_tree(['parent/f1', 'parent/f2'])
412
self.wt_commit(wt, 'first post', rev_id='a')
413
child_wt = wt.bzrdir.sprout('child').open_workingtree()
414
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
415
wt.merge_from_branch(child_wt.branch)
416
self.wt_commit(wt, 'merge branch 1', rev_id='c')
417
self.assertFormatterResult("""\
418
2 Joe Foo\t2005-11-22 [merge]
422
1.1.1 Joe Foo\t2005-11-22
426
1 Joe Foo\t2005-11-22
431
wt.branch, log.ShortLogFormatter,
432
formatter_kwargs=dict(levels=0,show_ids=True))
435
class TestShortLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
437
def test_short_merge_revs_log_with_merges(self):
438
wt = self._prepare_tree_with_merges()
439
# Note that the 1.1.1 indenting is in fact correct given that
440
# the revision numbers are right justified within 5 characters
441
# for mainline revnos and 9 characters for dotted revnos.
442
self.assertFormatterResult("""\
443
2 Joe Foo\t2005-11-22 [merge]
446
1.1.1 Joe Foo\t2005-11-22
449
1 Joe Foo\t2005-11-22
453
wt.branch, log.ShortLogFormatter,
454
formatter_kwargs=dict(levels=0))
456
def test_short_merge_revs_log_single_merge_revision(self):
457
wt = self._prepare_tree_with_merges()
458
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
459
rev = revspec.in_history(wt.branch)
460
self.assertFormatterResult("""\
461
1.1.1 Joe Foo\t2005-11-22
465
wt.branch, log.ShortLogFormatter,
466
formatter_kwargs=dict(levels=0),
467
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
470
class TestLongLogFormatter(TestCaseForLogFormatter):
264
472
def test_verbose_log(self):
265
473
"""Verbose log includes changed files
269
wt = self.make_branch_and_tree('.')
271
self.build_tree(['a'])
273
# XXX: why does a longer nick show up?
274
b.nick = 'test_verbose_log'
275
wt.commit(message='add a',
276
timestamp=1132711707,
278
committer='Lorem Ipsum <test@example.com>')
279
logfile = file('out.tmp', 'w+')
280
formatter = LongLogFormatter(to_file=logfile)
281
show_log(b, formatter, verbose=True)
284
log_contents = logfile.read()
285
self.assertEqualDiff(log_contents, '''\
477
wt = self.make_standard_commit('test_verbose_log', authors=[])
478
self.assertFormatterResult('''\
286
479
------------------------------------------------------------
288
481
committer: Lorem Ipsum <test@example.com>
289
482
branch nick: test_verbose_log
290
timestamp: Wed 2005-11-23 12:08:27 +1000
483
timestamp: Tue 2005-11-22 00:00:00 +0000
489
wt.branch, log.LongLogFormatter,
490
show_log_kwargs=dict(verbose=True))
297
492
def test_merges_are_indented_by_level(self):
298
493
wt = self.make_branch_and_tree('parent')
299
wt.commit('first post')
300
self.run_bzr('branch parent child')
301
self.run_bzr(['commit', '-m', 'branch 1', '--unchanged', 'child'])
302
self.run_bzr('branch child smallerchild')
303
self.run_bzr(['commit', '-m', 'branch 2', '--unchanged',
306
self.run_bzr('merge ../smallerchild')
307
self.run_bzr(['commit', '-m', 'merge branch 2'])
308
os.chdir('../parent')
309
self.run_bzr('merge ../child')
310
wt.commit('merge branch 1')
312
sio = self.make_utf8_encoded_stringio()
313
lf = LongLogFormatter(to_file=sio)
314
show_log(b, lf, verbose=True)
315
log = normalize_log(sio.getvalue())
316
self.assertEqualDiff(log, """\
494
self.wt_commit(wt, 'first post')
495
child_wt = wt.bzrdir.sprout('child').open_workingtree()
496
self.wt_commit(child_wt, 'branch 1')
497
smallerchild_wt = wt.bzrdir.sprout('smallerchild').open_workingtree()
498
self.wt_commit(smallerchild_wt, 'branch 2')
499
child_wt.merge_from_branch(smallerchild_wt.branch)
500
self.wt_commit(child_wt, 'merge branch 2')
501
wt.merge_from_branch(child_wt.branch)
502
self.wt_commit(wt, 'merge branch 1')
503
self.assertFormatterResult("""\
317
504
------------------------------------------------------------
319
committer: Lorem Ipsum <test@example.com>
506
committer: Joe Foo <joe@foo.com>
320
507
branch nick: parent
508
timestamp: Tue 2005-11-22 00:00:04 +0000
324
511
------------------------------------------------------------
326
committer: Lorem Ipsum <test@example.com>
513
committer: Joe Foo <joe@foo.com>
327
514
branch nick: child
515
timestamp: Tue 2005-11-22 00:00:03 +0000
331
518
------------------------------------------------------------
333
committer: Lorem Ipsum <test@example.com>
520
committer: Joe Foo <joe@foo.com>
334
521
branch nick: smallerchild
522
timestamp: Tue 2005-11-22 00:00:02 +0000
338
525
------------------------------------------------------------
340
committer: Lorem Ipsum <test@example.com>
527
committer: Joe Foo <joe@foo.com>
341
528
branch nick: child
529
timestamp: Tue 2005-11-22 00:00:01 +0000
345
532
------------------------------------------------------------
347
committer: Lorem Ipsum <test@example.com>
534
committer: Joe Foo <joe@foo.com>
348
535
branch nick: parent
536
timestamp: Tue 2005-11-22 00:00:00 +0000
540
wt.branch, log.LongLogFormatter,
541
formatter_kwargs=dict(levels=0),
542
show_log_kwargs=dict(verbose=True))
354
544
def test_verbose_merge_revisions_contain_deltas(self):
355
545
wt = self.make_branch_and_tree('parent')
356
546
self.build_tree(['parent/f1', 'parent/f2'])
357
547
wt.add(['f1','f2'])
358
wt.commit('first post')
359
self.run_bzr('branch parent child')
548
self.wt_commit(wt, 'first post')
549
child_wt = wt.bzrdir.sprout('child').open_workingtree()
360
550
os.unlink('child/f1')
361
file('child/f2', 'wb').write('hello\n')
362
self.run_bzr(['commit', '-m', 'removed f1 and modified f2',
365
self.run_bzr('merge ../child')
366
wt.commit('merge branch 1')
368
sio = self.make_utf8_encoded_stringio()
369
lf = LongLogFormatter(to_file=sio)
370
show_log(b, lf, verbose=True)
371
log = normalize_log(sio.getvalue())
372
self.assertEqualDiff(log, """\
551
self.build_tree_contents([('child/f2', 'hello\n')])
552
self.wt_commit(child_wt, 'removed f1 and modified f2')
553
wt.merge_from_branch(child_wt.branch)
554
self.wt_commit(wt, 'merge branch 1')
555
self.assertFormatterResult("""\
373
556
------------------------------------------------------------
375
committer: Lorem Ipsum <test@example.com>
558
committer: Joe Foo <joe@foo.com>
376
559
branch nick: parent
560
timestamp: Tue 2005-11-22 00:00:02 +0000
433
615
committer: Joe Foo <joe@foo.com>
434
616
branch nick: test
435
timestamp: Mon 2005-11-21 09:24:15 -0600
617
timestamp: Tue 2005-11-22 00:00:00 +0000
437
619
simple log message
621
b, log.LongLogFormatter)
440
623
def test_author_in_log(self):
441
624
"""Log includes the author name if it's set in
442
625
the revision properties
627
wt = self.make_standard_commit('test_author_log',
628
authors=['John Doe <jdoe@example.com>',
629
'Jane Rey <jrey@example.com>'])
630
self.assertFormatterResult("""\
631
------------------------------------------------------------
633
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
634
committer: Lorem Ipsum <test@example.com>
635
branch nick: test_author_log
636
timestamp: Tue 2005-11-22 00:00:00 +0000
640
wt.branch, log.LongLogFormatter)
642
def test_properties_in_log(self):
643
"""Log includes the custom properties returned by the registered
646
wt = self.make_standard_commit('test_properties_in_log')
647
def trivial_custom_prop_handler(revision):
648
return {'test_prop':'test_value'}
650
# Cleaned up in setUp()
651
log.properties_handler_registry.register(
652
'trivial_custom_prop_handler',
653
trivial_custom_prop_handler)
654
self.assertFormatterResult("""\
655
------------------------------------------------------------
657
test_prop: test_value
658
author: John Doe <jdoe@example.com>
659
committer: Lorem Ipsum <test@example.com>
660
branch nick: test_properties_in_log
661
timestamp: Tue 2005-11-22 00:00:00 +0000
665
wt.branch, log.LongLogFormatter)
667
def test_properties_in_short_log(self):
668
"""Log includes the custom properties returned by the registered
671
wt = self.make_standard_commit('test_properties_in_short_log')
672
def trivial_custom_prop_handler(revision):
673
return {'test_prop':'test_value'}
675
log.properties_handler_registry.register(
676
'trivial_custom_prop_handler',
677
trivial_custom_prop_handler)
678
self.assertFormatterResult("""\
679
1 John Doe\t2005-11-22
680
test_prop: test_value
684
wt.branch, log.ShortLogFormatter)
686
def test_error_in_properties_handler(self):
687
"""Log includes the custom properties returned by the registered
690
wt = self.make_standard_commit('error_in_properties_handler',
691
revprops={'first_prop':'first_value'})
692
sio = self.make_utf8_encoded_stringio()
693
formatter = log.LongLogFormatter(to_file=sio)
694
def trivial_custom_prop_handler(revision):
695
raise StandardError("a test error")
697
log.properties_handler_registry.register(
698
'trivial_custom_prop_handler',
699
trivial_custom_prop_handler)
700
self.assertRaises(StandardError, log.show_log, wt.branch, formatter,)
702
def test_properties_handler_bad_argument(self):
703
wt = self.make_standard_commit('bad_argument',
704
revprops={'a_prop':'test_value'})
705
sio = self.make_utf8_encoded_stringio()
706
formatter = log.LongLogFormatter(to_file=sio)
707
def bad_argument_prop_handler(revision):
708
return {'custom_prop_name':revision.properties['a_prop']}
710
log.properties_handler_registry.register(
711
'bad_argument_prop_handler',
712
bad_argument_prop_handler)
714
self.assertRaises(AttributeError, formatter.show_properties,
717
revision = wt.branch.repository.get_revision(wt.branch.last_revision())
718
formatter.show_properties(revision, '')
719
self.assertEqualDiff('''custom_prop_name: test_value\n''',
722
def test_show_ids(self):
723
wt = self.make_branch_and_tree('parent')
724
self.build_tree(['parent/f1', 'parent/f2'])
726
self.wt_commit(wt, 'first post', rev_id='a')
727
child_wt = wt.bzrdir.sprout('child').open_workingtree()
728
self.wt_commit(child_wt, 'branch 1 changes', rev_id='b')
729
wt.merge_from_branch(child_wt.branch)
730
self.wt_commit(wt, 'merge branch 1', rev_id='c')
731
self.assertFormatterResult("""\
732
------------------------------------------------------------
737
committer: Joe Foo <joe@foo.com>
739
timestamp: Tue 2005-11-22 00:00:02 +0000
742
------------------------------------------------------------
746
committer: Joe Foo <joe@foo.com>
748
timestamp: Tue 2005-11-22 00:00:01 +0000
751
------------------------------------------------------------
754
committer: Joe Foo <joe@foo.com>
756
timestamp: Tue 2005-11-22 00:00:00 +0000
760
wt.branch, log.LongLogFormatter,
761
formatter_kwargs=dict(levels=0,show_ids=True))
764
class TestLongLogFormatterWithoutMergeRevisions(TestCaseForLogFormatter):
766
def test_long_verbose_log(self):
767
"""Verbose log includes changed files
771
wt = self.make_standard_commit('test_long_verbose_log', authors=[])
772
self.assertFormatterResult("""\
773
------------------------------------------------------------
775
committer: Lorem Ipsum <test@example.com>
776
branch nick: test_long_verbose_log
777
timestamp: Tue 2005-11-22 00:00:00 +0000
783
wt.branch, log.LongLogFormatter,
784
formatter_kwargs=dict(levels=1),
785
show_log_kwargs=dict(verbose=True))
787
def test_long_verbose_contain_deltas(self):
788
wt = self.make_branch_and_tree('parent')
789
self.build_tree(['parent/f1', 'parent/f2'])
791
self.wt_commit(wt, 'first post')
792
child_wt = wt.bzrdir.sprout('child').open_workingtree()
793
os.unlink('child/f1')
794
self.build_tree_contents([('child/f2', 'hello\n')])
795
self.wt_commit(child_wt, 'removed f1 and modified f2')
796
wt.merge_from_branch(child_wt.branch)
797
self.wt_commit(wt, 'merge branch 1')
798
self.assertFormatterResult("""\
799
------------------------------------------------------------
801
committer: Joe Foo <joe@foo.com>
803
timestamp: Tue 2005-11-22 00:00:02 +0000
810
------------------------------------------------------------
812
committer: Joe Foo <joe@foo.com>
814
timestamp: Tue 2005-11-22 00:00:00 +0000
821
wt.branch, log.LongLogFormatter,
822
formatter_kwargs=dict(levels=1),
823
show_log_kwargs=dict(verbose=True))
825
def test_long_trailing_newlines(self):
444
826
wt = self.make_branch_and_tree('.')
446
self.build_tree(['a'])
448
b.nick = 'test_author_log'
449
wt.commit(message='add a',
450
timestamp=1132711707,
452
committer='Lorem Ipsum <test@example.com>',
453
author='John Doe <jdoe@example.com>')
455
formatter = LongLogFormatter(to_file=sio)
456
show_log(b, formatter)
457
self.assertEqualDiff(sio.getvalue(), '''\
827
b = self.make_commits_with_trailing_newlines(wt)
828
self.assertFormatterResult("""\
829
------------------------------------------------------------
831
committer: Joe Foo <joe@foo.com>
833
timestamp: Tue 2005-11-22 00:00:02 +0000
835
single line with trailing newline
836
------------------------------------------------------------
838
committer: Joe Foo <joe@foo.com>
840
timestamp: Tue 2005-11-22 00:00:01 +0000
845
------------------------------------------------------------
847
committer: Joe Foo <joe@foo.com>
849
timestamp: Tue 2005-11-22 00:00:00 +0000
853
b, log.LongLogFormatter,
854
formatter_kwargs=dict(levels=1))
856
def test_long_author_in_log(self):
857
"""Log includes the author name if it's set in
858
the revision properties
860
wt = self.make_standard_commit('test_author_log')
861
self.assertFormatterResult("""\
458
862
------------------------------------------------------------
460
864
author: John Doe <jdoe@example.com>
461
865
committer: Lorem Ipsum <test@example.com>
462
866
branch nick: test_author_log
463
timestamp: Wed 2005-11-23 12:08:27 +1000
470
class TestLineLogFormatter(TestCaseWithTransport):
867
timestamp: Tue 2005-11-22 00:00:00 +0000
871
wt.branch, log.LongLogFormatter,
872
formatter_kwargs=dict(levels=1))
874
def test_long_properties_in_log(self):
875
"""Log includes the custom properties returned by the registered
878
wt = self.make_standard_commit('test_properties_in_log')
879
def trivial_custom_prop_handler(revision):
880
return {'test_prop':'test_value'}
882
log.properties_handler_registry.register(
883
'trivial_custom_prop_handler',
884
trivial_custom_prop_handler)
885
self.assertFormatterResult("""\
886
------------------------------------------------------------
888
test_prop: test_value
889
author: John Doe <jdoe@example.com>
890
committer: Lorem Ipsum <test@example.com>
891
branch nick: test_properties_in_log
892
timestamp: Tue 2005-11-22 00:00:00 +0000
896
wt.branch, log.LongLogFormatter,
897
formatter_kwargs=dict(levels=1))
900
class TestLineLogFormatter(TestCaseForLogFormatter):
472
902
def test_line_log(self):
473
903
"""Line log should show revno
477
wt = self.make_branch_and_tree('.')
479
self.build_tree(['a'])
481
b.nick = 'test-line-log'
482
wt.commit(message='add a',
483
timestamp=1132711707,
485
committer='Line-Log-Formatter Tester <test@line.log>')
486
logfile = file('out.tmp', 'w+')
487
formatter = LineLogFormatter(to_file=logfile)
488
show_log(b, formatter)
491
log_contents = logfile.read()
492
self.assertEqualDiff(log_contents,
493
'1: Line-Log-Formatte... 2005-11-23 add a\n')
495
def test_short_log_with_merges(self):
496
wt = self.make_branch_and_memory_tree('.')
500
wt.commit('rev-1', rev_id='rev-1',
501
timestamp=1132586655, timezone=36000,
502
committer='Joe Foo <joe@foo.com>')
503
wt.commit('rev-merged', rev_id='rev-2a',
504
timestamp=1132586700, timezone=36000,
505
committer='Joe Foo <joe@foo.com>')
506
wt.set_parent_ids(['rev-1', 'rev-2a'])
507
wt.branch.set_last_revision_info(1, 'rev-1')
508
wt.commit('rev-2', rev_id='rev-2b',
509
timestamp=1132586800, timezone=36000,
510
committer='Joe Foo <joe@foo.com>')
511
logfile = self.make_utf8_encoded_stringio()
512
formatter = ShortLogFormatter(to_file=logfile)
513
show_log(wt.branch, formatter)
515
self.assertEqualDiff(logfile.getvalue(), """\
516
2 Joe Foo\t2005-11-22 [merge]
519
1 Joe Foo\t2005-11-22
907
wt = self.make_standard_commit('test-line-log',
908
committer='Line-Log-Formatter Tester <test@line.log>',
910
self.assertFormatterResult("""\
911
1: Line-Log-Formatte... 2005-11-22 add a
913
wt.branch, log.LineLogFormatter)
526
915
def test_trailing_newlines(self):
527
916
wt = self.make_branch_and_tree('.')
528
b = make_commits_with_trailing_newlines(wt)
529
sio = self.make_utf8_encoded_stringio()
530
lf = LineLogFormatter(to_file=sio)
532
self.assertEqualDiff(sio.getvalue(), """\
533
3: Joe Foo 2005-11-21 single line with trailing newline
534
2: Joe Bar 2005-11-21 multiline
535
1: Joe Foo 2005-11-21 simple log message
539
class TestGetViewRevisions(TestCaseWithTransport):
541
def make_tree_with_commits(self):
542
"""Create a tree with well-known revision ids"""
543
wt = self.make_branch_and_tree('tree1')
544
wt.commit('commit one', rev_id='1')
545
wt.commit('commit two', rev_id='2')
546
wt.commit('commit three', rev_id='3')
547
mainline_revs = [None, '1', '2', '3']
548
rev_nos = {'1': 1, '2': 2, '3': 3}
549
return mainline_revs, rev_nos, wt
551
def make_tree_with_merges(self):
552
"""Create a tree with well-known revision ids and a merge"""
553
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
554
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
555
tree2.commit('four-a', rev_id='4a')
556
wt.merge_from_branch(tree2.branch)
557
wt.commit('four-b', rev_id='4b')
558
mainline_revs.append('4b')
561
return mainline_revs, rev_nos, wt
563
def make_tree_with_many_merges(self):
564
"""Create a tree with well-known revision ids"""
565
wt = self.make_branch_and_tree('tree1')
566
wt.commit('commit one', rev_id='1')
567
wt.commit('commit two', rev_id='2')
568
tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
569
tree3.commit('commit three a', rev_id='3a')
570
tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
571
tree2.merge_from_branch(tree3.branch)
572
tree2.commit('commit three b', rev_id='3b')
573
wt.merge_from_branch(tree2.branch)
574
wt.commit('commit three c', rev_id='3c')
575
tree2.commit('four-a', rev_id='4a')
576
wt.merge_from_branch(tree2.branch)
577
wt.commit('four-b', rev_id='4b')
578
mainline_revs = [None, '1', '2', '3c', '4b']
579
rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
580
full_rev_nos_for_reference = {
583
'3a': '2.2.1', #first commit tree 3
584
'3b': '2.1.1', # first commit tree 2
585
'3c': '3', #merges 3b to main
586
'4a': '2.1.2', # second commit tree 2
587
'4b': '4', # merges 4a to main
589
return mainline_revs, rev_nos, wt
591
def test_get_view_revisions_forward(self):
592
"""Test the get_view_revisions method"""
593
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
594
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
596
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
598
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
599
'forward', include_merges=False))
600
self.assertEqual(revisions, revisions2)
602
def test_get_view_revisions_reverse(self):
603
"""Test the get_view_revisions with reverse"""
604
mainline_revs, rev_nos, wt = self.make_tree_with_commits()
605
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
607
self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
609
revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
610
'reverse', include_merges=False))
611
self.assertEqual(revisions, revisions2)
613
def test_get_view_revisions_merge(self):
614
"""Test get_view_revisions when there are merges"""
615
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
616
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
618
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
619
('4b', '4', 0), ('4a', '3.1.1', 1)],
621
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
622
'forward', include_merges=False))
623
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
627
def test_get_view_revisions_merge_reverse(self):
628
"""Test get_view_revisions in reverse when there are merges"""
629
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
630
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
632
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
633
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
635
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
636
'reverse', include_merges=False))
637
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
641
def test_get_view_revisions_merge2(self):
642
"""Test get_view_revisions when there are merges"""
643
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
644
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
646
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
647
('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
649
self.assertEqual(expected, revisions)
650
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
651
'forward', include_merges=False))
652
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
657
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
659
def create_tree_with_single_merge(self):
660
"""Create a branch with a moderate layout.
662
The revision graph looks like:
670
In this graph, A introduced files f1 and f2 and f3.
671
B modifies f1 and f3, and C modifies f2 and f3.
672
D merges the changes from B and C and resolves the conflict for f3.
674
# TODO: jam 20070218 This seems like it could really be done
675
# with make_branch_and_memory_tree() if we could just
676
# create the content of those files.
677
# TODO: jam 20070218 Another alternative is that we would really
678
# like to only create this tree 1 time for all tests that
679
# use it. Since 'log' only uses the tree in a readonly
680
# fashion, it seems a shame to regenerate an identical
681
# tree for each test.
682
tree = self.make_branch_and_tree('tree')
684
self.addCleanup(tree.unlock)
686
self.build_tree_contents([('tree/f1', 'A\n'),
690
tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
691
tree.commit('A', rev_id='A')
693
self.build_tree_contents([('tree/f2', 'A\nC\n'),
694
('tree/f3', 'A\nC\n'),
696
tree.commit('C', rev_id='C')
697
# Revert back to A to build the other history.
698
tree.set_last_revision('A')
699
tree.branch.set_last_revision_info(1, 'A')
700
self.build_tree_contents([('tree/f1', 'A\nB\n'),
702
('tree/f3', 'A\nB\n'),
704
tree.commit('B', rev_id='B')
705
tree.set_parent_ids(['B', 'C'])
706
self.build_tree_contents([('tree/f1', 'A\nB\n'),
707
('tree/f2', 'A\nC\n'),
708
('tree/f3', 'A\nB\nC\n'),
710
tree.commit('D', rev_id='D')
712
# Switch to a read lock for this tree.
713
# We still have addCleanup(unlock)
718
def test_tree_with_single_merge(self):
719
"""Make sure the tree layout is correct."""
720
tree = self.create_tree_with_single_merge()
721
rev_A_tree = tree.branch.repository.revision_tree('A')
722
rev_B_tree = tree.branch.repository.revision_tree('B')
724
f1_changed = (u'f1', 'f1-id', 'file', True, False)
725
f2_changed = (u'f2', 'f2-id', 'file', True, False)
726
f3_changed = (u'f3', 'f3-id', 'file', True, False)
728
delta = rev_B_tree.changes_from(rev_A_tree)
729
self.assertEqual([f1_changed, f3_changed], delta.modified)
730
self.assertEqual([], delta.renamed)
731
self.assertEqual([], delta.added)
732
self.assertEqual([], delta.removed)
734
rev_C_tree = tree.branch.repository.revision_tree('C')
735
delta = rev_C_tree.changes_from(rev_A_tree)
736
self.assertEqual([f2_changed, f3_changed], delta.modified)
737
self.assertEqual([], delta.renamed)
738
self.assertEqual([], delta.added)
739
self.assertEqual([], delta.removed)
741
rev_D_tree = tree.branch.repository.revision_tree('D')
742
delta = rev_D_tree.changes_from(rev_B_tree)
743
self.assertEqual([f2_changed, f3_changed], delta.modified)
744
self.assertEqual([], delta.renamed)
745
self.assertEqual([], delta.added)
746
self.assertEqual([], delta.removed)
748
delta = rev_D_tree.changes_from(rev_C_tree)
749
self.assertEqual([f1_changed, f3_changed], delta.modified)
750
self.assertEqual([], delta.renamed)
751
self.assertEqual([], delta.added)
752
self.assertEqual([], delta.removed)
754
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
755
"""Make sure _filter_revisions_touching_file_id returns the right values.
757
Get the return value from _filter_revisions_touching_file_id and make
758
sure they are correct.
760
# The api for _get_revisions_touching_file_id is a little crazy,
761
# So we do the setup here.
762
mainline = tree.branch.revision_history()
763
mainline.insert(0, None)
764
revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
765
view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
767
actual_revs = log._filter_revisions_touching_file_id(
771
list(view_revs_iter))
772
self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
774
def test_file_id_f1(self):
775
tree = self.create_tree_with_single_merge()
776
# f1 should be marked as modified by revisions A and B
777
self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
779
def test_file_id_f2(self):
780
tree = self.create_tree_with_single_merge()
781
# f2 should be marked as modified by revisions A, C, and D
782
# because D merged the changes from C.
783
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
785
def test_file_id_f3(self):
786
tree = self.create_tree_with_single_merge()
787
# f3 should be marked as modified by revisions A, B, C, and D
788
self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
791
class TestShowChangedRevisions(TestCaseWithTransport):
917
b = self.make_commits_with_trailing_newlines(wt)
918
self.assertFormatterResult("""\
919
3: Joe Foo 2005-11-22 single line with trailing newline
920
2: Joe Foo 2005-11-22 multiline
921
1: Joe Foo 2005-11-22 simple log message
923
b, log.LineLogFormatter)
925
def test_line_log_single_merge_revision(self):
926
wt = self._prepare_tree_with_merges()
927
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
928
rev = revspec.in_history(wt.branch)
929
self.assertFormatterResult("""\
930
1.1.1: Joe Foo 2005-11-22 rev-merged
932
wt.branch, log.LineLogFormatter,
933
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
935
def test_line_log_with_tags(self):
936
wt = self._prepare_tree_with_merges(with_tags=True)
937
self.assertFormatterResult("""\
938
3: Joe Foo 2005-11-22 {v1.0, v1.0rc1} rev-3
939
2: Joe Foo 2005-11-22 [merge] {v0.2} rev-2
940
1: Joe Foo 2005-11-22 rev-1
942
wt.branch, log.LineLogFormatter)
945
class TestLineLogFormatterWithMergeRevisions(TestCaseForLogFormatter):
947
def test_line_merge_revs_log(self):
948
"""Line log should show revno
952
wt = self.make_standard_commit('test-line-log',
953
committer='Line-Log-Formatter Tester <test@line.log>',
955
self.assertFormatterResult("""\
956
1: Line-Log-Formatte... 2005-11-22 add a
958
wt.branch, log.LineLogFormatter)
960
def test_line_merge_revs_log_single_merge_revision(self):
961
wt = self._prepare_tree_with_merges()
962
revspec = revisionspec.RevisionSpec.from_string('1.1.1')
963
rev = revspec.in_history(wt.branch)
964
self.assertFormatterResult("""\
965
1.1.1: Joe Foo 2005-11-22 rev-merged
967
wt.branch, log.LineLogFormatter,
968
formatter_kwargs=dict(levels=0),
969
show_log_kwargs=dict(start_revision=rev, end_revision=rev))
971
def test_line_merge_revs_log_with_merges(self):
972
wt = self._prepare_tree_with_merges()
973
self.assertFormatterResult("""\
974
2: Joe Foo 2005-11-22 [merge] rev-2
975
1.1.1: Joe Foo 2005-11-22 rev-merged
976
1: Joe Foo 2005-11-22 rev-1
978
wt.branch, log.LineLogFormatter,
979
formatter_kwargs=dict(levels=0))
982
class TestGnuChangelogFormatter(TestCaseForLogFormatter):
984
def test_gnu_changelog(self):
985
wt = self.make_standard_commit('nicky', authors=[])
986
self.assertFormatterResult('''\
987
2005-11-22 Lorem Ipsum <test@example.com>
992
wt.branch, log.GnuChangelogLogFormatter)
994
def test_with_authors(self):
995
wt = self.make_standard_commit('nicky',
996
authors=['Fooa Fooz <foo@example.com>',
997
'Bari Baro <bar@example.com>'])
998
self.assertFormatterResult('''\
999
2005-11-22 Fooa Fooz <foo@example.com>
1004
wt.branch, log.GnuChangelogLogFormatter)
1006
def test_verbose(self):
1007
wt = self.make_standard_commit('nicky')
1008
self.assertFormatterResult('''\
1009
2005-11-22 John Doe <jdoe@example.com>
1016
wt.branch, log.GnuChangelogLogFormatter,
1017
show_log_kwargs=dict(verbose=True))
1020
class TestShowChangedRevisions(tests.TestCaseWithTransport):
793
1022
def test_show_changed_revisions_verbose(self):
794
1023
tree = self.make_branch_and_tree('tree_a')
801
1030
self.assertNotContainsRe(s.getvalue(), 'foo')
804
class TestLogFormatter(TestCase):
1033
class TestLogFormatter(tests.TestCase):
1036
super(TestLogFormatter, self).setUp()
1037
self.rev = revision.Revision('a-id')
1038
self.lf = log.LogFormatter(None)
806
1040
def test_short_committer(self):
807
rev = Revision('a-id')
808
rev.committer = 'John Doe <jdoe@example.com>'
809
lf = LogFormatter(None)
810
self.assertEqual('John Doe', lf.short_committer(rev))
1041
def assertCommitter(expected, committer):
1042
self.rev.committer = committer
1043
self.assertEqual(expected, self.lf.short_committer(self.rev))
1045
assertCommitter('John Doe', 'John Doe <jdoe@example.com>')
1046
assertCommitter('John Smith', 'John Smith <jsmith@example.com>')
1047
assertCommitter('John Smith', 'John Smith')
1048
assertCommitter('jsmith@example.com', 'jsmith@example.com')
1049
assertCommitter('jsmith@example.com', '<jsmith@example.com>')
1050
assertCommitter('John Smith', 'John Smith jsmith@example.com')
812
1052
def test_short_author(self):
813
rev = Revision('a-id')
814
rev.committer = 'John Doe <jdoe@example.com>'
815
lf = LogFormatter(None)
816
self.assertEqual('John Doe', lf.short_author(rev))
817
rev.properties['author'] = 'John Smith <jsmith@example.com>'
818
self.assertEqual('John Smith', lf.short_author(rev))
1053
def assertAuthor(expected, author):
1054
self.rev.properties['author'] = author
1055
self.assertEqual(expected, self.lf.short_author(self.rev))
1057
assertAuthor('John Smith', 'John Smith <jsmith@example.com>')
1058
assertAuthor('John Smith', 'John Smith')
1059
assertAuthor('jsmith@example.com', 'jsmith@example.com')
1060
assertAuthor('jsmith@example.com', '<jsmith@example.com>')
1061
assertAuthor('John Smith', 'John Smith jsmith@example.com')
1063
def test_short_author_from_committer(self):
1064
self.rev.committer = 'John Doe <jdoe@example.com>'
1065
self.assertEqual('John Doe', self.lf.short_author(self.rev))
1067
def test_short_author_from_authors(self):
1068
self.rev.properties['authors'] = ('John Smith <jsmith@example.com>\n'
1069
'Jane Rey <jrey@example.com>')
1070
self.assertEqual('John Smith', self.lf.short_author(self.rev))
1073
class TestReverseByDepth(tests.TestCase):
1074
"""Test reverse_by_depth behavior.
1076
This is used to present revisions in forward (oldest first) order in a nice
1079
The tests use lighter revision description to ease reading.
1082
def assertReversed(self, forward, backward):
1083
# Transform the descriptions to suit the API: tests use (revno, depth),
1084
# while the API expects (revid, revno, depth)
1085
def complete_revisions(l):
1086
"""Transform the description to suit the API.
1088
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1089
Since the revid is arbitrary, we just duplicate revno
1091
return [ (r, r, d) for r, d in l]
1092
forward = complete_revisions(forward)
1093
backward= complete_revisions(backward)
1094
self.assertEqual(forward, log.reverse_by_depth(backward))
1097
def test_mainline_revisions(self):
1098
self.assertReversed([( '1', 0), ('2', 0)],
1099
[('2', 0), ('1', 0)])
1101
def test_merged_revisions(self):
1102
self.assertReversed([('1', 0), ('2', 0), ('2.2', 1), ('2.1', 1),],
1103
[('2', 0), ('2.1', 1), ('2.2', 1), ('1', 0),])
1104
def test_shifted_merged_revisions(self):
1105
"""Test irregular layout.
1107
Requesting revisions touching a file can produce "holes" in the depths.
1109
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1110
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1112
def test_merged_without_child_revisions(self):
1113
"""Test irregular layout.
1115
Revision ranges can produce "holes" in the depths.
1117
# When a revision of higher depth doesn't follow one of lower depth, we
1118
# assume a lower depth one is virtually there
1119
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1120
[('4', 4), ('3', 3), ('2', 2), ('1', 2),])
1121
# So we get the same order after reversing below even if the original
1122
# revisions are not in the same order.
1123
self.assertReversed([('1', 2), ('2', 2), ('3', 3), ('4', 4)],
1124
[('3', 3), ('4', 4), ('2', 2), ('1', 2),])
1127
class TestHistoryChange(tests.TestCaseWithTransport):
1129
def setup_a_tree(self):
1130
tree = self.make_branch_and_tree('tree')
1132
self.addCleanup(tree.unlock)
1133
tree.commit('1a', rev_id='1a')
1134
tree.commit('2a', rev_id='2a')
1135
tree.commit('3a', rev_id='3a')
1138
def setup_ab_tree(self):
1139
tree = self.setup_a_tree()
1140
tree.set_last_revision('1a')
1141
tree.branch.set_last_revision_info(1, '1a')
1142
tree.commit('2b', rev_id='2b')
1143
tree.commit('3b', rev_id='3b')
1146
def setup_ac_tree(self):
1147
tree = self.setup_a_tree()
1148
tree.set_last_revision(revision.NULL_REVISION)
1149
tree.branch.set_last_revision_info(0, revision.NULL_REVISION)
1150
tree.commit('1c', rev_id='1c')
1151
tree.commit('2c', rev_id='2c')
1152
tree.commit('3c', rev_id='3c')
1155
def test_all_new(self):
1156
tree = self.setup_ab_tree()
1157
old, new = log.get_history_change('1a', '3a', tree.branch.repository)
1158
self.assertEqual([], old)
1159
self.assertEqual(['2a', '3a'], new)
1161
def test_all_old(self):
1162
tree = self.setup_ab_tree()
1163
old, new = log.get_history_change('3a', '1a', tree.branch.repository)
1164
self.assertEqual([], new)
1165
self.assertEqual(['2a', '3a'], old)
1167
def test_null_old(self):
1168
tree = self.setup_ab_tree()
1169
old, new = log.get_history_change(revision.NULL_REVISION,
1170
'3a', tree.branch.repository)
1171
self.assertEqual([], old)
1172
self.assertEqual(['1a', '2a', '3a'], new)
1174
def test_null_new(self):
1175
tree = self.setup_ab_tree()
1176
old, new = log.get_history_change('3a', revision.NULL_REVISION,
1177
tree.branch.repository)
1178
self.assertEqual([], new)
1179
self.assertEqual(['1a', '2a', '3a'], old)
1181
def test_diverged(self):
1182
tree = self.setup_ab_tree()
1183
old, new = log.get_history_change('3a', '3b', tree.branch.repository)
1184
self.assertEqual(old, ['2a', '3a'])
1185
self.assertEqual(new, ['2b', '3b'])
1187
def test_unrelated(self):
1188
tree = self.setup_ac_tree()
1189
old, new = log.get_history_change('3a', '3c', tree.branch.repository)
1190
self.assertEqual(old, ['1a', '2a', '3a'])
1191
self.assertEqual(new, ['1c', '2c', '3c'])
1193
def test_show_branch_change(self):
1194
tree = self.setup_ab_tree()
1196
log.show_branch_change(tree.branch, s, 3, '3a')
1197
self.assertContainsRe(s.getvalue(),
1198
'[*]{60}\nRemoved Revisions:\n(.|\n)*2a(.|\n)*3a(.|\n)*'
1199
'[*]{60}\n\nAdded Revisions:\n(.|\n)*2b(.|\n)*3b')
1201
def test_show_branch_change_no_change(self):
1202
tree = self.setup_ab_tree()
1204
log.show_branch_change(tree.branch, s, 3, '3b')
1205
self.assertEqual(s.getvalue(),
1206
'Nothing seems to have changed\n')
1208
def test_show_branch_change_no_old(self):
1209
tree = self.setup_ab_tree()
1211
log.show_branch_change(tree.branch, s, 2, '2b')
1212
self.assertContainsRe(s.getvalue(), 'Added Revisions:')
1213
self.assertNotContainsRe(s.getvalue(), 'Removed Revisions:')
1215
def test_show_branch_change_no_new(self):
1216
tree = self.setup_ab_tree()
1217
tree.branch.set_last_revision_info(2, '2b')
1219
log.show_branch_change(tree.branch, s, 3, '3b')
1220
self.assertContainsRe(s.getvalue(), 'Removed Revisions:')
1221
self.assertNotContainsRe(s.getvalue(), 'Added Revisions:')
1224
class TestRevisionNotInBranch(TestCaseForLogFormatter):
1226
def setup_a_tree(self):
1227
tree = self.make_branch_and_tree('tree')
1229
self.addCleanup(tree.unlock)
1231
'committer': 'Joe Foo <joe@foo.com>',
1232
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1233
'timezone': 0, # UTC
1235
tree.commit('commit 1a', rev_id='1a', **kwargs)
1236
tree.commit('commit 2a', rev_id='2a', **kwargs)
1237
tree.commit('commit 3a', rev_id='3a', **kwargs)
1240
def setup_ab_tree(self):
1241
tree = self.setup_a_tree()
1242
tree.set_last_revision('1a')
1243
tree.branch.set_last_revision_info(1, '1a')
1245
'committer': 'Joe Foo <joe@foo.com>',
1246
'timestamp': 1132617600, # Mon 2005-11-22 00:00:00 +0000
1247
'timezone': 0, # UTC
1249
tree.commit('commit 2b', rev_id='2b', **kwargs)
1250
tree.commit('commit 3b', rev_id='3b', **kwargs)
1253
def test_one_revision(self):
1254
tree = self.setup_ab_tree()
1256
rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1257
log.show_log(tree.branch, lf, verbose=True, start_revision=rev,
1259
self.assertEqual(1, len(lf.revisions))
1260
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1261
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1263
def test_many_revisions(self):
1264
tree = self.setup_ab_tree()
1266
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1267
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1268
log.show_log(tree.branch, lf, verbose=True, start_revision=start_rev,
1269
end_revision=end_rev)
1270
self.assertEqual(3, len(lf.revisions))
1271
self.assertEqual(None, lf.revisions[0].revno) # Out-of-branch
1272
self.assertEqual('3a', lf.revisions[0].rev.revision_id)
1273
self.assertEqual(None, lf.revisions[1].revno) # Out-of-branch
1274
self.assertEqual('2a', lf.revisions[1].rev.revision_id)
1275
self.assertEqual('1', lf.revisions[2].revno) # In-branch
1277
def test_long_format(self):
1278
tree = self.setup_ab_tree()
1279
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1280
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1281
self.assertFormatterResult("""\
1282
------------------------------------------------------------
1284
committer: Joe Foo <joe@foo.com>
1286
timestamp: Tue 2005-11-22 00:00:00 +0000
1289
------------------------------------------------------------
1291
committer: Joe Foo <joe@foo.com>
1293
timestamp: Tue 2005-11-22 00:00:00 +0000
1296
------------------------------------------------------------
1298
committer: Joe Foo <joe@foo.com>
1300
timestamp: Tue 2005-11-22 00:00:00 +0000
1304
tree.branch, log.LongLogFormatter, show_log_kwargs={
1305
'start_revision': start_rev, 'end_revision': end_rev
1308
def test_short_format(self):
1309
tree = self.setup_ab_tree()
1310
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1311
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1312
self.assertFormatterResult("""\
1321
1 Joe Foo\t2005-11-22
1325
tree.branch, log.ShortLogFormatter, show_log_kwargs={
1326
'start_revision': start_rev, 'end_revision': end_rev
1329
def test_line_format(self):
1330
tree = self.setup_ab_tree()
1331
start_rev = revisionspec.RevisionInfo(tree.branch, None, '1a')
1332
end_rev = revisionspec.RevisionInfo(tree.branch, None, '3a')
1333
self.assertFormatterResult("""\
1334
Joe Foo 2005-11-22 commit 3a
1335
Joe Foo 2005-11-22 commit 2a
1336
1: Joe Foo 2005-11-22 commit 1a
1338
tree.branch, log.LineLogFormatter, show_log_kwargs={
1339
'start_revision': start_rev, 'end_revision': end_rev
1343
class TestLogWithBugs(TestCaseForLogFormatter, TestLogMixin):
1346
super(TestLogWithBugs, self).setUp()
1347
log.properties_handler_registry.register(
1348
'bugs_properties_handler',
1349
log._bugs_properties_handler)
1351
def make_commits_with_bugs(self):
1352
"""Helper method for LogFormatter tests"""
1353
tree = self.make_branch_and_tree(u'.')
1354
self.build_tree(['a', 'b'])
1356
self.wt_commit(tree, 'simple log message', rev_id='a1',
1357
revprops={'bugs': 'test://bug/id fixed'})
1359
self.wt_commit(tree, 'multiline\nlog\nmessage\n', rev_id='a2',
1360
authors=['Joe Bar <joe@bar.com>'],
1361
revprops={'bugs': 'test://bug/id fixed\n'
1362
'test://bug/2 fixed'})
1366
def test_long_bugs(self):
1367
tree = self.make_commits_with_bugs()
1368
self.assertFormatterResult("""\
1369
------------------------------------------------------------
1371
fixes bugs: test://bug/id test://bug/2
1372
author: Joe Bar <joe@bar.com>
1373
committer: Joe Foo <joe@foo.com>
1375
timestamp: Tue 2005-11-22 00:00:01 +0000
1380
------------------------------------------------------------
1382
fixes bug: test://bug/id
1383
committer: Joe Foo <joe@foo.com>
1385
timestamp: Tue 2005-11-22 00:00:00 +0000
1389
tree.branch, log.LongLogFormatter)
1391
def test_short_bugs(self):
1392
tree = self.make_commits_with_bugs()
1393
self.assertFormatterResult("""\
1394
2 Joe Bar\t2005-11-22
1395
fixes bugs: test://bug/id test://bug/2
1400
1 Joe Foo\t2005-11-22
1401
fixes bug: test://bug/id
1405
tree.branch, log.ShortLogFormatter)
1407
def test_wrong_bugs_property(self):
1408
tree = self.make_branch_and_tree(u'.')
1409
self.build_tree(['foo'])
1410
self.wt_commit(tree, 'simple log message', rev_id='a1',
1411
revprops={'bugs': 'test://bug/id invalid_value'})
1412
self.assertFormatterResult("""\
1413
1 Joe Foo\t2005-11-22
1417
tree.branch, log.ShortLogFormatter)
1419
def test_bugs_handler_present(self):
1420
self.properties_handler_registry.get('bugs_properties_handler')
1423
class TestLogForAuthors(TestCaseForLogFormatter):
1426
super(TestLogForAuthors, self).setUp()
1427
self.wt = self.make_standard_commit('nicky',
1428
authors=['John Doe <jdoe@example.com>',
1429
'Jane Rey <jrey@example.com>'])
1431
def assertFormatterResult(self, formatter, who, result):
1432
formatter_kwargs = dict()
1434
author_list_handler = log.author_list_registry.get(who)
1435
formatter_kwargs['author_list_handler'] = author_list_handler
1436
TestCaseForLogFormatter.assertFormatterResult(self, result,
1437
self.wt.branch, formatter, formatter_kwargs=formatter_kwargs)
1439
def test_line_default(self):
1440
self.assertFormatterResult(log.LineLogFormatter, None, """\
1441
1: John Doe 2005-11-22 add a
1444
def test_line_committer(self):
1445
self.assertFormatterResult(log.LineLogFormatter, 'committer', """\
1446
1: Lorem Ipsum 2005-11-22 add a
1449
def test_line_first(self):
1450
self.assertFormatterResult(log.LineLogFormatter, 'first', """\
1451
1: John Doe 2005-11-22 add a
1454
def test_line_all(self):
1455
self.assertFormatterResult(log.LineLogFormatter, 'all', """\
1456
1: John Doe, Jane Rey 2005-11-22 add a
1460
def test_short_default(self):
1461
self.assertFormatterResult(log.ShortLogFormatter, None, """\
1462
1 John Doe\t2005-11-22
1467
def test_short_committer(self):
1468
self.assertFormatterResult(log.ShortLogFormatter, 'committer', """\
1469
1 Lorem Ipsum\t2005-11-22
1474
def test_short_first(self):
1475
self.assertFormatterResult(log.ShortLogFormatter, 'first', """\
1476
1 John Doe\t2005-11-22
1481
def test_short_all(self):
1482
self.assertFormatterResult(log.ShortLogFormatter, 'all', """\
1483
1 John Doe, Jane Rey\t2005-11-22
1488
def test_long_default(self):
1489
self.assertFormatterResult(log.LongLogFormatter, None, """\
1490
------------------------------------------------------------
1492
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1493
committer: Lorem Ipsum <test@example.com>
1495
timestamp: Tue 2005-11-22 00:00:00 +0000
1500
def test_long_committer(self):
1501
self.assertFormatterResult(log.LongLogFormatter, 'committer', """\
1502
------------------------------------------------------------
1504
committer: Lorem Ipsum <test@example.com>
1506
timestamp: Tue 2005-11-22 00:00:00 +0000
1511
def test_long_first(self):
1512
self.assertFormatterResult(log.LongLogFormatter, 'first', """\
1513
------------------------------------------------------------
1515
author: John Doe <jdoe@example.com>
1516
committer: Lorem Ipsum <test@example.com>
1518
timestamp: Tue 2005-11-22 00:00:00 +0000
1523
def test_long_all(self):
1524
self.assertFormatterResult(log.LongLogFormatter, 'all', """\
1525
------------------------------------------------------------
1527
author: John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1528
committer: Lorem Ipsum <test@example.com>
1530
timestamp: Tue 2005-11-22 00:00:00 +0000
1535
def test_gnu_changelog_default(self):
1536
self.assertFormatterResult(log.GnuChangelogLogFormatter, None, """\
1537
2005-11-22 John Doe <jdoe@example.com>
1543
def test_gnu_changelog_committer(self):
1544
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'committer', """\
1545
2005-11-22 Lorem Ipsum <test@example.com>
1551
def test_gnu_changelog_first(self):
1552
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'first', """\
1553
2005-11-22 John Doe <jdoe@example.com>
1559
def test_gnu_changelog_all(self):
1560
self.assertFormatterResult(log.GnuChangelogLogFormatter, 'all', """\
1561
2005-11-22 John Doe <jdoe@example.com>, Jane Rey <jrey@example.com>
1568
class TestLogExcludeAncestry(tests.TestCaseWithTransport):
1570
def make_branch_with_alternate_ancestries(self, relpath='.'):
1571
# See test_merge_sorted_exclude_ancestry below for the difference with
1572
# bt.per_branch.test_iter_merge_sorted_revision.
1573
# TestIterMergeSortedRevisionsBushyGraph.
1574
# make_branch_with_alternate_ancestries
1575
# and test_merge_sorted_exclude_ancestry
1576
# See the FIXME in assertLogRevnos too.
1577
builder = branchbuilder.BranchBuilder(self.get_transport(relpath))
1589
builder.start_series()
1590
builder.build_snapshot('1', None, [
1591
('add', ('', 'TREE_ROOT', 'directory', '')),])
1592
builder.build_snapshot('1.1.1', ['1'], [])
1593
builder.build_snapshot('2', ['1'], [])
1594
builder.build_snapshot('1.2.1', ['1.1.1'], [])
1595
builder.build_snapshot('1.1.2', ['1.1.1', '1.2.1'], [])
1596
builder.build_snapshot('3', ['2', '1.1.2'], [])
1597
builder.finish_series()
1598
br = builder.get_branch()
1600
self.addCleanup(br.unlock)
1603
def assertLogRevnos(self, expected_revnos, b, start, end,
1604
exclude_common_ancestry, generate_merge_revisions=True):
1605
# FIXME: the layering in log makes it hard to test intermediate levels,
1606
# I wish adding filters with their parameters was easier...
1608
iter_revs = log._calc_view_revisions(
1609
b, start, end, direction='reverse',
1610
generate_merge_revisions=generate_merge_revisions,
1611
exclude_common_ancestry=exclude_common_ancestry)
1612
self.assertEqual(expected_revnos,
1613
[revid for revid, revno, depth in iter_revs])
1615
def test_merge_sorted_exclude_ancestry(self):
1616
b = self.make_branch_with_alternate_ancestries()
1617
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2', '1'],
1618
b, '1', '3', exclude_common_ancestry=False)
1619
# '2' is part of the '3' ancestry but not part of '1.1.1' ancestry so
1620
# it should be mentioned even if merge_sort order will make it appear
1622
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '2'],
1623
b, '1.1.1', '3', exclude_common_ancestry=True)
1625
def test_merge_sorted_simple_revnos_exclude_ancestry(self):
1626
b = self.make_branch_with_alternate_ancestries()
1627
self.assertLogRevnos(['3', '2'],
1628
b, '1', '3', exclude_common_ancestry=True,
1629
generate_merge_revisions=False)
1630
self.assertLogRevnos(['3', '1.1.2', '1.2.1', '1.1.1', '2'],
1631
b, '1', '3', exclude_common_ancestry=True,
1632
generate_merge_revisions=True)
1635
class TestLogDefaults(TestCaseForLogFormatter):
1636
def test_default_log_level(self):
1638
Test to ensure that specifying 'levels=1' to make_log_request_dict
1639
doesn't get overwritten when using a LogFormatter that supports more
1643
wt = self._prepare_tree_with_merges()
1646
class CustomLogFormatter(log.LogFormatter):
1647
def __init__(self, *args, **kwargs):
1648
super(CustomLogFormatter, self).__init__(*args, **kwargs)
1650
def get_levels(self):
1651
# log formatter supports all levels:
1653
def log_revision(self, revision):
1654
self.revisions.append(revision)
1656
log_formatter = LogCatcher()
1657
# First request we don't specify number of levels, we should get a
1658
# sensible default (whatever the LogFormatter handles - which in this
1659
# case is 0/everything):
1660
request = log.make_log_request_dict(limit=10)
1661
log.Logger(b, request).show(log_formatter)
1662
# should have all three revisions:
1663
self.assertEqual(len(log_formatter.revisions), 3)
1666
log_formatter = LogCatcher()
1667
# now explicitly request mainline revisions only:
1668
request = log.make_log_request_dict(limit=10, levels=1)
1669
log.Logger(b, request).show(log_formatter)
1670
# should now only have 2 revisions:
1671
self.assertEqual(len(log_formatter.revisions), 2)