~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_fetch.py

  • Committer: Robert Collins
  • Date: 2006-02-26 07:54:02 UTC
  • mto: (1587.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 1588.
  • Revision ID: robertc@robertcollins.net-20060226075402-92fca9fdb7b0070d
Check for incorrect revision parentage in the weave during revision access.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2007 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
 
import bzrlib
22
 
from bzrlib import (
23
 
    bzrdir,
24
 
    errors,
25
 
    merge,
26
 
    repository,
27
 
    versionedfile,
28
 
    )
29
20
from bzrlib.branch import Branch
30
21
from bzrlib.bzrdir import BzrDir
31
 
from bzrlib.repofmt import knitrepo
 
22
from bzrlib.builtins import merge
 
23
import bzrlib.errors
32
24
from bzrlib.tests import TestCaseWithTransport
33
 
from bzrlib.tests.http_utils import TestCaseWithWebserver
 
25
from bzrlib.tests.HTTPTestUtil import TestCaseWithWebserver
34
26
from bzrlib.tests.test_revision import make_branches
35
27
from bzrlib.trace import mutter
36
 
from bzrlib.upgrade import Convert
37
28
from bzrlib.workingtree import WorkingTree
38
29
 
39
 
# These tests are a bit old; please instead add new tests into
40
 
# interrepository_implementations/ so they'll run on all relevant
41
 
# combinations.
42
 
 
43
30
 
44
31
def has_revision(branch, revision_id):
45
32
    return branch.repository.has_revision(revision_id)
99
86
    self.assertEquals(fetched, 3, "fetched %d instead of 3" % fetched)
100
87
    # InstallFailed should be raised if the branch is missing the revision
101
88
    # that was requested.
102
 
    self.assertRaises(errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
103
 
 
104
 
    # TODO: Test trying to fetch from a branch that points to a revision not
105
 
    # actually present in its repository.  Not every branch format allows you
106
 
    # to directly point to such revisions, so it's a bit complicated to
107
 
    # construct.  One way would be to uncommit and gc the revision, but not
108
 
    # every branch supports that.  -- mbp 20070814
109
 
 
 
89
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2, 'pizza')
 
90
    # InstallFailed should be raised if the branch is missing a revision
 
91
    # from its own revision history
 
92
    br_a2.append_revision('a-b-c')
 
93
    self.assertRaises(bzrlib.errors.InstallFailed, br_a3.fetch, br_a2)
110
94
    #TODO: test that fetch correctly does reweaving when needed. RBC 20051008
111
95
    # Note that this means - updating the weave when ghosts are filled in to 
112
96
    # add the right parents.
116
100
 
117
101
    def test_fetch(self):
118
102
        #highest indices a: 5, b: 7
119
 
        br_a, br_b = make_branches(self, format='dirstate-tags')
 
103
        br_a, br_b = make_branches(self)
120
104
        fetch_steps(self, br_a, br_b, br_a)
121
105
 
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
 
        repo.lock_read()
151
 
        try:
152
 
            # Make sure fetch retrieved only what we requested
153
 
            self.assertEqual({('tree-root', 'rev1'):()},
154
 
                repo.texts.get_parent_map(
155
 
                    [('tree-root', 'rev1'), ('tree-root', 'rev2')]))
156
 
        finally:
157
 
            repo.unlock()
158
 
        branch.pull(tree.branch)
159
 
        # Make sure that the next revision in the root knit was retrieved,
160
 
        # even though the text, name, parent_id, etc., were unchanged.
161
 
        repo.lock_read()
162
 
        try:
163
 
            # Make sure fetch retrieved only what we requested
164
 
            self.assertEqual({('tree-root', 'rev2'):(('tree-root', 'rev1'),)},
165
 
                repo.texts.get_parent_map([('tree-root', 'rev2')]))
166
 
        finally:
167
 
            repo.unlock()
168
 
 
169
 
    def test_fetch_incompatible(self):
170
 
        knit_tree = self.make_branch_and_tree('knit', format='knit')
171
 
        knit3_tree = self.make_branch_and_tree('knit3',
172
 
            format='dirstate-with-subtree')
173
 
        knit3_tree.commit('blah')
174
 
        e = self.assertRaises(errors.IncompatibleRepositories,
175
 
                              knit_tree.branch.fetch, knit3_tree.branch)
176
 
        self.assertContainsRe(str(e),
177
 
            r"(?m).*/knit.*\nis not compatible with\n.*/knit3/.*\n"
178
 
            r"different rich-root support")
179
 
 
180
106
 
181
107
class TestMergeFetch(TestCaseWithTransport):
182
108
 
189
115
        wt2 = self.make_branch_and_tree('br2')
190
116
        br2 = wt2.branch
191
117
        wt2.commit(message='rev 2-1', rev_id='2-1')
192
 
        wt2.merge_from_branch(br1, from_revision='null:')
 
118
        merge(other_revision=['br1', -1], base_revision=['br1', 0],
 
119
              this_dir='br2')
193
120
        self._check_revs_present(br2)
194
121
 
195
122
    def test_merge_fetches(self):
200
127
        dir_2 = br1.bzrdir.sprout('br2')
201
128
        br2 = dir_2.open_branch()
202
129
        wt1.commit(message='rev 1-2', rev_id='1-2')
203
 
        wt2 = dir_2.open_workingtree()
204
 
        wt2.commit(message='rev 2-1', rev_id='2-1')
205
 
        wt2.merge_from_branch(br1)
 
130
        dir_2.open_workingtree().commit(message='rev 2-1', rev_id='2-1')
 
131
        merge(other_revision=['br1', -1], base_revision=[None, None], 
 
132
              this_dir='br2')
206
133
        self._check_revs_present(br2)
207
134
 
208
135
    def _check_revs_present(self, br2):
237
164
    def test_merge_fetches_file_history(self):
238
165
        """Merge brings across file histories"""
239
166
        br2 = Branch.open('br2')
240
 
        br1 = Branch.open('br1')
241
 
        wt2 = WorkingTree.open('br2').merge_from_branch(br1)
242
 
        br2.lock_read()
243
 
        self.addCleanup(br2.unlock)
 
167
        merge(other_revision=['br1', -1], base_revision=[None, None], 
 
168
              this_dir='br2')
244
169
        for rev_id, text in [('1-2', 'original from 1\n'),
245
170
                             ('1-3', 'agreement\n'),
246
171
                             ('2-1', 'contents in 2\n'),
261
186
        br_rem_a = Branch.open(self.get_readonly_url('branch1'))
262
187
        fetch_steps(self, br_rem_a, br_b, br_a)
263
188
 
264
 
    def _count_log_matches(self, target, logs):
265
 
        """Count the number of times the target file pattern was fetched in an http log"""
266
 
        get_succeeds_re = re.compile(
267
 
            '.*"GET .*%s HTTP/1.1" 20[06] - "-" "bzr/%s' %
268
 
            (     target,                    bzrlib.__version__))
269
 
        c = 0
270
 
        for line in logs:
271
 
            if get_succeeds_re.match(line):
272
 
                c += 1
273
 
        return c
274
 
 
275
189
    def test_weaves_are_retrieved_once(self):
276
190
        self.build_tree(("source/", "source/file", "target/"))
277
 
        # This test depends on knit dasta storage.
278
 
        wt = self.make_branch_and_tree('source', format='dirstate-tags')
 
191
        wt = self.make_branch_and_tree('source')
279
192
        branch = wt.branch
280
193
        wt.add(["file"], ["id"])
281
194
        wt.commit("added file")
282
 
        open("source/file", 'w').write("blah\n")
 
195
        print >>open("source/file", 'w'), "blah"
283
196
        wt.commit("changed file")
284
197
        target = BzrDir.create_branch_and_repo("target/")
285
198
        source = Branch.open(self.get_readonly_url("source/"))
286
199
        self.assertEqual(target.fetch(source), (2, []))
 
200
        log_pattern = '%%s HTTP/1.1" 200 - "-" "bzr/%s"' % bzrlib.__version__
287
201
        # this is the path to the literal file. As format changes 
288
202
        # occur it needs to be updated. FIXME: ask the store for the
289
203
        # path.
290
 
        self.log("web server logs are:")
291
 
        http_logs = self.get_readonly_server().logs
292
 
        self.log('\n'.join(http_logs))
293
 
        # unfortunately this log entry is branch format specific. We could 
294
 
        # factor out the 'what files does this format use' to a method on the 
295
 
        # repository, which would let us to this generically. RBC 20060419
296
 
        # RBC 20080408: Or perhaps we can assert that no files are fully read
297
 
        # twice?
298
 
        self.assertEqual(1, self._count_log_matches('/ce/id.kndx', http_logs))
299
 
        self.assertEqual(1, self._count_log_matches('/ce/id.knit', http_logs))
300
 
        self.assertEqual(1, self._count_log_matches('inventory.kndx', http_logs))
 
204
        weave_suffix = log_pattern % 'weaves/ce/id.weave'
 
205
        self.assertEqual(1,
 
206
            len([log for log in self.get_readonly_server().logs if log.endswith(weave_suffix)]))
 
207
        inventory_weave_suffix = log_pattern % 'inventory.weave'
 
208
        self.assertEqual(1,
 
209
            len([log for log in self.get_readonly_server().logs if log.endswith(
 
210
                inventory_weave_suffix)]))
301
211
        # this r-h check test will prevent regressions, but it currently already 
302
212
        # passes, before the patch to cache-rh is applied :[
303
 
        self.assertTrue(1 >= self._count_log_matches('revision-history',
304
 
                                                     http_logs))
305
 
        self.assertTrue(1 >= self._count_log_matches('last-revision',
306
 
                                                     http_logs))
 
213
        revision_history_suffix = log_pattern % 'revision-history'
 
214
        self.assertEqual(1,
 
215
            len([log for log in self.get_readonly_server().logs if log.endswith(
 
216
                revision_history_suffix)]))
307
217
        # FIXME naughty poking in there.
308
218
        self.get_readonly_server().logs = []
309
 
        # check there is nothing more to fetch.  We take care to re-use the
310
 
        # existing transport so that the request logs we're about to examine
311
 
        # aren't cluttered with redundant probes for a smart server.
312
 
        # XXX: Perhaps this further parameterisation: test http with smart
313
 
        # server, and test http without smart server?
314
 
        source = Branch.open(
315
 
            self.get_readonly_url("source/"),
316
 
            possible_transports=[source.bzrdir.root_transport])
 
219
        # check there is nothing more to fetch
 
220
        source = Branch.open(self.get_readonly_url("source/"))
317
221
        self.assertEqual(target.fetch(source), (0, []))
318
 
        # should make just two requests
319
 
        http_logs = self.get_readonly_server().logs
320
 
        self.log("web server logs are:")
321
 
        self.log('\n'.join(http_logs))
322
 
        self.assertEqual(1, self._count_log_matches('branch-format', http_logs))
323
 
        self.assertEqual(1, self._count_log_matches('branch/format', http_logs))
324
 
        self.assertEqual(1, self._count_log_matches('repository/format',
325
 
            http_logs))
326
 
        self.assertTrue(1 >= self._count_log_matches('revision-history',
327
 
                                                     http_logs))
328
 
        self.assertTrue(1 >= self._count_log_matches('last-revision',
329
 
                                                     http_logs))
330
 
        self.assertEqual(4, len(http_logs))
331
 
 
332
 
 
333
 
class TestKnitToPackFetch(TestCaseWithTransport):
334
 
 
335
 
    def find_get_record_stream(self, calls):
336
 
        """In a list of calls, find 'get_record_stream' calls.
337
 
 
338
 
        This also ensures that there is only one get_record_stream call.
339
 
        """
340
 
        get_record_call = None
341
 
        for call in calls:
342
 
            if call[0] == 'get_record_stream':
343
 
                self.assertIs(None, get_record_call,
344
 
                              "there should only be one call to"
345
 
                              " get_record_stream")
346
 
                get_record_call = call
347
 
        self.assertIsNot(None, get_record_call,
348
 
                         "there should be exactly one call to "
349
 
                         " get_record_stream")
350
 
        return get_record_call
351
 
 
352
 
    def test_fetch_with_deltas_no_delta_closure(self):
353
 
        tree = self.make_branch_and_tree('source', format='dirstate')
354
 
        target = self.make_repository('target', format='pack-0.92')
355
 
        self.build_tree(['source/file'])
356
 
        tree.set_root_id('root-id')
357
 
        tree.add('file', 'file-id')
358
 
        tree.commit('one', rev_id='rev-one')
359
 
        source = tree.branch.repository
360
 
        source.texts = versionedfile.RecordingVersionedFilesDecorator(
361
 
                        source.texts)
362
 
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
363
 
                        source.signatures)
364
 
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
365
 
                        source.revisions)
