~bzr-pqm/bzr/bzr.dev

5273.1.7 by Vincent Ladeuil
No more use of the get_transport imported *symbol*, all uses are through
1
# Copyright (C) 2007-2010 Canonical Ltd
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
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
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
16
17
"""Tests for fetch between repositories of the same type."""
18
19
from bzrlib import (
20
    bzrdir,
21
    errors,
2696.3.10 by Martin Pool
Add missing import
22
    gpg,
4360.2.1 by Robert Collins
Don't return ghosts in the keyset for PendingAncestryResult.
23
    graph,
4324.3.1 by Robert Collins
When adding rich root data follow the standard revision graph rules, so it does not create 'inconstent parents'.
24
    remote,
2948.3.1 by John Arbash Meinel
Fix bug #158333, make sure that Repository.fetch(self) is properly a no-op for all Repository implementations.
25
    repository,
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
26
    tests,
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
27
    )
4324.3.1 by Robert Collins
When adding rich root data follow the standard revision graph rules, so it does not create 'inconstent parents'.
28
from bzrlib.inventory import ROOT_ID
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
29
from bzrlib.tests import TestSkipped
3689.1.1 by John Arbash Meinel
Rename repository_implementations tests into per_repository tests
30
from bzrlib.tests.per_repository import TestCaseWithRepository
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
31
32
33
class TestFetchSameRepository(TestCaseWithRepository):
34
35
    def test_fetch(self):
36
        # smoke test fetch to ensure that the convenience function works.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
37
        # it is defined as a convenience function with the underlying
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
38
        # functionality provided by an InterRepository
39
        tree_a = self.make_branch_and_tree('a')
40
        self.build_tree(['a/foo'])
41
        tree_a.add('foo', 'file1')
42
        tree_a.commit('rev1', rev_id='rev1')
43
        # fetch with a default limit (grab everything)
2711.2.6 by Martin Pool
Fix up conversion of create_repository to make_repository in test_fetch
44
        repo = self.make_repository('b')
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
45
        if (tree_a.branch.repository.supports_rich_root() and not
46
            repo.supports_rich_root()):
47
            raise TestSkipped('Cannot fetch from model2 to model1')
48
        repo.fetch(tree_a.branch.repository,
49
                   revision_id=None)
50
4145.1.1 by Robert Collins
Explicitly prevent fetching while the target repository is in a write group.
51
    def test_fetch_fails_in_write_group(self):
52
        # fetch() manages a write group itself, fetching within one isn't safe.
53
        repo = self.make_repository('a')
54
        repo.lock_write()
55
        self.addCleanup(repo.unlock)
56
        repo.start_write_group()
57
        self.addCleanup(repo.abort_write_group)
58
        # Don't need a specific class - not expecting flow control based on
59
        # this.
60
        self.assertRaises(errors.BzrError, repo.fetch, repo)
61
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
62
    def test_fetch_to_knit3(self):
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
63
        # create a repository of the sort we are testing.
3242.2.17 by Aaron Bentley
Fix broken tests
64
        tree_a = self.make_branch_and_tree('a')
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
65
        self.build_tree(['a/foo'])
66
        tree_a.add('foo', 'file1')
67
        tree_a.commit('rev1', rev_id='rev1')
68
        # create a knit-3 based format to fetch into
69
        f = bzrdir.format_registry.make_bzrdir('dirstate-with-subtree')
70
        try:
71
            format = tree_a.branch.repository._format
72
            format.check_conversion_target(f.repository_format)
73
            # if we cannot convert data to knit3, skip the test.
74
        except errors.BadConversionTarget, e:
75
            raise TestSkipped(str(e))
76
        self.get_transport().mkdir('b')
77
        b_bzrdir = f.initialize(self.get_url('b'))
78
        knit3_repo = b_bzrdir.create_repository()
79
        # fetch with a default limit (grab everything)
80
        knit3_repo.fetch(tree_a.branch.repository, revision_id=None)
2592.3.96 by Robert Collins
Merge index improvements (includes bzr.dev).
81
        # Reopen to avoid any in-memory caching - ensure its reading from
