~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: Martin Pool
  • Date: 2005-10-04 11:13:33 UTC
  • mto: (1185.13.3)
  • mto: This revision was merged to the branch mainline in revision 1403.
  • Revision ID: mbp@sourcefrog.net-20051004111332-f7b8a6bd41b9fe22
- tweak capture_tree formatting

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#! /usr/bin/python2.4
2
2
 
3
3
# Copyright (C) 2005 by Canonical Ltd
4
 
#
 
4
 
5
5
# This program is free software; you can redistribute it and/or modify
6
6
# it under the terms of the GNU General Public License as published by
7
7
# the Free Software Foundation; either version 2 of the License, or
8
8
# (at your option) any later version.
9
 
#
 
9
 
10
10
# This program is distributed in the hope that it will be useful,
11
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
13
# GNU General Public License for more details.
14
 
#
 
14
 
15
15
# You should have received a copy of the GNU General Public License
16
16
# along with this program; if not, write to the Free Software
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
 
 
22
 
23
23
 
24
24
"""test suite for weave algorithm"""
25
25
 
26
26
from pprint import pformat
27
27
 
28
 
import bzrlib.errors as errors
29
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
 
28
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
30
29
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.tests import TestCase
 
30
from bzrlib.selftest import TestCase
32
31
from bzrlib.osutils import sha_string
33
32
 
34
33
 
38
37
          "A second line"]
39
38
 
40
39
 
 
40
 
41
41
class TestBase(TestCase):
42
42
    def check_read_write(self, k):
43
43
        """Check the weave k can be written & re-read."""
58
58
            self.log('         %r' % k._parents)
59
59
            self.log('         %r' % k2._parents)
60
60
            self.log('')
 
61
 
 
62
            
61
63
            self.fail('read/write check failed')
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)
 
64
        
 
65
        
71
66
 
72
67
 
73
68
class Easy(TestBase):
77
72
 
78
73
class StoreText(TestBase):
79
74
    """Store and retrieve a simple text."""
80
 
 
81
 
    def test_storing_text(self):
 
75
    def runTest(self):
82
76
        k = Weave()
83
 
        idx = k.add_lines('text0', [], TEXT_0)
84
 
        self.assertEqual(k.get_lines(idx), TEXT_0)
 
77
        idx = k.add('text0', [], TEXT_0)
 
78
        self.assertEqual(k.get(idx), TEXT_0)
85
79
        self.assertEqual(idx, 0)
86
80
 
87
81
 
 
82
 
88
83
class AnnotateOne(TestBase):
89
84
    def runTest(self):
90
85
        k = Weave()
91
 
        k.add_lines('text0', [], TEXT_0)
92
 
        self.assertEqual(k.annotate('text0'),
93
 
                         [('text0', TEXT_0[0])])
 
86
        k.add('text0', [], TEXT_0)
 
87
        self.assertEqual(k.annotate(0),
 
88
                         [(0, TEXT_0[0])])
94
89
 
95
90
 
96
91
class StoreTwo(TestBase):
97
92
    def runTest(self):
98
93
        k = Weave()
99
94
 
100
 
        idx = k.add_lines('text0', [], TEXT_0)
 
95
        idx = k.add('text0', [], TEXT_0)
101
96
        self.assertEqual(idx, 0)
102
97
 
103
 
        idx = k.add_lines('text1', [], TEXT_1)
 
98
        idx = k.add('text1', [], TEXT_1)
104
99
        self.assertEqual(idx, 1)
105
100
 
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):
 
101
        self.assertEqual(k.get(0), TEXT_0)
 
102
        self.assertEqual(k.get(1), TEXT_1)
 
103
 
 
104
 
 
105
 
 
106
class AddWithGivenSha(TestBase):
 
107
    def runTest(self):
 
108
        """Add with caller-supplied SHA-1"""
112
109
        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
 
                        
 
110
 
 
111
        t = 'text0'
 
112
        k.add('text0', [], [t], sha1=sha_string(t))
 
113
 
 
114
 
121
115
 
122
116
class InvalidAdd(TestBase):
123
117
    """Try to use invalid version number during add."""
124
118
    def runTest(self):
