/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_knit.py

  • Committer: Martin Pool
  • Date: 2009-03-13 07:54:48 UTC
  • mfrom: (4144 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4189.
  • Revision ID: mbp@sourcefrog.net-20090313075448-jlz1t7baz7gzipqn
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
    KnitSequenceMatcher,
43
43
    KnitVersionedFiles,
44
44
    PlainKnitContent,
 
45
    _VFContentMapGenerator,
45
46
    _DirectPackAccess,
46
47
    _KndxIndex,
47
48
    _KnitGraphIndex,
63
64
from bzrlib.versionedfile import (
64
65
    AbsentContentFactory,
65
66
    ConstantMapper,
 
67
    network_bytes_to_kind_and_offset,
66
68
    RecordingVersionedFilesDecorator,
67
69
    )
68
70
 
297
299
        access = self.get_access()
298
300
        memos = access.add_raw_records([('key', 10)], '1234567890')
299
301
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
300
 
 
 
302
 
301
303
    def test_add_several_raw_records(self):
302
304
        """add_raw_records with many records and read some back."""
303
305
        access = self.get_access()
1091
1093
            call[1][1].getvalue())
1092
1094
        self.assertEqual({'create_parent_dir': True}, call[2])
1093
1095
 
 
1096
    def assertTotalBuildSize(self, size, keys, positions):
 
1097
        self.assertEqual(size,
 
1098
                         knit._get_total_build_size(None, keys, positions))
 
1099
 
 
1100
    def test__get_total_build_size(self):
 
1101
        positions = {
 
1102
            ('a',): (('fulltext', False), (('a',), 0, 100), None),
 
1103
            ('b',): (('line-delta', False), (('b',), 100, 21), ('a',)),
 
1104
            ('c',): (('line-delta', False), (('c',), 121, 35), ('b',)),
 
1105
            ('d',): (('line-delta', False), (('d',), 156, 12), ('b',)),
 
1106
            }
 
1107
        self.assertTotalBuildSize(100, [('a',)], positions)
 
1108
        self.assertTotalBuildSize(121, [('b',)], positions)
 
1109
        # c needs both a & b
 
1110
        self.assertTotalBuildSize(156, [('c',)], positions)
 
1111
        # we shouldn't count 'b' twice
 
1112
        self.assertTotalBuildSize(156, [('b',), ('c',)], positions)
 
1113
        self.assertTotalBuildSize(133, [('d',)], positions)
 
1114
        self.assertTotalBuildSize(168, [('c',), ('d',)], positions)
 
1115
 
1094
1116
    def test_get_position(self):
1095
1117
        transport = MockTransport([
1096
1118
            _KndxIndex.HEADER,
1237
1259
            else:
1238
1260
                raise
1239
1261
 
 
1262
    def test_scan_unvalidated_index_not_implemented(self):
 
1263
        transport = MockTransport()
 
1264
        index = self.get_knit_index(transport, 'filename', 'r')
 
1265
        self.assertRaises(
 
1266
            NotImplementedError, index.scan_unvalidated_index,
 
1267
            'dummy graph_index')
 
1268
        self.assertRaises(
 
1269
            NotImplementedError, index.get_missing_compression_parents)
 
1270
 
1240
1271
    def test_short_line(self):
1241
1272
        transport = MockTransport([
1242
1273
            _KndxIndex.HEADER,
1296
1327
class TestBadShaError(KnitTests):
1297
1328
    """Tests for handling of sha errors."""
1298
1329
 
1299
 
    def test_exception_has_text(self):
 
1330
    def test_sha_exception_has_text(self):
1300
1331
        # having the failed text included in the error allows for recovery.
1301
1332
        source = self.make_test_knit()
1302
1333
        target = self.make_test_knit(name="target")
1313
1344
        target.insert_record_stream(
1314
1345
            source.get_record_stream([broken], 'unordered', False))
1315
1346
        err = self.assertRaises(errors.KnitCorrupt,
1316
 
            target.get_record_stream([broken], 'unordered', True).next)
 
1347
            target.get_record_stream([broken], 'unordered', True
 
1348
            ).next().get_bytes_as, 'chunked')
1317
1349
        self.assertEqual(['gam\n', 'bar\n'], err.content)
1318
1350
        # Test for formatting with live data
1319
1351
        self.assertStartsWith(str(err), "Knit ")
1524
1556
            [('parent',)])])
