/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: John Arbash Meinel
  • Date: 2008-08-18 22:34:21 UTC
  • mto: (3606.5.6 1.6)
  • mto: This revision was merged to the branch mainline in revision 3641.
  • Revision ID: john@arbash-meinel.com-20080818223421-todjny24vj4faj4t
Add tests for the fetching behavior.

The proper parameter passed is 'unordered' add an assert for it, and
fix callers that were passing 'unsorted' instead.
Add tests that we make the right get_record_stream call based
on the value of _fetch_uses_deltas.
Fix the fetch request for signatures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2007 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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
import os
 
18
import re
 
19
import sys
 
20
 
 
21
import bzrlib
 
22
from bzrlib import (
 
23
    bzrdir,
 
24
    errors,
 
25
    merge,
 
26
    repository,
 
27
    versionedfile,
 
28
    )
 
29
from bzrlib.branch import Branch
 
30
from bzrlib.bzrdir import BzrDir
 
31
from bzrlib.repofmt import knitrepo
 
32
from bzrlib.tests import TestCaseWithTransport
 
33
from bzrlib.tests.http_utils import TestCaseWithWebserver
 
34
from bzrlib.tests.test_revision import make_branches
 
35
from bzrlib.trace import mutter
 
36
from bzrlib.upgrade import Convert
 
37
from bzrlib.workingtree import WorkingTree
 
38
 
 
39
# These tests are a bit old; please instead add new tests into
 
40
# interrepository_implementations/ so they'll run on all relevant
 
41
# combinations.
 
42
 
 
43
 
 
44
def has_revision(branch, revision_id):
 
45
    return branch.repository.has_revision(revision_id)
 
46
 
 
47
def fetch_steps(self, br_a, br_b, writable_a):
 
48
    """A foreign test method for testing fetch locally and remotely."""
 
49
     
 
50
    # TODO RBC 20060201 make this a repository test.
 
51
    repo_b = br_b.repository
 
52
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
 
53
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
 
54
    self.assertEquals(len(br_b.revision_history()), 7)
 
55
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
 
56
    # branch.fetch is not supposed to alter the revision history
 
57
    self.assertEquals(len(br_b.revision_history()), 7)
 
58
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
 
59
 
 
60
    # fetching the next revision up in sample data copies one revision
 
61
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
 
62
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
 
63
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
 
64
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
 
65
 
 
66
    # When a non-branch ancestor is missing, it should be unlisted...
 
67
    # as its not reference from the inventory weave.
 
68
    br_b4 = self.make_branch('br_4')
 
69
    count, failures = br_b4.fetch(br_b)
 
70
    self.assertEqual(count, 7)
 
71
    self.assertEqual(failures, [])
 
72
 
 
73
    self.assertEqual(writable_a.fetch(br_b)[0], 1)
 
74
    self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
 
75
    self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
 
76
        
 
77
    br_b2 = self.make_branch('br_b2')
 
78
    self.assertEquals(br_b2.fetch(br_b)[0], 7)
 
79
    self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
 
80
    self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
 
81
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
 
82
 
 
83
    br_a2 = self.make_branch('br_a2')
 
84
    self.assertEquals(br_a2.fetch(br_a)[0], 9)
 
85
    self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
 
86
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
 
87
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
 
88
 
 
89
    br_a3 = self.make_branch('br_a3')
 
90
    # pulling a branch with no revisions grabs nothing, regardless of 
 
91
    # whats in the inventory.
 
92
    self.assertEquals(br_a3.fetch(br_a2)[0], 0)
 
93
    for revno in range(4):
 
94
        self.assertFalse(
 
95
            br_a3.repository.has_revision(br_a.revision_history()[revno]))
 
96
    self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
 
97
    # pull the 3 revisions introduced by a@u-0-3
 
98
    fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
 
99
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
 
100
    # InstallFailed should be raised if the branch is missing the revision
 
101
    # that was requested.
 
102
    self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
 
103
 
 
104
    # TODO: Test trying to fetch from a branch that points to a revision not
 