366
 
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
367
 
                        source.inventories)
368
 
        # precondition
369
 
        self.assertTrue(target._fetch_uses_deltas)
370
 
        target.fetch(source, revision_id='rev-one')
371
 
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
372
 
                          target._fetch_order, False),
373
 
                         self.find_get_record_stream(source.texts.calls))
374
 
        self.assertEqual(('get_record_stream', [('rev-one',)],
375
 
                          target._fetch_order, False),
376
 
                         self.find_get_record_stream(source.inventories.calls))
377
 
        self.assertEqual(('get_record_stream', [('rev-one',)],
378
 
                          target._fetch_order, False),
379
 
                         self.find_get_record_stream(source.revisions.calls))
380
 
        # XXX: Signatures is special, and slightly broken. The
381
 
        # standard item_keys_introduced_by actually does a lookup for every
382
 
        # signature to see if it exists, rather than waiting to do them all at
383
 
        # once at the end. The fetch code then does an all-at-once and just
384
 
        # allows for some of them to be missing.
385
 
        # So we know there will be extra calls, but the *last* one is the one
386
 
        # we care about.
387
 
        signature_calls = source.signatures.calls[-1:]
388
 
        self.assertEqual(('get_record_stream', [('rev-one',)],
389
 
                          target._fetch_order, False),
390
 
                         self.find_get_record_stream(signature_calls))
