/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_smart.py

  • Committer: Andrew Bennetts
  • Date: 2008-09-08 12:59:00 UTC
  • mfrom: (3695 +trunk)
  • mto: This revision was merged to the branch mainline in revision 3756.
  • Revision ID: andrew.bennetts@canonical.com-20080908125900-8ywtsr7jqyyatjz0
Merge from bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
    tests,
37
37
    urlutils,
38
38
    )
39
 
from bzrlib.branch import BranchReferenceFormat
 
39
from bzrlib.branch import Branch, BranchReferenceFormat
40
40
import bzrlib.smart.branch
41
41
import bzrlib.smart.bzrdir
42
42
import bzrlib.smart.repository
104
104
        # the default or a parameterized class, but rather use the
105
105
        # TestCaseWithTransport infrastructure to set up a smart server and
106
106
        # transport.
107
 
        self.transport_server = smart.server.SmartTCPServer_for_testing
 
107
        self.transport_server = self.make_transport_server
 
108
 
 
109
    def make_transport_server(self):
 
110
        return smart.server.SmartTCPServer_for_testing('-' + self.id())
108
111
 
109
112
    def get_smart_medium(self):
110
113
        """Get a smart medium to use in tests."""
389
392
        backing = self.get_transport()
390
393
        request = smart.branch.SmartServerBranchGetConfigFile(backing)
391
394
        branch = self.make_branch('.')
392
 
        branch.control_files.put_utf8('branch.conf', 'foo bar baz')
 
395
        branch._transport.put_bytes('branch.conf', 'foo bar baz')
393
396
        self.assertEqual(SmartServerResponse(('ok', ), 'foo bar baz'),
394
397
            request.execute(''))
395
398
 
396
399
 
397
 
class TestSmartServerBranchRequestSetLastRevision(tests.TestCaseWithMemoryTransport):
398
 
 
399
 
    def test_empty(self):
400
 
        backing = self.get_transport()
401
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
402
 
        b = self.make_branch('.')
403
 
        branch_token = b.lock_write()
404
 
        repo_token = b.repository.lock_write()
405
 
        b.repository.unlock()
406
 
        try:
407
 
            self.assertEqual(SmartServerResponse(('ok',)),
408
 
                request.execute(
409
 
                    '', branch_token, repo_token,
410
 
                    'null:'))
411
 
        finally:
412
 
            b.unlock()
413
 
 
414
 
    def test_not_present_revision_id(self):
415
 
        backing = self.get_transport()
416
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
417
 
        b = self.make_branch('.')
418
 
        branch_token = b.lock_write()
419
 
        repo_token = b.repository.lock_write()
420
 
        b.repository.unlock()
421
 
        try:
422
 
            revision_id = 'non-existent revision'
423
 
            self.assertEqual(
424
 
                SmartServerResponse(('NoSuchRevision', revision_id)),
425
 
                request.execute(
426
 
                    '', branch_token, repo_token,
427
 
                    revision_id))
428
 
        finally:
429
 
            b.unlock()
430
 
 
431
 
    def test_revision_id_present(self):
432
 
        backing = self.get_transport()
433
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
434
 
        tree = self.make_branch_and_memory_tree('.')
435
 
        tree.lock_write()
436
 
        tree.add('')
437
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
438
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
439
 
        r2 = tree.commit('2nd commit')
440
 
        tree.unlock()
441
 
        branch_token = tree.branch.lock_write()
442
 
        repo_token = tree.branch.repository.lock_write()
443
 
        tree.branch.repository.unlock()
444
 
        try:
445
 
            self.assertEqual(
446
 
                SmartServerResponse(('ok',)),
447
 
                request.execute(
448
 
                    '', branch_token, repo_token,
449
 
                    rev_id_utf8))
450
 
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
451
 
        finally:
452
 
            tree.branch.unlock()
453
 
 
454
 
    def test_revision_id_present2(self):
455
 
        backing = self.get_transport()