125
119
        k = Weave()
126
120
 
127
 
        self.assertRaises(errors.RevisionNotPresent,
128
 
                          k.add_lines,
 
121
        self.assertRaises(IndexError,
 
122
                          k.add,
129
123
                          'text0',
130
 
                          ['69'],
 
124
                          [69],
131
125
                          ['new text!'])
132
126
 
133
127
 
135
129
    """Add the same version twice; harmless."""
136
130
    def runTest(self):
137
131
        k = Weave()
138
 
        idx = k.add_lines('text0', [], TEXT_0)
139
 
        idx2 = k.add_lines('text0', [], TEXT_0)
 
132
        idx = k.add('text0', [], TEXT_0)
 
133
        idx2 = k.add('text0', [], TEXT_0)
140
134
        self.assertEqual(idx, idx2)
141
135
 
142
136
 
 
137
 
143
138
class InvalidRepeatedAdd(TestBase):
144
139
    def runTest(self):
145
140
        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,
 
141
        idx = k.add('text0', [], TEXT_0)
 
142
        self.assertRaises(WeaveError,
 
143
                          k.add,
150
144
                          'text0',
151
145
                          [],
152
146
                          ['not the same text'])
153
 
        self.assertRaises(errors.RevisionAlreadyPresent,
154
 
                          k.add_lines,
 
147
        self.assertRaises(WeaveError,
 
148
                          k.add,
155
149
                          'text0',
156
 
                          ['basis'],         # not the right parents
 
150
                          [12],         # not the right parents
157
151
                          TEXT_0)
158
152
        
159
153
 
 
154
 
160
155
class InsertLines(TestBase):
161
156
    """Store a revision that adds one line to the original.
162
157
 
165
160
    def runTest(self):
166
161
        k = Weave()
167
162
 
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),
 
163
        k.add('text0', [], ['line 1'])
 
164
        k.add('text1', [0], ['line 1', 'line 2'])
 
165
 
 
166
        self.assertEqual(k.annotate(0),
 
167
                         [(0, 'line 1')])
 
168
 
 
169
        self.assertEqual(k.get(1),
175
170
                         ['line 1',
176
171
                          'line 2'])
177
172
 
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')])
 
173
        self.assertEqual(k.annotate(1),
 
174
                         [(0, 'line 1'),
 
175
                          (1, 'line 2')])
 
176
 
 
177
        k.add('text2', [0], ['line 1', 'diverged line'])
 
178
 
 
179
        self.assertEqual(k.annotate(2),
 
180
                         [(0, 'line 1'),
 
181
                          (2, 'diverged line')])
187
182
 
188
183
        text3 = ['line 1', 'middle line', 'line 2']
189
 
        k.add_lines('text3',
190
 
              ['text0', 'text1'],
 
184
        k.add('text3',
 
185
              [0, 1],
191
186
              text3)
192
187
 
193
188
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
194
189
 
195
190
        self.log("k._weave=" + pformat(k._weave))
196
191
 
197
 
        self.assertEqual(k.annotate('text3'),
198
 
                         [('text0', 'line 1'),
199
 
                          ('text3', 'middle line'),
200
 
                          ('text1', 'line 2')])
 
192
        self.assertEqual(k.annotate(3),
 
193
                         [(0, 'line 1'),
 
194
                          (3, 'middle line'),
 
195
                          (1, 'line 2')])
201
196
 
202
197
        # now multiple insertions at different places
203
 
        k.add_lines('text4',
204
 
              ['text0', 'text1', 'text3'],
 
198
        k.add('text4',
 
199
              [0, 1, 3],
205
200
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
206
201
 
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')])
 
202
        self.assertEqual(k.annotate(4), 
 
203
                         [(0, 'line 1'),
 
204
                          (4, 'aaa'),
 
205
                          (3, 'middle line'),
 
206
                          (4, 'bbb'),
 
207
                          (1, 'line 2'),
 
208
                          (4, 'ccc')])
 
209
 
214
210
 
215
211
 
216
212
class DeleteLines(TestBase):
222
218
 
223
219
        base_text = ['one', 'two', 'three', 'four']
224
220
 
225
 
        k.add_lines('text0', [], base_text)
 
221
        k.add('text0', [], base_text)
226
222
        
227
223
        texts = [['one', 'two', 'three'],
228
224
                 ['two', 'three', 'four'],
232
228
 
233
229
        i = 1
234
230
        for t in texts:
235
 
            ver = k.add_lines('text%d' % i,
236
 
                        ['text0'], t)
 
231
            ver = k.add('text%d' % i,
 
232
                        [0], t)
237
233
            i += 1
238
234
 
239
235
        self.log('final weave:')
240
236
        self.log('k._weave=' + pformat(k._weave))
241
237
 
242
238
        for i in range(len(texts)):
243
 
            self.assertEqual(k.get_lines(i+1),
 
239
            self.assertEqual(k.get(i+1),
244
240
                             texts[i])
 
241
            
 
242
 
245
243
 
246
244
 
247
245
class SuicideDelete(TestBase):
263
261
        return 
264
262
 
265
263
        self.assertRaises(WeaveFormatError,
266
 
                          k.get_lines,
 
264
                          k.get,
267
265
                          0)        
268
266
 
269
267
 
 
268
 
270
269
class CannedDelete(TestBase):
271
270
    """Unpack canned weave with deleted lines."""
272
271
    def runTest(self):
283
282
                'last line',
284
283
                ('}', 0),
285
284
                ]
286
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
287
 
                  , sha_string('first linelast line')]
288
285
 
289
 
        self.assertEqual(k.get_lines(0),
 
286
        self.assertEqual(k.get(0),
290
287
                         ['first line',
291
288
                          'line to be deleted',
292
289
                          'last line',
293
290
                          ])
294
291
 
295
 
        self.assertEqual(k.get_lines(1),
 
292
        self.assertEqual(k.get(1),
296
293
                         ['first line',
297
294
                          'last line',
298
295
                          ])
299
296
 
300
297
 
 
298
 
301
299
class CannedReplacement(TestBase):
302
300
    """Unpack canned weave with deleted lines."""
303
301
    def runTest(self):
317
315
                'last line',
318
316
                ('}', 0),
319
317
                ]
320
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
321
 
                  , sha_string('first linereplacement linelast line')]
322
318
 
323
 
        self.assertEqual(k.get_lines(0),
 
319
        self.assertEqual(k.get(0),
324
320
                         ['first line',
325
321
                          'line to be deleted',
326
322
                          'last line',
327
323
                          ])
328
324
 
329
 
        self.assertEqual(k.get_lines(1),
 
325
        self.assertEqual(k.get(1),
330
326
                         ['first line',
331
327
                          'replacement line',
332
328
                          'last line',
333
329
                          ])
334
330
 
335
331
 
 
332
 
336
333
class BadWeave(TestBase):
337
334
    """Test that we trap an insert which should not occur."""
338
335
    def runTest(self):
418
415
                '}',
419
416
                ('}', 0)]
420
417
 
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),
 
418
        self.assertEqual(k.get(0),
428
419
                         ['foo {',
429
420
                          '}'])
430
421
 
431
 
        self.assertEqual(k.get_lines(1),
 
422
        self.assertEqual(k.get(1),
432
423
                         ['foo {',
433
424
                          '  added in version 1',
434
425
                          '  also from v1',
435
426
                          '}'])
436
427
                       
437
 
        self.assertEqual(k.get_lines(2),
 
428
        self.assertEqual(k.get(2),
438
429
                         ['foo {',
439
430
                          '  added in v2',
440
431
                          '}'])
441
432
 
442
 
        self.assertEqual(k.get_lines(3),
 
433
        self.assertEqual(k.get(3),
443
434
                         ['foo {',
444
435
                          '  added in version 1',
445
436
                          '  added in v2',
447
438
                          '}'])
448
439
                         
449
440
 
 
441
 
450
442
class DeleteLines2(TestBase):
451
443
    """Test recording revisions that delete lines.
452
444
 
455
447
    def runTest(self):
456
448
        k = Weave()
457
449
 
458
 
        k.add_lines('text0', [], ["line the first",
 
450
        k.add('text0', [], ["line the first",
459
451
                   "line 2",
460
452
                   "line 3",
461
453
                   "fine"])
462
454
 
463
 
        self.assertEqual(len(k.get_lines(0)), 4)
 
455
        self.assertEqual(len(k.get(0)), 4)
464
456
 
465
 
        k.add_lines('text1', ['text0'], ["line the first",
 
457
        k.add('text1', [0], ["line the first",
466
458
                   "fine"])
467
459
 
468
 
        self.assertEqual(k.get_lines(1),
 
460
        self.assertEqual(k.get(1),
469
461
                         ["line the first",
470
462
                          "fine"])
471
463
 
472
 
        self.assertEqual(k.annotate('text1'),
473
 
                         [('text0', "line the first"),
474
 
                          ('text0', "fine")])
 
464
        self.assertEqual(k.annotate(1),
 
465
                         [(0, "line the first"),
 
466
                          (0, "fine")])
 
467
 
475
468
 
476
469
 
477
470
class IncludeVersions(TestBase):
495
488
                "second line",
496
489
                ('}', 1)]
497
490
 
498
 
        k._sha1s = [sha_string('first line')
499
 
                  , sha_string('first linesecond line')]
500
 
 
501
 
        self.assertEqual(k.get_lines(1),
 
491
        self.assertEqual(k.get(1),
502
492
                         ["first line",
503
493
                          "second line"])
504
494
 
505
 
        self.assertEqual(k.get_lines(0),
 
495
        self.assertEqual(k.get(0),
506
496
                         ["first line"])
507
497
 
508
498
 
510
500
    """Weave with two diverged texts based on version 0.
511
501
    """
512
502
    def runTest(self):
513
 
        # FIXME make the weave, dont poke at it.
514
503
        k = Weave()
515
504
 
516
 
        k._names = ['0', '1', '2']
517
 
        k._name_map = {'0':0, '1':1, '2':2}
518
505
        k._parents = [frozenset(),
519
506
                frozenset([0]),
520
507
                frozenset([0]),
530
517
                ('}', 2),                
531
518
                ]
532
519
 
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),
 
520
        self.assertEqual(k.get(0),
538
521
                         ["first line"])
539
522
 
540
 
        self.assertEqual(k.get_lines(1),
 
523
        self.assertEqual(k.get(1),
541
524
                         ["first line",
542
525
                          "second line"])
543
526
 
544
 
        self.assertEqual(k.get_lines('2'),
 
527
        self.assertEqual(k.get(2),
545
528
                         ["first line",
546
529
                          "alternative second line"])
547
530
 
548
 
        self.assertEqual(list(k.get_ancestry(['2'])),
549
 
                         ['0', '2'])
 
531
        self.assertEqual(list(k.inclusions([2])),
 
532
                         [0, 2])
 
533
 
550
534
 
551
535
 
552
536
class ReplaceLine(TestBase):
556
540
        text0 = ['cheddar', 'stilton', 'gruyere']
557
541
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
558
542
        
559
 
        k.add_lines('text0', [], text0)
560
 
        k.add_lines('text1', ['text0'], text1)
 
543
        k.add('text0', [], text0)
 
544
        k.add('text1', [0], text1)
561
545
 
562
546
        self.log('k._weave=' + pformat(k._weave))
563
547
 
564
 
        self.assertEqual(k.get_lines(0), text0)
565
 
        self.assertEqual(k.get_lines(1), text1)
 
548
        self.assertEqual(k.get(0), text0)
 
549
        self.assertEqual(k.get(1), text1)
 
550
 
566
551
 
567
552
 
568
553
class Merge(TestBase):
576
561
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
577
562
                 ]
578
563
 
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])
 
564
        k.add('text0', [], texts[0])
 
565
        k.add('text1', [0], texts[1])
 
566
        k.add('text2', [0], texts[2])
 
567
        k.add('merge', [0, 1, 2], texts[3])
583
568
 
584
569
        for i, t in enumerate(texts):
585
 
            self.assertEqual(k.get_lines(i), t)
 
570
            self.assertEqual(k.get(i), t)
586
571
 
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'),
 
572
        self.assertEqual(k.annotate(3),
 
573
                         [(0, 'header'),
 
574
                          (1, ''),
 
575
                          (1, 'line from 1'),
 
576
                          (3, 'fixup line'),
 
577
                          (2, 'line from 2'),
593
578
                          ])
594
579
 
595
 
        self.assertEqual(list(k.get_ancestry(['merge'])),
596
 
                         ['text0', 'text1', 'text2', 'merge'])
 
580
        self.assertEqual(list(k.inclusions([3])),
 
581
                         [0, 1, 2, 3])
597
582
 
598
583
        self.log('k._weave=' + pformat(k._weave))
599
584
 
610
595
        return  # NOT RUN
611
596
        k = Weave()
612
597
 
613
 
        k.add_lines([], ['aaa', 'bbb'])
614
 
        k.add_lines([0], ['aaa', '111', 'bbb'])
615
 
        k.add_lines([1], ['aaa', '222', 'bbb'])
 
598
        k.add([], ['aaa', 'bbb'])
 
599
        k.add([0], ['aaa', '111', 'bbb'])
 
600
        k.add([1], ['aaa', '222', 'bbb'])
616
601
 
617
602
        merged = k.merge([1, 2])
618
603
 
621
606
                           [['bbb']]])
622
607
 
623
608
 
 
609
 
624
610
class NonConflict(TestBase):
625
611
    """Two descendants insert compatible changes.
626
612
 
629
615
        return  # NOT RUN
630
616
        k = Weave()
631
617
 
632
 
        k.add_lines([], ['aaa', 'bbb'])
633
 
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
634
 
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
 
618
        k.add([], ['aaa', 'bbb'])
 
619
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
 
620
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
 
621
 
 
622
    
 
623
    
 
624
 
 
625
 
 
626
class AutoMerge(TestBase):
 
627
    def runTest(self):
 
628
        k = Weave()
 
629
 
 
630
        texts = [['header', 'aaa', 'bbb'],
 
631
                 ['header', 'aaa', 'line from 1', 'bbb'],
 
632
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
 
633
                 ]
 
634
 
 
635
        k.add('text0', [], texts[0])
 
636
        k.add('text1', [0], texts[1])
 
637
        k.add('text2', [0], texts[2])
 
638
 
 
639
        self.log('k._weave=' + pformat(k._weave))
 
640
 
 
641
        m = list(k.mash_iter([0, 1, 2]))
 
642
 
 
643
        self.assertEqual(m,
 
644
                         ['header', 'aaa',
 
645
                          'line from 1',
 
646
                          'bbb',
 
647
                          'line from 2', 'more from 2'])
 
648
        
635
649
 
636
650
 
637
651
class Khayyam(TestBase):
638
652
    """Test changes to multi-line texts, and read/write"""
639
 
 
640
 
    def test_multi_line_merge(self):
 
653
    def runTest(self):
641
654
        rawtexts = [
642
655
            """A Book of Verses underneath the Bough,
643
656
            A Jug of Wine, a Loaf of Bread, -- and Thou
669
682
        parents = set()
670
683
        i = 0
671
684
        for t in texts:
672
 
            ver = k.add_lines('text%d' % i,
 
685
            ver = k.add('text%d' % i,
673
686
                        list(parents), t)
674
 
            parents.add('text%d' % i)
 
687
            parents.add(ver)
675
688
            i += 1
676
689
 
677
690
        self.log("k._weave=" + pformat(k._weave))
678
691
 
679
692
        for i, t in enumerate(texts):
680
 
            self.assertEqual(k.get_lines(i), t)
 
693
            self.assertEqual(k.get(i), t)
681
694
 
682
695
        self.check_read_write(k)
683
696
 
684
697
 
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.
 
698
 
 
699
class MergeCases(TestBase):
 
700
    def doMerge(self, base, a, b, mp):
771
701
        from cStringIO import StringIO
 
702
        from textwrap import dedent
772
703
 
 
704
        def addcrlf(x):
 
705
            return x + '\n'
 
706
        
773
707
        w = Weave()
774
 
        w.add_lines('v1', [], ['hello\n'])
775
 
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
708
        w.add('text0', [], map(addcrlf, base))
 
709
        w.add('text1', [0], map(addcrlf, a))
 
710
        w.add('text2', [0], map(addcrlf, b))
776
711
 
 
712
        self.log('weave is:')
777
713
        tmpf = StringIO()
778
714
        write_weave(w, tmpf)
779
 
 
780
 
        # Because we are corrupting, we need to make sure we have the exact text
781
 
        self.assertEquals('# bzr weave file v5\n'
782
 
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
783
 
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
784
 
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
785
 
                          tmpf.getvalue())
786
 
 
787
 
        # Change a single letter
788
 
        tmpf = StringIO('# bzr weave file v5\n'
789
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
790
 
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
791
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
792
 
 
793
 
        w = read_weave(tmpf)
794
 
 
795
 
        self.assertEqual('hello\n', w.get_text('v1'))
796
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
797
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
798
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
799
 
 
800
 
        # Change the sha checksum
801
 
        tmpf = StringIO('# bzr weave file v5\n'
802
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
803
 
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
804
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
805
 
 
806
 
        w = read_weave(tmpf)
807
 
 
808
 
        self.assertEqual('hello\n', w.get_text('v1'))
809
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
810
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
811
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
812
 
 
813
 
 
814
 
class InstrumentedWeave(Weave):
815
 
    """Keep track of how many times functions are called."""
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])))
 
715
        self.log(tmpf.getvalue())
 
716
 
 
717
        self.log('merge plan:')
 
718
        p = list(w.plan_merge(1, 2))
 
719
        for state, line in p:
 
720
            if line:
 
721
                self.log('%12s | %s' % (state, line[:-1]))
 
722
 
 
723
        self.log('merge:')
 
724
        mt = StringIO()
 
725
        mt.writelines(w.weave_merge(p))
 
726
        mt.seek(0)
 
727
        self.log(mt.getvalue())
 
728
 
 
729
        mp = map(addcrlf, mp)
 
730
        self.assertEqual(mt.readlines(), mp)
 
731
        
 
732
        
 
733
    def testOneInsert(self):
 
734
        self.doMerge([],
 
735
                     ['aa'],
 
736
                     [],
 
737
                     ['aa'])
 
738
 
 
739
    def testSeparateInserts(self):
 
740
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
741
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
742
                     ['aaa', 'bbb', 'yyy', 'ccc'],
 
743
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
744
 
 
745
    def testSameInsert(self):
 
746
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
747
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
748
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
 
749
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
750
 
 
751
    def testOverlappedInsert(self):
 
752
        self.doMerge(['aaa', 'bbb'],
 
753
                     ['aaa', 'xxx', 'yyy', 'bbb'],
 
754
                     ['aaa', 'xxx', 'bbb'],
 
755
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
756
 
 
757
        # really it ought to reduce this to 
 
758
        # ['aaa', 'xxx', 'yyy', 'bbb']
 
759
 
 
760
 
 
761
    def testClashReplace(self):
 
762
        self.doMerge(['aaa'],
 
763
                     ['xxx'],
 
764
                     ['yyy', 'zzz'],
 
765
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
766
 
 
767
    def testNonClashInsert(self):
 
768
        self.doMerge(['aaa'],
 
769
                     ['xxx', 'aaa'],
 
770
                     ['yyy', 'zzz'],
 
771
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
772
 
 
773
        self.doMerge(['aaa'],
 
774
                     ['aaa'],
 
775
                     ['yyy', 'zzz'],
 
776
                     ['yyy', 'zzz'])
 
777
 
 
778
 
 
779
    def testDeleteAndModify(self):
 
780
        """Clashing delete and modification.
 
781
 
 
782
        If one side modifies a region and the other deletes it then
 
783
        there should be a conflict with one side blank.
 
784
        """
 
785
 
 
786
        #######################################
 
787
        # skippd, not working yet
 
788
        return
 
789
        
 
790
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
791
                     ['aaa', 'ddd', 'ccc'],
 
792
                     ['aaa', 'ccc'],
 
793
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
 
794
    
 
795
 
 
796
 
 
797
if __name__ == '__main__':
 
798
    import sys
 
799
    import unittest
 
800
    sys.exit(unittest.main())
 
801