~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: Martin Pool
  • Date: 2005-05-11 08:01:27 UTC
  • Revision ID: mbp@sourcefrog.net-20050511080127-4829697fc2ac64f1
- put back support for running diff or status on 
  only selected files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python2.4
2
 
 
3
 
# Copyright (C) 2005 by Canonical Ltd
4
 
 
5
 
# This program is free software; you can redistribute it and/or modify
6
 
# it under the terms of the GNU General Public License as published by
7
 
# the Free Software Foundation; either version 2 of the License, or
8
 
# (at your option) any later version.
9
 
 
10
 
# This program is distributed in the hope that it will be useful,
11
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
# GNU General Public License for more details.
14
 
 
15
 
# You should have received a copy of the GNU General Public License
16
 
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 
 
19
 
 
20
 
# TODO: tests regarding version names
21
 
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
22
 
#       if it fails.
23
 
 
24
 
"""test suite for weave algorithm"""
25
 
 
26
 
from pprint import pformat
27
 
 
28
 
import bzrlib.errors as errors
29
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
30
 
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.selftest import TestCase
32
 
from bzrlib.osutils import sha_string
33
 
 
34
 
 
35
 
# texts for use in testing
36
 
TEXT_0 = ["Hello world"]
37
 
TEXT_1 = ["Hello world",
38
 
          "A second line"]
39
 
 
40
 
 
41
 
 
42
 
class TestBase(TestCase):
43
 
    def check_read_write(self, k):
44
 
        """Check the weave k can be written & re-read."""
45
 
        from tempfile import TemporaryFile
46
 
        tf = TemporaryFile()
47
 
 
48
 
        write_weave(k, tf)
49
 
        tf.seek(0)
50
 
        k2 = read_weave(tf)
51
 
 
52
 
        if k != k2:
53
 
            tf.seek(0)
54
 
            self.log('serialized weave:')
55
 
            self.log(tf.read())
56
 
 
57
 
            self.log('')
58
 
            self.log('parents: %s' % (k._parents == k2._parents))
59
 
            self.log('         %r' % k._parents)
60
 
            self.log('         %r' % k2._parents)
61
 
            self.log('')
62
 
            self.fail('read/write check failed')
63
 
 
64
 
 
65
 
class WeaveContains(TestBase):
66
 
    """Weave __contains__ operator"""
67
 
    def runTest(self):
68
 
        k = Weave()
69
 
        self.assertFalse('foo' in k)
70
 
        k.add('foo', [], TEXT_1)
71
 
        self.assertTrue('foo' in k)
72
 
 
73
 
 
74
 
class Easy(TestBase):
75
 
    def runTest(self):
76
 
        k = Weave()
77
 
 
78
 
 
79
 
class StoreText(TestBase):
80
 
    """Store and retrieve a simple text."""
81
 
    def runTest(self):
82
 
        k = Weave()
83
 
        idx = k.add('text0', [], TEXT_0)
84
 
        self.assertEqual(k.get(idx), TEXT_0)
85
 
        self.assertEqual(idx, 0)
86
 
 
87
 
 
88
 
 
89
 
class AnnotateOne(TestBase):
90
 
    def runTest(self):
91
 
        k = Weave()
92
 
        k.add('text0', [], TEXT_0)
93
 
        self.assertEqual(k.annotate(0),
94
 
                         [(0, TEXT_0[0])])
95
 
 
96
 
 
97
 
class StoreTwo(TestBase):
98
 
    def runTest(self):
99
 
        k = Weave()
100
 
 
101
 
        idx = k.add('text0', [], TEXT_0)
102
 
        self.assertEqual(idx, 0)
103
 
 
104
 
        idx = k.add('text1', [], TEXT_1)
105
 
        self.assertEqual(idx, 1)
106
 
 
107
 
        self.assertEqual(k.get(0), TEXT_0)
108
 
        self.assertEqual(k.get(1), TEXT_1)