391
 
 
392
 
    def test_fetch_no_deltas_with_delta_closure(self):
393
 
        tree = self.make_branch_and_tree('source', format='dirstate')
394
 
        target = self.make_repository('target', format='pack-0.92')
395
 
        self.build_tree(['source/file'])
396
 
        tree.set_root_id('root-id')
397
 
        tree.add('file', 'file-id')
398
 
        tree.commit('one', rev_id='rev-one')
399
 
        source = tree.branch.repository
400
 
        source.texts = versionedfile.RecordingVersionedFilesDecorator(
401
 
                        source.texts)
402
 
        source.signatures = versionedfile.RecordingVersionedFilesDecorator(
403
 
                        source.signatures)
404
 
        source.revisions = versionedfile.RecordingVersionedFilesDecorator(
405
 
                        source.revisions)
406
 
        source.inventories = versionedfile.RecordingVersionedFilesDecorator(
407
 
                        source.inventories)
408
 
        target._fetch_uses_deltas = False
409
 
        target.fetch(source, revision_id='rev-one')
410
 
        self.assertEqual(('get_record_stream', [('file-id', 'rev-one')],
411
 
                          target._fetch_order, True),
412
 
                         self.find_get_record_stream(source.texts.calls))
413
 
        self.assertEqual(('get_record_stream', [('rev-one',)],
414
 
                          target._fetch_order, True),
415
 
                         self.find_get_record_stream(source.inventories.calls))
