~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-06-26 16:23:10 UTC
  • mfrom: (1780.2.1 misc-fixen)
  • mto: This revision was merged to the branch mainline in revision 1815.
  • Revision ID: robertc@robertcollins.net-20060626162310-98f5b55b8cc19d46
(robertc) Misc minor typos and the like.

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
 
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
685
class JoinWeavesTests(TestBase):
797
686
    def setUp(self):
798
687
        super(JoinWeavesTests, self).setUp()
799
688
        self.weave1 = Weave()
800
689
        self.lines1 = ['hello\n']
801
690
        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)
 
691
        self.weave1.add_lines('v1', [], self.lines1)
 
692
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
693
        self.weave1.add_lines('v3', ['v2'], self.lines3)
805
694
        
806
695
    def test_join_empty(self):
807
696
        """Join two empty weaves."""
809
698
        w1 = Weave()
810
699
        w2 = Weave()
811
700
        w1.join(w2)
812
 
        eq(w1.numversions(), 0)
 
701
        eq(len(w1), 0)
813
702
        
814
703
    def test_join_empty_to_nonempty(self):
815
704
        """Join empty weave onto nonempty."""
819
708
    def test_join_unrelated(self):
820
709
        """Join two weaves with no history in common."""
821
710
        wb = Weave()
822
 
        wb.add('b1', [], ['line from b\n'])
 
711
        wb.add_lines('b1', [], ['line from b\n'])
823
712
        w1 = self.weave1
824
713
        w1.join(wb)
825
714
        eq = self.assertEqual
826
715
        eq(len(w1), 4)
827
 
        eq(sorted(list(w1.iter_names())),
 
716
        eq(sorted(w1.versions()),
828
717
           ['b1', 'v1', 'v2', 'v3'])
829
718
 
830
719
    def test_join_related(self):
831
720
        wa = self.weave1.copy()
832
721
        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'])
 
722
        wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
 
723
        wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
835
724
        eq = self.assertEquals
836
725
        eq(len(wa), 4)
837
726
        eq(len(wb), 4)
841
730
           ['hello\n', 'pale blue\n', 'world\n'])
842
731
 
843
732
    def test_join_parent_disagreement(self):
844
 
        """Cannot join weaves with different parents for a version."""
 
733
        #join reconciles differening parents into a union.
845
734
        wa = Weave()
846
735
        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)
 
736
        wa.add_lines('v1', [], ['hello\n'])
 
737
        wb.add_lines('v0', [], [])
 
738
        wb.add_lines('v1', ['v0'], ['hello\n'])
 
739
        wa.join(wb)
 
740
        self.assertEqual(['v0'], wa.get_parents('v1'))
852
741
 
853
742
    def test_join_text_disagreement(self):
854
743
        """Cannot join weaves with different texts for a version."""
855
744
        wa = Weave()
856
745
        wb = Weave()
857
 
        wa.add('v1', [], ['hello\n'])
858
 
        wb.add('v1', [], ['not\n', 'hello\n'])
 
746
        wa.add_lines('v1', [], ['hello\n'])
 
747
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
859
748
        self.assertRaises(WeaveError,
860
749
                          wa.join, wb)
861
750
 
865
754
        The source weave contains a different version at index 0."""
866
755
        wa = self.weave1.copy()
867
756
        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_join_with_ghosts(self):
877
 
        """Join that inserts parents of an existing revision.
878
 
 
879
 
        This can happen when merging from another branch who
880
 
        knows about revisions the destination does not.  In 
881
 
        this test the second weave knows of an additional parent of 
882
 
        v2.  Any revisions which are in common still have to have the 