105
    # actually present in its repository.  Not every branch format allows you
 
106
    # to directly point to such revisions, so it's a bit complicated to
 
107
    # construct.  One way would be to uncommit and gc the revision, but not
 
108
    # every branch supports that.  -- mbp 20070814
 
109
 
 
110
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
 
111
    # Note that this means - updating the weave when ghosts are filled in to 
 
112
    # add the right parents.
 
113
 
 
114
 
 
115
class TestFetch(TestCaseWithTransport):
 
116
 
 
117
    def test_fetch(self):
 
118
        #highest indices a: 5, b: 7
 
119
        br_a, br_b = make_branches(self, format='dirstate-tags')
 
120
        fetch_steps(self, br_a, br_b, br_a)
 
121
 
 
122
    def test_fetch_self(self):
 
123
        wt = self.make_branch_and_tree('br')
 
124
        self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
 
125
 
 
126
    def test_fetch_root_knit(self):
 
127
        """Ensure that knit2.fetch() updates the root knit
 
128
        
 
129
        This tests the case where the root has a new revision, but there are no
 
130
        corresponding filename, parent, contents or other changes.
 
131
        """
 
132
        knit1_format = bzrdir.BzrDirMetaFormat1()
 
133
        knit1_format.repository_format = knitrepo.RepositoryFormatKnit1()
 
134
        knit2_format = bzrdir.BzrDirMetaFormat1()
 
135
        knit2_format.repository_format = knitrepo.RepositoryFormatKnit3()
 
136
        # we start with a knit1 repository because that causes the
 
137
        # root revision to change for each commit, even though the content,
 
138
        # parent, name, and other attributes are unchanged.
 
139
        tree = self.make_branch_and_tree('tree', knit1_format)
 
140
        tree.set_root_id('tree-root')
 
141
        tree.commit('rev1', rev_id='rev1')
 
142
        tree.commit('rev2', rev_id='rev2')
 
143
 
 
144
        # Now we convert it to a knit2 repository so that it has a root knit
 
145
        Convert(tree.basedir, knit2_format)
 
146
        tree = WorkingTree.open(tree.basedir)
 
147
        branch = self.make_branch('branch', format=knit2_format)
 
148
        branch.pull(tree.branch, stop_revision='rev1')
 
149
        repo = branch.repository
 
150
        repo.lock_read()
 
151
        try:
 
152
            # Make sure fetch retrieved only what we requested
 
153
            self.assertEqual({('tree-root', 'rev1'):()},
 
154
                repo.texts.get_parent_map(
 
155
                    [('tree-root', 'rev1'), ('tree-root', 'rev2')]))
 
156
        finally:
 
157
            repo.unlock()
 
158
        branch.pull(tree.branch)
 
159
        # Make sure that the next revision in the root knit was retrieved,
 
160
        # even though the text, name, parent_id, etc., were unchanged.
 
161
        repo.lock_read()
 
162
        try:
 
163
            # Make sure fetch retrieved only what we requested
 
164
            self.assertEqual({('tree-root', 'rev2'):(('tree-root', 'rev1'),)},
 
165
                repo.texts.get_parent_map([('tree-root', 'rev2')]))
 
166
        finally:
 
167
            repo.unlock()
 
168
 
 
169
    def test_fetch_incompatible(self):
 
170
        knit_tree = self.make_branch_and_tree('knit', format='knit')
 
171
        knit3_tree = self.make_branch_and_tree('knit3',
 
172
            format='dirstate-with-subtree')
 
173
        knit3_tree.commit('blah')
 
174
        e = self.assertRaises(errors.IncompatibleRepositories,
 
175
                              knit_tree.branch.fetch, knit3_tree.branch)
 
176
        self.assertContainsRe(str(e),
 
177
            r"(?m).*/knit.*\nis not compatible with\n.*/knit3/.*\n"
 
178
            r"different rich-root support")
 
179
 
 
180
 
 
181
class TestMergeFetch(TestCaseWithTransport):
 
