/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

Merge with stacked-fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
from bzrlib.index import *
38
38
from bzrlib.knit import (
39
39
    AnnotatedKnitContent,
 
40
    DATA_SUFFIX,
 
41
    INDEX_SUFFIX,
40
42
    KnitContent,
41
43
    KnitGraphIndex,
42
44
    KnitVersionedFile,
45
47
    _KnitAccess,
46
48
    _KnitData,
47
49
    _KnitIndex,
 
50
    make_file_knit,
48
51
    _PackAccess,
49
52
    PlainKnitContent,
50
53
    _StreamAccess,
53
56
    KnitSequenceMatcher,
54
57
    )
55
58
from bzrlib.osutils import split_lines
 
59
from bzrlib.symbol_versioning import one_four
56
60
from bzrlib.tests import (
57
61
    Feature,
58
62
    TestCase,
158
162
        self.assertEqual(content.annotate(),
159
163
            [("bogus", "text1"), ("bogus", "text2")])
160
164
 
161
 
    def test_annotate_iter(self):
162
 
        content = self._make_content([])
163
 
        it = content.annotate_iter()
164
 
        self.assertRaises(StopIteration, it.next)
165
 
 
166
 
        content = self._make_content([("bogus", "text1"), ("bogus", "text2")])
167
 
        it = content.annotate_iter()
168
 
        self.assertEqual(it.next(), ("bogus", "text1"))
169
 
        self.assertEqual(it.next(), ("bogus", "text2"))
170
 
        self.assertRaises(StopIteration, it.next)
171
 
 
172
165
    def test_line_delta(self):
173
166
        content1 = self._make_content([("", "a"), ("", "b")])
174
167
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
196
189
        self.assertEqual(content.annotate(),
197
190
            [("origin1", "text1"), ("origin2", "text2")])
198
191
 
199
 
    def test_annotate_iter(self):
200
 
        content = self._make_content([])
201
 
        it = content.annotate_iter()
202
 
        self.assertRaises(StopIteration, it.next)
203
 
 
204
 
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
205
 
        it = content.annotate_iter()
206
 
        self.assertEqual(it.next(), ("origin1", "text1"))
207
 
        self.assertEqual(it.next(), ("origin2", "text2"))
208
 
        self.assertRaises(StopIteration, it.next)
209
 
 
210
192
    def test_line_delta(self):
211
193
        content1 = self._make_content([("", "a"), ("", "b")])
212
194
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
388
370
        self.assertEqual({'rev-id-1':(['foo\n', 'bar\n'], sha1sum)}, contents)
389
371
 
390
372
        raw_contents = list(data.read_records_iter_raw(records))
391
 
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
 
373
        self.assertEqual([('rev-id-1', gz_txt, sha1sum)], raw_contents)
392
374
 
393
375
    def test_not_enough_lines(self):
394
376
        sha1sum = sha.new('foo\n').hexdigest()
405
387
 
406
388
        # read_records_iter_raw won't detect that sort of mismatch/corruption
407
389
        raw_contents = list(data.read_records_iter_raw(records))
408
 
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
 
390
        self.assertEqual([('rev-id-1', gz_txt, sha1sum)], raw_contents)
409
391
 
410
392
    def test_too_many_lines(self):
411
393
        sha1sum = sha.new('foo\nbar\n').hexdigest()
423
405
 
424
406
        # read_records_iter_raw won't detect that sort of mismatch/corruption
425
407
        raw_contents = list(data.read_records_iter_raw(records))
426
 
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
 
408
        self.assertEqual([('rev-id-1', gz_txt, sha1sum)], raw_contents)
427
409
 
428
410
    def test_mismatched_version_id(self):
429
411
        sha1sum = sha.new('foo\nbar\n').hexdigest()
492
474
        self.addCleanup(reset)
493
475
        from bzrlib._knit_load_data_py import _load_data_py
494
476
        knit._load_data = _load_data_py
495
 
        return _KnitIndex(*args, **kwargs)
 
477
        return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
496
478
 
497
479
    def test_no_such_file(self):
498
480
        transport = MockTransport()
554
536
            "version option 0 1 .%s :" % (utf8_revision_id,)
555
537
            ])
556
538
        index = self.get_knit_index(transport, "filename", "r")
557
 
        self.assertEqual([utf8_revision_id],
 
539
        self.assertEqual((utf8_revision_id,),
558
540
            index.get_parents_with_ghosts("version"))
559
541
 
560
542
    def test_read_ignore_corrupted_lines(self):
588
570
        self.assertEqual("1", index._version_list_to_index(["version"]))
589
571
        self.assertEqual((None, 3, 4), index.get_position("version"))
590
572
        self.assertEqual(["options3"], index.get_options("version"))
591
 
        self.assertEqual(["parent", "other"],
 
573
        self.assertEqual(("parent", "other"),
592
574
            index.get_parents_with_ghosts("version"))
593
575
 
594
576
    def test_read_compressed_parents(self):
599
581
            "c option 0 1 1 0 :",
600
582
            ])
601
583
        index = self.get_knit_index(transport, "filename", "r")
602
 
        self.assertEqual(["a"], index.get_parents("b"))
603
 
        self.assertEqual(["b", "a"], index.get_parents("c"))
 
584
        self.assertEqual({"b":("a",), "c":("b", "a")},
 
585
            index.get_parent_map(["b", "c"]))
604
586
 
605
587
    def test_write_utf8_version_id(self):
606
588
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
628
610
            {}),
629
611
            transport.calls.pop(0))
630
612
 
631
 
    def test_get_graph(self):
632
 
        transport = MockTransport()
633
 
        index = self.get_knit_index(transport, "filename", "w", create=True)
634
 
        self.assertEqual([], index.get_graph())
635
 
 
636
 
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
637
 
        self.assertEqual([("a", ["b"])], index.get_graph())
638
 
 
639
 
        index.add_version("c", ["option"], (None, 0, 1), ["d"])
640
 
        self.assertEqual([("a", ["b"]), ("c", ["d"])],
641
 
            sorted(index.get_graph()))
642
 
 
643
613
    def test_get_ancestry(self):
