~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Martin Pool
  • Date: 2005-07-06 04:09:02 UTC
  • Revision ID: mbp@sourcefrog.net-20050706040902-5b98f739e5ee1841
- avoid copying string lists when handling unmatched regions

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 sys
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
24
 
import bzrlib.errors
25
 
from bzrlib.tests import TestCaseWithTransport
26
 
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
27
 
from bzrlib.tests.test_revision import make_branches
28
 
from bzrlib.trace import mutter
29
 
from bzrlib.upgrade import Convert
30
 
from bzrlib.workingtree import WorkingTree
31
 
 
32
 
 
33
 
def has_revision(branch, revision_id):
34
 
    return branch.repository.has_revision(revision_id)
35
 
 
36
 
def fetch_steps(self, br_a, br_b, writable_a):
37
 
    """A foreign test method for testing fetch locally and remotely."""
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]))
54
 
 
55
 
    # When a non-branch ancestor is missing, it should be unlisted...
56
 
    # as its not reference from the inventory weave.
57
 
    br_b4 = self.make_branch('br_4')
58
 
    count, failures = br_b4.fetch(br_b)
59
 
    self.assertEqual(count, 7)
60
 
    self.assertEqual(failures, [])
61
 
 
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]))
65
 
        
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)
82
 
    for revno in range(4):
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)
89
 
    # InstallFailed should be raised if the branch is missing the revision
90
 
    # that was requested.
91
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
92
 
    # InstallFailed should be raised if the branch is missing a revision
93
 
    # from its own revision history
94
 
    br_a2.append_revision('a-b-c')
95
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
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
101
 
 
102
 
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
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):
108
 
 
109
 
    def test_fetch(self):
110
 
        #highest indices a: 5, b: 7
111
 
        br_a, br_b = make_branches(self)
112
 
        fetch_steps(self, br_a, br_b, br_a)
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
 
 
225
 
 
226
 
class TestHttpFetch(TestCaseWithWebserver):
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 ?
230
 
 
231
 
    def test_fetch(self):
232
 
        #highest indices a: 5, b: 7
233
 
        br_a, br_b = make_branches(self)
234
 
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
235
 
        fetch_steps(self, br_rem_a, br_b, br_a)
236
 
 
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
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
 
        log_pattern = '%%s HTTP/1.1" 200 - "-" "bzr/%s' % bzrlib.__version__
261
 
        # this is the path to the literal file. As format changes 
262
 
        # occur it needs to be updated. FIXME: ask the store for the
263
 
        # path.
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))
273
 
        # this r-h check test will prevent regressions, but it currently already 
274
 
        # passes, before the patch to cache-rh is applied :[
275
 
        self.assertEqual(1, self._count_log_matches('revision-history', http_logs))
276
 
        # FIXME naughty poking in there.
277
 
        self.get_readonly_server().logs = []
278
 
        # check there is nothing more to fetch
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))