/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1
# Copyright (C) 2008 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
16
17
"""Tests for pack repositories.
18
19
These tests are repeated for all pack-based repository formats.
20
"""
21
3582.3.4 by Martin Pool
Use cStringIO rather than StringIO
22
from cStringIO import StringIO
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
23
from stat import S_ISDIR
24
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
25
from bzrlib.btree_index import BTreeGraphIndex
26
from bzrlib.index import GraphIndex
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
27
from bzrlib import (
28
    bzrdir,
29
    errors,
30
    inventory,
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
31
    osutils,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
32
    progress,
33
    repository,
34
    revision as _mod_revision,
35
    symbol_versioning,
36
    tests,
37
    ui,
38
    upgrade,
39
    workingtree,
40
    )
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
41
from bzrlib.repofmt import (
42
    pack_repo,
43
    groupcompress_repo,
44
    )
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
45
from bzrlib.repofmt.groupcompress_repo import RepositoryFormatCHK1
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
46
from bzrlib.smart import (
47
    client,
48
    server,
49
    )
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
50
from bzrlib.tests import (
51
    TestCase,
52
    TestCaseWithTransport,
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
53
    TestNotApplicable,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
54
    TestSkipped,
55
    )
56
from bzrlib.transport import (
57
    fakenfs,
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
58
    memory,
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
59
    get_transport,
60
    )
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
61
from bzrlib.tests.per_repository import TestCaseWithRepository
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
62
63
64
class TestPackRepository(TestCaseWithTransport):
65
    """Tests to be repeated across all pack-based formats.
66
67
    The following are populated from the test scenario:
68
69
    :ivar format_name: Registered name fo the format to test.
70
    :ivar format_string: On-disk format marker.
71
    :ivar format_supports_external_lookups: Boolean.
72
    """
73
74
    def get_format(self):
75
        return bzrdir.format_registry.make_bzrdir(self.format_name)
76
77
    def test_attribute__fetch_order(self):
3606.7.3 by John Arbash Meinel
We don't have to fetch in topological order, as long as we fix all of the delta logic pieces.
78
        """Packs do not need ordered data retrieval."""
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
79
        format = self.get_format()
80
        repo = self.make_repository('.', format=format)
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
81
        self.assertEqual('unordered', repo._format._fetch_order)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
82
83
    def test_attribute__fetch_uses_deltas(self):
84
        """Packs reuse deltas."""
85
        format = self.get_format()
86
        repo = self.make_repository('.', format=format)
4265.1.4 by John Arbash Meinel
Special case the CHK1 format to allow it to not fetch using deltas.
87
        if isinstance(format.repository_format, RepositoryFormatCHK1):
88
            # TODO: This is currently a workaround. CHK format repositories
89
            #       ignore the 'deltas' flag, but during conversions, we can't
90
            #       do unordered delta fetches. Remove this clause once we
91
            #       improve the inter-format fetching.
92
            self.assertEqual(False, repo._format._fetch_uses_deltas)
93
        else:
94
            self.assertEqual(True, repo._format._fetch_uses_deltas)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
95
96
    def test_disk_layout(self):
97
        format = self.get_format()
98
        repo = self.make_repository('.', format=format)
99
        # in case of side effects of locking.
100
        repo.lock_write()
101
        repo.unlock()
102
        t = repo.bzrdir.get_repository_transport(None)
103
        self.check_format(t)
104
        # XXX: no locks left when unlocked at the moment
105
        # self.assertEqualDiff('', t.get('lock').read())
106
        self.check_databases(t)
107
108
    def check_format(self, t):
109
        self.assertEqualDiff(
110
            self.format_string, # from scenario
111
            t.get('format').read())
112
113
    def assertHasNoKndx(self, t, knit_name):
114
        """Assert that knit_name has no index on t."""
115
        self.assertFalse(t.has(knit_name + '.kndx'))
116
117
    def assertHasNoKnit(self, t, knit_name):
118
        """Assert that knit_name exists on t."""
119
        # no default content
120
        self.assertFalse(t.has(knit_name + '.knit'))
121
122
    def check_databases(self, t):
123
        """check knit content for a repository."""
124
        # check conversion worked
125
        self.assertHasNoKndx(t, 'inventory')
126
        self.assertHasNoKnit(t, 'inventory')
127
        self.assertHasNoKndx(t, 'revisions')
128
        self.assertHasNoKnit(t, 'revisions')
129
        self.assertHasNoKndx(t, 'signatures')
130
        self.assertHasNoKnit(t, 'signatures')
131
        self.assertFalse(t.has('knits'))
132
        # revision-indexes file-container directory
133
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
134
            list(self.index_class(t, 'pack-names', None).iter_all_entries()))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
135
        self.assertTrue(S_ISDIR(t.stat('packs').st_mode))
136
        self.assertTrue(S_ISDIR(t.stat('upload').st_mode))
137
        self.assertTrue(S_ISDIR(t.stat('indices').st_mode))
138
        self.assertTrue(S_ISDIR(t.stat('obsolete_packs').st_mode))
139
140
    def test_shared_disk_layout(self):
141
        format = self.get_format()
142
        repo = self.make_repository('.', shared=True, format=format)
143
        # we want:
144
        t = repo.bzrdir.get_repository_transport(None)
145
        self.check_format(t)
146
        # XXX: no locks left when unlocked at the moment
147
        # self.assertEqualDiff('', t.get('lock').read())
148
        # We should have a 'shared-storage' marker file.
149
        self.assertEqualDiff('', t.get('shared-storage').read())
150
        self.check_databases(t)
151
152
    def test_shared_no_tree_disk_layout(self):
153
        format = self.get_format()
154
        repo = self.make_repository('.', shared=True, format=format)
155
        repo.set_make_working_trees(False)
156
        # we want:
157
        t = repo.bzrdir.get_repository_transport(None)
158
        self.check_format(t)
159
        # XXX: no locks left when unlocked at the moment
160
        # self.assertEqualDiff('', t.get('lock').read())
161
        # We should have a 'shared-storage' marker file.
162
        self.assertEqualDiff('', t.get('shared-storage').read())
163
        # We should have a marker for the no-working-trees flag.
164
        self.assertEqualDiff('', t.get('no-working-trees').read())
165
        # The marker should go when we toggle the setting.
