~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Robert Collins
  • Date: 2006-03-11 13:58:48 UTC
  • mto: (1615.1.2 bzr.mbp.integration)
  • mto: This revision was merged to the branch mainline in revision 1616.
  • Revision ID: robertc@robertcollins.net-20060311135848-789fa616b8da4662
Note potential improvements in knit adds.

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
 
19
19
 
20
20
# TODO: tests regarding version names
21
 
 
22
 
 
 
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
 
22
#       if it fails.
23
23
 
24
24
"""test suite for weave algorithm"""
25
25
 
26
26
from pprint import pformat
27
27
 
28
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
 
28
import bzrlib.errors as errors
 
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
29
30
from bzrlib.weavefile import write_weave, read_weave
30
 
from bzrlib.selftest import TestCase
 
31
from bzrlib.tests import TestCase
31
32
from bzrlib.osutils import sha_string
32
33
 
33
34
 
37
38
          "A second line"]
38
39
 
39
40
 
40
 
 
41
41
class TestBase(TestCase):
42
42
    def check_read_write(self, k):
43
43
        """Check the weave k can be written & re-read."""
58
58
            self.log('         %r' % k._parents)
59
59
            self.log('         %r' % k2._parents)
60
60
            self.log('')
61
 
 
62
 
            
63
61
            self.fail('read/write check failed')
64
 
        
65
 
        
 
62
 
 
63
 
 
64
class WeaveContains(TestBase):
 
65
    """Weave __contains__ operator"""
 
66
    def runTest(self):
 
67
        k = Weave()
 
68
        self.assertFalse('foo' in k)
 
69
        k.add_lines('foo', [], TEXT_1)
 
70
        self.assertTrue('foo' in k)
66
71
 
67
72
 
68
73
class Easy(TestBase):
72
77
 
73
78
class StoreText(TestBase):
74
79
    """Store and retrieve a simple text."""
75
 
    def runTest(self):
 
80
 
 
81
    def test_storing_text(self):
76
82
        k = Weave()
77
 
        idx = k.add('text0', [], TEXT_0)
78
 
        self.assertEqual(k.get(idx), TEXT_0)
 
83
        idx = k.add_lines('text0', [], TEXT_0)
 
84
        self.assertEqual(k.get_lines(idx), TEXT_0)
79
85
        self.assertEqual(idx, 0)
80
86
 
81
87
 
82
 
 
83
88
class AnnotateOne(TestBase):
84
89
    def runTest(self):
85
90
        k = Weave()
86
 
        k.add('text0', [], TEXT_0)
87
 
        self.assertEqual(k.annotate(0),
88
 
                         [(0, TEXT_0[0])])
 
91
        k.add_lines('text0', [], TEXT_0)
 
92
        self.assertEqual(k.annotate('text0'),
 
93
                         [('text0', TEXT_0[0])])
89
94
 
90
95
 
91
96
class StoreTwo(TestBase):
92
97
    def runTest(self):
93
98
        k = Weave()
94
99
 
95
 
        idx = k.add('text0', [], TEXT_0)
 
100
        idx = k.add_lines('text0', [], TEXT_0)
96
101
        self.assertEqual(idx, 0)
97
102
 
98
 
        idx = k.add('text1', [], TEXT_1)
 
103
        idx = k.add_lines('text1', [], TEXT_1)
99
104
        self.assertEqual(idx, 1)