1525
1557
        # but neither should have added data:
1526
1558
        self.assertEqual([[], [], [], []], self.caught_entries)
1527
 
        
 
1559
 
1528
1560
    def test_add_version_different_dup(self):
1529
1561
        index = self.two_graph_index(deltas=True, catch_adds=True)
1530
1562
        # change options
1531
1563
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1532
 
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1533
 
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1534
 
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
 
1564
            [(('tip',), 'line-delta', (None, 0, 100), [('parent',)])])
1535
1565
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1536
1566
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
1537
1567
        # parents
1538
1568
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1539
1569
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1540
1570
        self.assertEqual([], self.caught_entries)
1541
 
        
 
1571
 
1542
1572
    def test_add_versions_nodeltas(self):
1543
1573
        index = self.two_graph_index(catch_adds=True)
1544
1574
        index.add_records([
1586
1616
            [('parent',)])])
1587
1617
        # but neither should have added data.
1588
1618
        self.assertEqual([[], [], [], []], self.caught_entries)
1589
 
        
 
1619
 
1590
1620
    def test_add_versions_different_dup(self):
1591
1621
        index = self.two_graph_index(deltas=True, catch_adds=True)
1592
1622
        # change options
1593
1623
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1594
 
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1595
 
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1596
 
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
 
1624
            [(('tip',), 'line-delta', (None, 0, 100), [('parent',)])])
1597
1625
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1598
1626
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
1599
1627
        # parents
1602
1630
        # change options in the second record
1603
1631
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1604
1632
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)]),
1605
 
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
 
1633
             (('tip',), 'line-delta', (None, 0, 100), [('parent',)])])
1606
1634
        self.assertEqual([], self.caught_entries)
1607
1635
 
 
1636
    def make_g_index_missing_compression_parent(self):
 
1637
        graph_index = self.make_g_index('missing_comp', 2,
 
1638
            [(('tip', ), ' 100 78',
 
1639
              ([('missing-parent', ), ('ghost', )], [('missing-parent', )]))])
 
1640
        return graph_index
 
1641
 
 
1642
    def make_g_index_no_external_refs(self):
 
1643
        graph_index = self.make_g_index('no_external_refs', 2,
 
1644
            [(('rev', ), ' 100 78',
 
1645
              ([('parent', ), ('ghost', )], []))])
 
1646
        return graph_index
 
1647
 
 
1648
    def test_add_good_unvalidated_index(self):
 
1649
        unvalidated = self.make_g_index_no_external_refs()
 
1650
        combined = CombinedGraphIndex([unvalidated])
 
1651
        index = _KnitGraphIndex(combined, lambda: True, deltas=True)
 
1652
        index.scan_unvalidated_index(unvalidated)
 
1653
        self.assertEqual(frozenset(), index.get_missing_compression_parents())
 
1654
 
 
1655
    def test_add_incomplete_unvalidated_index(self):
 
1656
        unvalidated = self.make_g_index_missing_compression_parent()
 
1657
        combined = CombinedGraphIndex([unvalidated])
 
1658
        index = _KnitGraphIndex(combined, lambda: True, deltas=True)
 
1659
        index.scan_unvalidated_index(unvalidated)
 
1660
        # This also checks that its only the compression parent that is
 
1661
        # examined, otherwise 'ghost' would also be reported as a missing
 
1662
        # parent.
 
1663
        self.assertEqual(
 
1664
            frozenset([('missing-parent',)]),
 
1665
            index.get_missing_compression_parents())
 
1666
 
 
1667
    def test_add_unvalidated_index_with_present_external_references(self):
 