166
        repo.set_make_working_trees(True)
167
        self.assertFalse(t.has('no-working-trees'))
168
        self.check_databases(t)
169
170
    def test_adding_revision_creates_pack_indices(self):
171
        format = self.get_format()
172
        tree = self.make_branch_and_tree('.', format=format)
173
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
174
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
175
            list(self.index_class(trans, 'pack-names', None).iter_all_entries()))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
176
        tree.commit('foobarbaz')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
177
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
178
        index_nodes = list(index.iter_all_entries())
179
        self.assertEqual(1, len(index_nodes))
180
        node = index_nodes[0]
181
        name = node[1][0]
182
        # the pack sizes should be listed in the index
183
        pack_value = node[2]
184
        sizes = [int(digits) for digits in pack_value.split(' ')]
185
        for size, suffix in zip(sizes, ['.rix', '.iix', '.tix', '.six']):
186
            stat = trans.stat('indices/%s%s' % (name, suffix))
187
            self.assertEqual(size, stat.st_size)
188
189
    def test_pulling_nothing_leads_to_no_new_names(self):
190
        format = self.get_format()
191
        tree1 = self.make_branch_and_tree('1', format=format)
192
        tree2 = self.make_branch_and_tree('2', format=format)
193
        tree1.branch.repository.fetch(tree2.branch.repository)
194
        trans = tree1.branch.repository.bzrdir.get_repository_transport(None)
195
        self.assertEqual([],
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
196
            list(self.index_class(trans, 'pack-names', None).iter_all_entries()))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
197
198
    def test_commit_across_pack_shape_boundary_autopacks(self):
199
        format = self.get_format()
200
        tree = self.make_branch_and_tree('.', format=format)
201
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
202
        # This test could be a little cheaper by replacing the packs
203
        # attribute on the repository to allow a different pack distribution
204
        # and max packs policy - so we are checking the policy is honoured
205
        # in the test. But for now 11 commits is not a big deal in a single
206
        # test.
207
        for x in range(9):
208
            tree.commit('commit %s' % x)
209
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
210
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
211
        self.assertEqual(9, len(list(index.iter_all_entries())))
212
        # insert some files in obsolete_packs which should be removed by pack.
213
        trans.put_bytes('obsolete_packs/foo', '123')
214
        trans.put_bytes('obsolete_packs/bar', '321')
215
        # committing one more should coalesce to 1 of 10.
216
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
217
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
218
        self.assertEqual(1, len(list(index.iter_all_entries())))
219
        # packing should not damage data
220
        tree = tree.bzrdir.open_workingtree()
221
        check_result = tree.branch.repository.check(
222
            [tree.branch.last_revision()])
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
223
        nb_files = 5 # .pack, .rix, .iix, .tix, .six
224
        if tree.branch.repository._format.supports_chks:
225
            nb_files += 1 # .cix
226
        # We should have 10 x nb_files files in the obsolete_packs directory.
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
227
        obsolete_files = list(trans.list_dir('obsolete_packs'))
228
        self.assertFalse('foo' in obsolete_files)
229
        self.assertFalse('bar' in obsolete_files)
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
230
        self.assertEqual(10 * nb_files, len(obsolete_files))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
231
        # XXX: Todo check packs obsoleted correctly - old packs and indices
232
        # in the obsolete_packs directory.
233
        large_pack_name = list(index.iter_all_entries())[0][1][0]
234
        # finally, committing again should not touch the large pack.
235
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
236
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
237
        self.assertEqual(2, len(list(index.iter_all_entries())))
238
        pack_names = [node[1][0] for node in index.iter_all_entries()]
239
        self.assertTrue(large_pack_name in pack_names)
240
4462.2.4 by Robert Collins
Teach commit_write_group to return hint information for pack().
241
    def test_commit_write_group_returns_new_pack_names(self):
242
        format = self.get_format()
243
        tree = self.make_branch_and_tree('foo', format=format)
244
        tree.commit('first post')
245
        repo = tree.branch.repository
246
        repo.lock_write()
247
        try:
248
            repo.start_write_group()
249
            try:
250
                inv = inventory.Inventory(revision_id="A")
251
                inv.root.revision = "A"
252
                repo.texts.add_lines((inv.root.file_id, "A"), [], [])
253
                rev = _mod_revision.Revision(timestamp=0, timezone=None,
254
                    committer="Foo Bar <foo@example.com>", message="Message",
255
                    revision_id="A")
256
                rev.parent_ids = ()
257
                repo.add_revision("A", rev, inv=inv)
258
            except:
259
                repo.abort_write_group()
260
                raise
261
            else:
262
                old_names = repo._pack_collection._names.keys()
263
                result = repo.commit_write_group()
264
                cur_names = repo._pack_collection._names.keys()
265
                new_names = list(set(cur_names) - set(old_names))
266
                self.assertEqual(new_names, result)
267
        finally:
268
            repo.unlock()
269
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
270
    def test_fail_obsolete_deletion(self):
271
        # failing to delete obsolete packs is not fatal
272
        format = self.get_format()
273
        server = fakenfs.FakeNFSServer()
274
        server.setUp()
275
        self.addCleanup(server.tearDown)
276
        transport = get_transport(server.get_url())
277
        bzrdir = self.get_format().initialize_on_transport(transport)
278
        repo = bzrdir.create_repository()
279
        repo_transport = bzrdir.get_repository_transport(None)
280
        self.assertTrue(repo_transport.has('obsolete_packs'))
281
        # these files are in use by another client and typically can't be deleted
282
        repo_transport.put_bytes('obsolete_packs/.nfsblahblah', 'contents')
283
        repo._pack_collection._clear_obsolete_packs()
284
        self.assertTrue(repo_transport.has('obsolete_packs/.nfsblahblah'))
285
286
    def test_pack_after_two_commits_packs_everything(self):
287
        format = self.get_format()
288
        tree = self.make_branch_and_tree('.', format=format)
289
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
290
        tree.commit('start')
291
        tree.commit('more work')
292
        tree.branch.repository.pack()
293
        # there should be 1 pack:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
294
        index = self.index_class(trans, 'pack-names', None)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
295
        self.assertEqual(1, len(list(index.iter_all_entries())))
296
        self.assertEqual(2, len(tree.branch.repository.all_revision_ids()))
