~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-12-05 13:51:54 UTC
  • mfrom: (3878.1.3 sorted_get_record_stream)
  • Revision ID: pqm@pqm.ubuntu.com-20081205135154-uwqcpl3lruah9fo3
(jam) Use I/O order for an 'unordered' get_record_stream request.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
22
22
from bzrlib import (
23
23
    bzrdir,
24
24
    errors,
25
 
    osutils,
26
25
    merge,
27
26
    repository,
28
27
    versionedfile,
47
46
 
48
47
def fetch_steps(self, br_a, br_b, writable_a):
49
48
    """A foreign test method for testing fetch locally and remotely."""
50
 
 
 
49
     
51
50
    # TODO RBC 20060201 make this a repository test.
52
51
    repo_b = br_b.repository
53
52
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
54
53
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
55
54
    self.assertEquals(len(br_b.revision_history()), 7)
56
 
    br_b.fetch(br_a, br_a.revision_history()[2])
 
55
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
57
56
    # branch.fetch is not supposed to alter the revision history
58
57
    self.assertEquals(len(br_b.revision_history()), 7)
59
58
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
60
59
 
61
60
    # fetching the next revision up in sample data copies one revision
62
 
    br_b.fetch(br_a, br_a.revision_history()[3])
 
61
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
63
62
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
64
63
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
65
64
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
67
66
    # When a non-branch ancestor is missing, it should be unlisted...
68
67
    # as its not reference from the inventory weave.
69
68
    br_b4 = self.make_branch('br_4')
70
 
    br_b4.fetch(br_b)
 
69
    count, failures = br_b4.fetch(br_b)
 
70
    self.assertEqual(count, 7)
 
71
    self.assertEqual(failures, [])
71
72
 
72
 
    writable_a.fetch(br_b)
 
73
    self.assertEqual(writable_a.fetch(br_b)[0], 1)
73
74
    self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
74
75
    self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
75
 
 
 
76
        
76
77
    br_b2 = self.make_branch('br_b2')
77
 
    br_b2.fetch(br_b)
 
78
    self.assertEquals(br_b2.fetch(br_b)[0], 7)
78
79
    self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
79
80
    self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
80
81
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
81
82
 
82
83
    br_a2 = self.make_branch('br_a2')
83
 
    br_a2.fetch(br_a)
 
84
    self.assertEquals(br_a2.fetch(br_a)[0], 9)
84
85
    self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
85
86
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
86
87
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
87
88
 
88
89
    br_a3 = self.make_branch('br_a3')
89
 
    # pulling a branch with no revisions grabs nothing, regardless of
 
90
    # pulling a branch with no revisions grabs nothing, regardless of 
90
91
    # whats in the inventory.
91
 
    br_a3.fetch(br_a2)
 
92
    self.assertEquals(br_a3.fetch(br_a2)[0], 0)
92
93
    for revno in range(4):
93
94
        self.assertFalse(
94
95
            br_a3.repository.has_revision(br_a.revision_history()[revno]))
95
 
    br_a3.fetch(br_a2, br_a.revision_history()[2])
 
96
    self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
96
97
    # pull the 3 revisions introduced by a@u-0-3
97
 
    br_a3.fetch(br_a2, br_a.revision_history()[3])
98
 
    # NoSuchRevision should be raised if the branch is missing the revision
 
98
    fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
 
99
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
 
100
    # InstallFailed should be raised if the branch is missing the revision
99
101
    # that was requested.
100
 
    self.assertRaises(errors.NoSuchRevision, br_a3.fetch, br_a2, 'pizza')
 
102
    self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
101
103
 
102
104
    # TODO: Test trying to fetch from a branch that points to a revision not
103
105
    # actually present in its repository.  Not every branch format allows you
106
108
    # every branch supports that.  -- mbp 20070814
107
109
 
108
110
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
109
 
    # Note that this means - updating the weave when ghosts are filled in to
 
111
    # Note that this means - updating the weave when ghosts are filled in to 
110
112
    # add the right parents.
111
113
 
112
114
 
119
121
 
120
122
    def test_fetch_self(self):
121
123
        wt = self.make_branch_and_tree('br')
122
 
        wt.branch.fetch(wt.branch)
 
124
        self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
123
125
 
124
126
    def test_fetch_root_knit(self):
125
127
        """Ensure that knit2.fetch() updates the root knit
126
 
 
 
128
        
127
129
        This tests the case where the root has a new revision, but there are no
128
130
        corresponding filename, parent, contents or other changes.
129
131
        """
281
283
        wt.commit("changed file")
282
284
        target = BzrDir.create_branch_and_repo("target/")
283
285
        source = Branch.open(self.get_readonly_url("source/"))
284
 
        target.fetch(source)
285
 
        # this is the path to the literal file. As format changes
 
286
        self.assertEqual(target.fetch(source), (2, []))
 
287
        # this is the path to the literal file. As format changes 
286
288
        # occur it needs to be updated. FIXME: ask the store for the
287
289
        # path.
288
290
        self.log("web server logs are:")
289
291
        http_logs = self.get_readonly_server().logs
290
292
        self.log('\n'.join(http_logs))
291
 
        # unfortunately this log entry is branch format specific. We could
292
 
        # factor out the 'what files does this format use' to a method on the
 
293
        # unfortunately this log entry is branch format specific. We could 
 
294
        # factor out the 'what files does this format use' to a method on the 
293
295
        # repository, which would let us to this generically. RBC 20060419
294
296
        # RBC 20080408: Or perhaps we can assert that no files are fully read
295
297
        # twice?
296
298
        self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
297
299
        self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
298
300
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
299
 
        # this r-h check test will prevent regressions, but it currently already
 
301
        # this r-h check test will prevent regressions, but it currently already 
300
302
        # passes, before the patch to cache-rh is applied :[
301
303
        self.assertTrue(1 >= self._count_log_matches('revision-history',
302
304
                                                     http_logs))
312
314
        source = Branch.open(
313
315
            self.get_readonly_url("source/"),
314
316
            possible_transports=[source.bzrdir.root_transport])
315
 
        target.fetch(source)
 
317
        self.assertEqual(target.fetch(source), (0, []))
316
318
        # should make just two requests
317
319
        http_logs = self.get_readonly_server().logs
318
320
        self.log("web server logs are:")
321
323
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
322
324
        self.assertEqual(1, self._count_log_matches('repository/format',
323
325
            http_logs))
324
 
        self.assertEqual(1, self._count_log_matches('revisions.kndx',
325
 
            http_logs))
326
326
        self.assertTrue(1 >= self._count_log_matches('revision-history',
327
327
                                                     http_logs))
328
328
        self.assertTrue(1 >= self._count_log_matches('last-revision',
329
329
                                                     http_logs))
330
 
        self.assertLength(5, http_logs)
 
330
        self.assertEqual(4, len(http_logs))
331
331
 
332
332
 
333
333
class TestKnitToPackFetch(TestCaseWithTransport):
334
334
 
335
 
    def find_get_record_stream(self, calls, expected_count=1):
336
 
        """In a list of calls, find the last 'get_record_stream'.
 
335
    def find_get_record_stream(self, calls):
 
336
        """In a list of calls, find 'get_record_stream' calls.
337
337
 
338
 
        :param expected_count: The number of calls we should exepect to find.
339
 
            If a different number is found, an assertion is raised.
 
338
        This also ensures that there is only one get_record_stream call.
340
339
        """
341
340
        get_record_call = None
342
 
        call_count = 0
343
341
        for call in calls:
344
342
            if call[0] == 'get_record_stream':
345
 
                call_count += 1
 
343
                self.assertIs(None, get_record_call,
 
344
                              "there should only be one call to"
 
345
                              " get_record_stream")
346
346
                get_record_call = call
347
 
        self.assertEqual(expected_count, call_count)
 
347
        self.assertIsNot(None, get_record_call,
 
348
                         "there should be exactly one call to "
 
349
                         " get_record_stream")
348
350
        return get_record_call
349
351
 
350
352
    def test_fetch_with_deltas_no_delta_closure(self):
364
366
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
365
367
                        source.inventories)
366
368
        # precondition
367
 
        self.assertTrue(target._format._fetch_uses_deltas)
 
369
        self.assertTrue(target._fetch_uses_deltas)
368
370
        target.fetch(source, revision_id='rev-one')
369
371
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
370
 
                          target._format._fetch_order, False),
 
372
                          target._fetch_order, False),
371
373
                         self.find_get_record_stream(source.texts.calls))
372
374
        self.assertEqual(('get_record_stream', [('rev-one',)],
373
 
          target._format._fetch_order, False),
374
 
          self.find_get_record_stream(source.inventories.calls, 2))
 
375
                          target._fetch_order, False),
 
376
                         self.find_get_record_stream(source.inventories.calls))
375
377
        self.assertEqual(('get_record_stream', [('rev-one',)],
376
 
                          target._format._fetch_order, False),
 
378
                          target._fetch_order, False),
377
379
                         self.find_get_record_stream(source.revisions.calls))
378
380
        # XXX: Signatures is special, and slightly broken. The
379
381
        # standard item_keys_introduced_by actually does a lookup for every
384
386
        # we care about.
385
387
        signature_calls = source.signatures.calls[-1:]
386
388
        self.assertEqual(('get_record_stream', [('rev-one',)],
387
 
                          target._format._fetch_order, False),
 
389
                          target._fetch_order, False),
388
390
                         self.find_get_record_stream(signature_calls))
389
391
 
390
392
    def test_fetch_no_deltas_with_delta_closure(self):
403
405
                        source.revisions)
404
406
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
405
407
                        source.inventories)
406
 
        # XXX: This won't work in general, but for the dirstate format it does.
407
 
        old_fetch_uses_deltas_setting = target._format._fetch_uses_deltas
408
 
        def restore():
409
 
            target._format._fetch_uses_deltas = old_fetch_uses_deltas_setting
410
 
        self.addCleanup(restore)
411
 
        target._format._fetch_uses_deltas = False
 
408
        target._fetch_uses_deltas = False
412
409
        target.fetch(source, revision_id='rev-one')
413
410
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
414
 
                          target._format._fetch_order, True),
 
411
                          target._fetch_order, True),
415
412
                         self.find_get_record_stream(source.texts.calls))
416
413
        self.assertEqual(('get_record_stream', [('rev-one',)],
417
 
            target._format._fetch_order, True),
418
 
            self.find_get_record_stream(source.inventories.calls, 2))
 
414
                          target._fetch_order, True),
 
415
                         self.find_get_record_stream(source.inventories.calls))
419
416
        self.assertEqual(('get_record_stream', [('rev-one',)],
420
 
                          target._format._fetch_order, True),
 
417
                          target._fetch_order, True),
421
418
                         self.find_get_record_stream(source.revisions.calls))
422
419
        # XXX: Signatures is special, and slightly broken. The
423
420
        # standard item_keys_introduced_by actually does a lookup for every
428
425
        # we care about.
429
426
        signature_calls = source.signatures.calls[-1:]
430
427
        self.assertEqual(('get_record_stream', [('rev-one',)],
431
 
                          target._format._fetch_order, True),
 
428
                          target._fetch_order, True),
432
429
                         self.find_get_record_stream(signature_calls))
433
430
 
434
431
    def test_fetch_revisions_with_deltas_into_pack(self):
461
458
            'unordered', False).next()
462
459
        self.assertEqual('knit-ft-gz', record.storage_kind)
463
460
 
464
 
    def test_fetch_with_fallback_and_merge(self):
465
 
        builder = self.make_branch_builder('source', format='pack-0.92')
466
 
        builder.start_series()
467
 
        # graph
468
 
        #   A
469
 
        #   |\
470
 
        #   B C
471
 
        #   | |
472
 
        #   | D
473
 
        #   | |
474
 
        #   | E
475
 
        #    \|
476
 
        #     F
477
 
        # A & B are present in the base (stacked-on) repository, A-E are
478
 
        # present in the source.
479
 
        # This reproduces bug #304841
480
 
        # We need a large enough inventory that total size of compressed deltas
481
 
        # is shorter than the size of a compressed fulltext. We have to use
482
 
        # random ids because otherwise the inventory fulltext compresses too
483
 
        # well and the deltas get bigger.
484
 
        to_add = [
485
 
            ('add', ('', 'TREE_ROOT', 'directory', None))]
486
 
        for i in xrange(10):
487
 
            fname = 'file%03d' % (i,)
488
 
            fileid = '%s-%s' % (fname, osutils.rand_chars(64))
489
 
            to_add.append(('add', (fname, fileid, 'file', 'content\n')))
490
 
        builder.build_snapshot('A', None, to_add)
491
 
        builder.build_snapshot('B', ['A'], [])
492
 
        builder.build_snapshot('C', ['A'], [])
493
 
        builder.build_snapshot('D', ['C'], [])
494
 
        builder.build_snapshot('E', ['D'], [])
495
 
        builder.build_snapshot('F', ['E', 'B'], [])
496
 
        builder.finish_series()
497
 
        source_branch = builder.get_branch()
498
 
        source_branch.bzrdir.sprout('base', revision_id='B')
499
 
        target_branch = self.make_branch('target', format='1.6')
500
 
        target_branch.set_stacked_on_url('../base')
501
 
        source = source_branch.repository
502
 
        source.lock_read()
503
 
        self.addCleanup(source.unlock)
504
 
        source.inventories = versionedfile.OrderingVersionedFilesDecorator(
505
 
                        source.inventories,
506
 
                        key_priority={('E',): 1, ('D',): 2, ('C',): 4,
507
 
                                      ('F',): 3})
508
 
        # Ensure that the content is yielded in the proper order, and given as
509
 
        # the expected kinds
510
 
        records = [(record.key, record.storage_kind)
511
 
                   for record in source.inventories.get_record_stream(
512
 
                        [('D',), ('C',), ('E',), ('F',)], 'unordered', False)]
513
 
        self.assertEqual([(('E',), 'knit-delta-gz'), (('D',), 'knit-delta-gz'),
514
 
                          (('F',), 'knit-delta-gz'), (('C',), 'knit-delta-gz')],
515
 
                          records)
516
 
 
517
 
        target_branch.lock_write()
518
 
        self.addCleanup(target_branch.unlock)
519
 
        target = target_branch.repository
520
 
        target.fetch(source, revision_id='F')
521
 
        # 'C' should be expanded to a fulltext, but D and E should still be
522
 
        # deltas
523
 
        stream = target.inventories.get_record_stream(
524
 
            [('C',), ('D',), ('E',), ('F',)],
525
 
            'unordered', False)
526
 
        kinds = dict((record.key, record.storage_kind) for record in stream)
527
 
        self.assertEqual({('C',): 'knit-ft-gz', ('D',): 'knit-delta-gz',
528
 
                          ('E',): 'knit-delta-gz', ('F',): 'knit-delta-gz'},
529
 
                         kinds)
530
461
 
531
462
 
532
463
class Test1To2Fetch(TestCaseWithTransport):
578
509
        self.repo.fetch(self.tree.branch.repository, 'second-id')
579
510
        root_id = self.tree.get_root_id()
580
511
        self.assertEqual(
581
 
            ((root_id, 'left-parent'), (root_id, 'not-ghost-parent')),
 
512
            ((root_id, 'left-parent'), (root_id, 'ghost-parent'),
 
513
             (root_id, 'not-ghost-parent')),
582
514
            self.get_parents(root_id, 'second-id'))
583
515
 
584
516
    def make_two_commits(self, change_root, fetch_twice):