/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Robert Collins
  • Date: 2009-03-02 03:38:07 UTC
  • mto: This revision was merged to the branch mainline in revision 4067.
  • Revision ID: robertc@robertcollins.net-20090302033807-af1b7awmfueyawts
Streaming fetch from remote servers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007, 2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2007 Canonical Ltd
2
2
#
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
12
12
#
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
16
16
 
17
17
import os
18
18
import re
31
31
from bzrlib.bzrdir import BzrDir
32
32
from bzrlib.repofmt import knitrepo
33
33
from bzrlib.tests import TestCaseWithTransport
 
34
from bzrlib.tests.http_utils import TestCaseWithWebserver
34
35
from bzrlib.tests.test_revision import make_branches
35
36
from bzrlib.trace import mutter
36
37
from bzrlib.upgrade import Convert
37
38
from bzrlib.workingtree import WorkingTree
38
39
 
39
40
# These tests are a bit old; please instead add new tests into
40
 
# per_interrepository/ so they'll run on all relevant
 
41
# interrepository_implementations/ so they'll run on all relevant
41
42
# combinations.
42
43
 
43
44
 
52
53
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
53
54
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
54
55
    self.assertEquals(len(br_b.revision_history()), 7)
55
 
    br_b.fetch(br_a, br_a.revision_history()[2])
 
56
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
56
57
    # branch.fetch is not supposed to alter the revision history
57
58
    self.assertEquals(len(br_b.revision_history()), 7)
58
59
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
59
60
 
60
61
    # fetching the next revision up in sample data copies one revision
61
 
    br_b.fetch(br_a, br_a.revision_history()[3])
 
62
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
62
63
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
63
64
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
64
65
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
66
67
    # When a non-branch ancestor is missing, it should be unlisted...
67
68
    # as its not reference from the inventory weave.
68
69
    br_b4 = self.make_branch('br_4')
69
 
    br_b4.fetch(br_b)
 
70
    count, failures = br_b4.fetch(br_b)
 
71
    self.assertEqual(count, 7)
 
72
    self.assertEqual(failures, [])
70
73
 
71
 
    writable_a.fetch(br_b)
 
74
    self.assertEqual(writable_a.fetch(br_b)[0], 1)
72
75
    self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
73
76
    self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
74
77
 
75
78
    br_b2 = self.make_branch('br_b2')
76
 
    br_b2.fetch(br_b)
 
79
    self.assertEquals(br_b2.fetch(br_b)[0], 7)
77
80
    self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
78
81
    self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
79
82
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
80
83
 
81
84
    br_a2 = self.make_branch('br_a2')
82
 
    br_a2.fetch(br_a)
 
85
    self.assertEquals(br_a2.fetch(br_a)[0], 9)
83
86
    self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
84
87
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
85
88
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
87
90
    br_a3 = self.make_branch('br_a3')
88
91
    # pulling a branch with no revisions grabs nothing, regardless of
89
92
    # whats in the inventory.
90
 
    br_a3.fetch(br_a2)
 
93
    self.assertEquals(br_a3.fetch(br_a2)[0], 0)
91
94
    for revno in range(4):
92
95
        self.assertFalse(
93
96
            br_a3.repository.has_revision(br_a.revision_history()[revno]))
94
 
    br_a3.fetch(br_a2, br_a.revision_history()[2])
 
97
    self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
95
98
    # pull the 3 revisions introduced by a@u-0-3
96
 
    br_a3.fetch(br_a2, br_a.revision_history()[3])
97
 
    # NoSuchRevision should be raised if the branch is missing the revision
 
99
    fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
 
100
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
 
101
    # InstallFailed should be raised if the branch is missing the revision
98
102
    # that was requested.
99
 
    self.assertRaises(errors.NoSuchRevision, br_a3.fetch, br_a2, 'pizza')
 
103
    self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
100
104
 
101
105
    # TODO: Test trying to fetch from a branch that points to a revision not
102
106
    # actually present in its repository.  Not every branch format allows you
