1
# Copyright (C) 2005 Canonical Ltd
3
# Copyright (C) 2005 by Canonical Ltd
3
5
# This program is free software; you can redistribute it and/or modify
4
6
# it under the terms of the GNU General Public License as published by
5
7
# the Free Software Foundation; either version 2 of the License, or
6
8
# (at your option) any later version.
8
10
# This program is distributed in the hope that it will be useful,
9
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
13
# GNU General Public License for more details.
13
15
# You should have received a copy of the GNU General Public License
14
16
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
20
# TODO: tests regarding version names
19
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
22
24
"""test suite for weave algorithm"""
24
26
from pprint import pformat
28
import bzrlib.errors as errors
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
30
from bzrlib.weavefile import write_weave, read_weave
31
from bzrlib.tests import TestCase
29
32
from bzrlib.osutils import sha_string
30
from bzrlib.tests import TestCase, TestCaseInTempDir
31
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
32
from bzrlib.weavefile import write_weave, read_weave
35
35
# texts for use in testing
84
93
[('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,
87
122
class InvalidAdd(TestBase):
88
123
"""Try to use invalid version number during add."""
89
124
def runTest(self):
170
204
['text0', 'text1', 'text3'],
171
205
['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
173
self.assertEqual(k.annotate('text4'),
207
self.assertEqual(k.annotate('text4'),
174
208
[('text0', 'line 1'),
175
209
('text4', 'aaa'),
176
210
('text3', 'middle line'),
609
643
A Jug of Wine, a Loaf of Bread, -- and Thou
610
644
Beside me singing in the Wilderness --
611
645
Oh, Wilderness were Paradise enow!""",
613
647
"""A Book of Verses underneath the Bough,
614
648
A Jug of Wine, a Loaf of Bread, -- and Thou
615
649
Beside me singing in the Wilderness --
657
691
self.weave1.add_lines('v1', [], self.lines1)
658
692
self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
659
693
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')
661
765
def test_written_detection(self):
662
766
# Test detection of weave file corruption.
707
811
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:'))
725
814
class InstrumentedWeave(Weave):
726
815
"""Keep track of how many times functions are called."""
728
817
def __init__(self, weave_name=None):
729
818
self._extract_count = 0
730
819
Weave.__init__(self, weave_name=weave_name)
734
823
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
737
877
class TestNeedsReweave(TestCase):
738
878
"""Internal corner cases for when reweave is needed."""
750
890
self.assertFalse(w1._compatible_parents(set(), set([1])))
751
891
self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
752
892
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,