~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Olaf Conradi
  • Date: 2006-03-28 23:30:02 UTC
  • mto: (1661.1.1 bzr.mbp.remember)
  • mto: This revision was merged to the branch mainline in revision 1663.
  • Revision ID: olaf@conradi.org-20060328233002-f6262df0e19c1963
Added testcases for using pull with --remember. Moved remember code to
beginning of cmd_pull. This remembers the location in case of a failure
during pull.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
import bzrlib.errors as errors
29
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
30
30
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.selftest import TestCase
 
31
from bzrlib.tests import TestCase
32
32
from bzrlib.osutils import sha_string
33
33
 
34
34
 
38
38
          "A second line"]
39
39
 
40
40
 
41
 
 
42
41
class TestBase(TestCase):
43
42
    def check_read_write(self, k):
44
43
        """Check the weave k can be written & re-read."""
59
58
            self.log('         %r' % k._parents)
60
59
            self.log('         %r' % k2._parents)
61
60
            self.log('')
62
 
 
63
 
            
64
61
            self.fail('read/write check failed')
65
 
        
66
 
        
 
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)
67
71
 
68
72
 
69
73
class Easy(TestBase):
73
77
 
74
78
class StoreText(TestBase):
75
79
    """Store and retrieve a simple text."""
76
 
    def runTest(self):
 
80
 
 
81
    def test_storing_text(self):
77
82
        k = Weave()
78
 
        idx = k.add('text0', [], TEXT_0)
79
 
        self.assertEqual(k.get(idx), TEXT_0)
 
83
        idx = k.add_lines('text0', [], TEXT_0)
 
84
        self.assertEqual(k.get_lines(idx), TEXT_0)
80
85
        self.assertEqual(idx, 0)
81
86
 
82
87
 
83
 
 
84
88
class AnnotateOne(TestBase):
85
89
    def runTest(self):
86
90
        k = Weave()
87
 
        k.add('text0', [], TEXT_0)
88
 
        self.assertEqual(k.annotate(0),
89
 
                         [(0, TEXT_0[0])])
 
91
        k.add_lines('text0', [], TEXT_0)
 
92
        self.assertEqual(k.annotate('text0'),
 
93
                         [('text0', TEXT_0[0])])
90
94
 
91
95
 
92
96
class StoreTwo(TestBase):
93
97
    def runTest(self):
94
98
        k = Weave()
95
99
 
96
 
        idx = k.add('text0', [], TEXT_0)
 
100
        idx = k.add_lines('text0', [], TEXT_0)
97
101
        self.assertEqual(idx, 0)
98
102
 
99
 
        idx = k.add('text1', [], TEXT_1)
 
103
        idx = k.add_lines('text1', [], TEXT_1)
100
104
        self.assertEqual(idx, 1)
101
105
 
102
 
        self.assertEqual(k.get(0), TEXT_0)
103
 
        self.assertEqual(k.get(1), TEXT_1)
104
 
 
105
 
 
106
 
 
107
 
class AddWithGivenSha(TestBase):
108
 
    def runTest(self):
109
 
        """Add with caller-supplied SHA-1"""
 
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):
110
112
        k = Weave()
111
 
 
112
 
        t = 'text0'
113
 
        k.add('text0', [], [t], sha1=sha_string(t))
114
 
 
115
 
 
 
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
                        
116
121
 
117
122
class InvalidAdd(TestBase):
118
123
    """Try to use invalid version number during add."""
119
124
    def runTest(self):
120
125
        k = Weave()
121
126
 