456
 
        request = smart.branch.SmartServerBranchRequestSetLastRevision(backing)
457
 
        tree = self.make_branch_and_memory_tree('.')
458
 
        tree.lock_write()
459
 
        tree.add('')
460
 
        rev_id_utf8 = u'\xc8'.encode('utf-8')
461
 
        r1 = tree.commit('1st commit', rev_id=rev_id_utf8)
462
 
        r2 = tree.commit('2nd commit')
463
 
        tree.unlock()
464
 
        tree.branch.set_revision_history([])
465
 
        branch_token = tree.branch.lock_write()
466
 
        repo_token = tree.branch.repository.lock_write()
467
 
        tree.branch.repository.unlock()
468
 
        try:
469
 
            self.assertEqual(
470
 
                SmartServerResponse(('ok',)),
471
 
                request.execute(
472
 
                    '', branch_token, repo_token,
473
 
                    rev_id_utf8))
474
 
            self.assertEqual([rev_id_utf8], tree.branch.revision_history())
475
 
        finally:
476
 
            tree.branch.unlock()
 
400
class SetLastRevisionTestBase(tests.TestCaseWithMemoryTransport):
 
401
    """Base test case for verbs that implement set_last_revision."""
 
402
 
 
403
    def setUp(self):
 
404
        tests.TestCaseWithMemoryTransport.setUp(self)
 
405
        backing_transport = self.get_transport()
 
406
        self.request = self.request_class(backing_transport)
 
407
        self.tree = self.make_branch_and_memory_tree('.')
 
408
 
 
409
    def lock_branch(self):
 
410
        b = self.tree.branch
 
411
        branch_token = b.lock_write()
 
412
        repo_token = b.repository.lock_write()
 
413
        b.repository.unlock()
 
414
        return branch_token, repo_token
 
415
 
 
416
    def unlock_branch(self):
 
417
        self.tree.branch.unlock()
 
418
        
 
419
    def set_last_revision(self, revision_id, revno):
 
420
        branch_token, repo_token = self.lock_branch()
 
421
        response = self._set_last_revision(
 
422
            revision_id, revno, branch_token, repo_token)
 
423
        self.unlock_branch()
 
424
        return response
 
425
 
 
426
    def assertRequestSucceeds(self, revision_id, revno):
 
427
        response = self.set_last_revision(revision_id, revno)
 
428
        self.assertEqual(SuccessfulSmartServerResponse(('ok',)), response)
 
429
 
 
430
        
 
431
class TestSetLastRevisionVerbMixin(object):
 
432
    """Mixin test case for verbs that implement set_last_revision."""
 
433
 
 
434
    def test_set_null_to_null(self):
 
435
        """An empty branch can have its last revision set to 'null:'."""
 
436
        self.assertRequestSucceeds('null:', 0)
 
437
 
 
438
    def test_NoSuchRevision(self):
 
439
        """If the revision_id is not present, the verb returns NoSuchRevision.
 
440
        """
 
441
        revision_id = 'non-existent revision'
 
442
        self.assertEqual(
 
443
            FailedSmartServerResponse(('NoSuchRevision', revision_id)),
 
444
            self.set_last_revision(revision_id, 1))
 
445
 
 
446
    def make_tree_with_two_commits(self):
 
447
        self.tree.lock_write()
 
448
        self.tree.add('')
 
449
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
450
        r1 = self.tree.commit('1st commit', rev_id=rev_id_utf8)
 
451
        r2 = self.tree.commit('2nd commit', rev_id='rev-2')
 
452
        self.tree.unlock()
 
453
 
 
454
    def test_branch_last_revision_info_is_updated(self):
 
455
        """A branch's tip can be set to a revision that is present in its
 
456
        repository.
 
457
        """
 
458
        # Make a branch with an empty revision history, but two revisions in
 
459
        # its repository.
 
460
        self.make_tree_with_two_commits()
 
461
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
462
        self.tree.branch.set_revision_history([])
 
