~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_log.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-06-20 00:22:13 UTC
  • mfrom: (2537.1.1 bzr.ab.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20070620002213-fvt1s1yu2iujulio
Fix documentation of BzrError (Simó)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
17
import os
18
18
from cStringIO import StringIO
19
19
 
20
 
from bzrlib.selftest import BzrTestBase, TestCaseInTempDir
21
 
from bzrlib.log import LogFormatter, show_log, LongLogFormatter
 
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)
22
29
from bzrlib.branch import Branch
23
30
from bzrlib.errors import InvalidRevisionNumber
24
31
 
25
 
class _LogEntry(object):
26
 
    # should probably move into bzrlib.log?
27
 
    pass
28
 
 
29
32
 
30
33
class LogCatcher(LogFormatter):
31
34
    """Pull log messages into list rather than displaying them.
36
39
 
37
40
    We should also test the LogFormatter.
38
41
    """
 
42
 
 
43
    supports_delta = True
 
44
 
39
45
    def __init__(self):
40
46
        super(LogCatcher, self).__init__(to_file=None)
41
47
        self.logs = []
42
 
        
43
 
        
44
 
    def show(self, revno, rev, delta):
45
 
        le = _LogEntry()
46
 
        le.revno = revno
47
 
        le.rev = rev
48
 
        le.delta = delta
49
 
        self.logs.append(le)
50
 
 
51
 
 
52
 
class SimpleLogTest(TestCaseInTempDir):
 
48
 
 
49
    def log_revision(self, revision):
 
50
        self.logs.append(revision)
 
51
 
 
52
 
 
53
class SimpleLogTest(TestCaseWithTransport):
53
54
 
54
55
    def checkDelta(self, delta, **kw):
55
56
        """Check the filenames touched by a delta are as expected."""
57
58
            expected = kw.get(n, [])
58
59
 
59
60
            # tests are written with unix paths; fix them up for windows
60
 
            if os.sep != '/':
61
 
                expected = [x.replace('/', os.sep) for x in expected]
 
61
            #if os.sep != '/':
 
62
            #    expected = [x.replace('/', os.sep) for x in expected]
62
63
 
63
64
            # strip out only the path components
64
65
            got = [x[0] for x in getattr(delta, n)]
65
66
            self.assertEquals(expected, got)
66
67
 
67
68
    def test_cur_revno(self):
68
 
        b = Branch('.', init=True)
 
69
        wt = self.make_branch_and_tree('.')
 
70
        b = wt.branch
69
71
 
70
72
        lf = LogCatcher()
71
 
        b.commit('empty commit')
 
73
        wt.commit('empty commit')
72
74
        show_log(b, lf, verbose=True, start_revision=1, end_revision=1)
73
75
        self.assertRaises(InvalidRevisionNumber, show_log, b, lf,
74
76
                          start_revision=2, end_revision=1) 
86
88
    def test_simple_log(self):
87
89
        eq = self.assertEquals
88
90
        
89
 
        b = Branch('.', init=True)
 
91
        wt = self.make_branch_and_tree('.')
 
92
        b = wt.branch
90
93
 
91
94
        lf = LogCatcher()
92
95
        show_log(b, lf)
93
96
        # no entries yet
94
97
        eq(lf.logs, [])
95
98
 
96
 
 
97
 
        b.commit('empty commit')
 
99
        wt.commit('empty commit')
98
100
        lf = LogCatcher()
99
101
        show_log(b, lf, verbose=True)
100
102
        eq(len(lf.logs), 1)
101
 
        eq(lf.logs[0].revno, 1)
 
103
        eq(lf.logs[0].revno, '1')
102
104
        eq(lf.logs[0].rev.message, 'empty commit')
103
105
        d = lf.logs[0].delta
104
106
        self.log('log delta: %r' % d)
105
107
        self.checkDelta(d)
106
108
 
107
 
 
108
109
        self.build_tree(['hello'])
109
 
        b.add('hello')
110
 
        b.commit('add one file')
 
110
        wt.add('hello')
 
111
        wt.commit('add one file')
111
112
 
112
113
        lf = StringIO()
113
114
        # log using regular thing
122
123
        eq(len(lf.logs), 2)
123
124
        self.log('log entries:')
124
125
        for logentry in lf.logs:
125
 
            self.log('%4d %s' % (logentry.revno, logentry.rev.message))
 
126
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
126
127
        
127
128
        # first one is most recent
128
129
        logentry = lf.logs[0]