118
122
 
119
123
    def test_fetch_self(self):
120
124
        wt = self.make_branch_and_tree('br')
121
 
        wt.branch.fetch(wt.branch)
 
125
        self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
122
126
 
123
127
    def test_fetch_root_knit(self):
124
128
        """Ensure that knit2.fetch() updates the root knit
247
251
                    rev_id).get_file_text('this-file-id'), text)
248
252
 
249
253
 
 
254
class TestHttpFetch(TestCaseWithWebserver):
 
255
    # FIXME RBC 20060124 this really isn't web specific, perhaps an
 
256
    # instrumented readonly transport? Can we do an instrumented
 
257
    # adapter and use self.get_readonly_url ?
 
258
 
 
259
    def test_fetch(self):
 
260
        #highest indices a: 5, b: 7
 
261
        br_a, br_b = make_branches(self)
 
262
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
 
263
        fetch_steps(self, br_rem_a, br_b, br_a)
 
264
 
 
265
    def _count_log_matches(self, target, logs):
 
266
        """Count the number of times the target file pattern was fetched in an http log"""
 
267
        get_succeeds_re = re.compile(
 
268
            '.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
 
269
            (     target,                    bzrlib.__version__))
 
270
        c = 0
 
271
        for line in logs:
 
272
            if get_succeeds_re.match(line):
 
273
                c += 1
 
274
        return c
 
275
 
 
276
    def test_weaves_are_retrieved_once(self):
 
277
        self.build_tree(("source/", "source/file", "target/"))
 
278
        # This test depends on knit dasta storage.
 
279
        wt = self.make_branch_and_tree('source', format='dirstate-tags')
 
280
        branch = wt.branch
 
281
        wt.add(["file"], ["id"])
 
282
        wt.commit("added file")
 
283
        open("source/file", 'w').write("blah\n")
 
284
        wt.commit("changed file")
 
285
        target = BzrDir.create_branch_and_repo("target/")
 
286
        source = Branch.open(self.get_readonly_url("source/"))
 
287
        self.assertEqual(target.fetch(source), (2, []))
 
288
        # this is the path to the literal file. As format changes
 
289
        # occur it needs to be updated. FIXME: ask the store for the
 
290
        # path.
 
291
        self.log("web server logs are:")
 
292
        http_logs = self.get_readonly_server().logs
 
293
        self.log('\n'.join(http_logs))
 
294
        # unfortunately this log entry is branch format specific. We could
 
295
        # factor out the 'what files does this format use' to a method on the
 
296
        # repository, which would let us to this generically. RBC 20060419
 
297
        # RBC 20080408: Or perhaps we can assert that no files are fully read
 
298
        # twice?
 
299
        self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
 
300
        self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
 
301
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
 
302
        # this r-h check test will prevent regressions, but it currently already
 
303
        # passes, before the patch to cache-rh is applied :[
 
304
        self.assertTrue(1 >= self._count_log_matches('revision-history',
 
305
                                                     http_logs))
 
306
        self.assertTrue(1 >= self._count_log_matches('last-revision',
 
307
                                                     http_logs))
 
308
        # FIXME naughty poking in there.
 
309
        self.get_readonly_server().logs = []
 
310
        # check there is nothing more to fetch.  We take care to re-use the
 
311
        # existing transport so that the request logs we're about to examine
 
312
        # aren't cluttered with redundant probes for a smart server.
 
313
        # XXX: Perhaps this further parameterisation: test http with smart
 
314
        # server, and test http without smart server?
 
315
        source = Branch.open(
 
316
            self.get_readonly_url("source/"),
 
317
            possible_transports=[source.bzrdir.root_transport])
 
318
        self.assertEqual(target.fetch(source), (0, []))
 
319
        # should make just two requests
 
320
        http_logs = self.get_readonly_server().logs
 
321
        self.log("web server logs are:")
 
322
        self.log('\n'.join(http_logs))
 
323
        self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
 
324
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
 
325
        self.assertEqual(1, self._count_log_matches('repository/format',
 
326
            http_logs))
 
327
        self.assertTrue(1 >= self._count_log_matches('revision-history',
 
328
                                                     http_logs))
 
329
        self.assertTrue(1 >= self._count_log_matches('last-revision',
 
330
                                                     http_logs))
 
331
        self.assertEqual(4, len(http_logs))
 
332
 
 
333
 
250
334
class TestKnitToPackFetch(TestCaseWithTransport):
251
335
 
252
 
    def find_get_record_stream(self, calls, expected_count=1):
253
 
        """In a list of calls, find the last 'get_record_stream'.
 
