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)
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
151
# Make sure fetch retrieved only what we requested
152
self.assertEqual({('tree-root', 'rev1'):()},
153
repo.texts.get_parent_map(
154
[('tree-root', 'rev1'), ('tree-root', 'rev2')]))
157
branch.pull(tree.branch)
158
# Make sure that the next revision in the root knit was retrieved,
159
# even though the text, name, parent_id, etc., were unchanged.
162
# Make sure fetch retrieved only what we requested
163
self.assertEqual({('tree-root', 'rev2'):(('tree-root', 'rev1'),)},
164
repo.texts.get_parent_map([('tree-root', 'rev2')]))
168
def test_fetch_incompatible(self):
169
knit_tree = self.make_branch_and_tree('knit', format='knit')
170
knit3_tree = self.make_branch_and_tree('knit3',
171
format='dirstate-with-subtree')
172
knit3_tree.commit('blah')
173
self.assertRaises(errors.IncompatibleRepositories,
174
knit_tree.branch.fetch, knit3_tree.branch)
117
177
class TestMergeFetch(TestCaseWithTransport):
199
260
def _count_log_matches(self, target, logs):
200
261
"""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__)
262
get_succeeds_re = re.compile(
263
'.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
264
( target, bzrlib.__version__))
204
266
for line in logs:
205
# TODO: perhaps use a regexp instead so we can match more
207
if line.find(log_pattern) > -1:
267
if get_succeeds_re.match(line):
211
271
def test_weaves_are_retrieved_once(self):
212
272
self.build_tree(("source/", "source/file", "target/"))
213
wt = self.make_branch_and_tree('source')
273
# This test depends on knit dasta storage.
274
wt = self.make_branch_and_tree('source', format='dirstate-tags')
214
275
branch = wt.branch
215
276
wt.add(["file"], ["id"])
216
277
wt.commit("added file")
217
print >>open("source/file", 'w'), "blah"
278
open("source/file", 'w').write("blah\n")
218
279
wt.commit("changed file")
219
280
target = BzrDir.create_branch_and_repo("target/")
220
281
source = Branch.open(self.get_readonly_url("source/"))
221
282
self.assertEqual(target.fetch(source), (2, []))
222
log_pattern = '%%s HTTP/1.1" 200 - "-" "bzr/%s' % bzrlib.__version__
223
283
# this is the path to the literal file. As format changes
224
284
# occur it needs to be updated. FIXME: ask the store for the
226
286
self.log("web server logs are:")
227
287
http_logs = self.get_readonly_server().logs
228
288
self.log('\n'.join(http_logs))
229
self.assertEqual(1, self._count_log_matches('weaves/ce/id.weave', http_logs))
230
self.assertEqual(1, self._count_log_matches('inventory.weave', http_logs))
289
# unfortunately this log entry is branch format specific. We could
290
# factor out the 'what files does this format use' to a method on the
291
# repository, which would let us to this generically. RBC 20060419
292
# RBC 20080408: Or perhaps we can assert that no files are fully read
294
self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
295
self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
296
self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
231
297
# this r-h check test will prevent regressions, but it currently already
232
298
# passes, before the patch to cache-rh is applied :[
233
self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
299
self.assertTrue(1 >= self._count_log_matches('revision-history',
301
self.assertTrue(1 >= self._count_log_matches('last-revision',
234
303
# FIXME naughty poking in there.
235
304
self.get_readonly_server().logs = []
236
# check there is nothing more to fetch
237
source = Branch.open(self.get_readonly_url("source/"))
305
# check there is nothing more to fetch. We take care to re-use the
306
# existing transport so that the request logs we're about to examine
307
# aren't cluttered with redundant probes for a smart server.
308
# XXX: Perhaps this further parameterisation: test http with smart
309
# server, and test http without smart server?
310
source = Branch.open(
311
self.get_readonly_url("source/"),
312
possible_transports=[source.bzrdir.root_transport])
238
313
self.assertEqual(target.fetch(source), (0, []))
239
314
# should make just two requests
240
315
http_logs = self.get_readonly_server().logs
241
316
self.log("web server logs are:")
242
317
self.log('\n'.join(http_logs))
243
self.assertEqual(1, self._count_log_matches('branch-format', http_logs[0:1]))
244
self.assertEqual(1, self._count_log_matches('revision-history', http_logs[1:2]))
245
self.assertEqual(2, len(http_logs))
318
self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
319
self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
320
self.assertEqual(1, self._count_log_matches('repository/format',
322
self.assertTrue(1 >= self._count_log_matches('revision-history',
324
self.assertTrue(1 >= self._count_log_matches('last-revision',
326
self.assertEqual(4, len(http_logs))
329
class Test1To2Fetch(TestCaseWithTransport):
330
"""Tests for Model1To2 failure modes"""
332
def make_tree_and_repo(self):
333
self.tree = self.make_branch_and_tree('tree', format='pack-0.92')
334
self.repo = self.make_repository('rich-repo', format='rich-root-pack')
335
self.repo.lock_write()
336
self.addCleanup(self.repo.unlock)
338
def do_fetch_order_test(self, first, second):
339
"""Test that fetch works no matter what the set order of revision is.
341
This test depends on the order of items in a set, which is
342
implementation-dependant, so we test A, B and then B, A.
344
self.make_tree_and_repo()
345
self.tree.commit('Commit 1', rev_id=first)
346
self.tree.commit('Commit 2', rev_id=second)
347
self.repo.fetch(self.tree.branch.repository, second)
349
def test_fetch_order_AB(self):
350
"""See do_fetch_order_test"""
351
self.do_fetch_order_test('A', 'B')
353
def test_fetch_order_BA(self):
354
"""See do_fetch_order_test"""
355
self.do_fetch_order_test('B', 'A')
357
def get_parents(self, file_id, revision_id):
358
self.repo.lock_read()
360
parent_map = self.repo.texts.get_parent_map([(file_id, revision_id)])
361
return parent_map[(file_id, revision_id)]
365
def test_fetch_ghosts(self):
366
self.make_tree_and_repo()
367
self.tree.commit('first commit', rev_id='left-parent')
368
self.tree.add_parent_tree_id('ghost-parent')
369
fork = self.tree.bzrdir.sprout('fork', 'null:').open_workingtree()
370
fork.commit('not a ghost', rev_id='not-ghost-parent')
371
self.tree.branch.repository.fetch(fork.branch.repository,
373
self.tree.add_parent_tree_id('not-ghost-parent')
374
self.tree.commit('second commit', rev_id='second-id')
375
self.repo.fetch(self.tree.branch.repository, 'second-id')
376
root_id = self.tree.get_root_id()
378
((root_id, 'left-parent'), (root_id, 'ghost-parent'),
379
(root_id, 'not-ghost-parent')),
380
self.get_parents(root_id, 'second-id'))
382
def make_two_commits(self, change_root, fetch_twice):
383
self.make_tree_and_repo()
384
self.tree.commit('first commit', rev_id='first-id')
386
self.tree.set_root_id('unique-id')
387
self.tree.commit('second commit', rev_id='second-id')
389
self.repo.fetch(self.tree.branch.repository, 'first-id')
390
self.repo.fetch(self.tree.branch.repository, 'second-id')
392
def test_fetch_changed_root(self):
393
self.make_two_commits(change_root=True, fetch_twice=False)
394
self.assertEqual((), self.get_parents('unique-id', 'second-id'))
396
def test_two_fetch_changed_root(self):
397
self.make_two_commits(change_root=True, fetch_twice=True)
398
self.assertEqual((), self.get_parents('unique-id', 'second-id'))
400
def test_two_fetches(self):
401
self.make_two_commits(change_root=False, fetch_twice=True)
402
self.assertEqual((('TREE_ROOT', 'first-id'),),
403
self.get_parents('TREE_ROOT', 'second-id'))