~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Robert Collins
  • Date: 2007-11-09 17:50:31 UTC
  • mto: This revision was merged to the branch mainline in revision 2988.
  • Revision ID: robertc@robertcollins.net-20071109175031-agaiy6530rvbprmb
Change (without backwards compatibility) the
iter_lines_added_or_present_in_versions VersionedFile API to yield the
text version that each line is being returned from. This is useful for
reconcile in determining what inventories reference what texts.
(Robert Collins)

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
    )
 
28
from bzrlib.branch import Branch
 
29
from bzrlib.bzrdir import BzrDir
 
30
from bzrlib.repofmt import knitrepo
 
31
from bzrlib.symbol_versioning import (
 
32
    zero_ninetyone,
 
33
    )
 
34
from bzrlib.tests import TestCaseWithTransport
 
35
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
 
36
from bzrlib.tests.test_revision import make_branches
 
37
from bzrlib.trace import mutter
 
38
from bzrlib.upgrade import Convert
 
39
from bzrlib.workingtree import WorkingTree
 
40
 
 
41
# These tests are a bit old; please instead add new tests into
 
42
# interrepository_implementations/ so they'll run on all relevant
 
43
# combinations.
 
44
 
 
45
 
 
46
def has_revision(branch, revision_id):
 
47
    return branch.repository.has_revision(revision_id)
 
48
 
 
49
def fetch_steps(self, br_a, br_b, writable_a):
 
50
    """A foreign test method for testing fetch locally and remotely."""
 
51
     
 
52
    # TODO RBC 20060201 make this a repository test.
 
53
    repo_b = br_b.repository
 
54
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
 
55
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
 
56
    self.assertEquals(len(br_b.revision_history()), 7)
 
57
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
 
58
    # branch.fetch is not supposed to alter the revision history
 
59
    self.assertEquals(len(br_b.revision_history()), 7)
 
60
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
 
61
 
 
62
    # fetching the next revision up in sample data copies one revision
 
63
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
 
64
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
 
65
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
 
66
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
 
67
 
 
68
    # When a non-branch ancestor is missing, it should be unlisted...
 
69
    # as its not reference from the inventory weave.
 
70
    br_b4 = self.make_branch('br_4')
 
71
    count, failures = br_b4.fetch(br_b)
 
72
    self.assertEqual(count, 7)
 
73
    self.assertEqual(failures, [])
 
74
 
 
75
    self.assertEqual(writable_a.fetch(br_b)[0], 1)
 
76
    self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
 
77
    self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
 
78
        
 
79
    br_b2 = self.make_branch('br_b2')
 
80
    self.assertEquals(br_b2.fetch(br_b)[0], 7)
 
81
    self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
 
82
    self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
 
83
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
 
84
 
 
85
    br_a2 = self.make_branch('br_a2')
 
86
    self.assertEquals(br_a2.fetch(br_a)[0], 9)
 
87
    self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
 
88
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
 
89
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
 
90
 
 
91
    br_a3 = self.make_branch('br_a3')
 
92
    # pulling a branch with no revisions grabs nothing, regardless of 
 
93
    # whats in the inventory.
 
94
    self.assertEquals(br_a3.fetch(br_a2)[0], 0)
 
95
    for revno in range(4):
 
96
        self.assertFalse(
 
97
            br_a3.repository.has_revision(br_a.revision_history()[revno]))
 
98
    self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
 
99
    # pull the 3 revisions introduced by a@u-0-3
 
100
    fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
 
101
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
 
102
    # InstallFailed should be raised if the branch is missing the revision
 
103
    # that was requested.
 
104
    self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
 
105
 
 
106
    # TODO: Test trying to fetch from a branch that points to a revision not
 
107
    # actually present in its repository.  Not every branch format allows you
 
108
    # to directly point to such revisions, so it's a bit complicated to
 
109
    # construct.  One way would be to uncommit and gc the revision, but not
 
110
    # every branch supports that.  -- mbp 20070814
 
111
 
 
112
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
 
113
    # Note that this means - updating the weave when ghosts are filled in to 
 
114
    # add the right parents.
 
115
 
 
116
 
 
117
class TestFetch(TestCaseWithTransport):
 
118
 
 
119
    def test_fetch(self):
 
120
        #highest indices a: 5, b: 7
 
121
        br_a, br_b = make_branches(self, format='dirstate-tags')
 
122
        fetch_steps(self, br_a, br_b, br_a)
 
123
 
 
124
    def test_fetch_self(self):
 
125
        wt = self.make_branch_and_tree('br')
 
126
        self.assertEqual(wt.branch.fetch(wt.branch), (0, []))
 
127
 
 
128
    def test_fetch_root_knit(self):
 
