~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_knit.py

  • Committer: Martin Pool
  • Date: 2006-06-15 05:36:34 UTC
  • mto: This revision was merged to the branch mainline in revision 1797.
  • Revision ID: mbp@sourcefrog.net-20060615053634-4fd52ba691855659
Clean up many exception classes.

Errors indicating a user error are now shown with is_user_error on the
exception; use this rather than hardcoding a list of exceptions that should be
handled this way.

Exceptions now inherit from BzrNewException where possible to use consistent
formatting method.

Remove rather obsolete docstring test on Branch.missing_revisions.

Remove dead code from find_merge_base.


Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 by 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests for Knit data structure"""
 
18
 
 
19
 
 
20
import difflib
 
21
 
 
22
 
 
23
from bzrlib.errors import KnitError, RevisionAlreadyPresent
 
24
from bzrlib.knit import (
 
25
    KnitVersionedFile,
 
26
    KnitPlainFactory,
 
27
    KnitAnnotateFactory,
 
28
    WeaveToKnit)
 
29
from bzrlib.osutils import split_lines
 
30
from bzrlib.tests import TestCaseWithTransport
 
31
from bzrlib.transport import TransportLogger
 
32
from bzrlib.transport.local import LocalTransport
 
33
from bzrlib.transport.memory import MemoryTransport
 
34
from bzrlib.weave import Weave
 
35
 
 
36
 
 
37
class KnitTests(TestCaseWithTransport):
 
38
    """Class containing knit test helper routines."""
 
39
 
 
40
    def make_test_knit(self, annotate=False):
 
41
        if not annotate:
 
42
            factory = KnitPlainFactory()
 
43
        else:
 
44
            factory = None
 
45
        return KnitVersionedFile('test', LocalTransport('.'), access_mode='w', factory=factory, create=True)
 
46
 
 
47
 
 
48
class BasicKnitTests(KnitTests):
 
49
 
 
50
    def add_stock_one_and_one_a(self, k):
 
51
        k.add_lines('text-1', [], split_lines(TEXT_1))
 
52
        k.add_lines('text-1a', ['text-1'], split_lines(TEXT_1A))
 
53
 
 
54
    def test_knit_constructor(self):
 
55
        """Construct empty k"""
 
56
        self.make_test_knit()
 
57
 
 
58
    def test_knit_add(self):
 
59
        """Store one text in knit and retrieve"""
 
60
        k = self.make_test_knit()
 
61
        k.add_lines('text-1', [], split_lines(TEXT_1))
 
62
        self.assertTrue(k.has_version('text-1'))
 
63
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
 
64
 
 
65
    def test_knit_reload(self):
 
66
        # test that the content in a reloaded knit is correct
 
67
        k = self.make_test_knit()
 
68
        k.add_lines('text-1', [], split_lines(TEXT_1))
 
69
        del k
 
70
        k2 = KnitVersionedFile('test', LocalTransport('.'), access_mode='r', factory=KnitPlainFactory(), create=True)
 
71
        self.assertTrue(k2.has_version('text-1'))
 
72
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
 
73
 
 
74
    def test_knit_several(self):
 
75
        """Store several texts in a knit"""
 
76
        k = self.make_test_knit()
 
77
        k.add_lines('text-1', [], split_lines(TEXT_1))
 
78
        k.add_lines('text-2', [], split_lines(TEXT_2))
 
79
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
 
80
        self.assertEqualDiff(''.join(k.get_lines('text-2')), TEXT_2)
 
81
        
 
82
    def test_repeated_add(self):
 
83
        """Knit traps attempt to replace existing version"""
 
84
        k = self.make_test_knit()
 
85
        k.add_lines('text-1', [], split_lines(TEXT_1))
 
86
        self.assertRaises(RevisionAlreadyPresent, 
 
87
                k.add_lines,
 
88
                'text-1', [], split_lines(TEXT_1))
 
89
 
 
90
    def test_empty(self):
 
91
        k = self.make_test_knit(True)
 
92
        k.add_lines('text-1', [], [])
 
93
        self.assertEquals(k.get_lines('text-1'), [])
 
94
 
 
95
    def test_incomplete(self):
 
96
        """Test if texts without a ending line-end can be inserted and
 