100
105
 
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"""
 
106
        self.assertEqual(k.get_lines(0), TEXT_0)
 
107
        self.assertEqual(k.get_lines(1), TEXT_1)
 
108
 
 
109
 
 
110
class GetSha1(TestBase):
 
111
    def test_get_sha1(self):
109
112
        k = Weave()
110
 
 
111
 
        t = 'text0'
112
 
        k.add('text0', [], [t], sha1=sha_string(t))
113
 
 
114
 
 
 
113
        k.add_lines('text0', [], 'text0')
 
114
        self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
 
115
                         k.get_sha1('text0'))
 
116
        self.assertRaises(errors.RevisionNotPresent,
 
117
                          k.get_sha1, 0)
 
118
        self.assertRaises(errors.RevisionNotPresent,
 
119
                          k.get_sha1, 'text1')
 
120
                        
115
121
 
116
122
class InvalidAdd(TestBase):
117
123
    """Try to use invalid version number during add."""
118
124
    def runTest(self):
119
125
        k = Weave()
120
126
 
121
 
        self.assertRaises(IndexError,
122
 
                          k.add,
 
127
        self.assertRaises(errors.RevisionNotPresent,
 
128
                          k.add_lines,
123
129
                          'text0',
124
 
                          [69],
 
130
                          ['69'],
125
131
                          ['new text!'])
126
132
 
127
133
 
129
135
    """Add the same version twice; harmless."""
130
136
    def runTest(self):
131
137
        k = Weave()
132
 
        idx = k.add('text0', [], TEXT_0)
133
 
        idx2 = k.add('text0', [], TEXT_0)
 
138
        idx = k.add_lines('text0', [], TEXT_0)
 
139
        idx2 = k.add_lines('text0', [], TEXT_0)
134
140
        self.assertEqual(idx, idx2)
135
141
 
136
142
 
137
 
 
138
143
class InvalidRepeatedAdd(TestBase):
139
144
    def runTest(self):
140
145
        k = Weave()
141
 
        idx = k.add('text0', [], TEXT_0)
142
 
        self.assertRaises(WeaveError,
143
 
                          k.add,
 
146
        k.add_lines('basis', [], TEXT_0)
 
147
        idx = k.add_lines('text0', [], TEXT_0)
 
148
        self.assertRaises(errors.RevisionAlreadyPresent,
 
149
                          k.add_lines,
144
150
                          'text0',
145
151
                          [],
146
152
                          ['not the same text'])
147
 
        self.assertRaises(WeaveError,
148
 
                          k.add,
 
153
        self.assertRaises(errors.RevisionAlreadyPresent,
 
154
                          k.add_lines,
149
155
                          'text0',
150
 
                          [12],         # not the right parents
 
156
                          ['basis'],         # not the right parents
151
157
                          TEXT_0)
152
158
        
153
159
 
154
 
 
155
160
class InsertLines(TestBase):
156
161
    """Store a revision that adds one line to the original.
157
162
 
160
165
    def runTest(self):
161
166
        k = Weave()
162
167
 
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),
 
168
        k.add_lines('text0', [], ['line 1'])
 
169
        k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
 
170
 
 
171
        self.assertEqual(k.annotate('text0'),
 
172
                         [('text0', 'line 1')])
 
173
 
 
174
        self.assertEqual(k.get_lines(1),
170
175
                         ['line 1',
171
176
                          'line 2'])
172
177
 
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')])
 
178
        self.assertEqual(k.annotate('text1'),
 
179
                         [('text0', 'line 1'),
 
180
                          ('text1', 'line 2')])
 
181
 
 
182
        k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
 
183
 
 
184
        self.assertEqual(k.annotate('text2'),
 
185
                         [('text0', 'line 1'),
 
186
                          ('text2', 'diverged line')])
182
187
 
183
188
        text3 = ['line 1', 'middle line', 'line 2']
