~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-24 02:20:45 UTC
  • mto: (1185.50.57 bzr-jam-integration)
  • mto: This revision was merged to the branch mainline in revision 1550.
  • Revision ID: robertc@robertcollins.net-20051224022045-14efc8dfa0e1a4e9
Start tests for api usage.

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
21
from bzrlib.tests.test_revision 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
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
37
30
 
38
31
 
39
32
def has_revision(branch, revision_id):
40
 
    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
41
38
 
42
39
def fetch_steps(self, br_a, br_b, writable_a):
43
40
    """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]))
 
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]))
58
57
    self.assertFalse(has_revision(br_a, br_b.revision_history()[6]))
59
 
    self.assertTrue(br_a.repository.has_revision(br_b.revision_history()[5]))
 
58
    self.assert_(has_revision(br_a, br_b.revision_history()[5]))
60
59
 
61
60
    # When a non-branch ancestor is missing, it should be unlisted...
62
61
    # as its not reference from the inventory weave.
63
 
    br_b4 = self.make_branch('br_4')
64
 
    count, failures = br_b4.fetch(br_b)
 
62
    br_b4 = new_branch('br_4')
 
63
    count, failures = greedy_fetch(br_b4, br_b)
65
64
    self.assertEqual(count, 7)
66
65
    self.assertEqual(failures, [])
67
66
 
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]))
 
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]))
71
70
        
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]))
 
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]))
76
75
    self.assertFalse(has_revision(br_b2, br_a.revision_history()[3]))
77
76
 
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]))
 
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]))
83
82
 
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)
 
83
    br_a3 = new_branch('br_a3')
 
84
    self.assertEquals(greedy_fetch(br_a3, br_a2)[0], 0)
88
85
    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]
 
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]
94
89
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
95
90
    # InstallFailed should be raised if the branch is missing the revision
96
91
    # that was requested.
97
 
    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')
98
94
    # InstallFailed should be raised if the branch is missing a revision
99
95
    # from its own revision history
100
96
    br_a2.append_revision('a-b-c')
101
 
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
 
97
    self.assertRaises(bzrlib.errors.InstallFailed, greedy_fetch, br_a3,
 
98
                      br_a2)
102
99
 
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
100
 
110
101
    #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):
 
102
 
 
103
class TestFetch(TestCaseInTempDir):
116
104
 
117
105
    def test_fetch(self):
118
106
        #highest indices a: 5, b: 7
119
107
        br_a, br_b = make_branches(self)
120
108
        fetch_steps(self, br_a, br_b, br_a)
121
109
 
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):
 
110
 
 
111
class TestMergeFetch(TestCaseInTempDir):
172
112
 
173
113
    def test_merge_fetches_unrelated(self):
174
114
        """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')
 
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')
182
122
        merge(other_revision=['br1', -1], base_revision=['br1', 0],
183
123
              this_dir='br2')
184
124
        self._check_revs_present(br2)
185
125
 
186
126
    def test_merge_fetches(self):
187
127
        """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')
 
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')
195
135
        merge(other_revision=['br1', -1], base_revision=[None, None], 
196
136
              this_dir='br2')
197
137
        self._check_revs_present(br2)
198
138
 
199
139
    def _check_revs_present(self, br2):
200
140
        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)
 
141
            self.assertTrue(br2.has_revision(rev_id))
 
142
            rev = br2.get_revision(rev_id)
203
143
            self.assertEqual(rev.revision_id, rev_id)
204
 
            self.assertTrue(br2.repository.get_inventory(rev_id))
205
 
 
206
 
 
207
 
class TestMergeFileHistory(TestCaseWithTransport):
208
 
 
 
144
            self.assertTrue(br2.get_inventory(rev_id))
 
145
 
 
146
 
 
147
 
 
148
class TestMergeFileHistory(TestCaseInTempDir):
209
149
    def setUp(self):
210
 
        super(TestMergeFileHistory, self).setUp()
211
 
        wt1 = self.make_branch_and_tree('br1')
212
 
        br1 = wt1.branch
 
150
        TestCaseInTempDir.setUp(self)
 
151
        os.mkdir('br1')
 
152
        br1 = Branch.initialize('br1')
213
153
        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()
 
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')
219
158
        self.build_tree_contents([('br1/file', 'original from 1\n')])
220
 
        wt1.commit(message='rev 1-2', rev_id='1-2')
 
159
        br1.working_tree().commit(message='rev 1-2', rev_id='1-2')
221
160
        self.build_tree_contents([('br1/file', 'agreement\n')])
222
 
        wt1.commit(message='rev 1-3', rev_id='1-3')
 
161
        br1.working_tree().commit(message='rev 1-3', rev_id='1-3')
223
162
        self.build_tree_contents([('br2/file', 'contents in 2\n')])
224
 
        wt2.commit(message='rev 2-1', rev_id='2-1')
 
163
        br2.working_tree().commit(message='rev 2-1', rev_id='2-1')
225
164
        self.build_tree_contents([('br2/file', 'agreement\n')])
226
 
        wt2.commit(message='rev 2-2', rev_id='2-2')
 
165
        br2.working_tree().commit(message='rev 2-2', rev_id='2-2')
227
166
 
228
167
    def test_merge_fetches_file_history(self):
229
168
        """Merge brings across file histories"""
234
173
                             ('1-3', 'agreement\n'),
235
174
                             ('2-1', 'contents in 2\n'),
236
175
                             ('2-2', 'agreement\n')]:
237
 
            self.assertEqualDiff(
238
 
                br2.repository.revision_tree(
239
 
                    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
 
240
180
 
241
181
 
242
182
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 ?
 
183
 
 
184
    def setUp(self):
 
185
        super(TestHttpFetch, self).setUp()
 
186
        self.weblogs = []
246
187
 
247
188
    def test_fetch(self):
248
189
        #highest indices a: 5, b: 7
249
190
        br_a, br_b = make_branches(self)
250
 
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
 
191
        br_rem_a = Branch.open(self.get_remote_url(br_a._transport.base))
251
192
        fetch_steps(self, br_rem_a, br_b, br_a)
252
193
 
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
 
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])
263
200
 
264
201
    def test_weaves_are_retrieved_once(self):
265
202
        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")
 
203
        branch = Branch.initialize("source")
 
204
        branch.working_tree().add(["file"], ["id"])
 
205
        branch.working_tree().commit("added file")
270
206
        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, []))
 
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, []))
275
211
        # this is the path to the literal file. As format changes 
276
212
        # occur it needs to be updated. FIXME: ask the store for the
277
213
        # 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))
 
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)]))
287
221
        # this r-h check test will prevent regressions, but it currently already 
288
222
        # 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 = []
 
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 = []
295
228
        # 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))
 
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))