1
# Copyright (C) 2008, 2009 Canonical Ltd
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.
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.
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
17
"""Tests for the pyrex extension of groupcompress"""
19
from bzrlib import tests
21
from bzrlib import groupcompress
24
class _CompiledGroupCompress(tests.Feature):
28
import bzrlib._groupcompress_pyx
34
def feature_name(self):
35
return 'bzrlib._groupcompress_pyx'
37
CompiledGroupCompress = _CompiledGroupCompress()
42
which is meant to be matched
49
which is meant to differ from
56
which is meant to be matched
60
at the end of the file
66
common with the next text
70
some more bit of text, that
72
common with the previous text
73
and has some extra text
79
has some in common with the previous text
80
and has some extra text
82
common with the next text
96
class Test_GroupCompress(tests.TestCase):
97
"""Direct tests for the compiled extension."""
100
super(Test_GroupCompress, self).setUp()
101
self.requireFeature(CompiledGroupCompress)
102
from bzrlib import _groupcompress_pyx
103
self._gc_module = _groupcompress_pyx
106
class TestMakeAndApplyDelta(Test_GroupCompress):
109
super(TestMakeAndApplyDelta, self).setUp()
110
self.make_delta = self._gc_module.make_delta
111
self.apply_delta = self._gc_module.apply_delta
113
def test_make_delta_is_typesafe(self):
114
self.make_delta('a string', 'another string')
115
self.assertRaises(TypeError,
116
self.make_delta, 'a string', object())
117
self.assertRaises(TypeError,
118
self.make_delta, 'a string', u'not a string')
119
self.assertRaises(TypeError,
120
self.make_delta, object(), 'a string')
121
self.assertRaises(TypeError,
122
self.make_delta, u'not a string', 'a string')
124
def test_make_noop_delta(self):
125
ident_delta = self.make_delta(_text1, _text1)
126
self.assertEqual('MM\x90M', ident_delta)
127
ident_delta = self.make_delta(_text2, _text2)
128
self.assertEqual('NN\x90N', ident_delta)
129
ident_delta = self.make_delta(_text3, _text3)
130
self.assertEqual('\x87\x01\x87\x01\x90\x87', ident_delta)
132
def test_make_delta(self):
133
delta = self.make_delta(_text1, _text2)
134
self.assertEqual('MN\x90/\x1fdiffer from\nagainst other text\n', delta)
135
delta = self.make_delta(_text2, _text1)
136
self.assertEqual('NM\x90/\x1ebe matched\nagainst other text\n', delta)
137
delta = self.make_delta(_text3, _text1)
138
self.assertEqual('\x87\x01M\x90M', delta)
139
delta = self.make_delta(_text3, _text2)
140
self.assertEqual('\x87\x01N\x90/\x1fdiffer from\nagainst other text\n',
143
def test_apply_delta_is_typesafe(self):
144
self.apply_delta(_text1, 'MM\x90M')
145
self.assertRaises(TypeError,
146
self.apply_delta, object(), 'MM\x90M')
147
self.assertRaises(TypeError,
148
self.apply_delta, unicode(_text1), 'MM\x90M')
149
self.assertRaises(TypeError,
150
self.apply_delta, _text1, u'MM\x90M')
151
self.assertRaises(TypeError,
152
self.apply_delta, _text1, object())
154
def test_apply_delta(self):
155
target = self.apply_delta(_text1,
156
'MN\x90/\x1fdiffer from\nagainst other text\n')
157
self.assertEqual(_text2, target)
158
target = self.apply_delta(_text2,
159
'NM\x90/\x1ebe matched\nagainst other text\n')
160
self.assertEqual(_text1, target)
163
class TestDeltaIndex(Test_GroupCompress):
166
di = self._gc_module.DeltaIndex('test text\n')
167
self.assertEqual('DeltaIndex(1, 10)', repr(di))
169
def test_make_delta(self):
170
di = self._gc_module.DeltaIndex(_text1)
171
delta = di.make_delta(_text2)
172
self.assertEqual('MN\x90/\x1fdiffer from\nagainst other text\n', delta)
174
def test_delta_against_multiple_sources(self):
175
di = self._gc_module.DeltaIndex()
176
di.add_source(_first_text, 0)
177
self.assertEqual(len(_first_text), di._source_offset)
178
di.add_source(_second_text, 0)
179
self.assertEqual(len(_first_text) + len(_second_text), di._source_offset)
180
delta = di.make_delta(_third_text)
181
result = self._gc_module.apply_delta(_first_text + _second_text, delta)
182
self.assertEqualDiff(_third_text, result)
183
self.assertEqual('\xac\x01\x85\x01\x90\x14\x0chas some in '
184
'\x91v6\x03and\x91d"\x91:\n', delta)
186
def test_delta_with_offsets(self):
187
di = self._gc_module.DeltaIndex()
188
di.add_source(_first_text, 5)
189
self.assertEqual(len(_first_text) + 5, di._source_offset)
190
di.add_source(_second_text, 10)
191
self.assertEqual(len(_first_text) + len(_second_text) + 15,
193
delta = di.make_delta(_third_text)
194
self.assertIsNot(None, delta)
195
result = self._gc_module.apply_delta(
196
'12345' + _first_text + '1234567890' + _second_text, delta)
197
self.assertIsNot(None, result)
198
self.assertEqualDiff(_third_text, result)
199
self.assertEqual('\xbb\x01\x85\x01\x91\x05\x14\x0chas some in '
200
'\x91\x856\x03and\x91s"\x91?\n', delta)
202
def test_delta_with_delta_bytes(self):
203
di = self._gc_module.DeltaIndex()
205
di.add_source(_first_text, 0)
206
self.assertEqual(len(_first_text), di._source_offset)
207
delta = di.make_delta(_second_text)
208
self.assertEqual('Dh\tsome more\x91\x019'
209
'&previous text\nand has some extra text\n', delta)
210
di.add_delta_source(delta, 0)
212
self.assertEqual(len(_first_text) + len(delta), di._source_offset)
213
second_delta = di.make_delta(_third_text)
214
result = self._gc_module.apply_delta(source, second_delta)
215
self.assertEqualDiff(_third_text, result)
216
# We should be able to match against the 'previous text\nand has some...'
217
# that was part of the delta bytes
218
# Note that we don't match the 'common with the', because it isn't long
219
# enough to match in the original text, and those bytes are not present
220
# in the delta for the second text.
221
self.assertEqual('z\x85\x01\x90\x14\x1chas some in common with the '
222
'\x91T&\x03and\x91\x18,', second_delta)
223
# Add this delta, and create a new delta for the same text. We should
224
# find the remaining text, and only insert the short 'and' text.
225
di.add_delta_source(second_delta, 0)
226
source += second_delta
227
third_delta = di.make_delta(_third_text)
228
result = self._gc_module.apply_delta(source, third_delta)
229
self.assertEqualDiff(_third_text, result)
230
self.assertEqual('\xa6\x01\x85\x01\x90\x14\x91\x80\x1c'
231
'\x91T&\x03and\x91\x18,', third_delta)
232
# Now create a delta, which we know won't be able to be 'fit' into the
234
fourth_delta = di.make_delta(_fourth_text)
235
self.assertEqual(_fourth_text,
236
self._gc_module.apply_delta(source, fourth_delta))
237
self.assertEqual('\xa6\x01\x80\x01'
238
'\x7f123456789012345\nsame rabin hash\n'
239
'123456789012345\nsame rabin hash\n'
240
'123456789012345\nsame rabin hash\n'
241
'123456789012345\nsame rabin hash'
242
'\x01\n', fourth_delta)
243
di.add_delta_source(fourth_delta, 0)
244
source += fourth_delta
245
# With the next delta, everything should be found
246
fifth_delta = di.make_delta(_fourth_text)
247
self.assertEqual(_fourth_text,
248
self._gc_module.apply_delta(source, fifth_delta))
249
self.assertEqual('\xac\x02\x80\x01\x91\xab\x7f\x01\n', fifth_delta)