129
        """Ensure that knit2.fetch() updates the root knit
 
130
        
 
131
        This tests the case where the root has a new revision, but there are no
 
132
        corresponding filename, parent, contents or other changes.
 
133
        """
 
134
        knit1_format = bzrdir.BzrDirMetaFormat1()
 
135
        knit1_format.repository_format = knitrepo.RepositoryFormatKnit1()
 
136
        knit2_format = bzrdir.BzrDirMetaFormat1()
 
137
        knit2_format.repository_format = knitrepo.RepositoryFormatKnit3()
 
138
        # we start with a knit1 repository because that causes the
 
139
        # root revision to change for each commit, even though the content,
 
140
        # parent, name, and other attributes are unchanged.
 
141
        tree = self.make_branch_and_tree('tree', knit1_format)
 
142
        tree.set_root_id('tree-root')
 
143
        tree.commit('rev1', rev_id='rev1')
 
144
        tree.commit('rev2', rev_id='rev2')
 
145
 
 
146
        # Now we convert it to a knit2 repository so that it has a root knit
 
147
        Convert(tree.basedir, knit2_format)
 
148
        tree = WorkingTree.open(tree.basedir)
 
149
        branch = self.make_branch('branch', format=knit2_format)
 
150
        branch.pull(tree.branch, stop_revision='rev1')
 
151
        repo = branch.repository
 
152
        root_knit = repo.weave_store.get_weave('tree-root',
 
153
                                                repo.get_transaction())
 
154
        # Make sure fetch retrieved only what we requested
 
155
        self.assertTrue('rev1' in root_knit)
 
156
        self.assertTrue('rev2' not in root_knit)
 
157
        branch.pull(tree.branch)
 
158
        root_knit = repo.weave_store.get_weave('tree-root',
 
159
                                                repo.get_transaction())
 
160
        # Make sure that the next revision in the root knit was retrieved,
 
161
        # even though the text, name, parent_id, etc., were unchanged.
 
162
        self.assertTrue('rev2' in root_knit)
 
163
 
 
164
    def test_fetch_incompatible(self):
 
165
        knit_tree = self.make_branch_and_tree('knit', format='knit')
 
166
        knit3_tree = self.make_branch_and_tree('knit3',
 
167
            format='dirstate-with-subtree')
 
168
        knit3_tree.commit('blah')
 
169
        self.assertRaises(errors.IncompatibleRepositories,
 
170
                          knit_tree.branch.fetch, knit3_tree.branch)
 
171
 
 
172
 
 
173
class TestMergeFetch(TestCaseWithTransport):
 
174
 
 
175
    def test_merge_fetches_unrelated(self):
 
176
        """Merge brings across history from unrelated source"""
 
177
        wt1 = self.make_branch_and_tree('br1')
 
178
        br1 = wt1.branch
 
179
        wt1.commit(message='rev 1-1', rev_id='1-1')
 
180
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
181
        wt2 = self.make_branch_and_tree('br2')
 
182
        br2 = wt2.branch
 
183
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
184
        wt2.merge_from_branch(br1, from_revision='null:')
 
185
        self._check_revs_present(br2)
 
186
 
 
187
    def test_merge_fetches(self):
 
188
        """Merge brings across history from source"""
 
189
        wt1 = self.make_branch_and_tree('br1')
 
190
        br1 = wt1.branch
 
191
        wt1.commit(message='rev 1-1', rev_id='1-1')
 
192
        dir_2 = br1.bzrdir.sprout('br2')
 
193
        br2 = dir_2.open_branch()
 
194
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
195
        wt2 = dir_2.open_workingtree()
 
196
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
197
        wt2.merge_from_branch(br1)
 
198
        self._check_revs_present(br2)
 
199
 
 
200
    def _check_revs_present(self, br2):
 
201
        for rev_id in '1-1', '1-2', '2-1':
 
202
            self.assertTrue(br2.repository.has_revision(rev_id))
 
203
            rev = br2.repository.get_revision(rev_id)
 
204
            self.assertEqual(rev.revision_id, rev_id)
 
205
            self.assertTrue(br2.repository.get_inventory(rev_id))
 
206
 
 
207
 
 
208
class TestMergeFileHistory(TestCaseWithTransport):
 
209
 
 
210
    def setUp(self):
 
211
        super(TestMergeFileHistory, self).setUp()
 
212
        wt1 = self.make_branch_and_tree('br1')
 
213
        br1 = wt1.branch
 
214
        self.build_tree_contents([('br1/file', 'original contents\n')])
 
215
        wt1.add('file', 'this-file-id')
 
216
        wt1.commit(message='rev 1-1', rev_id='1-1')
 
217
        dir_2 = br1.bzrdir.sprout('br2')
 