129
 
        eq(logentry.revno, 2)
 
130
        eq(logentry.revno, '2')
130
131
        eq(logentry.rev.message, 'add one file')
131
132
        d = logentry.delta
132
133
        self.log('log 2 delta: %r' % d)
133
134
        # self.checkDelta(d, added=['hello'])
134
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 test_short_log_with_merges(self):
 
284
        wt = self.make_branch_and_memory_tree('.')
 
285
        wt.lock_write()
 
286
        try:
 
287
            wt.add('')
 
288
            wt.commit('rev-1', rev_id='rev-1',
 
289
                      timestamp=1132586655, timezone=36000,
 
290
                      committer='Joe Foo <joe@foo.com>')
 
291
            wt.commit('rev-merged', rev_id='rev-2a',
 
292
                      timestamp=1132586700, timezone=36000,
 
293
                      committer='Joe Foo <joe@foo.com>')
 
294
            wt.set_parent_ids(['rev-1', 'rev-2a'])
 
295
            wt.branch.set_last_revision_info(1, 'rev-1')
 
296
            wt.commit('rev-2', rev_id='rev-2b',
 
297
                      timestamp=1132586800, timezone=36000,
 
298
                      committer='Joe Foo <joe@foo.com>')
 
299
            logfile = StringIO()
 
300
            formatter = ShortLogFormatter(to_file=logfile)
 
301
            show_log(wt.branch, formatter)
 
302
            logfile.flush()
 
303
            self.assertEqualDiff("""\
 
304
    2 Joe Foo\t2005-11-22 [merge]
 
305
      rev-2
 
306
 
 
307
    1 Joe Foo\t2005-11-22
 
308
      rev-1
 
309
 
 
310
""", logfile.getvalue())
 
311
        finally:
 
312
            wt.unlock()
 
313
 
 
314
    def make_tree_with_commits(self):
 
315
        """Create a tree with well-known revision ids"""
 
316
        wt = self.make_branch_and_tree('tree1')
 
317
        wt.commit('commit one', rev_id='1')
 
318
        wt.commit('commit two', rev_id='2')
 
319
        wt.commit('commit three', rev_id='3')
 
320
        mainline_revs = [None, '1', '2', '3']
 
321
        rev_nos = {'1': 1, '2': 2, '3': 3}
 
322
        return mainline_revs, rev_nos, wt
 
323
 
 
324
    def make_tree_with_merges(self):
 
325
        """Create a tree with well-known revision ids and a merge"""
 
326
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
327
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
328
        tree2.commit('four-a', rev_id='4a')
 
329
        wt.merge_from_branch(tree2.branch)
 
330
        wt.commit('four-b', rev_id='4b')
 
331
        mainline_revs.append('4b')
 
332
        rev_nos['4b'] = 4
 
333
        # 4a: 3.1.1
 
334
        return mainline_revs, rev_nos, wt
 
335
 
 
336
    def make_tree_with_many_merges(self):
 
337
        """Create a tree with well-known revision ids"""
 
338
        wt = self.make_branch_and_tree('tree1')
 
339
        wt.commit('commit one', rev_id='1')
 
340
        wt.commit('commit two', rev_id='2')
 
341
        tree3 = wt.bzrdir.sprout('tree3').open_workingtree()
 
342
        tree3.commit('commit three a', rev_id='3a')
 
343
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
 
344
        tree2.merge_from_branch(tree3.branch)
 
345
        tree2.commit('commit three b', rev_id='3b')
 
346
        wt.merge_from_branch(tree2.branch)
 
347
        wt.commit('commit three c', rev_id='3c')
 
348
        tree2.commit('four-a', rev_id='4a')
 
349
        wt.merge_from_branch(tree2.branch)
 
350
        wt.commit('four-b', rev_id='4b')
 
351
        mainline_revs = [None, '1', '2', '3c', '4b']
 
352
        rev_nos = {'1':1, '2':2, '3c': 3, '4b':4}
 
353
        full_rev_nos_for_reference = {
 
354
            '1': '1',
 
355
            '2': '2',
 
356
            '3a': '2.2.1', #first commit tree 3
 
357
            '3b': '2.1.1', # first commit tree 2
 
358
            '3c': '3', #merges 3b to main
 
359
            '4a': '2.1.2', # second commit tree 2
 
360
            '4b': '4', # merges 4a to main
 
361
            }
 
362
        return mainline_revs, rev_nos, wt
 
