~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_interrepository/test_fetch.py

  • Committer: Martin Pool
  • Date: 2009-09-14 01:48:28 UTC
  • mfrom: (4685 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4688.
  • Revision ID: mbp@sourcefrog.net-20090914014828-ydr9rlkdfq2sv57z
Merge news

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
import sys
19
19
 
20
 
import bzrlib
21
20
from bzrlib import (
22
21
    errors,
23
22
    inventory,
28
27
from bzrlib.errors import (
29
28
    NoSuchRevision,
30
29
    )
 
30
from bzrlib.graph import (
 
31
    SearchResult,
 
32
    )
31
33
from bzrlib.revision import (
32
34
    NULL_REVISION,
33
35
    Revision,
43
45
    )
44
46
 
45
47
 
 
48
 
46
49
class TestInterRepository(TestCaseWithInterRepository):
47
50
 
 
51
    def disable_commit_write_group_paranoia(self, repo):
 
52
        pack_coll = getattr(repo, '_pack_collection', None)
 
53
        if pack_coll is not None:
 
54
            # Monkey-patch the pack collection instance to allow storing
 
55
            # incomplete revisions.
 
56
            pack_coll._check_new_inventories = lambda: []
 
57
 
48
58
    def test_fetch(self):
49
59
        tree_a = self.make_branch_and_tree('a')
50
60
        self.build_tree(['a/foo'])
124
134
            to_repo.texts.get_record_stream([('foo', revid)],
125
135
            'unordered', True).next().get_bytes_as('fulltext'))
126
136
 
 
137
    def test_fetch_parent_inventories_at_stacking_boundary_smart(self):
 
138
        self.setup_smart_server_with_call_log()
 
139
        self.test_fetch_parent_inventories_at_stacking_boundary()
 
140
 
 
141
    def test_fetch_parent_inventories_at_stacking_boundary_smart_old(self):
 
142
        self.setup_smart_server_with_call_log()
 
143
        self.disable_verb('Repository.insert_stream_1.19')
 
144
        self.test_fetch_parent_inventories_at_stacking_boundary()
 
145
 
127
146
    def test_fetch_parent_inventories_at_stacking_boundary(self):
128
147
        """Fetch to a stacked branch copies inventories for parents of
129
148
        revisions at the stacking boundary.
132
151
        altered by all revisions it contains, which means that it needs both
133
152
        the inventory for any revision it has, and the inventories of all that
134
153
        revision's parents.
 
154
 
 
155
        However, we should also skip any revisions which are ghosts in the
 
156
        parents.
135
157
        """
136
 
        to_repo = self.make_to_repository('to')
137
 
        if not to_repo._format.supports_external_lookups:
 
158
        if not self.repository_format_to.supports_external_lookups:
138
159
            raise TestNotApplicable("Need stacking support in the target.")
139
160
        builder = self.make_branch_builder('branch')
140
161
        builder.start_series()
141
162
        builder.build_snapshot('base', None, [
142
 
            ('add', ('', 'root-id', 'directory', ''))])
143
 
        builder.build_snapshot('left', ['base'], [])
144
 
        builder.build_snapshot('right', ['base'], [])
145
 
        builder.build_snapshot('merge', ['left', 'right'], [])
 
163
            ('add', ('', 'root-id', 'directory', '')),
 
164
            ('add', ('file', 'file-id', 'file', 'content\n'))])
 
165
        builder.build_snapshot('left', ['base'], [
 
166
            ('modify', ('file-id', 'left content\n'))])
 
167
        builder.build_snapshot('right', ['base'], [
 
168
            ('modify', ('file-id', 'right content\n'))])
 
169
        builder.build_snapshot('merge', ['left', 'right'], [
 
170
            ('modify', ('file-id', 'left and right content\n'))])
146
171
        builder.finish_series()
147
172
        branch = builder.get_branch()
148
173
        repo = self.make_to_repository('trunk')
161
186
        self.assertEqual(
162
187
            set([('left',), ('right',), ('merge',)]),
163
188
            unstacked_repo.inventories.keys())
 
189
        # And the basis inventories have been copied correctly
 
190
        trunk.lock_read()
 
191
        self.addCleanup(trunk.unlock)
 
192
        left_tree, right_tree = trunk.repository.revision_trees(
 
193
            ['left', 'right'])
 
