/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Kent Gibson
  • Date: 2007-05-02 11:02:46 UTC
  • mto: (2568.1.2 jam-integration)
  • mto: This revision was merged to the branch mainline in revision 2570.
  • Revision ID: warthog618@gmail.com-20070502110246-05zkgmrv5vm807ms
Long log format reports deltas on merge revisions.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
 
16
 
 
17
import os
 
18
from cStringIO import StringIO
 
19
 
 
20
from bzrlib import log
 
21
from bzrlib.tests import BzrTestBase, TestCaseWithTransport
 
22
from bzrlib.log import (show_log, 
 
23
                        get_view_revisions, 
 
24
                        LogRevision,
 
25
                        LogFormatter, 
 
26
                        LongLogFormatter, 
 
27
                        ShortLogFormatter, 
 
28
                        LineLogFormatter)
 
29
from bzrlib.branch import Branch
 
30
from bzrlib.errors import InvalidRevisionNumber
 
31
 
 
32
 
 
33
class LogCatcher(LogFormatter):
 
34
    """Pull log messages into list rather than displaying them.
 
35
 
 
36
    For ease of testing we save log messages here rather than actually
 
37
    formatting them, so that we can precisely check the result without
 
38
    being too dependent on the exact formatting.
 
39
 
 
40
    We should also test the LogFormatter.
 
41
    """
 
42
 
 
43
    supports_delta = True 
 
44
 
 
45
    def __init__(self):
 
46
        super(LogCatcher, self).__init__(to_file=None)
 
47
        self.logs = []
 
48
 
 
49
    def log_revision(self, revision):
 
50
        self.logs.append(revision)
 
51
 
 
52
 
 
53
class SimpleLogTest(TestCaseWithTransport):
 
54
 
 
55
    def checkDelta(self, delta, **kw):
 
56
        """Check the filenames touched by a delta are as expected."""
 
57
        for n in 'added', 'removed', 'renamed', 'modified', 'unchanged':
 
58
            expected = kw.get(n, [])
 
59
 
 
60
            # tests are written with unix paths; fix them up for windows
 
61
            #if os.sep != '/':
 
62
            #    expected = [x.replace('/', os.sep) for x in expected]
 
63
 
 
64
            # strip out only the path components
 
65
            got = [x[0] for x in getattr(delta, n)]
 
66
            self.assertEquals(expected, got)
 
67
 
 
68
    def test_cur_revno(self):
 
69
        wt = self.make_branch_and_tree('.')
 
70
        b = wt.branch
 
71
 
 
72
        lf = LogCatcher()
 
73
        wt.commit('empty commit')
 
74
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
 
75
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
76
                          start_revision=2, end_revision=1) 
 
77
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
78
                          start_revision=1, end_revision=2) 
 
79
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
80
                          start_revision=0, end_revision=2) 
 
81
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
82
                          start_revision=1, end_revision=0) 
 
83
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
84
                          start_revision=-1, end_revision=1) 
 
85
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
 
86
                          start_revision=1, end_revision=-1) 
 
87
 
 
88
    def test_simple_log(self):
 
89
        eq = self.assertEquals
 
90
        
 
91
        wt = self.make_branch_and_tree('.')
 
92
        b = wt.branch
 
93
 
 
94
        lf = LogCatcher()
 
95
        show_log(b, lf)
 
96
        # no entries yet
 
97
        eq(lf.logs, [])
 
98
 
 
99
        wt.commit('empty commit')
 
100
        lf = LogCatcher()
 
101
        show_log(b, lf, verbose=True)
 
102
        eq(len(lf.logs), 1)
 
103
        eq(lf.logs[0].revno, '1')
 
104
        eq(lf.logs[0].rev.message, 'empty commit')
 
105
        d = lf.logs[0].delta
 
106
        self.log('log delta: %r' % d)
 
107
        self.checkDelta(d)
 
108
 
 
109
        self.build_tree(['hello'])
 
110
        wt.add('hello')
 
111
        wt.commit('add one file')
 
112
 
 
113
        lf = StringIO()
 
114
        # log using regular thing
 
115
        show_log(b, LongLogFormatter(lf))
 
116
        lf.seek(0)
 
117
        for l in lf.readlines():
 
118
            self.log(l)
 
