~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-30 22:27:17 UTC
  • Revision ID: mbp@sourcefrog.net-20050330222717-027b5837127b938d
experiment with new nested inventory file format
not used by default yet

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