~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_revision.py

  • Committer: Patch Queue Manager
  • Date: 2012-10-25 11:13:27 UTC
  • mfrom: (6570.1.6 rubberstamp)
  • Revision ID: pqm@pqm.ubuntu.com-20121025111327-p0ylql0nh9fla0rs
(gz) Set approved revision and vote "Approve" when using lp-propose
 --approve (Jonathan Lange)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# (C) 2005 Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2011 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
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
 
 
17
 
 
18
 
import os
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
19
18
import warnings
20
19
 
21
 
from bzrlib.branch import Branch
22
 
from bzrlib.errors import NoSuchRevision
23
 
from bzrlib.commit import commit
24
 
from bzrlib.graph import Graph
25
 
from bzrlib.revision import (find_present_ancestors, combined_graph,
26
 
                             common_ancestor,
27
 
                             is_ancestor, MultipleRevisionSources)
28
 
from bzrlib.tests import TestCaseWithTransport
29
 
from bzrlib.trace import mutter
30
 
from bzrlib.workingtree import WorkingTree
 
20
from bzrlib import (
 
21
    bugtracker,
 
22
    revision,
 
23
    )
 
24
from bzrlib.errors import (
 
25
    InvalidBugStatus,
 
26
    InvalidLineInBugsProperty,
 
27
    )
 
28
from bzrlib.revision import NULL_REVISION
 
29
from bzrlib.tests import TestCase, TestCaseWithTransport
 
30
from bzrlib.tests.matchers import MatchesAncestry
31
31
 
32
32
# We're allowed to test deprecated interfaces
33
33
warnings.filterwarnings('ignore',
36
36
        r'bzrlib\.tests\.test_revision')
37
37
 
38
38
# XXX: Make this a method of a merge base case
39
 
def make_branches(self):
 
39
def make_branches(self, format=None):
40
40
    """Create two branches
41
41
 
42
42
    branch 1 has 6 commits, branch 2 has 3 commits
44
44
 
45
45
    the object graph is
46
46
    B:     A:
47
 
    a..0   a..0 
 
47
    a..0   a..0
48
48
    a..1   a..1
49
49
    a..2   a..2
50
50
    b..3   a..3 merges b..4
55
55
    so A is missing b6 at the start
56
56
    and B is missing a3, a4, a5
57
57
    """
58
 
    tree1 = self.make_branch_and_tree("branch1")
 
58
    tree1 = self.make_branch_and_tree("branch1", format=format)
59
59
    br1 = tree1.branch
60
 
    
 
60
 
61
61
    tree1.commit("Commit one", rev_id="a@u-0-0")
62
62
    tree1.commit("Commit two", rev_id="a@u-0-1")
63
63
    tree1.commit("Commit three", rev_id="a@u-0-2")
64
64
 
65
 
    tree2 = tree1.bzrdir.clone("branch2").open_workingtree()
 
65
    tree2 = tree1.bzrdir.sprout("branch2").open_workingtree()
66
66
    br2 = tree2.branch
67
67
    tree2.commit("Commit four", rev_id="b@u-0-3")
68
68
    tree2.commit("Commit five", rev_id="b@u-0-4")
69
 
    revisions_2 = br2.revision_history()
70
 
    
71
 
    br1.fetch(br2)
72
 
    tree1.add_pending_merge(revisions_2[4])
73
 
    self.assertEquals(revisions_2[4], 'b@u-0-4')
 
69
    self.assertEquals(br2.last_revision(), 'b@u-0-4')
 
70
 
 
71
    tree1.merge_from_branch(br2)
74
72
    tree1.commit("Commit six", rev_id="a@u-0-3")
75
73
    tree1.commit("Commit seven", rev_id="a@u-0-4")
76
74
    tree2.commit("Commit eight", rev_id="b@u-0-5")
77
 
    
78
 
    br1.fetch(br2)
79
 
    tree1.add_pending_merge(br2.revision_history()[5])
 
75
    self.assertEquals(br2.last_revision(), 'b@u-0-5')
 
76
 
 
77
    tree1.merge_from_branch(br2)
80
78
    tree1.commit("Commit nine", rev_id="a@u-0-5")
81
 
    # DO NOT FETCH HERE - we WANT a GHOST.
82
 
    # br2.fetch(br1)
83
 
    tree2.add_pending_merge(br1.revision_history()[4])
 
79
    # DO NOT MERGE HERE - we WANT a GHOST.
 
80
    br1.lock_read()
 
81
    try:
 
