~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bundle.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-06-22 17:11:20 UTC
  • mfrom: (4398.8.10 1.16-commit-fulltext)
  • Revision ID: pqm@pqm.ubuntu.com-20090622171120-fuxez9ylfqpxynqn
(jam) Add VF._add_text and reduce memory overhead during commit (see
        bug #109114)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007 Canonical Ltd
2
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
27
27
    inventory,
28
28
    merge,
29
29
    osutils,
 
30
    repository,
30
31
    revision as _mod_revision,
31
 
    symbol_versioning,
32
32
    tests,
33
33
    treebuilder,
34
34
    )
35
35
from bzrlib.bundle import read_mergeable_from_url
36
36
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
37
37
from bzrlib.bundle.bundle_data import BundleTree
 
38
from bzrlib.bzrdir import BzrDir
38
39
from bzrlib.directory_service import directories
39
40
from bzrlib.bundle.serializer import write_bundle, read_bundle, v09, v4
40
41
from bzrlib.bundle.serializer.v08 import BundleSerializerV08
41
42
from bzrlib.bundle.serializer.v09 import BundleSerializerV09
42
43
from bzrlib.bundle.serializer.v4 import BundleSerializerV4
 
44
from bzrlib.branch import Branch
43
45
from bzrlib.repofmt import knitrepo
44
46
from bzrlib.tests import (
45
47
    test_read_bundle,
46
48
    test_commit,
47
49
    )
48
50
from bzrlib.transform import TreeTransform
49
 
from bzrlib.tests import (
50
 
    features,
51
 
    )
52
 
 
53
 
 
54
 
def get_text(vf, key):
55
 
    """Get the fulltext for a given revision id that is present in the vf"""
56
 
    stream = vf.get_record_stream([key], 'unordered', True)
57
 
    record = stream.next()
58
 
    return record.get_bytes_as('fulltext')
59
 
 
60
 
 
61
 
def get_inventory_text(repo, revision_id):
62
 
    """Get the fulltext for the inventory at revision id"""
63
 
    repo.lock_read()
64
 
    try:
65
 
        return get_text(repo.inventories, (revision_id,))
66
 
    finally:
67
 
        repo.unlock()
68
51
 
69
52
 
70
53
class MockTree(object):
71
 
 
72
54
    def __init__(self):
73
55
        from bzrlib.inventory import InventoryDirectory, ROOT_ID
74
56
        object.__init__(self)
79
61
 
80
62
    inventory = property(lambda x:x)
81
63
 
82
 
    def all_file_ids(self):
83
 
        return set(self.paths.keys())
 
64
    def __iter__(self):
 
65
        return self.paths.iterkeys()
84
66
 
85
67
    def __getitem__(self, file_id):
86
68
        if file_id == self.root.file_id:
116
98
            ie = InventoryDirectory(file_id, name, parent_id)
117
99
        elif kind == 'file':
118
100
            ie = InventoryFile(file_id, name, parent_id)
119
 
            ie.text_sha1 = text_sha_1
120
 
            ie.text_size = text_size
121
101
        elif kind == 'symlink':
122
102
            ie = InventoryLink(file_id, name, parent_id)
123
103
        else:
124
104
            raise errors.BzrError('unknown kind %r' % kind)
 
105
        ie.text_sha1 = text_sha_1
 
106
        ie.text_size = text_size
125
107
        return ie
126
108
 
127
109
    def add_dir(self, file_id, path):
147
129
        result.seek(0,0)
148
130
        return result
149
131
 
150
 
    def get_file_revision(self, file_id):
151
 
        return self.inventory[file_id].revision
152
 
 
153
132
    def contents_stats(self, file_id):
154
133
        if file_id not in self.contents:
155
134
            return None, None
497
476
                                 % (ancestor,))
498
477
 
499
478
                # Now check that the file contents are all correct
500
 
                for inventory_id in old.all_file_ids():
 
479
                for inventory_id in old:
501
480
                    try:
502
481
                        old_file = old.get_file(inventory_id)
503
482
                    except errors.NoSuchFile:
511
490
                old.unlock()
512
491
        if not _mod_revision.is_null(rev_id):