184
 
        k.add('text3',
185
 
              [0, 1],
 
189
        k.add_lines('text3',
 
190
              ['text0', 'text1'],
186
191
              text3)
187
192
 
188
193
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
189
194
 
190
195
        self.log("k._weave=" + pformat(k._weave))
191
196
 
192
 
        self.assertEqual(k.annotate(3),
193
 
                         [(0, 'line 1'),
194
 
                          (3, 'middle line'),
195
 
                          (1, 'line 2')])
 
197
        self.assertEqual(k.annotate('text3'),
 
198
                         [('text0', 'line 1'),
 
199
                          ('text3', 'middle line'),
 
200
                          ('text1', 'line 2')])
196
201
 
197
202
        # now multiple insertions at different places
198
 
        k.add('text4',
199
 
              [0, 1, 3],
 
203
        k.add_lines('text4',
 
204
              ['text0', 'text1', 'text3'],
200
205
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
201
206
 
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
 
 
 
207
        self.assertEqual(k.annotate('text4'), 
 
208
                         [('text0', 'line 1'),
 
209
                          ('text4', 'aaa'),
 
210
                          ('text3', 'middle line'),
 
211
                          ('text4', 'bbb'),
 
212
                          ('text1', 'line 2'),
 
213
                          ('text4', 'ccc')])
210
214
 
211
215
 
212
216
class DeleteLines(TestBase):
218
222
 
219
223
        base_text = ['one', 'two', 'three', 'four']
220
224
 
221
 
        k.add('text0', [], base_text)
 
225
        k.add_lines('text0', [], base_text)
222
226
        
223
227
        texts = [['one', 'two', 'three'],
224
228
                 ['two', 'three', 'four'],
228
232
 
229
233
        i = 1
230
234
        for t in texts:
231
 
            ver = k.add('text%d' % i,
232
 
                        [0], t)
 
235
            ver = k.add_lines('text%d' % i,
 
236
                        ['text0'], t)
233
237
            i += 1
234
238
 
235
239
        self.log('final weave:')
236
240
        self.log('k._weave=' + pformat(k._weave))
237
241
 
238
242
        for i in range(len(texts)):
239
 
            self.assertEqual(k.get(i+1),
 
243
            self.assertEqual(k.get_lines(i+1),
240
244
                             texts[i])
241
 
            
242
 
 
243
245
 
244
246
 
245
247
class SuicideDelete(TestBase):
261
263
        return 
262
264
 
263
265
        self.assertRaises(WeaveFormatError,
264
 
                          k.get,
 
266
                          k.get_lines,
265
267
                          0)        
266
268
 
267
269
 
268
 
 
269
270
class CannedDelete(TestBase):
270
271
    """Unpack canned weave with deleted lines."""
271
272
    def runTest(self):
282
283
                'last line',
283
284
                ('}', 0),
284
285
                ]
 
286
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
287
                  , sha_string('first linelast line')]
285
288
 
286
 
        self.assertEqual(k.get(0),
 
289
        self.assertEqual(k.get_lines(0),
287
290
                         ['first line',
288
291
                          'line to be deleted',
289
292
                          'last line',
290
293
                          ])
291
294
 
292
 
        self.assertEqual(k.get(1),
 
295
        self.assertEqual(k.get_lines(1),
293
296
                         ['first line',
294
297
                          'last line',
295
298
                          ])
296
299
 
297
300
 
298
 
 
299
301
class CannedReplacement(TestBase):
300
302
    """Unpack canned weave with deleted lines."""
301
303
    def runTest(self):
315
317
                'last line',
316
318
                ('}', 0),
317
319
                ]
 
320
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
321
                  , sha_string('first linereplacement linelast line')]
318
322
 
319
 
        self.assertEqual(k.get(0),
 
323
        self.assertEqual(k.get_lines(0),
320
324
                         ['first line',
321
325
                          'line to be deleted',
322
326
                          'last line',
323
327
                          ])
324
328
 
325
 
        self.assertEqual(k.get(1),
 
329
        self.assertEqual(k.get_lines(1),
326
330
                         ['first line',
327
331
                          'replacement line',
328
332
                          'last line',
329
333
                          ])
330
334
 
331
335
 
332
 
 
333
336
class BadWeave(TestBase):
334
337
    """Test that we trap an insert which should not occur."""
335
338
    def runTest(self):
415
418
                '}',
416
419
                ('}', 0)]
417
420
 
418
 
        self.assertEqual(k.get(0),
 
421
        k._sha1s = [sha_string('foo {}')
 
422
                  , sha_string('foo {  added in version 1  also from v1}')
 
423
                  , sha_string('foo {  added in v2}')
 
424
                  , sha_string('foo {  added in version 1  added in v2  also from v1}')
 
425
                  ]
 
426
 
 
427
        self.assertEqual(k.get_lines(0),
419
428
                         ['foo {',
420
429
                          '}'])
421
430
 
422
 
        self.assertEqual(k.get(1),
 
431
        self.assertEqual(k.get_lines(1),
423
432
                         ['foo {',
424
433
                          '  added in version 1',
425
434
                          '  also from v1',
426
435
                          '}'])
427
436
                       
428
 
        self.assertEqual(k.get(2),
 
437
        self.assertEqual(k.get_lines(2),
429
438
                         ['foo {',
430
439
                          '  added in v2',
431
440
                          '}'])
432
441
 
433
 
        self.assertEqual(k.get(3),
 
442
        self.assertEqual(k.get_lines(3),
434
443
                         ['foo {',
435
444
                          '  added in version 1',
436
445
                          '  added in v2',
438
447
                          '}'])
439
448
                         
440
449
 
441
 
 
442
450
class DeleteLines2(TestBase):
443
451
    """Test recording revisions that delete lines.
444
452
 
447
455
    def runTest(self):
448
456
        k = Weave()
449
457
 
450
 
        k.add('text0', [], ["line the first",
 
458
        k.add_lines('text0', [], ["line the first",
451
459
                   "line 2",
452
460
                   "line 3",
453
461
                   "fine"])
454
462
 
455
 
        self.assertEqual(len(k.get(0)), 4)
 
463
        self.assertEqual(len(k.get_lines(0)), 4)
456
464
 
457
 
        k.add('text1', [0], ["line the first",
 
465
        k.add_lines('text1', ['text0'], ["line the first",
458
466
                   "fine"])
459
467
 
460
 
        self.assertEqual(k.get(1),
 
468
        self.assertEqual(k.get_lines(1),
461
469
                         ["line the first",
462
470
                          "fine"])
463
471
 
464
 
        self.assertEqual(k.annotate(1),
465
 
                         [(0, "line the first"),
466
 
                          (0, "fine")])
467
 
 
 
472
        self.assertEqual(k.annotate('text1'),
 
473
                         [('text0', "line the first"),
 
474
                          ('text0', "fine")])
468
475
 
469
476
 
470
477
class IncludeVersions(TestBase):
488
495
                "second line",
489
496
                ('}', 1)]
490
497
 
491
 
        self.assertEqual(k.get(1),
 
498
        k._sha1s = [sha_string('first line')
 
499
                  , sha_string('first linesecond line')]
 
500
 
 
501
        self.assertEqual(k.get_lines(1),
492
502
                         ["first line",
493
503
                          "second line"])
494
504
 
495
 
        self.assertEqual(k.get(0),
 
505
        self.assertEqual(k.get_lines(0),
496
506
                         ["first line"])
497
507
 
498
508
 
500
510
    """Weave with two diverged texts based on version 0.
501
511
    """
502
512
    def runTest(self):
 
513
        # FIXME make the weave, dont poke at it.
503
514
        k = Weave()
504
515
 
 
516
        k._names = ['0', '1', '2']
 
517
        k._name_map = {'0':0, '1':1, '2':2}
505
518
        k._parents = [frozenset(),
506
519
                frozenset([0]),
507
520
                frozenset([0]),
517
530
                ('}', 2),                
518
531
                ]
519
532
 
520
 
        self.assertEqual(k.get(0),
 
533
        k._sha1s = [sha_string('first line')
 
534
                  , sha_string('first linesecond line')
 
535
                  , sha_string('first linealternative second line')]
 
536
 
 
537
        self.assertEqual(k.get_lines(0),
521
538
                         ["first line"])
522
539
 
523
 
        self.assertEqual(k.get(1),
 
540
        self.assertEqual(k.get_lines(1),
524
541
                         ["first line",
525
542
                          "second line"])
526
543
 
527
 
        self.assertEqual(k.get(2),
 
544
        self.assertEqual(k.get_lines('2'),
528
545
                         ["first line",
529
546
                          "alternative second line"])
530
547
 
531
 
        self.assertEqual(list(k.inclusions([2])),
532
 
                         [0, 2])
533
 
 
 
548
        self.assertEqual(list(k.get_ancestry(['2'])),
 
549
                         ['0', '2'])
534
550
 
535
551
 
536
552
class ReplaceLine(TestBase):
540
556
        text0 = ['cheddar', 'stilton', 'gruyere']
541
557
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
542
558
        
543
 
        k.add('text0', [], text0)
544
 
        k.add('text1', [0], text1)
 
559
        k.add_lines('text0', [], text0)
 
560
        k.add_lines('text1', ['text0'], text1)
545
561
 
546
562
        self.log('k._weave=' + pformat(k._weave))
547
563
 
548
 
        self.assertEqual(k.get(0), text0)
549
 
        self.assertEqual(k.get(1), text1)
550
 
 
 
564
        self.assertEqual(k.get_lines(0), text0)
 
565
        self.assertEqual(k.get_lines(1), text1)
551
566
 
552
567
 
553
568
class Merge(TestBase):
561
576
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
562
577
                 ]
563
578
 
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])
 
579
        k.add_lines('text0', [], texts[0])
 
580
        k.add_lines('text1', ['text0'], texts[1])
 
581
        k.add_lines('text2', ['text0'], texts[2])
 
582
        k.add_lines('merge', ['text0', 'text1', 'text2'], texts[3])
568
583
 
569
584
        for i, t in enumerate(texts):
570
 
            self.assertEqual(k.get(i), t)
 
585
            self.assertEqual(k.get_lines(i), t)
571
586
 
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'),
 
587
        self.assertEqual(k.annotate('merge'),
 
588
                         [('text0', 'header'),
 
589
                          ('text1', ''),
 
590
                          ('text1', 'line from 1'),
 
591
                          ('merge', 'fixup line'),
 
592
                          ('text2', 'line from 2'),
578
593
                          ])
579
594
 
580
 
        self.assertEqual(list(k.inclusions([3])),
581
 
                         [0, 1, 2, 3])
 
595
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
596
                         ['text0', 'text1', 'text2', 'merge'])
582
597
 
583
598
        self.log('k._weave=' + pformat(k._weave))
584
599
 
595
610
        return  # NOT RUN
596
611
        k = Weave()
597
612
 
598
 
        k.add([], ['aaa', 'bbb'])
599
 
        k.add([0], ['aaa', '111', 'bbb'])
600
 
        k.add([1], ['aaa', '222', 'bbb'])
 
613
        k.add_lines([], ['aaa', 'bbb'])
 
614
        k.add_lines([0], ['aaa', '111', 'bbb'])
 
615
        k.add_lines([1], ['aaa', '222', 'bbb'])
601
616
 
602
617
        merged = k.merge([1, 2])
603
618
 
606
621
                           [['bbb']]])
607
622
 
608
623
 
609
 
 
610
624
class NonConflict(TestBase):
611
625
    """Two descendants insert compatible changes.
612
626
 
615
629
        return  # NOT RUN
616
630
        k = Weave()
617
631
 
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
 
        
 
632
        k.add_lines([], ['aaa', 'bbb'])
 
633
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
634
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
649
635
 
650
636
 
651
637
class Khayyam(TestBase):
652
638
    """Test changes to multi-line texts, and read/write"""
653
 
    def runTest(self):
 
639
 
 
640
    def test_multi_line_merge(self):
654
641
        rawtexts = [
655
642
            """A Book of Verses underneath the Bough,
656
643
            A Jug of Wine, a Loaf of Bread, -- and Thou
682
669
        parents = set()
683
670
        i = 0
684
671
        for t in texts:
685
 
            ver = k.add('text%d' % i,
 
672
            ver = k.add_lines('text%d' % i,
686
673
                        list(parents), t)
687
 
            parents.add(ver)
 
674
            parents.add('text%d' % i)
688
675
            i += 1
689
676
 
690
677
        self.log("k._weave=" + pformat(k._weave))
691
678
 
692
679
        for i, t in enumerate(texts):
693
 
            self.assertEqual(k.get(i), t)
 
680
            self.assertEqual(k.get_lines(i), t)
694
681
 
695
682
        self.check_read_write(k)
696
683
 
697
684
 
698
 
 
699
685
class MergeCases(TestBase):
700
686
    def doMerge(self, base, a, b, mp):
701
687
        from cStringIO import StringIO
705
691
            return x + '\n'
706
692
        
707
693
        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))
 
694
        w.add_lines('text0', [], map(addcrlf, base))
 
695
        w.add_lines('text1', ['text0'], map(addcrlf, a))
 
696
        w.add_lines('text2', ['text0'], map(addcrlf, b))
711
697
 
712
698
        self.log('weave is:')
713
699
        tmpf = StringIO()
715
701
        self.log(tmpf.getvalue())
716
702
 
717
703
        self.log('merge plan:')
718
 
        p = list(w.plan_merge(1, 2))
 
704
        p = list(w.plan_merge('text1', 'text2'))
719
705
        for state, line in p:
720
706
            if line:
721
707
                self.log('%12s | %s' % (state, line[:-1]))
752
738
        self.doMerge(['aaa', 'bbb'],
753
739
                     ['aaa', 'xxx', 'yyy', 'bbb'],
754
740
                     ['aaa', 'xxx', 'bbb'],
755
 
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
741
                     ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 'xxx', 
 
742
                      '>>>>>>> ', 'bbb'])
756
743
 
757
744
        # really it ought to reduce this to 
758
745
        # ['aaa', 'xxx', 'yyy', 'bbb']
762
749
        self.doMerge(['aaa'],
763
750
                     ['xxx'],
764
751
                     ['yyy', 'zzz'],
765
 
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
752
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
 
753
                      '>>>>>>> '])
766
754
 
767
755
    def testNonClashInsert(self):
768
756
        self.doMerge(['aaa'],
769
757
                     ['xxx', 'aaa'],
770
758
                     ['yyy', 'zzz'],
771
 
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
759
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
 
760
                      '>>>>>>> '])
772
761
 
773
762
        self.doMerge(['aaa'],
774
763
                     ['aaa'],
790
779
        self.doMerge(['aaa', 'bbb', 'ccc'],
791
780
                     ['aaa', 'ddd', 'ccc'],
792
781
                     ['aaa', 'ccc'],
793
 
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
 
782
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
794
783
 
795
784
 
796
785
class JoinWeavesTests(TestBase):
799
788
        self.weave1 = Weave()
800
789
        self.lines1 = ['hello\n']
801
790
        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)
 
791
        self.weave1.add_lines('v1', [], self.lines1)
 
792
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
793
        self.weave1.add_lines('v3', ['v2'], self.lines3)
805
794
        
806
795
    def test_join_empty(self):
807
796
        """Join two empty weaves."""
809
798
        w1 = Weave()
810
799
        w2 = Weave()
811
800
        w1.join(w2)
812
 
        eq(w1.numversions(), 0)
 
801
        eq(len(w1), 0)
813
802
        
814
803
    def test_join_empty_to_nonempty(self):
815
804
        """Join empty weave onto nonempty."""
819
808
    def test_join_unrelated(self):
820
809
        """Join two weaves with no history in common."""
821
810
        wb = Weave()
822
 
        wb.add('b1', [], ['line from b\n'])
 
811
        wb.add_lines('b1', [], ['line from b\n'])
823
812
        w1 = self.weave1
824
813
        w1.join(wb)
825
814
        eq = self.assertEqual
826
815
        eq(len(w1), 4)
827
 
        eq(sorted(list(w1.iter_names())),
 
816
        eq(sorted(w1.versions()),
828
817
           ['b1', 'v1', 'v2', 'v3'])
829
818
 
830
819
    def test_join_related(self):
831
820
        wa = self.weave1.copy()
832
821
        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'])
 
822
        wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
 
823
        wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
835
824
        eq = self.assertEquals
836
825
        eq(len(wa), 4)
837
826
        eq(len(wb), 4)
840
829
        eq(wa.get_lines('b1'),
841
830
           ['hello\n', 'pale blue\n', 'world\n'])
842
831
 
843
 
 
844
 
if __name__ == '__main__':
845
 
    import sys
846
 
    import unittest
847
 
    sys.exit(unittest.main())
 
832
    def test_join_parent_disagreement(self):
 
833
        #join reconciles differening parents into a union.
 
834
        wa = Weave()
 
835
        wb = Weave()
 
836
        wa.add_lines('v1', [], ['hello\n'])
 
837
        wb.add_lines('v0', [], [])
 
838
        wb.add_lines('v1', ['v0'], ['hello\n'])
 
839
        wa.join(wb)
 
840
        self.assertEqual(['v0'], wa.get_parents('v1'))
 
841
 
 
842
    def test_join_text_disagreement(self):
 
843
        """Cannot join weaves with different texts for a version."""
 
844
        wa = Weave()
 
845
        wb = Weave()
 
846
        wa.add_lines('v1', [], ['hello\n'])
 
847
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
 
848
        self.assertRaises(WeaveError,
 
849
                          wa.join, wb)
 
850
 
 
851
    def test_join_unordered(self):
 
852
        """Join weaves where indexes differ.
 
853
        
 
854
        The source weave contains a different version at index 0."""
 
855
        wa = self.weave1.copy()
 
856
        wb = Weave()
 
857
        wb.add_lines('x1', [], ['line from x1\n'])
 
858
        wb.add_lines('v1', [], ['hello\n'])
 
859
        wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
860
        wa.join(wb)
 
861
        eq = self.assertEquals
 
862
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
 
863
        eq(wa.get_text('x1'), 'line from x1\n')
 
864
 
 
865
    def test_written_detection(self):
 
866
        # Test detection of weave file corruption.
 
867
        #
 
868
        # Make sure that we can detect if a weave file has
 
869
        # been corrupted. This doesn't test all forms of corruption,
 
870
        # but it at least helps verify the data you get, is what you want.
 
871
        from cStringIO import StringIO
 
872
 
 
873
        w = Weave()
 
874
        w.add_lines('v1', [], ['hello\n'])
 
875
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
876
 
 
877
        tmpf = StringIO()
 
878
        write_weave(w, tmpf)
 
879
 
 
880
        # Because we are corrupting, we need to make sure we have the exact text
 
881
        self.assertEquals('# bzr weave file v5\n'
 
882
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
883
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
884
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
 
885
                          tmpf.getvalue())
 
886
 
 
887
        # Change a single letter
 
888
        tmpf = StringIO('# bzr weave file v5\n'
 
889
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
890
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
891
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
 
892
 
 
893
        w = read_weave(tmpf)
 
894
 
 
895
        self.assertEqual('hello\n', w.get_text('v1'))
 
896
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
897
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
898
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
899
 
 
900
        # Change the sha checksum
 
901
        tmpf = StringIO('# bzr weave file v5\n'
 
902
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
903
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
904
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
 
905
 
 
906
        w = read_weave(tmpf)
 
907
 
 
908
        self.assertEqual('hello\n', w.get_text('v1'))
 
909
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
910
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
911
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
912
 
 
913
 
 
914
class InstrumentedWeave(Weave):
 
915
    """Keep track of how many times functions are called."""
848
916
    
 
917
    def __init__(self, weave_name=None):
 
918
        self._extract_count = 0
 
919
        Weave.__init__(self, weave_name=weave_name)
 
920
 
 
921
    def _extract(self, versions):
 
922
        self._extract_count += 1
 
923
        return Weave._extract(self, versions)
 
924
 
 
925
 
 
926
class JoinOptimization(TestCase):
 
927
    """Test that Weave.join() doesn't extract all texts, only what must be done."""
 
928
 
 
929
    def test_join(self):
 
930
        w1 = InstrumentedWeave()
 
931
        w2 = InstrumentedWeave()
 
932
 
 
933
        txt0 = ['a\n']
 
934
        txt1 = ['a\n', 'b\n']
 
935
        txt2 = ['a\n', 'c\n']
 
936
        txt3 = ['a\n', 'b\n', 'c\n']
 
937
 
 
938
        w1.add_lines('txt0', [], txt0) # extract 1a
 
939
        w2.add_lines('txt0', [], txt0) # extract 1b
 
940
        w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
 
941
        w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
 
942
        w1.join(w2) # extract 3a to add txt2 
 
943
        w2.join(w1) # extract 3b to add txt1 
 
944
 
 
945
        w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a 
 
946
        w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
 
947
        # These secretly have inverted parents
 
948
 
 
949
        # This should not have to do any extractions
 
950
        w1.join(w2) # NO extract, texts already present with same parents
 
951
        w2.join(w1) # NO extract, texts already present with same parents
 
952
 
 
953
        self.assertEqual(4, w1._extract_count)
 
954
        self.assertEqual(4, w2._extract_count)
 
955
 
 
956
    def test_double_parent(self):
 
957
        # It should not be considered illegal to add
 
958
        # a revision with the same parent twice
 
959
        w1 = InstrumentedWeave()
 
960
        w2 = InstrumentedWeave()
 
961
 
 
962
        txt0 = ['a\n']
 
963
        txt1 = ['a\n', 'b\n']
 
964
        txt2 = ['a\n', 'c\n']
 
965
        txt3 = ['a\n', 'b\n', 'c\n']
 
966
 
 
967
        w1.add_lines('txt0', [], txt0)
 
968
        w2.add_lines('txt0', [], txt0)
 
969
        w1.add_lines('txt1', ['txt0'], txt1)
 
970
        w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
 
971
        # Same text, effectively the same, because the
 
972
        # parent is only repeated
 
973
        w1.join(w2) # extract 3a to add txt2 
 
974
        w2.join(w1) # extract 3b to add txt1 
 
975
 
 
976
 
 
977
class TestNeedsRweave(TestCase):
 
978
    """Internal corner cases for when reweave is needed."""
 
979
 
 
980
    def test_compatible_parents(self):
 
981
        w1 = Weave('a')
 
982
        my_parents = set([1, 2, 3])
 
983
        # subsets are ok
 
984
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
 
985
        # same sets
 
986
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
 
987
        # same empty corner case
 
988
        self.assertTrue(w1._compatible_parents(set(), set()))
 
989
        # other cannot contain stuff my_parents does not
 
990
        self.assertFalse(w1._compatible_parents(set(), set([1])))
 
991
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
 
992
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
 
993
        
 
994