82
        # disk.
83
        knit3_repo = b_bzrdir.open_repository()
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
84
        rev1_tree = knit3_repo.revision_tree('rev1')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
85
        rev1_tree.lock_read()
86
        try:
87
            lines = rev1_tree.get_file_lines(rev1_tree.get_root_id())
88
        finally:
89
            rev1_tree.unlock()
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
90
        self.assertEqual([], lines)
91
        b_branch = b_bzrdir.create_branch()
92
        b_branch.pull(tree_a.branch)
93
        try:
94
            tree_b = b_bzrdir.create_workingtree()
95
        except errors.NotLocalUrl:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
96
            try:
97
                tree_b = b_branch.create_checkout('b', lightweight=True)
98
            except errors.NotLocalUrl:
99
                raise TestSkipped("cannot make working tree with transport %r"
2696.3.2 by Martin Pool
Move some per-repository tests from big test_repository to test_fetch
100
                              % b_bzrdir.transport)
101
        tree_b.commit('no change', rev_id='rev2')
102
        rev2_tree = knit3_repo.revision_tree('rev2')
103
        self.assertEqual('rev1', rev2_tree.inventory.root.revision)
2696.3.9 by Martin Pool
merge trunk
104
4324.3.1 by Robert Collins
When adding rich root data follow the standard revision graph rules, so it does not create 'inconstent parents'.
105
    def do_test_fetch_to_rich_root_sets_parents_correctly(self, result,
106
        snapshots, root_id=ROOT_ID, allow_lefthand_ghost=False):
107
        """Assert that result is the parents of 'tip' after fetching snapshots.
108
109
        This helper constructs a 1.9 format source, and a test-format target
110
        and fetches the result of building snapshots in the source, then
111
        asserts that the parents of tip are result.
112
113
        :param result: A parents list for the inventories.get_parent_map call.
114
        :param snapshots: An iterable of snapshot parameters for
115
            BranchBuilder.build_snapshot.
116
        '"""
117
        # This overlaps slightly with the tests for commit builder about graph
118
        # consistency.
119
        # Cases:
120
        repo = self.make_repository('target')
4324.3.3 by Robert Collins
Fix silly typo.
121
        remote_format = isinstance(repo, remote.RemoteRepository)
4324.3.1 by Robert Collins
When adding rich root data follow the standard revision graph rules, so it does not create 'inconstent parents'.
122
        if not repo._format.rich_root_data and not remote_format:
123
            return # not relevant
124
        builder = self.make_branch_builder('source', format='1.9')
125
        builder.start_series()
126
        for revision_id, parent_ids, actions in snapshots:
127
            builder.build_snapshot(revision_id, parent_ids, actions,
128
            allow_leftmost_as_ghost=allow_lefthand_ghost)
129
        builder.finish_series()
130
        source = builder.get_branch()
131
        if remote_format and not repo._format.rich_root_data:
132
            # use a manual rich root format to ensure the code path is tested.
133
            repo = self.make_repository('remote-target',
134
                format='1.9-rich-root')
135
        repo.lock_write()
136
        self.addCleanup(repo.unlock)
137
        repo.fetch(source.repository)
138
        self.assertEqual(result,
139
            repo.texts.get_parent_map([(root_id, 'tip')])[(root_id, 'tip')])
140
141
    def test_fetch_to_rich_root_set_parent_no_parents(self):
142
        # No parents rev -> No parents
143
        self.do_test_fetch_to_rich_root_sets_parents_correctly((),
144
            [('tip', None, [('add', ('', ROOT_ID, 'directory', ''))]),
145
            ])
146
147
    def test_fetch_to_rich_root_set_parent_1_parent(self):
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
148
        # 1 parent rev -> 1 parent
4324.3.1 by Robert Collins
When adding rich root data follow the standard revision graph rules, so it does not create 'inconstent parents'.
149
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
150
            ((ROOT_ID, 'base'),),