122
 
        self.assertRaises(IndexError,
123
 
                          k.add,
 
127
        self.assertRaises(errors.RevisionNotPresent,
 
128
                          k.add_lines,
124
129
                          'text0',
125
 
                          [69],
 
130
                          ['69'],
126
131
                          ['new text!'])
127
132
 
128
133
 
130
135
    """Add the same version twice; harmless."""
131
136
    def runTest(self):
132
137
        k = Weave()
133
 
        idx = k.add('text0', [], TEXT_0)
134
 
        idx2 = k.add('text0', [], TEXT_0)
 
138
        idx = k.add_lines('text0', [], TEXT_0)
 
139
        idx2 = k.add_lines('text0', [], TEXT_0)
135
140
        self.assertEqual(idx, idx2)
136
141
 
137
142
 
138
 
 
139
143
class InvalidRepeatedAdd(TestBase):
140
144
    def runTest(self):
141
145
        k = Weave()
142
 
        idx = k.add('text0', [], TEXT_0)
143
 
        self.assertRaises(WeaveError,
144
 
                          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,
145
150
                          'text0',
146
151
                          [],
147
152
                          ['not the same text'])
148
 
        self.assertRaises(WeaveError,
149
 
                          k.add,
 
153
        self.assertRaises(errors.RevisionAlreadyPresent,
 
154
                          k.add_lines,
150
155
                          'text0',
151
 
                          [12],         # not the right parents
 
156
                          ['basis'],         # not the right parents
152
157
                          TEXT_0)
153
158
        
154
159
 
155
 
 
156
160
class InsertLines(TestBase):
157
161
    """Store a revision that adds one line to the original.
158
162
 
161
165
    def runTest(self):
162
166
        k = Weave()
163
167
 
164
 
        k.add('text0', [], ['line 1'])
165
 
        k.add('text1', [0], ['line 1', 'line 2'])
166
 
 
167
 
        self.assertEqual(k.annotate(0),
168
 
                         [(0, 'line 1')])
169
 
 
170
 
        self.assertEqual(k.get(1),
 
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),
171
175
                         ['line 1',
172
176
                          'line 2'])
173
177
 
174
 
        self.assertEqual(k.annotate(1),
175
 
                         [(0, 'line 1'),
176
 
                          (1, 'line 2')])
177
 
 
178
 
        k.add('text2', [0], ['line 1', 'diverged line'])
179
 
 
180
 
        self.assertEqual(k.annotate(2),
181
 
                         [(0, 'line 1'),
182
 
                          (2, 'diverged line')])
 
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')])
183
187
 
184
188
        text3 = ['line 1', 'middle line', 'line 2']
185
 
        k.add('text3',
186
 
              [0, 1],
 
189
        k.add_lines('text3',
 
190
              ['text0', 'text1'],
187
191
              text3)
188
192
 
189
193
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
190
194
 
191
195
        self.log("k._weave=" + pformat(k._weave))
192
196
 
193
 
        self.assertEqual(k.annotate(3),
194
 
                         [(0, 'line 1'),
195
 
                          (3, 'middle line'),
196
 
                          (1, 'line 2')])
 
197
        self.assertEqual(k.annotate('text3'),
 
198
                         [('text0', 'line 1'),
 
199
                          ('text3', 'middle line'),
 
200
                          ('text1', 'line 2')])
197
201
 
198
202
        # now multiple insertions at different places
199
 
        k.add('text4',
200
 
              [0, 1, 3],
 
203
        k.add_lines('text4',
 
204
              ['text0', 'text1', 'text3'],
201
205
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
202
206
 
203
 
        self.assertEqual(k.annotate(4), 
204
 
                         [(0, 'line 1'),
205
 
                          (4, 'aaa'),
206
 
                          (3, 'middle line'),
207
 
                          (4, 'bbb'),
208
 
                          (1, 'line 2'),
209
 
                          (4, 'ccc')])
210
 
 
 
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')])
211
214
 
212
215
 
213
216
class DeleteLines(TestBase):
219
222
 
220
223
        base_text = ['one', 'two', 'three', 'four']
221
224
 
222
 
        k.add('text0', [], base_text)
 
225
        k.add_lines('text0', [], base_text)
223
226
        
224
227
        texts = [['one', 'two', 'three'],
225
228
                 ['two', 'three', 'four'],
229
232
 
230
233
        i = 1
231
234
        for t in texts:
232
 
            ver = k.add('text%d' % i,
233
 
                        [0], t)
 
235
            ver = k.add_lines('text%d' % i,
 
236
                        ['text0'], t)
234
237
            i += 1
235
238
 
236
239
        self.log('final weave:')
237
240
        self.log('k._weave=' + pformat(k._weave))
238
241
 
239
242
        for i in range(len(texts)):
240
 
            self.assertEqual(k.get(i+1),
 
243
            self.assertEqual(k.get_lines(i+1),
241
244
                             texts[i])
242
 
            
243
 
 
244
245
 
245
246
 
246
247
class SuicideDelete(TestBase):
262
263
        return 
263
264
 
264
265
        self.assertRaises(WeaveFormatError,
265
 
                          k.get,
 
266
                          k.get_lines,
266
267
                          0)        
267
268
 
268
269
 
269
 
 
270
270
class CannedDelete(TestBase):
271
271
    """Unpack canned weave with deleted lines."""
272
272
    def runTest(self):
283
283
                'last line',
284
284
                ('}', 0),
285
285
                ]
 
286
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
287
                  , sha_string('first linelast line')]
286
288
 
287
 
        self.assertEqual(k.get(0),
 
289
        self.assertEqual(k.get_lines(0),
288
290
                         ['first line',
289
291
                          'line to be deleted',
290
292
                          'last line',
291
293
                          ])
292
294
 
293
 
        self.assertEqual(k.get(1),
 
295
        self.assertEqual(k.get_lines(1),
294
296
                         ['first line',
295
297
                          'last line',
296
298
                          ])
297
299
 
298
300
 
299
 
 
300
301
class CannedReplacement(TestBase):
301
302
    """Unpack canned weave with deleted lines."""
302
303
    def runTest(self):
316
317
                'last line',
317
318
                ('}', 0),
318
319
                ]
 
320
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
321
                  , sha_string('first linereplacement linelast line')]
319
322
 
320
 
        self.assertEqual(k.get(0),
 
323
        self.assertEqual(k.get_lines(0),
321
324
                         ['first line',
322
325
                          'line to be deleted',
323
326
                          'last line',
324
327
                          ])
325
328
 
326
 
        self.assertEqual(k.get(1),
 
329
        self.assertEqual(k.get_lines(1),
327
330
                         ['first line',
328
331
                          'replacement line',
329
332
                          'last line',
330
333
                          ])
331
334
 
332
335
 
333
 
 
334
336
class BadWeave(TestBase):
335
337
    """Test that we trap an insert which should not occur."""
336
338
    def runTest(self):
416
418
                '}',
417
419
                ('}', 0)]
418
420
 
419
 
        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),
420
428
                         ['foo {',
421
429
                          '}'])
422
430
 
423
 
        self.assertEqual(k.get(1),
 
431
        self.assertEqual(k.get_lines(1),
424
432
                         ['foo {',
425
433
                          '  added in version 1',
426
434
                          '  also from v1',
427
435
                          '}'])
428
436
                       
429
 
        self.assertEqual(k.get(2),
 
437
        self.assertEqual(k.get_lines(2),
430
438
                         ['foo {',
431
439
                          '  added in v2',
432
440
                          '}'])
433
441
 
434
 
        self.assertEqual(k.get(3),
 
442
        self.assertEqual(k.get_lines(3),
435
443
                         ['foo {',
436
444
                          '  added in version 1',
437
445
                          '  added in v2',
439
447
                          '}'])
440
448
                         
441
449
 
442
 
 
443
450
class DeleteLines2(TestBase):
444
451
    """Test recording revisions that delete lines.
445
452
 
448
455
    def runTest(self):
449
456
        k = Weave()
450
457
 
451
 
        k.add('text0', [], ["line the first",
 
458
        k.add_lines('text0', [], ["line the first",
452
459
                   "line 2",
453
460
                   "line 3",
454
461
                   "fine"])
455
462
 
456
 
        self.assertEqual(len(k.get(0)), 4)
 
463
        self.assertEqual(len(k.get_lines(0)), 4)
457
464
 
458
 
        k.add('text1', [0], ["line the first",
 
465
        k.add_lines('text1', ['text0'], ["line the first",
459
466
                   "fine"])
460
467
 
461
 
        self.assertEqual(k.get(1),
 
468
        self.assertEqual(k.get_lines(1),
462
469
                         ["line the first",
463
470
                          "fine"])
464
471
 
465
 
        self.assertEqual(k.annotate(1),
466
 
                         [(0, "line the first"),
467
 
                          (0, "fine")])
468
 
 
 
472
        self.assertEqual(k.annotate('text1'),
 
473
                         [('text0', "line the first"),
 
474
                          ('text0', "fine")])
469
475
 
470
476
 
471
477
class IncludeVersions(TestBase):
489
495
                "second line",
490
496
                ('}', 1)]
491
497
 
492
 
        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),
493
502
                         ["first line",
494
503
                          "second line"])
495
504
 
496
 
        self.assertEqual(k.get(0),
 
505
        self.assertEqual(k.get_lines(0),
497
506
                         ["first line"])
498
507
 
499
508
 
501
510
    """Weave with two diverged texts based on version 0.
502
511
    """
503
512
    def runTest(self):
 
513
        # FIXME make the weave, dont poke at it.
504
514
        k = Weave()
505
515
 
 
516
        k._names = ['0', '1', '2']
 
517
        k._name_map = {'0':0, '1':1, '2':2}
506
518
        k._parents = [frozenset(),
507
519
                frozenset([0]),
508
520
                frozenset([0]),
518
530
                ('}', 2),                
519
531
                ]
520
532
 
521
 
        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),
522
538
                         ["first line"])
523
539
 
524
 
        self.assertEqual(k.get(1),
 
540
        self.assertEqual(k.get_lines(1),
525
541
                         ["first line",
526
542
                          "second line"])
527
543
 
528
 
        self.assertEqual(k.get(2),
 
544
        self.assertEqual(k.get_lines('2'),
529
545
                         ["first line",
530
546
                          "alternative second line"])
531
547
 
532
 
        self.assertEqual(list(k.inclusions([2])),
533
 
                         [0, 2])
534
 
 
 
548
        self.assertEqual(list(k.get_ancestry(['2'])),
 
549
                         ['0', '2'])
535
550
 
536
551
 
537
552
class ReplaceLine(TestBase):
541
556
        text0 = ['cheddar', 'stilton', 'gruyere']
542
557
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
543
558
        
544
 
        k.add('text0', [], text0)
545
 
        k.add('text1', [0], text1)
 
559
        k.add_lines('text0', [], text0)
 
560
        k.add_lines('text1', ['text0'], text1)
546
561
 
547
562
        self.log('k._weave=' + pformat(k._weave))
548
563
 
549
 
        self.assertEqual(k.get(0), text0)
550
 
        self.assertEqual(k.get(1), text1)
551
 
 
 
564
        self.assertEqual(k.get_lines(0), text0)
 
565
        self.assertEqual(k.get_lines(1), text1)
552
566
 
553
567
 
554
568
class Merge(TestBase):
562
576
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
563
577
                 ]
564
578
 
565
 
        k.add('text0', [], texts[0])
566
 
        k.add('text1', [0], texts[1])
567
 
        k.add('text2', [0], texts[2])
568
 
        k.add('merge', [0, 1, 2], texts[3])
 
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])
569
583
 
570
584
        for i, t in enumerate(texts):
571
 
            self.assertEqual(k.get(i), t)
 
585
            self.assertEqual(k.get_lines(i), t)
572
586
 
573
 
        self.assertEqual(k.annotate(3),
574
 
                         [(0, 'header'),
575
 
                          (1, ''),
576
 
                          (1, 'line from 1'),
577
 
                          (3, 'fixup line'),
578
 
                          (2, 'line from 2'),
 
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'),
579
593
                          ])
580
594
 
581
 
        self.assertEqual(list(k.inclusions([3])),
582
 
                         [0, 1, 2, 3])
 
595
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
596
                         ['text0', 'text1', 'text2', 'merge'])
583
597
 
584
598
        self.log('k._weave=' + pformat(k._weave))
585
599
 
596
610
        return  # NOT RUN
597
611
        k = Weave()
598
612
 
599
 
        k.add([], ['aaa', 'bbb'])
600
 
        k.add([0], ['aaa', '111', 'bbb'])
601
 
        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'])
602
616
 
603
617
        merged = k.merge([1, 2])
604
618
 
607
621
                           [['bbb']]])
