~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testfetch.py

  • Committer: abentley
  • Date: 2005-10-14 03:50:50 UTC
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1460.
  • Revision ID: abentley@lappy-20051014035050-d779472ccb599a51
semi-broke merge

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
16
 
17
17
import os
18
 
import re
19
18
import sys
20
19
 
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
20
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
 
21
from bzrlib.selftest.testrevision import make_branches
34
22
from bzrlib.trace import mutter
35
 
from bzrlib.upgrade import Convert
36
 
from bzrlib.workingtree import WorkingTree
 
23
from bzrlib.branch import Branch
 
24
from bzrlib.fetch import greedy_fetch
 
25
 
 
26
from bzrlib.selftest import TestCaseInTempDir
 
27
from bzrlib.selftest.HTTPTestUtil import TestCaseWithWebserver
37
28
 
38
29
 
39
30
def has_revision(branch, revision_id):
40
 
    return branch.repository.has_revision(revision_id)
 
31
    try:
 
32
        branch.get_revision_xml_file(revision_id)
 
33
        return True
 
34
    except bzrlib.errors.NoSuchRevision:
 
35
        return False
41
36
 
42
37
def fetch_steps(self, br_a, br_b, writable_a):
43
38
    """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]))
 
39
    def new_branch(name):
 
40
        os.mkdir(name)
 
41
        return Branch.initialize(name)
 
42
            
 
43
    assert not has_revision(br_b, br_a.revision_history()[3])
 
44
    assert has_revision(br_b, br_a.revision_history()[2])
 
45
    assert len(br_b.revision_history()) == 7
 
46
    assert greedy_fetch(br_b, br_a, br_a.revision_history()[2])[0] == 0
 
47
 
 
48
    # greedy_fetch is not supposed to alter the revision history
 
49
    assert len(br_b.revision_history()) == 7
 
50
    assert not has_revision(br_b, br_a.revision_history()[3])
 
51
 
 
52
    assert len(br_b.revision_history()) == 7
 
53
    assert greedy_fetch(br_b, br_a, br_a.revision_history()[3])[0] == 1
 
54
    assert has_revision(br_b, br_a.revision_history()[3])
 
55
    assert not has_revision(br_a, br_b.revision_history()[6])
 
56
    assert has_revision(br_a, br_b.revision_history()[5])
60
57
 
61
58
    # When a non-branch ancestor is missing, it should be unlisted...
62
59
    # as its not reference from the inventory weave.
63
 
    br_b4 = self.make_branch('br_4')
64
 
    count, failures = br_b4.fetch(br_b)
 
60
    br_b4 = new_branch('br_4')
 
61
    count, failures = greedy_fetch(br_b4, br_b)
65
62
    self.assertEqual(count, 7)
66
63
    self.assertEqual(failures, [])
67
64
 
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]))
 
65
    self.assertEqual(greedy_fetch(writable_a, br_b)[0], 1)
 
66
    assert has_revision(br_a, br_b.revision_history()[3])
 
67
    assert has_revision(br_a, br_b.revision_history()[4])
71
68
        
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)
 
69
    br_b2 = new_branch('br_b2')
 
70
    assert greedy_fetch(br_b2, br_b)[0] == 7
 
71
    assert has_revision(br_b2, br_b.revision_history()[4])
 
72
    assert has_revision(br_b2, br_a.revision_history()[2])
 
73
    assert not has_revision(br_b2, br_a.revision_history()[3])
 
74
 
 
75
    br_a2 = new_branch('br_a2')
 
76
    assert greedy_fetch(br_a2, br_a)[0] == 9
 
77
    assert has_revision(br_a2, br_b.revision_history()[4])
 
78
    assert has_revision(br_a2, br_a.revision_history()[3])
 
79
    assert has_revision(br_a2, br_a.revision_history()[2])
 
80
 
 
81
    br_a3 = new_branch('br_a3')
 
82
    assert greedy_fetch(br_a3, br_a2)[0] == 0
88
83
    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)
 
84
        assert not has_revision(br_a3, br_a.revision_history()[revno])
 
85
    self.assertEqual(greedy_fetch(br_a3, br_a2, br_a.revision_history()[2])[0], 3)
 
86
    fetched = greedy_fetch(br_a3, br_a2, br_a.revision_history()[3])[0]
 
87
    assert fetched == 3, "fetched %d instead of 3" % fetched
95
88
    # InstallFailed should be raised if the branch is missing the revision
96
89
    # that was requested.
97
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
 
90
    self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
 
91
                      br_a2, 'pizza')
98
92
    # InstallFailed should be raised if the branch is missing a revision
99
93
    # from its own revision history
100
94
    br_a2.append_revision('a-b-c')
101
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
 
95
    self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
 
96
                      br_a2)
102
97
 
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
98
 
110
99
    #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):
 
100
 
 
101
class TestFetch(TestCaseInTempDir):
116
102
 
117
103
    def test_fetch(self):
118
104
        #highest indices a: 5, b: 7
119
 
        br_a, br_b = make_branches(self)
 
105
        br_a, br_b = make_branches()
120
106
        fetch_steps(self, br_a, br_b, br_a)
121
107
 
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):
 
108
 
 
109
class TestHttpFetch(TestCaseWithWebserver):
208
110
 
209
111
    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 ?
 
112
        super(TestHttpFetch, self).setUp()
 
113
        self.weblogs = []
246
114
 
247
115
    def test_fetch(self):
248
116
        #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'))
 
117
        br_a, br_b = make_branches()
 
118
        br_rem_a = Branch.open(self.get_remote_url(br_a._transport.base))
251
119
        fetch_steps(self, br_rem_a, br_b, br_a)
252
120
 
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
 
121
    def log(self, *args):
 
122
        """Capture web server log messages for introspection."""
 
123
        super(TestHttpFetch, self).log(*args)
 
124
        if args[0].startswith("webserver"):
 
125
            self.weblogs.append(args[0])
263
126
 
264
127
    def test_weaves_are_retrieved_once(self):
265
128
        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")
 
129
        branch = Branch.initialize("source")
 
130
        branch.add(["file"], ["id"])
 
131
        branch.commit("added file")
270
132
        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, []))
 
133
        branch.commit("changed file")
 
134
        target = Branch.initialize("target/")
 
135
        source = Branch.open(self.get_remote_url("source/"))
 
136
        self.assertEqual(greedy_fetch(target, source), (2, []))
275
137
        # this is the path to the literal file. As format changes 
276
138
        # occur it needs to be updated. FIXME: ask the store for the
277
139
        # 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))
 
140
        weave_suffix = 'weaves/ce/id.weave HTTP/1.1" 200 -'
 
141
        self.assertEqual(1,
 
142
            len([log for log in self.weblogs if log.endswith(weave_suffix)]))
 
143
        inventory_weave_suffix = 'inventory.weave HTTP/1.1" 200 -'
 
144
        self.assertEqual(1,
 
145
            len([log for log in self.weblogs if log.endswith(
 
146
                inventory_weave_suffix)]))
287
147
        # this r-h check test will prevent regressions, but it currently already 
288
148
        # 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 = []
 
149
        revision_history_suffix = 'revision-history HTTP/1.1" 200 -'
 
150
        self.assertEqual(1,
 
151
            len([log for log in self.weblogs if log.endswith(
 
152
                revision_history_suffix)]))
 
153
        self.weblogs = []
295
154
        # 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))
 
155
        source = Branch.open(self.get_remote_url("source/"))
 
156
        self.assertEqual(greedy_fetch(target, source), (0, []))
 
157
        self.failUnless(self.weblogs[0].endswith('branch-format HTTP/1.1" 200 -'))
 
158
        self.failUnless(self.weblogs[1].endswith('revision-history HTTP/1.1" 200 -'))
 
159
        self.assertEqual(2, len(self.weblogs))