644
614
        transport = MockTransport([
645
615
            _KnitIndex.HEADER,
689
659
        self.assertRaises(RevisionNotPresent,
690
660
            index.get_ancestry_with_ghosts, ["e"])
691
661
 
692
 
    def test_iter_parents(self):
693
 
        transport = MockTransport()
694
 
        index = self.get_knit_index(transport, "filename", "w", create=True)
695
 
        # no parents
696
 
        index.add_version('r0', ['option'], (None, 0, 1), [])
697
 
        # 1 parent
698
 
        index.add_version('r1', ['option'], (None, 0, 1), ['r0'])
699
 
        # 2 parents
700
 
        index.add_version('r2', ['option'], (None, 0, 1), ['r1', 'r0'])
701
 
        # XXX TODO a ghost
702
 
        # cases: each sample data individually:
703
 
        self.assertEqual(set([('r0', ())]),
704
 
            set(index.iter_parents(['r0'])))
705
 
        self.assertEqual(set([('r1', ('r0', ))]),
706
 
            set(index.iter_parents(['r1'])))
707
 
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
708
 
            set(index.iter_parents(['r2'])))
709
 
        # no nodes returned for a missing node
710
 
        self.assertEqual(set(),
711
 
            set(index.iter_parents(['missing'])))
712
 
        # 1 node returned with missing nodes skipped
713
 
        self.assertEqual(set([('r1', ('r0', ))]),
714
 
            set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
715
 
        # 2 nodes returned
716
 
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
717
 
            set(index.iter_parents(['r0', 'r1'])))
718
 
        # 2 nodes returned, missing skipped
719
 
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
720
 
            set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
721
 
 
722
662
    def test_num_versions(self):
723
663
        transport = MockTransport([
724
664
            _KnitIndex.HEADER
771
711
        self.assertEqual(1, index.num_versions())
772
712
        self.assertEqual((None, 0, 1), index.get_position("a"))
773
713
        self.assertEqual(["option"], index.get_options("a"))
774
 
        self.assertEqual(["b"], index.get_parents_with_ghosts("a"))
 
714
        self.assertEqual(("b",), index.get_parents_with_ghosts("a"))
775
715
 
776
716
        index.add_version("a", ["opt"], (None, 1, 2), ["c"])
777
717
        self.assertEqual(("append_bytes",
781
721
        self.assertEqual(1, index.num_versions())
782
722
        self.assertEqual((None, 1, 2), index.get_position("a"))
783
723
        self.assertEqual(["opt"], index.get_options("a"))
784
 
        self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
 
724
        self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
785
725
 
786
726
        index.add_version("b", ["option"], (None, 2, 3), ["a"])
787
727
        self.assertEqual(("append_bytes",
791
731
        self.assertEqual(2, index.num_versions())
792
732
        self.assertEqual((None, 2, 3), index.get_position("b"))
793
733
        self.assertEqual(["option"], index.get_options("b"))
794
 
        self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
 
734
        self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
795
735
 
796
736
    def test_add_versions(self):
797
737
        transport = MockTransport([
816
756
        self.assertEqual((None, 2, 3), index.get_position("b"))
817
757
        self.assertEqual(["opt"], index.get_options("a"))
818
758
        self.assertEqual(["option"], index.get_options("b"))
819
 
        self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
820
 
        self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
 
759
        self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
 
760
        self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
821
761
 
822
762
    def test_add_versions_random_id_is_accepted(self):
823
763
        transport = MockTransport([
902
842
        self.assertEqual(["opt1"], index.get_options("a"))
903
843
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
904
844
 
905
 
    def test_get_parents(self):
 
845
    def test_get_parent_map(self):
906
846
        transport = MockTransport([
907
847
            _KnitIndex.HEADER,
908
848
            "a option 0 1 :",
911
851
            ])
912
852
        index = self.get_knit_index(transport, "filename", "r")
913
853
 
914
 
        self.assertEqual([], index.get_parents("a"))
915
 
        self.assertEqual(["a", "c"], index.get_parents("b"))
916
 
        self.assertEqual(["b", "a"], index.get_parents("c"))
 
854
        self.assertEqual({
 
855
            "a":(),
 
856
            "b":("a", "c"),
 
857
            "c":("b", "a", "e"),
 
858
            }, index.get_parent_map(["a", "b", "c"]))
917
859
 
918
860
    def test_get_parents_with_ghosts(self):
919
861
        transport = MockTransport([
924
866
            ])
925
867
        index = self.get_knit_index(transport, "filename", "r")
926
868
 
927
 
        self.assertEqual([], index.get_parents_with_ghosts("a"))
928
 
        self.assertEqual(["a", "c"], index.get_parents_with_ghosts("b"))
929
 
        self.assertEqual(["b", "a", "e"],
 
869
        self.assertEqual((), index.get_parents_with_ghosts("a"))
 
870
        self.assertEqual(("a", "c"), index.get_parents_with_ghosts("b"))
 
871
        self.assertEqual(("b", "a", "e"),
930
872
            index.get_parents_with_ghosts("c"))
931
873
 
932
874
    def test_check_versions_present(self):
1085
1027
        self.addCleanup(reset)
1086
1028
        from bzrlib._knit_load_data_c import _load_data_c
1087
1029
        knit._load_data = _load_data_c
1088
 
        return _KnitIndex(*args, **kwargs)
1089
 
 
 
1030
        return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
1090
1031
 
1091
1032
 
1092
1033
class KnitTests(TestCaseWithTransport):
1093
1034
    """Class containing knit test helper routines."""
1094
1035
 
1095
1036
    def make_test_knit(self, annotate=False, delay_create=False, index=None,
1096
 
                       name='test'):
 
1037
                       name='test', delta=True, access_mode='w'):
1097
1038
        if not annotate:
1098
1039
            factory = KnitPlainFactory()
1099
1040
        else:
1100
1041
            factory = None
1101
 
        return KnitVersionedFile(name, get_transport('.'), access_mode='w',
1102
 
                                 factory=factory, create=True,
1103
 
                                 delay_create=delay_create, index=index)
 
1042
        if index is None:
 
1043
            index = _KnitIndex(get_transport('.'), name + INDEX_SUFFIX,
 
1044
                access_mode, create=True, file_mode=None,
 
1045
                create_parent_dir=False, delay_create=delay_create,
 
1046
                dir_mode=None, get_scope=lambda:None)
 
1047
        access = _KnitAccess(get_transport('.'), name + DATA_SUFFIX, None,
 
1048
            None, delay_create, False)
 
1049
        return KnitVersionedFile(name, get_transport('.'), factory=factory,
 
1050
            create=True, delay_create=delay_create, index=index,
 
1051
            access_method=access, delta=delta)
1104
1052
 
1105
1053
    def assertRecordContentEqual(self, knit, version_id, candidate_content):
1106
1054
        """Assert that some raw record content matches the raw record content
1108
1056
        """
1109
1057
        index_memo = knit._index.get_position(version_id)
1110
1058
        record = (version_id, index_memo)
1111
 
        [(_, expected_content)] = list(knit._data.read_records_iter_raw([record]))
 
1059
        [(_, expected_content, _)] = list(knit._data.read_records_iter_raw([record]))
1112
1060
        self.assertEqual(expected_content, candidate_content)
1113
1061
 
1114
1062
 
1125
1073
    def test_make_explicit_index(self):
1126
1074
        """We can supply an index to use."""
1127
1075
        knit = KnitVersionedFile('test', get_transport('.'),
1128
 
            index='strangelove')
 
1076
            index='strangelove', access_method="a")
1129
1077
        self.assertEqual(knit._index, 'strangelove')
1130
1078
 
1131
1079
    def test_knit_add(self):
1172
1120
        k = self.make_test_knit()
1173
1121
        k.add_lines('text-1', [], split_lines(TEXT_1))
1174
1122
        del k
1175
 
        k2 = KnitVersionedFile('test', get_transport('.'), access_mode='r', factory=KnitPlainFactory(), create=True)
 
1123
        k2 = make_file_knit('test', get_transport('.'), access_mode='r',
 
1124
            factory=KnitPlainFactory(), create=True)
1176
1125
        self.assertTrue(k2.has_version('text-1'))
1177
1126
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
1178
1127
 
1200
1149
    def test_incomplete(self):
1201
1150
        """Test if texts without a ending line-end can be inserted and
1202
1151
        extracted."""
1203
 
        k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
 
1152
        k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1204
1153
        k.add_lines('text-1', [], ['a\n',    'b'  ])
1205
1154
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1206
1155
        # reopening ensures maximum room for confusion
1207
 
        k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
 
1156
        k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1208
1157
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
1209
1158
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
1210
1159
 
1221
1170
        """Store in knit with parents"""
1222
1171
        k = self.make_test_knit()
1223
1172
        self.add_stock_one_and_one_a(k)
1224
 
        self.assertEquals(k.get_parents('text-1'), [])
1225
 
        self.assertEquals(k.get_parents('text-1a'), ['text-1'])
 
1173
        self.assertEqual({'text-1':(), 'text-1a':('text-1',)},
 
1174
            k.get_parent_map(['text-1', 'text-1a']))
1226
1175
 
1227
1176
    def test_ancestry(self):
1228
1177
        """Store in knit with parents"""
1232
1181
 
1233
1182
    def test_add_delta(self):
1234
1183
        """Store in knit with parents"""
1235
 
        k = KnitVersionedFile('test', get_transport('.'), factory=KnitPlainFactory(),
1236
 
            delta=True, create=True)
 
1184
        k = self.make_test_knit(annotate=False)
1237
1185
        self.add_stock_one_and_one_a(k)
1238
 
        k.clear_cache()
1239
1186
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1240
1187
 
1241
1188
    def test_add_delta_knit_graph_index(self):
1243
1190
        index = InMemoryGraphIndex(2)
1244
1191
        knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
1245
1192
            deltas=True)
1246
 
        k = KnitVersionedFile('test', get_transport('.'),
1247
 
            delta=True, create=True, index=knit_index)
 
1193
        k = self.make_test_knit(annotate=True, index=knit_index)
1248
1194
        self.add_stock_one_and_one_a(k)
1249
 
        k.clear_cache()
1250
1195
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1251
1196
        # check the index had the right data added.
1252
1197
        self.assertEqual(set([
1258
1203
 
1259
1204
    def test_annotate(self):
1260
1205
        """Annotations"""
1261
 
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1262
 
            delta=True, create=True)
 
1206
        k = self.make_test_knit(annotate=True, name='knit')
1263
1207
        self.insert_and_test_small_annotate(k)
1264
1208
 
1265
1209
    def insert_and_test_small_annotate(self, k):
1273
1217
 
1274
1218
    def test_annotate_fulltext(self):
1275
1219
        """Annotations"""
1276
 
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1277
 
            delta=False, create=True)
 
1220
        k = self.make_test_knit(annotate=True, name='knit', delta=False)
1278
1221
        self.insert_and_test_small_annotate(k)
1279
1222
 
1280
1223
    def test_annotate_merge_1(self):
1350
1293
        self.assertEquals(origins[1], ('text-1', 'b\n'))
1351
1294
        self.assertEquals(origins[2], ('text-1', 'c\n'))
1352
1295
 
1353
 
    def _test_join_with_factories(self, k1_factory, k2_factory):
1354
 
        k1 = KnitVersionedFile('test1', get_transport('.'), factory=k1_factory, create=True)
1355
 
        k1.add_lines('text-a', [], ['a1\n', 'a2\n', 'a3\n'])
1356
 
        k1.add_lines('text-b', ['text-a'], ['a1\n', 'b2\n', 'a3\n'])
1357
 
        k1.add_lines('text-c', [], ['c1\n', 'c2\n', 'c3\n'])
1358
 
        k1.add_lines('text-d', ['text-c'], ['c1\n', 'd2\n', 'd3\n'])
1359
 
        k1.add_lines('text-m', ['text-b', 'text-d'], ['a1\n', 'b2\n', 'd3\n'])
1360
 
        k2 = KnitVersionedFile('test2', get_transport('.'), factory=k2_factory, create=True)
1361
 
        count = k2.join(k1, version_ids=['text-m'])
1362
 
        self.assertEquals(count, 5)
1363
 
        self.assertTrue(k2.has_version('text-a'))
1364
 
        self.assertTrue(k2.has_version('text-c'))
1365
 
        origins = k2.annotate('text-m')
1366
 
        self.assertEquals(origins[0], ('text-a', 'a1\n'))
1367
 
        self.assertEquals(origins[1], ('text-b', 'b2\n'))
1368
 
        self.assertEquals(origins[2], ('text-d', 'd3\n'))
1369
 
 
1370
 
    def test_knit_join_plain_to_plain(self):
1371
 
        """Test joining a plain knit with a plain knit."""
1372
 
        self._test_join_with_factories(KnitPlainFactory(), KnitPlainFactory())
1373
 
 
1374
 
    def test_knit_join_anno_to_anno(self):
1375
 
        """Test joining an annotated knit with an annotated knit."""
1376
 
        self._test_join_with_factories(None, None)
1377
 
 
1378
 
    def test_knit_join_anno_to_plain(self):
1379
 
        """Test joining an annotated knit with a plain knit."""
1380
 
        self._test_join_with_factories(None, KnitPlainFactory())
1381
 
 
1382
 
    def test_knit_join_plain_to_anno(self):
1383
 
        """Test joining a plain knit with an annotated knit."""
1384
 
        self._test_join_with_factories(KnitPlainFactory(), None)
1385
 
 
1386
1296
    def test_reannotate(self):
1387
 
        k1 = KnitVersionedFile('knit1', get_transport('.'),
 
1297
        k1 = make_file_knit('knit1', get_transport('.'),
1388
1298
                               factory=KnitAnnotateFactory(), create=True)
1389
1299
        # 0
1390
1300
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
1391
1301
        # 1
1392
1302
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
1393
1303
 
1394
 
        k2 = KnitVersionedFile('test2', get_transport('.'),
 
1304
        k2 = make_file_knit('test2', get_transport('.'),
1395
1305
                               factory=KnitAnnotateFactory(), create=True)
1396
 
        k2.join(k1, version_ids=['text-b'])
 
1306
        k2.insert_record_stream(k1.get_record_stream(k1.versions(),
 
1307
            'unordered', False))
1397
1308
 
1398
1309
        # 2
1399
1310
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
1403
1314
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
1404
1315
 
1405
1316
        # test-c will have index 3
1406
 
        k1.join(k2, version_ids=['text-c'])
 
1317
        k1.insert_record_stream(k2.get_record_stream(['text-c'],
 
1318
            'unordered', False))
1407
1319
 
1408
1320
        lines = k1.get_lines('text-c')
1409
1321
        self.assertEquals(lines, ['z\n', 'c\n'])
1414
1326
 
1415
1327
    def test_get_line_delta_texts(self):
1416
1328
        """Make sure we can call get_texts on text with reused line deltas"""
1417
 
        k1 = KnitVersionedFile('test1', get_transport('.'), 
1418
 
                               factory=KnitPlainFactory(), create=True)
 
1329
        k1 = make_file_knit('test1', get_transport('.'),
 
1330
            factory=KnitPlainFactory(), create=True)
1419
1331
        for t in range(3):
1420
1332
            if t == 0:
1421
1333
                parents = []
1426
1338
        
1427
1339
    def test_iter_lines_reads_in_order(self):
1428
1340
        instrumented_t = get_transport('trace+memory:///')
1429
 
        k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
 
1341
        k1 = make_file_knit('id', instrumented_t, create=True, delta=True)
1430
1342
        self.assertEqual([('get', 'id.kndx',)], instrumented_t._activity)
1431
1343
        # add texts with no required ordering
1432
1344
        k1.add_lines('base', [], ['text\n'])
1433
1345
        k1.add_lines('base2', [], ['text2\n'])
1434
 
        k1.clear_cache()
1435
1346
        # clear the logged activity, but preserve the list instance in case of
1436
1347
        # clones pointing at it.
1437
1348
        del instrumented_t._activity[:]
1443
1354
            instrumented_t._activity)
1444
1355
        self.assertEqual([('text\n', 'base'), ('text2\n', 'base2')], results)
1445
1356
 
1446
 
    def test_create_empty_annotated(self):
1447
 
        k1 = self.make_test_knit(True)
1448
 
        # 0
1449
 
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
1450
 
        k2 = k1.create_empty('t', MemoryTransport())
1451
 
        self.assertTrue(isinstance(k2.factory, KnitAnnotateFactory))
1452
 
        self.assertEqual(k1.delta, k2.delta)
1453
 
        # the generic test checks for empty content and file class
1454
 
 
1455
1357
    def test_knit_format(self):
1456
1358
        # this tests that a new knit index file has the expected content
1457
1359
        # and that is writes the data we expect as records are added.
1471
1373
            "\nrevid2 line-delta 84 82 0 :",
1472
1374
            'test.kndx')
1473
1375
        # we should be able to load this file again
1474
 
        knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
 
1376
        knit = make_file_knit('test', get_transport('.'), access_mode='r')
1475
1377
        self.assertEqual(['revid', 'revid2'], knit.versions())
1476
1378
        # write a short write to the file and ensure that its ignored
1477
1379
        indexfile = file('test.kndx', 'ab')
1478
1380
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1479
1381
        indexfile.close()
1480
1382
        # we should be able to load this file again
1481
 
        knit = KnitVersionedFile('test', get_transport('.'), access_mode='w')
 
1383
        knit = make_file_knit('test', get_transport('.'), access_mode='w')
1482
1384
        self.assertEqual(['revid', 'revid2'], knit.versions())
1483
1385
        # and add a revision with the same id the failed write had
1484
1386
        knit.add_lines('revid3', ['revid2'], ['a\n'])
1485
1387
        # and when reading it revid3 should now appear.
1486
 
        knit = KnitVersionedFile('test', get_transport('.'), access_mode='r')
 
1388
        knit = make_file_knit('test', get_transport('.'), access_mode='r')
1487
1389
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
1488
 
        self.assertEqual(['revid2'], knit.get_parents('revid3'))
 
1390
        self.assertEqual({'revid3':('revid2',)}, knit.get_parent_map(['revid3']))
1489
1391
 
1490
1392
    def test_delay_create(self):
1491
1393
        """Test that passing delay_create=True creates files late"""
1504
1406
        """create_parent_dir can create knits in nonexistant dirs"""
1505
1407
        # Has no effect if we don't set 'delay_create'
1506
1408
        trans = get_transport('.')
1507
 
        self.assertRaises(NoSuchFile, KnitVersionedFile, 'dir/test',
 
1409
        self.assertRaises(NoSuchFile, make_file_knit, 'dir/test',
1508
1410
                          trans, access_mode='w', factory=None,
1509
1411
                          create=True, create_parent_dir=True)
1510
1412
        # Nothing should have changed yet
1511
 
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
 
1413
        knit = make_file_knit('dir/test', trans, access_mode='w',
1512
1414
                                 factory=None, create=True,
1513
1415
                                 create_parent_dir=True,
1514
1416
                                 delay_create=True)
1529
1431
        if not trans._can_roundtrip_unix_modebits():
1530
1432
            # Can't roundtrip, so no need to run this test
1531
1433
            return
1532
 
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1533
 
                                 factory=None, create=True,
1534
 
                                 create_parent_dir=True,
1535
 
                                 delay_create=True,
1536
 
                                 file_mode=0600,
1537
 
                                 dir_mode=0700)
 
1434
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
 
1435
            create=True, create_parent_dir=True, delay_create=True,
 
1436
            file_mode=0600, dir_mode=0700)
1538
1437
        knit.add_lines('revid', [], ['a\n'])
1539
1438
        self.assertTransportMode(trans, 'dir', 0700)
1540
1439
        self.assertTransportMode(trans, 'dir/test.knit', 0600)
1545
1444
        if not trans._can_roundtrip_unix_modebits():
1546
1445
            # Can't roundtrip, so no need to run this test
1547
1446
            return
1548
 
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1549
 
                                 factory=None, create=True,
1550
 
                                 create_parent_dir=True,
1551
 
                                 delay_create=True,
1552
 
                                 file_mode=0660,
1553
 
                                 dir_mode=0770)
 
1447
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
 
1448
            create=True, create_parent_dir=True, delay_create=True,
 
1449
            file_mode=0660, dir_mode=0770)
1554
1450
        knit.add_lines('revid', [], ['a\n'])
1555
1451
        self.assertTransportMode(trans, 'dir', 0770)
1556
1452
        self.assertTransportMode(trans, 'dir/test.knit', 0660)
1561
1457
        if not trans._can_roundtrip_unix_modebits():
1562
1458
            # Can't roundtrip, so no need to run this test
1563
1459
            return
1564
 
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1565
 
                                 factory=None, create=True,
1566
 
                                 create_parent_dir=True,
1567
 
                                 delay_create=True,
1568
 
                                 file_mode=0666,
1569
 
                                 dir_mode=0777)
 
1460
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
 
1461
            create=True, create_parent_dir=True, delay_create=True,
 
1462
            file_mode=0666, dir_mode=0777)
1570
1463
        knit.add_lines('revid', [], ['a\n'])
1571
1464
        self.assertTransportMode(trans, 'dir', 0777)
1572
1465
        self.assertTransportMode(trans, 'dir/test.knit', 0666)
1581
1474
        for plan_line, expected_line in zip(plan, AB_MERGE):
1582
1475
            self.assertEqual(plan_line, expected_line)
1583
1476
 
 
1477
 
 
1478
class GetDataStreamTests(KnitTests):
 
1479
    """Tests for get_data_stream."""
 
1480
 
1584
1481
    def test_get_stream_empty(self):
1585
1482
        """Get a data stream for an empty knit file."""
1586
1483
        k1 = self.make_test_knit()
1601
1498
            ]
1602
1499
        expected_data_list = [
1603
1500
            # version, options, length, parents
1604
 
            ('text-a', ['fulltext'], 122, []),
 
1501
            ('text-a', ['fulltext'], 122, ()),
1605
1502
           ]
1606
1503
        for version_id, parents, lines in test_data:
1607
1504
            k1.add_lines(version_id, parents, split_lines(lines))
1630
1527
            ]
1631
1528
        expected_data_list = [
1632
1529
            # version, options, length, parents
1633
 
            ('text-m', ['line-delta'], 84, ['text-b', 'text-d']),
 
1530
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1634
1531
            ]
1635
1532
        for version_id, parents, lines in test_data:
1636
1533
            k1.add_lines(version_id, parents, split_lines(lines))
1660
1557
        original_versions = k1.versions
1661
1558
        k1.versions = lambda: reversed(original_versions())
1662
1559
        expected_data_list = [
1663
 
            ('text-a', ['fulltext'], 122, []),
1664
 
            ('text-b', ['line-delta'], 84, ['text-a'])]
 
1560
            ('text-a', ['fulltext'], 122, ()),
 
1561
            ('text-b', ['line-delta'], 84, ('text-a',))]
1665
1562
        # now check the fulltext is first and the delta second
1666
1563
        format, data_list, _ = k1.get_data_stream(['text-a', 'text-b'])
1667
1564
        self.assertEqual('knit-plain', format)
1673
1570
        format, data_list, _ = k1.get_data_stream([
1674
1571
            'text-m', 'text-b', 'text-a'])
1675
1572
        self.assertEqual([
1676
 
            ('text-a', ['fulltext'], 122, []),
1677
 
            ('text-b', ['line-delta'], 84, ['text-a']),
1678
 
            ('text-m', ['line-delta'], 84, ['text-b', 'text-d']),
 
1573
            ('text-a', ['fulltext'], 122, ()),
 
1574
            ('text-b', ['line-delta'], 84, ('text-a',)),
 
1575
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1679
1576
            ], data_list)
1680
1577
 
1681
1578
    def test_get_stream_ghost_parent(self):
1688
1585
        # Expected data
1689
1586
        expected_data_list = [
1690
1587
            # version, options, length, parents
1691
 
            ('text-b', ['line-delta'], 84, ['text-a', 'text-ghost']),
 
1588
            ('text-b', ['line-delta'], 84, ('text-a', 'text-ghost')),
1692
1589
            ]
1693
1590
        
1694
1591
        format, data_list, reader_callable = k1.get_data_stream(['text-b'])
1716
1613
        # behaviour.
1717
1614
        expected_data_list = [
1718
1615
            # version, options, length, parents
1719
 
            ('text-d', ['line-delta'], 84, ['text-c']),
1720
 
            ('text-b', ['line-delta'], 84, ['text-a']),
 
1616
            ('text-d', ['line-delta'], 84, ('text-c',)),
 
1617
            ('text-b', ['line-delta'], 84, ('text-a',)),
1721
1618
            ]
1722
1619
        # Note that even though we request the revision IDs in a particular
1723
1620
        # order, the data stream may return them in any order it likes.  In this
1758
1655
        # behaviour.
1759
1656
        expected_data_list = [
1760
1657
            # version, options, length, parents
1761
 
            ('text-a', ['fulltext'], 122, []),
1762
 
            ('text-b', ['line-delta'], 84, ['text-a']),
1763
 
            ('text-m', ['line-delta'], 84, ['text-b', 'text-d']),
1764
 
            ('text-c', ['fulltext'], 121, []),
1765
 
            ('text-d', ['line-delta'], 84, ['text-c']),
 
1658
            ('text-a', ['fulltext'], 122, ()),
 
1659
            ('text-b', ['line-delta'], 84, ('text-a',)),
 
1660
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
 
1661
            ('text-c', ['fulltext'], 121, ()),
 
1662
            ('text-d', ['line-delta'], 84, ('text-c',)),
1766
1663
            ]
1767
1664
        format, data_list, reader_callable = k1.get_data_stream(
1768
1665
            ['text-a', 'text-b', 'text-c', 'text-d', 'text-m'])
1772
1669
            bytes = reader_callable(length)
1773
1670
            self.assertRecordContentEqual(k1, version_id, bytes)
1774
1671
 
 
1672
 
 
1673
class InsertDataStreamTests(KnitTests):
 
1674
    """Tests for insert_data_stream."""
 
1675
 
1775
1676
    def assertKnitFilesEqual(self, knit1, knit2):
1776
1677
        """Assert that the contents of the index and data files of two knits are
1777
1678
        equal.
1796
1697
            self.assertEqual(left.annotate(version),
1797
1698
                right.annotate(version))
1798
1699
 
1799
 
    def test_insert_data_stream_empty(self):
 
1700
    def test_empty_stream(self):
1800
1701
        """Inserting a data stream with no records should not put any data into
1801
1702
        the knit.
1802
1703
        """
1809
1710
                         k1.transport.get_bytes(k1._index._filename),
1810
1711
                         "The .kndx should have nothing apart from the header.")
1811
1712
 
1812
 
    def test_insert_data_stream_one_record(self):
 
1713
    def test_one_record(self):
1813
1714
        """Inserting a data stream with one record from a knit with one record
1814
1715
        results in byte-identical files.
1815
1716
        """
1820
1721
        target.insert_data_stream(data_stream)
1821
1722
        self.assertKnitFilesEqual(source, target)
1822
1723
 
1823
 
    def test_insert_data_stream_annotated_unannotated(self):
 
1724
    def test_annotated_stream_into_unannotated_knit(self):
1824
1725
        """Inserting an annotated datastream to an unannotated knit works."""
1825
1726
        # case one - full texts.
1826
1727
        source = self.make_test_knit(name='source', annotate=True)
1833
1734
        target.insert_data_stream(source.get_data_stream(['text-b']))
1834
1735
        self.assertKnitValuesEqual(source, target)
1835
1736
 
1836
 
    def test_insert_data_stream_unannotated_annotated(self):
 
1737
    def test_unannotated_stream_into_annotated_knit(self):
1837
1738
        """Inserting an unannotated datastream to an annotated knit works."""
1838
1739
        # case one - full texts.
1839
1740
        source = self.make_test_knit(name='source', annotate=False)
1846
1747
        target.insert_data_stream(source.get_data_stream(['text-b']))
1847
1748
        self.assertKnitValuesEqual(source, target)
1848
1749
 
1849
 
    def test_insert_data_stream_records_already_present(self):
 
1750
    def test_records_already_present(self):
1850
1751
        """Insert a data stream where some records are alreday present in the
1851
1752
        target, and some not.  Only the new records are inserted.
1852
1753
        """
1864
1765
        # record was not added a second time.
1865
1766
        self.assertKnitFilesEqual(source, target)
1866
1767
 
1867
 
    def test_insert_data_stream_multiple_records(self):
 
1768
    def test_multiple_records(self):
1868
1769
        """Inserting a data stream of all records from a knit with multiple
1869
1770
        records results in byte-identical files.
1870
1771
        """
1879
1780
        
1880
1781
        self.assertKnitFilesEqual(source, target)
1881
1782
 
1882
 
    def test_insert_data_stream_ghost_parent(self):
 
1783
    def test_ghost_parent(self):
1883
1784
        """Insert a data stream with a record that has a ghost parent."""
1884
1785
        # Make a knit with a record, text-a, that has a ghost parent.
1885
1786
        source = self.make_test_knit(name='source')
1895
1796
        # The target knit object is in a consistent state, i.e. the record we
1896
1797
        # just added is immediately visible.
1897
1798
        self.assertTrue(target.has_version('text-a'))
1898
 
        self.assertTrue(target.has_ghost('text-ghost'))
 
1799
        self.assertFalse(target.has_version('text-ghost'))
 
1800
        self.assertEqual({'text-a':('text-ghost',)},
 
1801
            target.get_parent_map(['text-a', 'text-ghost']))
1899
1802
        self.assertEqual(split_lines(TEXT_1), target.get_lines('text-a'))
1900
1803
 
1901
 
    def test_insert_data_stream_inconsistent_version_lines(self):
 
1804
    def test_inconsistent_version_lines(self):
1902
1805
        """Inserting a data stream which has different content for a version_id
1903
1806
        than already exists in the knit will raise KnitCorrupt.
1904
1807
        """
1912
1815
        self.assertRaises(
1913
1816
            errors.KnitCorrupt, target.insert_data_stream, data_stream)
1914
1817
 
1915
 
    def test_insert_data_stream_inconsistent_version_parents(self):
 
1818
    def test_inconsistent_version_parents(self):
1916
1819
        """Inserting a data stream which has different parents for a version_id
1917
1820
        than already exists in the knit will raise KnitCorrupt.
1918
1821
        """
1927
1830
        self.assertRaises(
1928
1831
            errors.KnitCorrupt, target.insert_data_stream, data_stream)
1929
1832
 
1930
 
    def test_insert_data_stream_unknown_format(self):
 
1833
    def test_unknown_stream_format(self):
1931
1834
        """A data stream in a different format to the target knit cannot be
1932
1835
        inserted.
1933
1836
 
1941
1844
            errors.KnitDataStreamUnknown,
1942
1845
            target.insert_data_stream, data_stream)
1943
1846
 
1944
 
    #  * test that a stream of "already present version, then new version"
1945
 
    #    inserts correctly.
1946
 
 
 
1847
    def test_bug_208418(self):
 
1848
        """You can insert a stream with an incompatible format, even when:
 
1849
          * the stream has a line-delta record,
 
1850
          * whose parent is in the target, also stored as a line-delta
 
1851
 
 
1852
        See <https://launchpad.net/bugs/208418>.
 
1853
        """
 
1854
        base_lines = split_lines(TEXT_1)
 
1855
        # Make the target
 
1856
        target = self.make_test_knit(name='target', annotate=True)
 
1857
        target.add_lines('version-1', [], base_lines)
 
1858
        target.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
 
1859
        # The second record should be a delta.
 
1860
        self.assertEqual('line-delta', target._index.get_method('version-2'))
 
1861
        
 
1862
        # Make a source, with a different format, but the same data
 
1863
        source = self.make_test_knit(name='source', annotate=False)
 
1864
        source.add_lines('version-1', [], base_lines)
 
1865
        source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
 
1866
        # Now add another record, which should be stored as a delta against
 
1867
        # version-2.
 
1868
        source.add_lines('version-3', ['version-2'], base_lines + ['b\n'])
 
1869
        self.assertEqual('line-delta', source._index.get_method('version-3'))
 
1870
 
 
1871
        # Make a stream of the new version
 
1872
        data_stream = source.get_data_stream(['version-3'])
 
1873
        # And insert into the target
 
1874
        target.insert_data_stream(data_stream)
 
1875
        # No errors should have been raised.
 
1876
 
 
1877
    def test_line_delta_record_into_non_delta_knit(self):
 
1878
        # Make a data stream with a line-delta record
 
1879
        source = self.make_test_knit(name='source', delta=True)
 
1880
        base_lines = split_lines(TEXT_1)
 
1881
        source.add_lines('version-1', [], base_lines)
 
1882
        source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
 
1883
        # The second record should be a delta.
 
1884
        self.assertEqual('line-delta', source._index.get_method('version-2'))
 
1885
        data_stream = source.get_data_stream(['version-1', 'version-2'])
 
1886
 
 
1887
        # Insert the stream into a non-delta knit.
 
1888
        target = self.make_test_knit(name='target', delta=False)
 
1889
        target.insert_data_stream(data_stream)
 
1890
        
 
1891
        # Both versions are fulltexts in the target
 
1892
        self.assertEqual('fulltext', target._index.get_method('version-1'))
 
1893
        self.assertEqual('fulltext', target._index.get_method('version-2'))
 
1894
 
 
1895
 
 
1896
class DataStreamTests(KnitTests):
1947
1897
 
1948
1898
    def assertMadeStreamKnit(self, source_knit, versions, target_knit):
1949
1899
        """Assert that a knit made from a stream is as expected."""
2073
2023
    def test_weave_to_knit_matches(self):
2074
2024
        # check that the WeaveToKnit is_compatible function
2075
2025
        # registers True for a Weave to a Knit.
2076
 
        w = Weave()
 
2026
        w = Weave(get_scope=lambda:None)
2077
2027
        k = self.make_test_knit()
2078
2028
        self.failUnless(WeaveToKnit.is_compatible(w, k))
2079
2029
        self.failIf(WeaveToKnit.is_compatible(k, w))
2081
2031
        self.failIf(WeaveToKnit.is_compatible(k, k))
2082
2032
 
2083
2033
 
2084
 
class TestKnitCaching(KnitTests):
2085
 
    
2086
 
    def create_knit(self):
2087
 
        k = self.make_test_knit(True)
2088
 
        k.add_lines('text-1', [], split_lines(TEXT_1))
2089
 
        k.add_lines('text-2', [], split_lines(TEXT_2))
2090
 
        return k
2091
 
 
2092
 
    def test_no_caching(self):
2093
 
        k = self.create_knit()
2094
 
        # Nothing should be cached without setting 'enable_cache'
2095
 
        self.assertEqual({}, k._data._cache)
2096
 
 
2097
 
    def test_cache_data_read_raw(self):
2098
 
        k = self.create_knit()
2099
 
 
2100
 
        # Now cache and read
2101
 
        k.enable_cache()
2102
 
 
2103
 
        def read_one_raw(version):
2104
 
            pos_map = k._get_components_positions([version])
2105
 
            method, index_memo, next = pos_map[version]
2106
 
            lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
2107
 
            self.assertEqual(1, len(lst))
2108
 
            return lst[0]
2109
 
 
2110
 
        val = read_one_raw('text-1')
2111
 
        self.assertEqual({'text-1':val[1]}, k._data._cache)
2112
 
 
2113
 
        k.clear_cache()
2114
 
        # After clear, new reads are not cached
2115
 
        self.assertEqual({}, k._data._cache)
2116
 
 
2117
 
        val2 = read_one_raw('text-1')
2118
 
        self.assertEqual(val, val2)
2119
 
        self.assertEqual({}, k._data._cache)
2120
 
 
2121
 
    def test_cache_data_read(self):
2122
 
        k = self.create_knit()
2123
 
 
2124
 
        def read_one(version):
2125
 
            pos_map = k._get_components_positions([version])
2126
 
            method, index_memo, next = pos_map[version]
2127
 
            lst = list(k._data.read_records_iter([(version, index_memo)]))
2128
 
            self.assertEqual(1, len(lst))
2129
 
            return lst[0]
2130
 
 
2131
 
        # Now cache and read
2132
 
        k.enable_cache()
2133
 
 
2134
 
        val = read_one('text-2')
2135
 
        self.assertEqual(['text-2'], k._data._cache.keys())
2136
 
        self.assertEqual('text-2', val[0])
2137
 
        content, digest = k._data._parse_record('text-2',
2138
 
                                                k._data._cache['text-2'])
2139
 
        self.assertEqual(content, val[1])
2140
 
        self.assertEqual(digest, val[2])
2141
 
 
2142
 
        k.clear_cache()
2143
 
        self.assertEqual({}, k._data._cache)
2144
 
 
2145
 
        val2 = read_one('text-2')
2146
 
        self.assertEqual(val, val2)
2147
 
        self.assertEqual({}, k._data._cache)
2148
 
 
2149
 
    def test_cache_read(self):
2150
 
        k = self.create_knit()
2151
 
        k.enable_cache()
2152
 
 
2153
 
        text = k.get_text('text-1')
2154
 
        self.assertEqual(TEXT_1, text)
2155
 
        self.assertEqual(['text-1'], k._data._cache.keys())
2156
 
 
2157
 
        k.clear_cache()
2158
 
        self.assertEqual({}, k._data._cache)
2159
 
 
2160
 
        text = k.get_text('text-1')
2161
 
        self.assertEqual(TEXT_1, text)
2162
 
        self.assertEqual({}, k._data._cache)
2163
 
 
2164
 
 
2165
2034
class TestKnitIndex(KnitTests):
2166
2035
 
2167
2036
    def test_add_versions_dictionary_compresses(self):
2185
2054
            'a-3 fulltext 0 0 1 :'
2186
2055
            )