194
        stacked_branch.lock_read()
 
195
        self.addCleanup(stacked_branch.unlock)
 
196
        (stacked_left_tree,
 
197
         stacked_right_tree) = stacked_branch.repository.revision_trees(
 
198
            ['left', 'right'])
 
199
        self.assertEqual(left_tree.inventory, stacked_left_tree.inventory)
 
200
        self.assertEqual(right_tree.inventory, stacked_right_tree.inventory)
 
201
 
 
202
        # Finally, it's not enough to see that the basis inventories are
 
203
        # present.  The texts introduced in merge (and only those) should be
 
204
        # present, and also generating a stream should succeed without blowing
 
205
        # up.
 
206
        self.assertTrue(unstacked_repo.has_revision('merge'))
 
207
        expected_texts = set([('file-id', 'merge')])
 
208
        if stacked_branch.repository.texts.get_parent_map([('root-id',
 
209
            'merge')]):
 
210
            # If a (root-id,merge) text exists, it should be in the stacked
 
211
            # repo.
 
212
            expected_texts.add(('root-id', 'merge'))
 
213
        self.assertEqual(expected_texts, unstacked_repo.texts.keys())
 
214
        self.assertCanStreamRevision(unstacked_repo, 'merge')
 
215
 
 
216
    def assertCanStreamRevision(self, repo, revision_id):
 
217
        exclude_keys = set(repo.all_revision_ids()) - set([revision_id])
 
218
        search = SearchResult([revision_id], exclude_keys, 1, [revision_id])
 
219
        source = repo._get_source(repo._format)
 
220
        for substream_kind, substream in source.get_stream(search):
 
221
            # Consume the substream
 
222
            list(substream)
 
223
 
 
224
    def test_fetch_across_stacking_boundary_ignores_ghost(self):
 
225
        if not self.repository_format_to.supports_external_lookups:
 
226
            raise TestNotApplicable("Need stacking support in the target.")
 
227
        to_repo = self.make_to_repository('to')
 
228
        builder = self.make_branch_builder('branch')
 
229
        builder.start_series()
 
230
        builder.build_snapshot('base', None, [
 
231
            ('add', ('', 'root-id', 'directory', '')),
 
232
            ('add', ('file', 'file-id', 'file', 'content\n'))])
 
233
        builder.build_snapshot('second', ['base'], [
 
234
            ('modify', ('file-id', 'second content\n'))])
 
235
        builder.build_snapshot('third', ['second', 'ghost'], [
 
236
            ('modify', ('file-id', 'third content\n'))])
 
237
        builder.finish_series()
 
238
        branch = builder.get_branch()
 
239
        repo = self.make_to_repository('trunk')
 
240
        trunk = repo.bzrdir.create_branch()
 
241
        trunk.repository.fetch(branch.repository, 'second')
 
242
        repo = self.make_to_repository('stacked')
 
243
        stacked_branch = repo.bzrdir.create_branch()
 
244
        stacked_branch.set_stacked_on_url(trunk.base)
 
245
        stacked_branch.repository.fetch(branch.repository, 'third')
 
246
        unstacked_repo = stacked_branch.bzrdir.open_repository()
 
247
        unstacked_repo.lock_read()
 
248
        self.addCleanup(unstacked_repo.unlock)
 
249
        self.assertFalse(unstacked_repo.has_revision('second'))
 
250
        self.assertFalse(unstacked_repo.has_revision('ghost'))
 
251
        self.assertEqual(
 
252
            set([('second',), ('third',)]),
 
253
            unstacked_repo.inventories.keys())
 
254
        # And the basis inventories have been copied correctly
 
255
        trunk.lock_read()
 
256
        self.addCleanup(trunk.unlock)
 
257
        second_tree = trunk.repository.revision_tree('second')
 
258
        stacked_branch.lock_read()
 
259
        self.addCleanup(stacked_branch.unlock)
 
260
        stacked_second_tree = stacked_branch.repository.revision_tree('second')
 
261
        self.assertEqual(second_tree.inventory, stacked_second_tree.inventory)
 
262
        # Finally, it's not enough to see that the basis inventories are
 
263
        # present.  The texts introduced in merge (and only those) should be
 
264
        # present, and also generating a stream should succeed without blowing
 
265
        # up.
 
266
        self.assertTrue(unstacked_repo.has_revision('third'))
 
