~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: John Arbash Meinel
  • Date: 2006-06-11 03:09:28 UTC
  • mto: (1711.7.2 win32)
  • mto: This revision was merged to the branch mainline in revision 1796.
  • Revision ID: john@arbash-meinel.com-20060611030928-502d4af47bd62fe1
Remove cp437 from the set of encodings, it isn't strictly needed

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
 
19
19
 
20
 
 
 
20
# TODO: tests regarding version names
 
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
 
22
#       if it fails.
21
23
 
22
24
"""test suite for weave algorithm"""
23
25
 
 
26
from pprint import pformat
24
27
 
25
 
import testsweet
26
 
from bzrlib.weave import Weave, WeaveFormatError
 
28
import bzrlib.errors as errors
 
29
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
27
30
from bzrlib.weavefile import write_weave, read_weave
28
 
from pprint import pformat
29
 
 
30
 
 
31
 
try:
32
 
    set
33
 
    frozenset
34
 
except NameError:
35
 
    from sets import Set, ImmutableSet
36
 
    set = Set
37
 
    frozenset = ImmutableSet
38
 
    del Set, ImmutableSet
39
 
 
 
31
from bzrlib.tests import TestCase
 
32
from bzrlib.osutils import sha_string
40
33
 
41
34
 
42
35
# texts for use in testing
45
38
          "A second line"]
46
39
 
47
40
 
48
 
 
49
 
class TestBase(testsweet.TestBase):
 
41
class TestBase(TestCase):
50
42
    def check_read_write(self, k):
51
43
        """Check the weave k can be written & re-read."""
52
44
        from tempfile import TemporaryFile
60
52
            tf.seek(0)
61
53
            self.log('serialized weave:')
62
54
            self.log(tf.read())
 
55
 
 
56
            self.log('')
 
57
            self.log('parents: %s' % (k._parents == k2._parents))
 
58
            self.log('         %r' % k._parents)
 
59
            self.log('         %r' % k2._parents)
 