182
 
 
183
    def test_merge_fetches_unrelated(self):
 
184
        """Merge brings across history from unrelated source"""
 
185
        wt1 = self.make_branch_and_tree('br1')
 
186
        br1 = wt1.branch
 
187
        wt1.commit(message='rev 1-1', rev_id='1-1')
 
188
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
189
        wt2 = self.make_branch_and_tree('br2')
 
190
        br2 = wt2.branch
 
191
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
192
        wt2.merge_from_branch(br1, from_revision='null:')
 
193
        self._check_revs_present(br2)
 
194
 
 
195
    def test_merge_fetches(self):
 
196
        """Merge brings across history from source"""
 
197
        wt1 = self.make_branch_and_tree('br1')
 
198
        br1 = wt1.branch
 
199
        wt1.commit(message='rev 1-1', rev_id='1-1')
 
200
        dir_2 = br1.bzrdir.sprout('br2')
 
201
        br2 = dir_2.open_branch()
 
202
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
203
        wt2 = dir_2.open_workingtree()
 
204
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
205
        wt2.merge_from_branch(br1)
 
206
        self._check_revs_present(br2)
 
207
 
 
208
    def _check_revs_present(self, br2):
 
209
        for rev_id in '1-1', '1-2', '2-1':
 
210
            self.assertTrue(br2.repository.has_revision(rev_id))
 
211
            rev = br2.repository.get_revision(rev_id)
 
212
            self.assertEqual(rev.revision_id, rev_id)
 
213
            self.assertTrue(br2.repository.get_inventory(rev_id))
 
214
 
 
215
 
 
216
class TestMergeFileHistory(TestCaseWithTransport):
 
217
 
 
218
    def setUp(self):
 
219
        super(TestMergeFileHistory, self).setUp()
 
220
        wt1 = self.make_branch_and_tree('br1')
 
221
        br1 = wt1.branch
 
222
        self.build_tree_contents([('br1/file', 'original contents\n')])
 
223
        wt1.add('file', 'this-file-id')
 
224
        wt1.commit(message='rev 1-1', rev_id='1-1')
 
225
        dir_2 = br1.bzrdir.sprout('br2')
 
226
        br2 = dir_2.open_branch()
 
227
        wt2 = dir_2.open_workingtree()
 
228
        self.build_tree_contents([('br1/file', 'original from 1\n')])
 
229
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
230
        self.build_tree_contents([('br1/file', 'agreement\n')])
 
231
        wt1.commit(message='rev 1-3', rev_id='1-3')
 
232
        self.build_tree_contents([('br2/file', 'contents in 2\n')])
 
233
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
234
        self.build_tree_contents([('br2/file', 'agreement\n')])
 
235
        wt2.commit(message='rev 2-2', rev_id='2-2')
 
236
 
 
237
    def test_merge_fetches_file_history(self):
 
238
        """Merge brings across file histories"""
 
239
        br2 = Branch.open('br2')
 
240
        br1 = Branch.open('br1')
 
241
        wt2 = WorkingTree.open('br2').merge_from_branch(br1)
 
242
        br2.lock_read()
 
243
        self.addCleanup(br2.unlock)
 
244
        for rev_id, text in [('1-2', 'original from 1\n'),
 
245
                             ('1-3', 'agreement\n'),
 
246
                             ('2-1', 'contents in 2\n'),
 
247
                             ('2-2', 'agreement\n')]:
 
248
            self.assertEqualDiff(
 
249
                br2.repository.revision_tree(
 
250
                    rev_id).get_file_text('this-file-id'), text)
 
251
 
 
252
 
 
253
class TestHttpFetch(TestCaseWithWebserver):
 
254
    # FIXME RBC 20060124 this really isn't web specific, perhaps an
 
255
    # instrumented readonly transport? Can we do an instrumented
 
256
    # adapter and use self.get_readonly_url ?
 
257
 
 
258
    def test_fetch(self):
 
259
        #highest indices a: 5, b: 7
 
260
        br_a, br_b = make_branches(self)
 
261
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
 