2187
2056
        self.assertEqual(['a-1', 'a-2', 'a-3'], idx._history)
2188
 
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0),
2189
 
                          'a-2':('a-2', ['fulltext'], 0, 0, ['a-1'], 1),
2190
 
                          'a-3':('a-3', ['fulltext'], 0, 0, ['a-2'], 2),
 
2057
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0),
 
2058
                          'a-2':('a-2', ['fulltext'], 0, 0, ('a-1',), 1),
 
2059
                          'a-3':('a-3', ['fulltext'], 0, 0, ('a-2',), 2),
2191
2060
                         }, idx._cache)
2192
2061
 
2193
2062
    def test_add_versions_fails_clean(self):
2211
2080
 
2212
2081
        def generate_failure():
2213
2082
            """Add some entries and then raise an exception"""
2214
 
            yield ('a-2', ['fulltext'], (None, 0, 0), ['a-1'])
2215
 
            yield ('a-3', ['fulltext'], (None, 0, 0), ['a-2'])
 
2083
            yield ('a-2', ['fulltext'], (None, 0, 0), ('a-1',))
 
2084
            yield ('a-3', ['fulltext'], (None, 0, 0), ('a-2',))
2216
2085
            raise StopEarly()
2217
2086
 
2218
2087
        # Assert the pre-condition
