~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testfetch.py

merge merge tweaks from aaron, which includes latest .dev

Show diffs side-by-side

added added

removed removed

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