262
        fetch_steps(self, br_rem_a, br_b, br_a)
 
263
 
 
264
    def _count_log_matches(self, target, logs):
 
265
        """Count the number of times the target file pattern was fetched in an http log"""
 
266
        get_succeeds_re = re.compile(
 
267
            '.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
 
268
            (     target,                    bzrlib.__version__))
 
269
        c = 0
 
270
        for line in logs:
 
271
            if get_succeeds_re.match(line):
 
272
                c += 1
 
273
        return c
 
274
 
 
275
    def test_weaves_are_retrieved_once(self):
 
276
        self.build_tree(("source/", "source/file", "target/"))
 
277
        # This test depends on knit dasta storage.
 
278
        wt = self.make_branch_and_tree('source', format='dirstate-tags')
 
279
        branch = wt.branch
 
280
        wt.add(["file"], ["id"])
 
281
        wt.commit("added file")
 
282
        open("source/file", 'w').write("blah\n")
 
283
        wt.commit("changed file")
 
284
        target = BzrDir.create_branch_and_repo("target/")
 
285
        source = Branch.open(self.get_readonly_url("source/"))
 
286
        self.assertEqual(target.fetch(source), (2, []))
 
287
        # this is the path to the literal file. As format changes 
 
288
        # occur it needs to be updated. FIXME: ask the store for the
 
289
        # path.
 
290
        self.log("web server logs are:")
 
291
        http_logs = self.get_readonly_server().logs
 
292
        self.log('\n'.join(http_logs))
 
293
        # unfortunately this log entry is branch format specific. We could 
 
294
        # factor out the 'what files does this format use' to a method on the 
 
295
        # repository, which would let us to this generically. RBC 20060419
 
296
        # RBC 20080408: Or perhaps we can assert that no files are fully read
 
297
        # twice?
 
298
        self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
 
299
        self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
 
300
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
 
301
        # this r-h check test will prevent regressions, but it currently already 
 
302
        # passes, before the patch to cache-rh is applied :[
 
303
        self.assertTrue(1 >= self._count_log_matches('revision-history',
 
304
                                                     http_logs))
 
305
        self.assertTrue(1 >= self._count_log_matches('last-revision',
 
306
                                                     http_logs))
 
307
        # FIXME naughty poking in there.
 
308
        self.get_readonly_server().logs = []
 
309
        # check there is nothing more to fetch.  We take care to re-use the
 
310
        # existing transport so that the request logs we're about to examine
 
311
        # aren't cluttered with redundant probes for a smart server.
 
312
        # XXX: Perhaps this further parameterisation: test http with smart
 
313
        # server, and test http without smart server?
 
314
        source = Branch.open(
 
315
            self.get_readonly_url("source/"),
 
316
            possible_transports=[source.bzrdir.root_transport])
 
317
        self.assertEqual(target.fetch(source), (0, []))
 
318
        # should make just two requests
 
319
        http_logs = self.get_readonly_server().logs
 
320
        self.log("web server logs are:")
 
321
        self.log('\n'.join(http_logs))
 
322
        self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
 
323
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
 
324
        self.assertEqual(1, self._count_log_matches('repository/format',
 
325
            http_logs))
 
326
        self.assertTrue(1 >= self._count_log_matches('revision-history',
 
327
                                                     http_logs))
 
328
        self.assertTrue(1 >= self._count_log_matches('last-revision',
 
329
                                                     http_logs))
 
330
        self.assertEqual(4, len(http_logs))
 
331
 
 
332
 
 
333
class TestKnitToPackFetch(TestCaseWithTransport):
 
334
 
 
335
    def find_get_record_stream(self, calls):
 
336
        """In a list of calls, find 'get_record_stream' calls.
 
337
 
 
338
        This also ensures that there is only one get_record_stream call.
 
339
        """
 
340
        get_record_call = None
 
341
        for call in calls:
 
342
            if call[0] == 'get_record_stream':
 
343
                self.assertIs(None, get_record_call,
 
344
                              "there should only be one call to"
 
345
                              " get_record_stream")
 