97
        extracted."""
 
98
        k = KnitVersionedFile('test', LocalTransport('.'), delta=False, create=True)
 
99
        k.add_lines('text-1', [], ['a\n',    'b'  ])
 
100
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
 
101
        # reopening ensures maximum room for confusion
 
102
        k = KnitVersionedFile('test', LocalTransport('.'), delta=False, create=True)
 
103
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
 
104
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
 
105
 
 
106
    def test_delta(self):
 
107
        """Expression of knit delta as lines"""
 
108
        k = self.make_test_knit()
 
109
        td = list(line_delta(TEXT_1.splitlines(True),
 
110
                             TEXT_1A.splitlines(True)))
 
111
        self.assertEqualDiff(''.join(td), delta_1_1a)
 
112
        out = apply_line_delta(TEXT_1.splitlines(True), td)
 
113
        self.assertEqualDiff(''.join(out), TEXT_1A)
 
114
 
 
115
    def test_add_with_parents(self):
 
116
        """Store in knit with parents"""
 
117
        k = self.make_test_knit()
 
118
        self.add_stock_one_and_one_a(k)
 
119
        self.assertEquals(k.get_parents('text-1'), [])
 
120
        self.assertEquals(k.get_parents('text-1a'), ['text-1'])
 
121
 
 
122
    def test_ancestry(self):
 
123
        """Store in knit with parents"""
 
124
        k = self.make_test_knit()
 
125
        self.add_stock_one_and_one_a(k)
 
126
        self.assertEquals(set(k.get_ancestry(['text-1a'])), set(['text-1a', 'text-1']))
 
127
 
 
128
    def test_add_delta(self):
 
129
        """Store in knit with parents"""
 
130
        k = KnitVersionedFile('test', LocalTransport('.'), factory=KnitPlainFactory(),
 
131
            delta=True, create=True)
 
132
        self.add_stock_one_and_one_a(k)
 
133
        k.clear_cache()
 
134
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
 
135
 
 
136
    def test_annotate(self):
 
137
        """Annotations"""
 
138
        k = KnitVersionedFile('knit', LocalTransport('.'), factory=KnitAnnotateFactory(),
 
139
            delta=True, create=True)
 
140
        self.insert_and_test_small_annotate(k)
 
141
 
 
142
    def insert_and_test_small_annotate(self, k):
 
143
        """test annotation with k works correctly."""
 
144
        k.add_lines('text-1', [], ['a\n', 'b\n'])
 
145
        k.add_lines('text-2', ['text-1'], ['a\n', 'c\n'])
 
146
 
 
147
        origins = k.annotate('text-2')
 
148
        self.assertEquals(origins[0], ('text-1', 'a\n'))
 
149
        self.assertEquals(origins[1], ('text-2', 'c\n'))
 
150
 
 
151
    def test_annotate_fulltext(self):
 
152
        """Annotations"""
 
153
        k = KnitVersionedFile('knit', LocalTransport('.'), factory=KnitAnnotateFactory(),
 
154
            delta=False, create=True)
 
155
        self.insert_and_test_small_annotate(k)
 
156
 
 
157
    def test_annotate_merge_1(self):
 
158
        k = self.make_test_knit(True)
 
159
        k.add_lines('text-a1', [], ['a\n', 'b\n'])
 
160
        k.add_lines('text-a2', [], ['d\n', 'c\n'])
 
161
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['d\n', 'b\n'])
 
162
        origins = k.annotate('text-am')
 
163
        self.assertEquals(origins[0], ('text-a2', 'd\n'))
 
164
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
 
165
 
 
166
    def test_annotate_merge_2(self):
 
167
        k = self.make_test_knit(True)
 
168
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
 
169
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
 
170
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['a\n', 'y\n', 'c\n'])
 
171
        origins = k.annotate('text-am')
 
172
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
 
173
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
 
174
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
 
175
 
 
176
    def test_annotate_merge_9(self):
 
177
        k = self.make_test_knit(True)
 
178
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
 
179
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
 
180
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'c\n'])
 
181
        origins = k.annotate('text-am')
 
182
        self.assertEquals(origins[0], ('text-am', 'k\n'))
 
183
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
 
184
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
 
185
 
 
186
    def test_annotate_merge_3(self):
 
187
        k = self.make_test_knit(True)
 
188
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
 
189
        k.add_lines('text-a2', [] ,['x\n', 'y\n', 'z\n'])
 
190
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'z\n'])
 
191
        origins = k.annotate('text-am')
 
192
        self.assertEquals(origins[0], ('text-am', 'k\n'))
 
193
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
 
194
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
 
195
 
 
196
    def test_annotate_merge_4(self):
 
197
        k = self.make_test_knit(True)
 
198
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
 
199
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
 
200
        k.add_lines('text-a3', ['text-a1'], ['a\n', 'b\n', 'p\n'])
 
201
        k.add_lines('text-am', ['text-a2', 'text-a3'], ['a\n', 'b\n', 'z\n'])
 
202
        origins = k.annotate('text-am')
 
203
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
 
204
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
 
205
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
 
206
 
 
207
    def test_annotate_merge_5(self):
 
208
        k = self.make_test_knit(True)
 
209
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
 
210
        k.add_lines('text-a2', [], ['d\n', 'e\n', 'f\n'])
 
211
        k.add_lines('text-a3', [], ['x\n', 'y\n', 'z\n'])
 
212
        k.add_lines('text-am',
 
213
                    ['text-a1', 'text-a2', 'text-a3'],
 
214
                    ['a\n', 'e\n', 'z\n'])
 
215
        origins = k.annotate('text-am')
 
216
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
 
217
        self.assertEquals(origins[1], ('text-a2', 'e\n'))
 
218
        self.assertEquals(origins[2], ('text-a3', 'z\n'))
 
219
 
 
220
    def test_annotate_file_cherry_pick(self):
 
221
        k = self.make_test_knit(True)
 
222
        k.add_lines('text-1', [], ['a\n', 'b\n', 'c\n'])
 
223
        k.add_lines('text-2', ['text-1'], ['d\n', 'e\n', 'f\n'])
 
224
        k.add_lines('text-3', ['text-2', 'text-1'], ['a\n', 'b\n', 'c\n'])
 
225
        origins = k.annotate('text-3')
 
226
        self.assertEquals(origins[0], ('text-1', 'a\n'))
 
227
        self.assertEquals(origins[1], ('text-1', 'b\n'))
 
228
        self.assertEquals(origins[2], ('text-1', 'c\n'))
 
229
 
 
230
    def test_knit_join(self):
 
231
        """Store in knit with parents"""
 
232
        k1 = KnitVersionedFile('test1', LocalTransport('.'), factory=KnitPlainFactory(), create=True)
 
233
        k1.add_lines('text-a', [], split_lines(TEXT_1))
 
234
        k1.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
 
235
 
 
236
        k1.add_lines('text-c', [], split_lines(TEXT_1))
 
237
        k1.add_lines('text-d', ['text-c'], split_lines(TEXT_1))
 
238
 
 
239
        k1.add_lines('text-m', ['text-b', 'text-d'], split_lines(TEXT_1))
 
240
 
 
241
        k2 = KnitVersionedFile('test2', LocalTransport('.'), factory=KnitPlainFactory(), create=True)
 
242
        count = k2.join(k1, version_ids=['text-m'])
 
243
        self.assertEquals(count, 5)
 
244
        self.assertTrue(k2.has_version('text-a'))
 
245
        self.assertTrue(k2.has_version('text-c'))
 
246
 
 
247
    def test_reannotate(self):
 
248
        k1 = KnitVersionedFile('knit1', LocalTransport('.'),
 
249
                               factory=KnitAnnotateFactory(), create=True)
 
250
        # 0
 
251
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
 
252
        # 1
 
253
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
 
254
 
 
255
        k2 = KnitVersionedFile('test2', LocalTransport('.'),
 
256
                               factory=KnitAnnotateFactory(), create=True)
 
257
        k2.join(k1, version_ids=['text-b'])
 
258
 
 
259
        # 2
 
260
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
 
261
        # 2
 
262
        k2.add_lines('text-c', ['text-b'], ['z\n', 'c\n'])
 
263
        # 3
 
264
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
 
265
 
 
266
        # test-c will have index 3
 
267
        k1.join(k2, version_ids=['text-c'])
 
268
 
 
269
        lines = k1.get_lines('text-c')
 
270
        self.assertEquals(lines, ['z\n', 'c\n'])
 
271
 
 
272
        origins = k1.annotate('text-c')
 
273
        self.assertEquals(origins[0], ('text-c', 'z\n'))
 
274
        self.assertEquals(origins[1], ('text-b', 'c\n'))
 
275
 
 
276
    def test_extraction_reads_components_once(self):
 
277
        t = MemoryTransport()
 
278
        instrumented_t = TransportLogger(t)
 
279
        k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
 
280
        # should read the index
 
281
        self.assertEqual([('id.kndx',)], instrumented_t._calls)
 
282
        instrumented_t._calls = []
 
283
        # add a text       
 
284
        k1.add_lines('base', [], ['text\n'])
 
285
        # should not have read at all
 
286
        self.assertEqual([], instrumented_t._calls)
 
287
 
 
288
        # add a text
 
289
        k1.add_lines('sub', ['base'], ['text\n', 'text2\n'])
 
290
        # should not have read at all
 
291
        self.assertEqual([], instrumented_t._calls)
 
292
        
 
293
        # read a text
 
294
        k1.get_lines('sub')
 
295
        # should not have read at all
 
296
        self.assertEqual([], instrumented_t._calls)
 
297
 
 
298
        # clear the cache
 
299
        k1.clear_cache()
 
300
 
 
301
        # read a text
 
302
        k1.get_lines('base')
 
303
        # should have read a component
 
304
        # should not have read the first component only
 
305
        self.assertEqual([('id.knit', [(0, 87)])], instrumented_t._calls)
 
306
        instrumented_t._calls = []
 
307
        # read again
 
308
        k1.get_lines('base')
 
309
        # should not have read at all
 
310
        self.assertEqual([], instrumented_t._calls)
 
311
        # and now read the other component
 
312
        k1.get_lines('sub')
 
313
        # should have read the second component
 
314
        self.assertEqual([('id.knit', [(87, 93)])], instrumented_t._calls)
 
315
        instrumented_t._calls = []
 
316
 
 
317
        # clear the cache
 
318
        k1.clear_cache()
 
319
        # add a text cold 
 
320
        k1.add_lines('sub2', ['base'], ['text\n', 'text3\n'])
 
321
        # should read the first component only
 
322
        self.assertEqual([('id.knit', [(0, 87)])], instrumented_t._calls)
 
323
        
 
324
    def test_iter_lines_reads_in_order(self):
 
325
        t = MemoryTransport()
 
326
        instrumented_t = TransportLogger(t)
 
327
        k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
 
328
        self.assertEqual([('id.kndx',)], instrumented_t._calls)
 
329
        # add texts with no required ordering
 
330
        k1.add_lines('base', [], ['text\n'])
 
331
        k1.add_lines('base2', [], ['text2\n'])
 
332
        k1.clear_cache()
 
333
        instrumented_t._calls = []
 
334
        # request a last-first iteration
 
335
        results = list(k1.iter_lines_added_or_present_in_versions(['base2', 'base']))
 
336
        self.assertEqual([('id.knit', [(0, 87), (87, 89)])], instrumented_t._calls)
 
337
        self.assertEqual(['text\n', 'text2\n'], results)
 
338
 
 
339
    def test_create_empty_annotated(self):
 
340
        k1 = self.make_test_knit(True)
 
341
        # 0
 
342
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
 
343
        k2 = k1.create_empty('t', MemoryTransport())
 
344
        self.assertTrue(isinstance(k2.factory, KnitAnnotateFactory))
 
345
        self.assertEqual(k1.delta, k2.delta)
 
346
        # the generic test checks for empty content and file class
 
347
 
 
348
    def test_knit_format(self):
 
349
        # this tests that a new knit index file has the expected content
 
350
        # and that is writes the data we expect as records are added.
 
351
        knit = self.make_test_knit(True)
 
352
        self.assertFileEqual("# bzr knit index 8\n", 'test.kndx')
 
353
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
 
354
        self.assertFileEqual(
 
355
            "# bzr knit index 8\n"
 
356
            "\n"
 
357
            "revid fulltext 0 84 .a_ghost :",
 
358
            'test.kndx')
 
359
        knit.add_lines_with_ghosts('revid2', ['revid'], ['a\n'])
 
360
        self.assertFileEqual(
 
361
            "# bzr knit index 8\n"
 
362
            "\nrevid fulltext 0 84 .a_ghost :"
 
363
            "\nrevid2 line-delta 84 82 0 :",
 
364
            'test.kndx')
 
365
        # we should be able to load this file again
 
366
        knit = KnitVersionedFile('test', LocalTransport('.'), access_mode='r')
 
367
        self.assertEqual(['revid', 'revid2'], knit.versions())
 
368
        # write a short write to the file and ensure that its ignored
 
369
        indexfile = file('test.kndx', 'at')
 
370
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
 
371
        indexfile.close()
 
372
        # we should be able to load this file again
 
373
        knit = KnitVersionedFile('test', LocalTransport('.'), access_mode='w')
 
374
        self.assertEqual(['revid', 'revid2'], knit.versions())
 
375
        # and add a revision with the same id the failed write had
 
376
        knit.add_lines('revid3', ['revid2'], ['a\n'])
 
377
        # and when reading it revid3 should now appear.
 
378
        knit = KnitVersionedFile('test', LocalTransport('.'), access_mode='r')
 
379
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
 
380
        self.assertEqual(['revid2'], knit.get_parents('revid3'))
 
381
 
 
382
    def test_plan_merge(self):
 
383
        my_knit = self.make_test_knit(annotate=True)
 
384
        my_knit.add_lines('text1', [], split_lines(TEXT_1))
 
385
        my_knit.add_lines('text1a', ['text1'], split_lines(TEXT_1A))
 
386
        my_knit.add_lines('text1b', ['text1'], split_lines(TEXT_1B))
 
387
        plan = list(my_knit.plan_merge('text1a', 'text1b'))
 
388
        for plan_line, expected_line in zip(plan, AB_MERGE):
 
389
            self.assertEqual(plan_line, expected_line)
 
390
 
 
391
 
 
392
TEXT_1 = """\
 
