1
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005, 2007 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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
28
from bzrlib.branch import Branch
21
29
from bzrlib.bzrdir import BzrDir
22
from bzrlib.builtins import merge
30
from bzrlib.repofmt import knitrepo
24
31
from bzrlib.tests import TestCaseWithTransport
25
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
32
from bzrlib.tests.http_utils import TestCaseWithWebserver
26
33
from bzrlib.tests.test_revision import make_branches
27
34
from bzrlib.trace import mutter
35
from bzrlib.upgrade import Convert
28
36
from bzrlib.workingtree import WorkingTree
38
# These tests are a bit old; please instead add new tests into
39
# interrepository_implementations/ so they'll run on all relevant
31
43
def has_revision(branch, revision_id):
32
44
return branch.repository.has_revision(revision_id)
86
98
self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
87
99
# InstallFailed should be raised if the branch is missing the revision
88
100
# that was requested.
89
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
90
# InstallFailed should be raised if the branch is missing a revision
91
# from its own revision history
92
br_a2.append_revision('a-b-c')
93
self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
101
self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
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
103
# TODO: Test trying to fetch from a branch that points to a revision not
104
# actually present in its repository. Not every branch format allows you
105
# to directly point to such revisions, so it's a bit complicated to
106
# construct. One way would be to uncommit and gc the revision, but not
107
# every branch supports that. -- mbp 20070814
100
109
#TODO: test that fetch correctly does reweaving when needed. RBC 20051008
101
110
# Note that this means - updating the weave when ghosts are filled in to
107
116
def test_fetch(self):
108
117
#highest indices a: 5, b: 7
109
br_a, br_b = make_branches(self)
118
br_a, br_b = make_branches(self, format='dirstate-tags')
110
119
fetch_steps(self, br_a, br_b, br_a)
112
121
def test_fetch_self(self):
113
122
wt = self.make_branch_and_tree('br')
114
123
self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
125
def test_fetch_root_knit(self):
126
"""Ensure that knit2.fetch() updates the root knit
128
This tests the case where the root has a new revision, but there are no
129
corresponding filename, parent, contents or other changes.
131
knit1_format = bzrdir.BzrDirMetaFormat1()
132
knit1_format.repository_format = knitrepo.RepositoryFormatKnit1()
133
knit2_format = bzrdir.BzrDirMetaFormat1()
134
knit2_format.repository_format = knitrepo.RepositoryFormatKnit3()
135
# we start with a knit1 repository because that causes the
136
# root revision to change for each commit, even though the content,
137
# parent, name, and other attributes are unchanged.
138
tree = self.make_branch_and_tree('tree', knit1_format)
139
tree.set_root_id('tree-root')
140
tree.commit('rev1', rev_id='rev1')
141
tree.commit('rev2', rev_id='rev2')
143
# Now we convert it to a knit2 repository so that it has a root knit
144
Convert(tree.basedir, knit2_format)
145
tree = WorkingTree.open(tree.basedir)
146
branch = self.make_branch('branch', format=knit2_format)
147
branch.pull(tree.branch, stop_revision='rev1')
148
repo = branch.repository
149
root_knit = repo.weave_store.get_weave('tree-root',
150
repo.get_transaction())
151
# Make sure fetch retrieved only what we requested
152
self.assertTrue('rev1' in root_knit)
153
self.assertTrue('rev2' not in root_knit)
154
branch.pull(tree.branch)
155
root_knit = repo.weave_store.get_weave('tree-root',
156
repo.get_transaction())
157
# Make sure that the next revision in the root knit was retrieved,
158
# even though the text, name, parent_id, etc., were unchanged.
159
self.assertTrue('rev2' in root_knit)
161
def test_fetch_incompatible(self):
162
knit_tree = self.make_branch_and_tree('knit', format='knit')
163
knit3_tree = self.make_branch_and_tree('knit3',
164
format='dirstate-with-subtree')
165
knit3_tree.commit('blah')
166
self.assertRaises(errors.IncompatibleRepositories,
167
knit_tree.branch.fetch, knit3_tree.branch)
117
170
class TestMergeFetch(TestCaseWithTransport):
199
253
def _count_log_matches(self, target, logs):
200
254
"""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__)
255
get_succeeds_re = re.compile(
256
'.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
257
( target, bzrlib.__version__))
204
259
for line in logs:
205
# TODO: perhaps use a regexp instead so we can match more
207
if line.find(log_pattern) > -1:
260
if get_succeeds_re.match(line):
211
264
def test_weaves_are_retrieved_once(self):
212
265
self.build_tree(("source/", "source/file", "target/"))
213
wt = self.make_branch_and_tree('source')
266
# This test depends on knit dasta storage.
267
wt = self.make_branch_and_tree('source', format='dirstate-tags')
214
268
branch = wt.branch
215
269
wt.add(["file"], ["id"])
216
270
wt.commit("added file")
217
print >>open("source/file", 'w'), "blah"
271
open("source/file", 'w').write("blah\n")
218
272
wt.commit("changed file")
219
273
target = BzrDir.create_branch_and_repo("target/")
220
274
source = Branch.open(self.get_readonly_url("source/"))
221
275
self.assertEqual(target.fetch(source), (2, []))
222
log_pattern = '%%s HTTP/1.1" 200 - "-" "bzr/%s' % bzrlib.__version__
223
276
# this is the path to the literal file. As format changes
224
277
# occur it needs to be updated. FIXME: ask the store for the
229
282
# unfortunately this log entry is branch format specific. We could
230
283
# factor out the 'what files does this format use' to a method on the
231
284
# repository, which would let us to this generically. RBC 20060419
285
# RBC 20080408: Or perhaps we can assert that no files are fully read
232
287
self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
233
288
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))
289
# XXX: This *should* be '1', but more intrusive fetch changes are
290
# needed to drop this back to 1.
291
self.assertEqual(2, self._count_log_matches('inventory.kndx', http_logs))
236
292
# this r-h check test will prevent regressions, but it currently already
237
293
# passes, before the patch to cache-rh is applied :[
238
self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
294
self.assertTrue(1 >= self._count_log_matches('revision-history',
296
self.assertTrue(1 >= self._count_log_matches('last-revision',
239
298
# FIXME naughty poking in there.
240
299
self.get_readonly_server().logs = []
241
# check there is nothing more to fetch
242
source = Branch.open(self.get_readonly_url("source/"))
300
# check there is nothing more to fetch. We take care to re-use the
301
# existing transport so that the request logs we're about to examine
302
# aren't cluttered with redundant probes for a smart server.
303
# XXX: Perhaps this further parameterisation: test http with smart
304
# server, and test http without smart server?
305
source = Branch.open(
306
self.get_readonly_url("source/"),
307
possible_transports=[source.bzrdir.root_transport])
243
308
self.assertEqual(target.fetch(source), (0, []))
244
309
# should make just two requests
245
310
http_logs = self.get_readonly_server().logs
247
312
self.log('\n'.join(http_logs))
248
313
self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
249
314
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))
315
self.assertEqual(1, self._count_log_matches('repository/format',
317
self.assertTrue(1 >= self._count_log_matches('revision-history',
319
self.assertTrue(1 >= self._count_log_matches('last-revision',
252
321
self.assertEqual(4, len(http_logs))
324
class Test1To2Fetch(TestCaseWithTransport):
325
"""Tests for Model1To2 failure modes"""
327
def make_tree_and_repo(self):
328
self.tree = self.make_branch_and_tree('tree', format='pack-0.92')
329
self.repo = self.make_repository('rich-repo', format='rich-root-pack')
330
self.repo.lock_write()
331
self.addCleanup(self.repo.unlock)
333
def do_fetch_order_test(self, first, second):
334
"""Test that fetch works no matter what the set order of revision is.
336
This test depends on the order of items in a set, which is
337
implementation-dependant, so we test A, B and then B, A.
339
self.make_tree_and_repo()
340
self.tree.commit('Commit 1', rev_id=first)
341
self.tree.commit('Commit 2', rev_id=second)
342
self.repo.fetch(self.tree.branch.repository, second)
344
def test_fetch_order_AB(self):
345
"""See do_fetch_order_test"""
346
self.do_fetch_order_test('A', 'B')
348
def test_fetch_order_BA(self):
349
"""See do_fetch_order_test"""
350
self.do_fetch_order_test('B', 'A')
352
def get_parents(self, file_id, revision_id):
353
transaction = self.repo.get_transaction()
354
vf = self.repo.weave_store.get_weave(file_id, transaction)
355
return vf.get_parents_with_ghosts(revision_id)
357
def test_fetch_ghosts(self):
358
self.make_tree_and_repo()
359
self.tree.commit('first commit', rev_id='left-parent')
360
self.tree.add_parent_tree_id('ghost-parent')
361
fork = self.tree.bzrdir.sprout('fork', 'null:').open_workingtree()
362
fork.commit('not a ghost', rev_id='not-ghost-parent')
363
self.tree.branch.repository.fetch(fork.branch.repository,
365
self.tree.add_parent_tree_id('not-ghost-parent')
366
self.tree.commit('second commit', rev_id='second-id')
367
self.repo.fetch(self.tree.branch.repository, 'second-id')
368
root_id = self.tree.get_root_id()
369
self.assertEqual(['left-parent', 'ghost-parent', 'not-ghost-parent'],
370
self.get_parents(root_id, 'second-id'))
372
def make_two_commits(self, change_root, fetch_twice):
373
self.make_tree_and_repo()
374
self.tree.commit('first commit', rev_id='first-id')
376
self.tree.set_root_id('unique-id')
377
self.tree.commit('second commit', rev_id='second-id')
379
self.repo.fetch(self.tree.branch.repository, 'first-id')
380
self.repo.fetch(self.tree.branch.repository, 'second-id')
382
def test_fetch_changed_root(self):
383
self.make_two_commits(change_root=True, fetch_twice=False)
384
self.assertEqual([], self.get_parents('unique-id', 'second-id'))
386
def test_two_fetch_changed_root(self):
387
self.make_two_commits(change_root=True, fetch_twice=True)
388
self.assertEqual([], self.get_parents('unique-id', 'second-id'))
390
def test_two_fetches(self):
391
self.make_two_commits(change_root=False, fetch_twice=True)
392
self.assertEqual(['first-id'],
393
self.get_parents('TREE_ROOT', 'second-id'))