~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Martin Pool
  • Date: 2005-08-18 07:51:58 UTC
  • Revision ID: mbp@sourcefrog.net-20050818075157-5f69075fa843d558
- add space to store revision-id in weave files

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 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
 
from bzrlib import (
22
 
    bzrdir,
23
 
    errors,
24
 
    repository,
25
 
    )
26
 
from bzrlib.branch import Branch
27
 
from bzrlib.bzrdir import BzrDir
28
 
from bzrlib.builtins import merge
29
 
import bzrlib.errors
30
 
from bzrlib.repofmt import knitrepo
31
 
from bzrlib.tests import TestCaseWithTransport
32
 
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
33
 
from bzrlib.tests.test_revision import make_branches
34
 
from bzrlib.trace import mutter
35
 
from bzrlib.upgrade import Convert
36
 
from bzrlib.workingtree import WorkingTree
37
 
 
38
 
 
39
 
def has_revision(branch, revision_id):
40
 
    return branch.repository.has_revision(revision_id)
41
 
 
42
 
def fetch_steps(self, br_a, br_b, writable_a):
43
 
    """A foreign test method for testing fetch locally and remotely."""
44
 
     
45
 
    # TODO RBC 20060201 make this a repository test.
46
 
    repo_b = br_b.repository
47
 
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
48
 
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[2]))
49
 
    self.assertEquals(len(br_b.revision_history()), 7)
50
 
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[2])[0], 0)
51
 
    # branch.fetch is not supposed to alter the revision history
52
 
    self.assertEquals(len(br_b.revision_history()), 7)
53
 
    self.assertFalse(repo_b.has_revision(br_a.revision_history()[3]))
54
 
 
55
 
    # fetching the next revision up in sample data copies one revision
56
 
    self.assertEquals(br_b.fetch(br_a, br_a.revision_history()[3])[0], 1)
57
 
    self.assertTrue(repo_b.has_revision(br_a.revision_history()[3]))
58
 
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
59
 
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
60
 
 
61
 
    # When a non-branch ancestor is missing, it should be unlisted...
62
 
    # as its not reference from the inventory weave.
63
 
    br_b4 = self.make_branch('br_4')
64
 
    count, failures = br_b4.fetch(br_b)
65
 
    self.assertEqual(count, 7)
66
 
    self.assertEqual(failures, [])
67
 
 
68
 
    self.assertEqual(writable_a.fetch(br_b)[0], 1)
69
 
    self.assertTrue(has_revision(br_a, br_b.revision_history()[3]))
70
 
    self.assertTrue(has_revision(br_a, br_b.revision_history()[4]))
71
 
        
72
 
    br_b2 = self.make_branch('br_b2')
73
 
    self.assertEquals(br_b2.fetch(br_b)[0], 7)
74
 
    self.assertTrue(has_revision(br_b2, br_b.revision_history()[4]))
75
 
    self.assertTrue(has_revision(br_b2, br_a.revision_history()[2]))
76
 
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
77
 
 
78
 
    br_a2 = self.make_branch('br_a2')
79
 
    self.assertEquals(br_a2.fetch(br_a)[0], 9)
80
 
    self.assertTrue(has_revision(br_a2, br_b.revision_history()[4]))
81
 
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[3]))
82
 
    self.assertTrue(has_revision(br_a2, br_a.revision_history()[2]))
83
 
 
84
 
    br_a3 = self.make_branch('br_a3')
85
 
    # pulling a branch with no revisions grabs nothing, regardless of 
86
 
    # whats in the inventory.
87
 
    self.assertEquals(br_a3.fetch(br_a2)[0], 0)
88
 
    for revno in range(4):
89
 
        self.assertFalse(
90
 
            br_a3.repository.has_revision(br_a.revision_history()[revno]))
91
 
    self.assertEqual(br_a3.fetch(br_a2, br_a.revision_history()[2])[0], 3)
92
 
    # pull the 3 revisions introduced by a@u-0-3
93
 
    fetched = br_a3.fetch(br_a2, br_a.revision_history()[3])[0]
94
 
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
95
 
    # InstallFailed should be raised if the branch is missing the revision
96
 
    # that was requested.
97
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
98
 
    # InstallFailed should be raised if the branch is missing a revision
99
 
    # from its own revision history
100
 
    br_a2.append_revision('a-b-c')
101
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
102
 
 
103
 
    # TODO: ADHB 20070116 Perhaps set_last_revision shouldn't accept