297
298
    def test_pack_layout(self):
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
299
        # Test that the ordering of revisions in pack repositories is
300
        # tip->ancestor
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
301
        format = self.get_format()
302
        tree = self.make_branch_and_tree('.', format=format)
303
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
304
        tree.commit('start', rev_id='1')
305
        tree.commit('more work', rev_id='2')
306
        tree.branch.repository.pack()
307
        tree.lock_read()
308
        self.addCleanup(tree.unlock)
309
        pack = tree.branch.repository._pack_collection.get_pack_by_name(
310
            tree.branch.repository._pack_collection.names()[0])
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
311
        # revision access tends to be tip->ancestor, so ordering that way on
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
312
        # disk is a good idea.
313
        for _1, key, val, refs in pack.revision_index.iter_all_entries():
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
314
            if type(format.repository_format) is RepositoryFormatCHK1:
315
                # group_start, group_len, internal_start, internal_len
316
                pos = map(int, val.split())
317
            else:
318
                # eol_flag, start, len
319
                pos = int(val[1:].split()[0])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
320
            if key == ('1',):
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
321
                pos_1 = pos
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
322
            else:
4350.2.1 by John Arbash Meinel
Update a test to support CHK formats.
323
                pos_2 = pos
324
        self.assertTrue(pos_2 < pos_1, 'rev 1 came before rev 2 %s > %s'
325
                                       % (pos_1, pos_2))
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
326
327
    def test_pack_repositories_support_multiple_write_locks(self):
328
        format = self.get_format()
329
        self.make_repository('.', shared=True, format=format)
330
        r1 = repository.Repository.open('.')
331
        r2 = repository.Repository.open('.')
332
        r1.lock_write()
333
        self.addCleanup(r1.unlock)
334
        r2.lock_write()
335
        r2.unlock()
336
337
    def _add_text(self, repo, fileid):
338
        """Add a text to the repository within a write group."""
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
339
        repo.texts.add_lines((fileid, 'samplerev+'+fileid), [],
340
            ['smaplerev+'+fileid])
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
341
342
    def test_concurrent_writers_merge_new_packs(self):
343
        format = self.get_format()
344
        self.make_repository('.', shared=True, format=format)
345
        r1 = repository.Repository.open('.')
346
        r2 = repository.Repository.open('.')
347
        r1.lock_write()
348
        try:
349
            # access enough data to load the names list
350
            list(r1.all_revision_ids())
351
            r2.lock_write()
352
            try:
353
                # access enough data to load the names list
354
                list(r2.all_revision_ids())
355
                r1.start_write_group()
356
                try:
357
                    r2.start_write_group()
358
                    try:
359
                        self._add_text(r1, 'fileidr1')
360
                        self._add_text(r2, 'fileidr2')
361
                    except:
362
                        r2.abort_write_group()
363
                        raise
364
                except:
365
                    r1.abort_write_group()
366
                    raise
367
                # both r1 and r2 have open write groups with data in them
368
                # created while the other's write group was open.
369
                # Commit both which requires a merge to the pack-names.
370
                try:
371
                    r1.commit_write_group()
372
                except:
373
                    r1.abort_write_group()
374
                    r2.abort_write_group()
375
                    raise
376
                r2.commit_write_group()
377
                # tell r1 to reload from disk
378
                r1._pack_collection.reset()
379
                # Now both repositories should know about both names
380
                r1._pack_collection.ensure_loaded()
381
                r2._pack_collection.ensure_loaded()
382
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
383
                self.assertEqual(2, len(r1._pack_collection.names()))
384
            finally:
385
                r2.unlock()
386
        finally:
387
            r1.unlock()
388
389
    def test_concurrent_writer_second_preserves_dropping_a_pack(self):
390
        format = self.get_format()
391
        self.make_repository('.', shared=True, format=format)
392
        r1 = repository.Repository.open('.')
393
        r2 = repository.Repository.open('.')
394
        # add a pack to drop
395
        r1.lock_write()
396
        try:
397
            r1.start_write_group()
398
            try:
399
                self._add_text(r1, 'fileidr1')
400
            except:
401
                r1.abort_write_group()
402
                raise
403
            else:
404
                r1.commit_write_group()
405
            r1._pack_collection.ensure_loaded()
406
            name_to_drop = r1._pack_collection.all_packs()[0].name
407
        finally:
408
            r1.unlock()
409
        r1.lock_write()
410
        try:
411
            # access enough data to load the names list
412
            list(r1.all_revision_ids())
413
            r2.lock_write()
414
            try:
415
                # access enough data to load the names list
416
                list(r2.all_revision_ids())
417
                r1._pack_collection.ensure_loaded()
418
                try:
419
                    r2.start_write_group()
420
                    try:
421
                        # in r1, drop the pack
422
                        r1._pack_collection._remove_pack_from_memory(
423
                            r1._pack_collection.get_pack_by_name(name_to_drop))
424
                        # in r2, add a pack
425
                        self._add_text(r2, 'fileidr2')
426
                    except:
427
                        r2.abort_write_group()
428
                        raise
429
                except:
430
                    r1._pack_collection.reset()
431
                    raise
432
                # r1 has a changed names list, and r2 an open write groups with
433
                # changes.
434
                # save r1, and then commit the r2 write group, which requires a
435
                # merge to the pack-names, which should not reinstate
436
                # name_to_drop
437
                try:
438
                    r1._pack_collection._save_pack_names()
439
                    r1._pack_collection.reset()
440
                except:
441
                    r2.abort_write_group()
442
                    raise
443
                try:
444
                    r2.commit_write_group()
445
                except:
446
                    r2.abort_write_group()
447
                    raise
448
                # Now both repositories should now about just one name.
449
                r1._pack_collection.ensure_loaded()
450
                r2._pack_collection.ensure_loaded()
451
                self.assertEqual(r1._pack_collection.names(), r2._pack_collection.names())
452
                self.assertEqual(1, len(r1._pack_collection.names()))
453
                self.assertFalse(name_to_drop in r1._pack_collection.names())
454
            finally:
455
                r2.unlock()
456
        finally:
457
            r1.unlock()
458
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
459
    def test_concurrent_pack_triggers_reload(self):
460
        # create 2 packs, which we will then collapse
461
        tree = self.make_branch_and_tree('tree')
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
462
        tree.lock_write()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
