~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-03-09 06:39:13 UTC
  • mfrom: (1596.2.6 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060309063913-6d8ce700706d0802
Merge knit performance stage 1.

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
from pprint import pformat
26
27
 
27
 
import testsweet
28
 
from bzrlib.weave import Weave, WeaveFormatError
 
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 pprint import pformat
31
 
 
32
 
 
33
 
try:
34
 
    set
35
 
    frozenset
36
 
except NameError:
37
 
    from sets import Set, ImmutableSet
38
 
    set = Set
39
 
    frozenset = ImmutableSet
40
 
    del Set, ImmutableSet
41
 
 
 
31
from bzrlib.tests import TestCase
 
32
from bzrlib.osutils import sha_string
42
33
 
43
34
 
44
35
# texts for use in testing
47
38
          "A second line"]
48
39
 
49
40
 
50
 
 
51
 
class TestBase(testsweet.TestBase):
 
41
class TestBase(TestCase):
52
42
    def check_read_write(self, k):
53
43
        """Check the weave k can be written & re-read."""
54
44
        from tempfile import TemporaryFile
68
58
            self.log('         %r' % k._parents)
69
59
            self.log('         %r' % k2._parents)
70
60
            self.log('')
71
 
 
72
 
            
73
61
            self.fail('read/write check failed')
74
 
        
75
 
        
 
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)
76
71
 
77
72
 
78
73
class Easy(TestBase):
82
77
 
83
78
class StoreText(TestBase):
84
79
    """Store and retrieve a simple text."""
85
 
    def runTest(self):
 
80
 
 
81
    def test_storing_text(self):
86
82
        k = Weave()
87
 
        idx = k.add('text0', [], TEXT_0)
88
 
        self.assertEqual(k.get(idx), TEXT_0)
 
83
        idx = k.add_lines('text0', [], TEXT_0)
 
84
        self.assertEqual(k.get_lines(idx), TEXT_0)
89
85
        self.assertEqual(idx, 0)
90
86
 
91
87
 
92
 
 
93
88
class AnnotateOne(TestBase):
94
89
    def runTest(self):
95
90
        k = Weave()
96
 
        k.add('text0', [], TEXT_0)
97
 
        self.assertEqual(k.annotate(0),
98
 
                         [(0, TEXT_0[0])])
 
91
        k.add_lines('text0', [], TEXT_0)
 
92
        self.assertEqual(k.annotate('text0'),
 
93
                         [('text0', TEXT_0[0])])
99
94
 
100
95
 
101
96
class StoreTwo(TestBase):
102
97
    def runTest(self):
103
98
        k = Weave()
104
99
 
105
 
        idx = k.add('text0', [], TEXT_0)
 
100
        idx = k.add_lines('text0', [], TEXT_0)
106
101
        self.assertEqual(idx, 0)
107
102
 
108
 
        idx = k.add('text1', [], TEXT_1)
 
103
        idx = k.add_lines('text1', [], TEXT_1)
109
104
        self.assertEqual(idx, 1)
110
105
 
111
 
        self.assertEqual(k.get(0), TEXT_0)
112
 
        self.assertEqual(k.get(1), TEXT_1)
113
 
 
114
 
        k.dump(self.TEST_LOG)
115
 
 
116
 
 
 
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):
 
112
        k = Weave()
 
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
                        
117
121
 
118
122
class InvalidAdd(TestBase):
119
123
    """Try to use invalid version number during add."""
120
124
    def runTest(self):
121
125
        k = Weave()
122
126
 