104
 
    #       revisions which are not present?  In that case, this test
105
 
    #       must be rewritten.
106
 
    #
107
 
    #       RBC 20060403 the way to do this is to uncommit the revision from
108
 
    #       the repository after the commit
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)
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
 
        root_knit = repo.weave_store.get_weave('tree-root',
151
 
                                                repo.get_transaction())
152
 
        # Make sure fetch retrieved only what we requested
153
 
        self.assertTrue('rev1' in root_knit)
154
 
        self.assertTrue('rev2' not in root_knit)
155
 
        branch.pull(tree.branch)
156
 
        root_knit = repo.weave_store.get_weave('tree-root',
157
 
                                                repo.get_transaction())
158
 
        # Make sure that the next revision in the root knit was retrieved,
159
 
        # even though the text, name, parent_id, etc., were unchanged.
160
 
        self.assertTrue('rev2' in root_knit)
161
 
 
162
 
    def test_fetch_incompatible(self):
163
 
        knit_tree = self.make_branch_and_tree('knit', format='knit')
164
 
        knit3_tree = self.make_branch_and_tree('knit3',
165
 
            format='dirstate-with-subtree')
166
 
        knit3_tree.commit('blah')
167
 
        self.assertRaises(errors.IncompatibleRepositories,
168
 
                          knit_tree.branch.fetch, knit3_tree.branch)
169
 
 
170
 
 
171
 
class TestMergeFetch(TestCaseWithTransport):
172
 
 
173
 
    def test_merge_fetches_unrelated(self):
174
 
        """Merge brings across history from unrelated source"""
175
 
        wt1 = self.make_branch_and_tree('br1')
176
 
        br1 = wt1.branch
177
 
        wt1.commit(message='rev 1-1', rev_id='1-1')
178
 
        wt1.commit(message='rev 1-2', rev_id='1-2')
179
 
        wt2 = self.make_branch_and_tree('br2')
180
 
        br2 = wt2.branch
181
 
        wt2.commit(message='rev 2-1', rev_id='2-1')
182
 
        merge(other_revision=['br1', -1], base_revision=['br1', 0],
183
 
              this_dir='br2')
184
 
        self._check_revs_present(br2)
185
 
 
186
 
    def test_merge_fetches(self):
187
 
        """Merge brings across history from source"""
188
 
        wt1 = self.make_branch_and_tree('br1')
189
 
        br1 = wt1.branch
190
 
        wt1.commit(message='rev 1-1', rev_id='1-1')
191
 
        dir_2 = br1.bzrdir.sprout('br2')
192
 
        br2 = dir_2.open_branch()
193
 
        wt1.commit(message='rev 1-2', rev_id='1-2')
194
 
        dir_2.open_workingtree().commit(message='rev 2-1', rev_id='2-1')
195
 
        merge(other_revision=['br1', -1], base_revision=[None, None], 
196
 
              this_dir='br2')
197
 
        self._check_revs_present(br2)
198
 
 
199
 
    def _check_revs_present(self, br2):
200
 
        for rev_id in '1-1', '1-2', '2-1':
201
 
            self.assertTrue(br2.repository.has_revision(rev_id))
202
 
            rev = br2.repository.get_revision(rev_id)
203
 
            self.assertEqual(rev.revision_id, rev_id)
204
 
            self.assertTrue(br2.repository.get_inventory(rev_id))
205
 
 
206
 
 
207
 
class TestMergeFileHistory(TestCaseWithTransport):
208
 
 
209
 
    def setUp(self):
210
 
        super(TestMergeFileHistory, self).setUp()
211
 
        wt1 = self.make_branch_and_tree('br1')
212
 
        br1 = wt1.branch
213
 
        self.build_tree_contents([('br1/file', 'original contents\n')])
214
 
        wt1.add('file', 'this-file-id')
215
 
        wt1.commit(message='rev 1-1', rev_id='1-1')
216
 
        dir_2 = br1.bzrdir.sprout('br2')
217
 
        br2 = dir_2.open_branch()
218
 
        wt2 = dir_2.open_workingtree()
219
 
        self.build_tree_contents([('br1/file', 'original from 1\n')])
220
 
        wt1.commit(message='rev 1-2', rev_id='1-2')
221
 
        self.build_tree_contents([('br1/file', 'agreement\n')])
222
 
        wt1.commit(message='rev 1-3', rev_id='1-3')