119
 
 
120
        # get log as data structure
 
121
        lf = LogCatcher()
 
122
        show_log(b, lf, verbose=True)
 
123
        eq(len(lf.logs), 2)
 
124
        self.log('log entries:')
 
125
        for logentry in lf.logs:
 
126
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
 
127
        
 
128
        # first one is most recent
 
129
        logentry = lf.logs[0]
 
130
        eq(logentry.revno, '2')
 
131
        eq(logentry.rev.message, 'add one file')
 
132
        d = logentry.delta
 
133
        self.log('log 2 delta: %r' % d)
 
134
        # self.checkDelta(d, added=['hello'])
 
135
        
 
136
        # commit a log message with control characters
 
137
        msg = "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
 
138
        self.log("original commit message: %r", msg)
 
139
        wt.commit(msg)
 
140
        lf = LogCatcher()
 
141
        show_log(b, lf, verbose=True)
 
142
        committed_msg = lf.logs[0].rev.message
 
143
        self.log("escaped commit message: %r", committed_msg)
 
144
        self.assert_(msg != committed_msg)
 
145
        self.assert_(len(committed_msg) > len(msg))
 
146
 
 
147
        # Check that log message with only XML-valid characters isn't
 
148
        # escaped.  As ElementTree apparently does some kind of
 
149
        # newline conversion, neither LF (\x0A) nor CR (\x0D) are
 
150
        # included in the test commit message, even though they are
 
151
        # valid XML 1.0 characters.
 
152
        msg = "\x09" + ''.join([unichr(x) for x in range(0x20, 256)])
 
153
        self.log("original commit message: %r", msg)
 
154
        wt.commit(msg)
 
155
        lf = LogCatcher()
 
156
        show_log(b, lf, verbose=True)
 
157
        committed_msg = lf.logs[0].rev.message
 
158
        self.log("escaped commit message: %r", committed_msg)
 
159
        self.assert_(msg == committed_msg)
 
160
 
 
161
    def test_trailing_newlines(self):
 
162
        wt = self.make_branch_and_tree('.')
 
163
        b = wt.branch
 
164
        b.nick='test'
 
165
        open('a', 'wb').write('hello moto\n')
 
166
        wt.add('a')
 
167
        wt.commit('simple log message', rev_id='a1'
 
168
                , timestamp=1132586655.459960938, timezone=-6*3600
 
169
                , committer='Joe Foo <joe@foo.com>')
 
170
        open('b', 'wb').write('goodbye\n')
 
171
        wt.add('b')
 
172
        wt.commit('multiline\nlog\nmessage\n', rev_id='a2'
 
173
                , timestamp=1132586842.411175966, timezone=-6*3600
 
174
                , committer='Joe Foo <joe@foo.com>')
 
175
 
 
176
        open('c', 'wb').write('just another manic monday\n')
 
177
        wt.add('c')
 
178
        wt.commit('single line with trailing newline\n', rev_id='a3'
 
179
                , timestamp=1132587176.835228920, timezone=-6*3600
 
180
                , committer = 'Joe Foo <joe@foo.com>')
 
181
 
 
182
        sio = StringIO()
 
183
        lf = ShortLogFormatter(to_file=sio)
 
184
        show_log(b, lf)
 
185
        self.assertEquals(sio.getvalue(), """\
 
186
    3 Joe Foo\t2005-11-21
 
187
      single line with trailing newline
 
188
 
 
189
    2 Joe Foo\t2005-11-21
 
190
      multiline
 
191
      log
 
192
      message
 
193
 
 
194
    1 Joe Foo\t2005-11-21
 
195
      simple log message
 
196
 
 
197
""")
 
198
 
 
199
        sio = StringIO()
 
200
        lf = LongLogFormatter(to_file=sio)
 
201
        show_log(b, lf)
 
202
        self.assertEquals(sio.getvalue(), """\
 
203
------------------------------------------------------------
 
204
revno: 3
 
205
committer: Joe Foo <joe@foo.com>
 
206
branch nick: test
 
207
timestamp: Mon 2005-11-21 09:32:56 -0600
 
208
message:
 
209
  single line with trailing newline
 
210
------------------------------------------------------------
 
211
revno: 2
 
212
committer: Joe Foo <joe@foo.com>
 
213
branch nick: test
 
214
timestamp: Mon 2005-11-21 09:27:22 -0600
 
215
message:
 
216
  multiline
 
217
  log
 
218
  message
 
219
------------------------------------------------------------
 
220
revno: 1
 
221
committer: Joe Foo <joe@foo.com>
 
222
branch nick: test
 
223
timestamp: Mon 2005-11-21 09:24:15 -0600
 
224
message:
 
225
  simple log message
 
226
""")
 
