~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_versionedfile.py

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
    progress,
31
31
    )
32
32
from bzrlib.errors import (
33
 
                           RevisionNotPresent, 
 
33
                           RevisionNotPresent,
34
34
                           RevisionAlreadyPresent,
35
35
                           WeaveParentMismatch
36
36
                           )
37
37
from bzrlib.knit import (
38
 
    KnitVersionedFile,
 
38
    make_file_knit,
39
39
    KnitAnnotateFactory,
40
40
    KnitPlainFactory,
41
41
    )
59
59
    they are strictly controlled by their owning repositories.
60
60
    """
61
61
 
 
62
    def get_transaction(self):
 
63
        if not hasattr(self, '_transaction'):
 
64
            self._transaction = None
 
65
        return self._transaction
 
66
 
62
67
    def test_add(self):
63
68
        f = self.get_file()
64
69
        f.add_lines('r0', [], ['a\n', 'b\n'])
242
247
        for version in multiparent.topo_iter(vf):
243
248
            mpdiff = vf.make_mpdiffs([version])[0]
244
249
            new_vf.add_mpdiffs([(version, vf.get_parent_map([version])[version],
245
 
                                 vf.get_sha1(version), mpdiff)])
 
250
                                 vf.get_sha1s([version])[0], mpdiff)])
246
251
            self.assertEqualDiff(vf.get_text(version),
247
252
                                 new_vf.get_text(version))
248
253
 
348
353
            set(f.get_ancestry('rM', topo_sorted=False)))
349
354
 
350
355
    def test_mutate_after_finish(self):
 
356
        self._transaction = 'before'
351
357
        f = self.get_file()
352
 
        f.transaction_finished()
 
358
        self._transaction = 'after'
353
359
        self.assertRaises(errors.OutSideTransaction, f.add_lines, '', [], [])
354
360
        self.assertRaises(errors.OutSideTransaction, f.add_lines_with_ghosts, '', [], [])
355
361
        self.assertRaises(errors.OutSideTransaction, f.join, '')
356
 
        self.assertRaises(errors.OutSideTransaction, f.clone_text, 'base', 'bar', ['foo'])
357
362
        
358
 
    def test_clear_cache(self):
359
 
        f = self.get_file()
360
 
        # on a new file it should not error
361
 
        f.clear_cache()
362
 
        # and after adding content, doing a clear_cache and a get should work.
363
 
        f.add_lines('0', [], ['a'])
364
 
        f.clear_cache()
365
 
        self.assertEqual(['a'], f.get_lines('0'))
366
 
 
367
363
    def test_clone_text(self):
368
364
        f = self.get_file()
369
365
        f.add_lines('r0', [], ['a\n', 'b\n'])
370
 
        f.clone_text('r1', 'r0', ['r0'])
 
366
        self.applyDeprecated(one_four, f.clone_text, 'r1', 'r0', ['r0'])
371
367
        def verify_file(f):
372
368
            self.assertEquals(f.get_lines('r1'), f.get_lines('r0'))
373
369
            self.assertEquals(f.get_lines('r1'), ['a\n', 'b\n'])
374
370
            self.assertEqual({'r1':('r0',)}, f.get_parent_map(['r1']))
375
371
            self.assertRaises(RevisionNotPresent,
376
 
                f.clone_text, 'r2', 'rX', [])
 
372
                self.applyDeprecated, one_four, f.clone_text, 'r2', 'rX', [])
377
373
            self.assertRaises(RevisionAlreadyPresent,
378
 
                f.clone_text, 'r1', 'r0', [])
 
374
                self.applyDeprecated, one_four, f.clone_text, 'r1', 'r0', [])
379
375
        verify_file(f)
380
376
        verify_file(self.reopen_file())
381
377
 
382
 
    def test_create_empty(self):
383
 
        f = self.get_file()
384
 
        f.add_lines('0', [], ['a\n'])
385
 
        new_f = f.create_empty('t', MemoryTransport())
386
 
        # smoke test, specific types should check it is honoured correctly for
387
 
        # non type attributes
388
 
        self.assertEqual([], new_f.versions())
389
 
        self.assertTrue(isinstance(new_f, f.__class__))
390
 
 
391
378
    def test_copy_to(self):
392
379
        f = self.get_file()
393
380
        f.add_lines('0', [], ['a\n'])
394
381
        t = MemoryTransport()
395
382
        f.copy_to('foo', t)
396
 
        for suffix in f.__class__.get_suffixes():
 
383
        for suffix in self.get_factory().get_suffixes():
397
384
            self.assertTrue(t.has('foo' + suffix))
398
385
 
399
386
    def test_get_suffixes(self):
400
387
        f = self.get_file()
401
 
        # should be the same
402
 
        self.assertEqual(f.__class__.get_suffixes(), f.__class__.get_suffixes())
403
388
        # and should be a list
404
 
        self.assertTrue(isinstance(f.__class__.get_suffixes(), list))
 
389
        self.assertTrue(isinstance(self.get_factory().get_suffixes(), list))
405
390
 
406
391
    def build_graph(self, file, graph):
407
392
        for node in topo_sort(graph.items()):
414
399
            'v2': ('v1', ),
415
400
            'v3': ('v2', )}
416
401
        self.build_graph(f, graph)
417
 
        self.assertEqual(graph, f.get_graph())
 
402
        self.assertEqual(graph, self.applyDeprecated(one_four, f.get_graph))
418
403
    
419
404
    def test_get_graph_partial(self):
420
405
        f = self.get_file()
441
426
        simple_b_gam.update(simple_gam)
442
427
        simple_b_gam.update(simple_b)
443
428
        self.build_graph(f, complex_graph)
444
 
        self.assertEqual(simple_a, f.get_graph(['a']))
445
 
        self.assertEqual(simple_b, f.get_graph(['b']))
446
 
        self.assertEqual(simple_gam, f.get_graph(['gam']))
447
 
        self.assertEqual(simple_b_gam, f.get_graph(['b', 'gam']))
 
429
        self.assertEqual(simple_a, self.applyDeprecated(one_four, f.get_graph,
 
430
            ['a']))
 
431
        self.assertEqual(simple_b, self.applyDeprecated(one_four, f.get_graph,
 
432
            ['b']))
 
433
        self.assertEqual(simple_gam, self.applyDeprecated(one_four,
 
434
            f.get_graph, ['gam']))
 
435
        self.assertEqual(simple_b_gam, self.applyDeprecated(one_four,
 
436
            f.get_graph, ['b', 'gam']))
448
437
 
449
438
    def test_get_parents(self):
450
439
        f = self.get_file()
534
523
        # XXX TODO a ghost
535
524
        # cases: each sample data individually:
536
525
        self.assertEqual(set([('r0', ())]),
537
 
            set(f.iter_parents(['r0'])))
 
526
            set(self.applyDeprecated(one_four, f.iter_parents, ['r0'])))
538
527
        self.assertEqual(set([('r1', ('r0', ))]),
539
 
            set(f.iter_parents(['r1'])))
 
528
            set(self.applyDeprecated(one_four, f.iter_parents, ['r1'])))
540
529
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
541
 
            set(f.iter_parents(['r2'])))
 
530
            set(self.applyDeprecated(one_four, f.iter_parents, ['r2'])))
542
531
        # no nodes returned for a missing node
543
532
        self.assertEqual(set(),
544
 
            set(f.iter_parents(['missing'])))
 
533
            set(self.applyDeprecated(one_four, f.iter_parents, ['missing'])))
545
534
        # 1 node returned with missing nodes skipped
546
535
        self.assertEqual(set([('r1', ('r0', ))]),
547
 
            set(f.iter_parents(['ghost1', 'r1', 'ghost'])))
 
536
            set(self.applyDeprecated(one_four, f.iter_parents, ['ghost1', 'r1',
 
537
                'ghost'])))
548
538
        # 2 nodes returned
549
539
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
550
 
            set(f.iter_parents(['r0', 'r1'])))
 
540
            set(self.applyDeprecated(one_four, f.iter_parents, ['r0', 'r1'])))
551
541
        # 2 nodes returned, missing skipped
552
542
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
553
 
            set(f.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
 
543
            set(self.applyDeprecated(one_four, f.iter_parents,
 
544
                ['a', 'r0', 'b', 'r1', 'c'])))
554
545
 
555
546
    def test_iter_lines_added_or_present_in_versions(self):
556
547
        # test that we get at least an equalset of the lines added by
630
621
            vf.add_lines_with_ghosts('notbxbfse', [parent_id_utf8], [])
631
622
        except NotImplementedError:
632
623
            # check the other ghost apis are also not implemented
633
 
            self.assertRaises(NotImplementedError, vf.has_ghost, 'foo')
634
624
            self.assertRaises(NotImplementedError, vf.get_ancestry_with_ghosts, ['foo'])
635
625
            self.assertRaises(NotImplementedError, vf.get_parents_with_ghosts, 'foo')
636
 
            self.assertRaises(NotImplementedError, vf.get_graph_with_ghosts)
637
626
            return
638
627
        vf = self.reopen_file()
639
628
        # test key graph related apis: getncestry, _graph, get_parents
642
631
        self.assertEqual(['notbxbfse'], vf.get_ancestry('notbxbfse'))
643
632
        self.assertEqual([],
644
633
            self.applyDeprecated(one_four, vf.get_parents, 'notbxbfse'))
645
 
        self.assertEqual({'notbxbfse':()}, vf.get_graph())
 
634
        self.assertEqual({'notbxbfse':()}, self.applyDeprecated(one_four,
 
635
            vf.get_graph))
646
636
        self.assertFalse(vf.has_version(parent_id_utf8))
647
637
        # we have _with_ghost apis to give us ghost information.
648
638
        self.assertEqual([parent_id_utf8, 'notbxbfse'], vf.get_ancestry_with_ghosts(['notbxbfse']))
649
639
        self.assertEqual([parent_id_utf8], vf.get_parents_with_ghosts('notbxbfse'))
650
 
        self.assertEqual({'notbxbfse':(parent_id_utf8,)}, vf.get_graph_with_ghosts())
651
 
        self.assertTrue(vf.has_ghost(parent_id_utf8))
 
640
        self.assertEqual({'notbxbfse':(parent_id_utf8,)},
 
641
            self.applyDeprecated(one_four, vf.get_graph_with_ghosts))
 
642
        self.assertTrue(self.applyDeprecated(one_four, vf.has_ghost,
 
643
            parent_id_utf8))
652
644
        # if we add something that is a ghost of another, it should correct the
653
645
        # results of the prior apis
654
646
        vf.add_lines(parent_id_utf8, [], [])
658
650
        self.assertEqual({parent_id_utf8:(),
659
651
                          'notbxbfse':(parent_id_utf8, ),
660
652
                          },
661
 
                         vf.get_graph())
 
653
                         self.applyDeprecated(one_four, vf.get_graph))
662
654
        self.assertTrue(vf.has_version(parent_id_utf8))
663
655
        # we have _with_ghost apis to give us ghost information.
664
656
        self.assertEqual([parent_id_utf8, 'notbxbfse'],
667
659
        self.assertEqual({parent_id_utf8:(),
668
660
                          'notbxbfse':(parent_id_utf8,),
669
661
                          },
670
 
                         vf.get_graph_with_ghosts())
671
 
        self.assertFalse(vf.has_ghost(parent_id_utf8))
 
662
            self.applyDeprecated(one_four, vf.get_graph_with_ghosts))
 
663
        self.assertFalse(self.applyDeprecated(one_four, vf.has_ghost,
 
664
            parent_id_utf8))
672
665
 
673
666
    def test_add_lines_with_ghosts_after_normal_revs(self):
674
667
        # some versioned file formats allow lines to be added with parent
678
671
        vf = self.get_file()
679
672
        # probe for ghost support
680
673
        try:
681
 
            vf.has_ghost('hoo')
 
674
            vf.add_lines_with_ghosts('base', [], ['line\n', 'line_b\n'])
682
675
        except NotImplementedError:
683
676
            return
684
 
        vf.add_lines_with_ghosts('base', [], ['line\n', 'line_b\n'])
685
677
        vf.add_lines_with_ghosts('references_ghost',
686
678
                                 ['base', 'a_ghost'],
687
679
                                 ['line\n', 'line_b\n', 'line_c\n'])
702
694
                          [],
703
695
                          [])
704
696
        self.assertRaises(errors.ReadOnlyError, vf.join, 'base')
705
 
        self.assertRaises(errors.ReadOnlyError, vf.clone_text, 'base', 'bar', ['foo'])
706
697
    
707
 
    def test_get_sha1(self):
 
698
    def test_get_sha1s(self):
708
699
        # check the sha1 data is available
709
700
        vf = self.get_file()
710
701
        # a simple file
713
704
        vf.add_lines('b', ['a'], ['a\n'])
714
705
        # a file differing only in last newline.
715
706
        vf.add_lines('c', [], ['a'])
716
 
        self.assertEqual(
717
 
            '3f786850e387550fdab836ed7e6dc881de23001b', vf.get_sha1('a'))
718
 
        self.assertEqual(
719
 
            '3f786850e387550fdab836ed7e6dc881de23001b', vf.get_sha1('b'))
720
 
        self.assertEqual(
721
 
            '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', vf.get_sha1('c'))
722
 
 
 
707
        # Deprecasted single-version API.
 
708
        self.assertEqual(
 
709
            '3f786850e387550fdab836ed7e6dc881de23001b',
 
710
            self.applyDeprecated(one_four, vf.get_sha1, 'a'))
 
711
        self.assertEqual(
 
712
            '3f786850e387550fdab836ed7e6dc881de23001b',
 
713
            self.applyDeprecated(one_four, vf.get_sha1, 'b'))
 
714
        self.assertEqual(
 
715
            '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8',
 
716
            self.applyDeprecated(one_four, vf.get_sha1, 'c'))
723
717
        self.assertEqual(['3f786850e387550fdab836ed7e6dc881de23001b',
724
718
                          '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8',
725
719
                          '3f786850e387550fdab836ed7e6dc881de23001b'],
729
723
class TestWeave(TestCaseWithMemoryTransport, VersionedFileTestMixIn):
730
724
 
731
725
    def get_file(self, name='foo'):
732
 
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
 
726
        return WeaveFile(name, get_transport(self.get_url('.')), create=True,
 
727
            get_scope=self.get_transaction)
733
728
 
734
729
    def get_file_corrupted_text(self):
735
 
        w = WeaveFile('foo', get_transport(self.get_url('.')), create=True)
 
730
        w = WeaveFile('foo', get_transport(self.get_url('.')), create=True,
 
731
            get_scope=self.get_transaction)
736
732
        w.add_lines('v1', [], ['hello\n'])
737
733
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
738
734
        
766
762
        return w
767
763
 
768
764
    def reopen_file(self, name='foo', create=False):
769
 
        return WeaveFile(name, get_transport(self.get_url('.')), create=create)
 
765
        return WeaveFile(name, get_transport(self.get_url('.')), create=create,
 
766
            get_scope=self.get_transaction)
770
767
 
771
768
    def test_no_implicit_create(self):
772
769
        self.assertRaises(errors.NoSuchFile,
773
770
                          WeaveFile,
774
771
                          'foo',
775
 
                          get_transport(self.get_url('.')))
 
772
                          get_transport(self.get_url('.')),
 
773
                          get_scope=self.get_transaction)
776
774
 
777
775
    def get_factory(self):
778
776
        return WeaveFile
782
780
 
783
781
    def get_file(self, name='foo'):
784
782
        return self.get_factory()(name, get_transport(self.get_url('.')),
785
 
                                  delta=True, create=True)
 
783
            delta=True, create=True, get_scope=self.get_transaction)
786
784
 
787
785
    def get_factory(self):
788
 
        return KnitVersionedFile
 
786
        return make_file_knit
789
787
 
790
788
    def get_file_corrupted_text(self):
791
789
        knit = self.get_file()
803
801
        knit.check()
804
802
 
805
803
    def test_no_implicit_create(self):
806
 
        self.assertRaises(errors.NoSuchFile,
807
 
                          KnitVersionedFile,
808
 
                          'foo',
809
 
                          get_transport(self.get_url('.')))
 
804
        self.assertRaises(errors.NoSuchFile, self.get_factory(), 'foo',
 
805
            get_transport(self.get_url('.')))
810
806
 
811
807
 
812
808
class TestPlaintextKnit(TestKnit):
813
809
    """Test a knit with no cached annotations"""
814
810
 
815
 
    def _factory(self, name, transport, file_mode=None, access_mode=None,
816
 
                 delta=True, create=False):
817
 
        return KnitVersionedFile(name, transport, file_mode, access_mode,
818
 
                                 KnitPlainFactory(), delta=delta,
819
 
                                 create=create)
820
 
 
821
811
    def get_factory(self):
822
 
        return self._factory
 
812
        return make_file_knit
823
813
 
824
814
 
825
815
class TestPlanMergeVersionedFile(TestCaseWithMemoryTransport):
826
816
 
827
817
    def setUp(self):
828
818
        TestCaseWithMemoryTransport.setUp(self)
829
 
        self.vf1 = KnitVersionedFile('root', self.get_transport(), create=True)
830
 
        self.vf2 = KnitVersionedFile('root', self.get_transport(), create=True)
 
819
        self.vf1 = make_file_knit('root', self.get_transport(), create=True)
 
820
        self.vf2 = make_file_knit('root', self.get_transport(), create=True)
831
821
        self.plan_merge_vf = versionedfile._PlanMergeVersionedFile('root',
832
822
            [self.vf1, self.vf2])
833
823
 
955
945
 
956
946
class TestReadonlyHttpMixin(object):
957
947
 
 
948
    def get_transaction(self):
 
949
        return 1
 
950
 
958
951
    def test_readonly_http_works(self):
959
952
        # we should be able to read from http with a versioned file.
960
953
        vf = self.get_file()
973
966
class TestWeaveHTTP(TestCaseWithWebserver, TestReadonlyHttpMixin):
974
967
 
975
968
    def get_file(self):
976
 
        return WeaveFile('foo', get_transport(self.get_url('.')), create=True)
 
969
        return WeaveFile('foo', get_transport(self.get_url('.')), create=True,
 
970
            get_scope=self.get_transaction)
977
971
 
978
972
    def get_factory(self):
979
973
        return WeaveFile
982
976
class TestKnitHTTP(TestCaseWithWebserver, TestReadonlyHttpMixin):
983
977
 
984
978
    def get_file(self):
985
 
        return KnitVersionedFile('foo', get_transport(self.get_url('.')),
986
 
                                 delta=True, create=True)
 
979
        return make_file_knit('foo', get_transport(self.get_url('.')),
 
980
            delta=True, create=True, get_scope=self.get_transaction)
987
981
 
988
982
    def get_factory(self):
989
 
        return KnitVersionedFile
 
983
        return make_file_knit
990
984
 
991
985
 
992
986
class MergeCasesMixin(object):
1229
1223
class TestKnitMerge(TestCaseWithMemoryTransport, MergeCasesMixin):
1230
1224
 
1231
1225
    def get_file(self, name='foo'):
1232
 
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
 
1226
        return make_file_knit(name, get_transport(self.get_url('.')),
1233
1227
                                 delta=True, create=True)
1234
1228
 
1235
1229
    def log_contents(self, w):
1249
1243
 
1250
1244
    overlappedInsertExpected = ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 
1251
1245
                                'xxx', '>>>>>>> ', 'bbb']
1252
 
 
1253
 
 
1254
 
class TestFormatSignatures(TestCaseWithMemoryTransport):
1255
 
 
1256
 
    def get_knit_file(self, name, annotated):
1257
 
        if annotated:
1258
 
            factory = KnitAnnotateFactory()
1259
 
        else:
1260
 
            factory = KnitPlainFactory()
1261
 
        return KnitVersionedFile(
1262
 
            name, get_transport(self.get_url('.')), create=True,
1263
 
            factory=factory)
1264
 
 
1265
 
    def test_knit_format_signatures(self):
1266
 
        """Different formats of knit have different signature strings."""
1267
 
        knit = self.get_knit_file('a', True)
1268
 
        self.assertEqual('knit-annotated', knit.get_format_signature())
1269
 
        knit = self.get_knit_file('p', False)
1270
 
        self.assertEqual('knit-plain', knit.get_format_signature())
1271