883
 
        same text."""
884
 
        return ###############################
885
 
        wa = self.weave1.copy()
886
 
        wb = Weave()
887
 
        wb.add('x1', [], ['line from x1\n'])
888
 
        wb.add('v1', [], ['hello\n'])
889
 
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
890
 
        wa.join(wb)
891
 
        eq = self.assertEquals
892
 
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
893
 
        eq(wa.get_text('x1'), 'line from x1\n')
894
 
 
895
 
 
896
 
if __name__ == '__main__':
897
 
    import sys
898
 
    import unittest
899
 
    sys.exit(unittest.main())
 
757
        wb.add_lines('x1', [], ['line from x1\n'])
 
758
        wb.add_lines('v1', [], ['hello\n'])
 
759
        wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
760
        wa.join(wb)
 
761
        eq = self.assertEquals
 
762
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
 
763
        eq(wa.get_text('x1'), 'line from x1\n')
 
764
 
 
765
    def test_written_detection(self):
 
766
        # Test detection of weave file corruption.
 
767
        #
 
768
        # Make sure that we can detect if a weave file has
 
769
        # been corrupted. This doesn't test all forms of corruption,
 
770
        # but it at least helps verify the data you get, is what you want.
 
771
        from cStringIO import StringIO
 
772
 
 
773
        w = Weave()
 
774
        w.add_lines('v1', [], ['hello\n'])
 
775
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
776
 
 
777
        tmpf = StringIO()
 
778
        write_weave(w, tmpf)
 
779
 
 
780
        # Because we are corrupting, we need to make sure we have the exact text
 
781
        self.assertEquals('# bzr weave file v5\n'
 
782
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
783
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
784
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
 
785
                          tmpf.getvalue())
 
786
 
 
787
        # Change a single letter
 
788
        tmpf = StringIO('# bzr weave file v5\n'
 
789
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
790
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
791
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
 
792
 
 
793
        w = read_weave(tmpf)
 
794
 
 
795
        self.assertEqual('hello\n', w.get_text('v1'))
 
796
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
797
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
798
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
799
 
 
800
        # Change the sha checksum
 
801
        tmpf = StringIO('# bzr weave file v5\n'
 
802
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
803
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
804
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
 
805
 
 
806
        w = read_weave(tmpf)
 
807
 
 
808
        self.assertEqual('hello\n', w.get_text('v1'))
 
809
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
810
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
811
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
812
 
 
813
 
 
814
class InstrumentedWeave(Weave):
 
815
    """Keep track of how many times functions are called."""
900
816
    
 
817
    def __init__(self, weave_name=None):
 
818
        self._extract_count = 0
 
819
        Weave.__init__(self, weave_name=weave_name)
 
820
 
 
821
    def _extract(self, versions):
 
822
        self._extract_count += 1
 
823
        return Weave._extract(self, versions)
 
824
 
 
825
 
 
826
class JoinOptimization(TestCase):
 
827
    """Test that Weave.join() doesn't extract all texts, only what must be done."""
 
828
 
 
829
    def test_join(self):
 
830
        w1 = InstrumentedWeave()
 
831
        w2 = InstrumentedWeave()
 
832
 
 
833
        txt0 = ['a\n']
 
834
        txt1 = ['a\n', 'b\n']
 
835
        txt2 = ['a\n', 'c\n']
 
836
        txt3 = ['a\n', 'b\n', 'c\n']
 
837
 
 
838
        w1.add_lines('txt0', [], txt0) # extract 1a
 
839
        w2.add_lines('txt0', [], txt0) # extract 1b
 
840
        w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
 
841
        w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
 
842
        w1.join(w2) # extract 3a to add txt2 
 
843
        w2.join(w1) # extract 3b to add txt1 
 
844
 
 
845
        w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a 
 
846
        w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
 
847
        # These secretly have inverted parents
 
848
 
 
849
        # This should not have to do any extractions
 
850
        w1.join(w2) # NO extract, texts already present with same parents
 
851
        w2.join(w1) # NO extract, texts already present with same parents
 
852
 
 
853
        self.assertEqual(4, w1._extract_count)
 
854
        self.assertEqual(4, w2._extract_count)
 
855
 
 
856
    def test_double_parent(self):
 
857
        # It should not be considered illegal to add
 
858
        # a revision with the same parent twice
 
859
        w1 = InstrumentedWeave()
 
860
        w2 = InstrumentedWeave()
 
861
 
 
862
        txt0 = ['a\n']
 
863
        txt1 = ['a\n', 'b\n']
 
864
        txt2 = ['a\n', 'c\n']
 
865
        txt3 = ['a\n', 'b\n', 'c\n']
 
866
 
 
867
        w1.add_lines('txt0', [], txt0)
 
868
        w2.add_lines('txt0', [], txt0)
 
869
        w1.add_lines('txt1', ['txt0'], txt1)
 
870
        w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
 
871
        # Same text, effectively the same, because the
 
872
        # parent is only repeated
 
873
        w1.join(w2) # extract 3a to add txt2 
 
874
        w2.join(w1) # extract 3b to add txt1 
 
875
 
 
876
 
 
877
class TestNeedsReweave(TestCase):
 
878
    """Internal corner cases for when reweave is needed."""
 
879
 
 
880
    def test_compatible_parents(self):
 
881
        w1 = Weave('a')
 
882
        my_parents = set([1, 2, 3])
 
883
        # subsets are ok
 
884
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
 
885
        # same sets
 
886
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
 
887
        # same empty corner case
 
888
        self.assertTrue(w1._compatible_parents(set(), set()))
 
889
        # other cannot contain stuff my_parents does not
 
890
        self.assertFalse(w1._compatible_parents(set(), set([1])))
 
891
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
 
892
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))