267
        expected_texts = set([('file-id', 'third')])
 
268
        if stacked_branch.repository.texts.get_parent_map([('root-id',
 
269
            'third')]):
 
270
            # If a (root-id,third) text exists, it should be in the stacked
 
271
            # repo.
 
272
            expected_texts.add(('root-id', 'third'))
 
273
        self.assertEqual(expected_texts, unstacked_repo.texts.keys())
 
274
        self.assertCanStreamRevision(unstacked_repo, 'third')
 
275
 
 
276
    def test_fetch_from_stacked_to_stacked_copies_parent_inventories(self):
 
277
        """Fetch from a stacked branch copies inventories for parents of
 
278
        revisions at the stacking boundary.
 
279
 
 
280
        Specifically, fetch will copy the parent inventories from the
 
281
        source for which the corresponding revisions are not present.  This
 
282
        will happen even when the source repository has no fallbacks configured
 
283
        (as is the case during upgrade).
 
284
        """
 
285
        if not self.repository_format.supports_external_lookups:
 
286
            raise TestNotApplicable("Need stacking support in the source.")
 
287
        if not self.repository_format_to.supports_external_lookups:
 
288
            raise TestNotApplicable("Need stacking support in the target.")
 
289
        builder = self.make_branch_builder('branch')
 
290
        builder.start_series()
 
291
        builder.build_snapshot('base', None, [
 
292
            ('add', ('', 'root-id', 'directory', '')),
 
293
            ('add', ('file', 'file-id', 'file', 'content\n'))])
 
294
        builder.build_snapshot('left', ['base'], [
 
295
            ('modify', ('file-id', 'left content\n'))])
 
296
        builder.build_snapshot('right', ['base'], [
 
297
            ('modify', ('file-id', 'right content\n'))])
 
298
        builder.build_snapshot('merge', ['left', 'right'], [
 
299
            ('modify', ('file-id', 'left and right content\n'))])
 
300
        builder.finish_series()
 
301
        branch = builder.get_branch()
 
302
        repo = self.make_repository('old-trunk')
 
303
        # Make a pair of equivalent trunk repos in the from and to formats.
 
304
        old_trunk = repo.bzrdir.create_branch()
 
305
        old_trunk.repository.fetch(branch.repository, 'left')
 
306
        old_trunk.repository.fetch(branch.repository, 'right')
 
307
        repo = self.make_to_repository('new-trunk')
 
308
        new_trunk = repo.bzrdir.create_branch()
 
309
        new_trunk.repository.fetch(branch.repository, 'left')
 
310
        new_trunk.repository.fetch(branch.repository, 'right')
 
311
        # Make the source; a repo stacked on old_trunk contained just the data
 
312
        # for 'merge'.
 
313
        repo = self.make_repository('old-stacked')
 
314
        old_stacked_branch = repo.bzrdir.create_branch()
 
315
        old_stacked_branch.set_stacked_on_url(old_trunk.base)
 
316
        old_stacked_branch.repository.fetch(branch.repository, 'merge')
 
317
        # Make the target, a repo stacked on new_trunk.
 
318
        repo = self.make_to_repository('new-stacked')
 
319
        new_stacked_branch = repo.bzrdir.create_branch()
 
320
        new_stacked_branch.set_stacked_on_url(new_trunk.base)
 
321
        old_unstacked_repo = old_stacked_branch.bzrdir.open_repository()
 
322
        new_unstacked_repo = new_stacked_branch.bzrdir.open_repository()
 
323
        # Reopen the source and target repos without any fallbacks, and fetch
 
324
        # 'merge'.
 
325
        new_unstacked_repo.fetch(old_unstacked_repo, 'merge')
 
326
        # Now check the results.  new_unstacked_repo should contain all the
 
327
        # data necessary to stream 'merge' (i.e. the parent inventories).
 
328
        new_unstacked_repo.lock_read()
 
329
        self.addCleanup(new_unstacked_repo.unlock)
 
330
        self.assertFalse(new_unstacked_repo.has_revision('left'))
 
331
        self.assertFalse(new_unstacked_repo.has_revision('right'))
 
332
        self.assertEqual(
 
333
            set([('left',), ('right',), ('merge',)]),
 
334
            new_unstacked_repo.inventories.keys())
 
335
        # And the basis inventories have been copied correctly
 