123
 
        self.assertRaises(IndexError,
124
 
                          k.add,
 
127
        self.assertRaises(errors.RevisionNotPresent,
 
128
                          k.add_lines,
125
129
                          'text0',
126
 
                          [69],
 
130
                          ['69'],
127
131
                          ['new text!'])
128
132
 
129
133
 
 
134
class RepeatedAdd(TestBase):
 
135
    """Add the same version twice; harmless."""
 
136
    def runTest(self):
 
137
        k = Weave()
 
138
        idx = k.add_lines('text0', [], TEXT_0)
 
139
        idx2 = k.add_lines('text0', [], TEXT_0)
 
140
        self.assertEqual(idx, idx2)
 
141
 
 
142
 
 
143
class InvalidRepeatedAdd(TestBase):
 
144
    def runTest(self):
 
145
        k = Weave()
 
146
        k.add_lines('basis', [], TEXT_0)
 
147
        idx = k.add_lines('text0', [], TEXT_0)
 
148
        self.assertRaises(errors.RevisionAlreadyPresent,
 
149
                          k.add_lines,
 
150
                          'text0',
 
151
                          [],
 
152
                          ['not the same text'])
 
153
        self.assertRaises(errors.RevisionAlreadyPresent,
 
154
                          k.add_lines,
 
155
                          'text0',
 
156
                          ['basis'],         # not the right parents
 
157
                          TEXT_0)
 
158
        
 
159
 
130
160
class InsertLines(TestBase):
131
161
    """Store a revision that adds one line to the original.
132
162
 
135
165
    def runTest(self):
136
166
        k = Weave()
137
167
 
138
 
        k.add('text0', [], ['line 1'])
139
 
        k.add('text1', [0], ['line 1', 'line 2'])
140
 
 
141
 
        self.assertEqual(k.annotate(0),
142
 
                         [(0, 'line 1')])
143
 
 
144
 
        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),
145
175
                         ['line 1',
146
176
                          'line 2'])
147
177
 
148
 
        self.assertEqual(k.annotate(1),
149
 
                         [(0, 'line 1'),
150
 
                          (1, 'line 2')])
151
 
 
152
 
        k.add('text2', [0], ['line 1', 'diverged line'])
153
 
 
154
 
        self.assertEqual(k.annotate(2),
155
 
                         [(0, 'line 1'),
156
 
                          (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')])
157
187
 
158
188
        text3 = ['line 1', 'middle line', 'line 2']
159
 
        k.add('text3',
160
 
              [0, 1],
 
189
        k.add_lines('text3',
 
190
              ['text0', 'text1'],
161
191
              text3)
162
192
 
163
193
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
164
194
 
165
195
        self.log("k._weave=" + pformat(k._weave))
166
196
 
167
 
        self.assertEqual(k.annotate(3),
168
 
                         [(0, 'line 1'),
169
 
                          (3, 'middle line'),
170
 
                          (1, 'line 2')])
 
197
        self.assertEqual(k.annotate('text3'),
 
198
                         [('text0', 'line 1'),
 
199
                          ('text3', 'middle line'),
 
200
                          ('text1', 'line 2')])
171
201
 
172
202
        # now multiple insertions at different places
173
 
        k.add('text4',
174
 
              [0, 1, 3],
 
203
        k.add_lines('text4',
 
204
              ['text0', 'text1', 'text3'],
175
205
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
176
206
 
177
 
        self.assertEqual(k.annotate(4), 
178
 
                         [(0, 'line 1'),
179
 
                          (4, 'aaa'),
180
 
                          (3, 'middle line'),
181
 
                          (4, 'bbb'),
182
 
                          (1, 'line 2'),
183
 
                          (4, 'ccc')])
184
 
 
 
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')])
185
214
 
186
215
 
187
216
class DeleteLines(TestBase):
193
222
 
194
223
        base_text = ['one', 'two', 'three', 'four']
195
224
 
196
 
        k.add('text0', [], base_text)
 
225
        k.add_lines('text0', [], base_text)
197
226
        
198
227
        texts = [['one', 'two', 'three'],
199
228
                 ['two', 'three', 'four'],
203
232
 
204
233
        i = 1
205
234
        for t in texts:
206
 
            ver = k.add('text%d' % i,
207
 
                        [0], t)
 
235
            ver = k.add_lines('text%d' % i,
 
236
                        ['text0'], t)
208
237
            i += 1
209
238
 
210
239
        self.log('final weave:')
211
240
        self.log('k._weave=' + pformat(k._weave))
212
241
 
213
242
        for i in range(len(texts)):
214
 
            self.assertEqual(k.get(i+1),
 
243
            self.assertEqual(k.get_lines(i+1),
215
244
                             texts[i])
216
 
            
217
 
 
218
245
 
219
246
 
220
247
class SuicideDelete(TestBase):
236
263
        return 
237
264
 
238
265
        self.assertRaises(WeaveFormatError,
239
 
                          k.get,
 
266
                          k.get_lines,
240
267
                          0)        
241
268
 
242
269
 
243
 
 
244
270
class CannedDelete(TestBase):
245
271
    """Unpack canned weave with deleted lines."""
246
272
    def runTest(self):
257
283
                'last line',
258
284
                ('}', 0),
259
285
                ]
 
286
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
287
                  , sha_string('first linelast line')]
260
288
 
261
 
        self.assertEqual(k.get(0),
 
289
        self.assertEqual(k.get_lines(0),
262
290
                         ['first line',
263
291
                          'line to be deleted',
264
292
                          'last line',
265
293
                          ])
266
294
 
267
 
        self.assertEqual(k.get(1),
 
295
        self.assertEqual(k.get_lines(1),
268
296
                         ['first line',
269
297
                          'last line',
270
298
                          ])
271
299
 
272
300
 
273
 
 
274
301
class CannedReplacement(TestBase):
275
302
    """Unpack canned weave with deleted lines."""
276
303
    def runTest(self):
290
317
                'last line',
291
318
                ('}', 0),
292
319
                ]
 
320
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
321
                  , sha_string('first linereplacement linelast line')]
293
322
 
294
 
        self.assertEqual(k.get(0),
 
323
        self.assertEqual(k.get_lines(0),
295
324
                         ['first line',
296
325
                          'line to be deleted',
297
326
                          'last line',
298
327
                          ])
299
328
 
300
 
        self.assertEqual(k.get(1),
 
329
        self.assertEqual(k.get_lines(1),
301
330
                         ['first line',
302
331
                          'replacement line',
303
332
                          'last line',
304
333
                          ])
305
334
 
306
335
 
307
 
 
308
336
class BadWeave(TestBase):
309
337
    """Test that we trap an insert which should not occur."""
310
338
    def runTest(self):
390
418
                '}',
391
419
                ('}', 0)]
392
420
 
393
 
        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),
394
428
                         ['foo {',
395
429
                          '}'])
396
430
 
397
 
        self.assertEqual(k.get(1),
 
431
        self.assertEqual(k.get_lines(1),
398
432
                         ['foo {',
399
433
                          '  added in version 1',
400
434
                          '  also from v1',
401
435
                          '}'])
402
436
                       
403
 
        self.assertEqual(k.get(2),
 
437
        self.assertEqual(k.get_lines(2),
404
438
                         ['foo {',
405
439
                          '  added in v2',
406
440
                          '}'])
407
441
 
408
 
        self.assertEqual(k.get(3),
 
442
        self.assertEqual(k.get_lines(3),
409
443
                         ['foo {',
410
444
                          '  added in version 1',
411
445
                          '  added in v2',
413
447
                          '}'])
414
448
                         
415
449
 
416
 
 
417
450
class DeleteLines2(TestBase):
418
451
    """Test recording revisions that delete lines.
419
452
 
422
455
    def runTest(self):
423
456
        k = Weave()
424
457
 
425
 
        k.add('text0', [], ["line the first",
 
458
        k.add_lines('text0', [], ["line the first",
426
459
                   "line 2",
427
460
                   "line 3",
428
461
                   "fine"])
429
462
 
430
 
        self.assertEqual(len(k.get(0)), 4)
 
463
        self.assertEqual(len(k.get_lines(0)), 4)
431
464
 
432
 
        k.add('text1', [0], ["line the first",
 
465
        k.add_lines('text1', ['text0'], ["line the first",
433
466
                   "fine"])
434
467
 
435
 
        self.assertEqual(k.get(1),
 
468
        self.assertEqual(k.get_lines(1),
436
469
                         ["line the first",
437
470
                          "fine"])
438
471
 
439
 
        self.assertEqual(k.annotate(1),
440
 
                         [(0, "line the first"),
441
 
                          (0, "fine")])
442
 
 
 
472
        self.assertEqual(k.annotate('text1'),
 
473
                         [('text0', "line the first"),
 
474
                          ('text0', "fine")])
443
475
 
444
476
 
445
477
class IncludeVersions(TestBase):
463
495
                "second line",
464
496
                ('}', 1)]
465
497
 
466
 
        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),
467
502
                         ["first line",
468
503
                          "second line"])
469
504
 
470
 
        self.assertEqual(k.get(0),
 
505
        self.assertEqual(k.get_lines(0),
471
506
                         ["first line"])
472
507
 
473
 
        k.dump(self.TEST_LOG)
474
 
 
475
508
 
476
509
class DivergedIncludes(TestBase):
477
510
    """Weave with two diverged texts based on version 0.
478
511
    """
479
512
    def runTest(self):
 
513
        # FIXME make the weave, dont poke at it.
480
514
        k = Weave()
481
515
 
 
516
        k._names = ['0', '1', '2']
 
517
        k._name_map = {'0':0, '1':1, '2':2}
482
518
        k._parents = [frozenset(),
483
519
                frozenset([0]),
484
520
                frozenset([0]),
494
530
                ('}', 2),                
495
531
                ]
496
532
 
497
 
        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),
498
538
                         ["first line"])
499
539
 
500
 
        self.assertEqual(k.get(1),
 
540
        self.assertEqual(k.get_lines(1),
501
541
                         ["first line",
502
542
                          "second line"])
503
543
 
504
 
        self.assertEqual(k.get(2),
 
544
        self.assertEqual(k.get_lines('2'),
505
545
                         ["first line",
506
546
                          "alternative second line"])
507
547
 
508
 
        self.assertEqual(list(k.inclusions([2])),
509
 
                         [0, 2])
510
 
 
 
548
        self.assertEqual(list(k.get_ancestry(['2'])),
 
549
                         ['0', '2'])
511
550
 
512
551
 
513
552
class ReplaceLine(TestBase):
517
556
        text0 = ['cheddar', 'stilton', 'gruyere']
518
557
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
519
558
        
520
 
        k.add('text0', [], text0)
521
 
        k.add('text1', [0], text1)
 
559
        k.add_lines('text0', [], text0)
 
560
        k.add_lines('text1', ['text0'], text1)
522
561
 
523
562
        self.log('k._weave=' + pformat(k._weave))
524
563
 
525
 
        self.assertEqual(k.get(0), text0)
526
 
        self.assertEqual(k.get(1), text1)
527
 
 
 
564
        self.assertEqual(k.get_lines(0), text0)
 
565
        self.assertEqual(k.get_lines(1), text1)
528
566
 
529
567
 
530
568
class Merge(TestBase):
538
576
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
539
577
                 ]
540
578
 
541
 
        k.add('text0', [], texts[0])
542
 
        k.add('text1', [0], texts[1])
543
 
        k.add('text2', [0], texts[2])
544
 
        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])
545
583
 
546
584
        for i, t in enumerate(texts):
547
 
            self.assertEqual(k.get(i), t)
 
585
            self.assertEqual(k.get_lines(i), t)
548
586
 
549
 
        self.assertEqual(k.annotate(3),
550
 
                         [(0, 'header'),
551
 
                          (1, ''),
552
 
                          (1, 'line from 1'),
553
 
                          (3, 'fixup line'),
554
 
                          (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'),
555
593
                          ])
556
594
 
557
 
        self.assertEqual(list(k.inclusions([3])),
558
 
                         [0, 1, 2, 3])
 
595
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
596
                         ['text0', 'text1', 'text2', 'merge'])
559
597
 
560
598
        self.log('k._weave=' + pformat(k._weave))
561
599
 
572
610
        return  # NOT RUN
573
611
        k = Weave()
574
612
 
575
 
        k.add([], ['aaa', 'bbb'])
576
 
        k.add([0], ['aaa', '111', 'bbb'])
577
 
        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'])
578
616
 
579
617
        merged = k.merge([1, 2])
580
618
 
583
621
                           [['bbb']]])
