~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-12 17:30:53 UTC
  • mfrom: (2338.3.1 hide-nested)
  • Revision ID: pqm@pqm.ubuntu.com-20070312173053-4cdb4cd14190d29e
Hide nested-tree commands and improve their docs

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005 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
18
19
import sys
19
20
 
 
21
from bzrlib import bzrdir, repository
20
22
from bzrlib.branch import Branch
 
23
from bzrlib.bzrdir import BzrDir
21
24
from bzrlib.builtins import merge
22
25
import bzrlib.errors
23
 
from bzrlib.fetch import greedy_fetch
 
26
from bzrlib.repofmt import knitrepo
 
27
from bzrlib.tests import TestCaseWithTransport
 
28
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
24
29
from bzrlib.tests.test_revision import make_branches
25
 
from bzrlib.tests import TestCaseInTempDir
26
 
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
27
30
from bzrlib.trace import mutter
 
31
from bzrlib.upgrade import Convert
 
32
from bzrlib.workingtree import WorkingTree
28
33
 
29
34
 
30
35
def has_revision(branch, revision_id):
31
 
    try:
32
 
        branch.repository.get_revision_xml_file(revision_id)
33
 
        return True
34
 
    except bzrlib.errors.NoSuchRevision:
35
 
        return False
 
36
    return branch.repository.has_revision(revision_id)
36
37
 
37
38
def fetch_steps(self, br_a, br_b, writable_a):
38
39
    """A foreign test method for testing fetch locally and remotely."""
39
 
    def new_branch(name):
40
 
        os.mkdir(name)
41
 
        return Branch.initialize(name)
42
 
            
43
 
    self.assertFalse(has_revision(br_b, br_a.revision_history()[3]))
44
 
    self.assert_(has_revision(br_b, br_a.revision_history()[2]))
45
 
    self.assertEquals(len(br_b.revision_history()), 7)
46
 
    self.assertEquals(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
 
    self.assertEquals(len(br_b.revision_history()), 7)
50
 
    self.assertFalse(has_revision(br_b, br_a.revision_history()[3]))
51
 
 
52
 
    self.assertEquals(len(br_b.revision_history()), 7)
53
 
    self.assertEquals(greedy_fetch(br_b, br_a, br_a.revision_history()[3])[0], 1)
54
 
    self.assert_(has_revision(br_b, br_a.revision_history()[3]))
 
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]))
55
54
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
56
 
    self.assert_(has_revision(br_a, br_b.revision_history()[5]))
 
55
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
57
56
 
58
57
    # When a non-branch ancestor is missing, it should be unlisted...
59
58
    # as its not reference from the inventory weave.
60
 
    br_b4 = new_branch('br_4')
61
 
    count, failures = greedy_fetch(br_b4, br_b)
 
59
    br_b4 = self.make_branch('br_4')
 
60
    count, failures = br_b4.fetch(br_b)
62
61
    self.assertEqual(count, 7)
63
62
    self.assertEqual(failures, [])
64
63
 
65
 
    self.assertEqual(greedy_fetch(writable_a, br_b)[0], 1)
66
 
    self.assert_(has_revision(br_a, br_b.revision_history()[3]))
67
 
    self.assert_(has_revision(br_a, br_b.revision_history()[4]))
 
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]))
68
67
        
69
 
    br_b2 = new_branch('br_b2')
70
 
    self.assertEquals(greedy_fetch(br_b2, br_b)[0], 7)
71
 
    self.assert_(has_revision(br_b2, br_b.revision_history()[4]))
72
 
    self.assert_(has_revision(br_b2, br_a.revision_history()[2]))
 
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]))
73
72
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
74
73
 
75
 
    br_a2 = new_branch('br_a2')
76
 
    self.assertEquals(greedy_fetch(br_a2, br_a)[0], 9)
77
 
    self.assert_(has_revision(br_a2, br_b.revision_history()[4]))
78
 
    self.assert_(has_revision(br_a2, br_a.revision_history()[3]))
79
 
    self.assert_(has_revision(br_a2, br_a.revision_history()[2]))
 
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]))
80
79
 
81
 
    br_a3 = new_branch('br_a3')
82
 
    self.assertEquals(greedy_fetch(br_a3, br_a2)[0], 0)
 
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)
83
84
    for revno in range(4):
84
 
        self.assertFalse(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
 
    self.assertEquals(fetched, 6, "fetched %d instead of 6" % fetched)
 
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)
88
91
    # InstallFailed should be raised if the branch is missing the revision
89
92
    # that was requested.
90
 
    self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
91
 
                      br_a2, 'pizza')
 
93
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
92
94
    # InstallFailed should be raised if the branch is missing a revision
93
95
    # from its own revision history
94
96
    br_a2.append_revision('a-b-c')
95
 
    self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
96
 
                      br_a2)
 
97
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
97
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
98
105
 
99
106
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
100
 
 
101
 
class TestFetch(TestCaseInTempDir):
 
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):
102
112
 
103
113
    def test_fetch(self):
104
114
        #highest indices a: 5, b: 7
105
115
        br_a, br_b = make_branches(self)
106
116
        fetch_steps(self, br_a, br_b, br_a)
107
117
 
108
 
 
109
 
class TestMergeFetch(TestCaseInTempDir):
 
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):
110
160
 
111
161
    def test_merge_fetches_unrelated(self):
112
162
        """Merge brings across history from unrelated source"""
113
 
        os.mkdir('br1')
114
 
        br1 = Branch.initialize('br1')
115
 
        br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
116
 
        br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
117
 
        os.mkdir('br2')
