~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: Robert Collins
  • Date: 2005-10-06 22:15:52 UTC
  • mfrom: (1185.13.2)
  • mto: This revision was merged to the branch mainline in revision 1420.
  • Revision ID: robertc@robertcollins.net-20051006221552-9b15c96fa504e0ad
mergeĀ fromĀ upstream

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
 
from bzrlib import (
29
 
    errors,
30
 
    )
 
28
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
 
29
from bzrlib.weavefile import write_weave, read_weave
 
30
from bzrlib.selftest import TestCase
31
31
from bzrlib.osutils import sha_string
32
 
from bzrlib.tests import TestCase, TestCaseInTempDir
33
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
34
 
from bzrlib.weavefile import write_weave, read_weave
35
32
 
36
33
 
37
34
# texts for use in testing
40
37
          "A second line"]
41
38
 
42
39
 
 
40
 
43
41
class TestBase(TestCase):
44
42
    def check_read_write(self, k):
45
43
        """Check the weave k can be written & re-read."""
60
58
            self.log('         %r' % k._parents)
61
59
            self.log('         %r' % k2._parents)
62
60
            self.log('')
 
61
 
 
62
            
63
63
            self.fail('read/write check failed')
64
 
 
65
 
 
66
 
class WeaveContains(TestBase):
67
 
    """Weave __contains__ operator"""
68
 
    def runTest(self):
69
 
        k = Weave()
70
 
        self.assertFalse('foo' in k)
71
 
        k.add_lines('foo', [], TEXT_1)
72
 
        self.assertTrue('foo' in k)
 
64
        
 
65
        
73
66
 
74
67
 
75
68
class Easy(TestBase):
79
72
 
80
73
class StoreText(TestBase):
81
74
    """Store and retrieve a simple text."""
82
 
 
83
 
    def test_storing_text(self):
 
75
    def runTest(self):
84
76
        k = Weave()
85
 
        idx = k.add_lines('text0', [], TEXT_0)
86
 
        self.assertEqual(k.get_lines(idx), TEXT_0)
 
77
        idx = k.add('text0', [], TEXT_0)
 
78
        self.assertEqual(k.get(idx), TEXT_0)
87
79
        self.assertEqual(idx, 0)
88
80
 
89
81
 
 
82
 
90
83
class AnnotateOne(TestBase):
91
84
    def runTest(self):
92
85
        k = Weave()
93
 
        k.add_lines('text0', [], TEXT_0)
94
 
        self.assertEqual(k.annotate('text0'),
95
 
                         [('text0', TEXT_0[0])])
 
86
        k.add('text0', [], TEXT_0)
 
87
        self.assertEqual(k.annotate(0),
 
88
                         [(0, TEXT_0[0])])
96
89
 
97
90
 
98
91
class StoreTwo(TestBase):
99
92
    def runTest(self):
100
93
        k = Weave()
101
94
 
102
 
        idx = k.add_lines('text0', [], TEXT_0)
 
95
        idx = k.add('text0', [], TEXT_0)
103
96
        self.assertEqual(idx, 0)
104
97
 
105
 
        idx = k.add_lines('text1', [], TEXT_1)
 
98
        idx = k.add('text1', [], TEXT_1)
106
99
        self.assertEqual(idx, 1)
107
100
 
108
 
        self.assertEqual(k.get_lines(0), TEXT_0)
109
 
        self.assertEqual(k.get_lines(1), TEXT_1)
110
 
 
111
 
 
112
 