2219
2088
        self.assertEqual(['a-1'], idx._history)
2220
 
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0)}, idx._cache)
 
2089
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2221
2090
 
2222
2091
        self.assertRaises(StopEarly, idx.add_versions, generate_failure())
2223
2092
 
2224
2093
        # And it shouldn't be modified
2225
2094
        self.assertEqual(['a-1'], idx._history)
2226
 
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0)}, idx._cache)
 
2095
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2227
2096
 
2228
2097
    def test_knit_index_ignores_empty_files(self):
2229
2098
        # There was a race condition in older bzr, where a ^C at the right time
2287
2156
        return KnitGraphIndex(combined_index, deltas=deltas,
2288
2157
            add_callback=add_callback)
2289
2158
 
2290
 
    def test_get_graph(self):
2291
 
        index = self.two_graph_index()
2292
 
        self.assertEqual(set([
2293
 
            ('tip', ('parent', )),
2294
 
            ('tail', ()),
2295
 
            ('parent', ('tail', 'ghost')),
2296
 
            ('separate', ()),
2297
 
            ]), set(index.get_graph()))
2298
 
 
2299
2159
    def test_get_ancestry(self):
2300
2160
        # get_ancestry is defined as eliding ghosts, not erroring.
2301
2161
        index = self.two_graph_index()
