~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_knit.py

  • Committer: Martin
  • Date: 2010-05-16 15:18:43 UTC
  • mfrom: (5235 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5239.
  • Revision ID: gzlist@googlemail.com-20100516151843-lu53u7caehm3ie3i
Merge bzr.dev to resolve conflicts in NEWS and _chk_map_pyx

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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
28
28
    multiparent,
29
29
    osutils,
30
30
    pack,
 
31
    tests,
31
32
    )
32
33
from bzrlib.errors import (
33
34
    RevisionAlreadyPresent,
69
70
    )
70
71
 
71
72
 
72
 
class _CompiledKnitFeature(Feature):
73
 
 
74
 
    def _probe(self):
75
 
        try:
76
 
            import bzrlib._knit_load_data_c
77
 
        except ImportError:
78
 
            return False
79
 
        return True
80
 
 
81
 
    def feature_name(self):
82
 
        return 'bzrlib._knit_load_data_c'
83
 
 
84
 
CompiledKnitFeature = _CompiledKnitFeature()
 
73
compiled_knit_feature = tests.ModuleAvailableFeature(
 
74
                            'bzrlib._knit_load_data_pyx')
85
75
 
86
76
 
87
77
class KnitContentTestsMixin(object):
366
356
        :return: (versioned_file, reload_counter)
367
357
            versioned_file  a KnitVersionedFiles using the packs for access