82
        graph = br1.repository.get_graph()
 
83
        revhistory = list(graph.iter_lefthand_ancestry(br1.last_revision(),
 
84
            [revision.NULL_REVISION]))
 
85
        revhistory.reverse()
 
86
    finally:
 
87
        br1.unlock()
 
88
    tree2.add_parent_tree_id(revhistory[4])
84
89
    tree2.commit("Commit ten - ghost merge", rev_id="b@u-0-6")
85
 
    
 
90
 
86
91
    return br1, br2
87
92
 
88
93
 
106
111
             ('a@u-0-5', ['a@u-0-0', 'a@u-0-1', 'a@u-0-2', 'a@u-0-3', 'a@u-0-4',
107
112
                          'b@u-0-3', 'b@u-0-4',
108
113
                          'b@u-0-5', 'a@u-0-5']),
109
 
             ('b@u-0-6', ['a@u-0-0', 'a@u-0-1', 'a@u-0-2',
 
114
             ('b@u-0-6', ['a@u-0-0', 'a@u-0-1', 'a@u-0-2', 'a@u-0-4',
110
115
                          'b@u-0-3', 'b@u-0-4',
111
116
                          'b@u-0-5', 'b@u-0-6']),
112
117
             ]
118
123
                    continue
119
124
                if rev_id in br2_only and not branch is br2:
120
125
                    continue
121
 
                mutter('ancestry of {%s}: %r',
122
 
                       rev_id, branch.repository.get_ancestry(rev_id))
123
 
                result = sorted(branch.repository.get_ancestry(rev_id))
124
 
                self.assertEquals(result, [None] + sorted(anc))
125
 
    
126
 
    
127
 
    def test_is_ancestor(self):
128
 
        """Test checking whether a revision is an ancestor of another revision"""
129
 
        br1, br2 = make_branches(self)
130
 
        revisions = br1.revision_history()
131
 
        revisions_2 = br2.revision_history()
132
 
        sources = br1
133
 
 
134
 
        self.assert_(is_ancestor(revisions[0], revisions[0], br1))
135
 
        self.assert_(is_ancestor(revisions[1], revisions[0], sources))
136
 
        self.assert_(not is_ancestor(revisions[0], revisions[1], sources))
137
 
        self.assert_(is_ancestor(revisions_2[3], revisions[0], sources))
138
 
        # disabled mbp 20050914, doesn't seem to happen anymore
139
 
        ## self.assertRaises(NoSuchRevision, is_ancestor, revisions_2[3],
140
 
        ##                  revisions[0], br1)        
141
 
        self.assert_(is_ancestor(revisions[3], revisions_2[4], sources))
142
 
        self.assert_(is_ancestor(revisions[3], revisions_2[4], br1))
143
 
        self.assert_(is_ancestor(revisions[3], revisions_2[3], sources))
144
 
        ## self.assert_(not is_ancestor(revisions[3], revisions_2[3], br1))
 
126
                self.assertThat(anc,
 
127
                    MatchesAncestry(branch.repository, rev_id))
145
128
 
146
129
 
147
130
class TestIntermediateRevisions(TestCaseWithTransport):
148
131
 
149
132
    def setUp(self):
150
 
        from bzrlib.commit import commit
151
133
        TestCaseWithTransport.setUp(self)
152
134
        self.br1, self.br2 = make_branches(self)
153
135
        wt1 = self.br1.bzrdir.open_workingtree()
156
138
        wt2.commit("Commit twelve", rev_id="b@u-0-8")
157
139
        wt2.commit("Commit thirtteen", rev_id="b@u-0-9")
158
140
 
159
 
        self.br1.fetch(self.br2)
160
 
        wt1.add_pending_merge(self.br2.revision_history()[6])
 
141
        wt1.merge_from_branch(self.br2)
161
142
        wt1.commit("Commit fourtten", rev_id="a@u-0-6")
162
143
 
163
 
        self.br2.fetch(self.br1)
164
 
        wt2.add_pending_merge(self.br1.revision_history()[6])
 
144
        wt2.merge_from_branch(self.br1)
165
145
        wt2.commit("Commit fifteen", rev_id="b@u-0-10")
166
146
 
167
 
        from bzrlib.revision import MultipleRevisionSources
168
 
        self.sources = MultipleRevisionSources(self.br1.repository,
169
 
                                               self.br2.repository)
170
 
 
171
 
 
172
147
 
173
148
class MockRevisionSource(object):
174
149
    """A RevisionSource that takes a pregenerated graph.
188
163
class TestCommonAncestor(TestCaseWithTransport):
189
164
    """Test checking whether a revision is an ancestor of another revision"""
190
165
 
191
 
    def test_common_ancestor(self):
192
 
        """Pick a reasonable merge base"""
193
 
        br1, br2 = make_branches(self)
194
 
        revisions = br1.revision_history()
195
 
        revisions_2 = br2.revision_history()
196
 
        sources = MultipleRevisionSources(br1.repository, br2.repository)
197
 
        expected_ancestors_list = {revisions[3]:(0, 0), 
198
 
                                   revisions[2]:(1, 1),
199
 
                                   revisions_2[4]:(2, 1), 
200
 
                                   revisions[1]:(3, 2),
201
 
                                   revisions_2[3]:(4, 2),
202
 
                                   revisions[0]:(5, 3) }
203
 
        ancestors_list = find_present_ancestors(revisions[3], sources)
204
 
        self.assertEquals(len(expected_ancestors_list), len(ancestors_list))
205
 
        for key, value in expected_ancestors_list.iteritems():
206
 
            self.assertEqual(ancestors_list[key], value, 
207
 
                              "key %r, %r != %r" % (key, ancestors_list[key],
208
 
                                                    value))
209
 
        self.assertEqual(common_ancestor(revisions[0], revisions[0], sources),
210
 
                          revisions[0])
211
 
        self.assertEqual(common_ancestor(revisions[1], revisions[2], sources),
212
 
                          revisions[1])
213
 
        self.assertEqual(common_ancestor(revisions[1], revisions[1], sources),
214
 
                          revisions[1])
215
 
        self.assertEqual(common_ancestor(revisions[2], revisions_2[4], sources),
216
 
                          revisions[2])
217
 
        self.assertEqual(common_ancestor(revisions[3], revisions_2[4], sources),
218
 
                          revisions_2[4])
219
 
        self.assertEqual(common_ancestor(revisions[4], revisions_2[5], sources),
220
 
                          revisions_2[4])
221
 
        self.assertTrue(common_ancestor(revisions[5], revisions_2[6], sources) in
222
 
                        (revisions[4], revisions_2[5]))
223
 
        self.assertTrue(common_ancestor(revisions_2[6], revisions[5], sources),
224
 
                        (revisions[4], revisions_2[5]))
225
 
        self.assertEqual(None, common_ancestor(None, revisions[5], sources))
226
 
 
227
 
    def test_combined(self):
228
 
        """combined_graph
229
 
        Ensure it's not order-sensitive
230
 
        """
231
 
        br1, br2 = make_branches(self)
232
 
        source = MultipleRevisionSources(br1.repository, br2.repository)
233
 
        combined_1 = combined_graph(br1.last_revision(), 
234
 
                                    br2.last_revision(), source)
235
 
        combined_2 = combined_graph(br2.last_revision(),
236
 
                                    br1.last_revision(), source)
237
 
        self.assertEquals(combined_1[1], combined_2[1])
238
 
        self.assertEquals(combined_1[2], combined_2[2])
239
 
        self.assertEquals(combined_1[3], combined_2[3])
240
 
        self.assertEquals(combined_1, combined_2)
241
 
 
242
166
    def test_get_history(self):
243
167
        # TODO: test ghosts on the left hand branch's impact
244
168
        # TODO: test ghosts on all parents, we should get some
258
182
        history = rev.get_history(tree.branch.repository)
259
183
        self.assertEqual([None, '1', '2' ,'3'], history)
260
184
 
261
 
    def test_common_ancestor_rootless_graph(self):
262
 
        # common_ancestor on a graph with no reachable roots - only
263
 
        # ghosts - should still return a useful value.
264
 
        graph = Graph()
265
 
        # add a ghost node which would be a root if it wasn't a ghost.
266
 
        graph.add_ghost('a_ghost')
267
 
        # add a normal commit on top of that
268
 
        graph.add_node('rev1', ['a_ghost'])
269
 
        # add a left-branch revision
270
 
        graph.add_node('left', ['rev1'])
271
 
        # add a right-branch revision
272
 
        graph.add_node('right', ['rev1'])
273
 
        source = MockRevisionSource(graph)
274
 
        self.assertEqual('rev1', common_ancestor('left', 'right', source))
275
 
 
276
 
 
277
 
class TestMultipleRevisionSources(TestCaseWithTransport):
278
 
    """Tests for the MultipleRevisionSources adapter."""
279
 
 
280
 
    def test_get_revision_graph_merges_ghosts(self):
281
 
        # when we ask for the revision graph for B, which
282
 
        # is in repo 1 with a ghost of A, and which is not
283
 
        # in repo 2, which has A, the revision_graph()
284
 
        # should return A and B both.
285
 
        tree_1 = self.make_branch_and_tree('1')
286
 
        tree_1.add_pending_merge('A')
287
 
        tree_1.commit('foo', rev_id='B', allow_pointless=True)
288
 
        tree_2 = self.make_branch_and_tree('2')
289
 
        tree_2.commit('bar', rev_id='A', allow_pointless=True)
290
 
        source = MultipleRevisionSources(tree_1.branch.repository,
291
 
                                         tree_2.branch.repository)
292
 
        self.assertEqual({'B':['A'],
293
 
                          'A':[]},
294
 
                         source.get_revision_graph('B'))
 
185
 
 
186
class TestReservedId(TestCase):
 
187
 
 
188
    def test_is_reserved_id(self):
 
189
        self.assertEqual(True, revision.is_reserved_id(NULL_REVISION))
 
190
        self.assertEqual(True, revision.is_reserved_id(
 
191
            revision.CURRENT_REVISION))
 
192
        self.assertEqual(True, revision.is_reserved_id('arch:'))
 
193
        self.assertEqual(False, revision.is_reserved_id('null'))
 
194
        self.assertEqual(False, revision.is_reserved_id(
 
195
            'arch:a@example.com/c--b--v--r'))
 
196
        self.assertEqual(False, revision.is_reserved_id(None))
 
197
 
 
198
 
 
199
class TestRevisionMethods(TestCase):
 
200
 
 
201
    def test_get_summary(self):
 
202
        r = revision.Revision('1')
 
203
        r.message = 'a'
 
204
        self.assertEqual('a', r.get_summary())
 
205
        r.message = 'a\nb'
 
206
        self.assertEqual('a', r.get_summary())
 
207
        r.message = '\na\nb'
 
208
        self.assertEqual('a', r.get_summary())
 
209
        r.message = None
 
210
        self.assertEqual('', r.get_summary())
 
211
 
 
212
    def test_get_apparent_authors(self):
 
213
        r = revision.Revision('1')
 
214
        r.committer = 'A'
 
215
        self.assertEqual(['A'], r.get_apparent_authors())
 
216
        r.properties['author'] = 'B'
 
217
        self.assertEqual(['B'], r.get_apparent_authors())
 
218
        r.properties['authors'] = 'C\nD'
 
219
        self.assertEqual(['C', 'D'], r.get_apparent_authors())
 
220
 
 
221
    def test_get_apparent_authors_no_committer(self):
 
222
        r = revision.Revision('1')
 
223
        self.assertEqual([], r.get_apparent_authors())
 
224
 
 
225
 
 
226
class TestRevisionBugs(TestCase):
 
227
    """Tests for getting the bugs that a revision is linked to."""
 
228
 
 
229
    def test_no_bugs(self):
 
230
        r = revision.Revision('1')
 
231
        self.assertEqual([], list(r.iter_bugs()))
 
232
 
 
233
    def test_some_bugs(self):
 
234
        r = revision.Revision(
 
235
            '1', properties={
 
236
                'bugs': bugtracker.encode_fixes_bug_urls(
 
237
                    ['http://example.com/bugs/1',
 
238
                     'http://launchpad.net/bugs/1234'])})
 
239
        self.assertEqual(
 
240
            [('http://example.com/bugs/1', bugtracker.FIXED),
 
241
             ('http://launchpad.net/bugs/1234', bugtracker.FIXED)],
 
242
            list(r.iter_bugs()))
 
243
 
 
244
    def test_no_status(self):
 
245
        r = revision.Revision(
 
246
            '1', properties={'bugs': 'http://example.com/bugs/1'})
 
247
        self.assertRaises(InvalidLineInBugsProperty, list, r.iter_bugs())
 
248
 
 
249
    def test_too_much_information(self):
 
250
        r = revision.Revision(
 
251
            '1', properties={'bugs': 'http://example.com/bugs/1 fixed bar'})
 
252
        self.assertRaises(InvalidLineInBugsProperty, list, r.iter_bugs())
 
253
 
 
254
    def test_invalid_status(self):
 
255
        r = revision.Revision(
 
256
            '1', properties={'bugs': 'http://example.com/bugs/1 faxed'})
 
257
        self.assertRaises(InvalidBugStatus, list, r.iter_bugs())