463
        try:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
464
            rev1 = tree.commit('one')
465
            rev2 = tree.commit('two')
466
            r2 = repository.Repository.open('tree')
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
467
            r2.lock_read()
468
            try:
469
                # Now r2 has read the pack-names file, but will need to reload
470
                # it after r1 has repacked
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
471
                tree.branch.repository.pack()
472
                self.assertEqual({rev2:(rev1,)}, r2.get_parent_map([rev2]))
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
473
            finally:
474
                r2.unlock()
475
        finally:
3789.1.2 by John Arbash Meinel
Add RepositoryPackCollection.reload_pack_names()
476
            tree.unlock()
3789.1.1 by John Arbash Meinel
add the failing acceptance test for the first portion.
477
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
478
    def test_concurrent_pack_during_get_record_reloads(self):
479
        tree = self.make_branch_and_tree('tree')
480
        tree.lock_write()
481
        try:
482
            rev1 = tree.commit('one')
483
            rev2 = tree.commit('two')
3789.2.14 by John Arbash Meinel
Update AggregateIndex to pass the reload_func into _DirectPackAccess
484
            keys = [(rev1,), (rev2,)]
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
485
            r2 = repository.Repository.open('tree')
486
            r2.lock_read()
487
            try:
488
                # At this point, we will start grabbing a record stream, and
489
                # trigger a repack mid-way
490
                packed = False
491
                result = {}
492
                record_stream = r2.revisions.get_record_stream(keys,
493
                                    'unordered', False)
494
                for record in record_stream:
495
                    result[record.key] = record
496
                    if not packed:
497
                        tree.branch.repository.pack()
498
                        packed = True
499
                # The first record will be found in the original location, but
500
                # after the pack, we have to reload to find the next record
3789.2.14 by John Arbash Meinel
Update AggregateIndex to pass the reload_func into _DirectPackAccess
501
                self.assertEqual(sorted(keys), sorted(result.keys()))
3789.2.8 by John Arbash Meinel
Add a test that KnitPackRepository.get_record_stream retries when appropriate.
502
            finally:
503
                r2.unlock()
504
        finally:
505
            tree.unlock()
506
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
507
    def test_lock_write_does_not_physically_lock(self):
508
        repo = self.make_repository('.', format=self.get_format())
509
        repo.lock_write()
510
        self.addCleanup(repo.unlock)
511
        self.assertFalse(repo.get_physical_lock_status())
512
513
    def prepare_for_break_lock(self):
514
        # Setup the global ui factory state so that a break-lock method call
515
        # will find usable input in the input stream.
516
        old_factory = ui.ui_factory
517
        def restoreFactory():
518
            ui.ui_factory = old_factory
519
        self.addCleanup(restoreFactory)
520
        ui.ui_factory = ui.SilentUIFactory()
521
        ui.ui_factory.stdin = StringIO("y\n")
522
523
    def test_break_lock_breaks_physical_lock(self):
524
        repo = self.make_repository('.', format=self.get_format())
525
        repo._pack_collection.lock_names()
3650.4.1 by Aaron Bentley
Fix test kipple in test_break_lock_breaks_physical_lock
526
        repo.control_files.leave_in_place()
527
        repo.unlock()
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
528
        repo2 = repository.Repository.open('.')
529
        self.assertTrue(repo.get_physical_lock_status())
530
        self.prepare_for_break_lock()
531
        repo2.break_lock()
532
        self.assertFalse(repo.get_physical_lock_status())
533
534
    def test_broken_physical_locks_error_on__unlock_names_lock(self):
535
        repo = self.make_repository('.', format=self.get_format())
536
        repo._pack_collection.lock_names()
537
        self.assertTrue(repo.get_physical_lock_status())
538
        repo2 = repository.Repository.open('.')
539
        self.prepare_for_break_lock()
540
        repo2.break_lock()
541
        self.assertRaises(errors.LockBroken, repo._pack_collection._unlock_names)
542
543
    def test_fetch_without_find_ghosts_ignores_ghosts(self):
544
        # we want two repositories at this point:
545
        # one with a revision that is a ghost in the other
546
        # repository.
547
        # 'ghost' is present in has_ghost, 'ghost' is absent in 'missing_ghost'.
548
        # 'references' is present in both repositories, and 'tip' is present
549
        # just in has_ghost.
550
        # has_ghost       missing_ghost
551
        #------------------------------
552
        # 'ghost'             -
553
        # 'references'    'references'
554
        # 'tip'               -
555
        # In this test we fetch 'tip' which should not fetch 'ghost'
556
        has_ghost = self.make_repository('has_ghost', format=self.get_format())
557
        missing_ghost = self.make_repository('missing_ghost',
558
            format=self.get_format())
559
560
        def add_commit(repo, revision_id, parent_ids):
561
            repo.lock_write()
562
            repo.start_write_group()
563
            inv = inventory.Inventory(revision_id=revision_id)
564
            inv.root.revision = revision_id
565
            root_id = inv.root.file_id
566
            sha1 = repo.add_inventory(revision_id, inv, [])
567
            repo.texts.add_lines((root_id, revision_id), [], [])
568
            rev = _mod_revision.Revision(timestamp=0,
569
                                         timezone=None,
570
                                         committer="Foo Bar <foo@example.com>",
571
                                         message="Message",
572
                                         inventory_sha1=sha1,
573
                                         revision_id=revision_id)
574
            rev.parent_ids = parent_ids
575
            repo.add_revision(revision_id, rev)
576
            repo.commit_write_group()
577
            repo.unlock()
578
        add_commit(has_ghost, 'ghost', [])
579
        add_commit(has_ghost, 'references', ['ghost'])
580
        add_commit(missing_ghost, 'references', ['ghost'])
581
        add_commit(has_ghost, 'tip', ['references'])
582
        missing_ghost.fetch(has_ghost, 'tip')
583
        # missing ghost now has tip and not ghost.
584
        rev = missing_ghost.get_revision('tip')
585
        inv = missing_ghost.get_inventory('tip')
586
        self.assertRaises(errors.NoSuchRevision,
587
            missing_ghost.get_revision, 'ghost')
588
        self.assertRaises(errors.NoSuchRevision,
589
            missing_ghost.get_inventory, 'ghost')
590
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
591
    def make_write_ready_repo(self):
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
592
        format = self.get_format()
