3
# Copyright (C) 2005 by Canonical Ltd
1
# Copyright (C) 2005 Canonical Ltd
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
7
5
# the Free Software Foundation; either version 2 of the License, or
8
6
# (at your option) any later version.
10
8
# This program is distributed in the hope that it will be useful,
11
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
11
# GNU General Public License for more details.
15
13
# You should have received a copy of the GNU General Public License
16
14
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
18
# TODO: tests regarding version names
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
19
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
24
22
"""test suite for weave algorithm"""
26
24
from pprint import pformat
28
import bzrlib.errors as errors
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
29
from bzrlib.osutils import sha_string
30
from bzrlib.tests import TestCase, TestCaseInTempDir
31
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
30
32
from bzrlib.weavefile import write_weave, read_weave
31
from bzrlib.tests import TestCase
32
from bzrlib.osutils import sha_string
35
35
# texts for use in testing
93
84
[('text0', TEXT_0[0])])
96
class StoreTwo(TestBase):
100
idx = k.add_lines('text0', [], TEXT_0)
101
self.assertEqual(idx, 0)
103
idx = k.add_lines('text1', [], TEXT_1)
104
self.assertEqual(idx, 1)
106
self.assertEqual(k.get_lines(0), TEXT_0)
107
self.assertEqual(k.get_lines(1), TEXT_1)
110
class GetSha1(TestBase):
111
def test_get_sha1(self):
113
k.add_lines('text0', [], 'text0')
114
self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
116
self.assertRaises(errors.RevisionNotPresent,
118
self.assertRaises(errors.RevisionNotPresent,
122
87
class InvalidAdd(TestBase):
123
88
"""Try to use invalid version number during add."""
124
89
def runTest(self):
204
170
['text0', 'text1', 'text3'],
205
171
['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
207
self.assertEqual(k.annotate('text4'),
173
self.assertEqual(k.annotate('text4'),
208
174
[('text0', 'line 1'),
209
175
('text4', 'aaa'),
210
176
('text3', 'middle line'),
643
609
A Jug of Wine, a Loaf of Bread, -- and Thou
644
610
Beside me singing in the Wilderness --
645
611
Oh, Wilderness were Paradise enow!""",
647
613
"""A Book of Verses underneath the Bough,
648
614
A Jug of Wine, a Loaf of Bread, -- and Thou
649
615
Beside me singing in the Wilderness --
691
657
self.weave1.add_lines('v1', [], self.lines1)
692
658
self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
693
659
self.weave1.add_lines('v3', ['v2'], self.lines3)
695
def test_join_empty(self):
696
"""Join two empty weaves."""
697
eq = self.assertEqual
703
def test_join_empty_to_nonempty(self):
704
"""Join empty weave onto nonempty."""
705
self.weave1.join(Weave())
706
self.assertEqual(len(self.weave1), 3)
708
def test_join_unrelated(self):
709
"""Join two weaves with no history in common."""
711
wb.add_lines('b1', [], ['line from b\n'])
714
eq = self.assertEqual
716
eq(sorted(w1.versions()),
717
['b1', 'v1', 'v2', 'v3'])
719
def test_join_related(self):
720
wa = self.weave1.copy()
721
wb = self.weave1.copy()
722
wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
723
wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
724
eq = self.assertEquals
729
eq(wa.get_lines('b1'),
730
['hello\n', 'pale blue\n', 'world\n'])
732
def test_join_parent_disagreement(self):
733
#join reconciles differening parents into a union.
736
wa.add_lines('v1', [], ['hello\n'])
737
wb.add_lines('v0', [], [])
738
wb.add_lines('v1', ['v0'], ['hello\n'])
740
self.assertEqual(['v0'], wa.get_parents('v1'))
742
def test_join_text_disagreement(self):
743
"""Cannot join weaves with different texts for a version."""
746
wa.add_lines('v1', [], ['hello\n'])
747
wb.add_lines('v1', [], ['not\n', 'hello\n'])
748
self.assertRaises(WeaveError,
751
def test_join_unordered(self):
752
"""Join weaves where indexes differ.
754
The source weave contains a different version at index 0."""
755
wa = self.weave1.copy()
757
wb.add_lines('x1', [], ['line from x1\n'])
758
wb.add_lines('v1', [], ['hello\n'])
759
wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
761
eq = self.assertEquals
762
eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
763
eq(wa.get_text('x1'), 'line from x1\n')
765
661
def test_written_detection(self):
766
662
# Test detection of weave file corruption.
811
707
self.assertRaises(errors.WeaveInvalidChecksum, w.check)
710
class TestWeave(TestCase):
712
def test_allow_reserved_false(self):
713
w = Weave('name', allow_reserved=False)
714
# Add lines is checked at the WeaveFile level, not at the Weave level
715
w.add_lines('name:', [], TEXT_1)
716
# But get_lines is checked at this level
717
self.assertRaises(errors.ReservedId, w.get_lines, 'name:')
719
def test_allow_reserved_true(self):
720
w = Weave('name', allow_reserved=True)
721
w.add_lines('name:', [], TEXT_1)
722
self.assertEqual(TEXT_1, w.get_lines('name:'))
814
725
class InstrumentedWeave(Weave):
815
726
"""Keep track of how many times functions are called."""
817
728
def __init__(self, weave_name=None):
818
729
self._extract_count = 0
819
730
Weave.__init__(self, weave_name=weave_name)
823
734
return Weave._extract(self, versions)
826
class JoinOptimization(TestCase):
827
"""Test that Weave.join() doesn't extract all texts, only what must be done."""
830
w1 = InstrumentedWeave()
831
w2 = InstrumentedWeave()
834
txt1 = ['a\n', 'b\n']
835
txt2 = ['a\n', 'c\n']
836
txt3 = ['a\n', 'b\n', 'c\n']
838
w1.add_lines('txt0', [], txt0) # extract 1a
839
w2.add_lines('txt0', [], txt0) # extract 1b
840
w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
841
w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
842
w1.join(w2) # extract 3a to add txt2
843
w2.join(w1) # extract 3b to add txt1
845
w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a
846
w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
847
# These secretly have inverted parents
849
# This should not have to do any extractions
850
w1.join(w2) # NO extract, texts already present with same parents
851
w2.join(w1) # NO extract, texts already present with same parents
853
self.assertEqual(4, w1._extract_count)
854
self.assertEqual(4, w2._extract_count)
856
def test_double_parent(self):
857
# It should not be considered illegal to add
858
# a revision with the same parent twice
859
w1 = InstrumentedWeave()
860
w2 = InstrumentedWeave()
863
txt1 = ['a\n', 'b\n']
864
txt2 = ['a\n', 'c\n']
865
txt3 = ['a\n', 'b\n', 'c\n']
867
w1.add_lines('txt0', [], txt0)
868
w2.add_lines('txt0', [], txt0)
869
w1.add_lines('txt1', ['txt0'], txt1)
870
w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
871
# Same text, effectively the same, because the
872
# parent is only repeated
873
w1.join(w2) # extract 3a to add txt2
874
w2.join(w1) # extract 3b to add txt1
877
737
class TestNeedsReweave(TestCase):
878
738
"""Internal corner cases for when reweave is needed."""
890
750
self.assertFalse(w1._compatible_parents(set(), set([1])))
891
751
self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
892
752
self.assertFalse(w1._compatible_parents(my_parents, set([4])))
755
class TestWeaveFile(TestCaseInTempDir):
757
def test_empty_file(self):
758
f = open('empty.weave', 'wb+')
760
self.assertRaises(errors.WeaveFormatError,