~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-20 07:55:43 UTC
  • mfrom: (1798 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1799.
  • Revision ID: mbp@sourcefrog.net-20060620075543-b10f6575d4a4fa32
[merge] bzr.dev

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