513
492
            rh = self.b1.revision_history()
514
 
            self.applyDeprecated(symbol_versioning.deprecated_in((2, 4, 0)),
515
 
                tree.branch.set_revision_history, rh[:rh.index(rev_id)+1])
 
493
            tree.branch.set_revision_history(rh[:rh.index(rev_id)+1])
516
494
            tree.update()
517
495
            delta = tree.changes_from(self.b1.repository.revision_tree(rev_id))
518
496
            self.assertFalse(delta.has_changed(),
580
558
        self.tree1 = self.make_branch_and_tree('b1')
581
559
        self.b1 = self.tree1.branch
582
560
 
583
 
        self.build_tree_contents([('b1/one', 'one\n')])
584
 
        self.tree1.add('one', 'one-id')
585
 
        self.tree1.set_root_id('root-id')
 
561
        open('b1/one', 'wb').write('one\n')
 
562
        self.tree1.add('one')
586
563
        self.tree1.commit('add one', rev_id='a@cset-0-1')
587
564
 
588
565
        bundle = self.get_valid_bundle('null:', 'a@cset-0-1')
599
576
                , 'b1/sub/sub/'
600
577
                , 'b1/sub/sub/nonempty.txt'
601
578
                ])
602
 
        self.build_tree_contents([('b1/sub/sub/emptyfile.txt', ''),
603
 
                                  ('b1/dir/nolastnewline.txt', 'bloop')])
 
579
        open('b1/sub/sub/emptyfile.txt', 'wb').close()
 
580
        open('b1/dir/nolastnewline.txt', 'wb').write('bloop')
604
581
        tt = TreeTransform(self.tree1)
605
582
        tt.new_file('executable', tt.root, '#!/bin/sh\n', 'exe-1', True)
606
583
        tt.apply()
639
616
 
640
617
        bundle = self.get_valid_bundle('a@cset-0-2', 'a@cset-0-3')
641
618
        self.assertRaises((errors.TestamentMismatch,
642
 
            errors.VersionedFileInvalidChecksum,
643
 
            errors.BadBundle), self.get_invalid_bundle,
 
619
            errors.VersionedFileInvalidChecksum), self.get_invalid_bundle,
644
620
            'a@cset-0-2', 'a@cset-0-3')
645
621
        # Check a rollup bundle
646
622
        bundle = self.get_valid_bundle('null:', 'a@cset-0-3')
670
646
                          verbose=False)
671
647
        bundle = self.get_valid_bundle('a@cset-0-5', 'a@cset-0-6')
672
648
        other = self.get_checkout('a@cset-0-5')
673
 
        tree1_inv = get_inventory_text(self.tree1.branch.repository,
674
 
                                       'a@cset-0-5')
675
 
        tree2_inv = get_inventory_text(other.branch.repository,
676
 
                                       'a@cset-0-5')
 
649
        tree1_inv = self.tree1.branch.repository.get_inventory_xml(
 
650
            'a@cset-0-5')
 
651
        tree2_inv = other.branch.repository.get_inventory_xml('a@cset-0-5')
677
652
        self.assertEqualDiff(tree1_inv, tree2_inv)
678
653
        other.rename_one('sub/dir/nolastnewline.txt', 'sub/nolastnewline.txt')
679
654
        other.commit('rename file', rev_id='a@cset-0-6b')
685
660
    def _test_symlink_bundle(self, link_name, link_target, new_link_target):
686
661
        link_id = 'link-1'
687
662
 
688
 
        self.requireFeature(features.SymlinkFeature)
 
663
        self.requireFeature(tests.SymlinkFeature)
689
664
        self.tree1 = self.make_branch_and_tree('b1')
690
665
        self.b1 = self.tree1.branch
691
666
 
732
707
        self._test_symlink_bundle('link', 'bar/foo', 'mars')
733
708
 
734
709
    def test_unicode_symlink_bundle(self):
735
 
        self.requireFeature(features.UnicodeFilenameFeature)
 
710
        self.requireFeature(tests.UnicodeFilenameFeature)
736
711
        self._test_symlink_bundle(u'\N{Euro Sign}link',
737
712
                                  u'bar/\N{Euro Sign}foo',
738
713
                                  u'mars\N{Euro Sign}')