2385
2245
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2386
2246
        self.assertEqual(['fulltext'], index.get_options('parent'))
2387
2247
 
2388
 
    def test_get_parents(self):
2389
 
        # get_parents ignores ghosts
2390
 
        index = self.two_graph_index()
2391
 
        self.assertEqual(('tail', ), index.get_parents('parent'))
2392
 
        # and errors on ghosts.
2393
 
        self.assertRaises(errors.RevisionNotPresent,
2394
 
            index.get_parents, 'ghost')
2395
 
 
2396
2248
    def test_get_parents_with_ghosts(self):
2397
2249
        index = self.two_graph_index()
2398
2250
        self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
2519
2371
             ('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2520
2372
        self.assertEqual([], self.caught_entries)
2521
2373
 
2522
 
    def test_iter_parents(self):
2523
 
        index1 = self.make_g_index('1', 1, [
2524
 
        # no parents
2525
 
            (('r0', ), 'N0 100', ([], )),
2526
 
        # 1 parent
2527
 
            (('r1', ), '', ([('r0', )], ))])
2528
 
        index2 = self.make_g_index('2', 1, [
2529
 
        # 2 parents
2530
 
            (('r2', ), 'N0 100', ([('r1', ), ('r0', )], )),
2531
 
            ])
2532
 
        combined_index = CombinedGraphIndex([index1, index2])
2533
 
        index = KnitGraphIndex(combined_index)
2534
 
        # XXX TODO a ghost
2535
 
        # cases: each sample data individually:
2536
 
        self.assertEqual(set([('r0', ())]),
2537
 
            set(index.iter_parents(['r0'])))
2538
 
        self.assertEqual(set([('r1', ('r0', ))]),
2539
 
            set(index.iter_parents(['r1'])))
2540
 
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
2541
 
            set(index.iter_parents(['r2'])))