584
622
 
585
623
 
586
 
 
587
624
class NonConflict(TestBase):
588
625
    """Two descendants insert compatible changes.
589
626
 
592
629
        return  # NOT RUN
593
630
        k = Weave()
594
631
 
595
 
        k.add([], ['aaa', 'bbb'])
596
 
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
597
 
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
598
 
 
599
 
    
600
 
    
601
 
 
602
 
 
603
 
class AutoMerge(TestBase):
604
 
    def runTest(self):
605
 
        k = Weave()
606
 
 
607
 
        texts = [['header', 'aaa', 'bbb'],
608
 
                 ['header', 'aaa', 'line from 1', 'bbb'],
609
 
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
610
 
                 ]
611
 
 
612
 
        k.add('text0', [], texts[0])
613
 
        k.add('text1', [0], texts[1])
614
 
        k.add('text2', [0], texts[2])
615
 
 
616
 
        self.log('k._weave=' + pformat(k._weave))
617
 
 
618
 
        m = list(k.mash_iter([0, 1, 2]))
619
 
 
620
 
        self.assertEqual(m,
621
 
                         ['header', 'aaa',
622
 
                          'line from 1',
623
 
                          'bbb',
624
 
                          'line from 2', 'more from 2'])
625
 
        
 
632
        k.add_lines([], ['aaa', 'bbb'])
 
633
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
634
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
626
635
 
627
636
 
628
637
class Khayyam(TestBase):
629
638
    """Test changes to multi-line texts, and read/write"""
630
 
    def runTest(self):
 
639
 
 
640
    def test_multi_line_merge(self):
631
641
        rawtexts = [
632
642
            """A Book of Verses underneath the Bough,