608
622
 
609
623
 
610
 
 
611
624
class NonConflict(TestBase):
612
625
    """Two descendants insert compatible changes.
613
626
 
616
629
        return  # NOT RUN
617
630
        k = Weave()
618
631
 
619
 
        k.add([], ['aaa', 'bbb'])
620
 
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
621
 
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
622
 
 
623
 
    
624
 
    
625
 
 
626
 
 
627
 
class AutoMerge(TestBase):
628
 
    def runTest(self):
629
 
        k = Weave()
630
 
 
631
 
        texts = [['header', 'aaa', 'bbb'],
632
 
                 ['header', 'aaa', 'line from 1', 'bbb'],
633
 
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
634
 
                 ]
635
 
 
636
 
        k.add('text0', [], texts[0])
637
 
        k.add('text1', [0], texts[1])
638
 
        k.add('text2', [0], texts[2])
639
 
 
640
 
        self.log('k._weave=' + pformat(k._weave))
641
 
 
642
 
        m = list(k.mash_iter([0, 1, 2]))
643
 
 
644
 
        self.assertEqual(m,
645
 
                         ['header', 'aaa',
646
 
                          'line from 1',
647
 
                          'bbb',
648
 
                          'line from 2', 'more from 2'])
649
 
        
 
632
        k.add_lines([], ['aaa', 'bbb'])
 
633
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
634
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
650
635
 
651
636
 
652
637
class Khayyam(TestBase):
653
638
    """Test changes to multi-line texts, and read/write"""
654
 
    def runTest(self):
 
639
 
 
640
    def test_multi_line_merge(self):
655
641
        rawtexts = [
656
642
            """A Book of Verses underneath the Bough,