151
            [('base', None, [('add', ('', ROOT_ID, 'directory', ''))]),
152
             ('tip', None, []),
153
            ])
154
155
    def test_fetch_to_rich_root_set_parent_1_ghost_parent(self):
156
        # 1 ghost parent -> No parents
157
        self.do_test_fetch_to_rich_root_sets_parents_correctly((),
158
            [('tip', ['ghost'], [('add', ('', ROOT_ID, 'directory', ''))]),
159
            ], allow_lefthand_ghost=True)
160
161
    def test_fetch_to_rich_root_set_parent_2_head_parents(self):
162
        # 2 parents both heads -> 2 parents
163
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
164
            ((ROOT_ID, 'left'), (ROOT_ID, 'right')),
165
            [('base', None, [('add', ('', ROOT_ID, 'directory', ''))]),
166
             ('left', None, []),
167
             ('right', ['base'], []),
168
             ('tip', ['left', 'right'], []),
169
            ])
170
171
    def test_fetch_to_rich_root_set_parent_2_parents_1_head(self):
172
        # 2 parents one head -> 1 parent
173
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
174
            ((ROOT_ID, 'right'),),
175
            [('left', None, [('add', ('', ROOT_ID, 'directory', ''))]),
176
             ('right', None, []),
177
             ('tip', ['left', 'right'], []),
178
            ])
179
180
    def test_fetch_to_rich_root_set_parent_1_parent_different_id_gone(self):
181
        # 1 parent different fileid, ours missing -> no parents
182
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
183
            (),
184
            [('base', None, [('add', ('', ROOT_ID, 'directory', ''))]),
185
             ('tip', None, [('unversion', ROOT_ID),
186
                            ('add', ('', 'my-root', 'directory', '')),
187
                            ]),
188
            ], root_id='my-root')
189
190
    def test_fetch_to_rich_root_set_parent_1_parent_different_id_moved(self):
191
        # 1 parent different fileid, ours moved -> 1 parent
192
        # (and that parent honours the changing revid of the other location)
193
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
194
            (('my-root', 'origin'),),
195
            [('origin', None, [('add', ('', ROOT_ID, 'directory', '')),
196
                             ('add', ('child', 'my-root', 'directory', ''))]),
197
             ('base', None, []),
198
             ('tip', None, [('unversion', 'my-root'),
199
                            ('unversion', ROOT_ID),
200
                            ('add', ('', 'my-root', 'directory', '')),
201
                            ]),
202
            ], root_id='my-root')
203
204
    def test_fetch_to_rich_root_set_parent_2_parent_1_different_id_gone(self):
205
        # 2 parents, 1 different fileid, our second missing -> 1 parent
206
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
207
            (('my-root', 'right'),),
208
            [('base', None, [('add', ('', ROOT_ID, 'directory', ''))]),
209
             ('right', None, [('unversion', ROOT_ID),
210
                              ('add', ('', 'my-root', 'directory', ''))]),
211
             ('tip', ['base', 'right'], [('unversion', ROOT_ID),
212
                            ('add', ('', 'my-root', 'directory', '')),
213
                            ]),
214
            ], root_id='my-root')
215
216
    def test_fetch_to_rich_root_set_parent_2_parent_2_different_id_moved(self):
217
        # 2 parents, 1 different fileid, our second moved -> 2 parent
218
        # (and that parent honours the changing revid of the other location)
219
        self.do_test_fetch_to_rich_root_sets_parents_correctly(
220
            (('my-root', 'right'),),
221
            # 'my-root' at 'child'.
222
            [('origin', None, [('add', ('', ROOT_ID, 'directory', '')),
223
                             ('add', ('child', 'my-root', 'directory', ''))]),
224
             ('base', None, []),
225
            # 'my-root' at root
226
             ('right', None, [('unversion', 'my-root'),
227
                              ('unversion', ROOT_ID),
228
                              ('add', ('', 'my-root', 'directory', ''))]),
229
             ('tip', ['base', 'right'], [('unversion', 'my-root'),
230
                            ('unversion', ROOT_ID),
231
                            ('add', ('', 'my-root', 'directory', '')),
232
                            ]),
233
            ], root_id='my-root')