416
 
        self.assertEqual(('get_record_stream', [('rev-one',)],
417
 
                          target._fetch_order, True),
418
 
                         self.find_get_record_stream(source.revisions.calls))
419
 
        # XXX: Signatures is special, and slightly broken. The
420
 
        # standard item_keys_introduced_by actually does a lookup for every
421
 
        # signature to see if it exists, rather than waiting to do them all at
422
 
        # once at the end. The fetch code then does an all-at-once and just
423
 
        # allows for some of them to be missing.
424
 
        # So we know there will be extra calls, but the *last* one is the one
425
 
        # we care about.
426
 
        signature_calls = source.signatures.calls[-1:]
427
 
        self.assertEqual(('get_record_stream', [('rev-one',)],
428
 
                          target._fetch_order, True),
429
 
                         self.find_get_record_stream(signature_calls))
430
 
 
431
 
    def test_fetch_revisions_with_deltas_into_pack(self):
432
 
        # See BUG #261339, dev versions of bzr could accidentally create deltas
433
 
        # in revision texts in knit branches (when fetching from packs). So we
434
 
        # ensure that *if* a knit repository has a delta in revisions, that it
435
 
        # gets properly expanded back into a fulltext when stored in the pack
436
 
        # file.
437
 
        tree = self.make_branch_and_tree('source', format='dirstate')
438
 
        target = self.make_repository('target', format='pack-0.92')
439
 
        self.build_tree(['source/file'])
440
 
        tree.set_root_id('root-id')
441
 
        tree.add('file', 'file-id')
442
 
        tree.commit('one', rev_id='rev-one')
443
 
        # Hack the KVF for revisions so that it "accidentally" allows a delta
444
 
        tree.branch.repository.revisions._max_delta_chain = 200
445
 
        tree.commit('two', rev_id='rev-two')
446
 
        source = tree.branch.repository
447
 
        # Ensure that we stored a delta
448
 
        source.lock_read()
449
 
        self.addCleanup(source.unlock)
450
 
        record = source.revisions.get_record_stream([('rev-two',)],
451
 
            'unordered', False).next()
452
 
        self.assertEqual('knit-delta-gz', record.storage_kind)
453
 
        target.fetch(tree.branch.repository, revision_id='rev-two')
454
 
        # The record should get expanded back to a fulltext
455
 
        target.lock_read()
456
 
        self.addCleanup(target.unlock)