593
        if isinstance(format.repository_format, RepositoryFormatCHK1):
594
            raise TestNotApplicable("No missing compression parents")
595
        repo = self.make_repository('.', format=format)
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
596
        repo.lock_write()
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
597
        self.addCleanup(repo.unlock)
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
598
        repo.start_write_group()
4360.4.6 by John Arbash Meinel
Change how 'missing.*parent_prevents_commit' determines what to skip.
599
        self.addCleanup(repo.abort_write_group)
4011.5.6 by Andrew Bennetts
Make sure it's not possible to commit a pack write group when any versioned file has missing compression parents.
600
        return repo
601
602
    def test_missing_inventories_compression_parent_prevents_commit(self):
603
        repo = self.make_write_ready_repo()
604
        key = ('junk',)
605
        repo.inventories._index._missing_compression_parents.add(key)
606
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
607
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
608
609
    def test_missing_revisions_compression_parent_prevents_commit(self):
610
        repo = self.make_write_ready_repo()
611
        key = ('junk',)
612
        repo.revisions._index._missing_compression_parents.add(key)
613
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
614
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
615
616
    def test_missing_signatures_compression_parent_prevents_commit(self):
617
        repo = self.make_write_ready_repo()
618
        key = ('junk',)
619
        repo.signatures._index._missing_compression_parents.add(key)
620
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
621
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
622
623
    def test_missing_text_compression_parent_prevents_commit(self):
624
        repo = self.make_write_ready_repo()
625
        key = ('some', 'junk')
626
        repo.texts._index._missing_compression_parents.add(key)
627
        self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
628
        e = self.assertRaises(errors.BzrCheckError, repo.commit_write_group)
629
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
630
    def test_supports_external_lookups(self):
631
        repo = self.make_repository('.', format=self.get_format())
632
        self.assertEqual(self.format_supports_external_lookups,
633
            repo._format.supports_external_lookups)
634
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
635
    def test_abort_write_group_does_not_raise_when_suppressed(self):
636
        """Similar to per_repository.test_write_group's test of the same name.
637
638
        Also requires that the exception is logged.
639
        """
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
640
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
641
        repo = self.make_repository('repo', format=self.get_format())
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
642
        token = repo.lock_write()
643
        self.addCleanup(repo.unlock)
644
        repo.start_write_group()
645
        # Damage the repository on the filesystem
646
        self.get_transport('').rename('repo', 'foo')
647
        # abort_write_group will not raise an error
648
        self.assertEqual(None, repo.abort_write_group(suppress_errors=True))
649
        # But it does log an error
650
        log_file = self._get_log(keep_log_file=True)
651
        self.assertContainsRe(log_file, 'abort_write_group failed')
652
        self.assertContainsRe(log_file, r'INFO  bzr: ERROR \(ignored\):')
653
        if token is not None:
654
            repo.leave_lock_in_place()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
655
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
656
    def test_abort_write_group_does_raise_when_not_suppressed(self):
3825.4.2 by Andrew Bennetts
Run the abort_write_group tests against a memory transport to avoid platform-specific limits on changing files that may be in use.
657
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
658
        repo = self.make_repository('repo', format=self.get_format())
3825.4.1 by Andrew Bennetts
Add suppress_errors to abort_write_group.
659
        token = repo.lock_write()
660
        self.addCleanup(repo.unlock)
661
        repo.start_write_group()
662
        # Damage the repository on the filesystem
663
        self.get_transport('').rename('repo', 'foo')
664
        # abort_write_group will not raise an error
665
        self.assertRaises(Exception, repo.abort_write_group)
666
        if token is not None:
667
            repo.leave_lock_in_place()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
668
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
669
    def test_suspend_write_group(self):
670
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
671
        repo = self.make_repository('repo', format=self.get_format())
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
672
        token = repo.lock_write()
673
        self.addCleanup(repo.unlock)
674
        repo.start_write_group()
675
        repo.texts.add_lines(('file-id', 'revid'), (), ['lines'])
676
        wg_tokens = repo.suspend_write_group()
677
        expected_pack_name = wg_tokens[0] + '.pack'
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
678
        expected_names = [wg_tokens[0] + ext for ext in
679
                            ('.rix', '.iix', '.tix', '.six')]
680
        if repo.chk_bytes is not None:
681
            expected_names.append(wg_tokens[0] + '.cix')
682
        expected_names.append(expected_pack_name)
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
683
        upload_transport = repo._pack_collection._upload_transport
684
        limbo_files = upload_transport.list_dir('')
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
685
        self.assertEqual(sorted(expected_names), sorted(limbo_files))
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
686
        md5 = osutils.md5(upload_transport.get_bytes(expected_pack_name))
687
        self.assertEqual(wg_tokens[0], md5.hexdigest())
688
4343.3.8 by John Arbash Meinel
Some cleanup passes.
689
    def test_resume_chk_bytes(self):
690
        self.vfs_transport_factory = memory.MemoryServer
691
        repo = self.make_repository('repo', format=self.get_format())
692
        if repo.chk_bytes is None:
693
            raise TestNotApplicable('no chk_bytes for this repository')
694
        token = repo.lock_write()
695
        self.addCleanup(repo.unlock)
696
        repo.start_write_group()
697
        text = 'a bit of text\n'
698
        key = ('sha1:' + osutils.sha_string(text),)
699
        repo.chk_bytes.add_lines(key, (), [text])
700
        wg_tokens = repo.suspend_write_group()
701
        same_repo = repo.bzrdir.open_repository()
702
        same_repo.lock_write()
703
        self.addCleanup(same_repo.unlock)
704
        same_repo.resume_write_group(wg_tokens)
705
        self.assertEqual([key], list(same_repo.chk_bytes.keys()))
706
        self.assertEqual(
707
            text, same_repo.chk_bytes.get_record_stream([key],
708
                'unordered', True).next().get_bytes_as('fulltext'))
709
        same_repo.abort_write_group()
710
        self.assertEqual([], list(same_repo.chk_bytes.keys()))
711
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
712
    def test_resume_write_group_then_abort(self):
713
        # Create a repo, start a write group, insert some data, suspend.
714
        self.vfs_transport_factory = memory.MemoryServer
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
715
        repo = self.make_repository('repo', format=self.get_format())
