~bzr-pqm/bzr/bzr.dev

2520.4.85 by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!)
1
# Copyright (C) 2007 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2520.4.85 by Aaron Bentley
Get all test passing (which just proves there aren't enough tests!)
16
0.9.1 by Aaron Bentley
Get trivial case passing
17
from unittest import TestCase
18
2520.4.2 by Aaron Bentley
Integrate mpdiff into bazaar
19
from bzrlib import (
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
20
    knit,
2520.4.2 by Aaron Bentley
Integrate mpdiff into bazaar
21
    multiparent,
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
22
    patiencediff,
2520.4.2 by Aaron Bentley
Integrate mpdiff into bazaar
23
    tests,
24
    )
0.9.1 by Aaron Bentley
Get trivial case passing
25
0.9.3 by Aaron Bentley
Get three-parent comparisions under test
26
0.9.1 by Aaron Bentley
Get trivial case passing
27
LINES_1 = "a\nb\nc\nd\ne\n".splitlines(True)
0.9.2 by Aaron Bentley
Get single-parent comparison working
28
LINES_2 = "a\nc\nd\ne\n".splitlines(True)
0.9.3 by Aaron Bentley
Get three-parent comparisions under test
29
LINES_3 = "a\nb\nc\nd\n".splitlines(True)
2520.4.30 by Aaron Bentley
Do our own line splitting for mp-diffs
30
LF_SPLIT_LINES = ['\x00\n', '\x00\r\x01\n', '\x02\r\xff']
0.9.2 by Aaron Bentley
Get single-parent comparison working
31
32
33
class Mock(object):
34
35
    def __init__(self, **kwargs):
36
        self.__dict__ = kwargs
37
0.9.1 by Aaron Bentley
Get trivial case passing
38
39
class TestMulti(TestCase):
40
0.9.2 by Aaron Bentley
Get single-parent comparison working
41
    def test_compare_no_parent(self):
42
        diff = multiparent.MultiParent.from_lines(LINES_1)
43
        self.assertEqual([multiparent.NewText(LINES_1)], diff.hunks)
44
45
    def test_compare_one_parent(self):
46
        diff = multiparent.MultiParent.from_lines(LINES_1, [LINES_2])
47
        self.assertEqual([multiparent.ParentText(0, 0, 0, 1),
48
                          multiparent.NewText(['b\n']),
49
                          multiparent.ParentText(0, 1, 2, 3)],
50
                         diff.hunks)
51
2520.4.138 by Aaron Bentley
Fix benign off-by-one error generating mpdiffs
52
        diff = multiparent.MultiParent.from_lines(LINES_2, [LINES_1])
53
        self.assertEqual([multiparent.ParentText(0, 0, 0, 1),
54
                          multiparent.ParentText(0, 2, 1, 3)],
55
                         diff.hunks)
56
0.9.3 by Aaron Bentley
Get three-parent comparisions under test
57
    def test_compare_two_parents(self):
58
        diff = multiparent.MultiParent.from_lines(LINES_1, [LINES_2, LINES_3])
59
        self.assertEqual([multiparent.ParentText(1, 0, 0, 4),
60
                          multiparent.ParentText(0, 3, 4, 1)],
61
                         diff.hunks)
62
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
63
    def test_compare_two_parents_blocks(self):
64
        matcher = patiencediff.PatienceSequenceMatcher(None, LINES_2, LINES_1)
65
        blocks = matcher.get_matching_blocks()
66
        diff = multiparent.MultiParent.from_lines(LINES_1, [LINES_2, LINES_3],
67
                                                  left_blocks=blocks)
68
        self.assertEqual([multiparent.ParentText(1, 0, 0, 4),
69
                          multiparent.ParentText(0, 3, 4, 1)],
70
                         diff.hunks)
71
2520.4.139 by Aaron Bentley
Support Multiparent.get_matching_blocks
72
    def test_get_matching_blocks(self):
73
        diff = multiparent.MultiParent.from_lines(LINES_1, [LINES_2])
74
        self.assertEqual([(0, 0, 1), (1, 2, 3), (4, 5, 0)],
75
                         list(diff.get_matching_blocks(0, len(LINES_2))))
76
77
        diff = multiparent.MultiParent.from_lines(LINES_2, [LINES_1])
78
        self.assertEqual([(0, 0, 1), (2, 1, 3), (5, 4, 0)],
79
                         list(diff.get_matching_blocks(0, len(LINES_1))))
80
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
81
    def test_range_iterator(self):
82
        diff = multiparent.MultiParent.from_lines(LINES_1, [LINES_2, LINES_3])
83
        diff.hunks.append(multiparent.NewText(['q\n']))
84
        self.assertEqual([(0, 4, 'parent', (1, 0, 4)),
85
                          (4, 5, 'parent', (0, 3, 4)),
86
                          (5, 6, 'new', ['q\n'])],
87
                         list(diff.range_iterator()))
88
0.9.2 by Aaron Bentley
Get single-parent comparison working
89
    def test_eq(self):
90
        diff = multiparent.MultiParent.from_lines(LINES_1)
91
        diff2 = multiparent.MultiParent.from_lines(LINES_1)
92
        self.assertEqual(diff, diff2)
93
        diff3 = multiparent.MultiParent.from_lines(LINES_2)
94
        self.assertFalse(diff == diff3)
95
        self.assertFalse(diff == Mock(hunks=[multiparent.NewText(LINES_1)]))
96
        self.assertEqual(multiparent.MultiParent(
97
                         [multiparent.NewText(LINES_1),
98
                          multiparent.ParentText(0, 1, 2, 3)]),
99
                         multiparent.MultiParent(
100
                         [multiparent.NewText(LINES_1),
101
                          multiparent.ParentText(0, 1, 2, 3)]))
0.9.1 by Aaron Bentley
Get trivial case passing
102
0.9.4 by Aaron Bentley
Start supporting serialization
103
    def test_to_patch(self):
104
        self.assertEqual(['i 1\n', 'a\n', '\n', 'c 0 1 2 3\n'],
105
            list(multiparent.MultiParent([multiparent.NewText(['a\n']),
106
            multiparent.ParentText(0, 1, 2, 3)]).to_patch()))
107
0.9.18 by Aaron Bentley
Implement from_patch
108
    def test_from_patch(self):
109
        self.assertEqual(multiparent.MultiParent(
110
            [multiparent.NewText(['a\n']),
111
             multiparent.ParentText(0, 1, 2, 3)]),
2520.4.30 by Aaron Bentley
Do our own line splitting for mp-diffs
112
             multiparent.MultiParent.from_patch('i 1\na\n\nc 0 1 2 3'))
0.9.18 by Aaron Bentley
Implement from_patch
113
        self.assertEqual(multiparent.MultiParent(
114
            [multiparent.NewText(['a']),
115
             multiparent.ParentText(0, 1, 2, 3)]),
2520.4.30 by Aaron Bentley
Do our own line splitting for mp-diffs
116
             multiparent.MultiParent.from_patch('i 1\na\nc 0 1 2 3\n'))
117
118
    def test_binary_content(self):
119
        patch = list(
120
            multiparent.MultiParent.from_lines(LF_SPLIT_LINES).to_patch())
121
        multiparent.MultiParent.from_patch(''.join(patch))
0.9.18 by Aaron Bentley
Implement from_patch
122
2520.4.90 by Aaron Bentley
Handle \r terminated lines in Weaves properly
123
    def test_make_patch_from_binary(self):
2520.4.92 by Aaron Bentley
fix line-splitting in MultiParent.from_texts
124
        patch = multiparent.MultiParent.from_texts(''.join(LF_SPLIT_LINES))
125
        expected = multiparent.MultiParent([
126
            multiparent.NewText(LF_SPLIT_LINES)])
127
        self.assertEqual(expected, patch)
2520.4.90 by Aaron Bentley
Handle \r terminated lines in Weaves properly
128
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
129
    def test_num_lines(self):
130
        mp = multiparent.MultiParent([multiparent.NewText(['a\n'])])
131
        self.assertEqual(1, mp.num_lines())
132
        mp.hunks.append(multiparent.NewText(['b\n', 'c\n']))
133
        self.assertEqual(3, mp.num_lines())
134
        mp.hunks.append(multiparent.ParentText(0, 0, 3, 2))
135
        self.assertEqual(5, mp.num_lines())
136
        mp.hunks.append(multiparent.NewText(['f\n', 'g\n']))
137
        self.assertEqual(7, mp.num_lines())
138
2520.4.103 by Aaron Bentley
Add MultiParent.to_lines
139
    def test_to_lines(self):
140
        mpdiff = multiparent.MultiParent.from_texts('a\nb\nc\n', ('b\nc\n',))
141
        lines = mpdiff.to_lines(('b\ne\n',))
142
        self.assertEqual(['a\n', 'b\n', 'e\n'], lines)
143
0.9.1 by Aaron Bentley
Get trivial case passing
144
145
class TestNewText(TestCase):
146
147
    def test_eq(self):
148
        self.assertEqual(multiparent.NewText([]), multiparent.NewText([]))
149
        self.assertFalse(multiparent.NewText(['a']) ==
150
                         multiparent.NewText(['b']))
0.9.2 by Aaron Bentley
Get single-parent comparison working
151
        self.assertFalse(multiparent.NewText(['a']) == Mock(lines=['a']))
152
0.9.4 by Aaron Bentley
Start supporting serialization
153
    def test_to_patch(self):
154
        self.assertEqual(['i 0\n', '\n'],
155
                         list(multiparent.NewText([]).to_patch()))
156
        self.assertEqual(['i 1\n', 'a', '\n'],
157
                         list(multiparent.NewText(['a']).to_patch()))
158
        self.assertEqual(['i 1\n', 'a\n', '\n'],
159
                         list(multiparent.NewText(['a\n']).to_patch()))
160
0.9.2 by Aaron Bentley
Get single-parent comparison working
161
162
class TestParentText(TestCase):
163
164
    def test_eq(self):
165
        self.assertEqual(multiparent.ParentText(1, 2, 3, 4),
166
                         multiparent.ParentText(1, 2, 3, 4))
167
        self.assertFalse(multiparent.ParentText(1, 2, 3, 4) ==
168
                         multiparent.ParentText(2, 2, 3, 4))
169
        self.assertFalse(multiparent.ParentText(1, 2, 3, 4) ==
170
                         Mock(parent=1, parent_pos=2, child_pos=3,
171
                              num_lines=4))
0.9.4 by Aaron Bentley
Start supporting serialization
172
173
    def test_to_patch(self):
174
        self.assertEqual(['c 0 1 2 3\n'],
175
                         list(multiparent.ParentText(0, 1, 2, 3).to_patch()))
0.9.8 by Aaron Bentley
get add_version working
176
177
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
178
REV_A = ['a\n', 'b\n', 'c\n', 'd\n']
179
REV_B = ['a\n', 'c\n', 'd\n', 'e\n']
180
REV_C = ['a\n', 'b\n', 'e\n', 'f\n']
181
182
0.9.8 by Aaron Bentley
get add_version working
183
class TestVersionedFile(TestCase):
184
185
    def add_version(self, vf, text, version_id, parent_ids):
186
        vf.add_version([(t+'\n') for t in text], version_id, parent_ids)
187
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
188
    def make_vf(self):
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
189
        vf = multiparent.MultiMemoryVersionedFile()
0.9.8 by Aaron Bentley
get add_version working
190
        self.add_version(vf, 'abcd', 'rev-a', [])
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
191
        self.add_version(vf, 'acde', 'rev-b', [])
192
        self.add_version(vf, 'abef', 'rev-c', ['rev-a', 'rev-b'])
193
        return vf
194
195
    def test_add_version(self):
196
        vf = self.make_vf()
197
        self.assertEqual(REV_A, vf._lines['rev-a'])
0.9.8 by Aaron Bentley
get add_version working
198
        vf.clear_cache()
199
        self.assertEqual(vf._lines, {})
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
200
201
    def test_get_line_list(self):
202
        vf = self.make_vf()
203
        vf.clear_cache()
204
        self.assertEqual(REV_A, vf.get_line_list(['rev-a'])[0])
205
        self.assertEqual([REV_B, REV_C], vf.get_line_list(['rev-b', 'rev-c']))
206
2520.4.16 by Aaron Bentley
Handle empty versions correctly
207
    def test_reconstruct_empty(self):
208
        vf = multiparent.MultiMemoryVersionedFile()
209
        vf.add_version([], 'a', [])
210
        self.assertEqual([], self.reconstruct_version(vf, 'a'))
211
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
212
    @staticmethod
213
    def reconstruct(vf, revision_id, start, end):
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
214
        reconstructor = multiparent._Reconstructor(vf, vf._lines,
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
215
                                                   vf._parents)
216
        lines = []
217
        reconstructor._reconstruct(lines, revision_id, start, end)
218
        return lines
219
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
220
    @staticmethod
221
    def reconstruct_version(vf, revision_id):
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
222
        reconstructor = multiparent._Reconstructor(vf, vf._lines,
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
223
                                                   vf._parents)
224
        lines = []
225
        reconstructor.reconstruct_version(lines, revision_id)
226
        return lines
227
0.9.9 by Aaron Bentley
Much progress on non-naive text reconstruction
228
    def test_reconstructor(self):
229
        vf = self.make_vf()
230
        self.assertEqual(['a\n', 'b\n'], self.reconstruct(vf, 'rev-a',  0, 2))
231
        self.assertEqual(['c\n', 'd\n'], self.reconstruct(vf, 'rev-a',  2, 4))
232
        self.assertEqual(['e\n', 'f\n'], self.reconstruct(vf, 'rev-c',  2, 4))
0.9.10 by Aaron Bentley
Text reconstruction seems to work
233
        self.assertEqual(['a\n', 'b\n', 'e\n', 'f\n'],
234
                          self.reconstruct(vf, 'rev-c',  0, 4))
0.9.11 by Aaron Bentley
Implement reconstruct_version, handle all hunks through that
235
        self.assertEqual(['a\n', 'b\n', 'e\n', 'f\n'],
236
                          self.reconstruct_version(vf, 'rev-c'))
0.9.22 by Aaron Bentley
Fix restoration bug
237
238
    def test_reordered(self):
239
        """Check for a corner case that requires re-starting the cursor"""
0.9.30 by Aaron Bentley
Split into MultiVersionedFile and MultiMemoryVersionedFile
240
        vf = multiparent.MultiMemoryVersionedFile()
0.9.22 by Aaron Bentley
Fix restoration bug
241
        # rev-b must have at least two hunks, so split a and b with c.
242
        self.add_version(vf, 'c', 'rev-a', [])
243
        self.add_version(vf, 'acb', 'rev-b', ['rev-a'])
244
        # rev-c and rev-d must each have a line from a different rev-b hunk
245
        self.add_version(vf, 'b', 'rev-c', ['rev-b'])
246
        self.add_version(vf, 'a', 'rev-d', ['rev-b'])
247
        # The lines from rev-c and rev-d must appear in the opposite order
248
        self.add_version(vf, 'ba', 'rev-e', ['rev-c', 'rev-d'])
249
        vf.clear_cache()
250
        lines = vf.get_line_list(['rev-e'])[0]
251
        self.assertEqual(['b\n', 'a\n'], lines)
0.9.34 by Aaron Bentley
Implement save, load, snapshot-by-size
252
253
254
class TestMultiVersionedFile(tests.TestCaseInTempDir):
255
256
    def test_save_load(self):
257
        vf = multiparent.MultiVersionedFile('foop')
258
        vf.add_version('a\nb\nc\nd'.splitlines(True), 'a', [])
259
        vf.add_version('a\ne\nd\n'.splitlines(True), 'b', ['a'])
260
        vf.save()
261
        newvf = multiparent.MultiVersionedFile('foop')
262
        newvf.load()
263
        self.assertEqual('a\nb\nc\nd', ''.join(newvf.get_line_list(['a'])[0]))
264
        self.assertEqual('a\ne\nd\n', ''.join(newvf.get_line_list(['b'])[0]))
265
266
    def test_filenames(self):
267
        vf = multiparent.MultiVersionedFile('foop')
268
        vf.add_version('a\nb\nc\nd'.splitlines(True), 'a', [])
269
        self.failUnlessExists('foop.mpknit')
270
        self.failIfExists('foop.mpidx')
271
        vf.save()
272
        self.failUnlessExists('foop.mpidx')
273
        vf.destroy()
274
        self.failIfExists('foop.mpknit')
275
        self.failIfExists('foop.mpidx')