234
2948.3.1 by John Arbash Meinel
Fix bug #158333, make sure that Repository.fetch(self) is properly a no-op for all Repository implementations.
235
    def test_fetch_all_from_self(self):
236
        tree = self.make_branch_and_tree('.')
237
        rev_id = tree.commit('one')
238
        # This needs to be a new copy of the repository, if this changes, the
239
        # test needs to be rewritten
240
        repo = tree.branch.repository.bzrdir.open_repository()
241
        # This fetch should be a no-op see bug #158333
242
        tree.branch.repository.fetch(repo, None)
243
244
    def test_fetch_from_self(self):
245
        tree = self.make_branch_and_tree('.')
246
        rev_id = tree.commit('one')
247
        repo = tree.branch.repository.bzrdir.open_repository()
248
        # This fetch should be a no-op see bug #158333
249
        tree.branch.repository.fetch(repo, rev_id)
250
251
    def test_fetch_missing_from_self(self):
252
        tree = self.make_branch_and_tree('.')
253
        rev_id = tree.commit('one')
254
        # Even though the fetch() is a NO-OP it should assert the revision id
255
        # is present
256
        repo = tree.branch.repository.bzrdir.open_repository()
257
        self.assertRaises(errors.NoSuchRevision, tree.branch.repository.fetch,
258
                          repo, 'no-such-revision')
259
2696.3.9 by Martin Pool
merge trunk
260
    def makeARepoWithSignatures(self):
261
        wt = self.make_branch_and_tree('a-repo-with-sigs')
262
        wt.commit('rev1', allow_pointless=True, rev_id='rev1')
263
        repo = wt.branch.repository
2592.3.96 by Robert Collins
Merge index improvements (includes bzr.dev).
264
        repo.lock_write()
265
        repo.start_write_group()
2592.3.103 by Robert Collins
Fix some more pack-related test breakage.
266
        repo.sign_revision('rev1', gpg.LoopbackGPGStrategy(None))
2592.3.96 by Robert Collins
Merge index improvements (includes bzr.dev).
267
        repo.commit_write_group()
268
        repo.unlock()
2696.3.9 by Martin Pool
merge trunk
269
        return repo
270
271
    def test_fetch_copies_signatures(self):
272
        source_repo = self.makeARepoWithSignatures()
273
        target_repo = self.make_repository('target')
274
        target_repo.fetch(source_repo, revision_id=None)
275
        self.assertEqual(
276
            source_repo.get_signature_text('rev1'),
277
            target_repo.get_signature_text('rev1'))
2535.3.48 by Andrew Bennetts
Merge from bzr.dev.
278
279
    def make_repository_with_one_revision(self):
3242.2.17 by Aaron Bentley
Fix broken tests
280
        wt = self.make_branch_and_tree('source')
2535.3.48 by Andrew Bennetts
Merge from bzr.dev.
281
        wt.commit('rev1', allow_pointless=True, rev_id='rev1')
282
        return wt.branch.repository
283
284
    def test_fetch_revision_already_exists(self):
285
        # Make a repository with one revision.
286
        source_repo = self.make_repository_with_one_revision()
287
        # Fetch that revision into a second repository.
288
        target_repo = self.make_repository('target')
289
        target_repo.fetch(source_repo, revision_id='rev1')
290
        # Now fetch again; there will be nothing to do.  This should work
291
        # without causing any errors.
292
        target_repo.fetch(source_repo, revision_id='rev1')
293
1551.19.36 by Aaron Bentley
Prevent fetch all from causing pack collisions
294
    def test_fetch_all_same_revisions_twice(self):
295
        # Blind-fetching all the same revisions twice should succeed and be a
296
        # no-op the second time.
297
        repo = self.make_repository('repo')
298
        tree = self.make_branch_and_tree('tree')
299
        revision_id = tree.commit('test')
300
        repo.fetch(tree.branch.repository)
301
        repo.fetch(tree.branch.repository)