218
        br2 = dir_2.open_branch()
 
219
        wt2 = dir_2.open_workingtree()
 
220
        self.build_tree_contents([('br1/file', 'original from 1\n')])
 
221
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
222
        self.build_tree_contents([('br1/file', 'agreement\n')])
 
223
        wt1.commit(message='rev 1-3', rev_id='1-3')
 
224
        self.build_tree_contents([('br2/file', 'contents in 2\n')])
 
225
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
226
        self.build_tree_contents([('br2/file', 'agreement\n')])
 
227
        wt2.commit(message='rev 2-2', rev_id='2-2')
 
228
 
 
229
    def test_merge_fetches_file_history(self):
 
230
        """Merge brings across file histories"""
 
231
        br2 = Branch.open('br2')
 
232
        br1 = Branch.open('br1')
 
233
        wt2 = WorkingTree.open('br2').merge_from_branch(br1)
 
234
        for rev_id, text in [('1-2', 'original from 1\n'),
 
235
                             ('1-3', 'agreement\n'),
 
236
                             ('2-1', 'contents in 2\n'),
 
237
                             ('2-2', 'agreement\n')]:
 
238
            self.assertEqualDiff(
 
239
                br2.repository.revision_tree(
 
240
                    rev_id).get_file_text('this-file-id'), text)
 
241
 
 
242
 
 
243
class TestHttpFetch(TestCaseWithWebserver):
 
244
    # FIXME RBC 20060124 this really isn't web specific, perhaps an
 
245
    # instrumented readonly transport? Can we do an instrumented
 
246
    # adapter and use self.get_readonly_url ?
 
247
 
 
248
    def test_fetch(self):
 
249
        #highest indices a: 5, b: 7
 
250
        br_a, br_b = make_branches(self)
 
251
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
 
252
        fetch_steps(self, br_rem_a, br_b, br_a)
 
253
 
 
254
    def _count_log_matches(self, target, logs):
 
255
        """Count the number of times the target file pattern was fetched in an http log"""
 
256
        get_succeeds_re = re.compile(
 
257
            '.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
 
258
            (     target,                    bzrlib.__version__))
 
259
        c = 0
 
260
        for line in logs:
 
261
            if get_succeeds_re.match(line):
 
262
                c += 1
 
263
        return c
 
264
 
 
265
    def test_weaves_are_retrieved_once(self):
 
266
        self.build_tree(("source/", "source/file", "target/"))
 
267
        wt = self.make_branch_and_tree('source')
 
268
        branch = wt.branch
 
269
        wt.add(["file"], ["id"])
 
270
        wt.commit("added file")
 
271
        open("source/file", 'w').write("blah\n")
 
272
        wt.commit("changed file")
 
273
        target = BzrDir.create_branch_and_repo("target/")
 
274
        source = Branch.open(self.get_readonly_url("source/"))
 
275
        self.assertEqual(target.fetch(source), (2, []))
 
276
        # this is the path to the literal file. As format changes 
 
277
        # occur it needs to be updated. FIXME: ask the store for the
 
278
        # path.
 
279
        self.log("web server logs are:")
 
280
        http_logs = self.get_readonly_server().logs
 
281
        self.log('\n'.join(http_logs))
 
282
        # unfortunately this log entry is branch format specific. We could 
 
283
        # factor out the 'what files does this format use' to a method on the 
 
284
        # repository, which would let us to this generically. RBC 20060419
 
285
        self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
 
286
        self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
 
287
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
 
288
        # this r-h check test will prevent regressions, but it currently already 
 
289
        # passes, before the patch to cache-rh is applied :[
 
290
        self.assertTrue(1 >= self._count_log_matches('revision-history',
 
291
                                                     http_logs))
 
292
        self.assertTrue(1 >= self._count_log_matches('last-revision',
 
293
                                                     http_logs))
 
294
        # FIXME naughty poking in there.
 
295
        self.get_readonly_server().logs = []
 
296
        # check there is nothing more to fetch
 
297
        source = Branch.open(self.get_readonly_url("source/"))
 
298
        self.assertEqual(target.fetch(source), (0, []))
 
299
        # should make just two requests
 
300
        http_logs = self.get_readonly_server().logs
 
301
        self.log("web server logs are:")
 
302
        self.log('\n'.join(http_logs))
 
303
        self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
 
304
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
 
305
        self.assertEqual(1, self._count_log_matches('repository/format', http_logs))
 
306
        self.assertTrue(1 >= self._count_log_matches('revision-history',
 
307
                                                     http_logs))
 
308
        self.assertTrue(1 >= self._count_log_matches('last-revision',
 
309
                                                     http_logs))
 
310
        self.assertEqual(4, len(http_logs))