18
18
from cStringIO import StringIO
20
from bzrlib import log, registry
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 (
32
InvalidRevisionNumber,
34
from bzrlib.revision import Revision
35
from bzrlib.revisionspec import (
41
class TestCaseWithoutPropsHandler(TestCaseWithTransport):
30
class TestCaseWithoutPropsHandler(tests.TestCaseWithTransport):
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()
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
58
class LogCatcher(LogFormatter):
42
log.properties_handler_registry = self.properties_handler_registry
45
class LogCatcher(log.LogFormatter):
59
46
"""Pull log messages into list rather than displaying them.
61
48
For ease of testing we save log messages here rather than actually
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)
108
def test_simple_log(self):
109
eq = self.assertEquals
92
log.show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
94
# Since there is a single revision in the branch all the combinations
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)
103
def test_empty_branch(self):
111
104
wt = self.make_branch_and_tree('.')
114
106
lf = LogCatcher()
107
log.show_log(wt.branch, lf)
109
self.assertEqual([], lf.logs)
111
def test_empty_commit(self):
112
wt = self.make_branch_and_tree('.')
119
114
wt.commit('empty commit')
120
115
lf = LogCatcher()
121
show_log(b, lf, verbose=True)
123
eq(lf.logs[0].revno, '1')
124
eq(lf.logs[0].rev.message, 'empty commit')
126
self.log('log delta: %r' % 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)
122
def test_simple_commit(self):
123
wt = self.make_branch_and_tree('.')
124
wt.commit('empty commit')
129
125
self.build_tree(['hello'])
131
127
wt.commit('add one file',
132
128
committer=u'\u013d\xf3r\xe9m \xcdp\u0161\xfam '
133
129
u'<test@example.com>')
135
lf = self.make_utf8_encoded_stringio()
136
# log using regular thing
137
show_log(b, LongLogFormatter(lf))
139
for l in lf.readlines():
142
# get log as data structure
143
130
lf = LogCatcher()
144
show_log(b, lf, verbose=True)
146
self.log('log entries:')
147
for logentry in lf.logs:
148
self.log('%4s %s' % (logentry.revno, logentry.rev.message))
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')
155
self.log('log 2 delta: %r' % d)
156
self.checkDelta(d, added=['hello'])
134
log_entry = lf.logs[0]
135
self.assertEqual('2', log_entry.revno)
136
self.assertEqual('add one file', log_entry.rev.message)
137
self.checkDelta(log_entry.delta, added=['hello'])
158
# commit a log message with control characters
139
def test_commit_message_with_control_chars(self):
140
wt = self.make_branch_and_tree('.')
159
141
msg = u"All 8-bit chars: " + ''.join([unichr(x) for x in range(256)])
160
142
msg = msg.replace(u'\r', u'\n')
161
self.log("original commit message: %r", msg)
163
144
lf = LogCatcher()
164
show_log(b, lf, verbose=True)
145
log.show_log(wt.branch, lf, verbose=True)
165
146
committed_msg = lf.logs[0].rev.message
166
self.log("escaped commit message: %r", committed_msg)
167
self.assert_(msg != committed_msg)
168
self.assert_(len(committed_msg) > len(msg))
147
self.assertNotEqual(msg, committed_msg)
148
self.assertTrue(len(committed_msg) > len(msg))
170
# Check that log message with only XML-valid characters isn't
150
def test_commit_message_without_control_chars(self):
151
wt = self.make_branch_and_tree('.')
171
152
# escaped. As ElementTree apparently does some kind of
172
153
# newline conversion, neither LF (\x0A) nor CR (\x0D) are
173
154
# included in the test commit message, even though they are
174
155
# valid XML 1.0 characters.
175
156
msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
176
self.log("original commit message: %r", msg)
178
158
lf = LogCatcher()
179
show_log(b, lf, verbose=True)
159
log.show_log(wt.branch, lf, verbose=True)
180
160
committed_msg = lf.logs[0].rev.message
181
self.log("escaped commit message: %r", committed_msg)
182
self.assert_(msg == committed_msg)
161
self.assertEqual(msg, committed_msg)
184
163
def test_deltas_in_merge_revisions(self):
185
164
"""Check deltas created for both mainline and merge revisions"""
186
eq = self.assertEquals
187
165
wt = self.make_branch_and_tree('parent')
188
166
self.build_tree(['parent/file1', 'parent/file2', 'parent/file3'])
202
180
lf = LogCatcher()
203
181
lf.supports_merge_revisions = True
204
show_log(b, lf, verbose=True)
182
log.show_log(b, lf, verbose=True)
184
self.assertEqual(3, len(lf.logs))
206
186
logentry = lf.logs[0]
207
eq(logentry.revno, '2')
208
eq(logentry.rev.message, 'merge child branch')
210
self.checkDelta(d, removed=['file1'], modified=['file2'])
187
self.assertEqual('2', logentry.revno)
188
self.assertEqual('merge child branch', logentry.rev.message)
189
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
211
191
logentry = lf.logs[1]
212
eq(logentry.revno, '1.1.1')
213
eq(logentry.rev.message, 'remove file1 and modify file2')
215
self.checkDelta(d, removed=['file1'], modified=['file2'])
192
self.assertEqual('1.1.1', logentry.revno)
193
self.assertEqual('remove file1 and modify file2', logentry.rev.message)
194
self.checkDelta(logentry.delta, removed=['file1'], modified=['file2'])
216
196
logentry = lf.logs[2]
217
eq(logentry.revno, '1')
218
eq(logentry.rev.message, 'add file1 and file2')
220
self.checkDelta(d, added=['file1', 'file2'])
197
self.assertEqual('1', logentry.revno)
198
self.assertEqual('add file1 and file2', logentry.rev.message)
199
self.checkDelta(logentry.delta, added=['file1', 'file2'])
222
201
def test_merges_nonsupporting_formatter(self):
223
202
"""Tests that show_log will raise if the formatter doesn't
224
203
support merge revisions."""
225
204
wt = self.make_branch_and_memory_tree('.')
229
wt.commit('rev-1', rev_id='rev-1',
230
timestamp=1132586655, timezone=36000,
231
committer='Joe Foo <joe@foo.com>')
232
wt.commit('rev-merged', rev_id='rev-2a',
233
timestamp=1132586700, timezone=36000,
234
committer='Joe Foo <joe@foo.com>')
235
wt.set_parent_ids(['rev-1', 'rev-2a'])
236
wt.branch.set_last_revision_info(1, 'rev-1')
237
wt.commit('rev-2', rev_id='rev-2b',
238
timestamp=1132586800, timezone=36000,
239
committer='Joe Foo <joe@foo.com>')
240
logfile = self.make_utf8_encoded_stringio()
241
formatter = ShortLogFormatter(to_file=logfile)
244
revspec = RevisionSpec.from_string('1.1.1')
245
rev = revspec.in_history(wtb)
246
self.assertRaises(BzrCommandError, show_log, wtb, lf,
247
start_revision=rev, end_revision=rev)
206
self.addCleanup(wt.unlock)
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)
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)
252
229
def make_commits_with_trailing_newlines(wt):
253
"""Helper method for LogFormatter tests"""
230
"""Helper method for LogFormatter tests"""
256
233
open('a', 'wb').write('hello moto\n')
310
287
1 Joe Foo\t2005-11-21
311
288
simple log message
315
293
def test_short_log_with_merges(self):
316
294
wt = self.make_branch_and_memory_tree('.')
320
wt.commit('rev-1', rev_id='rev-1',
321
timestamp=1132586655, timezone=36000,
322
committer='Joe Foo <joe@foo.com>')
323
wt.commit('rev-merged', rev_id='rev-2a',
324
timestamp=1132586700, timezone=36000,
325
committer='Joe Foo <joe@foo.com>')
326
wt.set_parent_ids(['rev-1', 'rev-2a'])
327
wt.branch.set_last_revision_info(1, 'rev-1')
328
wt.commit('rev-2', rev_id='rev-2b',
329
timestamp=1132586800, timezone=36000,
330
committer='Joe Foo <joe@foo.com>')
331
logfile = self.make_utf8_encoded_stringio()
332
formatter = ShortLogFormatter(to_file=logfile)
333
show_log(wt.branch, formatter)
334
self.assertEqualDiff(logfile.getvalue(), """\
296
self.addCleanup(wt.unlock)
298
wt.commit('rev-1', rev_id='rev-1',
299
timestamp=1132586655, timezone=36000,
300
committer='Joe Foo <joe@foo.com>')
301
wt.commit('rev-merged', rev_id='rev-2a',
302
timestamp=1132586700, timezone=36000,
303
committer='Joe Foo <joe@foo.com>')
304
wt.set_parent_ids(['rev-1', 'rev-2a'])
305
wt.branch.set_last_revision_info(1, 'rev-1')
306
wt.commit('rev-2', rev_id='rev-2b',
307
timestamp=1132586800, timezone=36000,
308
committer='Joe Foo <joe@foo.com>')
309
logfile = self.make_utf8_encoded_stringio()
310
formatter = log.ShortLogFormatter(to_file=logfile)
311
log.show_log(wt.branch, formatter)
312
self.assertEqualDiff("""\
335
313
2 Joe Foo\t2005-11-22 [merge]
338
316
1 Joe Foo\t2005-11-22
345
322
def test_short_log_single_merge_revision(self):
346
323
wt = self.make_branch_and_memory_tree('.')
350
wt.commit('rev-1', rev_id='rev-1',
351
timestamp=1132586655, timezone=36000,
352
committer='Joe Foo <joe@foo.com>')
353
wt.commit('rev-merged', rev_id='rev-2a',
354
timestamp=1132586700, timezone=36000,
355
committer='Joe Foo <joe@foo.com>')
356
wt.set_parent_ids(['rev-1', 'rev-2a'])
357
wt.branch.set_last_revision_info(1, 'rev-1')
358
wt.commit('rev-2', rev_id='rev-2b',
359
timestamp=1132586800, timezone=36000,
360
committer='Joe Foo <joe@foo.com>')
361
logfile = self.make_utf8_encoded_stringio()
362
formatter = ShortLogFormatter(to_file=logfile)
363
revspec = RevisionSpec.from_string('1.1.1')
365
rev = revspec.in_history(wtb)
366
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
367
self.assertEqualDiff(logfile.getvalue(), """\
325
self.addCleanup(wt.unlock)
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')
342
rev = revspec.in_history(wtb)
343
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
344
self.assertEqualDiff("""\
368
345
1.1.1 Joe Foo\t2005-11-22
376
352
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
699
681
committer='Line-Log-Formatter Tester <test@line.log>')
700
682
logfile = file('out.tmp', 'w+')
701
formatter = LineLogFormatter(to_file=logfile)
702
show_log(b, formatter)
683
formatter = log.LineLogFormatter(to_file=logfile)
684
log.show_log(b, formatter)
705
687
log_contents = logfile.read()
706
self.assertEqualDiff(log_contents,
707
'1: Line-Log-Formatte... 2005-11-23 add a\n')
688
self.assertEqualDiff('1: Line-Log-Formatte... 2005-11-23 add a\n',
709
691
def test_trailing_newlines(self):
710
692
wt = self.make_branch_and_tree('.')
711
693
b = make_commits_with_trailing_newlines(wt)
712
694
sio = self.make_utf8_encoded_stringio()
713
lf = LineLogFormatter(to_file=sio)
715
self.assertEqualDiff(sio.getvalue(), """\
695
lf = log.LineLogFormatter(to_file=sio)
697
self.assertEqualDiff("""\
716
698
3: Joe Foo 2005-11-21 single line with trailing newline
717
699
2: Joe Bar 2005-11-21 multiline
718
700
1: Joe Foo 2005-11-21 simple log message
721
704
def test_line_log_single_merge_revision(self):
722
705
wt = self.make_branch_and_memory_tree('.')
726
wt.commit('rev-1', rev_id='rev-1',
727
timestamp=1132586655, timezone=36000,
728
committer='Joe Foo <joe@foo.com>')
729
wt.commit('rev-merged', rev_id='rev-2a',
730
timestamp=1132586700, timezone=36000,
731
committer='Joe Foo <joe@foo.com>')
732
wt.set_parent_ids(['rev-1', 'rev-2a'])
733
wt.branch.set_last_revision_info(1, 'rev-1')
734
wt.commit('rev-2', rev_id='rev-2b',
735
timestamp=1132586800, timezone=36000,
736
committer='Joe Foo <joe@foo.com>')
737
logfile = self.make_utf8_encoded_stringio()
738
formatter = LineLogFormatter(to_file=logfile)
739
revspec = RevisionSpec.from_string('1.1.1')
741
rev = revspec.in_history(wtb)
742
show_log(wtb, formatter, start_revision=rev, end_revision=rev)
743
self.assertEqualDiff(logfile.getvalue(), """\
707
self.addCleanup(wt.unlock)
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')
724
rev = revspec.in_history(wtb)
725
log.show_log(wtb, formatter, start_revision=rev, end_revision=rev)
726
self.assertEqualDiff("""\
744
727
1.1.1: Joe Foo 2005-11-22 rev-merged
751
class TestGetViewRevisions(TestCaseWithTransport):
733
class TestGetViewRevisions(tests.TestCaseWithTransport):
753
735
def make_tree_with_commits(self):
754
736
"""Create a tree with well-known revision ids"""
831
823
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
833
825
self.addCleanup(wt.unlock)
834
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
836
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
837
('4b', '4', 0), ('4a', '3.1.1', 1)],
839
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
840
'forward', include_merges=False))
841
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
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)],
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),
845
838
def test_get_view_revisions_merge_reverse(self):
846
839
"""Test get_view_revisions in reverse when there are merges"""
847
840
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
849
842
self.addCleanup(wt.unlock)
850
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
843
revisions = list(log.get_view_revisions(
844
mainline_revs, rev_nos, wt.branch, 'reverse'))
852
845
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
853
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
855
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
856
'reverse', include_merges=False))
846
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
848
revisions = list(log.get_view_revisions(
849
mainline_revs, rev_nos, wt.branch, 'reverse',
850
include_merges=False))
857
851
self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
861
855
def test_get_view_revisions_merge2(self):
862
856
"""Test get_view_revisions when there are merges"""
863
857
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
865
859
self.addCleanup(wt.unlock)
866
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
860
revisions = list(log.get_view_revisions(
861
mainline_revs, rev_nos, wt.branch, 'forward'))
868
862
expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
869
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
863
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
871
865
self.assertEqual(expected, revisions)
872
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
873
'forward', include_merges=False))
866
revisions = list(log.get_view_revisions(
867
mainline_revs, rev_nos, wt.branch, 'forward',
868
include_merges=False))
874
869
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
879
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
874
def test_file_id_for_range(self):
875
mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
877
self.addCleanup(wt.unlock)
879
def rev_from_rev_id(revid, branch):
880
revspec = revisionspec.RevisionSpec.from_string('revid:%s' % revid)
881
return revspec.in_history(branch)
883
def view_revs(start_rev, end_rev, file_id, direction):
884
revs = log.calculate_view_revisions(
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
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'))
907
class TestGetRevisionsTouchingFileID(tests.TestCaseWithTransport):
881
909
def create_tree_with_single_merge(self):
882
910
"""Create a branch with a moderate layout.
932
960
tree.commit('D', rev_id='D')
934
962
# Switch to a read lock for this tree.
935
# We still have addCleanup(unlock)
963
# We still have an addCleanup(tree.unlock) pending
968
def check_delta(self, delta, **kw):
969
"""Check the filenames touched by a delta are as expected.
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).
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)
940
981
def test_tree_with_single_merge(self):
941
982
"""Make sure the tree layout is correct."""
942
983
tree = self.create_tree_with_single_merge()
943
984
rev_A_tree = tree.branch.repository.revision_tree('A')
944
985
rev_B_tree = tree.branch.repository.revision_tree('B')
946
f1_changed = (u'f1', 'f1-id', 'file', True, False)
947
f2_changed = (u'f2', 'f2-id', 'file', True, False)
948
f3_changed = (u'f3', 'f3-id', 'file', True, False)
950
delta = rev_B_tree.changes_from(rev_A_tree)
951
self.assertEqual([f1_changed, f3_changed], delta.modified)
952
self.assertEqual([], delta.renamed)
953
self.assertEqual([], delta.added)
954
self.assertEqual([], delta.removed)
956
986
rev_C_tree = tree.branch.repository.revision_tree('C')
957
delta = rev_C_tree.changes_from(rev_A_tree)
958
self.assertEqual([f2_changed, f3_changed], delta.modified)
959
self.assertEqual([], delta.renamed)
960
self.assertEqual([], delta.added)
961
self.assertEqual([], delta.removed)
963
987
rev_D_tree = tree.branch.repository.revision_tree('D')
964
delta = rev_D_tree.changes_from(rev_B_tree)
965
self.assertEqual([f2_changed, f3_changed], delta.modified)
966
self.assertEqual([], delta.renamed)
967
self.assertEqual([], delta.added)
968
self.assertEqual([], delta.removed)
970
delta = rev_D_tree.changes_from(rev_C_tree)
971
self.assertEqual([f1_changed, f3_changed], delta.modified)
972
self.assertEqual([], delta.renamed)
973
self.assertEqual([], delta.added)
974
self.assertEqual([], delta.removed)
989
self.check_delta(rev_B_tree.changes_from(rev_A_tree),
990
modified=['f1', 'f3'])
992
self.check_delta(rev_C_tree.changes_from(rev_A_tree),
993
modified=['f2', 'f3'])
995
self.check_delta(rev_D_tree.changes_from(rev_B_tree),
996
modified=['f2', 'f3'])
998
self.check_delta(rev_D_tree.changes_from(rev_C_tree),
999
modified=['f1', 'f3'])
976
1001
def assertAllRevisionsForFileID(self, tree, file_id, revisions):
977
"""Make sure _filter_revisions_touching_file_id returns the right values.
1002
"""Ensure _filter_revisions_touching_file_id returns the right values.
979
1004
Get the return value from _filter_revisions_touching_file_id and make
980
1005
sure they are correct.
982
# The api for _get_revisions_touching_file_id is a little crazy,
1007
# The api for _filter_revisions_touching_file_id is a little crazy.
983
1008
# So we do the setup here.
984
1009
mainline = tree.branch.revision_history()
985
1010
mainline.insert(0, None)
1070
1095
self.assertEqual('jsmith@example.com', lf.short_author(rev))
1071
1096
rev.properties['author'] = 'John Smith jsmith@example.com'
1072
1097
self.assertEqual('John Smith', lf.short_author(rev))
1100
class TestReverseByDepth(tests.TestCase):
1101
"""Test reverse_by_depth behavior.
1103
This is used to present revisions in forward (oldest first) order in a nice
1106
The tests use lighter revision description to ease reading.
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.
1115
Tests use (revno, depth) whil the API expects (revid, revno, depth).
1116
Since the revid is arbitrary, we just duplicate revno
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))
1124
def test_mainline_revisions(self):
1125
self.assertReversed([( '1', 0), ('2', 0)],
1126
[('2', 0), ('1', 0)])
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.
1134
Requesting revisions touching a file can produce "holes" in the depths.
1136
self.assertReversed([('1', 0), ('2', 0), ('1.1', 2), ('1.2', 2),],
1137
[('2', 0), ('1.2', 2), ('1.1', 2), ('1', 0),])
1139
def test_merged_without_child_revisions(self):
1140
"""Test irregular layout.
1142
Revision ranges can produce "holes" in the depths.
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),])