463
        self.assertEqual(
 
464
            (0, 'null:'), self.tree.branch.last_revision_info())
 
465
        # We can update the branch to a revision that is present in the
 
466
        # repository.
 
467
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
468
        self.assertEqual(
 
469
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
470
 
 
471
    def test_branch_last_revision_info_rewind(self):
 
472
        """A branch's tip can be set to a revision that is an ancestor of the
 
473
        current tip.
 
474
        """
 
475
        self.make_tree_with_two_commits()
 
476
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
477
        self.assertEqual(
 
478
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
479
        self.assertRequestSucceeds(rev_id_utf8, 1)
 
480
        self.assertEqual(
 
481
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
482
 
 
483
    def test_TipChangeRejected(self):
 
484
        """If a pre_change_branch_tip hook raises TipChangeRejected, the verb
 
485
        returns TipChangeRejected.
 
486
        """
 
487
        rejection_message = u'rejection message\N{INTERROBANG}'
 
488
        def hook_that_rejects(params):
 
489
            raise errors.TipChangeRejected(rejection_message)
 
490
        Branch.hooks.install_named_hook(
 
491
            'pre_change_branch_tip', hook_that_rejects, None)
 
492
        self.assertEqual(
 
493
            FailedSmartServerResponse(
 
494
                ('TipChangeRejected', rejection_message.encode('utf-8'))),
 
495
            self.set_last_revision('null:', 0))
 
496
 
 
497
 
 
498
class TestSmartServerBranchRequestSetLastRevision(
 
499
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
500
    """Tests for Branch.set_last_revision verb."""
 
501
 
 
502
    request_class = smart.branch.SmartServerBranchRequestSetLastRevision
 
503
 
 
504
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
505
        return self.request.execute(
 
506
            '', branch_token, repo_token, revision_id)
 
507
 
 
508
 
 
509
class TestSmartServerBranchRequestSetLastRevisionInfo(
 
510
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
511
    """Tests for Branch.set_last_revision_info verb."""
 
512
 
 
513
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionInfo
 
514
 
 
515
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
516
        return self.request.execute(
 
517
            '', branch_token, repo_token, revno, revision_id)
 
518
 
 
519
    def test_NoSuchRevision(self):
 
520
        """Branch.set_last_revision_info does not have to return
 
521
        NoSuchRevision if the revision_id is absent.
 
522
        """
 
523
        raise tests.TestNotApplicable()
 
524
 
 
525
 
 
526
class TestSmartServerBranchRequestSetLastRevisionEx(
 
527
        SetLastRevisionTestBase, TestSetLastRevisionVerbMixin):
 
528
    """Tests for Branch.set_last_revision_ex verb."""
 
529
 
 
530
    request_class = smart.branch.SmartServerBranchRequestSetLastRevisionEx
 
531
 
 
532
    def _set_last_revision(self, revision_id, revno, branch_token, repo_token):
 
533
        return self.request.execute(
 
534
            '', branch_token, repo_token, revision_id, 0, 0)
 
535
 
 
536
    def assertRequestSucceeds(self, revision_id, revno):
 
537
        response = self.set_last_revision(revision_id, revno)
 
538
        self.assertEqual(
 
539
            SuccessfulSmartServerResponse(('ok', revno, revision_id)),
 
540
            response)
 
541
        
 
542
    def test_branch_last_revision_info_rewind(self):
 
543
        """A branch's tip can be set to a revision that is an ancestor of the
 
544
        current tip, but only if allow_overwrite_descendant is passed.
 
545
        """
 
546
        self.make_tree_with_two_commits()
 
547
        rev_id_utf8 = u'\xc8'.encode('utf-8')
 
548
        self.assertEqual(
 
549
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
550
        # If allow_overwrite_descendant flag is 0, then trying to set the tip
 
551
        # to an older revision ID has no effect.
 
552
        branch_token, repo_token = self.lock_branch()
 
553
        response = self.request.execute(
 
554
            '', branch_token, repo_token, rev_id_utf8, 0, 0)
 
555
        self.assertEqual(
 
556
            SuccessfulSmartServerResponse(('ok', 2, 'rev-2')),
 
557
            response)
 
558
        self.assertEqual(
 
559
            (2, 'rev-2'), self.tree.branch.last_revision_info())
 
560
 
 
561
        # If allow_overwrite_descendant flag is 1, then setting the tip to an
 
562
        # ancestor works.
 
563
        response = self.request.execute(
 
564
            '', branch_token, repo_token, rev_id_utf8, 0, 1)
 
565
        self.assertEqual(
 
566
            SuccessfulSmartServerResponse(('ok', 1, rev_id_utf8)),
 
567
            response)
 
568
        self.unlock_branch()
 
569
        self.assertEqual(
 
570
            (1, rev_id_utf8), self.tree.branch.last_revision_info())
 
571
 
 
572
    def make_branch_with_divergent_history(self):
 
573
        """Make a branch with divergent history in its repo.
 
574
 
 
575
        The branch's tip will be 'child-2', and the repo will also contain
 
576
        'child-1', which diverges from a common base revision.
 
577
        """
 
578
        self.tree.lock_write()
 
579
        self.tree.add('')
 
580
        r1 = self.tree.commit('1st commit')
 
581
        revno_1, revid_1 = self.tree.branch.last_revision_info()
 
582
        r2 = self.tree.commit('2nd commit', rev_id='child-1')
 
583
        # Undo the second commit
 
584
        self.tree.branch.set_last_revision_info(revno_1, revid_1)
 
585
        self.tree.set_parent_ids([revid_1])
 
586
        # Make a new second commit, child-2.  child-2 has diverged from
 
587
        # child-1.
 
588
        new_r2 = self.tree.commit('2nd commit', rev_id='child-2')
 
589
        self.tree.unlock()
 
590
        
 
591
    def test_not_allow_diverged(self):
 
592
        """If allow_diverged is not passed, then setting a divergent history
 
593
        returns a Diverged error.
 
594
        """
 
595
        self.make_branch_with_divergent_history()
 
596
        self.assertEqual(
 
597
            FailedSmartServerResponse(('Diverged',)),
 
598
            self.set_last_revision('child-1', 2))
 
599
        # The branch tip was not changed.
 
600
        self.assertEqual('child-2', self.tree.branch.last_revision())
 
601
 
 
602
    def test_allow_diverged(self):
 
603
        """If allow_diverged is passed, then setting a divergent history
 
604
        succeeds.
 
605
        """
 
606
        self.make_branch_with_divergent_history()
 
607
        branch_token, repo_token = self.lock_branch()
 
608
        response = self.request.execute(
 
609
            '', branch_token, repo_token, 'child-1', 1, 0)
 
610
        self.assertEqual(
 
611
            SuccessfulSmartServerResponse(('ok', 2, 'child-1')),
 
612
            response)
 
613
        self.unlock_branch()
 
614
        # The branch tip was changed.
 
615
        self.assertEqual('child-1', self.tree.branch.last_revision())
477
616
 
478
617
 
479
618
class TestSmartServerBranchRequestLockWrite(tests.TestCaseWithMemoryTransport):
630
769
            request.execute, 'subdir')
631
770
 
632
771
 
633
 
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithTransport):
 
772
class TestSmartServerRepositoryGetParentMap(tests.TestCaseWithMemoryTransport):
634
773
 
635
774
    def test_trivial_bzipped(self):
636
775
        # This tests that the wire encoding is actually bzipped
729
868
        request = smart.repository.SmartServerRepositoryGatherStats(backing)
730
869
        repository = self.make_repository('.')
731
870
        stats = repository.gather_stats()
732
 
        size = stats['size']
733
 
        expected_body = 'revisions: 0\nsize: %d\n' % size
 
871
        expected_body = 'revisions: 0\n'
734
872
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
735
873
                         request.execute('', '', 'no'))
736
874
 
749
887
        tree.unlock()
750
888
 
751
889
        stats = tree.branch.repository.gather_stats()
752
 
        size = stats['size']
753
890
        expected_body = ('firstrev: 123456.200 3600\n'
754
891
                         'latestrev: 654321.400 0\n'
755
 
                         'revisions: 2\n'
756
 
                         'size: %d\n' % size)
 
892
                         'revisions: 2\n')
757
893
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
758
894
                         request.execute('',
759
895
                                         rev_id_utf8, 'no'))
774
910
        tree.unlock()
775
911
        stats = tree.branch.repository.gather_stats()
776
912
 
777
 
        size = stats['size']
778
913
        expected_body = ('committers: 2\n'
779
914
                         'firstrev: 123456.200 3600\n'
780
915
                         'latestrev: 654321.400 0\n'
781
 
                         'revisions: 2\n'
782
 
                         'size: %d\n' % size)
 
916
                         'revisions: 2\n')