4002.1.1 by Andrew Bennetts
Implement suspend_write_group/resume_write_group.
716
        token = repo.lock_write()
717
        self.addCleanup(repo.unlock)
718
        repo.start_write_group()
719
        text_key = ('file-id', 'revid')
720
        repo.texts.add_lines(text_key, (), ['lines'])
721
        wg_tokens = repo.suspend_write_group()
722
        # Get a fresh repository object for the repo on the filesystem.
723
        same_repo = repo.bzrdir.open_repository()
724
        # Resume
725
        same_repo.lock_write()
726
        self.addCleanup(same_repo.unlock)
727
        same_repo.resume_write_group(wg_tokens)
728
        same_repo.abort_write_group()
729
        self.assertEqual(
730
            [], same_repo._pack_collection._upload_transport.list_dir(''))
731
        self.assertEqual(
732
            [], same_repo._pack_collection._pack_transport.list_dir(''))
733
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
734
    def test_commit_resumed_write_group(self):
735
        self.vfs_transport_factory = memory.MemoryServer
736
        repo = self.make_repository('repo', format=self.get_format())
737
        token = repo.lock_write()
738
        self.addCleanup(repo.unlock)
739
        repo.start_write_group()
740
        text_key = ('file-id', 'revid')
741
        repo.texts.add_lines(text_key, (), ['lines'])
742
        wg_tokens = repo.suspend_write_group()
743
        # Get a fresh repository object for the repo on the filesystem.
744
        same_repo = repo.bzrdir.open_repository()
745
        # Resume
746
        same_repo.lock_write()
747
        self.addCleanup(same_repo.unlock)
748
        same_repo.resume_write_group(wg_tokens)
749
        same_repo.commit_write_group()
750
        expected_pack_name = wg_tokens[0] + '.pack'
751
        expected_names = [wg_tokens[0] + ext for ext in
752
                            ('.rix', '.iix', '.tix', '.six')]
753
        if repo.chk_bytes is not None:
754
            expected_names.append(wg_tokens[0] + '.cix')
755
        self.assertEqual(
756
            [], same_repo._pack_collection._upload_transport.list_dir(''))
757
        index_names = repo._pack_collection._index_transport.list_dir('')
758
        self.assertEqual(sorted(expected_names), sorted(index_names))
759
        pack_names = repo._pack_collection._pack_transport.list_dir('')
760
        self.assertEqual([expected_pack_name], pack_names)
761
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
762
    def test_resume_malformed_token(self):
763
        self.vfs_transport_factory = memory.MemoryServer
764
        # Make a repository with a suspended write group
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
765
        repo = self.make_repository('repo', format=self.get_format())
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
766
        token = repo.lock_write()
767
        self.addCleanup(repo.unlock)
768
        repo.start_write_group()
769
        text_key = ('file-id', 'revid')
770
        repo.texts.add_lines(text_key, (), ['lines'])
771
        wg_tokens = repo.suspend_write_group()
772
        # Make a new repository
4343.3.7 by John Arbash Meinel
Update the suspend/resume/commit/abort_write_group tests for CHK1.
773
        new_repo = self.make_repository('new_repo', format=self.get_format())
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
774
        token = new_repo.lock_write()
775
        self.addCleanup(new_repo.unlock)
776
        hacked_wg_token = (
777
            '../../../../repo/.bzr/repository/upload/' + wg_tokens[0])
778
        self.assertRaises(
4002.1.7 by Andrew Bennetts
Rename UnresumableWriteGroups to UnresumableWriteGroup.
779
            errors.UnresumableWriteGroup,
4002.1.5 by Andrew Bennetts
Fix possible security issue with resuming write groups: make sure the token is well-formed so that it's not possible to steal a write group from another repo.
780
            new_repo.resume_write_group, [hacked_wg_token])
781
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
782
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
783
class TestPackRepositoryStacking(TestCaseWithTransport):
784
785
    """Tests for stacking pack repositories"""
786
787
    def setUp(self):
788
        if not self.format_supports_external_lookups:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
789
            raise TestNotApplicable("%r doesn't support stacking"
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
790
                % (self.format_name,))
791
        super(TestPackRepositoryStacking, self).setUp()
792
793
    def get_format(self):
794
        return bzrdir.format_registry.make_bzrdir(self.format_name)
795
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
796
    def test_stack_checks_rich_root_compatibility(self):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
797
        # early versions of the packing code relied on pack internals to
798
        # stack, but the current version should be able to stack on any
799
        # format.
800
        #
801
        # TODO: Possibly this should be run per-repository-format and raise
802
        # TestNotApplicable on formats that don't support stacking. -- mbp
803
        # 20080729
804
        repo = self.make_repository('repo', format=self.get_format())
805
        if repo.supports_rich_root():
806
            # can only stack on repositories that have compatible internal
807
            # metadata
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
808
            if getattr(repo._format, 'supports_tree_reference', False):
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
809
                matching_format_name = 'pack-0.92-subtree'
810
            else:
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
811
                if repo._format.supports_chks:
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
812
                    matching_format_name = 'development6-rich-root'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
813
                else:
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
814
                    matching_format_name = 'rich-root-pack'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
815
            mismatching_format_name = 'pack-0.92'
816
        else:
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
817
            # We don't have a non-rich-root CHK format.
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
818
            if repo._format.supports_chks:
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
819
                raise AssertionError("no non-rich-root CHK formats known")
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
820
            else:
821
                matching_format_name = 'pack-0.92'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
822
            mismatching_format_name = 'pack-0.92-subtree'
823
        base = self.make_repository('base', format=matching_format_name)
824
        repo.add_fallback_repository(base)
825
        # you can't stack on something with incompatible data
826
        bad_repo = self.make_repository('mismatch',
827
            format=mismatching_format_name)
828
        e = self.assertRaises(errors.IncompatibleRepositories,
829
            repo.add_fallback_repository, bad_repo)
830
        self.assertContainsRe(str(e),
831
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
832
            r'.*Repository.*/repo/.*\n'
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
833
            r'different rich-root support')
834
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
835
    def test_stack_checks_serializers_compatibility(self):
836
        repo = self.make_repository('repo', format=self.get_format())
837
        if getattr(repo._format, 'supports_tree_reference', False):
838
            # can only stack on repositories that have compatible internal