2542
 
        # no nodes returned for a missing node
2543
 
        self.assertEqual(set(),
2544
 
            set(index.iter_parents(['missing'])))
2545
 
        # 1 node returned with missing nodes skipped
2546
 
        self.assertEqual(set([('r1', ('r0', ))]),
2547
 
            set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
2548
 
        # 2 nodes returned
2549
 
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2550
 
            set(index.iter_parents(['r0', 'r1'])))
2551
 
        # 2 nodes returned, missing skipped
2552
 
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2553
 
            set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
2554
 
 
2555
 
 
2556
2374
class TestNoParentsGraphIndexKnit(KnitTests):
2557
2375
    """Tests for knits using KnitGraphIndex with no parents."""
2558
2376
 
2593
2411
        return KnitGraphIndex(combined_index, parents=False,
2594
2412
            add_callback=add_callback)
2595
2413
 
2596
 
    def test_get_graph(self):
2597
 
        index = self.two_graph_index()
2598
 
        self.assertEqual(set([
2599
 
            ('tip', ()),
2600
 
            ('tail', ()),
2601
 
            ('parent', ()),
2602
 
            ('separate', ()),
2603
 
            ]), set(index.get_graph()))
2604
 
 
2605
2414
    def test_get_ancestry(self):
2606
2415
        # with no parents, ancestry is always just the key.