633
643
            A Jug of Wine, a Loaf of Bread, -- and Thou
659
669
        parents = set()
660
670
        i = 0
661
671
        for t in texts:
662
 
            ver = k.add('text%d' % i,
 
672
            ver = k.add_lines('text%d' % i,
663
673
                        list(parents), t)
664
 
            parents.add(ver)
 
674
            parents.add('text%d' % i)
665
675
            i += 1
666
676
 
667
677
        self.log("k._weave=" + pformat(k._weave))
668
678
 
669
679
        for i, t in enumerate(texts):
670
 
            self.assertEqual(k.get(i), t)
 
680
            self.assertEqual(k.get_lines(i), t)
671
681
 
672
682
        self.check_read_write(k)
673
683
 
674
684
 
675
 
 
676
685
class MergeCases(TestBase):
677
686
    def doMerge(self, base, a, b, mp):
678
687
        from cStringIO import StringIO
682
691
            return x + '\n'
683
692
        
684
693
        w = Weave()
685
 
        w.add('text0', [], map(addcrlf, base))
686
 
        w.add('text1', [0], map(addcrlf, a))
687
 
        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))
688
697
 
689
698
        self.log('weave is:')
690
699
        tmpf = StringIO()
692
701
        self.log(tmpf.getvalue())
693
702
 