657
643
            A Jug of Wine, a Loaf of Bread, -- and Thou
683
669
        parents = set()
684
670
        i = 0
685
671
        for t in texts:
686
 
            ver = k.add('text%d' % i,
 
672
            ver = k.add_lines('text%d' % i,
687
673
                        list(parents), t)
688
 
            parents.add(ver)
 
674
            parents.add('text%d' % i)
689
675
            i += 1
690
676
 
691
677
        self.log("k._weave=" + pformat(k._weave))
692
678
 
693
679
        for i, t in enumerate(texts):
694
 
            self.assertEqual(k.get(i), t)
 
680
            self.assertEqual(k.get_lines(i), t)
695
681
 
696
682
        self.check_read_write(k)
697
683
 
698
684
 
699
 
 
700
685
class MergeCases(TestBase):
701
686
    def doMerge(self, base, a, b, mp):
702
687
        from cStringIO import StringIO
706
691
            return x + '\n'
707
692
        
708
693
        w = Weave()
709
 
        w.add('text0', [], map(addcrlf, base))
710
 
        w.add('text1', [0], map(addcrlf, a))
711
 
        w.add('text2', [0], map(addcrlf, b))
 
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))
712
697
 
713
698
        self.log('weave is:')
714
699
        tmpf = StringIO()
