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