694
703
        self.log('merge plan:')
695
 
        p = list(w.plan_merge(1, 2))
 
704
        p = list(w.plan_merge('text1', 'text2'))
696
705
        for state, line in p:
697
706
            if line:
698
707
                self.log('%12s | %s' % (state, line[:-1]))
729
738
        self.doMerge(['aaa', 'bbb'],
730
739
                     ['aaa', 'xxx', 'yyy', 'bbb'],
731
740
                     ['aaa', 'xxx', 'bbb'],
732
 
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
741
                     ['aaa', '<<<<<<< ', 'xxx', 'yyy', '=======', 'xxx', 
 
742
                      '>>>>>>> ', 'bbb'])
733
743
 
734
744
        # really it ought to reduce this to 
735
745
        # ['aaa', 'xxx', 'yyy', 'bbb']
739
749
        self.doMerge(['aaa'],
740
750
                     ['xxx'],
741
751
                     ['yyy', 'zzz'],
742
 
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
752
                     ['<<<<<<< ', 'xxx', '=======', 'yyy', 'zzz', 
 
753
                      '>>>>>>> '])
743
754
 
744
755
    def testNonClashInsert(self):
745
756
        self.doMerge(['aaa'],
746
757
                     ['xxx', 'aaa'],
747
758
                     ['yyy', 'zzz'],
748
 
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
759
                     ['<<<<<<< ', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
 
760
                      '>>>>>>> '])
