~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Aaron Bentley
  • Date: 2007-03-12 19:56:41 UTC
  • mto: (1551.19.24 Aaron's mergeable stuff)
  • mto: This revision was merged to the branch mainline in revision 2353.
  • Revision ID: abentley@panoramicfeedback.com-20070312195641-ezjnseqwgjtkh0iu
merge3 auto-detects line endings for conflict markers

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#! /usr/bin/python2.4
2
2
 
3
 
# Copyright (C) 2005 by Canonical Ltd
4
 
 
 
3
# Copyright (C) 2005 Canonical Ltd
 
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
 
 
22
 
 
 
21
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
 
22
#       if it fails.
23
23
 
24
24
"""test suite for weave algorithm"""
25
25
 
26
26
from pprint import pformat
27
27
 
28
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
 
28
from bzrlib import (
 
29
    errors,
 
30
    )
 
31
from bzrlib.osutils import sha_string
 
32
from bzrlib.tests import TestCase, TestCaseInTempDir
 
33
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
29
34
from bzrlib.weavefile import write_weave, read_weave
30
 
from bzrlib.selftest import TestCase
31
 
from bzrlib.osutils import sha_string
32
35
 
33
36
 
34
37
# texts for use in testing
37
40
          "A second line"]
38
41
 
39
42
 
40
 
 
41
43
class TestBase(TestCase):
42
44
    def check_read_write(self, k):
43
45
        """Check the weave k can be written & re-read."""
58
60
            self.log('         %r' % k._parents)
59
61
            self.log('         %r' % k2._parents)
60
62
            self.log('')
61
 
 
62
 
            
63
63
            self.fail('read/write check failed')
64
 
        
65
 
        
 
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)
66
73
 
67
74
 
68
75
class Easy(TestBase):
72
79
 
73
80
class StoreText(TestBase):
74
81
    """Store and retrieve a simple text."""
75
 
    def runTest(self):
 
82
 
 
83
    def test_storing_text(self):
76
84
        k = Weave()
77
 
        idx = k.add('text0', [], TEXT_0)
78
 
        self.assertEqual(k.get(idx), TEXT_0)
 
85
        idx = k.add_lines('text0', [], TEXT_0)
 
86
        self.assertEqual(k.get_lines(idx), TEXT_0)
79
87
        self.assertEqual(idx, 0)
80
88
 
81
89
 
82
 
 
83
90
class AnnotateOne(TestBase):
84
91
    def runTest(self):
85
92
        k = Weave()
86
 
        k.add('text0', [], TEXT_0)
87
 
        self.assertEqual(k.annotate(0),
88
 
                         [(0, TEXT_0[0])])
 
93
        k.add_lines('text0', [], TEXT_0)
 
94
        self.assertEqual(k.annotate('text0'),
 
95
                         [('text0', TEXT_0[0])])
89
96
 
90
97
 
91
98
class StoreTwo(TestBase):
92
99
    def runTest(self):
93
100
        k = Weave()
94
101
 
95
 
        idx = k.add('text0', [], TEXT_0)
 
102
        idx = k.add_lines('text0', [], TEXT_0)
96
103
        self.assertEqual(idx, 0)
97
104
 
98
 
        idx = k.add('text1', [], TEXT_1)
 
105
        idx = k.add_lines('text1', [], TEXT_1)
99
106
        self.assertEqual(idx, 1)