class GetSha1(TestBase):
113
 
    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"""
114
109
        k = Weave()
115
 
        k.add_lines('text0', [], 'text0')
116
 
        self.assertEqual('34dc0e430c642a26c3dd1c2beb7a8b4f4445eb79',
117
 
                         k.get_sha1('text0'))
118
 
        self.assertRaises(errors.RevisionNotPresent,
119
 
                          k.get_sha1, 0)
120
 
        self.assertRaises(errors.RevisionNotPresent,
121
 
                          k.get_sha1, 'text1')
122
 
                        
 
110
 
 
111
        t = 'text0'
 
112
        k.add('text0', [], [t], sha1=sha_string(t))
 
113
 
 
114
 
123
115
 
124
116
class InvalidAdd(TestBase):
125
117
    """Try to use invalid version number during add."""
126
118
    def runTest(self):
127
119
        k = Weave()
128
120
 
129
 
        self.assertRaises(errors.RevisionNotPresent,
130
 
                          k.add_lines,
 
121
        self.assertRaises(IndexError,
 
122
                          k.add,
131
123
                          'text0',
132
 
                          ['69'],
 
124
                          [69],
133
125
                          ['new text!'])
134
126
 
135
127
 
137
129
    """Add the same version twice; harmless."""
138
130
    def runTest(self):
139
131
        k = Weave()
140
 
        idx = k.add_lines('text0', [], TEXT_0)
141
 
        idx2 = k.add_lines('text0', [], TEXT_0)
 
132
        idx = k.add('text0', [], TEXT_0)
 
133
        idx2 = k.add('text0', [], TEXT_0)
142
134
        self.assertEqual(idx, idx2)
143
135
 
144
136
 
 
137
 
145
138
class InvalidRepeatedAdd(TestBase):
146
139
    def runTest(self):
147
140
        k = Weave()
148
 
        k.add_lines('basis', [], TEXT_0)
149
 
        idx = k.add_lines('text0', [], TEXT_0)
150
 
        self.assertRaises(errors.RevisionAlreadyPresent,
151
 
                          k.add_lines,
 
141
        idx = k.add('text0', [], TEXT_0)
 
142
        self.assertRaises(WeaveError,
 
143
                          k.add,
152
144
                          'text0',
153
145
                          [],
154
146
                          ['not the same text'])
155
 
        self.assertRaises(errors.RevisionAlreadyPresent,
156
 
                          k.add_lines,
 
147
        self.assertRaises(WeaveError,
 
148
                          k.add,
157
149
                          'text0',
158
 
                          ['basis'],         # not the right parents
 
150
                          [12],         # not the right parents
159
151
                          TEXT_0)
160
152
        
161
153
 
 
154
 
162
155
class InsertLines(TestBase):
163
156
    """Store a revision that adds one line to the original.
164
157
 
167
160
    def runTest(self):
168
161
        k = Weave()
169
162
 
170
 
        k.add_lines('text0', [], ['line 1'])
171
 
        k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
172
 
 
173
 
        self.assertEqual(k.annotate('text0'),
174
 
                         [('text0', 'line 1')])
175
 
 
176
 
        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),
177
170
                         ['line 1',
178
171
                          'line 2'])
179
172
 
180
 
        self.assertEqual(k.annotate('text1'),
181
 
                         [('text0', 'line 1'),
182
 
                          ('text1', 'line 2')])
183
 
 
184
 
        k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
185
 
 
186
 
        self.assertEqual(k.annotate('text2'),
187
 
                         [('text0', 'line 1'),
188
 
                          ('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')])
189
182
 
190
183
        text3 = ['line 1', 'middle line', 'line 2']
