~bzr-pqm/bzr/bzr.dev

2359.1.6 by John Arbash Meinel
Create a helper tree which has a semi-interesting history.
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1685.1.80 by Wouter van Heyst
more code cleanup
2
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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.
1685.1.80 by Wouter van Heyst
more code cleanup
7
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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.
1685.1.80 by Wouter van Heyst
more code cleanup
12
#
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
18
from cStringIO import StringIO
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
19
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
20
from bzrlib import log
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
21
from bzrlib.tests import BzrTestBase, TestCaseWithTransport
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
22
from bzrlib.log import (show_log,
23
                        get_view_revisions,
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
24
                        LogRevision,
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
25
                        LogFormatter,
26
                        LongLogFormatter,
27
                        ShortLogFormatter,
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
28
                        LineLogFormatter)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
29
from bzrlib.branch import Branch
974.1.54 by aaron.bentley at utoronto
Fixed the revno bug in log
30
from bzrlib.errors import InvalidRevisionNumber
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
31
1685.1.69 by Wouter van Heyst
merge bzr.dev 1740
32
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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
    """
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
42
2490.1.2 by John Arbash Meinel
Cleanup according to PEP8 and some other small whitespace fixes
43
    supports_delta = True
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
44
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
45
    def __init__(self):
46
        super(LogCatcher, self).__init__(to_file=None)
47
        self.logs = []
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
48
2466.8.1 by Kent Gibson
Reworked LogFormatter API to simplify extending the attributes of the revision being logged. Added support for begin_log() and end_log() hooks in LogFormatters.
49
    def log_revision(self, revision):
50
        self.logs.append(revision)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
51
52
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
53
class SimpleLogTest(TestCaseWithTransport):
1102 by Martin Pool
- merge test refactoring from robertc
54
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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
1185.31.34 by John Arbash Meinel
Removing instances of os.sep
61
            #if os.sep != '/':
62
            #    expected = [x.replace('/', os.sep) for x in expected]
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
63
64
            # strip out only the path components
65
            got = [x[0] for x in getattr(delta, n)]
66
            self.assertEquals(expected, got)
67
974.1.54 by aaron.bentley at utoronto
Fixed the revno bug in log
68
    def test_cur_revno(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
69
        wt = self.make_branch_and_tree('.')
70
        b = wt.branch
1092.3.4 by Robert Collins
update symlink branch to integration
71
72
        lf = LogCatcher()
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
73
        wt.commit('empty commit')
1092.3.4 by Robert Collins
update symlink branch to integration
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
1102 by Martin Pool
- merge test refactoring from robertc
88
    def test_simple_log(self):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
89
        eq = self.assertEquals
90
        
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
91
        wt = self.make_branch_and_tree('.')
92
        b = wt.branch
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
93
94
        lf = LogCatcher()
95
        show_log(b, lf)
96
        # no entries yet
97
        eq(lf.logs, [])
98
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
99
        wt.commit('empty commit')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
100
        lf = LogCatcher()
101
        show_log(b, lf, verbose=True)
102
        eq(len(lf.logs), 1)
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
103
        eq(lf.logs[0].revno, '1')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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'])
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
110
        wt.add('hello')
111
        wt.commit('add one file')
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
112
113
        lf = StringIO()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
114
        # log using regular thing
1123 by Martin Pool
* move bzr-specific code from testsweet into bzrlib.selftest
115
        show_log(b, LongLogFormatter(lf))
116
        lf.seek(0)
117
        for l in lf.readlines():
118
            self.log(l)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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:
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
126
            self.log('%4s %s' % (logentry.revno, logentry.rev.message))
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
127
        
128
        # first one is most recent
129
        logentry = lf.logs[0]
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
130
        eq(logentry.revno, '2')
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
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
        
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
136
        # commit a log message with control characters
137
        msg = "All 8-bit chars: " +  ''.join([unichr(x) for x in range(256)])
1393.4.2 by Harald Meland
Cleanup + better test of commit-msg control character escape code.
138
        self.log("original commit message: %r", msg)
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
139
        wt.commit(msg)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
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))
1393.4.2 by Harald Meland
Cleanup + better test of commit-msg control character escape code.
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)
1534.4.36 by Robert Collins
Finish deprecating Branch.working_tree()
154
        wt.commit(msg)
1393.4.2 by Harald Meland
Cleanup + better test of commit-msg control character escape code.
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)
1185.31.22 by John Arbash Meinel
[merge] bzr.dev
160
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
161
    def test_trailing_newlines(self):
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
162
        wt = self.make_branch_and_tree('.')
163
        b = wt.branch
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
164
        b.nick='test'
165
        open('a', 'wb').write('hello moto\n')
1185.33.54 by Martin Pool
[merge] test renames and other fixes (John)
166
        wt.add('a')
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
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')
1185.33.54 by Martin Pool
[merge] test renames and other fixes (John)
171
        wt.add('b')
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
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')
1185.33.54 by Martin Pool
[merge] test renames and other fixes (John)
177
        wt.add('c')
1185.31.21 by John Arbash Meinel
Added test for log formatting, found bug when redirecting short logs to a file instead of stdout.
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
        
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
228
    def test_verbose_log(self):
229
        """Verbose log includes changed files