100
107
 
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"""
 
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):
109
114
        k = Weave()
110
 
 
111
 
        t = 'text0'
112
 
        k.add('text0', [], [t], sha1=sha_string(t))
113
 
 
114
 
 
 
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
                        
115
123
 
116
124
class InvalidAdd(TestBase):
117
125
    """Try to use invalid version number during add."""
118
126
    def runTest(self):
119
127
        k = Weave()
120
128
 
121
 
        self.assertRaises(IndexError,
122
 
                          k.add,
 
129
        self.assertRaises(errors.RevisionNotPresent,
 
130
                          k.add_lines,
123
131
                          'text0',
124
 
                          [69],
 
132
                          ['69'],
125
133
                          ['new text!'])
126
134
 
127
135
 
129
137
    """Add the same version twice; harmless."""
130
138
    def runTest(self):
131
139
        k = Weave()
132
 
        idx = k.add('text0', [], TEXT_0)
133
 
        idx2 = k.add('text0', [], TEXT_0)
 
140
        idx = k.add_lines('text0', [], TEXT_0)
 
141
        idx2 = k.add_lines('text0', [], TEXT_0)
134
142
        self.assertEqual(idx, idx2)
135
143
 
136
144
 
137
 
 
138
145
class InvalidRepeatedAdd(TestBase):
139
146
    def runTest(self):
140
147
        k = Weave()
141
 
        idx = k.add('text0', [], TEXT_0)
142
 
        self.assertRaises(WeaveError,
143
 
                          k.add,
 
148
        k.add_lines('basis', [], TEXT_0)
 
149
        idx = k.add_lines('text0', [], TEXT_0)
 
150
        self.assertRaises(errors.RevisionAlreadyPresent,
 
151
                          k.add_lines,
144
152
                          'text0',
145
153
                          [],
146
154
                          ['not the same text'])
147
 
        self.assertRaises(WeaveError,
148
 
                          k.add,
 
155
        self.assertRaises(errors.RevisionAlreadyPresent,
 
156
                          k.add_lines,
149
157
                          'text0',
150
 
                          [12],         # not the right parents
 
158
                          ['basis'],         # not the right parents
151
159
                          TEXT_0)
152
160
        
153
161
 
154
 
 
155
162
class InsertLines(TestBase):
156
163
    """Store a revision that adds one line to the original.
157
164
 
160
167
    def runTest(self):
161
168
        k = Weave()
162
169
 
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),
 
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),
170
177
                         ['line 1',
171
178
                          'line 2'])
172
179
 
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')])
 
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')])
182
189
 
183
190
        text3 = ['line 1', 'middle line', 'line 2']