393
Banana cup cakes:
 
394
 
 
395
- bananas
 
396
- eggs
 
397
- broken tea cups
 
398
"""
 
399
 
 
400
TEXT_1A = """\
 
401
Banana cup cake recipe
 
402
(serves 6)
 
403
 
 
404
- bananas
 
405
- eggs
 
406
- broken tea cups
 
407
- self-raising flour
 
408
"""
 
409
 
 
410
TEXT_1B = """\
 
411
Banana cup cake recipe
 
412
 
 
413
- bananas (do not use plantains!!!)
 
414
- broken tea cups
 
415
- flour
 
416
"""
 
417
 
 
418
delta_1_1a = """\
 
419
0,1,2
 
420
Banana cup cake recipe
 
421
(serves 6)
 
422
5,5,1
 
423
- self-raising flour
 
424
"""
 
425
 
 
426
TEXT_2 = """\
 
427
Boeuf bourguignon
 
428
 
 
429
- beef
 
430
- red wine
 
431
- small onions
 
432
- carrot
 
433
- mushrooms
 
434
"""
 
435
 
 
436
AB_MERGE_TEXT="""unchanged|Banana cup cake recipe
 
437
new-a|(serves 6)
 
438
unchanged|
 
439
killed-b|- bananas
 
440
killed-b|- eggs
 