839
814
        return bundle_file.getvalue()
840
815
 
841
816
    def test_unicode_bundle(self):
842
 
        self.requireFeature(features.UnicodeFilenameFeature)
 
817
        self.requireFeature(tests.UnicodeFilenameFeature)
843
818
        # Handle international characters
844
819
        os.mkdir('b1')
845
820
        f = open(u'b1/with Dod\N{Euro Sign}', 'wb')
1045
1020
        bundle = read_bundle(self.create_bundle_text('null:', 'rev2')[0])
1046
1021
        repo = self.make_repository('repo', format='dirstate-with-subtree')
1047
1022
        bundle.install_revisions(repo)
1048
 
        inv_text = repo._get_inventory_xml('rev2')
 
1023
        inv_text = repo.get_inventory_xml('rev2')
1049
1024
        self.assertNotContainsRe(inv_text, 'format="5"')
1050
1025
        self.assertContainsRe(inv_text, 'format="7"')
1051
1026
 
1071
1046
 
1072
1047
    def test_inv_hash_across_serializers(self):
1073
1048
        repo = self.make_repo_with_installed_revisions()
1074
 
        recorded_inv_sha1 = repo.get_revision('rev2').inventory_sha1
1075
 
        xml = repo._get_inventory_xml('rev2')
 
1049
        recorded_inv_sha1 = repo.get_inventory_sha1('rev2')
 
1050
        xml = repo.get_inventory_xml('rev2')
1076
1051
        self.assertEqual(osutils.sha_string(xml), recorded_inv_sha1)
1077
1052
 
1078
1053
    def test_across_models_incompatible(self):
1342
1317
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1343
1318
        new_text = new_text.replace('<file file_id="exe-1"',
1344
1319
                                    '<file executable="y" file_id="exe-1"')
1345
 
        new_text = new_text.replace('B260', 'B275')
 
1320
        new_text = new_text.replace('B222', 'B237')
1346
1321
        bundle_txt = StringIO()
1347
1322
        bundle_txt.write(serializer._get_bundle_header('4'))
1348
1323
        bundle_txt.write('\n')
1418
1393
        branch = tree_a.branch
1419
1394
        repo_a = branch.repository
1420
1395
        tree_a.commit("base", allow_pointless=True, rev_id='A')
1421
 
        self.assertFalse(branch.repository.has_signature_for_revision_id('A'))
 
1396
        self.failIf(branch.repository.has_signature_for_revision_id('A'))
1422
1397
        try:
1423
1398
            from bzrlib.testament import Testament
1424
1399
            # monkey patch gpg signing mechanism
1448
1423
        install_bundle(repo_b, serializer.read(s))
1449
1424
 
1450
1425
 
1451
 
class V4_2aBundleTester(V4BundleTester):
 
1426
class V4WeaveBundleTester(V4BundleTester):
1452
1427
 
1453
1428
    def bzrdir_format(self):
1454
 
        return '2a'
1455
 
 
1456
 
    def get_invalid_bundle(self, base_rev_id, rev_id):
1457
 
        """Create a bundle from base_rev_id -> rev_id in built-in branch.
1458
 
        Munge the text so that it's invalid.
1459
 
 
1460
 
        :return: The in-memory bundle
1461
 
        """
1462
 
        from bzrlib.bundle import serializer
1463
 
        bundle_txt, rev_ids = self.create_bundle_text(base_rev_id, rev_id)
1464
 
        new_text = self.get_raw(StringIO(''.join(bundle_txt)))
1465
 
        # We are going to be replacing some text to set the executable bit on a
1466
 
        # file. Make sure the text replacement actually works correctly.
1467
 
        self.assertContainsRe(new_text, '(?m)B244\n\ni 1\n<inventory')
1468
 
        new_text = new_text.replace('<file file_id="exe-1"',
1469
 
                                    '<file executable="y" file_id="exe-1"')
1470
 
        new_text = new_text.replace('B244', 'B259')
1471
 
        bundle_txt = StringIO()
1472
 
        bundle_txt.write(serializer._get_bundle_header('4'))
