~bzr-pqm/bzr/bzr.dev

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