346
                get_record_call = call
 
347
        self.assertIsNot(None, get_record_call,
 
348
                         "there should be exactly one call to "
 
349
                         " get_record_stream")
 
350
        return get_record_call
 
351
 
 
352
    def test_fetch_with_deltas_no_delta_closure(self):
 
353
        tree = self.make_branch_and_tree('source', format='dirstate')
 
354
        target = self.make_repository('target', format='pack-0.92')
 
355
        self.build_tree(['source/file'])
 
356
        tree.set_root_id('root-id')
 
357
        tree.add('file', 'file-id')
 
358
        tree.commit('one', rev_id='rev-one')
 
359
        source = tree.branch.repository
 
360
        source.texts = versionedfile.RecordingVersionedFilesDecorator(
 
361
                        source.texts)
 
362
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
 
363
                        source.signatures)
 
364
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
 
365
                        source.revisions)
 
366
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
 
367
                        source.inventories)
 
368
        # precondition
 
369
        self.assertTrue(target._fetch_uses_deltas)
 
370
        target.fetch(source, revision_id='rev-one')
 
371
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
 
372
                          target._fetch_order, False),
 
373
                         self.find_get_record_stream(source.texts.calls))
 
374
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
375
                          target._fetch_order, False),
 
376
                         self.find_get_record_stream(source.inventories.calls))
 
377
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
378
                          target._fetch_order, False),
 
379
                         self.find_get_record_stream(source.revisions.calls))
 
380
        # XXX: Signatures is special, and slightly broken. The
 
381
        # standard item_keys_introduced_by actually does a lookup for every
 
382
        # signature to see if it exists, rather than waiting to do them all at
 
383
        # once at the end. The fetch code then does an all-at-once and just
 
384
        # allows for some of them to be missing.
 
385
        # So we know there will be extra calls, but the *last* one is the one
 
386
        # we care about.
 
387
        signature_calls = source.signatures.calls[-1:]
 
388
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
389
                          target._fetch_order, False),
 
390
                         self.find_get_record_stream(signature_calls))
 
391
 
 
392
    def test_fetch_no_deltas_with_delta_closure(self):
 
393
        tree = self.make_branch_and_tree('source', format='dirstate')
 
394
        target = self.make_repository('target', format='pack-0.92')
 
395
        self.build_tree(['source/file'])
 
396
        tree.set_root_id('root-id')
 
397
        tree.add('file', 'file-id')
 
398
        tree.commit('one', rev_id='rev-one')
 
399
        source = tree.branch.repository
 
400
        source.texts = versionedfile.RecordingVersionedFilesDecorator(
 
401
                        source.texts)
 
402
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
 
403
                        source.signatures)
 
404
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
 
405
                        source.revisions)
 
406
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
 
407
                        source.inventories)
 
408
        target._fetch_uses_deltas = False
 
409
        target.fetch(source, revision_id='rev-one')
 
410
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
 
411
                          target._fetch_order, True),
 
412
                         self.find_get_record_stream(source.texts.calls))
 
413
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
414
                          target._fetch_order, True),
 
415
                         self.find_get_record_stream(source.inventories.calls))
 
416
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
417
                          target._fetch_order, True),
 
418
                         self.find_get_record_stream(source.revisions.calls))
 
419
        # XXX: Signatures is special, and slightly broken. The
 
420
        # standard item_keys_introduced_by actually does a lookup for every
 
421
        # signature to see if it exists, rather than waiting to do them all at
 
422
        # once at the end. The fetch code then does an all-at-once and just
 
423
        # allows for some of them to be missing.
 
424
        # So we know there will be extra calls, but the *last* one is the one
 
425
        # we care about.
 
426
        signature_calls = source.signatures.calls[-1:]
 
427
        self.assertEqual(('get_record_stream', [('rev-one',)],
 
428
                          target._fetch_order, True),
 
429
                         self.find_get_record_stream(signature_calls))
 
430
 
 
431
 
 
432
class Test1To2Fetch(TestCaseWithTransport):
 