109
 
 
110
 
 
111
 
 
112
 
class AddWithGivenSha(TestBase):
113
 
    def runTest(self):
114
 
        """Add with caller-supplied SHA-1"""
115
 
        k = Weave()
116
 
 
117
 
        t = 'text0'
118
 
        k.add('text0', [], [t], sha1=sha_string(t))
119
 
 
120
 
 
121
 
 
122
 
class InvalidAdd(TestBase):
123
 
    """Try to use invalid version number during add."""
124
 
    def runTest(self):
125
 
        k = Weave()
126
 
 
127
 
        self.assertRaises(IndexError,
128
 
                          k.add,
129
 
                          'text0',
130
 
                          [69],
131
 
                          ['new text!'])
132
 
 
133
 
 
134
 
class RepeatedAdd(TestBase):
135
 
    """Add the same version twice; harmless."""
136
 
    def runTest(self):
137
 
        k = Weave()
138
 
        idx = k.add('text0', [], TEXT_0)
139
 
        idx2 = k.add('text0', [], TEXT_0)
140
 
        self.assertEqual(idx, idx2)
141
 
 
142
 
 
143
 
 
144
 
class InvalidRepeatedAdd(TestBase):
145
 
    def runTest(self):
146
 
        k = Weave()
147
 
        idx = k.add('text0', [], TEXT_0)
148
 
        self.assertRaises(WeaveError,
149
 
                          k.add,
150
 
                          'text0',
151
 
                          [],
152
 
                          ['not the same text'])
153
 
        self.assertRaises(WeaveError,
154
 
                          k.add,
155
 
                          'text0',
156
 
                          [12],         # not the right parents
157
 
                          TEXT_0)
158
 
        
159
 
 
160
 
 
161
 
class InsertLines(TestBase):
162
 
    """Store a revision that adds one line to the original.
163
 
 
164
 
    Look at the annotations to make sure that the first line is matched
165
 
    and not stored repeatedly."""
166
 
    def runTest(self):
167
 
        k = Weave()
168
 
 
169
 
        k.add('text0', [], ['line 1'])
170
 
        k.add('text1', [0], ['line 1', 'line 2'])
171
 
 
172
 
        self.assertEqual(k.annotate(0),
173
 
                         [(0, 'line 1')])
174
 
 
175
 
        self.assertEqual(k.get(1),
176
 
                         ['line 1',
177
 
                          'line 2'])
178
 
 
179
 
        self.assertEqual(k.annotate(1),
180
 
                         [(0, 'line 1'),
181
 
                          (1, 'line 2')])
182
 
 
183
 
        k.add('text2', [0], ['line 1', 'diverged line'])
184
 
 
185
 
        self.assertEqual(k.annotate(2),
186
 
                         [(0, 'line 1'),
187
 
                          (2, 'diverged line')])
188
 
 
189
 
        text3 = ['line 1', 'middle line', 'line 2']
190
 
        k.add('text3',
191
 
              [0, 1],
192
 
              text3)
193
 
 
194
 
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
195
 
 
196
 
        self.log("k._weave=" + pformat(k._weave))
197
 
 
198
 
        self.assertEqual(k.annotate(3),
199
 
                         [(0, 'line 1'),
200
 
                          (3, 'middle line'),
201
 
                          (1, 'line 2')])
202
 
 
203
 
        # now multiple insertions at different places