1473
 
        bundle_txt.write('\n')
1474
 
        bundle_txt.write(new_text.encode('bz2'))
1475
 
        bundle_txt.seek(0)
1476
 
        bundle = read_bundle(bundle_txt)
1477
 
        self.valid_apply_bundle(base_rev_id, bundle)
1478
 
        return bundle
1479
 
 
1480
 
    def make_merged_branch(self):
1481
 
        builder = self.make_branch_builder('source')
1482
 
        builder.start_series()
1483
 
        builder.build_snapshot('a@cset-0-1', None, [
1484
 
            ('add', ('', 'root-id', 'directory', None)),
1485
 
            ('add', ('file', 'file-id', 'file', 'original content\n')),
1486
 
            ])
1487
 
        builder.build_snapshot('a@cset-0-2a', ['a@cset-0-1'], [
1488
 
            ('modify', ('file-id', 'new-content\n')),
1489
 
            ])
1490
 
        builder.build_snapshot('a@cset-0-2b', ['a@cset-0-1'], [
1491
 
            ('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1492
 
            ])
1493
 
        builder.build_snapshot('a@cset-0-3', ['a@cset-0-2a', 'a@cset-0-2b'], [
1494
 
            ('add', ('other-file', 'file2-id', 'file', 'file2-content\n')),
1495
 
            ])
1496
 
        builder.finish_series()
1497
 
        self.b1 = builder.get_branch()
1498
 
        self.b1.lock_read()
1499
 
        self.addCleanup(self.b1.unlock)
1500
 
 
1501
 
    def make_bundle_just_inventories(self, base_revision_id,
1502
 
                                     target_revision_id,
1503
 
                                     revision_ids):
1504
 
        sio = StringIO()
1505
 
        writer = v4.BundleWriteOperation(base_revision_id, target_revision_id,
1506
 
                                         self.b1.repository, sio)
1507
 
        writer.bundle.begin()
1508
 
        writer._add_inventory_mpdiffs_from_serializer(revision_ids)
1509
 
        writer.bundle.end()
1510
 
        sio.seek(0)
1511
 
        return sio
1512
 
 
1513
 
    def test_single_inventory_multiple_parents_as_xml(self):
1514
 
        self.make_merged_branch()
1515
 
        sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1516
 
                                                ['a@cset-0-3'])
1517
 
        reader = v4.BundleReader(sio, stream_input=False)
1518
 
        records = list(reader.iter_records())
1519
 
        self.assertEqual(1, len(records))
1520
 
        (bytes, metadata, repo_kind, revision_id,
1521
 
         file_id) = records[0]
1522
 
        self.assertIs(None, file_id)
1523
 
        self.assertEqual('a@cset-0-3', revision_id)
1524
 
        self.assertEqual('inventory', repo_kind)
1525
 
        self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1526
 
                          'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1527
 
                          'storage_kind': 'mpdiff',
1528
 
                         }, metadata)
1529
 
        # We should have an mpdiff that takes some lines from both parents.
1530
 
        self.assertEqualDiff(
1531
 
            'i 1\n'
1532
 
            '<inventory format="10" revision_id="a@cset-0-3">\n'
1533
 
            '\n'
1534
 
            'c 0 1 1 2\n'
1535
 
            'c 1 3 3 2\n', bytes)
1536
 
 
1537
 
    def test_single_inv_no_parents_as_xml(self):
1538
 
        self.make_merged_branch()
1539
 
        sio = self.make_bundle_just_inventories('null:', 'a@cset-0-1',
1540
 
                                                ['a@cset-0-1'])
1541
 
        reader = v4.BundleReader(sio, stream_input=False)
1542
 
        records = list(reader.iter_records())
1543
 
        self.assertEqual(1, len(records))
1544
 
        (bytes, metadata, repo_kind, revision_id,
1545
 
         file_id) = records[0]
1546
 
        self.assertIs(None, file_id)
1547
 
        self.assertEqual('a@cset-0-1', revision_id)
1548
 
        self.assertEqual('inventory', repo_kind)
