~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: mbp at sourcefrog
  • Date: 2005-03-24 00:44:18 UTC
  • Revision ID: mbp@sourcefrog.net-20050324004418-b4a050f656c07f5f
show space usage for various stores in the info command

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