4360.2.1 by Robert Collins
Don't return ghosts in the keyset for PendingAncestryResult.
302
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
303
    def make_simple_branch_with_ghost(self):
4476.3.59 by Andrew Bennetts
Undo changes that aren't needed anymore.
304
        builder = self.make_branch_builder('source')
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
305
        builder.start_series()
306
        builder.build_snapshot('A-id', None, [
4476.3.59 by Andrew Bennetts
Undo changes that aren't needed anymore.
307
            ('add', ('', 'root-id', 'directory', None)),
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
308
            ('add', ('file', 'file-id', 'file', 'content\n'))])
309
        builder.build_snapshot('B-id', ['A-id', 'ghost-id'], [])
310
        builder.finish_series()
311
        source_b = builder.get_branch()
312
        source_b.lock_read()
313
        self.addCleanup(source_b.unlock)
314
        return source_b
315
316
    def test_fetch_with_ghost(self):
317
        source_b = self.make_simple_branch_with_ghost()
318
        target = self.make_repository('target')
319
        target.lock_write()
320
        self.addCleanup(target.unlock)
321
        target.fetch(source_b.repository, revision_id='B-id')
322
323
    def test_fetch_into_smart_with_ghost(self):
324
        trans = self.make_smart_server('target')
325
        source_b = self.make_simple_branch_with_ghost()
326
        target = self.make_repository('target')
327
        # Re-open the repository over the smart protocol
328
        target = repository.Repository.open(trans.base)
329
        target.lock_write()
330
        self.addCleanup(target.unlock)
331
        try:
332
            target.fetch(source_b.repository, revision_id='B-id')
333
        except errors.TokenLockingNotSupported:
334
            # The code inside fetch() that tries to lock and then fails, also
335
            # causes weird problems with 'lock_not_held' later on...
336
            target.lock_read()
337
            raise tests.KnownFailure('some repositories fail to fetch'
338
                ' via the smart server because of locking issues.')
339
340
    def test_fetch_from_smart_with_ghost(self):
341
        trans = self.make_smart_server('source')
342
        source_b = self.make_simple_branch_with_ghost()
343
        target = self.make_repository('target')
344
        target.lock_write()
345
        self.addCleanup(target.unlock)
346
        # Re-open the repository over the smart protocol
347
        source = repository.Repository.open(trans.base)
348
        source.lock_read()
349
        self.addCleanup(source.unlock)
350
        target.fetch(source, revision_id='B-id')
351
4360.2.1 by Robert Collins
Don't return ghosts in the keyset for PendingAncestryResult.
352
353
class TestSource(TestCaseWithRepository):
354
    """Tests for/about the results of Repository._get_source."""
355
356
    def test_no_absent_records_in_stream_with_ghosts(self):
4523.1.3 by Martin Pool
Rename to per_interrepository
357
        # XXX: Arguably should be in per_interrepository but
4360.2.1 by Robert Collins
Don't return ghosts in the keyset for PendingAncestryResult.
358
        # doesn't actually gain coverage there; need a specific set of
359
        # permutations to cover it.
4360.2.2 by Robert Collins
Add bug info.
360
        # bug lp:376255 was reported about this.
4360.2.1 by Robert Collins
Don't return ghosts in the keyset for PendingAncestryResult.
361
        builder = self.make_branch_builder('repo')
362
        builder.start_series()
363
        builder.build_snapshot('tip', ['ghost'],
364
            [('add', ('', 'ROOT_ID', 'directory', ''))],
365
            allow_leftmost_as_ghost=True)
366
        builder.finish_series()
367
        b = builder.get_branch()
368
        b.lock_read()
369
        self.addCleanup(b.unlock)
370
        repo = b.repository
371
        source = repo._get_source(repo._format)
372
        search = graph.PendingAncestryResult(['tip'], repo)
373
        stream = source.get_stream(search)
374
        for substream_type, substream in stream:
375
            for record in substream:
376
                self.assertNotEqual('absent', record.storage_kind,
377
                    "Absent record for %s" % (((substream_type,) + record.key),))