204
 
        k.add('text4',
205
 
              [0, 1, 3],
206
 
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
207
 
 
208
 
        self.assertEqual(k.annotate(4), 
209
 
                         [(0, 'line 1'),
210
 
                          (4, 'aaa'),
211
 
                          (3, 'middle line'),
212
 
                          (4, 'bbb'),
213
 
                          (1, 'line 2'),
214
 
                          (4, 'ccc')])
215
 
 
216
 
 
217
 
 
218
 
class DeleteLines(TestBase):
219
 
    """Deletion of lines from existing text.
220
 
 
221
 
    Try various texts all based on a common ancestor."""
222
 
    def runTest(self):
223
 
        k = Weave()
224
 
 
225
 
        base_text = ['one', 'two', 'three', 'four']
226
 
 
227
 
        k.add('text0', [], base_text)
228
 
        
229
 
        texts = [['one', 'two', 'three'],
230
 
                 ['two', 'three', 'four'],
231
 
                 ['one', 'four'],
232
 
                 ['one', 'two', 'three', 'four'],
233
 
                 ]
234
 
 
235
 
        i = 1
236
 
        for t in texts:
237
 
            ver = k.add('text%d' % i,
238
 
                        [0], t)
239
 
            i += 1
240
 
 
241
 
        self.log('final weave:')
242
 
        self.log('k._weave=' + pformat(k._weave))
243
 
 
244
 
        for i in range(len(texts)):
245
 
            self.assertEqual(k.get(i+1),
246
 
                             texts[i])
247
 
            
248
 
 
249
 
 
250
 
 
251
 
class SuicideDelete(TestBase):
252
 
    """Invalid weave which tries to add and delete simultaneously."""
253
 
    def runTest(self):
254
 
        k = Weave()
255
 
 
256
 
        k._parents = [(),
257
 
                ]
258
 
        k._weave = [('{', 0),
259
 
                'first line',
260
 
                ('[', 0),
261
 
                'deleted in 0',
262
 
                (']', 0),
263
 
                ('}', 0),
264
 
                ]
265
 
        ################################### SKIPPED
266
 
        # Weave.get doesn't trap this anymore
267
 
        return 
268
 
 
269
 
        self.assertRaises(WeaveFormatError,
270
 
                          k.get,
271
 
                          0)        
272
 
 
273
 
 
274
 
 
275
 
class CannedDelete(TestBase):
276
 
    """Unpack canned weave with deleted lines."""
277
 
    def runTest(self):
278
 
        k = Weave()
279
 
 
280
 
        k._parents = [(),
281
 
                frozenset([0]),
282
 
                ]
283
 
        k._weave = [('{', 0),
284
 
                'first line',
285
 
                ('[', 1),
286
 
                'line to be deleted',
287
 
                (']', 1),
288
 
                'last line',
289
 
                ('}', 0),
290
 
                ]
291
 
 
292
 
        self.assertEqual(k.get(0),
293
 
                         ['first line',
294
 
                          'line to be deleted',
295
 
                          'last line',
296
 
                          ])
297
 
 
298
 
        self.assertEqual(k.get(1),
299
 
                         ['first line',
300
 
                          'last line',
301
 
                          ])
302
 
 
303
 
 
304
 
 
305
 
class CannedReplacement(TestBase):
306
 
    """Unpack canned weave with deleted lines."""
307
 
    def runTest(self):
308
 
        k = Weave()
309
 
 
310
 
        k._parents = [frozenset(),
311
 
                frozenset([0]),
312
 
                ]
313
 
        k._weave = [('{', 0),
314
 
                'first line',
315
 
                ('[', 1),
316
 
                'line to be deleted',
317
 
                (']', 1),
318
 
                ('{', 1),
319
 
                'replacement line',                
320
 
                ('}', 1),
321
 
                'last line',
322
 
                ('}', 0),
323
 
                ]
324
 
 
325
 
        self.assertEqual(k.get(0),
326
 
                         ['first line',
327
 
                          'line to be deleted',
328
 
                          'last line',
329
 
                          ])
330
 
 
331
 
        self.assertEqual(k.get(1),
332
 
                         ['first line',
333
 
                          'replacement line',
334
 
                          'last line',
335
 
                          ])
336
 
 
337
 
 
338
 
 
339
 
class BadWeave(TestBase):
340
 
    """Test that we trap an insert which should not occur."""
341
 
    def runTest(self):
342
 
        k = Weave()
343
 
 
344
 
        k._parents = [frozenset(),
345
 
                ]
346
 
        k._weave = ['bad line',
347
 
                ('{', 0),
348
 
                'foo {',
349
 
                ('{', 1),
350
 
                '  added in version 1',
351
 
                ('{', 2),
352
 
                '  added in v2',
353
 
                ('}', 2),
354
 
                '  also from v1',
355
 
                ('}', 1),
356
 
                '}',
357
 
                ('}', 0)]
358
 
 
359
 
        ################################### SKIPPED
360
 
        # Weave.get doesn't trap this anymore
361
 
        return 
362
 
 
363
 
 
364
 
        self.assertRaises(WeaveFormatError,
365
 
                          k.get,
366
 
                          0)
367
 
 
368
 
 
369
 
class BadInsert(TestBase):
370
 
    """Test that we trap an insert which should not occur."""
371
 
    def runTest(self):
372
 
        k = Weave()
373
 
 
374
 
        k._parents = [frozenset(),
375
 
                frozenset([0]),
376
 
                frozenset([0]),
377
 
                frozenset([0,1,2]),
378
 
                ]
379
 
        k._weave = [('{', 0),
380
 
                'foo {',
381
 
                ('{', 1),
382
 
                '  added in version 1',
383
 
                ('{', 1),
384
 
                '  more in 1',
385
 
                ('}', 1),
386
 
                ('}', 1),
387
 
                ('}', 0)]
388
 
 
389
 
 
390
 
        # this is not currently enforced by get
391
 
        return  ##########################################
392
 
 
393
 
        self.assertRaises(WeaveFormatError,
394
 
                          k.get,
395
 
                          0)
396
 
 
397
 
        self.assertRaises(WeaveFormatError,
398
 
                          k.get,
399
 
                          1)
400
 
 
401
 
 
402
 
class InsertNested(TestBase):
403
 
    """Insertion with nested instructions."""
404
 
    def runTest(self):
405
 
        k = Weave()
406
 
 
407
 
        k._parents = [frozenset(),
408
 
                frozenset([0]),
409
 
                frozenset([0]),
410
 
                frozenset([0,1,2]),
411
 
                ]
412
 
        k._weave = [('{', 0),
413
 
                'foo {',
414
 
                ('{', 1),
415
 
                '  added in version 1',
416
 
                ('{', 2),
417
 
                '  added in v2',
418
 
                ('}', 2),
419
 
                '  also from v1',
420
 
                ('}', 1),
421
 
                '}',
422
 
                ('}', 0)]
423
 
 
424
 
        self.assertEqual(k.get(0),
425
 
                         ['foo {',
426
 
                          '}'])
427
 
 
428
 
        self.assertEqual(k.get(1),
429
 
                         ['foo {',
430
 
                          '  added in version 1',
431
 
                          '  also from v1',
432
 
                          '}'])
433
 
                       
434
 
        self.assertEqual(k.get(2),
435
 
                         ['foo {',
436
 
                          '  added in v2',
437
 
                          '}'])
438
 
 
439
 
        self.assertEqual(k.get(3),
440
 
                         ['foo {',
441
 
                          '  added in version 1',
442
 
                          '  added in v2',
443
 
                          '  also from v1',
444
 
                          '}'])
445
 
                         
446
 
 
447
 
 
448
 
class DeleteLines2(TestBase):
449
 
    """Test recording revisions that delete lines.
450
 
 
451
 
    This relies on the weave having a way to represent lines knocked
452
 
    out by a later revision."""
453
 
    def runTest(self):
454
 
        k = Weave()
455
 
 
456
 
        k.add('text0', [], ["line the first",
457
 
                   "line 2",
458
 
                   "line 3",
459
 
                   "fine"])
460
 
 
461
 
        self.assertEqual(len(k.get(0)), 4)
462
 
 
463
 
        k.add('text1', [0], ["line the first",
464
 
                   "fine"])
465
 
 
466
 
        self.assertEqual(k.get(1),
467
 
                         ["line the first",
468
 
                          "fine"])
469
 
 
470
 
        self.assertEqual(k.annotate(1),
471
 
                         [(0, "line the first"),
472
 
                          (0, "fine")])
473
 
 
474
 
 
475
 
 
476
 
class IncludeVersions(TestBase):
477
 
    """Check texts that are stored across multiple revisions.
478
 
 
479
 
    Here we manually create a weave with particular encoding and make
480
 
    sure it unpacks properly.
481
 
 
482
 
    Text 0 includes nothing; text 1 includes text 0 and adds some
483
 
    lines.
484
 
    """
485
 
 
486
 
    def runTest(self):
487
 
        k = Weave()
488
 
 
489
 
        k._parents = [frozenset(), frozenset([0])]
490
 
        k._weave = [('{', 0),
491
 
                "first line",
492
 
                ('}', 0),
493
 
                ('{', 1),
494
 
                "second line",
495
 
                ('}', 1)]
496
 
 
497
 
        self.assertEqual(k.get(1),
498
 
                         ["first line",
499
 
                          "second line"])
500
 
 
501
 
        self.assertEqual(k.get(0),
502
 
                         ["first line"])
503
 
 
504
 
 
505
 
class DivergedIncludes(TestBase):
506
 
    """Weave with two diverged texts based on version 0.
507
 
    """
508
 
    def runTest(self):
509
 
        k = Weave()
510
 
 
511
 
        k._parents = [frozenset(),
512
 
                frozenset([0]),
513
 
                frozenset([0]),
514
 
                ]
515
 
        k._weave = [('{', 0),
516
 
                "first line",
517
 
                ('}', 0),
518
 
                ('{', 1),
519
 
                "second line",
520
 
                ('}', 1),
521
 
                ('{', 2),
522
 
                "alternative second line",
523
 
                ('}', 2),                
524
 
                ]
525
 
 
526
 
        self.assertEqual(k.get(0),
527
 
                         ["first line"])
528
 
 
529
 
        self.assertEqual(k.get(1),
530
 
                         ["first line",
531
 
                          "second line"])
532
 
 
533
 
        self.assertEqual(k.get(2),
534
 
                         ["first line",
535
 
                          "alternative second line"])
536
 
 
537
 
        self.assertEqual(list(k.inclusions([2])),
538
 
                         [0, 2])
539
 
 
540
 
 
541
 
 
542
 
class ReplaceLine(TestBase):
543
 
    def runTest(self):
544
 
        k = Weave()
545
 
 
546
 
        text0 = ['cheddar', 'stilton', 'gruyere']
547
 
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
548
 
        
549
 
        k.add('text0', [], text0)
550
 
        k.add('text1', [0], text1)
551
 
 
552
 
        self.log('k._weave=' + pformat(k._weave))
553
 
 
554
 
        self.assertEqual(k.get(0), text0)
555
 
        self.assertEqual(k.get(1), text1)
556
 
 
557
 
 
558
 
 
559
 
class Merge(TestBase):
560
 
    """Storage of versions that merge diverged parents"""
561
 
    def runTest(self):
562
 
        k = Weave()
563
 
 
564
 
        texts = [['header'],
565
 
                 ['header', '', 'line from 1'],
566
 
                 ['header', '', 'line from 2', 'more from 2'],
567
 
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
568
 
                 ]
569
 
 
570
 
        k.add('text0', [], texts[0])
571
 
        k.add('text1', [0], texts[1])
572
 
        k.add('text2', [0], texts[2])
573
 
        k.add('merge', [0, 1, 2], texts[3])
574
 
 
575
 
        for i, t in enumerate(texts):
576
 
            self.assertEqual(k.get(i), t)
577
 
 
578
 
        self.assertEqual(k.annotate(3),
579
 
                         [(0, 'header'),
580
 
                          (1, ''),
581
 
                          (1, 'line from 1'),
582
 
                          (3, 'fixup line'),
583
 
                          (2, 'line from 2'),
584
 
                          ])
585
 
 
586
 
        self.assertEqual(list(k.inclusions([3])),
587
 
                         [0, 1, 2, 3])
588
 
 
589
 
        self.log('k._weave=' + pformat(k._weave))
590
 
 
591
 
        self.check_read_write(k)
592
 
 
593
 
 
594
 
class Conflicts(TestBase):
595
 
    """Test detection of conflicting regions during a merge.
596
 
 
597
 
    A base version is inserted, then two descendents try to
598
 
    insert different lines in the same place.  These should be
599
 
    reported as a possible conflict and forwarded to the user."""
600
 
    def runTest(self):
601
 
        return  # NOT RUN
602
 
        k = Weave()
603
 
 
604
 
        k.add([], ['aaa', 'bbb'])
605
 
        k.add([0], ['aaa', '111', 'bbb'])
606
 
        k.add([1], ['aaa', '222', 'bbb'])
607
 
 
608
 
        merged = k.merge([1, 2])
609
 
 
610
 
        self.assertEquals([[['aaa']],
611
 
                           [['111'], ['222']],
612
 
                           [['bbb']]])
613
 
 
614
 
 
615
 
 
616
 
class NonConflict(TestBase):
617
 
    """Two descendants insert compatible changes.
618
 
 
619
 
    No conflict should be reported."""
620
 
    def runTest(self):
621
 
        return  # NOT RUN
622
 
        k = Weave()
623
 
 
624
 
        k.add([], ['aaa', 'bbb'])
625
 
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
626
 
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
627
 
 
628
 
    
629
 
    
630
 
 
631
 
 
632
 
class AutoMerge(TestBase):
633
 
    def runTest(self):
634
 
        k = Weave()
635
 
 
636
 
        texts = [['header', 'aaa', 'bbb'],
637
 
                 ['header', 'aaa', 'line from 1', 'bbb'],
638
 
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
639
 
                 ]
640
 
 
641
 
        k.add('text0', [], texts[0])
642
 
        k.add('text1', [0], texts[1])
643
 
        k.add('text2', [0], texts[2])
644
 
 
645
 
        self.log('k._weave=' + pformat(k._weave))
646
 
 
647
 
        m = list(k.mash_iter([0, 1, 2]))
648
 
 
649
 
        self.assertEqual(m,
650
 
                         ['header', 'aaa',
651
 
                          'line from 1',
652
 
                          'bbb',
653
 
                          'line from 2', 'more from 2'])
654
 
        
655
 
 
656
 
 
657
 
class Khayyam(TestBase):
658
 
    """Test changes to multi-line texts, and read/write"""
659
 
    def runTest(self):
660
 
        rawtexts = [
661
 
            """A Book of Verses underneath the Bough,
662
 
            A Jug of Wine, a Loaf of Bread, -- and Thou
663
 
            Beside me singing in the Wilderness --
664
 
            Oh, Wilderness were Paradise enow!""",
665
 
            
666
 
            """A Book of Verses underneath the Bough,
667
 
            A Jug of Wine, a Loaf of Bread, -- and Thou
668
 
            Beside me singing in the Wilderness --
669
 
            Oh, Wilderness were Paradise now!""",
670
 
 
671
 
            """A Book of poems underneath the tree,
672
 
            A Jug of Wine, a Loaf of Bread,
673
 
            and Thou
674
 
            Beside me singing in the Wilderness --
675
 
            Oh, Wilderness were Paradise now!
676
 
 
677
 
            -- O. Khayyam""",
678
 
 
679
 
            """A Book of Verses underneath the Bough,
680
 
            A Jug of Wine, a Loaf of Bread,
681
 
            and Thou
682
 
            Beside me singing in the Wilderness --
683
 
            Oh, Wilderness were Paradise now!""",
684
 
            ]
685
 
        texts = [[l.strip() for l in t.split('\n')] for t in rawtexts]
686
 
 
687
 
        k = Weave()
688
 
        parents = set()
689
 
        i = 0
690
 
        for t in texts:
691
 
            ver = k.add('text%d' % i,
692
 
                        list(parents), t)
693
 
            parents.add(ver)
694
 
            i += 1
695
 
 
696
 
        self.log("k._weave=" + pformat(k._weave))
697
 
 
698
 
        for i, t in enumerate(texts):
699
 
            self.assertEqual(k.get(i), t)
700
 
 
701
 
        self.check_read_write(k)
702
 
 
703
 
 
704
 
 
705
 
class MergeCases(TestBase):
706
 
    def doMerge(self, base, a, b, mp):
707
 
        from cStringIO import StringIO
708
 
        from textwrap import dedent
709
 
 
710
 
        def addcrlf(x):
711
 
            return x + '\n'
712
 
        
713
 
        w = Weave()
714
 
        w.add('text0', [], map(addcrlf, base))
715
 
        w.add('text1', [0], map(addcrlf, a))
716
 
        w.add('text2', [0], map(addcrlf, b))
717
 
 
718
 
        self.log('weave is:')
719
 
        tmpf = StringIO()
720
 
        write_weave(w, tmpf)
721
 
        self.log(tmpf.getvalue())
722
 
 
723
 
        self.log('merge plan:')
724
 
        p = list(w.plan_merge(1, 2))
725
 
        for state, line in p:
726
 
            if line:
727
 
                self.log('%12s | %s' % (state, line[:-1]))
728
 
 
729
 
        self.log('merge:')
730
 
        mt = StringIO()
731
 
        mt.writelines(w.weave_merge(p))
732
 
        mt.seek(0)
733
 
        self.log(mt.getvalue())
734
 
 
735
 
        mp = map(addcrlf, mp)
736
 
        self.assertEqual(mt.readlines(), mp)
737
 
        
738
 
        
739
 
    def testOneInsert(self):
740
 
        self.doMerge([],
741
 
                     ['aa'],
742
 
                     [],
743
 
                     ['aa'])
744
 
 
745
 
    def testSeparateInserts(self):
746
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
747
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
748
 
                     ['aaa', 'bbb', 'yyy', 'ccc'],
749
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
750
 
 
751
 
    def testSameInsert(self):
752
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
753
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
754
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
755
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
756
 
 
757
 
    def testOverlappedInsert(self):
758
 
        self.doMerge(['aaa', 'bbb'],
759
 
                     ['aaa', 'xxx', 'yyy', 'bbb'],
760
 
                     ['aaa', 'xxx', 'bbb'],
761
 
                     ['aaa', '<<<<<<<', 'xxx', 'yyy', '=======', 'xxx', 
762
 
                      '>>>>>>>', 'bbb'])
763
 
 
764
 
        # really it ought to reduce this to 
765
 
        # ['aaa', 'xxx', 'yyy', 'bbb']
766
 
 
767
 
 
768
 
    def testClashReplace(self):
769
 
        self.doMerge(['aaa'],
770
 
                     ['xxx'],
771
 
                     ['yyy', 'zzz'],
772
 
                     ['<<<<<<<', 'xxx', '=======', 'yyy', 'zzz', 
773
 
                      '>>>>>>>'])
774
 
 
775
 
    def testNonClashInsert(self):
776
 
        self.doMerge(['aaa'],
777
 
                     ['xxx', 'aaa'],
778
 
                     ['yyy', 'zzz'],
779
 
                     ['<<<<<<<', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
780
 
                      '>>>>>>>'])
781
 
 
782
 
        self.doMerge(['aaa'],
783
 
                     ['aaa'],
784
 
                     ['yyy', 'zzz'],
785
 
                     ['yyy', 'zzz'])
786
 
 
787
 
 
788
 
    def testDeleteAndModify(self):
789
 
        """Clashing delete and modification.
790
 
 
791
 
        If one side modifies a region and the other deletes it then
792
 
        there should be a conflict with one side blank.
793
 
        """
794
 
 
795
 
        #######################################
796
 
        # skippd, not working yet
797
 
        return
798
 
        
799
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
800
 
                     ['aaa', 'ddd', 'ccc'],
801
 
                     ['aaa', 'ccc'],
802
 
                     ['<<<<<<<<', 'aaa', '=======', '>>>>>>>', 'ccc'])