1668
        index = self.two_graph_index(deltas=True)
 
1669
        # Ugly hack to get at one of the underlying GraphIndex objects that
 
1670
        # two_graph_index built.
 
1671
        unvalidated = index._graph_index._indices[1]
 
1672
        # 'parent' is an external ref of _indices[1] (unvalidated), but is
 
1673
        # present in _indices[0].
 
1674
        index.scan_unvalidated_index(unvalidated)
 
1675
        self.assertEqual(frozenset(), index.get_missing_compression_parents())
 
1676
 
 
1677
    def make_new_missing_parent_g_index(self, name):
 
1678
        missing_parent = name + '-missing-parent'
 
1679
        graph_index = self.make_g_index(name, 2,
 
1680
            [((name + 'tip', ), ' 100 78',
 
1681
              ([(missing_parent, ), ('ghost', )], [(missing_parent, )]))])
 
1682
        return graph_index
 
1683
 
 
1684
    def test_add_mulitiple_unvalidated_indices_with_missing_parents(self):
 
1685
        g_index_1 = self.make_new_missing_parent_g_index('one')
 
1686
        g_index_2 = self.make_new_missing_parent_g_index('two')
 
1687
        combined = CombinedGraphIndex([g_index_1, g_index_2])
 
1688
        index = _KnitGraphIndex(combined, lambda: True, deltas=True)
 
1689
        index.scan_unvalidated_index(g_index_1)
 
1690
        index.scan_unvalidated_index(g_index_2)
 
1691
        self.assertEqual(
 
1692
            frozenset([('one-missing-parent',), ('two-missing-parent',)]),
 
1693
            index.get_missing_compression_parents())
 
1694
 
 
1695
    def test_add_mulitiple_unvalidated_indices_with_mutual_dependencies(self):
 
1696
        graph_index_a = self.make_g_index('one', 2,
 
1697
            [(('parent-one', ), ' 100 78', ([('non-compression-parent',)], [])),
 
1698
             (('child-of-two', ), ' 100 78',
 
1699
              ([('parent-two',)], [('parent-two',)]))])
 
1700
        graph_index_b = self.make_g_index('two', 2,
 
1701
            [(('parent-two', ), ' 100 78', ([('non-compression-parent',)], [])),
 
1702
             (('child-of-one', ), ' 100 78',
 
1703
              ([('parent-one',)], [('parent-one',)]))])
 
1704
        combined = CombinedGraphIndex([graph_index_a, graph_index_b])
 
1705
        index = _KnitGraphIndex(combined, lambda: True, deltas=True)
 
1706
        index.scan_unvalidated_index(graph_index_a)
 
1707
        index.scan_unvalidated_index(graph_index_b)
 
1708
        self.assertEqual(
 
1709
            frozenset([]), index.get_missing_compression_parents())
 
1710
 
1608
1711
 
1609
1712
class TestNoParentsGraphIndexKnit(KnitTests):
1610
1713
    """Tests for knits using _KnitGraphIndex with no parents."""
1618
1721
        size = trans.put_file(name, stream)
1619
1722
        return GraphIndex(trans, name, size)
1620
1723
 
 
1724
    def test_add_good_unvalidated_index(self):
 
1725
        unvalidated = self.make_g_index('unvalidated')
 
1726
        combined = CombinedGraphIndex([unvalidated])
 
1727
        index = _KnitGraphIndex(combined, lambda: True, parents=False)
 
1728
        index.scan_unvalidated_index(unvalidated)
 
1729
        self.assertEqual(frozenset(),
 
1730
            index.get_missing_compression_parents())
 
1731
 
1621
1732
    def test_parents_deltas_incompatible(self):
1622
1733
        index = CombinedGraphIndex([])
