~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Aaron Bentley
  • Date: 2006-11-10 01:55:55 UTC
  • mto: This revision was merged to the branch mainline in revision 2127.
  • Revision ID: aaron.bentley@utoronto.ca-20061110015555-f48202744b630209
Ignore html docs (both kinds)

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