230
        
231
        bug #4676
232
        """
1534.4.26 by Robert Collins
Move working tree initialisation out from Branch.initialize, deprecated Branch.initialize to Branch.create.
233
        wt = self.make_branch_and_tree('.')
234
        b = wt.branch
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
235
        self.build_tree(['a'])
1185.33.45 by Martin Pool
[merge] refactoring of branch vs working tree, etc (robertc)
236
        wt.add('a')
1185.33.41 by Martin Pool
Fix regression of 'bzr log -v' - it wasn't showing changed files at all. (#4676)
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
''')
1185.85.4 by John Arbash Meinel
currently broken, trying to fix things up.
260
1704.2.20 by Martin Pool
log --line shows revision numbers (Alexander)
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')
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
282
2483.2.1 by John Arbash Meinel
Add a test with a merged revision
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("""\
2483.2.2 by John Arbash Meinel
Add [merge] after the timestamp for revisions with merges.
304
    2 Joe Foo\t2005-11-22 [merge]
2483.2.1 by John Arbash Meinel
Add a test with a merged revision
305
      rev-2
306
307
    1 Joe Foo\t2005-11-22
308
      rev-1
309
310
""", logfile.getvalue())
311
        finally:
312
            wt.unlock()
313
1756.2.22 by Aaron Bentley
Apply review comments
314
    def make_tree_with_commits(self):
315
        """Create a tree with well-known revision ids"""
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
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']
1756.2.22 by Aaron Bentley
Apply review comments
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()
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
327
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
328
        tree2.commit('four-a', rev_id='4a')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
329
        wt.merge_from_branch(tree2.branch)
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
330
        wt.commit('four-b', rev_id='4b')
331
        mainline_revs.append('4b')
1756.2.22 by Aaron Bentley
Apply review comments
332
        rev_nos['4b'] = 4
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
333
        # 4a: 3.1.1
1756.2.22 by Aaron Bentley
Apply review comments
334
        return mainline_revs, rev_nos, wt
335
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
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')
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
343
        tree2 = wt.bzrdir.sprout('tree2').open_workingtree()
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
344
        tree2.merge_from_branch(tree3.branch)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
345
        tree2.commit('commit three b', rev_id='3b')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
346
        wt.merge_from_branch(tree2.branch)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
347
        wt.commit('commit three c', rev_id='3c')
348
        tree2.commit('four-a', rev_id='4a')
1979.2.1 by Robert Collins
(robertc) adds a convenience method "merge_from_branch" to WorkingTree.
349
        wt.merge_from_branch(tree2.branch)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
350
        wt.commit('four-b', rev_id='4b')
351
        mainline_revs = [None, '1', '2', '3c', '4b']
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
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
            }
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
362
        return mainline_revs, rev_nos, wt
363
1756.2.22 by Aaron Bentley
Apply review comments
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'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
369
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0)],
370
            revisions)
1756.2.22 by Aaron Bentley
Apply review comments
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'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
380
        self.assertEqual([('3', '3', 0), ('2', '2', 0), ('1', '1', 0), ],
381
            revisions)
1756.2.22 by Aaron Bentley
Apply review comments
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()
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
389
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
390
                                            'forward'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
391
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
392
            ('4b', '4', 0), ('4a', '3.1.1', 1)],
393
            revisions)
1756.2.20 by Aaron Bentley
Optimize log formats that don't show merges
394
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
1756.2.22 by Aaron Bentley
Apply review comments
395
                                             'forward', include_merges=False))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
396
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3', '3', 0),
397
            ('4b', '4', 0)],
398
            revisions)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
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'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
405
        self.assertEqual([('4b', '4', 0), ('4a', '3.1.1', 1),
406
            ('3', '3', 0), ('2', '2', 0), ('1', '1', 0)],
407
            revisions)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
408
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
409
                                             'reverse', include_merges=False))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
410
        self.assertEqual([('4b', '4', 0), ('3', '3', 0), ('2', '2', 0),
411
            ('1', '1', 0)],
412
            revisions)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
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'))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
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)
1756.2.24 by Aaron Bentley
Forward sorting shows merges under mainline revision
423
        revisions = list(get_view_revisions(mainline_revs, rev_nos, wt.branch,
424
                                             'forward', include_merges=False))
1988.4.2 by Robert Collins
``bzr log`` Now shows dotted-decimal revision numbers for all revisions,
425
        self.assertEqual([('1', '1', 0), ('2', '2', 0), ('3c', '3', 0),
426
            ('4b', '4', 0)],
427
            revisions)
2359.1.6 by John Arbash Meinel
Create a helper tree which has a semi-interesting history.
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
2359.1.7 by John Arbash Meinel
Create a direct test for _get_revisions_touching_file_id
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'])