457
 
        record = target.revisions.get_record_stream([('rev-two',)],
458
 
            'unordered', False).next()
459
 
        self.assertEqual('knit-ft-gz', record.storage_kind)
460
 
 
461
 
 
462
 
 
463
 
class Test1To2Fetch(TestCaseWithTransport):
464
 
    """Tests for Model1To2 failure modes"""
465
 
 
466
 
    def make_tree_and_repo(self):
467
 
        self.tree = self.make_branch_and_tree('tree', format='pack-0.92')
468
 
        self.repo = self.make_repository('rich-repo', format='rich-root-pack')
469
 
        self.repo.lock_write()
470
 
        self.addCleanup(self.repo.unlock)
471
 
 
472
 
    def do_fetch_order_test(self, first, second):
473
 
        """Test that fetch works no matter what the set order of revision is.
474
 
 
475
 
        This test depends on the order of items in a set, which is
476
 
        implementation-dependant, so we test A, B and then B, A.
477
 
        """
478
 
        self.make_tree_and_repo()
479
 
        self.tree.commit('Commit 1', rev_id=first)
480
 
        self.tree.commit('Commit 2', rev_id=second)
481
 
        self.repo.fetch(self.tree.branch.repository, second)
482
 
 
483
 
    def test_fetch_order_AB(self):
484
 
        """See do_fetch_order_test"""
485
 
        self.do_fetch_order_test('A', 'B')
486
 
 
487
 
    def test_fetch_order_BA(self):
488
 
        """See do_fetch_order_test"""
489
 
        self.do_fetch_order_test('B', 'A')
490
 
 
491
 
    def get_parents(self, file_id, revision_id):
492
 
        self.repo.lock_read()
493
 
        try:
494
 
            parent_map = self.repo.texts.get_parent_map([(file_id, revision_id)])
495
 
            return parent_map[(file_id, revision_id)]
496
 
        finally:
497
 
            self.repo.unlock()
498
 
 
499
 
    def test_fetch_ghosts(self):
500
 
        self.make_tree_and_repo()
501
 
        self.tree.commit('first commit', rev_id='left-parent')
502
 
        self.tree.add_parent_tree_id('ghost-parent')
503
 
        fork = self.tree.bzrdir.sprout('fork', 'null:').open_workingtree()
504
 
        fork.commit('not a ghost', rev_id='not-ghost-parent')
505
 
        self.tree.branch.repository.fetch(fork.branch.repository,
506
 
                                     'not-ghost-parent')
507
 
        self.tree.add_parent_tree_id('not-ghost-parent')
508
 
        self.tree.commit('second commit', rev_id='second-id')
509
 
        self.repo.fetch(self.tree.branch.repository, 'second-id')
510
 
        root_id = self.tree.get_root_id()
511
 
        self.assertEqual(
512
 
            ((root_id, 'left-parent'), (root_id, 'ghost-parent'),
513
 
             (root_id, 'not-ghost-parent')),
514
 
            self.get_parents(root_id, 'second-id'))
515
 
 
516
 
    def make_two_commits(self, change_root, fetch_twice):
517
 
        self.make_tree_and_repo()
518
 
        self.tree.commit('first commit', rev_id='first-id')
519
 
        if change_root:
520
 
            self.tree.set_root_id('unique-id')
521
 
        self.tree.commit('second commit', rev_id='second-id')
522
 
        if fetch_twice:
523
 
            self.repo.fetch(self.tree.branch.repository, 'first-id')
524
 
        self.repo.fetch(self.tree.branch.repository, 'second-id')
525
 
 
526
 
    def test_fetch_changed_root(self):
527
 
        self.make_two_commits(change_root=True, fetch_twice=False)
528
 
        self.assertEqual((), self.get_parents('unique-id', 'second-id'))
529
 
 
530
 
    def test_two_fetch_changed_root(self):
531
 
        self.make_two_commits(change_root=True, fetch_twice=True)
532
 
        self.assertEqual((), self.get_parents('unique-id', 'second-id'))
533
 
 
534
 
    def test_two_fetches(self):
535
 
        self.make_two_commits(change_root=False, fetch_twice=True)
536
 
        self.assertEqual((('TREE_ROOT', 'first-id'),),
537
 
            self.get_parents('TREE_ROOT', 'second-id'))
 
222
        self.failUnless(self.get_readonly_server().logs[0].endswith(log_pattern % 'branch-format'))
 
223
        self.failUnless(self.get_readonly_server().logs[1].endswith(log_pattern % 'revision-history'))
 
224
        self.assertEqual(2, len(self.get_readonly_server().logs))