1549
 
        self.assertEqual({'parents': [],
1550
 
                          'sha1': 'a13f42b142d544aac9b085c42595d304150e31a2',
1551
 
                          'storage_kind': 'mpdiff',
1552
 
                         }, metadata)
1553
 
        # We should have an mpdiff that takes some lines from both parents.
1554
 
        self.assertEqualDiff(
1555
 
            'i 4\n'
1556
 
            '<inventory format="10" revision_id="a@cset-0-1">\n'
1557
 
            '<directory file_id="root-id" name=""'
1558
 
                ' revision="a@cset-0-1" />\n'
1559
 
            '<file file_id="file-id" name="file" parent_id="root-id"'
1560
 
                ' revision="a@cset-0-1"'
1561
 
                ' text_sha1="09c2f8647e14e49e922b955c194102070597c2d1"'
1562
 
                ' text_size="17" />\n'
1563
 
            '</inventory>\n'
1564
 
            '\n', bytes)
1565
 
 
1566
 
    def test_multiple_inventories_as_xml(self):
1567
 
        self.make_merged_branch()
1568
 
        sio = self.make_bundle_just_inventories('a@cset-0-1', 'a@cset-0-3',
1569
 
            ['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'])
1570
 
        reader = v4.BundleReader(sio, stream_input=False)
1571
 
        records = list(reader.iter_records())
1572
 
        self.assertEqual(3, len(records))
1573
 
        revision_ids = [rev_id for b, m, k, rev_id, f in records]
1574
 
        self.assertEqual(['a@cset-0-2a', 'a@cset-0-2b', 'a@cset-0-3'],
1575
 
                         revision_ids)
1576
 
        metadata_2a = records[0][1]
1577
 
        self.assertEqual({'parents': ['a@cset-0-1'],
1578
 
                          'sha1': '1e105886d62d510763e22885eec733b66f5f09bf',
1579
 
                          'storage_kind': 'mpdiff',
1580
 
                         }, metadata_2a)
1581
 
        metadata_2b = records[1][1]
1582
 
        self.assertEqual({'parents': ['a@cset-0-1'],
1583
 
                          'sha1': 'f03f12574bdb5ed2204c28636c98a8547544ccd8',
1584
 
                          'storage_kind': 'mpdiff',
1585
 
                         }, metadata_2b)
1586
 
        metadata_3 = records[2][1]
1587
 
        self.assertEqual({'parents': ['a@cset-0-2a', 'a@cset-0-2b'],
1588
 
                          'sha1': '09c53b0c4de0895e11a2aacc34fef60a6e70865c',
1589
 
                          'storage_kind': 'mpdiff',
1590
 
                         }, metadata_3)
1591
 
        bytes_2a = records[0][0]
1592
 
        self.assertEqualDiff(
1593
 
            'i 1\n'
1594
 
            '<inventory format="10" revision_id="a@cset-0-2a">\n'
1595
 
            '\n'
1596
 
            'c 0 1 1 1\n'
1597
 
            'i 1\n'
1598
 
            '<file file_id="file-id" name="file" parent_id="root-id"'
1599
 
                ' revision="a@cset-0-2a"'
1600
 
                ' text_sha1="50f545ff40e57b6924b1f3174b267ffc4576e9a9"'
1601
 
                ' text_size="12" />\n'
1602
 
            '\n'
1603
 
            'c 0 3 3 1\n', bytes_2a)
1604
 
        bytes_2b = records[1][0]
1605
 
        self.assertEqualDiff(
1606
 
            'i 1\n'
1607
 
            '<inventory format="10" revision_id="a@cset-0-2b">\n'
1608
 
            '\n'
1609
 
            'c 0 1 1 2\n'
1610
 
            'i 1\n'
1611
 
            '<file file_id="file2-id" name="other-file" parent_id="root-id"'
1612
 
                ' revision="a@cset-0-2b"'
1613
 
                ' text_sha1="b46c0c8ea1e5ef8e46fc8894bfd4752a88ec939e"'
1614
 
                ' text_size="14" />\n'
1615
 
            '\n'
1616
 
            'c 0 3 4 1\n', bytes_2b)
1617
 
        bytes_3 = records[2][0]