441
new-b|- bananas (do not use plantains!!!)
 
442
unchanged|- broken tea cups
 
443
new-a|- self-raising flour
 
444
new-b|- flour
 
445
"""
 
446
AB_MERGE=[tuple(l.split('|')) for l in AB_MERGE_TEXT.splitlines(True)]
 
447
 
 
448
 
 
449
def line_delta(from_lines, to_lines):
 
450
    """Generate line-based delta from one text to another"""
 
451
    s = difflib.SequenceMatcher(None, from_lines, to_lines)
 
452
    for op in s.get_opcodes():
 
453
        if op[0] == 'equal':
 
454
            continue
 
455
        yield '%d,%d,%d\n' % (op[1], op[2], op[4]-op[3])
 
456
        for i in range(op[3], op[4]):
 
457
            yield to_lines[i]
 
458
 
 
459
 
 
460
def apply_line_delta(basis_lines, delta_lines):
 
461
    """Apply a line-based perfect diff
 
462
    
 
463
    basis_lines -- text to apply the patch to
 
464
    delta_lines -- diff instructions and content
 
465
    """
 
466
    out = basis_lines[:]
 
467
    i = 0
 
468
    offset = 0
 
469
    while i < len(delta_lines):
 
470
        l = delta_lines[i]
 
471
        a, b, c = map(long, l.split(','))
 
472
        i = i + 1
 
473
        out[offset+a:offset+b] = delta_lines[i:i+c]
 
474
        i = i + c
 
475
        offset = offset + (b - a) + c
 
476
    return out
 
477
 
 
478
 
 
479
class TestWeaveToKnit(KnitTests):
 
480
 
 
481
    def test_weave_to_knit_matches(self):
 
482
        # check that the WeaveToKnit is_compatible function
 
483
        # registers True for a Weave to a Knit.
 
484
        w = Weave()
 
485
        k = self.make_test_knit()
 
486
        self.failUnless(WeaveToKnit.is_compatible(w, k))
 
487
        self.failIf(WeaveToKnit.is_compatible(k, w))
 
488
        self.failIf(WeaveToKnit.is_compatible(w, w))
 
489
        self.failIf(WeaveToKnit.is_compatible(k, k))