20
from bzrlib.branch import Branch
21
from bzrlib.bzrdir import BzrDir
22
from bzrlib.builtins import merge
20
23
import bzrlib.errors
24
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
21
26
from bzrlib.tests.test_revision import make_branches
22
27
from bzrlib.trace import mutter
23
from bzrlib.branch import Branch
24
from bzrlib.fetch import greedy_fetch
25
from bzrlib.merge import merge
26
from bzrlib.clone import copy_branch
28
from bzrlib.tests import TestCaseInTempDir
29
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
28
from bzrlib.workingtree import WorkingTree
32
31
def has_revision(branch, revision_id):
34
branch.get_revision_xml(revision_id)
36
except bzrlib.errors.NoSuchRevision:
32
return branch.repository.has_revision(revision_id)
39
34
def fetch_steps(self, br_a, br_b, writable_a):
40
35
"""A foreign test method for testing fetch locally and remotely."""
43
return Branch.initialize(name)
45
self.assertFalse(has_revision(br_b, br_a.revision_history()[3]))
46
self.assert_(has_revision(br_b, br_a.revision_history()[2]))
47
self.assertEquals(len(br_b.revision_history()), 7)
48
self.assertEquals(greedy_fetch(br_b, br_a, br_a.revision_history()[2])[0], 0)
50
# greedy_fetch is not supposed to alter the revision history
51
self.assertEquals(len(br_b.revision_history()), 7)
52
self.assertFalse(has_revision(br_b, br_a.revision_history()[3]))
54
self.assertEquals(len(br_b.revision_history()), 7)
55
self.assertEquals(greedy_fetch(br_b, br_a, br_a.revision_history()[3])[0], 1)
56
self.assert_(has_revision(br_b, br_a.revision_history()[3]))
37
# TODO RBC 20060201 make this a repository test.
38
repo_b = br_b.repository
39
self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
40
self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
41
self.assertEquals(len(br_b.revision_history()), 7)
42
self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
43
# branch.fetch is not supposed to alter the revision history
44
self.assertEquals(len(br_b.revision_history()), 7)
45
self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
47
# fetching the next revision up in sample data copies one revision
48
self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
49
self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
57
50
self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
58
self.assert_(has_revision(br_a, br_b.revision_history()[5]))
51
self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
60
53
# When a non-branch ancestor is missing, it should be unlisted...
61
54
# as its not reference from the inventory weave.
62
br_b4 = new_branch('br_4')
63
count, failures = greedy_fetch(br_b4, br_b)
55
br_b4 = self.make_branch('br_4')
56
count, failures = br_b4.fetch(br_b)
64
57
self.assertEqual(count, 7)
65
58
self.assertEqual(failures, [])
67
self.assertEqual(greedy_fetch(writable_a, br_b)[0], 1)
68
self.assert_(has_revision(br_a, br_b.revision_history()[3]))
69
self.assert_(has_revision(br_a, br_b.revision_history()[4]))
60
self.assertEqual(writable_a.fetch(br_b)[0], 1)
61
self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
62
self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
71
br_b2 = new_branch('br_b2')
72
self.assertEquals(greedy_fetch(br_b2, br_b)[0], 7)
73
self.assert_(has_revision(br_b2, br_b.revision_history()[4]))
74
self.assert_(has_revision(br_b2, br_a.revision_history()[2]))
64
br_b2 = self.make_branch('br_b2')
65
self.assertEquals(br_b2.fetch(br_b)[0], 7)
66
self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
67
self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
75
68
self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
77
br_a2 = new_branch('br_a2')
78
self.assertEquals(greedy_fetch(br_a2, br_a)[0], 9)
79
self.assert_(has_revision(br_a2, br_b.revision_history()[4]))
80
self.assert_(has_revision(br_a2, br_a.revision_history()[3]))
81
self.assert_(has_revision(br_a2, br_a.revision_history()[2]))
70
br_a2 = self.make_branch('br_a2')
71
self.assertEquals(br_a2.fetch(br_a)[0], 9)
72
self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
73
self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
74
self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
83
br_a3 = new_branch('br_a3')
84
self.assertEquals(greedy_fetch(br_a3, br_a2)[0], 0)
76
br_a3 = self.make_branch('br_a3')
77
# pulling a branch with no revisions grabs nothing, regardless of
78
# whats in the inventory.
79
self.assertEquals(br_a3.fetch(br_a2)[0], 0)
85
80
for revno in range(4):
86
self.assertFalse(has_revision(br_a3, br_a.revision_history()[revno]))
87
self.assertEqual(greedy_fetch(br_a3, br_a2, br_a.revision_history()[2])[0], 3)
88
fetched = greedy_fetch(br_a3, br_a2, br_a.revision_history()[3])[0]
82
br_a3.repository.has_revision(br_a.revision_history()[revno]))
83
self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
84
# pull the 3 revisions introduced by a@u-0-3
85
fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
89
86
self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
90
87
# InstallFailed should be raised if the branch is missing the revision
91
88
# that was requested.
92
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
89
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
94
90
# InstallFailed should be raised if the branch is missing a revision
95
91
# from its own revision history
96
92
br_a2.append_revision('a-b-c')
97
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
93
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
95
# TODO: jam 20051218 Branch should no longer allow append_revision for revisions
96
# which don't exist. So this test needs to be rewritten
97
# RBC 20060403 the way to do this is to uncommit the revision from the
98
# repository after the commit
101
100
#TODO: test that fetch correctly does reweaving when needed. RBC 20051008
103
class TestFetch(TestCaseInTempDir):
101
# Note that this means - updating the weave when ghosts are filled in to
102
# add the right parents.
105
class TestFetch(TestCaseWithTransport):
105
107
def test_fetch(self):
106
108
#highest indices a: 5, b: 7
107
109
br_a, br_b = make_branches(self)
108
110
fetch_steps(self, br_a, br_b, br_a)
111
class TestMergeFetch(TestCaseInTempDir):
112
def test_fetch_self(self):
113
wt = self.make_branch_and_tree('br')
114
self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
117
class TestMergeFetch(TestCaseWithTransport):
113
119
def test_merge_fetches_unrelated(self):
114
120
"""Merge brings across history from unrelated source"""
116
br1 = Branch.initialize('br1')
117
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
118
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
120
br2 = Branch.initialize('br2')
121
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
121
wt1 = self.make_branch_and_tree('br1')
123
wt1.commit(message='rev 1-1', rev_id='1-1')
124
wt1.commit(message='rev 1-2', rev_id='1-2')
125
wt2 = self.make_branch_and_tree('br2')
127
wt2.commit(message='rev 2-1', rev_id='2-1')
122
128
merge(other_revision=['br1', -1], base_revision=['br1', 0],
124
130
self._check_revs_present(br2)
126
132
def test_merge_fetches(self):
127
133
"""Merge brings across history from source"""
129
br1 = Branch.initialize('br1')
130
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
131
copy_branch(br1, 'br2')
132
br2 = Branch.open('br2')
133
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
134
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
134
wt1 = self.make_branch_and_tree('br1')
136
wt1.commit(message='rev 1-1', rev_id='1-1')
137
dir_2 = br1.bzrdir.sprout('br2')
138
br2 = dir_2.open_branch()
139
wt1.commit(message='rev 1-2', rev_id='1-2')
140
dir_2.open_workingtree().commit(message='rev 2-1', rev_id='2-1')
135
141
merge(other_revision=['br1', -1], base_revision=[None, None],
137
143
self._check_revs_present(br2)
139
145
def _check_revs_present(self, br2):
140
146
for rev_id in '1-1', '1-2', '2-1':
141
self.assertTrue(br2.has_revision(rev_id))
142
rev = br2.get_revision(rev_id)
147
self.assertTrue(br2.repository.has_revision(rev_id))
148
rev = br2.repository.get_revision(rev_id)
143
149
self.assertEqual(rev.revision_id, rev_id)
144
self.assertTrue(br2.get_inventory(rev_id))
148
class TestMergeFileHistory(TestCaseInTempDir):
150
self.assertTrue(br2.repository.get_inventory(rev_id))
153
class TestMergeFileHistory(TestCaseWithTransport):
150
TestCaseInTempDir.setUp(self)
152
br1 = Branch.initialize('br1')
156
super(TestMergeFileHistory, self).setUp()
157
wt1 = self.make_branch_and_tree('br1')
153
159
self.build_tree_contents([('br1/file', 'original contents\n')])
154
br1.working_tree().add(['file'], ['this-file-id'])
155
br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
156
copy_branch(br1, 'br2')
157
br2 = Branch.open('br2')
160
wt1.add('file', 'this-file-id')
161
wt1.commit(message='rev 1-1', rev_id='1-1')
162
dir_2 = br1.bzrdir.sprout('br2')
163
br2 = dir_2.open_branch()
164
wt2 = dir_2.open_workingtree()
158
165
self.build_tree_contents([('br1/file', 'original from 1\n')])
159
br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
166
wt1.commit(message='rev 1-2', rev_id='1-2')
160
167
self.build_tree_contents([('br1/file', 'agreement\n')])
161
br1.working_tree().commit(message='rev 1-3', rev_id='1-3')
168
wt1.commit(message='rev 1-3', rev_id='1-3')
162
169
self.build_tree_contents([('br2/file', 'contents in 2\n')])
163
br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
170
wt2.commit(message='rev 2-1', rev_id='2-1')
164
171
self.build_tree_contents([('br2/file', 'agreement\n')])
165
br2.working_tree().commit(message='rev 2-2', rev_id='2-2')
172
wt2.commit(message='rev 2-2', rev_id='2-2')
167
174
def test_merge_fetches_file_history(self):
168
175
"""Merge brings across file histories"""
173
180
('1-3', 'agreement\n'),
174
181
('2-1', 'contents in 2\n'),
175
182
('2-2', 'agreement\n')]:
176
self.assertEqualDiff(br2.revision_tree(rev_id).get_file_text('this-file-id'),
183
self.assertEqualDiff(
184
br2.repository.revision_tree(
185
rev_id).get_file_text('this-file-id'), text)
182
188
class TestHttpFetch(TestCaseWithWebserver):
185
super(TestHttpFetch, self).setUp()
189
# FIXME RBC 20060124 this really isn't web specific, perhaps an
190
# instrumented readonly transport? Can we do an instrumented
191
# adapter and use self.get_readonly_url ?
188
193
def test_fetch(self):
189
194
#highest indices a: 5, b: 7
190
195
br_a, br_b = make_branches(self)
191
br_rem_a = Branch.open(self.get_remote_url(br_a._transport.base))
196
br_rem_a = Branch.open(self.get_readonly_url('branch1'))
192
197
fetch_steps(self, br_rem_a, br_b, br_a)
194
def log(self, *args):
195
"""Capture web server log messages for introspection."""
196
super(TestHttpFetch, self).log(*args)
197
# if this call indicates a url being fetched, save it specially
198
if args[0].startswith("webserver"):
199
self.weblogs.append(args[3])
199
def _count_log_matches(self, target, logs):
200
"""Count the number of times the target file pattern was fetched in an http log"""
201
log_pattern = '%s HTTP/1.1" 200 - "-" "bzr/%s' % \
202
(target, bzrlib.__version__)
205
# TODO: perhaps use a regexp instead so we can match more
207
if line.find(log_pattern) > -1:
201
211
def test_weaves_are_retrieved_once(self):
202
212
self.build_tree(("source/", "source/file", "target/"))
203
branch = Branch.initialize("source")
204
branch.working_tree().add(["file"], ["id"])
205
branch.working_tree().commit("added file")
213
wt = self.make_branch_and_tree('source')
215
wt.add(["file"], ["id"])
216
wt.commit("added file")
206
217
print >>open("source/file", 'w'), "blah"
207
branch.working_tree().commit("changed file")
208
target = Branch.initialize("target/")
209
source = Branch.open(self.get_remote_url("source/"))
210
self.assertEqual(greedy_fetch(target, source), (2, []))
218
wt.commit("changed file")
219
target = BzrDir.create_branch_and_repo("target/")
220
source = Branch.open(self.get_readonly_url("source/"))
221
self.assertEqual(target.fetch(source), (2, []))
222
log_pattern = '%%s HTTP/1.1" 200 - "-" "bzr/%s' % bzrlib.__version__
211
223
# this is the path to the literal file. As format changes
212
224
# occur it needs to be updated. FIXME: ask the store for the
214
weave_suffix = 'weaves/ce/id.weave HTTP/1.1" 200 -'
216
len([log for log in self.weblogs if log.endswith(weave_suffix)]))
217
inventory_weave_suffix = 'inventory.weave HTTP/1.1" 200 -'
219
len([log for log in self.weblogs if log.endswith(
220
inventory_weave_suffix)]))
226
self.log("web server logs are:")
227
http_logs = self.get_readonly_server().logs
228
self.log('\n'.join(http_logs))
229
# unfortunately this log entry is branch format specific. We could
230
# factor out the 'what files does this format use' to a method on the
231
# repository, which would let us to this generically. RBC 20060419
232
self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
233
self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
234
self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
221
235
# this r-h check test will prevent regressions, but it currently already
222
236
# passes, before the patch to cache-rh is applied :[
223
revision_history_suffix = 'revision-history HTTP/1.1" 200 -'
225
len([log for log in self.weblogs if log.endswith(
226
revision_history_suffix)]))
237
self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
238
# FIXME naughty poking in there.
239
self.get_readonly_server().logs = []
228
240
# check there is nothing more to fetch
229
source = Branch.open(self.get_remote_url("source/"))
230
self.assertEqual(greedy_fetch(target, source), (0, []))
231
self.failUnless(self.weblogs[0].endswith('branch-format HTTP/1.1" 200 -'))
232
self.failUnless(self.weblogs[1].endswith('revision-history HTTP/1.1" 200 -'))
233
self.assertEqual(2, len(self.weblogs))
241
source = Branch.open(self.get_readonly_url("source/"))
242
self.assertEqual(target.fetch(source), (0, []))
243
# should make just two requests
244
http_logs = self.get_readonly_server().logs
245
self.log("web server logs are:")
246
self.log('\n'.join(http_logs))
247
self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
248
self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
249
self.assertEqual(1, self._count_log_matches('repository/format', http_logs))
250
self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
251
self.assertEqual(4, len(http_logs))