336
    def find_get_record_stream(self, calls):
 
337
        """In a list of calls, find 'get_record_stream' calls.
254
338
 
255
 
        :param expected_count: The number of calls we should exepect to find.
256
 
            If a different number is found, an assertion is raised.
 
339
        This also ensures that there is only one get_record_stream call.
257
340
        """
258
341
        get_record_call = None
259
 
        call_count = 0
260
342
        for call in calls:
261
343
            if call[0] == 'get_record_stream':
262
 
                call_count += 1
 
344
                self.assertIs(None, get_record_call,
 
345
                              "there should only be one call to"
 
346
                              " get_record_stream")
263
347
                get_record_call = call
264
 
        self.assertEqual(expected_count, call_count)
 
348
        self.assertIsNot(None, get_record_call,
 
349
                         "there should be exactly one call to "
 
350
                         " get_record_stream")
265
351
        return get_record_call
266
352
 
267
353
    def test_fetch_with_deltas_no_delta_closure(self):
287
373
                          target._format._fetch_order, False),
288
374
                         self.find_get_record_stream(source.texts.calls))
289
375
        self.assertEqual(('get_record_stream', [('rev-one',)],
290
 
          target._format._fetch_order, False),
291
 
          self.find_get_record_stream(source.inventories.calls, 2))
 
376
                          target._format._fetch_order, False),
 
377
                         self.find_get_record_stream(source.inventories.calls))
292
378
        self.assertEqual(('get_record_stream', [('rev-one',)],
293
379
                          target._format._fetch_order, False),
294
380
                         self.find_get_record_stream(source.revisions.calls))
321
407
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
322
408
                        source.inventories)
323
409
        # XXX: This won't work in general, but for the dirstate format it does.
324
 
        self.overrideAttr(target._format, '_fetch_uses_deltas', False)
 
410
        old_fetch_uses_deltas_setting = target._format._fetch_uses_deltas
 
411
        def restore():
 
412
            target._format._fetch_uses_deltas = old_fetch_uses_deltas_setting
 
413
        self.addCleanup(restore)
 
414
        target._format._fetch_uses_deltas = False
325
415
        target.fetch(source, revision_id='rev-one')
326
416
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
327
417
                          target._format._fetch_order, True),
328
418
                         self.find_get_record_stream(source.texts.calls))
329
419
        self.assertEqual(('get_record_stream', [('rev-one',)],
330
 
            target._format._fetch_order, True),
331
 
            self.find_get_record_stream(source.inventories.calls, 2))
 
420
                          target._format._fetch_order, True),
 
421
                         self.find_get_record_stream(source.inventories.calls))
332
422
        self.assertEqual(('get_record_stream', [('rev-one',)],
333
423
                          target._format._fetch_order, True),
334
424
                         self.find_get_record_stream(source.revisions.calls))
491
581
        self.repo.fetch(self.tree.branch.repository, 'second-id')
492
582
        root_id = self.tree.get_root_id()
493
583
        self.assertEqual(
494
 
            ((root_id, 'left-parent'), (root_id, 'not-ghost-parent')),
 
584
            ((root_id, 'left-parent'), (root_id, 'ghost-parent'),
 
585
             (root_id, 'not-ghost-parent')),
495
586
            self.get_parents(root_id, 'second-id'))
496
587
 
497
588
    def make_two_commits(self, change_root, fetch_twice):