1623
1734
        self.assertRaises(errors.KnitError, _KnitGraphIndex, lambda:True,
1704
1815
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
1705
1816
        # but neither should have added data.
1706
1817
        self.assertEqual([[], [], [], []], self.caught_entries)
1707
 
        
 
1818
 
1708
1819
    def test_add_version_different_dup(self):
1709
1820
        index = self.two_graph_index(catch_adds=True)
1710
1821
        # change options
1718
1829
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1719
1830
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
1720
1831
        self.assertEqual([], self.caught_entries)
1721
 
        
 
1832
 
1722
1833
    def test_add_versions(self):
1723
1834
        index = self.two_graph_index(catch_adds=True)
1724
1835
        index.add_records([
1756
1867
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
1757
1868
        # but neither should have added data.
1758
1869
        self.assertEqual([[], [], [], []], self.caught_entries)
1759
 
        
 
1870
 
1760
1871
    def test_add_versions_different_dup(self):
1761
1872
        index = self.two_graph_index(catch_adds=True)
1762
1873
        # change options
1776
1887
        self.assertEqual([], self.caught_entries)
1777
1888
 
1778
1889
 
 
1890
class TestKnitVersionedFiles(KnitTests):
 
1891
 
 
1892
    def assertGroupKeysForIo(self, exp_groups, keys, non_local_keys,
 
1893
                             positions, _min_buffer_size=None):
 
1894
        kvf = self.make_test_knit()
 
1895
        if _min_buffer_size is None:
 
1896
            _min_buffer_size = knit._STREAM_MIN_BUFFER_SIZE
 
1897
        self.assertEqual(exp_groups, kvf._group_keys_for_io(keys,
 
1898
                                        non_local_keys, positions,
 
1899
                                        _min_buffer_size=_min_buffer_size))
 
1900
 
 
1901
    def assertSplitByPrefix(self, expected_map, expected_prefix_order,
 
1902
                            keys):
 
1903
        split, prefix_order = KnitVersionedFiles._split_by_prefix(keys)
 
1904
        self.assertEqual(expected_map, split)
 
1905
        self.assertEqual(expected_prefix_order, prefix_order)
 
1906
 
 
1907
    def test__group_keys_for_io(self):
 
1908
        ft_detail = ('fulltext', False)
 
1909
        ld_detail = ('line-delta', False)
 
1910
        f_a = ('f', 'a')
 
1911
        f_b = ('f', 'b')
 
1912
        f_c = ('f', 'c')
 
1913
        g_a = ('g', 'a')
 
1914
        g_b = ('g', 'b')
 
1915
        g_c = ('g', 'c')
 
1916
        positions = {
 
1917
            f_a: (ft_detail, (f_a, 0, 100), None),
 
1918
            f_b: (ld_detail, (f_b, 100, 21), f_a),
 
1919
            f_c: (ld_detail, (f_c, 180, 15), f_b),
 
1920
            g_a: (ft_detail, (g_a, 121, 35), None),
 
1921
            g_b: (ld_detail, (g_b, 156, 12), g_a),
 
1922
            g_c: (ld_detail, (g_c, 195, 13), g_a),
 
1923
            }
 
1924
        self.assertGroupKeysForIo([([f_a], set())],
 
1925
                                  [f_a], [], positions)
 
1926
        self.assertGroupKeysForIo([([f_a], set([f_a]))],
 
1927
                                  [f_a], [f_a], positions)
 
1928
        self.assertGroupKeysForIo([([f_a, f_b], set([]))],
 
1929
                                  [f_a, f_b], [], positions)
 
1930
        self.assertGroupKeysForIo([([f_a, f_b], set([f_b]))],
 
1931
                                  [f_a, f_b], [f_b], positions)
 
1932
        self.assertGroupKeysForIo([([f_a, f_b, g_a, g_b], set())],
 
1933
                                  [f_a, g_a, f_b, g_b], [], positions)
 
1934
        self.assertGroupKeysForIo([([f_a, f_b, g_a, g_b], set())],
 
1935
                                  [f_a, g_a, f_b, g_b], [], positions,
 
1936
                                  _min_buffer_size=150)
 
1937
        self.assertGroupKeysForIo([([f_a, f_b], set()), ([g_a, g_b], set())],
 
1938
                                  [f_a, g_a, f_b, g_b], [], positions,
 
1939
                                  _min_buffer_size=100)
 
1940
        self.assertGroupKeysForIo([([f_c], set()), ([g_b], set())],
 
1941
                                  [f_c, g_b], [], positions,
 
1942
                                  _min_buffer_size=125)
 
1943
        self.assertGroupKeysForIo([([g_b, f_c], set())],
 
1944
                                  [g_b, f_c], [], positions,
 
1945
                                  _min_buffer_size=125)
 
1946
 
 
1947
    def test__split_by_prefix(self):
 
1948
        self.assertSplitByPrefix({'f': [('f', 'a'), ('f', 'b')],
 
1949
                                  'g': [('g', 'b'), ('g', 'a')],
 
1950
                                 }, ['f', 'g'],
 
1951
                                 [('f', 'a'), ('g', 'b'),
 
1952
                                  ('g', 'a'), ('f', 'b')])
 
1953
 
 
1954
        self.assertSplitByPrefix({'f': [('f', 'a'), ('f', 'b')],
 
1955
                                  'g': [('g', 'b'), ('g', 'a')],
 
1956
                                 }, ['f', 'g'],
 
1957
                                 [('f', 'a'), ('f', 'b'),
 
1958
                                  ('g', 'b'), ('g', 'a')])
 
1959
 
 
1960
        self.assertSplitByPrefix({'f': [('f', 'a'), ('f', 'b')],
 
1961
                                  'g': [('g', 'b'), ('g', 'a')],
 
1962
                                 }, ['f', 'g'],
 
1963
                                 [('f', 'a'), ('f', 'b'),
 
1964
                                  ('g', 'b'), ('g', 'a')])
 
1965
 
 
1966
        self.assertSplitByPrefix({'f': [('f', 'a'), ('f', 'b')],
 
1967
                                  'g': [('g', 'b'), ('g', 'a')],
 
1968
                                  '': [('a',), ('b',)]
 
1969
                                 }, ['f', 'g', ''],
 
1970
                                 [('f', 'a'), ('g', 'b'),
 
1971
                                  ('a',), ('b',),
 
1972
                                  ('g', 'a'), ('f', 'b')])
 
1973
 
 
1974
 
1779
1975
class TestStacking(KnitTests):
1780
1976
 
1781
1977
    def get_basis_and_test_knit(self):
1842
2038
 
1843
2039
    def test_check(self):
1844
2040
        # At the moment checking a stacked knit does implicitly check the
1845
 
        # fallback files.  
 
2041
        # fallback files.
1846
2042
        basis, test = self.get_basis_and_test_knit()
1847
2043
        test.check()
1848
2044
 
1940
2136
                True).next()
1941
2137
            self.assertEqual(record.key, result[0])
1942
2138
            self.assertEqual(record.sha1, result[1])
1943
 
            self.assertEqual(record.storage_kind, result[2])
 
2139
            # We used to check that the storage kind matched, but actually it
 
2140
            # depends on whether it was sourced from the basis, or in a single
 
2141
            # group, because asking for full texts returns proxy objects to a
 
2142
            # _ContentMapGenerator object; so checking the kind is unneeded.
1944
2143
            self.assertEqual(record.get_bytes_as('fulltext'), result[3])
1945
2144
        # It's not strictly minimal, but it seems reasonable for now for it to
1946
2145
        # ask which fallbacks have which parents.
2083
2282
 
2084
2283
    def test_iter_lines_added_or_present_in_keys(self):
2085
2284
        # Lines from the basis are returned, and lines for a given key are only
2086
 
        # returned once. 
 
2285
        # returned once.
2087
2286
        key1 = ('foo1',)
2088
2287
        key2 = ('foo2',)
2089
2288
        # all sources are asked for keys:
2177
2376
        self.assertEqual(set([key_left, key_right]), set(last_call[1]))
2178
2377
        self.assertEqual('unordered', last_call[2])
2179
2378
        self.assertEqual(True, last_call[3])
 
2379
 
 
2380
 
 
2381
class TestNetworkBehaviour(KnitTests):
 
2382
    """Tests for getting data out of/into knits over the network."""
 
2383
 
 
2384
    def test_include_delta_closure_generates_a_knit_delta_closure(self):
 
2385
        vf = self.make_test_knit(name='test')
 
2386
        # put in three texts, giving ft, delta, delta
 
2387
        vf.add_lines(('base',), (), ['base\n', 'content\n'])
 
2388
        vf.add_lines(('d1',), (('base',),), ['d1\n'])
 
2389
        vf.add_lines(('d2',), (('d1',),), ['d2\n'])
 
2390
        # But heuristics could interfere, so check what happened:
 
2391
        self.assertEqual(['knit-ft-gz', 'knit-delta-gz', 'knit-delta-gz'],
 
2392
            [record.storage_kind for record in
 
2393
             vf.get_record_stream([('base',), ('d1',), ('d2',)],
 
2394
                'topological', False)])
 
2395
        # generate a stream of just the deltas include_delta_closure=True,
 
2396
        # serialise to the network, and check that we get a delta closure on the wire.
 
2397
        stream = vf.get_record_stream([('d1',), ('d2',)], 'topological', True)
 
2398
        netb = [record.get_bytes_as(record.storage_kind) for record in stream]
 
2399
        # The first bytes should be a memo from _ContentMapGenerator, and the
 
2400
        # second bytes should be empty (because its a API proxy not something
 
2401
        # for wire serialisation.
 
2402
        self.assertEqual('', netb[1])
 
2403
        bytes = netb[0]
 
2404
        kind, line_end = network_bytes_to_kind_and_offset(bytes)
 
2405
        self.assertEqual('knit-delta-closure', kind)
 
2406
 
 
2407
 
 
2408
class TestContentMapGenerator(KnitTests):
 
2409
    """Tests for ContentMapGenerator"""
 
2410
 
 
2411
    def test_get_record_stream_gives_records(self):
 
2412
        vf = self.make_test_knit(name='test')
 
2413
        # put in three texts, giving ft, delta, delta
 
2414
        vf.add_lines(('base',), (), ['base\n', 'content\n'])
 
2415
        vf.add_lines(('d1',), (('base',),), ['d1\n'])
 
2416
        vf.add_lines(('d2',), (('d1',),), ['d2\n'])
 
2417
        keys = [('d1',), ('d2',)]
 
2418
        generator = _VFContentMapGenerator(vf, keys,
 
2419
            global_map=vf.get_parent_map(keys))
 
2420
        for record in generator.get_record_stream():
 
2421
            if record.key == ('d1',):
 
2422
                self.assertEqual('d1\n', record.get_bytes_as('fulltext'))
 
2423
            else:
 
2424
                self.assertEqual('d2\n', record.get_bytes_as('fulltext'))
 
2425
 
 
2426
    def test_get_record_stream_kinds_are_raw(self):
 
2427
        vf = self.make_test_knit(name='test')
 
2428
        # put in three texts, giving ft, delta, delta
 
2429
        vf.add_lines(('base',), (), ['base\n', 'content\n'])
 
2430
        vf.add_lines(('d1',), (('base',),), ['d1\n'])
 
2431
        vf.add_lines(('d2',), (('d1',),), ['d2\n'])
 
2432
        keys = [('base',), ('d1',), ('d2',)]
 
2433
        generator = _VFContentMapGenerator(vf, keys,
 
2434
            global_map=vf.get_parent_map(keys))
 
2435
        kinds = {('base',): 'knit-delta-closure',
 
2436
            ('d1',): 'knit-delta-closure-ref',
 
2437
            ('d2',): 'knit-delta-closure-ref',
 
2438
            }
 
2439
        for record in generator.get_record_stream():
 
2440
            self.assertEqual(kinds[record.key], record.storage_kind)