2607
2416
        index = self.two_graph_index()
2660
2469
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2661
2470
        self.assertEqual(['fulltext'], index.get_options('parent'))
2662
2471
 
2663
 
    def test_get_parents(self):
2664
 
        index = self.two_graph_index()
2665
 
        self.assertEqual((), index.get_parents('parent'))
2666
 
        # and errors on ghosts.
2667
 
        self.assertRaises(errors.RevisionNotPresent,
2668
 
            index.get_parents, 'ghost')
2669
 
 
2670
2472
    def test_get_parents_with_ghosts(self):
2671
2473
        index = self.two_graph_index()
2672
2474
        self.assertEqual((), index.get_parents_with_ghosts('parent'))
2786
2588
             ('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2787
2589
        self.assertEqual([], self.caught_entries)
2788
2590
 
2789
 
    def test_iter_parents(self):
2790
 
        index = self.two_graph_index()
2791
 
        self.assertEqual(set([
2792
 
            ('tip', ()), ('tail', ()), ('parent', ()), ('separate', ())
2793
 
            ]),
2794
 
            set(index.iter_parents(['tip', 'tail', 'ghost', 'parent', 'separate'])))
2795
 
        self.assertEqual(set([('tip', ())]),
2796
 
            set(index.iter_parents(['tip'])))
2797
 
        self.assertEqual(set(),
2798
 
            set(index.iter_parents([])))
2799
 
 
2800
 
 
2801
2591
class TestPackKnits(KnitTests):
2802
2592
    """Tests that use a _PackAccess and KnitGraphIndex."""
2803
2593
 
2852
2642
            set(result),
2853
2643
            set(index.get_ancestry(ancestry_versions, False)))
