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'])
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'])
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')
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))
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)
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)
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'])
201
180
lf = LogCatcher()
202
181
lf.supports_merge_revisions = True
203
show_log(b, lf, verbose=True)
182
log.show_log(b, lf, verbose=True)
184
self.assertEqual(3, len(lf.logs))
205
186
logentry = lf.logs[0]
206
eq(logentry.revno, '2')
207
eq(logentry.rev.message, 'merge child branch')
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'])
210
191
logentry = lf.logs[1]
211
eq(logentry.revno, '1.1.1')
212
eq(logentry.rev.message, 'remove file1 and modify file2')
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'])
215
196
logentry = lf.logs[2]
216
eq(logentry.revno, '1')
217
eq(logentry.rev.message, 'add file1 and file2')
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'])
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('.')
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)
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)
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)
251
229
def make_commits_with_trailing_newlines(wt):
252
"""Helper method for LogFormatter tests"""
230
"""Helper method for LogFormatter tests"""
255
233
open('a', 'wb').write('hello moto\n')
309
287
1 Joe Foo\t2005-11-21
310
288
simple log message
314
293
def test_short_log_with_merges(self):
315
294
wt = self.make_branch_and_memory_tree('.')
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)
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]
337
316
1 Joe Foo\t2005-11-22
344
322
def test_short_log_single_merge_revision(self):
345
323
wt = self.make_branch_and_memory_tree('.')
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')
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)
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("""\
367
345
1.1.1 Joe Foo\t2005-11-22
375
352
class TestLongLogFormatter(TestCaseWithoutPropsHandler):
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)
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',
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)
714
self.assertEqualDiff(sio.getvalue(), """\
695
lf = log.LineLogFormatter(to_file=sio)
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
720
704
def test_line_log_single_merge_revision(self):
721
705
wt = self.make_branch_and_memory_tree('.')
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')
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)
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("""\
743
727
1.1.1: Joe Foo 2005-11-22 rev-merged
750
class TestGetViewRevisions(TestCaseWithTransport):
733
class TestGetViewRevisions(tests.TestCaseWithTransport):
752
735
def make_tree_with_commits(self):
753
736
"""Create a tree with well-known revision ids"""
830
823
mainline_revs, rev_nos, wt = self.make_tree_with_merges()
832
825
self.addCleanup(wt.unlock)
833
revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
835
self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
836
('4b', '4', 0), ('4a', '3.1.1', 1)],
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),
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),
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()
848
842
self.addCleanup(wt.unlock)
849
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'))
851
845
self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
852
('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
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)],
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),
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()
864
859
self.addCleanup(wt.unlock)
865
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'))
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),
863
('3a', '2.1.1', 1), ('3b', '2.2.1', 1), ('4b', '4', 0),
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),
878
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):
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')
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
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)
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')
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)
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)
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)
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)
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)
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'])
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.
978
1004
Get the return value from _filter_revisions_touching_file_id and make
979
1005
sure they are correct.
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)
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))
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),])