1
# Copyright (C) 2005-2011 Canonical Ltd
1
# (C) 2005 Canonical Ltd
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
from bzrlib.errors import (
26
InvalidLineInBugsProperty,
28
from bzrlib.revision import NULL_REVISION
29
from bzrlib.tests import TestCase, TestCaseWithTransport
30
from bzrlib.tests.matchers import MatchesAncestry
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,
27
is_ancestor, MultipleRevisionSources)
28
from bzrlib.tests import TestCaseWithTransport
29
from bzrlib.trace import mutter
30
from bzrlib.workingtree import WorkingTree
32
32
# We're allowed to test deprecated interfaces
33
33
warnings.filterwarnings('ignore',
55
55
so A is missing b6 at the start
56
56
and B is missing a3, a4, a5
58
tree1 = self.make_branch_and_tree("branch1", format=format)
58
tree1 = self.make_branch_and_tree("branch1")
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")
65
tree2 = tree1.bzrdir.sprout("branch2").open_workingtree()
65
tree2 = tree1.bzrdir.clone("branch2").open_workingtree()
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
69
revisions_2 = br2.revision_history()
70
self.assertEquals(revisions_2[-1], 'b@u-0-4')
72
tree1.merge_from_branch(br2)
72
tree1.add_pending_merge(revisions_2[4])
73
self.assertEquals(revisions_2[4], 'b@u-0-4')
73
74
tree1.commit("Commit six", rev_id="a@u-0-3")
74
75
tree1.commit("Commit seven", rev_id="a@u-0-4")
75
76
tree2.commit("Commit eight", rev_id="b@u-0-5")
76
self.assertEquals(br2.revision_history()[-1], 'b@u-0-5')
78
tree1.merge_from_branch(br2)
79
tree1.add_pending_merge(br2.revision_history()[5])
79
80
tree1.commit("Commit nine", rev_id="a@u-0-5")
80
# DO NOT MERGE HERE - we WANT a GHOST.
81
tree2.add_parent_tree_id(br1.revision_history()[4])
81
# DO NOT FETCH HERE - we WANT a GHOST.
83
tree2.add_pending_merge(br1.revision_history()[4])
82
84
tree2.commit("Commit ten - ghost merge", rev_id="b@u-0-6")
117
119
if rev_id in br2_only and not branch is br2:
120
MatchesAncestry(branch.repository, rev_id))
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))
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()
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))
123
147
class TestIntermediateRevisions(TestCaseWithTransport):
150
from bzrlib.commit import commit
126
151
TestCaseWithTransport.setUp(self)
127
152
self.br1, self.br2 = make_branches(self)
128
153
wt1 = self.br1.bzrdir.open_workingtree()
156
188
class TestCommonAncestor(TestCaseWithTransport):
157
189
"""Test checking whether a revision is an ancestor of another revision"""
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),
199
revisions_2[4]:(2, 1),
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],
209
self.assertEqual(common_ancestor(revisions[0], revisions[0], sources),
211
self.assertEqual(common_ancestor(revisions[1], revisions[2], sources),
213
self.assertEqual(common_ancestor(revisions[1], revisions[1], sources),
215
self.assertEqual(common_ancestor(revisions[2], revisions_2[4], sources),
217
self.assertEqual(common_ancestor(revisions[3], revisions_2[4], sources),
219
self.assertEqual(common_ancestor(revisions[4], revisions_2[5], sources),
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))
227
def test_combined(self):
229
Ensure it's not order-sensitive
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)
159
242
def test_get_history(self):
160
243
# TODO: test ghosts on the left hand branch's impact
161
244
# TODO: test ghosts on all parents, we should get some
175
258
history = rev.get_history(tree.branch.repository)
176
259
self.assertEqual([None, '1', '2' ,'3'], history)
179
class TestReservedId(TestCase):
181
def test_is_reserved_id(self):
182
self.assertEqual(True, revision.is_reserved_id(NULL_REVISION))
183
self.assertEqual(True, revision.is_reserved_id(
184
revision.CURRENT_REVISION))
185
self.assertEqual(True, revision.is_reserved_id('arch:'))
186
self.assertEqual(False, revision.is_reserved_id('null'))
187
self.assertEqual(False, revision.is_reserved_id(
188
'arch:a@example.com/c--b--v--r'))
189
self.assertEqual(False, revision.is_reserved_id(None))
192
class TestRevisionMethods(TestCase):
194
def test_get_summary(self):
195
r = revision.Revision('1')
197
self.assertEqual('a', r.get_summary())
199
self.assertEqual('a', r.get_summary())
201
self.assertEqual('a', r.get_summary())
203
self.assertEqual('', r.get_summary())
205
def test_get_apparent_authors(self):
206
r = revision.Revision('1')
208
self.assertEqual(['A'], r.get_apparent_authors())
209
r.properties['author'] = 'B'
210
self.assertEqual(['B'], r.get_apparent_authors())
211
r.properties['authors'] = 'C\nD'
212
self.assertEqual(['C', 'D'], r.get_apparent_authors())
214
def test_get_apparent_authors_no_committer(self):
215
r = revision.Revision('1')
216
self.assertEqual([], r.get_apparent_authors())
219
class TestRevisionBugs(TestCase):
220
"""Tests for getting the bugs that a revision is linked to."""
222
def test_no_bugs(self):
223
r = revision.Revision('1')
224
self.assertEqual([], list(r.iter_bugs()))
226
def test_some_bugs(self):
227
r = revision.Revision(
229
'bugs': bugtracker.encode_fixes_bug_urls(
230
['http://example.com/bugs/1',
231
'http://launchpad.net/bugs/1234'])})
233
[('http://example.com/bugs/1', bugtracker.FIXED),
234
('http://launchpad.net/bugs/1234', bugtracker.FIXED)],
237
def test_no_status(self):
238
r = revision.Revision(
239
'1', properties={'bugs': 'http://example.com/bugs/1'})
240
self.assertRaises(InvalidLineInBugsProperty, list, r.iter_bugs())
242
def test_too_much_information(self):
243
r = revision.Revision(
244
'1', properties={'bugs': 'http://example.com/bugs/1 fixed bar'})
245
self.assertRaises(InvalidLineInBugsProperty, list, r.iter_bugs())
247
def test_invalid_status(self):
248
r = revision.Revision(
249
'1', properties={'bugs': 'http://example.com/bugs/1 faxed'})
250
self.assertRaises(InvalidBugStatus, list, r.iter_bugs())
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.
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))
277
class TestMultipleRevisionSources(TestCaseWithTransport):
278
"""Tests for the MultipleRevisionSources adapter."""
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'],
294
source.get_revision_graph('B'))