783
917
        self.assertEqual(SmartServerResponse(('ok', ), expected_body),
784
918
                         request.execute('',
785
919
                                         rev_id_utf8, 'yes'))
871
1005
            SmartServerResponse(('TokenMismatch',)), response)
872
1006
 
873
1007
 
874
 
class TestSmartServerRepositoryTarball(tests.TestCaseWithTransport):
875
 
 
876
 
    def test_repository_tarball(self):
877
 
        backing = self.get_transport()
878
 
        request = smart.repository.SmartServerRepositoryTarball(backing)
879
 
        repository = self.make_repository('.')
880
 
        # make some extraneous junk in the repository directory which should
881
 
        # not be copied
882
 
        self.build_tree(['.bzr/repository/extra-junk'])
883
 
        response = request.execute('', 'bz2')
884
 
        self.assertEqual(('ok',), response.args)
885
 
        # body should be a tbz2
886
 
        body_file = StringIO(response.body)
887
 
        body_tar = tarfile.open('body_tar.tbz2', fileobj=body_file,
888
 
            mode='r|bz2')
889
 
        # let's make sure there are some key repository components inside it.
890
 
        # the tarfile returns directories with trailing slashes...
891
 
        names = set([n.rstrip('/') for n in body_tar.getnames()])
892
 
        self.assertTrue('.bzr/repository/lock' in names)