803
 
 
804
 
 
805
 
class JoinWeavesTests(TestBase):
806
 
    def setUp(self):
807
 
        super(JoinWeavesTests, self).setUp()
808
 
        self.weave1 = Weave()
809
 
        self.lines1 = ['hello\n']
810
 
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
811
 
        self.weave1.add('v1', [], self.lines1)
812
 
        self.weave1.add('v2', [0], ['hello\n', 'world\n'])
813
 
        self.weave1.add('v3', [1], self.lines3)
814
 
        
815
 
    def test_join_empty(self):
816
 
        """Join two empty weaves."""
817
 
        eq = self.assertEqual
818
 
        w1 = Weave()
819
 
        w2 = Weave()
820
 
        w1.join(w2)
821
 
        eq(w1.numversions(), 0)
822
 
        
823
 
    def test_join_empty_to_nonempty(self):
824
 
        """Join empty weave onto nonempty."""
825
 
        self.weave1.join(Weave())
826
 
        self.assertEqual(len(self.weave1), 3)
827
 
 
828
 
    def test_join_unrelated(self):
829
 
        """Join two weaves with no history in common."""
830
 
        wb = Weave()
831
 
        wb.add('b1', [], ['line from b\n'])
832
 
        w1 = self.weave1
833
 
        w1.join(wb)