716
701
        self.log(tmpf.getvalue())
717
702
 
718
703
        self.log('merge plan:')
719
 
        p = list(w.plan_merge(1, 2))
 
704
        p = list(w.plan_merge('text1', 'text2'))
720
705
        for state, line in p:
721
706
            if line:
722
707
                self.log('%12s | %s' % (state, line[:-1]))
753
738
        self.doMerge(['aaa', 'bbb'],
754
739
                     ['aaa', 'xxx', 'yyy', 'bbb'],
755
740
                     ['aaa', 'xxx', 'bbb'],
756
 
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
741
                     ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 'xxx', 
 
742
                      '>>>>>>> ', 'bbb'])
757
743
 
758
744
        # really it ought to reduce this to 
759
745
        # ['aaa', 'xxx', 'yyy', 'bbb']
763
749
        self.doMerge(['aaa'],
764
750
                     ['xxx'],
765
751
                     ['yyy', 'zzz'],
766
 
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
752
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
 
753
                      '>>>>>>> '])
767
754
 
768
755
    def testNonClashInsert(self):
769
756
        self.doMerge(['aaa'],
770
757
                     ['xxx', 'aaa'],
771
758
                     ['yyy', 'zzz'],
772
 
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
759
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
 
760
                      '>>>>>>> '])
773
761
 