839
            # metadata
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
840
            matching_format_name = 'pack-0.92-subtree'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
841
            mismatching_format_name = 'rich-root-pack'
842
        else:
843
            if repo.supports_rich_root():
4343.3.27 by John Arbash Meinel
Now that dev6 supports external references, the tests for
844
                if repo._format.supports_chks:
845
                    matching_format_name = 'development6-rich-root'
846
                else:
847
                    matching_format_name = 'rich-root-pack'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
848
                mismatching_format_name = 'pack-0.92-subtree'
849
            else:
850
                raise TestNotApplicable('No formats use non-v5 serializer'
851
                    ' without having rich-root also set')
852
        base = self.make_repository('base', format=matching_format_name)
853
        repo.add_fallback_repository(base)
854
        # you can't stack on something with incompatible data
855
        bad_repo = self.make_repository('mismatch',
856
            format=mismatching_format_name)
857
        e = self.assertRaises(errors.IncompatibleRepositories,
858
            repo.add_fallback_repository, bad_repo)
859
        self.assertContainsRe(str(e),
860
            r'(?m)KnitPackRepository.*/mismatch/.*\nis not compatible with\n'
3735.2.9 by Robert Collins
Get a working chk_map using inventory implementation bootstrapped.
861
            r'.*Repository.*/repo/.*\n'
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
862
            r'different serializers')
863
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
864
    def test_adding_pack_does_not_record_pack_names_from_other_repositories(self):
865
        base = self.make_branch_and_tree('base', format=self.get_format())
866
        base.commit('foo')
867
        referencing = self.make_branch_and_tree('repo', format=self.get_format())
868
        referencing.branch.repository.add_fallback_repository(base.branch.repository)
869
        referencing.commit('bar')
870
        new_instance = referencing.bzrdir.open_repository()
871
        new_instance.lock_read()
872
        self.addCleanup(new_instance.unlock)
873
        new_instance._pack_collection.ensure_loaded()
874
        self.assertEqual(1, len(new_instance._pack_collection.all_packs()))
875
876
    def test_autopack_only_considers_main_repo_packs(self):
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
877
        format = self.get_format()
878
        base = self.make_branch_and_tree('base', format=format)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
879
        base.commit('foo')
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
880
        tree = self.make_branch_and_tree('repo', format=format)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
881
        tree.branch.repository.add_fallback_repository(base.branch.repository)
882
        trans = tree.branch.repository.bzrdir.get_repository_transport(None)
883
        # This test could be a little cheaper by replacing the packs
884
        # attribute on the repository to allow a different pack distribution
885
        # and max packs policy - so we are checking the policy is honoured
886
        # in the test. But for now 11 commits is not a big deal in a single
887
        # test.
888
        for x in range(9):
889
            tree.commit('commit %s' % x)
890
        # there should be 9 packs:
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
891
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
892
        self.assertEqual(9, len(list(index.iter_all_entries())))
893
        # committing one more should coalesce to 1 of 10.