191
 
        k.add_lines('text3',
192
 
              ['text0', 'text1'],
 
184
        k.add('text3',
 
185
              [0, 1],
193
186
              text3)
194
187
 
195
188
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
196
189
 
197
190
        self.log("k._weave=" + pformat(k._weave))
198
191
 
199
 
        self.assertEqual(k.annotate('text3'),
200
 
                         [('text0', 'line 1'),
201
 
                          ('text3', 'middle line'),
202
 
                          ('text1', 'line 2')])
 
192
        self.assertEqual(k.annotate(3),
 
193
                         [(0, 'line 1'),
 
194
                          (3, 'middle line'),
 
195
                          (1, 'line 2')])
203
196
 
204
197
        # now multiple insertions at different places
205
 
        k.add_lines('text4',
206
 
              ['text0', 'text1', 'text3'],
 
198
        k.add('text4',
 
199
              [0, 1, 3],
207
200
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
208
201
 
209
 
        self.assertEqual(k.annotate('text4'), 
210
 
                         [('text0', 'line 1'),
211
 
                          ('text4', 'aaa'),
212
 
                          ('text3', 'middle line'),
213
 
                          ('text4', 'bbb'),
214
 
                          ('text1', 'line 2'),
215
 
                          ('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
 
216
210
 
217
211
 
218
212
class DeleteLines(TestBase):
224
218
 
225
219
        base_text = ['one', 'two', 'three', 'four']
226
220
 
227
 
        k.add_lines('text0', [], base_text)
 
221
        k.add('text0', [], base_text)
228
222
        
229
223
        texts = [['one', 'two', 'three'],
230
224
                 ['two', 'three', 'four'],
234
228
 
235
229
        i = 1
236
230
        for t in texts:
237
 
            ver = k.add_lines('text%d' % i,
238
 
                        ['text0'], t)
 
231
            ver = k.add('text%d' % i,
 
232
                        [0], t)
239
233
            i += 1
240
234
 
241
235
        self.log('final weave:')
242
236
        self.log('k._weave=' + pformat(k._weave))
243
237
 
244
238
        for i in range(len(texts)):
245
 
            self.assertEqual(k.get_lines(i+1),
 
239
            self.assertEqual(k.get(i+1),
246
240
                             texts[i])
 
241
            
 
242
 
247
243
 
248
244
 
249
245
class SuicideDelete(TestBase):
265
261
        return 
266
262
 
267
263
        self.assertRaises(WeaveFormatError,
268
 
                          k.get_lines,
 
264
                          k.get,
269
265
                          0)        
270
266
 
271
267
 
 
268
 
272
269
class CannedDelete(TestBase):
273
270
    """Unpack canned weave with deleted lines."""
274
271
    def runTest(self):
285
282
                'last line',
286
283
                ('}', 0),
287
284
                ]
288
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
289
 
                  , sha_string('first linelast line')]
290
285
 
291
 
        self.assertEqual(k.get_lines(0),
 
286
        self.assertEqual(k.get(0),
292
287
                         ['first line',
293
288
                          'line to be deleted',
294
289
                          'last line',
295
290
                          ])
296
291
 
297
 
        self.assertEqual(k.get_lines(1),
 
292
        self.assertEqual(k.get(1),
298
293
                         ['first line',
299
294
                          'last line',
300
295
                          ])
301
296
 
302
297
 
 
298
 
303
299
class CannedReplacement(TestBase):
304
300
    """Unpack canned weave with deleted lines."""
305
301
    def runTest(self):
319
315
                'last line',
320
316
                ('}', 0),
321
317
                ]
322
 
        k._sha1s = [sha_string('first lineline to be deletedlast line')
323
 
                  , sha_string('first linereplacement linelast line')]
324
318
 
325
 
        self.assertEqual(k.get_lines(0),
 
319
        self.assertEqual(k.get(0),
326
320
                         ['first line',
327
321
                          'line to be deleted',
328
322
                          'last line',
329
323
                          ])
330
324
 
331
 
        self.assertEqual(k.get_lines(1),
 
325
        self.assertEqual(k.get(1),
332
326
                         ['first line',
333
327
                          'replacement line',
334
328
                          'last line',
335
329
                          ])
336
330
 
337
331
 
 
332
 
338
333
class BadWeave(TestBase):
339
334
    """Test that we trap an insert which should not occur."""
340
335
    def runTest(self):
420
415
                '}',
421
416
                ('}', 0)]
422
417
 
423
 
        k._sha1s = [sha_string('foo {}')
424
 
                  , sha_string('foo {  added in version 1  also from v1}')
425
 
                  , sha_string('foo {  added in v2}')
426
 
                  , sha_string('foo {  added in version 1  added in v2  also from v1}')
427
 
                  ]
428
 
 
429
 
        self.assertEqual(k.get_lines(0),
 
418
        self.assertEqual(k.get(0),
430
419
                         ['foo {',
431
420
                          '}'])
432
421
 
433
 
        self.assertEqual(k.get_lines(1),
 
422
        self.assertEqual(k.get(1),
434
423
                         ['foo {',
435
424
                          '  added in version 1',
436
425
                          '  also from v1',
437
426
                          '}'])
438
427
                       
439
 
        self.assertEqual(k.get_lines(2),
 
428
        self.assertEqual(k.get(2),
440
429
                         ['foo {',
441
430
                          '  added in v2',
442
431
                          '}'])
443
432
 
444
 
        self.assertEqual(k.get_lines(3),
 
433
        self.assertEqual(k.get(3),
445
434
                         ['foo {',
446
435
                          '  added in version 1',
447
436
                          '  added in v2',
449
438
                          '}'])
450
439
                         
451
440
 
 
441
 
452
442
class DeleteLines2(TestBase):
453
443
    """Test recording revisions that delete lines.
454
444
 
457
447
    def runTest(self):
458
448
        k = Weave()
459
449
 
460
 
        k.add_lines('text0', [], ["line the first",
 
450
        k.add('text0', [], ["line the first",
461
451
                   "line 2",
462
452
                   "line 3",
463
453
                   "fine"])
464
454
 
465
 
        self.assertEqual(len(k.get_lines(0)), 4)
 
455
        self.assertEqual(len(k.get(0)), 4)
466
456
 
467
 
        k.add_lines('text1', ['text0'], ["line the first",
 
457
        k.add('text1', [0], ["line the first",
468
458
                   "fine"])
469
459
 
470
 
        self.assertEqual(k.get_lines(1),
 
460
        self.assertEqual(k.get(1),
471
461
                         ["line the first",
472
462
                          "fine"])
473
463
 
474
 
        self.assertEqual(k.annotate('text1'),
475
 
                         [('text0', "line the first"),
476
 
                          ('text0', "fine")])
 
464
        self.assertEqual(k.annotate(1),
 
465
                         [(0, "line the first"),
 
466
                          (0, "fine")])
 
467
 
477
468
 
478
469
 
479
470
class IncludeVersions(TestBase):
497
488
                "second line",
498
489
                ('}', 1)]
499
490
 
500
 
        k._sha1s = [sha_string('first line')
501
 
                  , sha_string('first linesecond line')]
502
 
 
503
 
        self.assertEqual(k.get_lines(1),
 
491
        self.assertEqual(k.get(1),
504
492
                         ["first line",
505
493
                          "second line"])
506
494
 
507
 
        self.assertEqual(k.get_lines(0),
 
495
        self.assertEqual(k.get(0),
508
496
                         ["first line"])
509
497
 
510
498
 
512
500
    """Weave with two diverged texts based on version 0.
513
501
    """
514
502
    def runTest(self):
515
 
        # FIXME make the weave, dont poke at it.
516
503
        k = Weave()
517
504
 
518
 
        k._names = ['0', '1', '2']
519
 
        k._name_map = {'0':0, '1':1, '2':2}
520
505
        k._parents = [frozenset(),
521
506
                frozenset([0]),
522
507
                frozenset([0]),
532
517
                ('}', 2),                
533
518
                ]
534
519
 
535
 
        k._sha1s = [sha_string('first line')
536
 
                  , sha_string('first linesecond line')
537
 
                  , sha_string('first linealternative second line')]
538
 
 
539
 
        self.assertEqual(k.get_lines(0),
 
520
        self.assertEqual(k.get(0),
540
521
                         ["first line"])
541
522
 
542
 
        self.assertEqual(k.get_lines(1),
 
523
        self.assertEqual(k.get(1),
543
524
                         ["first line",
544
525
                          "second line"])
545
526
 
546
 
        self.assertEqual(k.get_lines('2'),
 
527
        self.assertEqual(k.get(2),
547
528
                         ["first line",
548
529
                          "alternative second line"])
549
530
 
550
 
        self.assertEqual(list(k.get_ancestry(['2'])),
551
 
                         ['0', '2'])
 
531
        self.assertEqual(list(k.inclusions([2])),
 
532
                         [0, 2])
 
533
 
552
534
 
553
535
 
554
536
class ReplaceLine(TestBase):
558
540
        text0 = ['cheddar', 'stilton', 'gruyere']
559
541
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
560
542
        
561
 
        k.add_lines('text0', [], text0)
562
 
        k.add_lines('text1', ['text0'], text1)
 
543
        k.add('text0', [], text0)
 
544
        k.add('text1', [0], text1)
563
545
 
564
546
        self.log('k._weave=' + pformat(k._weave))
565
547
 
566
 
        self.assertEqual(k.get_lines(0), text0)
567
 
        self.assertEqual(k.get_lines(1), text1)
 
548
        self.assertEqual(k.get(0), text0)
 
549
        self.assertEqual(k.get(1), text1)
 
550
 
568
551
 
569
552
 
570
553
class Merge(TestBase):
578
561
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
579
562
                 ]
580
563
 
581
 
        k.add_lines('text0', [], texts[0])
582
 
        k.add_lines('text1', ['text0'], texts[1])
583
 
        k.add_lines('text2', ['text0'], texts[2])
584
 
        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])
585
568
 
586
569
        for i, t in enumerate(texts):
587
 
            self.assertEqual(k.get_lines(i), t)
 
570
            self.assertEqual(k.get(i), t)
588
571
 
589
 
        self.assertEqual(k.annotate('merge'),
590
 
                         [('text0', 'header'),
591
 
                          ('text1', ''),
592
 
                          ('text1', 'line from 1'),
593
 
                          ('merge', 'fixup line'),
594
 
                          ('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'),
595
578
                          ])
596
579
 
597
 
        self.assertEqual(list(k.get_ancestry(['merge'])),
598
 
                         ['text0', 'text1', 'text2', 'merge'])
 
580
        self.assertEqual(list(k.inclusions([3])),
 
581
                         [0, 1, 2, 3])
599
582
 
600
583
        self.log('k._weave=' + pformat(k._weave))
601
584
 
612
595
        return  # NOT RUN
613
596
        k = Weave()
614
597
 
615
 
        k.add_lines([], ['aaa', 'bbb'])
616
 
        k.add_lines([0], ['aaa', '111', 'bbb'])
617
 
        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'])
618
601
 
619
602
        merged = k.merge([1, 2])
620
603
 
623
606
                           [['bbb']]])
624
607
 
625
608
 
 
609
 
626
610
class NonConflict(TestBase):
627
611
    """Two descendants insert compatible changes.
628
612
 
631
615
        return  # NOT RUN
632
616
        k = Weave()
633
617
 
634
 
        k.add_lines([], ['aaa', 'bbb'])
635
 
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
636
 
        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
        
637
649
 
638
650
 
639
651
class Khayyam(TestBase):
640
652
    """Test changes to multi-line texts, and read/write"""
641
 
 
642
 
    def test_multi_line_merge(self):
 
653
    def runTest(self):
643
654
        rawtexts = [
644
655
            """A Book of Verses underneath the Bough,
645
656
            A Jug of Wine, a Loaf of Bread, -- and Thou
671
682
        parents = set()
672
683
        i = 0
673
684
        for t in texts:
674
 
            ver = k.add_lines('text%d' % i,
 
685
            ver = k.add('text%d' % i,
675
686
                        list(parents), t)
676
 
            parents.add('text%d' % i)
 
687
            parents.add(ver)
677
688
            i += 1
678
689
 
679
690
        self.log("k._weave=" + pformat(k._weave))
680
691
 
681
692
        for i, t in enumerate(texts):
682
 
            self.assertEqual(k.get_lines(i), t)
 
693
            self.assertEqual(k.get(i), t)
683
694
 
684
695
        self.check_read_write(k)
685
696
 
686
697
 
 
698
 
 
699
class MergeCases(TestBase):
 
700
    def doMerge(self, base, a, b, mp):
 
701
        from cStringIO import StringIO
 
702
        from textwrap import dedent
 
703
 
 
704
        def addcrlf(x):
 
705
            return x + '\n'
 
706
        
 
707
        w = Weave()
 
708
        w.add('text0', [], map(addcrlf, base))
 
709
        w.add('text1', [0], map(addcrlf, a))
 
710
        w.add('text2', [0], map(addcrlf, b))
 
711
 
 
712
        self.log('weave is:')
 
713
        tmpf = StringIO()
 
714
        write_weave(w, tmpf)
 
715
        self.log(tmpf.getvalue())
 
716
 
 
717
        self.log('merge plan:')
 
718
        p = list(w.plan_merge(1, 2))
 
719
        for state, line in p:
 
720
            if line:
 
721
                self.log('%12s | %s' % (state, line[:-1]))
 
722
 
 
723
        self.log('merge:')
 
724
        mt = StringIO()
 
725
        mt.writelines(w.weave_merge(p))
 
726
        mt.seek(0)
 
727
        self.log(mt.getvalue())
 
728
 
 
729
        mp = map(addcrlf, mp)
 
730
        self.assertEqual(mt.readlines(), mp)
 
731
        
 
732
        
 
733
    def testOneInsert(self):
 
734
        self.doMerge([],
 
735
                     ['aa'],
 
736
                     [],
 
737
                     ['aa'])
 
738
 
 
739
    def testSeparateInserts(self):
 
740
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
741
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
742
                     ['aaa', 'bbb', 'yyy', 'ccc'],
 
743
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
744
 
 
745
    def testSameInsert(self):
 
746
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
747
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
748
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
 
749
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
750
 
 
751
    def testOverlappedInsert(self):
 
752
        self.doMerge(['aaa', 'bbb'],
 
753
                     ['aaa', 'xxx', 'yyy', 'bbb'],
 
754
                     ['aaa', 'xxx', 'bbb'],
 
755
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
756
 
 
757
        # really it ought to reduce this to 
 
758
        # ['aaa', 'xxx', 'yyy', 'bbb']
 
759
 
 
760
 
 
761
    def testClashReplace(self):
 
762
        self.doMerge(['aaa'],
 
763
                     ['xxx'],
 
764
                     ['yyy', 'zzz'],
 
765
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
766
 
 
767
    def testNonClashInsert(self):
 
768
        self.doMerge(['aaa'],
 
769
                     ['xxx', 'aaa'],
 
770
                     ['yyy', 'zzz'],
 
771
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
772
 
 
773
        self.doMerge(['aaa'],
 
774
                     ['aaa'],
 
775
                     ['yyy', 'zzz'],
 
776
                     ['yyy', 'zzz'])
 
777
 
 
778
 
 
779
    def testDeleteAndModify(self):
 
780
        """Clashing delete and modification.
 
781
 
 
782
        If one side modifies a region and the other deletes it then
 
783
        there should be a conflict with one side blank.
 
784
        """
 
785
 
 
786
        #######################################
 
787
        # skippd, not working yet
 
788
        return
 
789
        
 
790
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
791
                     ['aaa', 'ddd', 'ccc'],
 
792
                     ['aaa', 'ccc'],
 
793
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
 
794
 
 
795
 
687
796
class JoinWeavesTests(TestBase):
688
797
    def setUp(self):
689
798
        super(JoinWeavesTests, self).setUp()
690
799
        self.weave1 = Weave()
691
800
        self.lines1 = ['hello\n']
692
801
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
693
 
        self.weave1.add_lines('v1', [], self.lines1)
694
 
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
695
 
        self.weave1.add_lines('v3', ['v2'], self.lines3)
 
802
        self.weave1.add('v1', [], self.lines1)
 
803
        self.weave1.add('v2', [0], ['hello\n', 'world\n'])
 
804
        self.weave1.add('v3', [1], self.lines3)
696
805
        
697
806
    def test_join_empty(self):
698
807
        """Join two empty weaves."""
700
809
        w1 = Weave()
701
810
        w2 = Weave()
702
811
        w1.join(w2)
703
 
        eq(len(w1), 0)
 
812
        eq(w1.numversions(), 0)
704
813
        
705
814
    def test_join_empty_to_nonempty(self):
706
815
        """Join empty weave onto nonempty."""
710
819
    def test_join_unrelated(self):
711
820
        """Join two weaves with no history in common."""
712
821
        wb = Weave()
713
 
        wb.add_lines('b1', [], ['line from b\n'])
 
822
        wb.add('b1', [], ['line from b\n'])
714
823
        w1 = self.weave1
715
824
        w1.join(wb)
716
825
        eq = self.assertEqual
717
826
        eq(len(w1), 4)
718
 
        eq(sorted(w1.versions()),
 
827
        eq(sorted(list(w1.iter_names())),
719
828
           ['b1', 'v1', 'v2', 'v3'])
720
829
 
721
830
    def test_join_related(self):
722
831
        wa = self.weave1.copy()
723
832
        wb = self.weave1.copy()
724
 
        wa.add_lines('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
725
 
        wb.add_lines('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
 
833
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
 
834
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
726
835
        eq = self.assertEquals
727
836
        eq(len(wa), 4)
728
837
        eq(len(wb), 4)
732
841
           ['hello\n', 'pale blue\n', 'world\n'])
733
842
 
734
843
    def test_join_parent_disagreement(self):
735
 
        #join reconciles differening parents into a union.
 
844
        """Cannot join weaves with different parents for a version."""
736
845
        wa = Weave()
737
846
        wb = Weave()
738
 
        wa.add_lines('v1', [], ['hello\n'])
739
 
        wb.add_lines('v0', [], [])
740
 
        wb.add_lines('v1', ['v0'], ['hello\n'])
741
 
        wa.join(wb)
742
 
        self.assertEqual(['v0'], wa.get_parents('v1'))
 
847
        wa.add('v1', [], ['hello\n'])
 
848
        wb.add('v0', [], [])
 
849
        wb.add('v1', ['v0'], ['hello\n'])
 
850
        self.assertRaises(WeaveError,
 
851
                          wa.join, wb)
743
852
 
744
853
    def test_join_text_disagreement(self):
745
854
        """Cannot join weaves with different texts for a version."""
746
855
        wa = Weave()
747
856
        wb = Weave()
748
 
        wa.add_lines('v1', [], ['hello\n'])
749
 
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
 
857
        wa.add('v1', [], ['hello\n'])
 
858
        wb.add('v1', [], ['not\n', 'hello\n'])
750
859
        self.assertRaises(WeaveError,
751
860
                          wa.join, wb)
752
861
 
756
865
        The source weave contains a different version at index 0."""
757
866
        wa = self.weave1.copy()
758
867
        wb = Weave()
759
 
        wb.add_lines('x1', [], ['line from x1\n'])
760
 
        wb.add_lines('v1', [], ['hello\n'])
761
 
        wb.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
762
 
        wa.join(wb)
763
 
        eq = self.assertEquals
764
 
        eq(sorted(wa.versions()), ['v1', 'v2', 'v3', 'x1',])
765
 
        eq(wa.get_text('x1'), 'line from x1\n')
766
 
 
767
 
    def test_written_detection(self):
768
 
        # Test detection of weave file corruption.
769
 
        #
770
 
        # Make sure that we can detect if a weave file has
771
 
        # been corrupted. This doesn't test all forms of corruption,
772
 
        # but it at least helps verify the data you get, is what you want.
773
 
        from cStringIO import StringIO
774
 
 
775
 
        w = Weave()
776
 
        w.add_lines('v1', [], ['hello\n'])
777
 
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
778
 
 
779
 
        tmpf = StringIO()
780
 
        write_weave(w, tmpf)
781
 
 
782
 
        # Because we are corrupting, we need to make sure we have the exact text
783
 
        self.assertEquals('# bzr weave file v5\n'
784
 
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
785
 
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
786
 
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
787
 
                          tmpf.getvalue())
788
 
 
789
 
        # Change a single letter
790
 
        tmpf = StringIO('# bzr weave file v5\n'
791
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
792
 
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
793
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
794
 
 
795
 
        w = read_weave(tmpf)
796
 
 
797
 
        self.assertEqual('hello\n', w.get_text('v1'))
798
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
799
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
800
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
801
 
 
802
 
        # Change the sha checksum
803
 
        tmpf = StringIO('# bzr weave file v5\n'
804
 
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
805
 
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
806
 
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
807
 
 
808
 
        w = read_weave(tmpf)
809
 
 
810
 
        self.assertEqual('hello\n', w.get_text('v1'))
811
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
812
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
813
 
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
814
 
 
815
 
 
816
 
class InstrumentedWeave(Weave):
817
 
    """Keep track of how many times functions are called."""
818
 
    
819
 
    def __init__(self, weave_name=None):
820
 
        self._extract_count = 0
821
 
        Weave.__init__(self, weave_name=weave_name)
822
 
 
823
 
    def _extract(self, versions):
824
 
        self._extract_count += 1
825
 
        return Weave._extract(self, versions)
826
 
 
827
 
 
828
 
class JoinOptimization(TestCase):
829
 
    """Test that Weave.join() doesn't extract all texts, only what must be done."""
830
 
 
831
 
    def test_join(self):
832
 
        w1 = InstrumentedWeave()
833
 
        w2 = InstrumentedWeave()
834
 
 
835
 
        txt0 = ['a\n']
836
 
        txt1 = ['a\n', 'b\n']
837
 
        txt2 = ['a\n', 'c\n']
838
 
        txt3 = ['a\n', 'b\n', 'c\n']
839
 
 
840
 
        w1.add_lines('txt0', [], txt0) # extract 1a
841
 
        w2.add_lines('txt0', [], txt0) # extract 1b
842
 
        w1.add_lines('txt1', ['txt0'], txt1)# extract 2a
843
 
        w2.add_lines('txt2', ['txt0'], txt2)# extract 2b
844
 
        w1.join(w2) # extract 3a to add txt2 
845
 
        w2.join(w1) # extract 3b to add txt1 
846
 
 
847
 
        w1.add_lines('txt3', ['txt1', 'txt2'], txt3) # extract 4a 
848
 
        w2.add_lines('txt3', ['txt2', 'txt1'], txt3) # extract 4b
849
 
        # These secretly have inverted parents
850
 
 
851
 
        # This should not have to do any extractions
852
 
        w1.join(w2) # NO extract, texts already present with same parents
853
 
        w2.join(w1) # NO extract, texts already present with same parents
854
 
 
855
 
        self.assertEqual(4, w1._extract_count)
856
 
        self.assertEqual(4, w2._extract_count)
857
 
 
858
 
    def test_double_parent(self):
859
 
        # It should not be considered illegal to add
860
 
        # a revision with the same parent twice
861
 
        w1 = InstrumentedWeave()
862
 
        w2 = InstrumentedWeave()
863
 
 
864
 
        txt0 = ['a\n']
865
 
        txt1 = ['a\n', 'b\n']
866
 
        txt2 = ['a\n', 'c\n']
867
 
        txt3 = ['a\n', 'b\n', 'c\n']
868
 
 
869
 
        w1.add_lines('txt0', [], txt0)
870
 
        w2.add_lines('txt0', [], txt0)
871
 
        w1.add_lines('txt1', ['txt0'], txt1)
872
 
        w2.add_lines('txt1', ['txt0', 'txt0'], txt1)
873
 
        # Same text, effectively the same, because the
874
 
        # parent is only repeated
875
 
        w1.join(w2) # extract 3a to add txt2 
876
 
        w2.join(w1) # extract 3b to add txt1 
877
 
 
878
 
 
879
 
class TestNeedsReweave(TestCase):
880
 
    """Internal corner cases for when reweave is needed."""
881
 
 
882
 
    def test_compatible_parents(self):
883
 
        w1 = Weave('a')
884
 
        my_parents = set([1, 2, 3])
885
 
        # subsets are ok
886
 
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
887
 
        # same sets
888
 
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
889
 
        # same empty corner case
890
 
        self.assertTrue(w1._compatible_parents(set(), set()))
891
 
        # other cannot contain stuff my_parents does not
892
 
        self.assertFalse(w1._compatible_parents(set(), set([1])))
893
 
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
894
 
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
895
 
 
896
 
 
897
 
class TestWeaveFile(TestCaseInTempDir):
898
 
    
899
 
    def test_empty_file(self):
900
 
        f = open('empty.weave', 'wb+')
901
 
        try:
902
 
            self.assertRaises(errors.WeaveFormatError,
903
 
                              read_weave, f)
904
 
        finally:
905
 
            f.close()
 
868
        wb.add('x1', [], ['line from x1\n'])
 
869
        wb.add('v1', [], ['hello\n'])
 
870
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
 
871
        wa.join(wb)
 
872
        eq = self.assertEquals
 
873
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
874
        eq(wa.get_text('x1'), 'line from x1\n')
 
875
 
 
876
    def test_join_with_ghosts(self):
 
877
        """Join that inserts parents of an existing revision.
 
878
 
 
879
        This can happen when merging from another branch who
 
880
        knows about revisions the destination does not.  In 
 
881
        this test the second weave knows of an additional parent of 
 
882
        v2.  Any revisions which are in common still have to have the 
 
883
        same text."""
 
884
        return ###############################
 
885
        wa = self.weave1.copy()
 
886
        wb = Weave()
 
887
        wb.add('x1', [], ['line from x1\n'])
 
888
        wb.add('v1', [], ['hello\n'])
 
889
        wb.add('v2', ['v1', 'x1'], ['hello\n', 'world\n'])
 
890
        wa.join(wb)
 
891
        eq = self.assertEquals
 
892
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
 
893
        eq(wa.get_text('x1'), 'line from x1\n')
 
894
 
 
895
 
 
896
if __name__ == '__main__':
 
897
    import sys
 
898
    import unittest
 
899
    sys.exit(unittest.main())
 
900