363
 
 
364
    def test_get_view_revisions_forward(self):
 
365
        """Test the get_view_revisions method"""
 
366
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
367
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
368
                                            'forward'))
 
369
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
 
370
            revisions)
 
371
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
372
                                             'forward', include_merges=False))
 
373
        self.assertEqual(revisions, revisions2)
 
374
 
 
375
    def test_get_view_revisions_reverse(self):
 
376
        """Test the get_view_revisions with reverse"""
 
377
        mainline_revs, rev_nos, wt = self.make_tree_with_commits()
 
378
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
379
                                            'reverse'))
 
380
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
 
381
            revisions)
 
382
        revisions2 = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
383
                                             'reverse', include_merges=False))
 
384
        self.assertEqual(revisions, revisions2)
 
385
 
 
386
    def test_get_view_revisions_merge(self):
 
387
        """Test get_view_revisions when there are merges"""
 
388
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
389
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
390
                                            'forward'))
 
391
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
392
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
 
393
            revisions)
 
394
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
395
                                             'forward', include_merges=False))
 
396
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
 
397
            ('4b', '4', 0)],
 
398
            revisions)
 
399
 
 
400
    def test_get_view_revisions_merge_reverse(self):
 
401
        """Test get_view_revisions in reverse when there are merges"""
 
402
        mainline_revs, rev_nos, wt = self.make_tree_with_merges()
 
403
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
404
                                            'reverse'))
 
405
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
 
406
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
 
407
            revisions)
 
408
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
409
                                             'reverse', include_merges=False))
 
410
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
 
411
            ('1', '1', 0)],
 
412
            revisions)
 
413
 
 
414
    def test_get_view_revisions_merge2(self):
 
415
        """Test get_view_revisions when there are merges"""
 
416
        mainline_revs, rev_nos, wt = self.make_tree_with_many_merges()
 
417
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
418
                                            'forward'))
 
419
        expected = [('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
420
            ('3a', '2.2.1', 1), ('3b', '2.1.1', 1), ('4b', '4', 0),
 
421
            ('4a', '2.1.2', 1)]
 
422
        self.assertEqual(expected, revisions)
 
423
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
 
424
                                             'forward', include_merges=False))
 
425
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
 
426
            ('4b', '4', 0)],
 
427
            revisions)
 
428
 
 
429
 
 
430
class TestGetRevisionsTouchingFileID(TestCaseWithTransport):
 
431
 
 
432
    def create_tree_with_single_merge(self):
 
433
        """Create a branch with a moderate layout.
 
434
 
 
435
        The revision graph looks like:
 
436
 
 
437
           A
 
438
           |\
 
439
           B C
 
440
           |/
 
441
           D
 
442
 
 
443
        In this graph, A introduced files f1 and f2 and f3.
 
444
        B modifies f1 and f3, and C modifies f2 and f3.
 
445
        D merges the changes from B and C and resolves the conflict for f3.
 
446
        """
 
447
        # TODO: jam 20070218 This seems like it could really be done
 
448
        #       with make_branch_and_memory_tree() if we could just
 
449
        #       create the content of those files.
 
450
        # TODO: jam 20070218 Another alternative is that we would really
 
451
        #       like to only create this tree 1 time for all tests that
 
452
        #       use it. Since 'log' only uses the tree in a readonly
 
453
        #       fashion, it seems a shame to regenerate an identical
 
454
        #       tree for each test.
 
455
        tree = self.make_branch_and_tree('tree')
 
456
        tree.lock_write()
 
457
        self.addCleanup(tree.unlock)
 
458
 
 
459
        self.build_tree_contents([('tree/f1', 'A\n'),
 
460
                                  ('tree/f2', 'A\n'),
 
461
                                  ('tree/f3', 'A\n'),
 
462
                                 ])
 
463
        tree.add(['f1', 'f2', 'f3'], ['f1-id', 'f2-id', 'f3-id'])
 
464
        tree.commit('A', rev_id='A')
 
465
 
 
466
        self.build_tree_contents([('tree/f2', 'A\nC\n'),
 
467
                                  ('tree/f3', 'A\nC\n'),
 
468
                                 ])
 
469
        tree.commit('C', rev_id='C')
 
470
        # Revert back to A to build the other history.
 
471
        tree.set_last_revision('A')
 
472
        tree.branch.set_last_revision_info(1, 'A')
 
473
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
474
                                  ('tree/f2', 'A\n'),
 