227
        
 
228
    def test_verbose_log(self):
 
229
        """Verbose log includes changed files
 
230
        
 
231
        bug #4676
 
232
        """
 
233
        wt = self.make_branch_and_tree('.')
 
234
        b = wt.branch
 
235
        self.build_tree(['a'])
 
236
        wt.add('a')
 
237
        # XXX: why does a longer nick show up?
 
238
        b.nick = 'test_verbose_log'
 
239
        wt.commit(message='add a', 
 
240
                  timestamp=1132711707, 
 
241
                  timezone=36000,
 
242
                  committer='Lorem Ipsum <test@example.com>')
 
243
        logfile = file('out.tmp', 'w+')
 
244
        formatter = LongLogFormatter(to_file=logfile)
 
245
        show_log(b, formatter, verbose=True)
 
246
        logfile.flush()
 
247
        logfile.seek(0)
 
248
        log_contents = logfile.read()
 
249
        self.assertEqualDiff(log_contents, '''\
 
250
------------------------------------------------------------
 
251
revno: 1
 
252
committer: Lorem Ipsum <test@example.com>
 
253
branch nick: test_verbose_log
 
254
timestamp: Wed 2005-11-23 12:08:27 +1000
 
255
message:
 
256
  add a
 
257
added:
 
258
  a
 
259
''')
 
260
 
 
261
    def test_line_log(self):
 
262
        """Line log should show revno
 
263
        
 
264
        bug #5162
 
265
        """
 
266
        wt = self.make_branch_and_tree('.')
 
267
        b = wt.branch
 
268
        self.build_tree(['a'])
 
269
        wt.add('a')
 
270
        b.nick = 'test-line-log'
 
271
        wt.commit(message='add a', 
 
272
                  timestamp=1132711707, 
 
273
                  timezone=36000,
 
274
                  committer='Line-Log-Formatter Tester <test@line.log>')
 
275
        logfile = file('out.tmp', 'w+')
 
276
        formatter = LineLogFormatter(to_file=logfile)
 
277
        show_log(b, formatter)
 
278
        logfile.flush()
 
279
        logfile.seek(0)
 
280
        log_contents = logfile.read()
 
281
        self.assertEqualDiff(log_contents, '1: Line-Log-Formatte... 2005-11-23 add a\n')
 
282
 
 
283
    def make_tree_with_commits(self):
 
284
        """Create a tree with well-known revision ids"""
 
285
        wt = self.make_branch_and_tree('tree1')
 
286
        wt.commit('commit one', rev_id='1')
 
287
        wt.commit('commit two', rev_id='2')
 
288
        wt.commit('commit three', rev_id='3')
 
289
        mainline_revs = [None, '1', '2', '3']
 
290
        rev_nos = {'1': 1, '2': 2, '3': 3}
 
291
        return mainline_revs, rev_nos, wt
 
292
 
 
293
    def make_tree_with_merges(self):
 
294
        """Create a tree with well-known revision ids and a merge"""
 
295
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
296
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
297
        tree2.commit('four-a', rev_id='4a')
 
298
        wt.merge_from_branch(tree2.branch)
 
299
        wt.commit('four-b', rev_id='4b')
 
300
        mainline_revs.append('4b')
 
301
        rev_nos['4b'] = 4
 
302
        # 4a: 3.1.1
 
303
        return mainline_revs, rev_nos, wt
 
304
 
 
305
    def make_tree_with_many_merges(self):
 
306
        """Create a tree with well-known revision ids"""
 
307
        wt = self.make_branch_and_tree('tree1')
 
308
        wt.commit('commit one', rev_id='1')
 
309
        wt.commit('commit two', rev_id='2')
 
310
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
 
311
        tree3.commit('commit three a', rev_id='3a')
 
312
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
313
        tree2.merge_from_branch(tree3.branch)
 