336
        new_trunk.lock_read()
 
337
        self.addCleanup(new_trunk.unlock)
 
338
        left_tree, right_tree = new_trunk.repository.revision_trees(
 
339
            ['left', 'right'])
 
340
        new_stacked_branch.lock_read()
 
341
        self.addCleanup(new_stacked_branch.unlock)
 
342
        (stacked_left_tree,
 
343
         stacked_right_tree) = new_stacked_branch.repository.revision_trees(
 
344
            ['left', 'right'])
 
345
        self.assertEqual(left_tree.inventory, stacked_left_tree.inventory)
 
346
        self.assertEqual(right_tree.inventory, stacked_right_tree.inventory)
 
347
        # Finally, it's not enough to see that the basis inventories are
 
348
        # present.  The texts introduced in merge (and only those) should be
 
349
        # present, and also generating a stream should succeed without blowing
 
350
        # up.
 
351
        self.assertTrue(new_unstacked_repo.has_revision('merge'))
 
352
        expected_texts = set([('file-id', 'merge')])
 
353
        if new_stacked_branch.repository.texts.get_parent_map([('root-id',
 
354
            'merge')]):
 
355
            # If a (root-id,merge) text exists, it should be in the stacked
 
356
            # repo.
 
357
            expected_texts.add(('root-id', 'merge'))
 
358
        self.assertEqual(expected_texts, new_unstacked_repo.texts.keys())
 
359
        self.assertCanStreamRevision(new_unstacked_repo, 'merge')
164
360
 
165
361
    def test_fetch_missing_basis_text(self):
166
362
        """If fetching a delta, we should die if a basis is not present."""
177
373
        to_repo.lock_write()
178
374
        try:
179
375
            to_repo.start_write_group()
180
 
            inv = tree.branch.repository.get_inventory('rev-one')
181
 
            to_repo.add_inventory('rev-one', inv, [])
182
 
            rev = tree.branch.repository.get_revision('rev-one')
183
 
            to_repo.add_revision('rev-one', rev, inv=inv)
184
 
            to_repo.commit_write_group()
 
376
            try:
 
377
                inv = tree.branch.repository.get_inventory('rev-one')
 
378
                to_repo.add_inventory('rev-one', inv, [])
 
379
                rev = tree.branch.repository.get_revision('rev-one')
 
380
                to_repo.add_revision('rev-one', rev, inv=inv)
 
381
                self.disable_commit_write_group_paranoia(to_repo)
 
382
                to_repo.commit_write_group()
 
383
            except:
 
384
                to_repo.abort_write_group(suppress_errors=True)
 
385
                raise
185
386
        finally:
186
387
            to_repo.unlock()
187
388
 
236
437
        source_tree.add(['id'], ['id'])
237
438
        source_tree.commit('a', rev_id='a')
238
439
        # now we manually insert a revision with an inventory referencing
239
 
        # 'id' at revision 'b', but we do not insert revision b.
 
440
        # file 'id' at revision 'b', but we do not insert revision b.
240
441
        # this should ensure that the new versions of files are being checked
241
442
        # for during pull operations
242
443
        inv = source.get_inventory('a')
254
455
                       revision_id='b')
255
456
        rev.parent_ids = ['a']
256
457
        source.add_revision('b', rev)
 
458
        self.disable_commit_write_group_paranoia(source)
257
459
        source.commit_write_group()
258
460
        self.assertRaises(errors.RevisionNotPresent, target.fetch, source)
259
461
        self.assertFalse(target.has_revision('b'))
276
478
        to_repo = self.make_to_repository('to')
277
479
        to_repo.fetch(from_tree.branch.repository)
278
480
        recorded_inv_sha1 = to_repo.get_inventory_sha1('foo-id')
279
 
        xml = to_repo.get_inventory_xml('foo-id')
280
 
        computed_inv_sha1 = osutils.sha_string(xml)
 
481
        to_repo.lock_read()
 
482
        self.addCleanup(to_repo.unlock)
 
483
        stream = to_repo.inventories.get_record_stream([('foo-id',)],
 
484
                                                       'unordered', True)
 
485
        bytes = stream.next().get_bytes_as('fulltext')
 
486
        computed_inv_sha1 = osutils.sha_string(bytes)
281
487
        self.assertEqual(computed_inv_sha1, recorded_inv_sha1)
282
488
 
283
489