/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: Aaron Bentley
  • Date: 2005-10-27 23:09:26 UTC
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1491.
  • Revision ID: abentley@panoramicfeedback.com-20051027230926-4e9911d404596430
Got reprocessing working

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', 
 
757
                      '>>>>>>>', 'bbb'])
 
758
 
 
759
        # really it ought to reduce this to 
 
760
        # ['aaa', 'xxx', 'yyy', 'bbb']
 
761
 
 
762
 
 
763
    def testClashReplace(self):
 
764
        self.doMerge(['aaa'],
 
765
                     ['xxx'],
 
766
                     ['yyy', 'zzz'],
 
767
                     ['<<<<<<<', 'xxx', '=======', 'yyy', 'zzz', 
 
768
                      '>>>>>>>'])
 
769
 
 
770
    def testNonClashInsert(self):
 
771
        self.doMerge(['aaa'],
 
772
                     ['xxx', 'aaa'],
 
773
                     ['yyy', 'zzz'],
 
774
                     ['<<<<<<<', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
 
775
                      '>>>>>>>'])
 
776
 
 
777
        self.doMerge(['aaa'],
 
778
                     ['aaa'],
 
779
                     ['yyy', 'zzz'],
 
780
                     ['yyy', 'zzz'])
 
781
 
 
782
 
 
783
    def testDeleteAndModify(self):
 
784
        """Clashing delete and modification.
 
785
 
 
786
        If one side modifies a region and the other deletes it then
 
787
        there should be a conflict with one side blank.
 
788
        """
 
789
 
 
790
        #######################################
 
791
        # skippd, not working yet
 
792
        return
 
793
        
 
794
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
795
                     ['aaa', 'ddd', 'ccc'],
 
796
                     ['aaa', 'ccc'],
 
797
                     ['<<<<<<<<', 'aaa', '=======', '>>>>>>>', 'ccc'])
 
798
 
 
799
 
 
800
class JoinWeavesTests(TestBase):
 
801
    def setUp(self):
 
802
        super(JoinWeavesTests, self).setUp()
 
803
        self.weave1 = Weave()
 
804
        self.lines1 = ['hello\n']
 
805
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
 
806
        self.weave1.add('v1', [], self.lines1)
 
807
        self.weave1.add('v2', [0], ['hello\n', 'world\n'])
 
808
        self.weave1.add('v3', [1], self.lines3)
 
809
        
 
810
    def test_join_empty(self):
 
811
        """Join two empty weaves."""
 
812
        eq = self.assertEqual
 
813
        w1 = Weave()
 
814
        w2 = Weave()
 
815
        w1.join(w2)
 
816
        eq(w1.numversions(), 0)
 
817
        
 
818
    def test_join_empty_to_nonempty(self):
 
819
        """Join empty weave onto nonempty."""
 
820
        self.weave1.join(Weave())
 
821
        self.assertEqual(len(self.weave1), 3)
 
822
 
 
823
    def test_join_unrelated(self):
 
824
        """Join two weaves with no history in common."""
 
825
        wb = Weave()
 
826
        wb.add('b1', [], ['line from b\n'])
 
827
        w1 = self.weave1
 
828
        w1.join(wb)
 
829
        eq = self.assertEqual
 
830
        eq(len(w1), 4)
 
831
        eq(sorted(list(w1.iter_names())),
 
832
           ['b1', 'v1', 'v2', 'v3'])
 
833
 
 
834
    def test_join_related(self):
 
835
        wa = self.weave1.copy()
 
836
        wb = self.weave1.copy()
 
837
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
 
838
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
 
839
        eq = self.assertEquals
 
840
        eq(len(wa), 4)
 
841
        eq(len(wb), 4)
 
842
        wa.join(wb)
 
843
        eq(len(wa), 5)
 
844
        eq(wa.get_lines('b1'),
 
845
           ['hello\n', 'pale blue\n', 'world\n'])
 
846
 
 
847
    def test_join_parent_disagreement(self):
 
848
        """Cannot join weaves with different parents for a version."""
 
849
        wa = Weave()
 
850
        wb = Weave()
 
851
        wa.add('v1', [], ['hello\n'])
 
852
        wb.add('v0', [], [])
 
853
        wb.add('v1', ['v0'], ['hello\n'])
 
854
        self.assertRaises(WeaveError,
 
855
                          wa.join, wb)
 
856
 
 
857
    def test_join_text_disagreement(self):
 
858
        """Cannot join weaves with different texts for a version."""
 
859
        wa = Weave()
 
860
        wb = Weave()
 
861
        wa.add('v1', [], ['hello\n'])
 
862
        wb.add('v1', [], ['not\n', 'hello\n'])
 
863
        self.assertRaises(WeaveError,
 
864
                          wa.join, wb)
 
865
 
 
866
    def test_join_unordered(self):
 
867
        """Join weaves where indexes differ.
 
868
        
 
869
        The source weave contains a different version at index 0."""
 
870
        wa = self.weave1.copy()
 
871
        wb = Weave()
 
872
        wb.add('x1', [], ['line from x1\n'])
 
873
        wb.add('v1', [], ['hello\n'])
 
874
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
 
875
        wa.join(wb)
 
876
        eq = self.assertEquals
 
877
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
878
        eq(wa.get_text('x1'), 'line from x1\n')
 
879
 
 
880
    def test_reweave_with_empty(self):
 
881
        wb = Weave()
 
882
        wr = reweave(self.weave1, wb)
 
883
        eq = self.assertEquals
 
884
        eq(sorted(wr.iter_names()), ['v1', 'v2', 'v3'])
 
885
        eq(wr.get_lines('v3'), ['hello\n', 'cruel\n', 'world\n'])
 
886
        self.weave1.reweave(wb)
 
887
        self.assertEquals(wr, self.weave1)
 
888
 
 
889
    def test_join_with_ghosts_raises_parent_mismatch(self):
 
890
        wa = self.weave1.copy()
 
891
        wb = Weave()
 
892
        wb.add('x1', [], ['line from x1\n'])
 
893
        wb.add('v1', [], ['hello\n'])
 
894
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
 
895
        self.assertRaises(errors.WeaveParentMismatch, wa.join, wb)
 
896
 
 
897
    def test_reweave_with_ghosts(self):
 
898
        """Join that inserts parents of an existing revision.
 
899
 
 
900
        This can happen when merging from another branch who
 
901
        knows about revisions the destination does not.  In 
 
902
        this test the second weave knows of an additional parent of 
 
903
        v2.  Any revisions which are in common still have to have the 
 
904
        same text."""
 
905
        wa = self.weave1.copy()
 
906
        wb = Weave()
 
907
        wb.add('x1', [], ['line from x1\n'])
 
908
        wb.add('v1', [], ['hello\n'])
 
909
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
 
910
        wc = reweave(wa, wb)
 
911
        eq = self.assertEquals
 
912
        eq(sorted(wc.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
913
        eq(wc.get_text('x1'), 'line from x1\n')
 
914
        eq(wc.get_lines('v2'), ['hello\n', 'world\n'])
 
915
        eq(wc.parent_names('v2'), ['v1', 'x1'])
 
916
        self.weave1.reweave(wb)
 
917
        self.assertEquals(wc, self.weave1)
 
918
 
 
919
 
 
920
if __name__ == '__main__':
 
921
    import sys
 
922
    import unittest
 
923
    sys.exit(unittest.main())
 
924