893
 
        self.assertTrue('.bzr/repository/format' in names)
894
 
        self.assertTrue('.bzr/repository/extra-junk' not in names,
895
 
            "extraneous file present in tar file")
896
 
 
897
 
 
898
 
class TestSmartServerRepositoryStreamKnitData(tests.TestCaseWithMemoryTransport):
899
 
 
900
 
    def test_fetch_revisions(self):
901
 
        backing = self.get_transport()
902
 
        request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
903
 
        tree = self.make_branch_and_memory_tree('.')
904
 
        tree.lock_write()
905
 
        tree.add('')
906
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
907
 
        rev_id2_utf8 = u'\xc9'.encode('utf-8')
908
 
        r1 = tree.commit('1st commit', rev_id=rev_id1_utf8)
909
 
        r1 = tree.commit('2nd commit', rev_id=rev_id2_utf8)
910
 
        tree.unlock()
911
 
 
912
 
        response = request.execute('', rev_id2_utf8)
913
 
        self.assertEqual(('ok',), response.args)
914
 
        unpacker = pack.ContainerReader(StringIO(response.body))
915
 
        names = []
916
 
        for [name], read_bytes in unpacker.iter_records():
917
 
            names.append(name)
918
 
            bytes = read_bytes(None)
919
 
            # The bytes should be a valid bencoded string.
920
 
            bencode.bdecode(bytes)
921
 
            # XXX: assert that the bencoded knit records have the right
922
 
            # contents?
923
 
        
924
 
    def test_no_such_revision_error(self):
925
 
        backing = self.get_transport()