475
                                  ('tree/f3', 'A\nB\n'),
 
476
                                 ])
 
477
        tree.commit('B', rev_id='B')
 
478
        tree.set_parent_ids(['B', 'C'])
 
479
        self.build_tree_contents([('tree/f1', 'A\nB\n'),
 
480
                                  ('tree/f2', 'A\nC\n'),
 
481
                                  ('tree/f3', 'A\nB\nC\n'),
 
482
                                 ])
 
483
        tree.commit('D', rev_id='D')
 
484
 
 
485
        # Switch to a read lock for this tree.
 
486
        # We still have addCleanup(unlock)
 
487
        tree.unlock()
 
488
        tree.lock_read()
 
489
        return tree
 
490
 
 
491
    def test_tree_with_single_merge(self):
 
492
        """Make sure the tree layout is correct."""
 
493
        tree = self.create_tree_with_single_merge()
 
494
        rev_A_tree = tree.branch.repository.revision_tree('A')
 
495
        rev_B_tree = tree.branch.repository.revision_tree('B')
 
496
 
 
497
        f1_changed = (u'f1', 'f1-id', 'file', True, False)
 
498
        f2_changed = (u'f2', 'f2-id', 'file', True, False)
 
499
        f3_changed = (u'f3', 'f3-id', 'file', True, False)
 
500
 
 
501
        delta = rev_B_tree.changes_from(rev_A_tree)
 
502
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
503
        self.assertEqual([], delta.renamed)
 
504
        self.assertEqual([], delta.added)
 
505
        self.assertEqual([], delta.removed)
 
506
 
 
507
        rev_C_tree = tree.branch.repository.revision_tree('C')
 
508
        delta = rev_C_tree.changes_from(rev_A_tree)
 
509
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
510
        self.assertEqual([], delta.renamed)
 
511
        self.assertEqual([], delta.added)
 
512
        self.assertEqual([], delta.removed)
 
513
 
 
514
        rev_D_tree = tree.branch.repository.revision_tree('D')
 
515
        delta = rev_D_tree.changes_from(rev_B_tree)
 
516
        self.assertEqual([f2_changed, f3_changed], delta.modified)
 
517
        self.assertEqual([], delta.renamed)
 
518
        self.assertEqual([], delta.added)
 
519
        self.assertEqual([], delta.removed)
 
520
 
 
521
        delta = rev_D_tree.changes_from(rev_C_tree)
 
522
        self.assertEqual([f1_changed, f3_changed], delta.modified)
 
523
        self.assertEqual([], delta.renamed)
 
524
        self.assertEqual([], delta.added)
 
525
        self.assertEqual([], delta.removed)
 
526
 
 
527
    def assertAllRevisionsForFileID(self, tree, file_id, revisions):
 
528
        """Make sure _get_revisions_touching_file_id returns the right values.
 
529
 
 
530
        Get the return value from _get_revisions_touching_file_id and make
 
531
        sure they are correct.
 
532
        """
 
533
        # The api for _get_revisions_touching_file_id is a little crazy,
 
534
        # So we do the setup here.
 
535
        mainline = tree.branch.revision_history()
 
536
        mainline.insert(0, None)
 
537
        revnos = dict((rev, idx+1) for idx, rev in enumerate(mainline))
 
538
        view_revs_iter = log.get_view_revisions(mainline, revnos, tree.branch,
 
539
                                                'reverse', True)
 
540
        actual_revs = log._get_revisions_touching_file_id(tree.branch, file_id,
 
541
                                                          mainline,
 
542
                                                          view_revs_iter)
 
543
        self.assertEqual(revisions, [r for r, revno, depth in actual_revs])
 
544
 
 
545
    def test_file_id_f1(self):
 
546
        tree = self.create_tree_with_single_merge()
 
547
        # f1 should be marked as modified by revisions A and B
 
548
        self.assertAllRevisionsForFileID(tree, 'f1-id', ['B', 'A'])
 
549
 
 
550
    def test_file_id_f2(self):
 
551
        tree = self.create_tree_with_single_merge()
 
552
        # f2 should be marked as modified by revisions A, C, and D
 
553
        # because D merged the changes from C.
 
554
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])
 
555
 
 
556
    def test_file_id_f3(self):
 
557
        tree = self.create_tree_with_single_merge()
 
558
        # f3 should be marked as modified by revisions A, B, C, and D
 
559
        self.assertAllRevisionsForFileID(tree, 'f2-id', ['D', 'C', 'A'])