2854
2644
 
2855
 
    def assertIterParents(self, knit, versions, parent_versions, result):
2856
 
        """Check the result of an iter_parents call on knit."""
2857
 
        index = self.get_index(knit, knit.get_data_stream(versions))
2858
 
        self.assertEqual(result, index.iter_parents(parent_versions))
2859
 
 
2860
2645
    def assertGetMethod(self, knit, versions, version, result):
2861
2646
        index = self.get_index(knit, knit.get_data_stream(versions))
2862
2647
        self.assertEqual(result, index.get_method(version))
2903
2688
        """Constructing a StreamIndex generates index data."""
2904
2689
        data_list = [('text-a', ['fulltext'], 127, []),
2905
2690
            ('text-b', ['option'], 128, ['text-c'])]
2906
 
        index = _StreamIndex(data_list)
 
2691
        index = _StreamIndex(data_list, None)
2907
2692
        self.assertEqual({'text-a':(['fulltext'], (0, 127), []),
2908
2693
            'text-b':(['option'], (127, 127 + 128), ['text-c'])},
2909
2694
            index._by_version)
2927
2712
        # we thunk across.
2928
2713
        self.assertGetMethod(knit, ['c'], 'b', 'fulltext')
2929
2714
 
2930
 
    def test_iter_parents(self):
2931
 
        knit = self.make_knit_with_4_versions_2_dags()
2932
 
        self.assertIterParents(knit, ['a'], ['a'], [('a', [])])
2933
 
        self.assertIterParents(knit, ['a', 'b'], ['a', 'b'],
2934
 
            [('a', []), ('b', [])])
2935
 
        self.assertIterParents(knit, ['a', 'b', 'c'], ['a', 'b', 'c'],
2936
 
            [('a', []), ('b', []), ('c', ['b', 'a'])])
2937
 
        self.assertIterParents(knit, ['a', 'b', 'c', 'd'],
2938
 
            ['a', 'b', 'c', 'd'],
2939
 
            [('a', []), ('b', []), ('c', ['b', 'a']), ('d', ['e', 'f'])])
2940
 
        self.assertIterParents(knit, ['c'], ['a', 'b', 'c'],
2941
 
            [('c', ['b', 'a'])])
2942
 
 
2943
2715
    def test_get_options(self):
2944
2716
        knit = self.make_knit_with_4_versions_2_dags()
2945
2717
        self.assertGetOptions(knit, 'a', ['no-eol', 'fulltext'])
2947
2719
 
2948
2720
    def test_get_parents_with_ghosts(self):
2949
2721
        knit = self.make_knit_with_4_versions_2_dags()
2950
 
        self.assertGetParentsWithGhosts(knit, ['a'], 'a', [])
2951
 
        self.assertGetParentsWithGhosts(knit, ['c'], 'c', ['b', 'a'])
2952
 
        self.assertGetParentsWithGhosts(knit, ['d'], 'd', ['e', 'f'])
 
2722
        self.assertGetParentsWithGhosts(knit, ['a'], 'a', ())
 
2723
        self.assertGetParentsWithGhosts(knit, ['c'], 'c', ('b', 'a'))
 
2724
        self.assertGetParentsWithGhosts(knit, ['d'], 'd', ('e', 'f'))
2953
2725
 
2954
2726
    def test_get_position(self):
2955
2727
        knit = self.make_knit_with_4_versions_2_dags()
3014
2786
        target_knit = self.make_test_knit(name='annotated', annotate=True)
3015
2787
        source_knit.add_lines("A", [], ["Foo\n"])
3016
2788
        # Give the target A, so we can try to thunk across to it.
3017
 
        target_knit.join(source_knit)
 
2789
        target_knit.insert_record_stream(source_knit.get_record_stream(['A'],
 
2790
            'unordered', False))
3018
2791
        index, access = self.get_index_access(target_knit,
3019
2792
            source_knit.get_data_stream([]))
3020
2793
        raw_data = list(access.get_raw_records([(True, "A", None, None)]))[0]
3031
2804
            knit.get_data_stream([]))
3032
2805
        self.assertRaises(errors.KnitCorrupt,
3033
2806
            list, access.get_raw_records([(True, "A", None, None)]))
 
2807
 
 
2808
 
 
2809
class TestFormatSignatures(KnitTests):
 
2810
 
 
2811
    def test_knit_format_signatures(self):
 
2812
        """Different formats of knit have different signature strings."""
 
2813
        knit = self.make_test_knit(name='a', annotate=True)
 
2814
        self.assertEqual('knit-annotated', knit.get_format_signature())
 
2815
        knit = self.make_test_knit(name='p', annotate=False)
 
2816
        self.assertEqual('knit-plain', knit.get_format_signature())