774
762
        self.doMerge(['aaa'],
775
763
                     ['aaa'],
791
779
        self.doMerge(['aaa', 'bbb', 'ccc'],
792
780
                     ['aaa', 'ddd', 'ccc'],
793
781
                     ['aaa', 'ccc'],
794
 
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
 
782
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
795
783
 
796
784
 
797
785
class JoinWeavesTests(TestBase):
800
788
        self.weave1 = Weave()
801
789
        self.lines1 = ['hello\n']
802
790
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
803
 
        self.weave1.add('v1', [], self.lines1)
804
 
        self.weave1.add('v2', [0], ['hello\n', 'world\n'])
805
 
        self.weave1.add('v3', [1], self.lines3)
 
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)
806
794
        
807
795
    def test_join_empty(self):
808
796
        """Join two empty weaves."""
810
798
        w1 = Weave()
811
799
        w2 = Weave()
812
800
        w1.join(w2)
813
 
        eq(w1.numversions(), 0)
 
801
        eq(len(w1), 0)
814
802
        
815
803
    def test_join_empty_to_nonempty(self):
816
804
        """Join empty weave onto nonempty."""
820
808
    def test_join_unrelated(self):
821
809
        """Join two weaves with no history in common."""
822
810
        wb = Weave()
823
 
        wb.add('b1', [], ['line from b\n'])
 
811
        wb.add_lines('b1', [], ['line from b\n'])
824
812
        w1 = self.weave1
825
813
        w1.join(wb)
826
814
        eq = self.assertEqual
827
815
        eq(len(w1), 4)
828
 
        eq(sorted(list(w1.iter_names())),
 
816
        eq(sorted(w1.versions()),
829
817
           ['b1', 'v1', 'v2', 'v3'])
830
818
 
831
819
    def test_join_related(self):
832
820
        wa = self.weave1.copy()
833
821
        wb = self.weave1.copy()
834
 
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
835
 
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
 
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'])
836
824
        eq = self.assertEquals
837
825
        eq(len(wa), 4)
838
826
        eq(len(wb), 4)
842
830
           ['hello\n', 'pale blue\n', 'world\n'])
843
831
 
844
832
    def test_join_parent_disagreement(self):
845
 
        """Cannot join weaves with different parents for a version."""
 
833
        #join reconciles differening parents into a union.
846
834
        wa = Weave()
847
835
        wb = Weave()
848
 
        wa.add('v1', [], ['hello\n'])
849
 
        wb.add('v0', [], [])
850
 
        wb.add('v1', ['v0'], ['hello\n'])
851
 
        self.assertRaises(WeaveError,
852
 
                          wa.join, wb)
 
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'))
853
841
 
854
842
    def test_join_text_disagreement(self):
855
843
        """Cannot join weaves with different texts for a version."""
856
844
        wa = Weave()
857
845
        wb = Weave()
858
 
        wa.add('v1', [], ['hello\n'])
859
 
        wb.add('v1', [], ['not\n', 'hello\n'])
 
846
        wa.add_lines('v1', [], ['hello\n'])
 
847
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
860
848
        self.assertRaises(WeaveError,
861
849
                          wa.join, wb)
862
850
 
866
854
        The source weave contains a different version at index 0."""
867
855
        wa = self.weave1.copy()
868
856
        wb = Weave()
869
 
        wb.add('x1', [], ['line from x1\n'])
870
 
        wb.add('v1', [], ['hello\n'])
871
 
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
 
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'])
872
860
        wa.join(wb)
873
861
        eq = self.assertEquals
874
 
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
862
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
875
863
        eq(wa.get_text('x1'), 'line from x1\n')
876
864
 
877
 
    def test_reweave_with_empty(self):
878
 
        wb = Weave()
879
 
        wr = reweave(self.weave1, wb)
880
 
        eq = self.assertEquals
881
 
        eq(sorted(wr.iter_names()), ['v1', 'v2', 'v3'])
882
 
        eq(wr.get_lines('v3'), ['hello\n', 'cruel\n', 'world\n'])
883
 
        self.weave1.reweave(wb)
884
 
        self.assertEquals(wr, self.weave1)
885
 
 
886
 
    def test_join_with_ghosts_raises_parent_mismatch(self):
887
 
        wa = self.weave1.copy()
888
 
        wb = Weave()
889
 
        wb.add('x1', [], ['line from x1\n'])
890
 
        wb.add('v1', [], ['hello\n'])
891
 
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
892
 
        self.assertRaises(errors.WeaveParentMismatch, wa.join, wb)
893
 
 
894
 
    def test_reweave_with_ghosts(self):
895
 
        """Join that inserts parents of an existing revision.
896
 
 
897
 
        This can happen when merging from another branch who
898
 
        knows about revisions the destination does not.  In 
899
 
        this test the second weave knows of an additional parent of 
900
 
        v2.  Any revisions which are in common still have to have the 
901
 
        same text."""
902
 
        wa = self.weave1.copy()
903
 
        wb = Weave()
904
 
        wb.add('x1', [], ['line from x1\n'])
905
 
        wb.add('v1', [], ['hello\n'])
906
 
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
907
 
        wc = reweave(wa, wb)
908
 
        eq = self.assertEquals
909
 
        eq(sorted(wc.iter_names()), ['v1', 'v2', 'v3', 'x1',])
910
 
        eq(wc.get_text('x1'), 'line from x1\n')
911
 
        eq(wc.get_lines('v2'), ['hello\n', 'world\n'])
912
 
        eq(wc.parent_names('v2'), ['v1', 'x1'])
913
 
        self.weave1.reweave(wb)
914
 
        self.assertEquals(wc, self.weave1)
915
 
 
916
 
 
917
 
if __name__ == '__main__':
918
 
    import sys
919
 
    import unittest
920
 
    sys.exit(unittest.main())
 
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."""
921
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