~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Robert Collins
  • Date: 2005-12-08 05:57:00 UTC
  • Revision ID: robertc@robertcollins.net-20051208055700-5d85edd70fd15d4d
Test the uses of WorkingTree from branch.py

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