368
358
        """
369
 
        tree = self.make_branch_and_memory_tree('tree')
370
 
        tree.lock_write()
371
 
        self.addCleanup(tree.branch.repository.unlock)
372
 
        tree.add([''], ['root-id'])
373
 
        tree.commit('one', rev_id='rev-1')
374
 
        tree.commit('two', rev_id='rev-2')
375
 
        tree.commit('three', rev_id='rev-3')
 
359
        builder = self.make_branch_builder('.', format="1.9")
 
360
        builder.start_series()
 
361
        builder.build_snapshot('rev-1', None, [
 
362
            ('add', ('', 'root-id', 'directory', None)),
 
363
            ('add', ('file', 'file-id', 'file', 'content\nrev 1\n')),
 
364
            ])
 
365
        builder.build_snapshot('rev-2', ['rev-1'], [
 
366
            ('modify', ('file-id', 'content\nrev 2\n')),
 
367
            ])
 
368
        builder.build_snapshot('rev-3', ['rev-2'], [
 
369
            ('modify', ('file-id', 'content\nrev 3\n')),
 
370
            ])
 
371
        builder.finish_series()
 
372
        b = builder.get_branch()
 
373
        b.lock_write()
 
374
        self.addCleanup(b.unlock)
376
375
        # Pack these three revisions into another pack file, but don't remove
377
376
        # the originals
378
 
        repo = tree.branch.repository
 
377
        repo = b.repository
379
378
        collection = repo._pack_collection
380
379
        collection.ensure_loaded()
381
380
        orig_packs = collection.packs
384
383
        # forget about the new pack
385
384
        collection.reset()
386
385
        repo.refresh_data()
387
 
        vf = tree.branch.repository.revisions
388
 
        del tree
 
386
        vf = repo.revisions
389
387
        # Set up a reload() function that switches to using the new pack file
390
388
        new_index = new_pack.revision_index
391
389
        access_tuple = new_pack.access_tuple()
864
862
 
865
863
    def get_knit_index(self, transport, name, mode):
866
864
        mapper = ConstantMapper(name)
867
 
        orig = knit._load_data
868
 
        def reset():
869
 
            knit._load_data = orig
870
 
        self.addCleanup(reset)
871
865
        from bzrlib._knit_load_data_py import _load_data_py
872
 
        knit._load_data = _load_data_py
 
866
        self.overrideAttr(knit, '_load_data', _load_data_py)
873
867
        allow_writes = lambda: 'w' in mode
874
868
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
875
869
 
1300
1294
 
1301
1295
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
1302
1296
 
1303
 
    _test_needs_features = [CompiledKnitFeature]
 
1297
    _test_needs_features = [compiled_knit_feature]
1304
1298
 
1305
1299
    def get_knit_index(self, transport, name, mode):
1306
1300
        mapper = ConstantMapper(name)
1307
 
        orig = knit._load_data
1308
 
        def reset():
1309
 
            knit._load_data = orig
1310
 
        self.addCleanup(reset)
1311
 
        from bzrlib._knit_load_data_c import _load_data_c
1312
 
        knit._load_data = _load_data_c
 
1301
        from bzrlib._knit_load_data_pyx import _load_data_c
 
1302
        self.overrideAttr(knit, '_load_data', _load_data_c)
1313
1303
        allow_writes = lambda: mode == 'w'
1314
 
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
 
1304
        return _KndxIndex(transport, mapper, lambda:None,
 
1305
                          allow_writes, lambda:True)
 
1306
 
 
1307
 
 
1308
class Test_KnitAnnotator(TestCaseWithMemoryTransport):
 
1309
 
 
1310
    def make_annotator(self):
 
1311
        factory = knit.make_pack_factory(True, True, 1)
 
1312
        vf = factory(self.get_transport())
 
1313
        return knit._KnitAnnotator(vf)
 
1314
 
 
1315
    def test__expand_fulltext(self):
 
1316
        ann = self.make_annotator()
 
1317
        rev_key = ('rev-id',)
 
1318
        ann._num_compression_children[rev_key] = 1
 
1319
        res = ann._expand_record(rev_key, (('parent-id',),), None,
 
1320
                           ['line1\n', 'line2\n'], ('fulltext', True))
 
1321
        # The content object and text lines should be cached appropriately
 
1322
        self.assertEqual(['line1\n', 'line2'], res)
 
1323
        content_obj = ann._content_objects[rev_key]
 
1324
        self.assertEqual(['line1\n', 'line2\n'], content_obj._lines)
 
1325
        self.assertEqual(res, content_obj.text())
 
1326
        self.assertEqual(res, ann._text_cache[rev_key])
 
1327
 
 
1328
    def test__expand_delta_comp_parent_not_available(self):
 
1329
        # Parent isn't available yet, so we return nothing, but queue up this
 
1330
        # node for later processing
 
1331
        ann = self.make_annotator()
 
1332
        rev_key = ('rev-id',)
 
1333
        parent_key = ('parent-id',)
 
1334
        record = ['0,1,1\n', 'new-line\n']
 
1335
        details = ('line-delta', False)
 
1336
        res = ann._expand_record(rev_key, (parent_key,), parent_key,
 
1337
                                 record, details)
 
1338
        self.assertEqual(None, res)
 
1339
        self.assertTrue(parent_key in ann._pending_deltas)
 
1340
        pending = ann._pending_deltas[parent_key]
 
1341
        self.assertEqual(1, len(pending))
 
1342
        self.assertEqual((rev_key, (parent_key,), record, details), pending[0])
 
1343
 
 
1344
    def test__expand_record_tracks_num_children(self):
 
1345
        ann = self.make_annotator()
 
1346
        rev_key = ('rev-id',)
 
1347
        rev2_key = ('rev2-id',)
 
1348
        parent_key = ('parent-id',)
 
1349
        record = ['0,1,1\n', 'new-line\n']
 
1350
        details = ('line-delta', False)
 
1351
        ann._num_compression_children[parent_key] = 2
 
1352
        ann._expand_record(parent_key, (), None, ['line1\n', 'line2\n'],
 
1353
                           ('fulltext', False))
 
1354
        res = ann._expand_record(rev_key, (parent_key,), parent_key,
 
1355
                                 record, details)
 
1356
        self.assertEqual({parent_key: 1}, ann._num_compression_children)
 
1357
        # Expanding the second child should remove the content object, and the
 
1358
        # num_compression_children entry
 
1359
        res = ann._expand_record(rev2_key, (parent_key,), parent_key,
 
1360
                                 record, details)
 
1361
        self.assertFalse(parent_key in ann._content_objects)
 
1362
        self.assertEqual({}, ann._num_compression_children)
 
1363
        # We should not cache the content_objects for rev2 and rev, because
 
1364
        # they do not have compression children of their own.
 
1365
        self.assertEqual({}, ann._content_objects)
 
1366
 
 
1367
    def test__expand_delta_records_blocks(self):
 
1368
        ann = self.make_annotator()
 
1369
        rev_key = ('rev-id',)
 
1370
        parent_key = ('parent-id',)
 
1371
        record = ['0,1,1\n', 'new-line\n']
 
1372
        details = ('line-delta', True)
 
1373
        ann._num_compression_children[parent_key] = 2
 
1374
        ann._expand_record(parent_key, (), None,
 
1375
                           ['line1\n', 'line2\n', 'line3\n'],
 
1376
                           ('fulltext', False))
 
1377
        ann._expand_record(rev_key, (parent_key,), parent_key, record, details)
 
1378
        self.assertEqual({(rev_key, parent_key): [(1, 1, 1), (3, 3, 0)]},
 
1379
                         ann._matching_blocks)
 
1380
        rev2_key = ('rev2-id',)
 
1381
        record = ['0,1,1\n', 'new-line\n']
 
1382
        details = ('line-delta', False)
 
1383
        ann._expand_record(rev2_key, (parent_key,), parent_key, record, details)
 
1384
        self.assertEqual([(1, 1, 2), (3, 3, 0)],
 
1385
                         ann._matching_blocks[(rev2_key, parent_key)])
 
1386
 
 
1387
    def test__get_parent_ann_uses_matching_blocks(self):
 
1388
        ann = self.make_annotator()
 
1389
        rev_key = ('rev-id',)
 
1390
        parent_key = ('parent-id',)
 
1391
        parent_ann = [(parent_key,)]*3
 
1392
        block_key = (rev_key, parent_key)
 
1393
        ann._annotations_cache[parent_key] = parent_ann
 
1394
        ann._matching_blocks[block_key] = [(0, 1, 1), (3, 3, 0)]
 
1395
        # We should not try to access any parent_lines content, because we know
 
1396
        # we already have the matching blocks
 
1397
        par_ann, blocks = ann._get_parent_annotations_and_matches(rev_key,
 
1398
                                        ['1\n', '2\n', '3\n'], parent_key)
 
1399
        self.assertEqual(parent_ann, par_ann)
 
1400
        self.assertEqual([(0, 1, 1), (3, 3, 0)], blocks)
 
1401
        self.assertEqual({}, ann._matching_blocks)
 
1402
 
 
1403
    def test__process_pending(self):
 
1404
        ann = self.make_annotator()
 
1405
        rev_key = ('rev-id',)
 
1406
        p1_key = ('p1-id',)
 
1407
        p2_key = ('p2-id',)
 
1408
        record = ['0,1,1\n', 'new-line\n']
 
1409
        details = ('line-delta', False)
 
1410
        p1_record = ['line1\n', 'line2\n']
 
1411
        ann._num_compression_children[p1_key] = 1
 
1412
        res = ann._expand_record(rev_key, (p1_key,p2_key), p1_key,
 
1413
                                 record, details)
 
1414
        self.assertEqual(None, res)
 
1415
        # self.assertTrue(p1_key in ann._pending_deltas)
 
1416
        self.assertEqual({}, ann._pending_annotation)
 
1417
        # Now insert p1, and we should be able to expand the delta
 
1418
        res = ann._expand_record(p1_key, (), None, p1_record,
 
1419
                                 ('fulltext', False))
 
1420
        self.assertEqual(p1_record, res)
 
1421
        ann._annotations_cache[p1_key] = [(p1_key,)]*2
 
1422
        res = ann._process_pending(p1_key)
 
1423
        self.assertEqual([], res)
 
1424
        self.assertFalse(p1_key in ann._pending_deltas)
 
1425
        self.assertTrue(p2_key in ann._pending_annotation)
 
1426
        self.assertEqual({p2_key: [(rev_key, (p1_key, p2_key))]},
 
1427
                         ann._pending_annotation)
 
1428
        # Now fill in parent 2, and pending annotation should be satisfied
 
1429
        res = ann._expand_record(p2_key, (), None, [], ('fulltext', False))
 
1430
        ann._annotations_cache[p2_key] = []
 
1431
        res = ann._process_pending(p2_key)
 
1432
        self.assertEqual([rev_key], res)
 
1433
        self.assertEqual({}, ann._pending_annotation)
 
1434
        self.assertEqual({}, ann._pending_deltas)
 
1435
 
 
1436
    def test_record_delta_removes_basis(self):
 
1437
        ann = self.make_annotator()
 
1438
        ann._expand_record(('parent-id',), (), None,
 
1439
                           ['line1\n', 'line2\n'], ('fulltext', False))
 
1440
        ann._num_compression_children['parent-id'] = 2
 
1441
 
 
1442
    def test_annotate_special_text(self):
 
1443
        ann = self.make_annotator()
 
1444
        vf = ann._vf
 
1445
        rev1_key = ('rev-1',)
 
1446
        rev2_key = ('rev-2',)
 
1447
        rev3_key = ('rev-3',)
 
1448
        spec_key = ('special:',)
 
1449
        vf.add_lines(rev1_key, [], ['initial content\n'])
 
1450
        vf.add_lines(rev2_key, [rev1_key], ['initial content\n',
 
1451
                                            'common content\n',
 
1452
                                            'content in 2\n'])
 
1453
        vf.add_lines(rev3_key, [rev1_key], ['initial content\n',
 
1454
                                            'common content\n',
 
1455
                                            'content in 3\n'])
 
1456
        spec_text = ('initial content\n'
 
1457
                     'common content\n'
 
1458
                     'content in 2\n'
 
1459
                     'content in 3\n')
 
1460
        ann.add_special_text(spec_key, [rev2_key, rev3_key], spec_text)
 
1461
        anns, lines = ann.annotate(spec_key)
 
1462
        self.assertEqual([(rev1_key,),
 
1463
                          (rev2_key, rev3_key),
 
1464
                          (rev2_key,),
 
1465
                          (rev3_key,),
 
1466
                         ], anns)
 
1467
        self.assertEqualDiff(spec_text, ''.join(lines))
1315
1468
 
1316
1469
 
1317
1470
class KnitTests(TestCaseWithTransport):
1637
1790
              ([('missing-parent', ), ('ghost', )], [('missing-parent', )]))])
1638
1791
        return graph_index
1639
1792
 
 
1793
    def make_g_index_missing_parent(self):
 
1794
        graph_index = self.make_g_index('missing_parent', 2,
 
1795
            [(('parent', ), ' 100 78', ([], [])),
 
1796
             (('tip', ), ' 100 78',
 
1797
              ([('parent', ), ('missing-parent', )], [('parent', )])),
 
1798
              ])
 
1799
        return graph_index
 
1800
 
1640
1801
    def make_g_index_no_external_refs(self):
1641
1802
        graph_index = self.make_g_index('no_external_refs', 2,
1642
1803
            [(('rev', ), ' 100 78',
1650
1811
        index.scan_unvalidated_index(unvalidated)
1651
1812
        self.assertEqual(frozenset(), index.get_missing_compression_parents())
1652
1813
 
1653
 
    def test_add_incomplete_unvalidated_index(self):
 
1814
    def test_add_missing_compression_parent_unvalidated_index(self):
1654
1815
        unvalidated = self.make_g_index_missing_compression_parent()
1655
1816
        combined = CombinedGraphIndex([unvalidated])
1656
1817
        index = _KnitGraphIndex(combined, lambda: True, deltas=True)
1662
1823
            frozenset([('missing-parent',)]),
1663
1824
            index.get_missing_compression_parents())
1664
1825
 
 
1826
    def test_add_missing_noncompression_parent_unvalidated_index(self):
 
1827
        unvalidated = self.make_g_index_missing_parent()
 
1828
        combined = CombinedGraphIndex([unvalidated])
 
1829
        index = _KnitGraphIndex(combined, lambda: True, deltas=True,
 
1830
            track_external_parent_refs=True)
 
1831
        index.scan_unvalidated_index(unvalidated)
 
1832
        self.assertEqual(
 
1833
            frozenset([('missing-parent',)]), index.get_missing_parents())
 
1834
 
 
1835
    def test_track_external_parent_refs(self):
 
1836
        g_index = self.make_g_index('empty', 2, [])
 
1837
        combined = CombinedGraphIndex([g_index])
 
1838
        index = _KnitGraphIndex(combined, lambda: True, deltas=True,
 
1839
            add_callback=self.catch_add, track_external_parent_refs=True)
 
1840
        self.caught_entries = []
 
1841
        index.add_records([
 
1842
            (('new-key',), 'fulltext,no-eol', (None, 50, 60),
 
1843
             [('parent-1',), ('parent-2',)])])
 
1844
        self.assertEqual(
 
1845
            frozenset([('parent-1',), ('parent-2',)]),
 
1846
            index.get_missing_parents())
 
1847
 
1665
1848
    def test_add_unvalidated_index_with_present_external_references(self):
1666
1849
        index = self.two_graph_index(deltas=True)
1667
1850
        # Ugly hack to get at one of the underlying GraphIndex objects that
2030
2213
        # self.assertEqual([("annotate", key_basis)], basis.calls)
2031
2214
        self.assertEqual([('get_parent_map', set([key_basis])),
2032
2215
            ('get_parent_map', set([key_basis])),
2033
 
            ('get_parent_map', set([key_basis])),
2034
 
            ('get_record_stream', [key_basis], 'unordered', True)],
 
2216
            ('get_record_stream', [key_basis], 'topological', True)],
2035
2217
            basis.calls)
2036
2218
 
2037
2219
    def test_check(self):
2143
2325
        # ask which fallbacks have which parents.
2144
2326
        self.assertEqual([
2145
2327
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
2146
 
            # unordered is asked for by the underlying worker as it still
2147
 
            # buffers everything while answering - which is a problem!
2148
 
            ("get_record_stream", [key_basis_2, key_basis], 'unordered', True)],
 
2328
            # topological is requested from the fallback, because that is what
 
2329
            # was requested at the top level.
 
2330
            ("get_record_stream", [key_basis_2, key_basis], 'topological', True)],
2149
2331
            calls)
2150
2332
 
2151
2333
    def test_get_record_stream_unordered_deltas(self):
2372
2554
        last_call = basis.calls[-1]
2373
2555
        self.assertEqual('get_record_stream', last_call[0])
2374
2556
        self.assertEqual(set([key_left, key_right]), set(last_call[1]))
2375
 
        self.assertEqual('unordered', last_call[2])
 
2557
        self.assertEqual('topological', last_call[2])
2376
2558
        self.assertEqual(True, last_call[3])
2377
2559
 
2378
2560