433
    """Tests for Model1To2 failure modes"""
 
434
 
 
435
    def make_tree_and_repo(self):
 
436
        self.tree = self.make_branch_and_tree('tree', format='pack-0.92')
 
437
        self.repo = self.make_repository('rich-repo', format='rich-root-pack')
 
438
        self.repo.lock_write()
 
439
        self.addCleanup(self.repo.unlock)
 
440
 
 
441
    def do_fetch_order_test(self, first, second):
 
442
        """Test that fetch works no matter what the set order of revision is.
 
443
 
 
444
        This test depends on the order of items in a set, which is
 
445
        implementation-dependant, so we test A, B and then B, A.
 
446
        """
 
447
        self.make_tree_and_repo()
 
448
        self.tree.commit('Commit 1', rev_id=first)
 
449
        self.tree.commit('Commit 2', rev_id=second)
 
450
        self.repo.fetch(self.tree.branch.repository, second)
 
451
 
 
452
    def test_fetch_order_AB(self):
 
453
        """See do_fetch_order_test"""
 
454
        self.do_fetch_order_test('A', 'B')
 
455
 
 
456
    def test_fetch_order_BA(self):
 
457
        """See do_fetch_order_test"""
 
458
        self.do_fetch_order_test('B', 'A')
 
459
 
 
460
    def get_parents(self, file_id, revision_id):
 
461
        self.repo.lock_read()
 
462
        try:
 
463
            parent_map = self.repo.texts.get_parent_map([(file_id, revision_id)])
 
464
            return parent_map[(file_id, revision_id)]
 
465
        finally:
 
466
            self.repo.unlock()
 
467
 
 
468
    def test_fetch_ghosts(self):
 
469
        self.make_tree_and_repo()
 
470
        self.tree.commit('first commit', rev_id='left-parent')
 
471
        self.tree.add_parent_tree_id('ghost-parent')
 
472
        fork = self.tree.bzrdir.sprout('fork', 'null:').open_workingtree()
 
473
        fork.commit('not a ghost', rev_id='not-ghost-parent')
 
474
        self.tree.branch.repository.fetch(fork.branch.repository,
 
475
                                     'not-ghost-parent')
 
476
        self.tree.add_parent_tree_id('not-ghost-parent')
 
477
        self.tree.commit('second commit', rev_id='second-id')
 
478
        self.repo.fetch(self.tree.branch.repository, 'second-id')
 
479
        root_id = self.tree.get_root_id()
 
480
        self.assertEqual(
 
481
            ((root_id, 'left-parent'), (root_id, 'ghost-parent'),
 
482
             (root_id, 'not-ghost-parent')),
 
483
            self.get_parents(root_id, 'second-id'))
 
484
 
 
485
    def make_two_commits(self, change_root, fetch_twice):
 
486
        self.make_tree_and_repo()
 
487
        self.tree.commit('first commit', rev_id='first-id')
 
488
        if change_root:
 
489
            self.tree.set_root_id('unique-id')
 
490
        self.tree.commit('second commit', rev_id='second-id')
 
491
        if fetch_twice:
 
492
            self.repo.fetch(self.tree.branch.repository, 'first-id')
 
493
        self.repo.fetch(self.tree.branch.repository, 'second-id')
 
494
 
 
495
    def test_fetch_changed_root(self):
 
496
        self.make_two_commits(change_root=True, fetch_twice=False)
 
497
        self.assertEqual((), self.get_parents('unique-id', 'second-id'))
 
498
 
 
499
    def test_two_fetch_changed_root(self):
 
500
        self.make_two_commits(change_root=True, fetch_twice=True)
 
501
        self.assertEqual((), self.get_parents('unique-id', 'second-id'))
 
502
 
 
503
    def test_two_fetches(self):
 
504
        self.make_two_commits(change_root=False, fetch_twice=True)
 
505
        self.assertEqual((('TREE_ROOT', 'first-id'),),
 
506
            self.get_parents('TREE_ROOT', 'second-id'))