749
761
 
750
762
        self.doMerge(['aaa'],
751
763
                     ['aaa'],
767
779
        self.doMerge(['aaa', 'bbb', 'ccc'],
768
780
                     ['aaa', 'ddd', 'ccc'],
769
781
                     ['aaa', 'ccc'],
770
 
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
771
 
    
772
 
 
773
 
 
774
 
def testweave():
775
 
    import testsweet
776
 
    from unittest import TestSuite, TestLoader
777
 
    import testweave
778
 
 
779
 
    tl = TestLoader()
780
 
    suite = TestSuite()
781
 
    suite.addTest(tl.loadTestsFromModule(testweave))
782
 
    
783
 
    return int(not testsweet.run_suite(suite)) # for shell 0=true
784
 
 
785
 
 
786
 
if __name__ == '__main__':
787
 
    import sys
788
 
    sys.exit(testweave())
789
 
    
 
782
                     ['<<<<<<<< ', 'aaa', '=======', '>>>>>>> ', 'ccc'])
 
783
 
 
784
 
 
785
class JoinWeavesTests(TestBase):
 
786
    def setUp(self):
 
787
        super(JoinWeavesTests, self).setUp()
 
788
        self.weave1 = Weave()
 
789
        self.lines1 = ['hello\n']
 
790
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
 
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)
 
794
        
 
795
    def test_join_empty(self):
 
796
        """Join two empty weaves."""
 
797
        eq = self.assertEqual
 
798
        w1 = Weave()
 
799
        w2 = Weave()
 
800
        w1.join(w2)
 
801
        eq(len(w1), 0)
 
802
        
 
803
    def test_join_empty_to_nonempty(self):
 
804
        """Join empty weave onto nonempty."""
 
805
        self.weave1.join(Weave())
 
806
        self.assertEqual(len(self.weave1), 3)
 
807
 
 
808
    def test_join_unrelated(self):
 
809
        """Join two weaves with no history in common."""
 
810
        wb = Weave()
 
811
        wb.add_lines('b1', [], ['line from b\n'])
 
812
        w1 = self.weave1
 
813
        w1.join(wb)
 
814
        eq = self.assertEqual
 
815
        eq(len(w1), 4)
 
816
        eq(sorted(w1.versions()),
 
817
           ['b1', 'v1', 'v2', 'v3'])
 
818
 
 
819
    def test_join_related(self):
 
820
        wa = self.weave1.copy()
 
821
        wb = self.weave1.copy()
 
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'])
 
824
        eq = self.assertEquals
 
825
        eq(len(wa), 4)
 
826
        eq(len(wb), 4)
 
827
        wa.join(wb)
 
828
        eq(len(wa), 5)
 
829
        eq(wa.get_lines('b1'),
 
830
           ['hello\n', 'pale blue\n', 'world\n'])
 
831
 
 
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."""
 
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