184
 
        k.add('text3',
185
 
              [0, 1],
 
191
        k.add_lines('text3',
 
192
              ['text0', 'text1'],
186
193
              text3)
187
194
 
188
195
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
189
196
 
190
197
        self.log("k._weave=" + pformat(k._weave))
191
198
 
192
 
        self.assertEqual(k.annotate(3),
193
 
                         [(0, 'line 1'),
194
 
                          (3, 'middle line'),
195
 
                          (1, 'line 2')])
 
199
        self.assertEqual(k.annotate('text3'),
 
200
                         [('text0', 'line 1'),
 
201
                          ('text3', 'middle line'),
 
202
                          ('text1', 'line 2')])
196
203
 
197
204
        # now multiple insertions at different places
198
 
        k.add('text4',
199
 
              [0, 1, 3],
 
205
        k.add_lines('text4',
 
206
              ['text0', 'text1', 'text3'],
200
207
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
201
208
 
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
 
 
 
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')])
210
216
 
211
217
 
212
218
class DeleteLines(TestBase):
218
224
 
219
225
        base_text = ['one', 'two', 'three', 'four']
220
226
 
221
 
        k.add('text0', [], base_text)
 
227
        k.add_lines('text0', [], base_text)
222
228
        
223
229
        texts = [['one', 'two', 'three'],
224
230
                 ['two', 'three', 'four'],
228
234
 
229
235
        i = 1
230
236
        for t in texts:
231
 
            ver = k.add('text%d' % i,
232
 
                        [0], t)
 
237
            ver = k.add_lines('text%d' % i,
 
238
                        ['text0'], t)
233
239
            i += 1
234
240
 
235
241
        self.log('final weave:')
236
242
        self.log('k._weave=' + pformat(k._weave))
237
243
 
238
244
        for i in range(len(texts)):
239
 
            self.assertEqual(k.get(i+1),
 
245
            self.assertEqual(k.get_lines(i+1),
240
246
                             texts[i])
241
 
            
242
 
 
243
247
 
244
248
 
245
249
class SuicideDelete(TestBase):
261
265
        return 
262
266
 
263
267
        self.assertRaises(WeaveFormatError,
264
 
                          k.get,
 
268
                          k.get_lines,
265
269
                          0)        
266
270
 
267
271
 
268
 
 
269
272
class CannedDelete(TestBase):
270
273
    """Unpack canned weave with deleted lines."""
271
274
    def runTest(self):
282
285
                'last line',
283
286
                ('}', 0),
284
287
                ]
 
288
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
289
                  , sha_string('first linelast line')]
285
290
 
286
 
        self.assertEqual(k.get(0),
 
291
        self.assertEqual(k.get_lines(0),
287
292
                         ['first line',
288
293
                          'line to be deleted',
289
294
                          'last line',
290
295
                          ])
291
296
 
292
 
        self.assertEqual(k.get(1),
 
297
        self.assertEqual(k.get_lines(1),
293
298
                         ['first line',
294
299
                          'last line',
295
300
                          ])
296
301
 
297
302
 
298
 
 
299
303
class CannedReplacement(TestBase):
300
304
    """Unpack canned weave with deleted lines."""
301
305
    def runTest(self):
315
319
                'last line',
316
320
                ('}', 0),
317
321
                ]
 
322
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
323
                  , sha_string('first linereplacement linelast line')]
318
324
 
319
 
        self.assertEqual(k.get(0),
 
325
        self.assertEqual(k.get_lines(0),
320
326
                         ['first line',
321
327
                          'line to be deleted',
322
328
                          'last line',
323
329
                          ])
324
330
 
325
 
        self.assertEqual(k.get(1),
 
331
        self.assertEqual(k.get_lines(1),
326
332
                         ['first line',
327
333
                          'replacement line',
328
334
                          'last line',
329
335
                          ])
330
336
 
331
337
 
332
 
 
333
338
class BadWeave(TestBase):
334
339
    """Test that we trap an insert which should not occur."""
335
340
    def runTest(self):
415
420
                '}',
416
421
                ('}', 0)]
417
422
 
418
 
        self.assertEqual(k.get(0),
 
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),
419
430
                         ['foo {',
420
431
                          '}'])
421
432
 
422
 
        self.assertEqual(k.get(1),
 
433
        self.assertEqual(k.get_lines(1),
423
434
                         ['foo {',
424
435
                          '  added in version 1',
425
436
                          '  also from v1',
426
437
                          '}'])
427
438
                       
428
 
        self.assertEqual(k.get(2),
 
439
        self.assertEqual(k.get_lines(2),
429
440
                         ['foo {',
430
441
                          '  added in v2',
431
442
                          '}'])
432
443
 
433
 
        self.assertEqual(k.get(3),
 
444
        self.assertEqual(k.get_lines(3),
434
445
                         ['foo {',
435
446
                          '  added in version 1',
436
447
                          '  added in v2',
438
449
                          '}'])
439
450
                         
440
451
 
441
 
 
442
452
class DeleteLines2(TestBase):
443
453
    """Test recording revisions that delete lines.
444
454
 
447
457
    def runTest(self):
448
458
        k = Weave()
449
459
 
450
 
        k.add('text0', [], ["line the first",
 
460
        k.add_lines('text0', [], ["line the first",
451
461
                   "line 2",
452
462
                   "line 3",
453
463
                   "fine"])
454
464
 
455
 
        self.assertEqual(len(k.get(0)), 4)
 
465
        self.assertEqual(len(k.get_lines(0)), 4)
456
466
 
457
 
        k.add('text1', [0], ["line the first",
 
467
        k.add_lines('text1', ['text0'], ["line the first",
458
468
                   "fine"])
459
469
 
460
 
        self.assertEqual(k.get(1),
 
470
        self.assertEqual(k.get_lines(1),
461
471
                         ["line the first",
462
472
                          "fine"])
463
473
 
464
 
        self.assertEqual(k.annotate(1),
465
 
                         [(0, "line the first"),
466
 
                          (0, "fine")])
467
 
 
 
474
        self.assertEqual(k.annotate('text1'),
 
475
                         [('text0', "line the first"),
 
476
                          ('text0', "fine")])
468
477
 
469
478
 
470
479
class IncludeVersions(TestBase):
488
497
                "second line",
489
498
                ('}', 1)]
490
499
 
491
 
        self.assertEqual(k.get(1),
 
500
        k._sha1s = [sha_string('first line')
 
501
                  , sha_string('first linesecond line')]
 
502
 
 
503
        self.assertEqual(k.get_lines(1),
492
504
                         ["first line",
493
505
                          "second line"])
494
506
 
495
 
        self.assertEqual(k.get(0),
 
507
        self.assertEqual(k.get_lines(0),
496
508
                         ["first line"])
497
509
 
498
510
 
500
512
    """Weave with two diverged texts based on version 0.
501
513
    """
502
514
    def runTest(self):
 
515
        # FIXME make the weave, dont poke at it.
503
516
        k = Weave()
504
517
 
 
518
        k._names = ['0', '1', '2']
 
519
        k._name_map = {'0':0, '1':1, '2':2}
505
520
        k._parents = [frozenset(),
506
521
                frozenset([0]),
507
522
                frozenset([0]),
517
532
                ('}', 2),                
518
533
                ]
519
534
 
520
 
        self.assertEqual(k.get(0),
 
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),
521
540
                         ["first line"])
522
541
 
523
 
        self.assertEqual(k.get(1),
 
542
        self.assertEqual(k.get_lines(1),
524
543
                         ["first line",
525
544
                          "second line"])
526
545
 
527
 
        self.assertEqual(k.get(2),
 
546
        self.assertEqual(k.get_lines('2'),
528
547
                         ["first line",
529
548
                          "alternative second line"])
530
549
 
531
 
        self.assertEqual(list(k.inclusions([2])),
532
 
                         [0, 2])
533
 
 
 
550
        self.assertEqual(list(k.get_ancestry(['2'])),
 
551
                         ['0', '2'])
534
552
 
535
553
 
536
554
class ReplaceLine(TestBase):
540
558
        text0 = ['cheddar', 'stilton', 'gruyere']
541
559
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
542
560
        
543
 
        k.add('text0', [], text0)
544
 
        k.add('text1', [0], text1)
 
561
        k.add_lines('text0', [], text0)
 
562
        k.add_lines('text1', ['text0'], text1)
545
563
 
546
564
        self.log('k._weave=' + pformat(k._weave))
547
565
 
548
 
        self.assertEqual(k.get(0), text0)
549
 
        self.assertEqual(k.get(1), text1)
550
 
 
 
566
        self.assertEqual(k.get_lines(0), text0)
 
567
        self.assertEqual(k.get_lines(1), text1)
551
568
 
552
569
 
553
570
class Merge(TestBase):
561
578
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
562
579
                 ]
563
580
 
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])
 
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])
568
585
 
569
586
        for i, t in enumerate(texts):
570
 
            self.assertEqual(k.get(i), t)
 
587
            self.assertEqual(k.get_lines(i), t)
571
588
 
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'),
 
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'),
578
595
                          ])
579
596
 
580
 
        self.assertEqual(list(k.inclusions([3])),
581
 
                         [0, 1, 2, 3])
 
597
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
598
                         ['text0', 'text1', 'text2', 'merge'])
582
599
 
583
600
        self.log('k._weave=' + pformat(k._weave))
584
601
 
595
612
        return  # NOT RUN
596
613
        k = Weave()
597
614
 
598
 
        k.add([], ['aaa', 'bbb'])
599
 
        k.add([0], ['aaa', '111', 'bbb'])
600
 
        k.add([1], ['aaa', '222', 'bbb'])
 
615
        k.add_lines([], ['aaa', 'bbb'])
 
616
        k.add_lines([0], ['aaa', '111', 'bbb'])
 
617
        k.add_lines([1], ['aaa', '222', 'bbb'])
601
618
 
602
619
        merged = k.merge([1, 2])
603
620
 
606
623
                           [['bbb']]])
607
624
 
608
625
 
609
 
 
610
626
class NonConflict(TestBase):
611
627
    """Two descendants insert compatible changes.
612
628
 
615
631
        return  # NOT RUN
616
632
        k = Weave()
617
633
 
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
 
        
 
634
        k.add_lines([], ['aaa', 'bbb'])
 
635
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
636
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
649
637
 
650
638
 
651
639
class Khayyam(TestBase):
652
640
    """Test changes to multi-line texts, and read/write"""
653
 
    def runTest(self):
 
641
 
 
642
    def test_multi_line_merge(self):
654
643
        rawtexts = [
655
644
            """A Book of Verses underneath the Bough,
656
645
            A Jug of Wine, a Loaf of Bread, -- and Thou
682
671
        parents = set()
683
672
        i = 0
684
673
        for t in texts:
685
 
            ver = k.add('text%d' % i,
 
674
            ver = k.add_lines('text%d' % i,
686
675
                        list(parents), t)
687
 
            parents.add(ver)
 
676
            parents.add('text%d' % i)
688
677
            i += 1
689
678
 
690
679
        self.log("k._weave=" + pformat(k._weave))
691
680
 
692
681
        for i, t in enumerate(texts):
693
 
            self.assertEqual(k.get(i), t)
 
682
            self.assertEqual(k.get_lines(i), t)
694
683
 
695
684
        self.check_read_write(k)
696
685
 
697
686
 
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
 
 
796
687
class JoinWeavesTests(TestBase):
797
688
    def setUp(self):
798
689
        super(JoinWeavesTests, self).setUp()
799
690
        self.weave1 = Weave()
800
691
        self.lines1 = ['hello\n']
801
692
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
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)
 
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)
805
696
        
806
697
    def test_join_empty(self):
807
698
        """Join two empty weaves."""
809
700
        w1 = Weave()
810
701
        w2 = Weave()
811
702
        w1.join(w2)
812
 
        eq(w1.numversions(), 0)
 
703
        eq(len(w1), 0)
813
704
        
814
705
    def test_join_empty_to_nonempty(self):
815
706
        """Join empty weave onto nonempty."""
819
710
    def test_join_unrelated(self):
820
711
        """Join two weaves with no history in common."""
821
712
        wb = Weave()
822
 
        wb.add('b1', [], ['line from b\n'])
 
713
        wb.add_lines('b1', [], ['line from b\n'])
823
714
        w1 = self.weave1
824
715
        w1.join(wb)
825
716
        eq = self.assertEqual
826
717
        eq(len(w1), 4)
827
 
        eq(sorted(list(w1.iter_names())),
 
718
        eq(sorted(w1.versions()),
828
719
           ['b1', 'v1', 'v2', 'v3'])
829
720
 
830
721
    def test_join_related(self):
831
722
        wa = self.weave1.copy()
832
723
        wb = self.weave1.copy()
833
 
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
834
 
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
 
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'])
835
726
        eq = self.assertEquals
836
727
        eq(len(wa), 4)
837
728
        eq(len(wb), 4)
841
732
           ['hello\n', 'pale blue\n', 'world\n'])
842
733
 
843
734
    def test_join_parent_disagreement(self):
844
 
        """Cannot join weaves with different parents for a version."""
 
735
        #join reconciles differening parents into a union.
845
736
        wa = Weave()
846
737
        wb = Weave()
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)
 
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'))
852
743
 
853
744
    def test_join_text_disagreement(self):
854
745
        """Cannot join weaves with different texts for a version."""
855
746
        wa = Weave()
856
747
        wb = Weave()
857
 
        wa.add('v1', [], ['hello\n'])
858
 
        wb.add('v1', [], ['not\n', 'hello\n'])
 
748
        wa.add_lines('v1', [], ['hello\n'])
 
749
        wb.add_lines('v1', [], ['not\n', 'hello\n'])
859
750
        self.assertRaises(WeaveError,
860
751
                          wa.join, wb)
861
752
 
865
756
        The source weave contains a different version at index 0."""
866
757
        wa = self.weave1.copy()
867
758
        wb = Weave()
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
 
    
 
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()