60
            self.log('')
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([], 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([], 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([], TEXT_0)
 
100
        idx = k.add_lines('text0', [], TEXT_0)
96
101
        self.assertEqual(idx, 0)
97
102
 
98
 
        idx = k.add([], 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
 
        k.dump(self.TEST_LOG)
105
 
 
106
 
 
 
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
                        
107
121
 
108
122
class InvalidAdd(TestBase):
109
123
    """Try to use invalid version number during add."""
110
124
    def runTest(self):
111
125
        k = Weave()
112
126
 
113
 
        self.assertRaises(IndexError,
114
 
                          k.add,
115
 
                          [69],
 
127
        self.assertRaises(errors.RevisionNotPresent,
 
128
                          k.add_lines,
 
129
                          'text0',
 
130
                          ['69'],
116
131
                          ['new text!'])
117
132
 
118
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
 
119
160
class InsertLines(TestBase):
120
161
    """Store a revision that adds one line to the original.
121
162
 
124
165
    def runTest(self):
125
166
        k = Weave()
126
167
 
127
 
        k.add([], ['line 1'])
128
 
        k.add([0], ['line 1', 'line 2'])
129
 
 
130
 
        self.assertEqual(k.annotate(0),
131
 
                         [(0, 'line 1')])
132
 
 
133
 
        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),
134
175
                         ['line 1',
135
176
                          'line 2'])
136
177
 
137
 
        self.assertEqual(k.annotate(1),
138
 
                         [(0, 'line 1'),
139
 
                          (1, 'line 2')])
140
 
 
141
 
        k.add([0], ['line 1', 'diverged line'])
142
 
 
143
 
        self.assertEqual(k.annotate(2),
144
 
                         [(0, 'line 1'),
145
 
                          (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')])
146
187
 
147
188
        text3 = ['line 1', 'middle line', 'line 2']
148
 
        k.add([0, 1],
 
189
        k.add_lines('text3',
 
190
              ['text0', 'text1'],
149
191
              text3)
150
192
 
151
193
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
152
194
 
153
195
        self.log("k._weave=" + pformat(k._weave))
154
196
 
155
 
        self.assertEqual(k.annotate(3),
156
 
                         [(0, 'line 1'),
157
 
                          (3, 'middle line'),
158
 
                          (1, 'line 2')])
 
197
        self.assertEqual(k.annotate('text3'),
 
198
                         [('text0', 'line 1'),
 
199
                          ('text3', 'middle line'),
 
200
                          ('text1', 'line 2')])
159
201
 
160
202
        # now multiple insertions at different places
161
 
        k.add([0, 1, 3],
 
203
        k.add_lines('text4',
 
204
              ['text0', 'text1', 'text3'],
162
205
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
163
206
 
164
 
        self.assertEqual(k.annotate(4), 
165
 
                         [(0, 'line 1'),
166
 
                          (4, 'aaa'),
167
 
                          (3, 'middle line'),
168
 
                          (4, 'bbb'),
169
 
                          (1, 'line 2'),
170
 
                          (4, 'ccc')])
171
 
 
 
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')])
172
214
 
173
215
 
174
216
class DeleteLines(TestBase):
180
222
 
181
223
        base_text = ['one', 'two', 'three', 'four']
182
224
 
183
 
        k.add([], base_text)
 
225
        k.add_lines('text0', [], base_text)
184
226
        
185
227
        texts = [['one', 'two', 'three'],
186
228
                 ['two', 'three', 'four'],
188
230
                 ['one', 'two', 'three', 'four'],
189
231
                 ]
190
232
 
 
233
        i = 1
191
234
        for t in texts:
192
 
            ver = k.add([0], t)
 
235
            ver = k.add_lines('text%d' % i,
 
236
                        ['text0'], t)
 
237
            i += 1
193
238
 
194
239
        self.log('final weave:')
195
240
        self.log('k._weave=' + pformat(k._weave))
196
241
 
197
242
        for i in range(len(texts)):
198
 
            self.assertEqual(k.get(i+1),
 
243
            self.assertEqual(k.get_lines(i+1),
199
244
                             texts[i])
200
 
            
201
 
 
202
245
 
203
246
 
204
247
class SuicideDelete(TestBase):
220
263
        return 
221
264
 
222
265
        self.assertRaises(WeaveFormatError,
223
 
                          k.get,
 
266
                          k.get_lines,
224
267
                          0)        
225
268
 
226
269
 
227
 
 
228
270
class CannedDelete(TestBase):
229
271
    """Unpack canned weave with deleted lines."""
230
272
    def runTest(self):
241
283
                'last line',
242
284
                ('}', 0),
243
285
                ]
 
286
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
287
                  , sha_string('first linelast line')]
244
288
 
245
 
        self.assertEqual(k.get(0),
 
289
        self.assertEqual(k.get_lines(0),
246
290
                         ['first line',
247
291
                          'line to be deleted',
248
292
                          'last line',
249
293
                          ])
250
294
 
251
 
        self.assertEqual(k.get(1),
 
295
        self.assertEqual(k.get_lines(1),
252
296
                         ['first line',
253
297
                          'last line',
254
298
                          ])
255
299
 
256
300
 
257
 
 
258
301
class CannedReplacement(TestBase):
259
302
    """Unpack canned weave with deleted lines."""
260
303
    def runTest(self):
274
317
                'last line',
275
318
                ('}', 0),
276
319
                ]
 
320
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
321
                  , sha_string('first linereplacement linelast line')]
277
322
 
278
 
        self.assertEqual(k.get(0),
 
323
        self.assertEqual(k.get_lines(0),
279
324
                         ['first line',
280
325
                          'line to be deleted',
281
326
                          'last line',
282
327
                          ])
283
328
 
284
 
        self.assertEqual(k.get(1),
 
329
        self.assertEqual(k.get_lines(1),
285
330
                         ['first line',
286
331
                          'replacement line',
287
332
                          'last line',
288
333
                          ])
289
334
 
290
335
 
291
 
 
292
336
class BadWeave(TestBase):
293
337
    """Test that we trap an insert which should not occur."""
294
338
    def runTest(self):
374
418
                '}',
375
419
                ('}', 0)]
376
420
 
377
 
        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),
378
428
                         ['foo {',
379
429
                          '}'])
380
430
 
381
 
        self.assertEqual(k.get(1),
 
431
        self.assertEqual(k.get_lines(1),
382
432
                         ['foo {',
383
433
                          '  added in version 1',
384
434
                          '  also from v1',
385
435
                          '}'])
386
436
                       
387
 
        self.assertEqual(k.get(2),
 
437
        self.assertEqual(k.get_lines(2),
388
438
                         ['foo {',
389
439
                          '  added in v2',
390
440
                          '}'])
391
441
 
392
 
        self.assertEqual(k.get(3),
 
442
        self.assertEqual(k.get_lines(3),
393
443
                         ['foo {',
394
444
                          '  added in version 1',
395
445
                          '  added in v2',
397
447
                          '}'])
398
448
                         
399
449
 
400
 
 
401
450
class DeleteLines2(TestBase):
402
451
    """Test recording revisions that delete lines.
403
452
 
406
455
    def runTest(self):
407
456
        k = Weave()
408
457
 
409
 
        k.add([], ["line the first",
 
458
        k.add_lines('text0', [], ["line the first",
410
459
                   "line 2",
411
460
                   "line 3",
412
461
                   "fine"])
413
462
 
414
 
        self.assertEqual(len(k.get(0)), 4)
 
463
        self.assertEqual(len(k.get_lines(0)), 4)
415
464
 
416
 
        k.add([0], ["line the first",
 
465
        k.add_lines('text1', ['text0'], ["line the first",
417
466
                   "fine"])
418
467
 
419
 
        self.assertEqual(k.get(1),
 
468
        self.assertEqual(k.get_lines(1),
420
469
                         ["line the first",
421
470
                          "fine"])
422
471
 
423
 
        self.assertEqual(k.annotate(1),
424
 
                         [(0, "line the first"),
425
 
                          (0, "fine")])
426
 
 
 
472
        self.assertEqual(k.annotate('text1'),
 
473
                         [('text0', "line the first"),
 
474
                          ('text0', "fine")])
427
475
 
428
476
 
429
477
class IncludeVersions(TestBase):
447
495
                "second line",
448
496
                ('}', 1)]
449
497
 
450
 
        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),
451
502
                         ["first line",
452
503
                          "second line"])
453
504
 
454
 
        self.assertEqual(k.get(0),
 
505
        self.assertEqual(k.get_lines(0),
455
506
                         ["first line"])
456
507
 
457
 
        k.dump(self.TEST_LOG)
458
 
 
459
508
 
460
509
class DivergedIncludes(TestBase):
461
510
    """Weave with two diverged texts based on version 0.
462
511
    """
463
512
    def runTest(self):
 
513
        # FIXME make the weave, dont poke at it.
464
514
        k = Weave()
465
515
 
 
516
        k._names = ['0', '1', '2']
 
517
        k._name_map = {'0':0, '1':1, '2':2}
466
518
        k._parents = [frozenset(),
467
519
                frozenset([0]),
468
520
                frozenset([0]),
478
530
                ('}', 2),                
479
531
                ]
480
532
 
481
 
        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),
482
538
                         ["first line"])
483
539
 
484
 
        self.assertEqual(k.get(1),
 
540
        self.assertEqual(k.get_lines(1),
485
541
                         ["first line",
486
542
                          "second line"])
487
543
 
488
 
        self.assertEqual(k.get(2),
 
544
        self.assertEqual(k.get_lines('2'),
489
545
                         ["first line",
490
546
                          "alternative second line"])
491
547
 
492
 
        self.assertEqual(list(k.inclusions([2])),
493
 
                         [0, 2])
494
 
 
 
548
        self.assertEqual(list(k.get_ancestry(['2'])),
 
549
                         ['0', '2'])
495
550
 
496
551
 
497
552
class ReplaceLine(TestBase):
501
556
        text0 = ['cheddar', 'stilton', 'gruyere']
502
557
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
503
558
        
504
 
        k.add([], text0)
505
 
        k.add([0], text1)
 
559
        k.add_lines('text0', [], text0)
 
560
        k.add_lines('text1', ['text0'], text1)
506
561
 
507
562
        self.log('k._weave=' + pformat(k._weave))
508
563
 
509
 
        self.assertEqual(k.get(0), text0)
510
 
        self.assertEqual(k.get(1), text1)
511
 
 
 
564
        self.assertEqual(k.get_lines(0), text0)
 
565
        self.assertEqual(k.get_lines(1), text1)
512
566
 
513
567
 
514
568
class Merge(TestBase):
522
576
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
523
577
                 ]
524
578
 
525
 
        k.add([], texts[0])
526
 
        k.add([0], texts[1])
527
 
        k.add([0], texts[2])
528
 
        k.add([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])
529
583
 
530
584
        for i, t in enumerate(texts):
531
 
            self.assertEqual(k.get(i), t)
 
585
            self.assertEqual(k.get_lines(i), t)
532
586
 
533
 
        self.assertEqual(k.annotate(3),
534
 
                         [(0, 'header'),
535
 
                          (1, ''),
536
 
                          (1, 'line from 1'),
537
 
                          (3, 'fixup line'),
538
 
                          (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'),
539
593
                          ])
540
594
 
541
 
        self.assertEqual(list(k.inclusions([3])),
542
 
                         [0, 1, 2, 3])
 
595
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
596
                         ['text0', 'text1', 'text2', 'merge'])
543
597
 
544
598
        self.log('k._weave=' + pformat(k._weave))
545
599
 
556
610
        return  # NOT RUN
557
611
        k = Weave()
558
612
 
559
 
        k.add([], ['aaa', 'bbb'])
560
 
        k.add([0], ['aaa', '111', 'bbb'])
561
 
        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'])
562
616
 
563
617
        merged = k.merge([1, 2])
564
618
 
567
621
                           [['bbb']]])
568
622
 
569
623
 
570
 
 
571
624
class NonConflict(TestBase):
572
625
    """Two descendants insert compatible changes.
573
626
 
576
629
        return  # NOT RUN
577
630
        k = Weave()
578
631
 
579
 
        k.add([], ['aaa', 'bbb'])
580
 
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
581
 
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
582
 
 
583
 
    
584
 
    
585
 
 
586
 
 
587
 
class AutoMerge(TestBase):
588
 
    def runTest(self):
589
 
        k = Weave()
590
 
 
591
 
        texts = [['header', 'aaa', 'bbb'],
592
 
                 ['header', 'aaa', 'line from 1', 'bbb'],
593
 
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
594
 
                 ]
595
 
 
596
 
        k.add([], texts[0])
597
 
        k.add([0], texts[1])
598
 
        k.add([0], texts[2])
599
 
 
600
 
        self.log('k._weave=' + pformat(k._weave))
601
 
 
602
 
        m = list(k.mash_iter([0, 1, 2]))
603
 
 
604
 
        self.assertEqual(m,
605
 
                         ['header', 'aaa',
606
 
                          'line from 1',
607
 
                          'bbb',
608
 
                          'line from 2', 'more from 2'])
609
 
        
 
632
        k.add_lines([], ['aaa', 'bbb'])
 
633
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
634
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
610
635
 
611
636
 
612
637
class Khayyam(TestBase):
613
638
    """Test changes to multi-line texts, and read/write"""
614
 
    def runTest(self):
 
639
 
 
640
    def test_multi_line_merge(self):
615
641
        rawtexts = [
616
642
            """A Book of Verses underneath the Bough,
617
643
            A Jug of Wine, a Loaf of Bread, -- and Thou
641
667
 
642
668
        k = Weave()
643
669
        parents = set()
 
670
        i = 0
644
671
        for t in texts:
645
 
            ver = k.add(list(parents), t)
646
 
            parents.add(ver)
 
672
            ver = k.add_lines('text%d' % i,
 
673
                        list(parents), t)
 
674
            parents.add('text%d' % i)
 
675
            i += 1
647
676
 
648
677
        self.log("k._weave=" + pformat(k._weave))
649
678
 
650
679
        for i, t in enumerate(texts):
651
 
            self.assertEqual(k.get(i), t)
 
680
            self.assertEqual(k.get_lines(i), t)
652
681
 
653
682
        self.check_read_write(k)
654
683
 
655
684
 
656
 
 
657
 
class MergeCases(TestBase):
658
 
    def doMerge(self, base, a, b, mp):
 
685
class JoinWeavesTests(TestBase):
 
686
    def setUp(self):
 
687
        super(JoinWeavesTests, self).setUp()
 
688
        self.weave1 = Weave()
 
689
        self.lines1 = ['hello\n']
 
690
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
 
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)
 
694
        
 
695
    def test_join_empty(self):
 
696
        """Join two empty weaves."""
 
697
        eq = self.assertEqual
 
698
        w1 = Weave()
 
699
        w2 = Weave()
 
700
        w1.join(w2)
 
701
        eq(len(w1), 0)
 
702
        
 
703
    def test_join_empty_to_nonempty(self):
 
704
        """Join empty weave onto nonempty."""
 
705
        self.weave1.join(Weave())
 
706
        self.assertEqual(len(self.weave1), 3)
 
707
 
 
708
    def test_join_unrelated(self):
 
709
        """Join two weaves with no history in common."""
 
710
        wb = Weave()
 
711
        wb.add_lines('b1', [], ['line from b\n'])
 
712
        w1 = self.weave1
 
713
        w1.join(wb)
 
714
        eq = self.assertEqual
 
715
        eq(len(w1), 4)
 
716
        eq(sorted(w1.versions()),
 
717
           ['b1', 'v1', 'v2', 'v3'])
 
718
 
 
719
    def test_join_related(self):
 
720
        wa = self.weave1.copy()
 
721
        wb = self.weave1.copy()
 
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'])
 
724
        eq = self.assertEquals
 
725
        eq(len(wa), 4)
 
726
        eq(len(wb), 4)
 
727
        wa.join(wb)
 
728
        eq(len(wa), 5)
 
729
        eq(wa.get_lines('b1'),
 
730
           ['hello\n', 'pale blue\n', 'world\n'])
 
731
 
 
732
    def test_join_parent_disagreement(self):
 
733
        #join reconciles differening parents into a union.
 
734
        wa = Weave()
 
735
        wb = Weave()
 
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'))
 
741
 
 
742
    def test_join_text_disagreement(self):
 
743
        """Cannot join weaves with different texts for a version."""
 
744
        wa = Weave()
 
745
        wb = Weave()
 
746
        wa.add_lines('v1', [], ['hello\n'])
 
747
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
 
748
        self.assertRaises(WeaveError,
 
749
                          wa.join, wb)
 
750
 
 
751
    def test_join_unordered(self):
 
752
        """Join weaves where indexes differ.
 
753
        
 
754
        The source weave contains a different version at index 0."""
 
755
        wa = self.weave1.copy()
 
756
        wb = Weave()
 
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.
659
771
        from cStringIO import StringIO
660
 
        from textwrap import dedent
661
772
 
662
 
        def addcrlf(x):
663
 
            return x + '\n'
664
 
        
665
773
        w = Weave()
666
 
        w.add([], map(addcrlf, base))
667
 
        w.add([0], map(addcrlf, a))
668
 
        w.add([0], map(addcrlf, b))
 
774
        w.add_lines('v1', [], ['hello\n'])
 
775
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
669
776
 
670
 
        self.log('weave is:')
671
777
        tmpf = StringIO()
672
778
        write_weave(w, tmpf)
673
 
        self.log(tmpf.getvalue())
674
 
 
675
 
        self.log('merge plan:')
676
 
        p = list(w.plan_merge(1, 2))
677
 
        for state, line in p:
678
 
            if line:
679
 
                self.log('%12s | %s' % (state, line[:-1]))
680
 
 
681
 
        self.log('merge:')
682
 
        mt = StringIO()
683
 
        mt.writelines(w.weave_merge(p))
684
 
        mt.seek(0)
685
 
        self.log(mt.getvalue())
686
 
 
687
 
        mp = map(addcrlf, mp)
688
 
        self.assertEqual(mt.readlines(), mp)
689
 
        
690
 
        
691
 
    def testOneInsert(self):
692
 
        self.doMerge([],
693
 
                     ['aa'],
694
 
                     [],
695
 
                     ['aa'])
696
 
 
697
 
    def testSeparateInserts(self):
698
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
699
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
700
 
                     ['aaa', 'bbb', 'yyy', 'ccc'],
701
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
702
 
 
703
 
    def testSameInsert(self):
704
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
705
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
706
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
707
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
708
 
 
709
 
    def testOverlappedInsert(self):
710
 
        self.doMerge(['aaa', 'bbb'],
711
 
                     ['aaa', 'xxx', 'yyy', 'bbb'],
712
 
                     ['aaa', 'xxx', 'bbb'],
713
 
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
714
 
 
715
 
        # really it ought to reduce this to 
716
 
        # ['aaa', 'xxx', 'yyy', 'bbb']
717
 
 
718
 
 
719
 
    def testClashReplace(self):
720
 
        self.doMerge(['aaa'],
721
 
                     ['xxx'],
722
 
                     ['yyy', 'zzz'],
723
 
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
724
 
 
725
 
    def testNonClashInsert(self):
726
 
        self.doMerge(['aaa'],
727
 
                     ['xxx', 'aaa'],
728
 
                     ['yyy', 'zzz'],
729
 
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
730
 
 
731
 
        self.doMerge(['aaa'],
732
 
                     ['aaa'],
733
 
                     ['yyy', 'zzz'],
734
 
                     ['yyy', 'zzz'])
735
 
 
736
 
 
737
 
    def testDeleteAndModify(self):
738
 
        """Clashing delete and modification.
739
 
 
740
 
        If one side modifies a region and the other deletes it then
741
 
        there should be a conflict with one side blank.
742
 
        """
743
 
 
744
 
        #######################################
745
 
        # skippd, not working yet
746
 
        return
747
 
        
748
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
749
 
                     ['aaa', 'ddd', 'ccc'],
750
 
                     ['aaa', 'ccc'],
751
 
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
752
 
    
753
 
 
754
 
 
755
 
def testweave():
756
 
    import testsweet
757
 
    from unittest import TestSuite, TestLoader
758
 
    import testweave
759
 
 
760
 
    tl = TestLoader()
761
 
    suite = TestSuite()
762
 
    suite.addTest(tl.loadTestsFromModule(testweave))
763
 
    
764
 
    return int(not testsweet.run_suite(suite)) # for shell 0=true
765
 
 
766
 
 
767
 
if __name__ == '__main__':
768
 
    import sys
769
 
    sys.exit(testweave())
770
 
    
 
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."""
 
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])))