314
        tree2.commit('commit three b', rev_id='3b')
 
315
        wt.merge_from_branch(tree2.branch)
 
316
        wt.commit('commit three c', rev_id='3c')
 
317
        tree2.commit('four-a', rev_id='4a')
 
318
        wt.merge_from_branch(tree2.branch)
 
319
        wt.commit('four-b', rev_id='4b')
 
320
        mainline_revs = [None, '1', '2', '3c', '4b']
 
321
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
 
322
        full_rev_nos_for_reference = {
 
323
            '1': '1',
 
324
            '2': '2',
 
325
            '3a': '2.2.1', #first commit tree 3
 
326
            '3b': '2.1.1', # first commit tree 2
 
327
            '3c': '3', #merges 3b to main
 
328
            '4a': '2.1.2', # second commit tree 2
 
329
            '4b': '4', # merges 4a to main
 
330
            }
 
331
        return mainline_revs, rev_nos, wt
 
332
 
 
333
    def test_get_view_revisions_forward(self):
 
334
        """Test the get_view_revisions method"""
 
335
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
336
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
337
                                            'forward'))
 
338
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
 
339
            revisions)
 
340
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
341
                                             'forward', include_merges=False))
 
342
        self.assertEqual(revisions, revisions2)
 
343
 
 
344
    def test_get_view_revisions_reverse(self):
 
345
        """Test the get_view_revisions with reverse"""
 
346
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
347
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
348
                                            'reverse'))
 
349
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
 
350
            revisions)
 
351
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
352
                                             'reverse', include_merges=False))
 
353
        self.assertEqual(revisions, revisions2)
 
354
 
 
355
    def test_get_view_revisions_merge(self):
 
356
        """Test get_view_revisions when there are merges"""
 
357
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
358
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
359
                                            'forward'))
 
360
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
361
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
362
            revisions)
 
363
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
364
                                             'forward', include_merges=False))
 
365
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
366
            ('4b', '4', 0)],
 
367
            revisions)
 
368
 
 
369
    def test_get_view_revisions_merge_reverse(self):
 
370
        """Test get_view_revisions in reverse when there are merges"""
 
371
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
372
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
373
                                            'reverse'))
 
374
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
 
375
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
376
            revisions)
 
377
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
378
                                             'reverse', include_merges=False))
 
379
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
 
380
            ('1', '1', 0)],
 
381
            revisions)
 
382
 
 
383
    def test_get_view_revisions_merge2(self):
 
384
        """Test get_view_revisions when there are merges"""
 
385
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
 
386
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
387
                                            'forward'))
 
388
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
389
            ('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
 
390
            ('4a', '2.1.2', 1)]
 
391
        self.assertEqual(expected, revisions)
 
392
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
393
                                             'forward', include_merges=False))
 
394
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
395
            ('4b', '4', 0)],
 
396
            revisions)
 
397
 
 
398
 
 
399
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
 
400
 
 
401
    def create_tree_with_single_merge(self):
 
402
        """Create a branch with a moderate layout.
 
403
 
 
404
        The revision graph looks like:
 
405
 
 
406
           A
 
407
           |\
 
408
           B C
 
409
           |/
 
410
           D
 
411
 
 
412
        In this graph, A introduced files f1 and f2 and f3.
 
413
        B modifies f1 and f3, and C modifies f2 and f3.
 
414
        D merges the changes from B and C and resolves the conflict for f3.
 
415
        """
 
416
        # TODO: jam 20070218 This seems like it could really be done
 
417
        #       with make_branch_and_memory_tree() if we could just
 
418
        #       create the content of those files.
 
419
        # TODO: jam 20070218 Another alternative is that we would really
 
420
        #       like to only create this tree 1 time for all tests that
 
421
        #       use it. Since 'log' only uses the tree in a readonly
 
422
        #       fashion, it seems a shame to regenerate an identical
 
423
        #       tree for each test.
 
424
        tree = self.make_branch_and_tree('tree')
 
425
        tree.lock_write()
 
426
        self.addCleanup(tree.unlock)
 
427
 
 
428
        self.build_tree_contents([('tree/f1', 'A\n'),
 
429
                                  ('tree/f2', 'A\n'),
 
430
                                  ('tree/f3', 'A\n'),
 
431
                                 ])
 
