~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_multiparent.py

  • Committer: John Arbash Meinel
  • Date: 2009-07-06 18:59:24 UTC
  • mto: This revision was merged to the branch mainline in revision 4522.
  • Revision ID: john@arbash-meinel.com-20090706185924-qlhn1j607117lgdj
Start implementing an Annotator.add_special_text functionality.

The Python implementation supports it. Basically, it is meant to allow things
like WT and PreviewTree to insert the 'current' content into the graph, so that
we can get local modifications into the annotations.
There is also some work here to get support for texts that are already cached
in the annotator. So that we avoid extracting them, and can shortcut the
history.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from unittest import TestCase
 
18
 
 
19
from bzrlib import (
 
20
    knit,
 
21
    multiparent,
 
22
    patiencediff,
 
23
    tests,
 
24
    )
 
25
 
 
26
 
 
27
LINES_1 = "a\nb\nc\nd\ne\n".splitlines(True)
 
28
LINES_2 = "a\nc\nd\ne\n".splitlines(True)
 
29
LINES_3 = "a\nb\nc\nd\n".splitlines(True)
 
30
LF_SPLIT_LINES = ['\x00\n', '\x00\r\x01\n', '\x02\r\xff']
 
31
 
 
32
 
 
33
class Mock(object):
 
34
 
 
35
    def __init__(self, **kwargs):
 
36
        self.__dict__ = kwargs
 
37
 
 
38
 
 
39
class TestMulti(TestCase):
 
40
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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
 
 
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)]))
 
102
 
 
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
 
 
108
    def test_from_patch(self):
 
109
        self.assertEqual(multiparent.MultiParent(
 
110
            [multiparent.NewText(['a\n']),
 
111
             multiparent.ParentText(0, 1, 2, 3)]),
 
112
             multiparent.MultiParent.from_patch('i 1\na\n\nc 0 1 2 3'))
 
113
        self.assertEqual(multiparent.MultiParent(
 
114
            [multiparent.NewText(['a']),
 
115
             multiparent.ParentText(0, 1, 2, 3)]),
 
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))
 
122
 
 
123
    def test_make_patch_from_binary(self):
 
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)
 
128
 
 
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
 
 
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
 
 
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']))
 
151
        self.assertFalse(multiparent.NewText(['a']) == Mock(lines=['a']))
 
152
 
 
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
 
 
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))
 
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()))
 
176
 
 
177
 
 
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
 
 
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
 
 
188
    def make_vf(self):
 
189
        vf = multiparent.MultiMemoryVersionedFile()
 
190
        self.add_version(vf, 'abcd', 'rev-a', [])
 
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'])
 
198
        vf.clear_cache()
 
199
        self.assertEqual(vf._lines, {})
 
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
 
 
207
    def test_reconstruct_empty(self):
 
208
        vf = multiparent.MultiMemoryVersionedFile()
 
209
        vf.add_version([], 'a', [])
 
210
        self.assertEqual([], self.reconstruct_version(vf, 'a'))
 
211
 
 
212
    @staticmethod
 
213
    def reconstruct(vf, revision_id, start, end):
 
214
        reconstructor = multiparent._Reconstructor(vf, vf._lines,
 
215
                                                   vf._parents)
 
216
        lines = []
 
217
        reconstructor._reconstruct(lines, revision_id, start, end)
 
218
        return lines
 
219
 
 
220
    @staticmethod
 
221
    def reconstruct_version(vf, revision_id):
 
222
        reconstructor = multiparent._Reconstructor(vf, vf._lines,
 
223
                                                   vf._parents)
 
224
        lines = []
 
225
        reconstructor.reconstruct_version(lines, revision_id)
 
226
        return lines
 
227
 
 
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))
 
233
        self.assertEqual(['a\n', 'b\n', 'e\n', 'f\n'],
 
234
                          self.reconstruct(vf, 'rev-c',  0, 4))
 
235
        self.assertEqual(['a\n', 'b\n', 'e\n', 'f\n'],
 
236
                          self.reconstruct_version(vf, 'rev-c'))
 
237
 
 
238
    def test_reordered(self):
 
239
        """Check for a corner case that requires re-starting the cursor"""
 
240
        vf = multiparent.MultiMemoryVersionedFile()
 
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)
 
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')