894
        tree.commit('commit triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
895
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
896
        self.assertEqual(1, len(list(index.iter_all_entries())))
897
        # packing should not damage data
898
        tree = tree.bzrdir.open_workingtree()
899
        check_result = tree.branch.repository.check(
900
            [tree.branch.last_revision()])
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
901
        nb_files = 5 # .pack, .rix, .iix, .tix, .six
902
        if tree.branch.repository._format.supports_chks:
903
            nb_files += 1 # .cix
904
        # We should have 10 x nb_files files in the obsolete_packs directory.
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
905
        obsolete_files = list(trans.list_dir('obsolete_packs'))
906
        self.assertFalse('foo' in obsolete_files)
907
        self.assertFalse('bar' in obsolete_files)
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
908
        self.assertEqual(10 * nb_files, len(obsolete_files))
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
909
        # XXX: Todo check packs obsoleted correctly - old packs and indices
910
        # in the obsolete_packs directory.
911
        large_pack_name = list(index.iter_all_entries())[0][1][0]
912
        # finally, committing again should not touch the large pack.
913
        tree.commit('commit not triggering pack')
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
914
        index = self.index_class(trans, 'pack-names', None)
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
915
        self.assertEqual(2, len(list(index.iter_all_entries())))
916
        pack_names = [node[1][0] for node in index.iter_all_entries()]
917
        self.assertTrue(large_pack_name in pack_names)
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
918
919
4343.3.33 by John Arbash Meinel
Clear KeyDependencies on abort/suspend/commit_write_group.
920
class TestKeyDependencies(TestCaseWithTransport):
921
922
    def get_format(self):
923
        return bzrdir.format_registry.make_bzrdir(self.format_name)
924
925
    def create_source_and_target(self):
926
        builder = self.make_branch_builder('source', format=self.get_format())
927
        builder.start_series()
928
        builder.build_snapshot('A-id', None, [
929
            ('add', ('', 'root-id', 'directory', None))])
930
        builder.build_snapshot('B-id', ['A-id', 'ghost-id'], [])
931
        builder.finish_series()
932
        repo = self.make_repository('target')
933
        b = builder.get_branch()
934
        b.lock_read()
935
        self.addCleanup(b.unlock)
936
        repo.lock_write()
937
        self.addCleanup(repo.unlock)
938
        return b.repository, repo
939
940
    def test_key_dependencies_cleared_on_abort(self):
941
        source_repo, target_repo = self.create_source_and_target()
942
        target_repo.start_write_group()
943
        try:
944
            stream = source_repo.revisions.get_record_stream([('B-id',)],
945
                                                             'unordered', True)
946
            target_repo.revisions.insert_record_stream(stream)
947
            key_refs = target_repo.revisions._index._key_dependencies
948
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
949
        finally:
950
            target_repo.abort_write_group()
951
        self.assertEqual([], sorted(key_refs.get_referrers()))
952
953
    def test_key_dependencies_cleared_on_suspend(self):
954
        source_repo, target_repo = self.create_source_and_target()
955
        target_repo.start_write_group()
956
        try:
957
            stream = source_repo.revisions.get_record_stream([('B-id',)],
958
                                                             'unordered', True)
959
            target_repo.revisions.insert_record_stream(stream)
960
            key_refs = target_repo.revisions._index._key_dependencies
961
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
962
        finally:
963
            target_repo.suspend_write_group()
964
        self.assertEqual([], sorted(key_refs.get_referrers()))
965
966
    def test_key_dependencies_cleared_on_commit(self):
967
        source_repo, target_repo = self.create_source_and_target()
968
        target_repo.start_write_group()
969
        try:
970
            stream = source_repo.revisions.get_record_stream([('B-id',)],
971
                                                             'unordered', True)
972
            target_repo.revisions.insert_record_stream(stream)
973
            key_refs = target_repo.revisions._index._key_dependencies
974
            self.assertEqual([('B-id',)], sorted(key_refs.get_referrers()))
975
        finally:
976
            target_repo.commit_write_group()
977
        self.assertEqual([], sorted(key_refs.get_referrers()))
978
979
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
980
class TestSmartServerAutopack(TestCaseWithTransport):
981
982
    def setUp(self):
983
        super(TestSmartServerAutopack, self).setUp()
984
        # Create a smart server that publishes whatever the backing VFS server
985
        # does.
986
        self.smart_server = server.SmartTCPServer_for_testing()
987
        self.smart_server.setUp(self.get_server())
988
        self.addCleanup(self.smart_server.tearDown)
989
        # Log all HPSS calls into self.hpss_calls.
990
        client._SmartClient.hooks.install_named_hook(
991
            'call', self.capture_hpss_call, None)
992
        self.hpss_calls = []
993
994
    def capture_hpss_call(self, params):
995
        self.hpss_calls.append(params.method)
996
997
    def get_format(self):
998
        return bzrdir.format_registry.make_bzrdir(self.format_name)
999
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
1000
    def test_autopack_or_streaming_rpc_is_used_when_using_hpss(self):
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1001
        # Make local and remote repos
3735.2.98 by John Arbash Meinel
Merge bzr.dev 4032. Resolve the new streaming fetch.
1002
        format = self.get_format()
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
1003
        tree = self.make_branch_and_tree('local', format=format)
1004
        self.make_branch_and_tree('remote', format=format)
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1005
        remote_branch_url = self.smart_server.get_url() + 'remote'
1006
        remote_branch = bzrdir.BzrDir.open(remote_branch_url).open_branch()
1007
        # Make 9 local revisions, and push them one at a time to the remote
1008
        # repo to produce 9 pack files.
1009
        for x in range(9):
1010
            tree.commit('commit %s' % x)
1011
            tree.branch.push(remote_branch)
1012
        # Make one more push to trigger an autopack
1013
        self.hpss_calls = []
1014
        tree.commit('commit triggering pack')
1015
        tree.branch.push(remote_branch)
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
1016
        autopack_calls = len([call for call in self.hpss_calls if call ==
1017
            'PackRepository.autopack'])
1018
        streaming_calls = len([call for call in self.hpss_calls if call ==
1019
            'Repository.insert_stream'])
1020
        if autopack_calls:
1021
            # Non streaming server
1022
            self.assertEqual(1, autopack_calls)
1023
            self.assertEqual(0, streaming_calls)
1024
        else:
1025
            # Streaming was used, which autopacks on the remote end.
1026
            self.assertEqual(0, autopack_calls)
1027
            # NB: The 2 calls are because of the sanity check that the server
1028
            # supports the verb (see remote.py:RemoteSink.insert_stream for
1029
            # details).
1030
            self.assertEqual(2, streaming_calls)
3801.1.18 by Andrew Bennetts
Add a test that ensures that the autopack RPC is actually used for all pack formats.
1031
1032
4084.5.1 by Robert Collins
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.
1033
def load_tests(basic_tests, module, loader):
3582.3.3 by Martin Pool
Reenable tests for stacking pack repositories
1034
    # these give the bzrdir canned format name, and the repository on-disk
1035
    # format string
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1036
    scenarios_params = [
1037
         dict(format_name='pack-0.92',
1038
              format_string="Bazaar pack repository format 1 (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1039
              format_supports_external_lookups=False,
1040
              index_class=GraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1041
         dict(format_name='pack-0.92-subtree',
1042
              format_string="Bazaar pack repository format 1 "
1043
              "with subtree support (needs bzr 0.92)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1044
              format_supports_external_lookups=False,
1045
              index_class=GraphIndex),
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
1046
         dict(format_name='1.6',
1047
              format_string="Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1048
              format_supports_external_lookups=True,
1049
              index_class=GraphIndex),
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
1050
         dict(format_name='1.6.1-rich-root',
3582.3.2 by Martin Pool
Add 1.6 formats to pack repository tests
1051
              format_string="Bazaar RepositoryFormatKnitPack5RichRoot "
3606.10.5 by John Arbash Meinel
Switch out --1.6-rich-root for --1.6.1-rich-root.
1052
                  "(bzr 1.6.1)\n",
3735.1.1 by Robert Collins
Add development2 formats using BTree indices.
1053
              format_supports_external_lookups=True,
1054
              index_class=GraphIndex),
3805.3.1 by John Arbash Meinel
Add repository 1.9 format, and update the documentation.
1055
         dict(format_name='1.9',
1056
              format_string="Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n",
1057
              format_supports_external_lookups=True,
1058
              index_class=BTreeGraphIndex),
1059
         dict(format_name='1.9-rich-root',
1060
              format_string="Bazaar RepositoryFormatKnitPack6RichRoot "
1061
                  "(bzr 1.9)\n",
1062
              format_supports_external_lookups=True,
1063
              index_class=BTreeGraphIndex),
4241.6.8 by Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)
1064
         dict(format_name='development6-rich-root',
1065
              format_string='Bazaar development format - group compression '
1066
                  'and chk inventory (needs bzr.dev from 1.14)\n',
4343.3.8 by John Arbash Meinel
Some cleanup passes.
1067
              format_supports_external_lookups=True,
3735.2.40 by Robert Collins
Add development4 which has a parent_id to basename index on CHKInventory objects.
1068
              index_class=BTreeGraphIndex),
3582.3.1 by Martin Pool
Split pack repository tests into their own file and use scenarios
1069
         ]
1070
    # name of the scenario is the format name
4084.5.1 by Robert Collins
Bulk update all test adaptation into a single approach, using multiply_tests rather than test adapters.
1071
    scenarios = [(s['format_name'], s) for s in scenarios_params]
1072
    return tests.multiply_tests(basic_tests, scenarios, loader.suiteClass())