20
from bzrlib.branch import Branch
21
from bzrlib.bzrdir import BzrDir
22
from bzrlib.builtins import merge
23
20
import bzrlib.errors
24
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
26
from bzrlib.tests.test_revision import make_branches
21
from bzrlib.selftest.testrevision import make_branches
27
22
from bzrlib.trace import mutter
28
from bzrlib.workingtree import WorkingTree
23
from bzrlib.branch import Branch
24
from bzrlib.fetch import greedy_fetch
26
from bzrlib.selftest import TestCaseInTempDir
27
from bzrlib.selftest.HTTPTestUtil import TestCaseWithWebserver
31
30
def has_revision(branch, revision_id):
32
return branch.repository.has_revision(revision_id)
32
branch.get_revision_xml_file(revision_id)
34
except bzrlib.errors.NoSuchRevision:
34
37
def fetch_steps(self, br_a, br_b, writable_a):
35
38
"""A foreign test method for testing fetch locally and remotely."""
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]))
50
self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
51
self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
41
return Branch.initialize(name)
43
assert not has_revision(br_b, br_a.revision_history()[3])
44
assert has_revision(br_b, br_a.revision_history()[2])
45
assert len(br_b.revision_history()) == 7
46
assert greedy_fetch(br_b, br_a, br_a.revision_history()[2])[0] == 0
48
# greedy_fetch is not supposed to alter the revision history
49
assert len(br_b.revision_history()) == 7
50
assert not has_revision(br_b, br_a.revision_history()[3])
52
assert len(br_b.revision_history()) == 7
53
assert greedy_fetch(br_b, br_a, br_a.revision_history()[3])[0] == 1
54
assert has_revision(br_b, br_a.revision_history()[3])
55
assert not has_revision(br_a, br_b.revision_history()[6])
56
assert has_revision(br_a, br_b.revision_history()[5])
53
58
# When a non-branch ancestor is missing, it should be unlisted...
54
59
# as its not reference from the inventory weave.
55
br_b4 = self.make_branch('br_4')
56
count, failures = br_b4.fetch(br_b)
60
br_b4 = new_branch('br_4')
61
count, failures = greedy_fetch(br_b4, br_b)
57
62
self.assertEqual(count, 7)
58
63
self.assertEqual(failures, [])
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]))
65
self.assertEqual(greedy_fetch(writable_a, br_b)[0], 1)
66
assert has_revision(br_a, br_b.revision_history()[3])
67
assert has_revision(br_a, br_b.revision_history()[4])
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]))
68
self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
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]))
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)
69
br_b2 = new_branch('br_b2')
70
assert greedy_fetch(br_b2, br_b)[0] == 7
71
assert has_revision(br_b2, br_b.revision_history()[4])
72
assert has_revision(br_b2, br_a.revision_history()[2])
73
assert not has_revision(br_b2, br_a.revision_history()[3])
75
br_a2 = new_branch('br_a2')
76
assert greedy_fetch(br_a2, br_a)[0] == 9
77
assert has_revision(br_a2, br_b.revision_history()[4])
78
assert has_revision(br_a2, br_a.revision_history()[3])
79
assert has_revision(br_a2, br_a.revision_history()[2])
81
br_a3 = new_branch('br_a3')
82
assert greedy_fetch(br_a3, br_a2)[0] == 0
80
83
for revno in range(4):
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]
86
self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
84
assert not has_revision(br_a3, br_a.revision_history()[revno])
85
self.assertEqual(greedy_fetch(br_a3, br_a2, br_a.revision_history()[2])[0], 3)
86
fetched = greedy_fetch(br_a3, br_a2, br_a.revision_history()[3])[0]
87
assert fetched == 3, "fetched %d instead of 3" % fetched
87
88
# InstallFailed should be raised if the branch is missing the revision
88
89
# that was requested.
89
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
90
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
90
92
# InstallFailed should be raised if the branch is missing a revision
91
93
# from its own revision history
92
94
br_a2.append_revision('a-b-c')
93
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
95
self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
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
100
99
#TODO: test that fetch correctly does reweaving when needed. RBC 20051008
101
# Note that this means - updating the weave when ghosts are filled in to
102
# add the right parents.
105
class TestFetch(TestCaseWithTransport):
101
class TestFetch(TestCaseInTempDir):
107
103
def test_fetch(self):
108
104
#highest indices a: 5, b: 7
109
br_a, br_b = make_branches(self)
105
br_a, br_b = make_branches()
110
106
fetch_steps(self, br_a, br_b, br_a)
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):
119
def test_merge_fetches_unrelated(self):
120
"""Merge brings across history from unrelated source"""
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')
128
merge(other_revision=['br1', -1], base_revision=['br1', 0],
130
self._check_revs_present(br2)
132
def test_merge_fetches(self):
133
"""Merge brings across history from source"""
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')
141
merge(other_revision=['br1', -1], base_revision=[None, None],
143
self._check_revs_present(br2)
145
def _check_revs_present(self, br2):
146
for rev_id in '1-1', '1-2', '2-1':
147
self.assertTrue(br2.repository.has_revision(rev_id))
148
rev = br2.repository.get_revision(rev_id)
149
self.assertEqual(rev.revision_id, rev_id)
150
self.assertTrue(br2.repository.get_inventory(rev_id))
153
class TestMergeFileHistory(TestCaseWithTransport):
109
class TestHttpFetch(TestCaseWithWebserver):
156
super(TestMergeFileHistory, self).setUp()
157
wt1 = self.make_branch_and_tree('br1')
159
self.build_tree_contents([('br1/file', 'original contents\n')])
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()
165
self.build_tree_contents([('br1/file', 'original from 1\n')])
166
wt1.commit(message='rev 1-2', rev_id='1-2')
167
self.build_tree_contents([('br1/file', 'agreement\n')])
168
wt1.commit(message='rev 1-3', rev_id='1-3')
169
self.build_tree_contents([('br2/file', 'contents in 2\n')])
170
wt2.commit(message='rev 2-1', rev_id='2-1')
171
self.build_tree_contents([('br2/file', 'agreement\n')])
172
wt2.commit(message='rev 2-2', rev_id='2-2')
174
def test_merge_fetches_file_history(self):
175
"""Merge brings across file histories"""
176
br2 = Branch.open('br2')
177
merge(other_revision=['br1', -1], base_revision=[None, None],
179
for rev_id, text in [('1-2', 'original from 1\n'),
180
('1-3', 'agreement\n'),
181
('2-1', 'contents in 2\n'),
182
('2-2', 'agreement\n')]:
183
self.assertEqualDiff(
184
br2.repository.revision_tree(
185
rev_id).get_file_text('this-file-id'), text)
188
class TestHttpFetch(TestCaseWithWebserver):
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 ?
112
super(TestHttpFetch, self).setUp()
193
115
def test_fetch(self):
194
116
#highest indices a: 5, b: 7
195
br_a, br_b = make_branches(self)
196
br_rem_a = Branch.open(self.get_readonly_url('branch1'))
117
br_a, br_b = make_branches()
118
br_rem_a = Branch.open(self.get_remote_url(br_a._transport.base))
197
119
fetch_steps(self, br_rem_a, br_b, br_a)
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:
121
def log(self, *args):
122
"""Capture web server log messages for introspection."""
123
super(TestHttpFetch, self).log(*args)
124
if args[0].startswith("webserver"):
125
self.weblogs.append(args[0])
211
127
def test_weaves_are_retrieved_once(self):
212
128
self.build_tree(("source/", "source/file", "target/"))
213
wt = self.make_branch_and_tree('source')
215
wt.add(["file"], ["id"])
216
wt.commit("added file")
129
branch = Branch.initialize("source")
130
branch.add(["file"], ["id"])
131
branch.commit("added file")
217
132
print >>open("source/file", 'w'), "blah"
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__
133
branch.commit("changed file")
134
target = Branch.initialize("target/")
135
source = Branch.open(self.get_remote_url("source/"))
136
self.assertEqual(greedy_fetch(target, source), (2, []))
223
137
# this is the path to the literal file. As format changes
224
138
# occur it needs to be updated. FIXME: ask the store for the
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))
235
self.assertEqual(1, self._count_log_matches('inventory.knit', http_logs))
140
weave_suffix = 'weaves/ce/id.weave HTTP/1.1" 200 -'
142
len([log for log in self.weblogs if log.endswith(weave_suffix)]))
143
inventory_weave_suffix = 'inventory.weave HTTP/1.1" 200 -'
145
len([log for log in self.weblogs if log.endswith(
146
inventory_weave_suffix)]))
236
147
# this r-h check test will prevent regressions, but it currently already
237
148
# passes, before the patch to cache-rh is applied :[
238
self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
239
# FIXME naughty poking in there.
240
self.get_readonly_server().logs = []
149
revision_history_suffix = 'revision-history HTTP/1.1" 200 -'
151
len([log for log in self.weblogs if log.endswith(
152
revision_history_suffix)]))
241
154
# check there is nothing more to fetch
242
source = Branch.open(self.get_readonly_url("source/"))
243
self.assertEqual(target.fetch(source), (0, []))
244
# should make just two requests
245
http_logs = self.get_readonly_server().logs
246
self.log("web server logs are:")
247
self.log('\n'.join(http_logs))
248
self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
249
self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
250
self.assertEqual(1, self._count_log_matches('repository/format', http_logs))
251
self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
252
self.assertEqual(4, len(http_logs))
155
source = Branch.open(self.get_remote_url("source/"))
156
self.assertEqual(greedy_fetch(target, source), (0, []))
157
self.failUnless(self.weblogs[0].endswith('branch-format HTTP/1.1" 200 -'))
158
self.failUnless(self.weblogs[1].endswith('revision-history HTTP/1.1" 200 -'))
159
self.assertEqual(2, len(self.weblogs))