1618
 
        self.assertEqualDiff(
1619
 
            'i 1\n'
1620
 
            '<inventory format="10" revision_id="a@cset-0-3">\n'
1621
 
            '\n'
1622
 
            'c 0 1 1 2\n'
1623
 
            'c 1 3 3 2\n', bytes_3)
1624
 
 
1625
 
    def test_creating_bundle_preserves_chk_pages(self):
1626
 
        self.make_merged_branch()
1627
 
        target = self.b1.bzrdir.sprout('target',
1628
 
                                       revision_id='a@cset-0-2a').open_branch()
1629
 
        bundle_txt, rev_ids = self.create_bundle_text('a@cset-0-2a',
1630
 
                                                      'a@cset-0-3')
1631
 
        self.assertEqual(['a@cset-0-2b', 'a@cset-0-3'], rev_ids)
1632
 
        bundle = read_bundle(bundle_txt)
1633
 
        target.lock_write()
1634
 
        self.addCleanup(target.unlock)
1635
 
        install_bundle(target.repository, bundle)
1636
 
        inv1 = self.b1.repository.inventories.get_record_stream([
1637
 
            ('a@cset-0-3',)], 'unordered',
1638
 
            True).next().get_bytes_as('fulltext')
1639
 
        inv2 = target.repository.inventories.get_record_stream([
1640
 
            ('a@cset-0-3',)], 'unordered',
1641
 
            True).next().get_bytes_as('fulltext')
1642
 
        self.assertEqualDiff(inv1, inv2)
 
1429
        return 'metaweave'
1643
1430
 
1644
1431
 
1645
1432
class MungedBundleTester(object):
1820
1607
            def look_up(self, name, url):
1821
1608
                return 'source'
1822
1609
        directories.register('foo:', FooService, 'Testing directory service')
1823
 
        self.addCleanup(directories.remove, 'foo:')
 
1610
        self.addCleanup(lambda: directories.remove('foo:'))
1824
1611
        self.build_tree_contents([('./foo:bar', out.getvalue())])
1825
1612
        self.assertRaises(errors.NotABundle, read_mergeable_from_url,
1826
1613
                          'foo:bar')
1827
1614
 
1828
 
    def test_infinite_redirects_are_not_a_bundle(self):
1829
 
        """If a URL causes TooManyRedirections then NotABundle is raised.
1830
 
        """
1831
 
        from bzrlib.tests.blackbox.test_push import RedirectingMemoryServer
1832
 
        server = RedirectingMemoryServer()
1833
 
        self.start_server(server)
1834
 
        url = server.get_url() + 'infinite-loop'
1835
 
        self.assertRaises(errors.NotABundle, read_mergeable_from_url, url)
1836
 
 
1837
1615
    def test_smart_server_connection_reset(self):
1838
1616
        """If a smart server connection fails during the attempt to read a
1839
1617
        bundle, then the ConnectionReset error should be propagated.
1840
1618
        """
1841
1619
        # Instantiate a server that will provoke a ConnectionReset
1842
1620
        sock_server = _DisconnectingTCPServer()
1843
 
        self.start_server(sock_server)
 
1621
        sock_server.setUp()
 
1622
        self.addCleanup(sock_server.tearDown)
1844
1623
        # We don't really care what the url is since the server will close the
1845
1624
        # connection without interpreting it
1846
1625
        url = sock_server.get_url()
1850
1629
class _DisconnectingTCPServer(object):
1851
1630
    """A TCP server that immediately closes any connection made to it."""
1852
1631
 
1853
 
    def start_server(self):
 
1632
    def setUp(self):
1854
1633
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1855
1634
        self.sock.bind(('127.0.0.1', 0))
1856
1635
        self.sock.listen(1)
1868
1647
    def get_url(self):
1869
1648
        return 'bzr://127.0.0.1:%d/' % (self.port,)
1870
1649
 
1871
 
    def stop_server(self):
 
1650
    def tearDown(self):
1872
1651
        try:
1873
1652
            # make sure the thread dies by connecting to the listening socket,
1874
1653
            # just in case the test failed to do so.
1879
1658
            pass
1880
1659
        self.sock.close()
1881
1660
        self.thread.join()
 
1661