118
 
        br2 = Branch.initialize('br2')
119
 
        br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
 
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')
120
170
        merge(other_revision=['br1', -1], base_revision=['br1', 0],
121
171
              this_dir='br2')
122
172
        self._check_revs_present(br2)
123
173
 
124
174
    def test_merge_fetches(self):
125
175
        """Merge brings across history from source"""
126
 
        os.mkdir('br1')
127
 
        br1 = Branch.initialize('br1')
128
 
        br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
129
 
        br1.clone('br2')
130
 
        br2 = Branch.open('br2')
131
 
        br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
132
 
        br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
 
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')
133
183
        merge(other_revision=['br1', -1], base_revision=[None, None], 
134
184
              this_dir='br2')
135
185
        self._check_revs_present(br2)
142
192
            self.assertTrue(br2.repository.get_inventory(rev_id))
143
193
 
144
194
 
 
195
class TestMergeFileHistory(TestCaseWithTransport):
145
196
 
146
 
class TestMergeFileHistory(TestCaseInTempDir):
147
197
    def setUp(self):
148
 
        TestCaseInTempDir.setUp(self)
149
 
        os.mkdir('br1')
150
 
        br1 = Branch.initialize('br1')
 
198
        super(TestMergeFileHistory, self).setUp()
 
199
        wt1 = self.make_branch_and_tree('br1')
 
200
        br1 = wt1.branch
151
201
        self.build_tree_contents([('br1/file', 'original contents\n')])
152
 
        br1.working_tree().add(['file'], ['this-file-id'])
153
 
        br1.working_tree().commit(message='rev 1-1', rev_id='1-1')
154
 
        br1.clone('br2')
155
 
        br2 = Branch.open('br2')
 
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()
156
207
        self.build_tree_contents([('br1/file', 'original from 1\n')])
157
 
        br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
 
208
        wt1.commit(message='rev 1-2', rev_id='1-2')
158
209
        self.build_tree_contents([('br1/file', 'agreement\n')])
159
 
        br1.working_tree().commit(message='rev 1-3', rev_id='1-3')
 
210
        wt1.commit(message='rev 1-3', rev_id='1-3')
160
211
        self.build_tree_contents([('br2/file', 'contents in 2\n')])
161
 
        br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
 
212
        wt2.commit(message='rev 2-1', rev_id='2-1')
162
213
        self.build_tree_contents([('br2/file', 'agreement\n')])
163
 
        br2.working_tree().commit(message='rev 2-2', rev_id='2-2')
 
214
        wt2.commit(message='rev 2-2', rev_id='2-2')
164
215
 
165
216
    def test_merge_fetches_file_history(self):
166
217
        """Merge brings across file histories"""
176
227
                    rev_id).get_file_text('this-file-id'), text)
177
228
 
178
229
 
179
 
 
180
 
 
181
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 ?
182
234
 
183
235
    def test_fetch(self):
184
236
        #highest indices a: 5, b: 7
185
237
        br_a, br_b = make_branches(self)
186
 
        br_rem_a = Branch.open(self.get_remote_url(br_a.base))
 
238
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
187
239
        fetch_steps(self, br_rem_a, br_b, br_a)
188
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
 
189
252
    def test_weaves_are_retrieved_once(self):
190
253
        self.build_tree(("source/", "source/file", "target/"))
191
 
        branch = Branch.initialize("source")
192
 
        branch.working_tree().add(["file"], ["id"])
193
 
        branch.working_tree().commit("added file")
 
254
        wt = self.make_branch_and_tree('source')
 
255
        branch = wt.branch
 
256
        wt.add(["file"], ["id"])
 
257
        wt.commit("added file")
194
258
        print >>open("source/file", 'w'), "blah"
195
 
        branch.working_tree().commit("changed file")
196
 
        target = Branch.initialize("target/")
197
 
        source = Branch.open(self.get_remote_url("source/"))
198
 
        self.assertEqual(greedy_fetch(target, source), (2, []))
 
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, []))
199
263
        # this is the path to the literal file. As format changes 
200
264
        # occur it needs to be updated. FIXME: ask the store for the
201
265
        # path.
202
 
        weave_suffix = 'weaves/ce/id.weave HTTP/1.1" 200 -'
203
 
        self.assertEqual(1,
204
 
            len([log for log in self.server.logs if log.endswith(weave_suffix)]))
205
 
        inventory_weave_suffix = 'inventory.weave HTTP/1.1" 200 -'
206
 
        self.assertEqual(1,
207
 
            len([log for log in self.server.logs if log.endswith(
208
 
                inventory_weave_suffix)]))
 
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))
209
275
        # this r-h check test will prevent regressions, but it currently already 
210
276
        # passes, before the patch to cache-rh is applied :[
211
 
        revision_history_suffix = 'revision-history HTTP/1.1" 200 -'
212
 
        self.assertEqual(1,
213
 
            len([log for log in self.server.logs if log.endswith(
214
 
                revision_history_suffix)]))
 
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))
215
281
        # FIXME naughty poking in there.
216
 
        self.server.logs = []
 
282
        self.get_readonly_server().logs = []
217
283
        # check there is nothing more to fetch
218
 
        source = Branch.open(self.get_remote_url("source/"))
219
 
        self.assertEqual(greedy_fetch(target, source), (0, []))
220
 
        self.failUnless(self.server.logs[0].endswith('branch-format HTTP/1.1" 200 -'))
221
 
        self.failUnless(self.server.logs[1].endswith('revision-history HTTP/1.1" 200 -'))
222
 
        self.assertEqual(2, len(self.server.logs))
 
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))