~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Alexander Belchenko
  • Date: 2007-01-24 19:42:26 UTC
  • mfrom: (2234.3.5 0.14)
  • mto: This revision was merged to the branch mainline in revision 2243.
  • Revision ID: bialix@ukr.net-20070124194226-rl1rlatr3jc5cvhh
merge latest fixes from bzr.0.14

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