926
 
        request = smart.repository.SmartServerRepositoryStreamKnitDataForRevisions(backing)
927
 
        repo = self.make_repository('.')
928
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
929
 
        response = request.execute('', rev_id1_utf8)
930
 
        self.assertEqual(
931
 
            SmartServerResponse(('NoSuchRevision', rev_id1_utf8)),
932
 
            response)
933
 
 
934
 
 
935
 
class TestSmartServerRepositoryStreamRevisionsChunked(tests.TestCaseWithMemoryTransport):
936
 
 
937
 
    def test_fetch_revisions(self):
938
 
        backing = self.get_transport()
939
 
        request = smart.repository.SmartServerRepositoryStreamRevisionsChunked(
940
 
            backing)
941
 
        tree = self.make_branch_and_memory_tree('.')
942
 
        tree.lock_write()
943
 
        tree.add('')
944
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
945
 
        rev_id2_utf8 = u'\xc9'.encode('utf-8')
946
 
        tree.commit('1st commit', rev_id=rev_id1_utf8)
947
 
        tree.commit('2nd commit', rev_id=rev_id2_utf8)
948
 
        tree.unlock()
949
 
 
950
 
        response = request.execute('')
951
 
        self.assertEqual(None, response)
952
 
        response = request.do_body("%s\n%s\n1" % (rev_id2_utf8, rev_id1_utf8))
953
 
        self.assertEqual(('ok',), response.args)
954
 
        parser = pack.ContainerPushParser()
955
 
        names = []
956
 
        for stream_bytes in response.body_stream:
957
 
            parser.accept_bytes(stream_bytes)
958
 
            for [name], record_bytes in parser.read_pending_records():
959
 
                names.append(name)
960
 
                # The bytes should be a valid bencoded string.
961
 
                bencode.bdecode(record_bytes)
962
 
                # XXX: assert that the bencoded knit records have the right
963
 
                # contents?
964
 
        
965
 
    def test_no_such_revision_error(self):
966
 
        backing = self.get_transport()
967
 
        request = smart.repository.SmartServerRepositoryStreamRevisionsChunked(
968
 
            backing)
969
 
        repo = self.make_repository('.')
970
 
        rev_id1_utf8 = u'\xc8'.encode('utf-8')
971
 
        response = request.execute('')
972
 
        self.assertEqual(None, response)
973
 
        response = request.do_body("%s\n\n1" % (rev_id1_utf8,))
974
 
        self.assertEqual(
975
 
            FailedSmartServerResponse(('NoSuchRevision', )),
976
 
            response)
977
 
 
978
 
 
979
1008
class TestSmartServerIsReadonly(tests.TestCaseWithMemoryTransport):
980
1009
 
981
1010
    def test_is_readonly_no(self):
996
1025
class TestHandlers(tests.TestCase):
997
1026
    """Tests for the request.request_handlers object."""
998
1027
 
 
1028
    def test_all_registrations_exist(self):
 
1029
        """All registered request_handlers can be found."""
 
1030
        # If there's a typo in a register_lazy call, this loop will fail with
 
1031
        # an AttributeError.
 
1032
        for key, item in smart.request.request_handlers.iteritems():
 
1033
            pass
 
1034
 
999
1035
    def test_registered_methods(self):
1000
1036
        """Test that known methods are registered to the correct object."""
1001
1037
        self.assertEqual(
1014
1050
            smart.request.request_handlers.get('Branch.set_last_revision'),
1015
1051
            smart.branch.SmartServerBranchRequestSetLastRevision)
1016
1052
        self.assertEqual(
 
1053
            smart.request.request_handlers.get('Branch.set_last_revision_info'),
 
1054
            smart.branch.SmartServerBranchRequestSetLastRevisionInfo)
 
1055
        self.assertEqual(
1017
1056
            smart.request.request_handlers.get('Branch.unlock'),
1018
1057
            smart.branch.SmartServerBranchRequestUnlock)
1019
1058
        self.assertEqual(