223
 
        self.build_tree_contents([('br2/file', 'contents in 2\n')])
224
 
        wt2.commit(message='rev 2-1', rev_id='2-1')
225
 
        self.build_tree_contents([('br2/file', 'agreement\n')])
226
 
        wt2.commit(message='rev 2-2', rev_id='2-2')
227
 
 
228
 
    def test_merge_fetches_file_history(self):
229
 
        """Merge brings across file histories"""
230
 
        br2 = Branch.open('br2')
231
 
        merge(other_revision=['br1', -1], base_revision=[None, None], 
232
 
              this_dir='br2')
233
 
        for rev_id, text in [('1-2', 'original from 1\n'),
234
 
                             ('1-3', 'agreement\n'),
235
 
                             ('2-1', 'contents in 2\n'),
236
 
                             ('2-2', 'agreement\n')]:
237
 
            self.assertEqualDiff(
238
 
                br2.repository.revision_tree(
239
 
                    rev_id).get_file_text('this-file-id'), text)
240
 
 
241
 
 
242
 
class TestHttpFetch(TestCaseWithWebserver):
243
 
    # FIXME RBC 20060124 this really isn't web specific, perhaps an
244
 
    # instrumented readonly transport? Can we do an instrumented
245
 
    # adapter and use self.get_readonly_url ?
246
 
 
247
 
    def test_fetch(self):
248
 
        #highest indices a: 5, b: 7
249
 
        br_a, br_b = make_branches(self)
250
 
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
251
 
        fetch_steps(self, br_rem_a, br_b, br_a)
252
 
 
253
 
    def _count_log_matches(self, target, logs):
254
 
        """Count the number of times the target file pattern was fetched in an http log"""
255
 
        get_succeeds_re = re.compile(
256
 
            '.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
257
 
            (     target,                    bzrlib.__version__))
258
 
        c = 0
259
 
        for line in logs:
260
 
            if get_succeeds_re.match(line):
261
 
                c += 1
262
 
        return c
263
 
 
264
 
    def test_weaves_are_retrieved_once(self):
265
 
        self.build_tree(("source/", "source/file", "target/"))
266
 
        wt = self.make_branch_and_tree('source')
267
 
        branch = wt.branch
268
 
        wt.add(["file"], ["id"])
269
 
        wt.commit("added file")
270
 
        print >>open("source/file", 'w'), "blah"
271
 
        wt.commit("changed file")
272
 
        target = BzrDir.create_branch_and_repo("target/")
273
 
        source = Branch.open(self.get_readonly_url("source/"))
274
 
        self.assertEqual(target.fetch(source), (2, []))
275
 
        # this is the path to the literal file. As format changes 
276
 
        # occur it needs to be updated. FIXME: ask the store for the
277
 
        # path.
278
 
        self.log("web server logs are:")
279
 
        http_logs = self.get_readonly_server().logs
280
 
        self.log('\n'.join(http_logs))
281
 
        # unfortunately this log entry is branch format specific. We could 
282
 
        # factor out the 'what files does this format use' to a method on the 
283
 
        # repository, which would let us to this generically. RBC 20060419
284
 
        self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
285
 
        self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
286
 
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
287
 
        # this r-h check test will prevent regressions, but it currently already 
288
 
        # passes, before the patch to cache-rh is applied :[
289
 
        self.assertTrue(1 >= self._count_log_matches('revision-history',
290
 
                                                     http_logs))
291
 
        self.assertTrue(1 >= self._count_log_matches('last-revision',
292
 
                                                     http_logs))
293
 
        # FIXME naughty poking in there.
294
 
        self.get_readonly_server().logs = []
295
 
        # check there is nothing more to fetch
296
 
        source = Branch.open(self.get_readonly_url("source/"))
297
 
        self.assertEqual(target.fetch(source), (0, []))
298
 
        # should make just two requests
299
 
        http_logs = self.get_readonly_server().logs
300
 
        self.log("web server logs are:")
301
 
        self.log('\n'.join(http_logs))
302
 
        self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
303
 
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
304
 
        self.assertEqual(1, self._count_log_matches('repository/format', http_logs))
305
 
        self.assertTrue(1 >= self._count_log_matches('revision-history',
306
 
                                                     http_logs))
307
 
        self.assertTrue(1 >= self._count_log_matches('last-revision',
308
 
                                                     http_logs))
309
 
        self.assertEqual(4, len(http_logs))