834
 
        eq = self.assertEqual
835
 
        eq(len(w1), 4)
836
 
        eq(sorted(list(w1.iter_names())),
837
 
           ['b1', 'v1', 'v2', 'v3'])
838
 
 
839
 
    def test_join_related(self):
840
 
        wa = self.weave1.copy()
841
 
        wb = self.weave1.copy()
842
 
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
843
 
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
844
 
        eq = self.assertEquals
845
 
        eq(len(wa), 4)
846
 
        eq(len(wb), 4)
847
 
        wa.join(wb)
848
 
        eq(len(wa), 5)
849
 
        eq(wa.get_lines('b1'),
850
 
           ['hello\n', 'pale blue\n', 'world\n'])
851
 
 
852
 
    def test_join_parent_disagreement(self):
853
 
        """Cannot join weaves with different parents for a version."""
854
 
        wa = Weave()
855
 
        wb = Weave()
856
 
        wa.add('v1', [], ['hello\n'])
857
 
        wb.add('v0', [], [])
858
 
        wb.add('v1', ['v0'], ['hello\n'])
859
 
        self.assertRaises(WeaveError,
860
 
                          wa.join, wb)
861
 
 
862
 
    def test_join_text_disagreement(self):
863
 
        """Cannot join weaves with different texts for a version."""
864
 
        wa = Weave()
865
 
        wb = Weave()
866
 
        wa.add('v1', [], ['hello\n'])
867
 
        wb.add('v1', [], ['not\n', 'hello\n'])
868
 
        self.assertRaises(WeaveError,
869
 
                          wa.join, wb)
870
 
 
871
 
    def test_join_unordered(self):
872
 
        """Join weaves where indexes differ.
873
 
        
874
 
        The source weave contains a different version at index 0."""
875
 
        wa = self.weave1.copy()
876
 
        wb = Weave()
877
 
        wb.add('x1', [], ['line from x1\n'])
878
 
        wb.add('v1', [], ['hello\n'])
879
 
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
880
 
        wa.join(wb)
881
 
        eq = self.assertEquals
882
 
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
883
 
        eq(wa.get_text('x1'), 'line from x1\n')