432
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
 
433
        tree.commit('A', rev_id='A')
 
434
 
 
435
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
 
436
                                  ('tree/f3', 'A\nC\n'),
 
437
                                 ])
 
438
        tree.commit('C', rev_id='C')
 
439
        # Revert back to A to build the other history.
 
440
        tree.set_last_revision('A')
 
441
        tree.branch.set_last_revision_info(1, 'A')
 
442
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
443
                                  ('tree/f2', 'A\n'),
 
444
                                  ('tree/f3', 'A\nB\n'),
 
445
                                 ])
 
446
        tree.commit('B', rev_id='B')
 
447
        tree.set_parent_ids(['B', 'C'])
 
448
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
449
                                  ('tree/f2', 'A\nC\n'),
 
450
                                  ('tree/f3', 'A\nB\nC\n'),
 
451
                                 ])
 
452
        tree.commit('D', rev_id='D')
 
453
 
 
454
        # Switch to a read lock for this tree.
 
455
        # We still have addCleanup(unlock)
 
456
        tree.unlock()
 
457
        tree.lock_read()
 
458
        return tree
 
459
 
 
460
    def test_tree_with_single_merge(self):
 
461
        """Make sure the tree layout is correct."""
 
462
        tree = self.create_tree_with_single_merge()
 
463
        rev_A_tree = tree.branch.repository.revision_tree('A')
 
464
        rev_B_tree = tree.branch.repository.revision_tree('B')
 
465
 
 
466
        f1_changed = (u'f1', 'f1-id', 'file', True, False)
 
467
        f2_changed = (u'f2', 'f2-id', 'file', True, False)
 
468
        f3_changed = (u'f3', 'f3-id', 'file', True, False)
 
469
 
 
470
        delta = rev_B_tree.changes_from(rev_A_tree)
 
471
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
472
        self.assertEqual([], delta.renamed)
 
473
        self.assertEqual([], delta.added)
 
474
        self.assertEqual([], delta.removed)
 
475
 
 
476
        rev_C_tree = tree.branch.repository.revision_tree('C')
 
477
        delta = rev_C_tree.changes_from(rev_A_tree)
 
478
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
479
        self.assertEqual([], delta.renamed)
 
480
        self.assertEqual([], delta.added)
 
481
        self.assertEqual([], delta.removed)
 
482
 
 
483
        rev_D_tree = tree.branch.repository.revision_tree('D')
 
484
        delta = rev_D_tree.changes_from(rev_B_tree)
 
485
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
486
        self.assertEqual([], delta.renamed)
 
487
        self.assertEqual([], delta.added)
 
488
        self.assertEqual([], delta.removed)
 
489
 
 
490
        delta = rev_D_tree.changes_from(rev_C_tree)
 
491
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
492
        self.assertEqual([], delta.renamed)
 
493
        self.assertEqual([], delta.added)
 
494
        self.assertEqual([], delta.removed)
 
495
 
 
496
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
 
497
        """Make sure _get_revisions_touching_file_id returns the right values.
 
498
 
 
499
        Get the return value from _get_revisions_touching_file_id and make
 
500
        sure they are correct.
 
501
        """
 
502
        # The api for _get_revisions_touching_file_id is a little crazy,
 
503
        # So we do the setup here.
 
504
        mainline = tree.branch.revision_history()
 
505
        mainline.insert(0, None)
 
506
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
 
507
        view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
 
508
                                                'reverse', True)
 
509
        actual_revs = log._get_revisions_touching_file_id(tree.branch, file_id,
 
510
                                                          mainline,
 
511
                                                          view_revs_iter)
 
512
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
 
513
 
 
514
    def test_file_id_f1(self):
 
515
        tree = self.create_tree_with_single_merge()
 
516
        # f1 should be marked as modified by revisions A and B
 
517
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
 
518
 
 
519
    def test_file_id_f2(self):
 
520
        tree = self.create_tree_with_single_merge()
 
521
        # f2 should be marked as modified by revisions A, C, and D
 
522
        # because D merged the changes from C.
 
523
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
524
 
 
525
    def test_file_id_f3(self):
 
526
        tree = self.create_tree_with_single_merge()
 
527
        # f3 should be marked as modified by revisions A, B, C, and D
 
528
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])