~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-11-09 18:19:47 UTC
  • mfrom: (5524.2.1 noshfolder)
  • Revision ID: pqm@pqm.ubuntu.com-20101109181947-h26505clmkdhh2uz
(GaryvdM) Exclude SHFOLDER.dll from py2exe builds,
        as it breaks subvertpy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#! /usr/bin/python2.4
2
 
 
3
 
# Copyright (C) 2005 by Canonical Ltd
4
 
 
 
1
# Copyright (C) 2005 Canonical Ltd
 
2
#
5
3
# This program is free software; you can redistribute it and/or modify
6
4
# it under the terms of the GNU General Public License as published by
7
5
# the Free Software Foundation; either version 2 of the License, or
8
6
# (at your option) any later version.
9
 
 
 
7
#
10
8
# This program is distributed in the hope that it will be useful,
11
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
11
# GNU General Public License for more details.
14
 
 
 
12
#
15
13
# You should have received a copy of the GNU General Public License
16
14
# along with this program; if not, write to the Free Software
17
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
16
 
19
17
 
20
18
# TODO: tests regarding version names
21
 
# TODO: rbc 20050108 test that join does not leave an inconsistent weave 
 
19
# TODO: rbc 20050108 test that join does not leave an inconsistent weave
22
20
#       if it fails.
23
21
 
24
22
"""test suite for weave algorithm"""
25
23
 
26
24
from pprint import pformat
27
25
 
28
 
import bzrlib.errors as errors
29
 
from bzrlib.weave import Weave, WeaveFormatError, WeaveError, reweave
 
26
from bzrlib import (
 
27
    errors,
 
28
    )
 
29
from bzrlib.osutils import sha_string
 
30
from bzrlib.tests import TestCase, TestCaseInTempDir
 
31
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
30
32
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.tests import TestCase
32
 
from bzrlib.osutils import sha_string
33
33
 
34
34
 
35
35
# texts for use in testing
38
38
          "A second line"]
39
39
 
40
40
 
41
 
 
42
41
class TestBase(TestCase):
 
42
 
43
43
    def check_read_write(self, k):
44
44
        """Check the weave k can be written & re-read."""
45
45
        from tempfile import TemporaryFile
65
65
class WeaveContains(TestBase):
66
66
    """Weave __contains__ operator"""
67
67
    def runTest(self):
68
 
        k = Weave()
 
68
        k = Weave(get_scope=lambda:None)
69
69
        self.assertFalse('foo' in k)
70
 
        k.add('foo', [], TEXT_1)
 
70
        k.add_lines('foo', [], TEXT_1)
71
71
        self.assertTrue('foo' in k)
72
72
 
73
73
 
76
76
        k = Weave()
77
77
 
78
78
 
79
 
class StoreText(TestBase):
80
 
    """Store and retrieve a simple text."""
81
 
    def runTest(self):
82
 
        k = Weave()
83
 
        idx = k.add('text0', [], TEXT_0)
84
 
        self.assertEqual(k.get(idx), TEXT_0)
85
 
        self.assertEqual(idx, 0)
86
 
 
87
 
 
88
 
 
89
79
class AnnotateOne(TestBase):
90
80
    def runTest(self):
91
81
        k = Weave()
92
 
        k.add('text0', [], TEXT_0)
93
 
        self.assertEqual(k.annotate(0),
94
 
                         [(0, TEXT_0[0])])
95
 
 
96
 
 
97
 
class StoreTwo(TestBase):
98
 
    def runTest(self):
99
 
        k = Weave()
100
 
 
101
 
        idx = k.add('text0', [], TEXT_0)
102
 
        self.assertEqual(idx, 0)
103
 
 
104
 
        idx = k.add('text1', [], TEXT_1)
105
 
        self.assertEqual(idx, 1)
106
 
 
107
 
        self.assertEqual(k.get(0), TEXT_0)
108
 
        self.assertEqual(k.get(1), TEXT_1)
109
 
 
110
 
 
111
 
 
112
 
class AddWithGivenSha(TestBase):
113
 
    def runTest(self):
114
 
        """Add with caller-supplied SHA-1"""
115
 
        k = Weave()
116
 
 
117
 
        t = 'text0'
118
 
        k.add('text0', [], [t], sha1=sha_string(t))
119
 
 
 
82
        k.add_lines('text0', [], TEXT_0)
 
83
        self.assertEqual(k.annotate('text0'),
 
84
                         [('text0', TEXT_0[0])])
120
85
 
121
86
 
122
87
class InvalidAdd(TestBase):
124
89
    def runTest(self):
125
90
        k = Weave()
126
91
 
127
 
        self.assertRaises(IndexError,
128
 
                          k.add,
 
92
        self.assertRaises(errors.RevisionNotPresent,
 
93
                          k.add_lines,
129
94
                          'text0',
130
 
                          [69],
 
95
                          ['69'],
131
96
                          ['new text!'])
132
97
 
133
98
 
134
99
class RepeatedAdd(TestBase):
135
100
    """Add the same version twice; harmless."""
136
 
    def runTest(self):
 
101
 
 
102
    def test_duplicate_add(self):
137
103
        k = Weave()
138
 
        idx = k.add('text0', [], TEXT_0)
139
 
        idx2 = k.add('text0', [], TEXT_0)
 
104
        idx = k.add_lines('text0', [], TEXT_0)
 
105
        idx2 = k.add_lines('text0', [], TEXT_0)
140
106
        self.assertEqual(idx, idx2)
141
107
 
142
108
 
143
 
 
144
109
class InvalidRepeatedAdd(TestBase):
145
110
    def runTest(self):
146
111
        k = Weave()
147
 
        idx = k.add('text0', [], TEXT_0)
148
 
        self.assertRaises(WeaveError,
149
 
                          k.add,
 
112
        k.add_lines('basis', [], TEXT_0)
 
113
        idx = k.add_lines('text0', [], TEXT_0)
 
114
        self.assertRaises(errors.RevisionAlreadyPresent,
 
115
                          k.add_lines,
150
116
                          'text0',
151
117
                          [],
152
118
                          ['not the same text'])
153
 
        self.assertRaises(WeaveError,
154
 
                          k.add,
 
119
        self.assertRaises(errors.RevisionAlreadyPresent,
 
120
                          k.add_lines,
155
121
                          'text0',
156
 
                          [12],         # not the right parents
 
122
                          ['basis'],         # not the right parents
157
123
                          TEXT_0)
158
 
        
159
124
 
160
125
 
161
126
class InsertLines(TestBase):
166
131
    def runTest(self):
167
132
        k = Weave()
168
133
 
169
 
        k.add('text0', [], ['line 1'])
170
 
        k.add('text1', [0], ['line 1', 'line 2'])
171
 
 
172
 
        self.assertEqual(k.annotate(0),
173
 
                         [(0, 'line 1')])
174
 
 
175
 
        self.assertEqual(k.get(1),
 
134
        k.add_lines('text0', [], ['line 1'])
 
135
        k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
 
136
 
 
137
        self.assertEqual(k.annotate('text0'),
 
138
                         [('text0', 'line 1')])
 
139
 
 
140
        self.assertEqual(k.get_lines(1),
176
141
                         ['line 1',
177
142
                          'line 2'])
178
143
 
179
 
        self.assertEqual(k.annotate(1),
180
 
                         [(0, 'line 1'),
181
 
                          (1, 'line 2')])
182
 
 
183
 
        k.add('text2', [0], ['line 1', 'diverged line'])
184
 
 
185
 
        self.assertEqual(k.annotate(2),
186
 
                         [(0, 'line 1'),
187
 
                          (2, 'diverged line')])
 
144
        self.assertEqual(k.annotate('text1'),
 
145
                         [('text0', 'line 1'),
 
146
                          ('text1', 'line 2')])
 
147
 
 
148
        k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
 
149
 
 
150
        self.assertEqual(k.annotate('text2'),
 
151
                         [('text0', 'line 1'),
 
152
                          ('text2', 'diverged line')])
188
153
 
189
154
        text3 = ['line 1', 'middle line', 'line 2']
190
 
        k.add('text3',
191
 
              [0, 1],
 
155
        k.add_lines('text3',
 
156
              ['text0', 'text1'],
192
157
              text3)
193
158
 
194
159
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
195
160
 
196
161
        self.log("k._weave=" + pformat(k._weave))
197
162
 
198
 
        self.assertEqual(k.annotate(3),
199
 
                         [(0, 'line 1'),
200
 
                          (3, 'middle line'),
201
 
                          (1, 'line 2')])
 
163
        self.assertEqual(k.annotate('text3'),
 
164
                         [('text0', 'line 1'),
 
165
                          ('text3', 'middle line'),
 
166
                          ('text1', 'line 2')])
202
167
 
203
168
        # now multiple insertions at different places
204
 
        k.add('text4',
205
 
              [0, 1, 3],
 
169
        k.add_lines('text4',
 
170
              ['text0', 'text1', 'text3'],
206
171
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
207
172
 
208
 
        self.assertEqual(k.annotate(4), 
209
 
                         [(0, 'line 1'),
210
 
                          (4, 'aaa'),
211
 
                          (3, 'middle line'),
212
 
                          (4, 'bbb'),
213
 
                          (1, 'line 2'),
214
 
                          (4, 'ccc')])
215
 
 
 
173
        self.assertEqual(k.annotate('text4'),
 
174
                         [('text0', 'line 1'),
 
175
                          ('text4', 'aaa'),
 
176
                          ('text3', 'middle line'),
 
177
                          ('text4', 'bbb'),
 
178
                          ('text1', 'line 2'),
 
179
                          ('text4', 'ccc')])
216
180
 
217
181
 
218
182
class DeleteLines(TestBase):
224
188
 
225
189
        base_text = ['one', 'two', 'three', 'four']
226
190
 
227
 
        k.add('text0', [], base_text)
228
 
        
 
191
        k.add_lines('text0', [], base_text)
 
192
 
229
193
        texts = [['one', 'two', 'three'],
230
194
                 ['two', 'three', 'four'],
231
195
                 ['one', 'four'],
234
198
 
235
199
        i = 1
236
200
        for t in texts:
237
 
            ver = k.add('text%d' % i,
238
 
                        [0], t)
 
201
            ver = k.add_lines('text%d' % i,
 
202
                        ['text0'], t)
239
203
            i += 1
240
204
 
241
205
        self.log('final weave:')
242
206
        self.log('k._weave=' + pformat(k._weave))
243
207
 
244
208
        for i in range(len(texts)):
245
 
            self.assertEqual(k.get(i+1),
 
209
            self.assertEqual(k.get_lines(i+1),
246
210
                             texts[i])
247
 
            
248
 
 
249
211
 
250
212
 
251
213
class SuicideDelete(TestBase):
264
226
                ]
265
227
        ################################### SKIPPED
266
228
        # Weave.get doesn't trap this anymore
267
 
        return 
 
229
        return
268
230
 
269
231
        self.assertRaises(WeaveFormatError,
270
 
                          k.get,
271
 
                          0)        
272
 
 
 
232
                          k.get_lines,
 
233
                          0)
273
234
 
274
235
 
275
236
class CannedDelete(TestBase):
288
249
                'last line',
289
250
                ('}', 0),
290
251
                ]
 
252
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
253
                  , sha_string('first linelast line')]
291
254
 
292
 
        self.assertEqual(k.get(0),
 
255
        self.assertEqual(k.get_lines(0),
293
256
                         ['first line',
294
257
                          'line to be deleted',
295
258
                          'last line',
296
259
                          ])
297
260
 
298
 
        self.assertEqual(k.get(1),
 
261
        self.assertEqual(k.get_lines(1),
299
262
                         ['first line',
300
263
                          'last line',
301
264
                          ])
302
265
 
303
266
 
304
 
 
305
267
class CannedReplacement(TestBase):
306
268
    """Unpack canned weave with deleted lines."""
307
269
    def runTest(self):
316
278
                'line to be deleted',
317
279
                (']', 1),
318
280
                ('{', 1),
319
 
                'replacement line',                
 
281
                'replacement line',
320
282
                ('}', 1),
321
283
                'last line',
322
284
                ('}', 0),
323
285
                ]
 
286
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
287
                  , sha_string('first linereplacement linelast line')]
324
288
 
325
 
        self.assertEqual(k.get(0),
 
289
        self.assertEqual(k.get_lines(0),
326
290
                         ['first line',
327
291
                          'line to be deleted',
328
292
                          'last line',
329
293
                          ])
330
294
 
331
 
        self.assertEqual(k.get(1),
 
295
        self.assertEqual(k.get_lines(1),
332
296
                         ['first line',
333
297
                          'replacement line',
334
298
                          'last line',
335
299
                          ])
336
300
 
337
301
 
338
 
 
339
302
class BadWeave(TestBase):
340
303
    """Test that we trap an insert which should not occur."""
341
304
    def runTest(self):
358
321
 
359
322
        ################################### SKIPPED
360
323
        # Weave.get doesn't trap this anymore
361
 
        return 
 
324
        return
362
325
 
363
326
 
364
327
        self.assertRaises(WeaveFormatError,
421
384
                '}',
422
385
                ('}', 0)]
423
386
 
424
 
        self.assertEqual(k.get(0),
425
 
                         ['foo {',
426
 
                          '}'])
427
 
 
428
 
        self.assertEqual(k.get(1),
429
 
                         ['foo {',
430
 
                          '  added in version 1',
431
 
                          '  also from v1',
432
 
                          '}'])
433
 
                       
434
 
        self.assertEqual(k.get(2),
435
 
                         ['foo {',
436
 
                          '  added in v2',
437
 
                          '}'])
438
 
 
439
 
        self.assertEqual(k.get(3),
440
 
                         ['foo {',
441
 
                          '  added in version 1',
442
 
                          '  added in v2',
443
 
                          '  also from v1',
444
 
                          '}'])
445
 
                         
 
387
        k._sha1s = [sha_string('foo {}')
 
388
                  , sha_string('foo {  added in version 1  also from v1}')
 
389
                  , sha_string('foo {  added in v2}')
 
390
                  , sha_string('foo {  added in version 1  added in v2  also from v1}')
 
391
                  ]
 
392
 
 
393
        self.assertEqual(k.get_lines(0),
 
394
                         ['foo {',
 
395
                          '}'])
 
396
 
 
397
        self.assertEqual(k.get_lines(1),
 
398
                         ['foo {',
 
399
                          '  added in version 1',
 
400
                          '  also from v1',
 
401
                          '}'])
 
402
 
 
403
        self.assertEqual(k.get_lines(2),
 
404
                         ['foo {',
 
405
                          '  added in v2',
 
406
                          '}'])
 
407
 
 
408
        self.assertEqual(k.get_lines(3),
 
409
                         ['foo {',
 
410
                          '  added in version 1',
 
411
                          '  added in v2',
 
412
                          '  also from v1',
 
413
                          '}'])
446
414
 
447
415
 
448
416
class DeleteLines2(TestBase):
453
421
    def runTest(self):
454
422
        k = Weave()
455
423
 
456
 
        k.add('text0', [], ["line the first",
 
424
        k.add_lines('text0', [], ["line the first",
457
425
                   "line 2",
458
426
                   "line 3",
459
427
                   "fine"])
460
428
 
461
 
        self.assertEqual(len(k.get(0)), 4)
 
429
        self.assertEqual(len(k.get_lines(0)), 4)
462
430
 
463
 
        k.add('text1', [0], ["line the first",
 
431
        k.add_lines('text1', ['text0'], ["line the first",
464
432
                   "fine"])
465
433
 
466
 
        self.assertEqual(k.get(1),
 
434
        self.assertEqual(k.get_lines(1),
467
435
                         ["line the first",
468
436
                          "fine"])
469
437
 
470
 
        self.assertEqual(k.annotate(1),
471
 
                         [(0, "line the first"),
472
 
                          (0, "fine")])
473
 
 
 
438
        self.assertEqual(k.annotate('text1'),
 
439
                         [('text0', "line the first"),
 
440
                          ('text0', "fine")])
474
441
 
475
442
 
476
443
class IncludeVersions(TestBase):
494
461
                "second line",
495
462
                ('}', 1)]
496
463
 
497
 
        self.assertEqual(k.get(1),
 
464
        k._sha1s = [sha_string('first line')
 
465
                  , sha_string('first linesecond line')]
 
466
 
 
467
        self.assertEqual(k.get_lines(1),
498
468
                         ["first line",
499
469
                          "second line"])
500
470
 
501
 
        self.assertEqual(k.get(0),
 
471
        self.assertEqual(k.get_lines(0),
502
472
                         ["first line"])
503
473
 
504
474
 
506
476
    """Weave with two diverged texts based on version 0.
507
477
    """
508
478
    def runTest(self):
 
479
        # FIXME make the weave, dont poke at it.
509
480
        k = Weave()
510
481
 
 
482
        k._names = ['0', '1', '2']
 
483
        k._name_map = {'0':0, '1':1, '2':2}
511
484
        k._parents = [frozenset(),
512
485
                frozenset([0]),
513
486
                frozenset([0]),
520
493
                ('}', 1),
521
494
                ('{', 2),
522
495
                "alternative second line",
523
 
                ('}', 2),                
 
496
                ('}', 2),
524
497
                ]
525
498
 
526
 
        self.assertEqual(k.get(0),
 
499
        k._sha1s = [sha_string('first line')
 
500
                  , sha_string('first linesecond line')
 
501
                  , sha_string('first linealternative second line')]
 
502
 
 
503
        self.assertEqual(k.get_lines(0),
527
504
                         ["first line"])
528
505
 
529
 
        self.assertEqual(k.get(1),
 
506
        self.assertEqual(k.get_lines(1),
530
507
                         ["first line",
531
508
                          "second line"])
532
509
 
533
 
        self.assertEqual(k.get(2),
 
510
        self.assertEqual(k.get_lines('2'),
534
511
                         ["first line",
535
512
                          "alternative second line"])
536
513
 
537
 
        self.assertEqual(list(k.inclusions([2])),
538
 
                         [0, 2])
539
 
 
 
514
        self.assertEqual(list(k.get_ancestry(['2'])),
 
515
                         ['0', '2'])
540
516
 
541
517
 
542
518
class ReplaceLine(TestBase):
545
521
 
546
522
        text0 = ['cheddar', 'stilton', 'gruyere']
547
523
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
548
 
        
549
 
        k.add('text0', [], text0)
550
 
        k.add('text1', [0], text1)
 
524
 
 
525
        k.add_lines('text0', [], text0)
 
526
        k.add_lines('text1', ['text0'], text1)
551
527
 
552
528
        self.log('k._weave=' + pformat(k._weave))
553
529
 
554
 
        self.assertEqual(k.get(0), text0)
555
 
        self.assertEqual(k.get(1), text1)
556
 
 
 
530
        self.assertEqual(k.get_lines(0), text0)
 
531
        self.assertEqual(k.get_lines(1), text1)
557
532
 
558
533
 
559
534
class Merge(TestBase):
567
542
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
568
543
                 ]
569
544
 
570
 
        k.add('text0', [], texts[0])
571
 
        k.add('text1', [0], texts[1])
572
 
        k.add('text2', [0], texts[2])
573
 
        k.add('merge', [0, 1, 2], texts[3])
 
545
        k.add_lines('text0', [], texts[0])
 
546
        k.add_lines('text1', ['text0'], texts[1])
 
547
        k.add_lines('text2', ['text0'], texts[2])
 
548
        k.add_lines('merge', ['text0', 'text1', 'text2'], texts[3])
574
549
 
575
550
        for i, t in enumerate(texts):
576
 
            self.assertEqual(k.get(i), t)
 
551
            self.assertEqual(k.get_lines(i), t)
577
552
 
578
 
        self.assertEqual(k.annotate(3),
579
 
                         [(0, 'header'),
580
 
                          (1, ''),
581
 
                          (1, 'line from 1'),
582
 
                          (3, 'fixup line'),
583
 
                          (2, 'line from 2'),
 
553
        self.assertEqual(k.annotate('merge'),
 
554
                         [('text0', 'header'),
 
555
                          ('text1', ''),
 
556
                          ('text1', 'line from 1'),
 
557
                          ('merge', 'fixup line'),
 
558
                          ('text2', 'line from 2'),
584
559
                          ])
585
560
 
586
 
        self.assertEqual(list(k.inclusions([3])),
587
 
                         [0, 1, 2, 3])
 
561
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
562
                         ['text0', 'text1', 'text2', 'merge'])
588
563
 
589
564
        self.log('k._weave=' + pformat(k._weave))
590
565
 
601
576
        return  # NOT RUN
602
577
        k = Weave()
603
578
 
604
 
        k.add([], ['aaa', 'bbb'])
605
 
        k.add([0], ['aaa', '111', 'bbb'])
606
 
        k.add([1], ['aaa', '222', 'bbb'])
 
579
        k.add_lines([], ['aaa', 'bbb'])
 
580
        k.add_lines([0], ['aaa', '111', 'bbb'])
 
581
        k.add_lines([1], ['aaa', '222', 'bbb'])
607
582
 
608
583
        merged = k.merge([1, 2])
609
584
 
612
587
                           [['bbb']]])
613
588
 
614
589
 
615
 
 
616
590
class NonConflict(TestBase):
617
591
    """Two descendants insert compatible changes.
618
592
 
621
595
        return  # NOT RUN
622
596
        k = Weave()
623
597
 
624
 
        k.add([], ['aaa', 'bbb'])
625
 
        k.add([0], ['111', 'aaa', 'ccc', 'bbb'])
626
 
        k.add([1], ['aaa', 'ccc', 'bbb', '222'])
627
 
 
628
 
    
629
 
    
630
 
 
631
 
 
632
 
class AutoMerge(TestBase):
633
 
    def runTest(self):
634
 
        k = Weave()
635
 
 
636
 
        texts = [['header', 'aaa', 'bbb'],
637
 
                 ['header', 'aaa', 'line from 1', 'bbb'],
638
 
                 ['header', 'aaa', 'bbb', 'line from 2', 'more from 2'],
639
 
                 ]
640
 
 
641
 
        k.add('text0', [], texts[0])
642
 
        k.add('text1', [0], texts[1])
643
 
        k.add('text2', [0], texts[2])
644
 
 
645
 
        self.log('k._weave=' + pformat(k._weave))
646
 
 
647
 
        m = list(k.mash_iter([0, 1, 2]))
648
 
 
649
 
        self.assertEqual(m,
650
 
                         ['header', 'aaa',
651
 
                          'line from 1',
652
 
                          'bbb',
653
 
                          'line from 2', 'more from 2'])
654
 
        
 
598
        k.add_lines([], ['aaa', 'bbb'])
 
599
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
600
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
655
601
 
656
602
 
657
603
class Khayyam(TestBase):
658
604
    """Test changes to multi-line texts, and read/write"""
659
 
    def runTest(self):
 
605
 
 
606
    def test_multi_line_merge(self):
660
607
        rawtexts = [
661
608
            """A Book of Verses underneath the Bough,
662
609
            A Jug of Wine, a Loaf of Bread, -- and Thou
663
610
            Beside me singing in the Wilderness --
664
611
            Oh, Wilderness were Paradise enow!""",
665
 
            
 
612
 
666
613
            """A Book of Verses underneath the Bough,
667
614
            A Jug of Wine, a Loaf of Bread, -- and Thou
668
615
            Beside me singing in the Wilderness --
688
635
        parents = set()
689
636
        i = 0
690
637
        for t in texts:
691
 
            ver = k.add('text%d' % i,
 
638
            ver = k.add_lines('text%d' % i,
692
639
                        list(parents), t)
693
 
            parents.add(ver)
 
640
            parents.add('text%d' % i)
694
641
            i += 1
695
642
 
696
643
        self.log("k._weave=" + pformat(k._weave))
697
644
 
698
645
        for i, t in enumerate(texts):
699
 
            self.assertEqual(k.get(i), t)
 
646
            self.assertEqual(k.get_lines(i), t)
700
647
 
701
648
        self.check_read_write(k)
702
649
 
703
650
 
704
 
 
705
 
class MergeCases(TestBase):
706
 
    def doMerge(self, base, a, b, mp):
707
 
        from cStringIO import StringIO
708
 
        from textwrap import dedent
709
 
 
710
 
        def addcrlf(x):
711
 
            return x + '\n'
712
 
        
713
 
        w = Weave()
714
 
        w.add('text0', [], map(addcrlf, base))
715
 
        w.add('text1', [0], map(addcrlf, a))
716
 
        w.add('text2', [0], map(addcrlf, b))
717
 
 
718
 
        self.log('weave is:')
719
 
        tmpf = StringIO()
720
 
        write_weave(w, tmpf)
721
 
        self.log(tmpf.getvalue())
722
 
 
723
 
        self.log('merge plan:')
724
 
        p = list(w.plan_merge(1, 2))
725
 
        for state, line in p:
726
 
            if line:
727
 
                self.log('%12s | %s' % (state, line[:-1]))
728
 
 
729
 
        self.log('merge:')
730
 
        mt = StringIO()
731
 
        mt.writelines(w.weave_merge(p))
732
 
        mt.seek(0)
733
 
        self.log(mt.getvalue())
734
 
 
735
 
        mp = map(addcrlf, mp)
736
 
        self.assertEqual(mt.readlines(), mp)
737
 
        
738
 
        
739
 
    def testOneInsert(self):
740
 
        self.doMerge([],
741
 
                     ['aa'],
742
 
                     [],
743
 
                     ['aa'])
744
 
 
745
 
    def testSeparateInserts(self):
746
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
747
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
748
 
                     ['aaa', 'bbb', 'yyy', 'ccc'],
749
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
750
 
 
751
 
    def testSameInsert(self):
752
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
753
 
                     ['aaa', 'xxx', 'bbb', 'ccc'],
754
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
755
 
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
756
 
 
757
 
    def testOverlappedInsert(self):
758
 
        self.doMerge(['aaa', 'bbb'],
759
 
                     ['aaa', 'xxx', 'yyy', 'bbb'],
760
 
                     ['aaa', 'xxx', 'bbb'],
761
 
                     ['aaa', '<<<<<<<', 'xxx', 'yyy', '=======', 'xxx', 
762
 
                      '>>>>>>>', 'bbb'])
763
 
 
764
 
        # really it ought to reduce this to 
765
 
        # ['aaa', 'xxx', 'yyy', 'bbb']
766
 
 
767
 
 
768
 
    def testClashReplace(self):
769
 
        self.doMerge(['aaa'],
770
 
                     ['xxx'],
771
 
                     ['yyy', 'zzz'],
772
 
                     ['<<<<<<<', 'xxx', '=======', 'yyy', 'zzz', 
773
 
                      '>>>>>>>'])
774
 
 
775
 
    def testNonClashInsert(self):
776
 
        self.doMerge(['aaa'],
777
 
                     ['xxx', 'aaa'],
778
 
                     ['yyy', 'zzz'],
779
 
                     ['<<<<<<<', 'xxx', 'aaa', '=======', 'yyy', 'zzz', 
780
 
                      '>>>>>>>'])
781
 
 
782
 
        self.doMerge(['aaa'],
783
 
                     ['aaa'],
784
 
                     ['yyy', 'zzz'],
785
 
                     ['yyy', 'zzz'])
786
 
 
787
 
 
788
 
    def testDeleteAndModify(self):
789
 
        """Clashing delete and modification.
790
 
 
791
 
        If one side modifies a region and the other deletes it then
792
 
        there should be a conflict with one side blank.
793
 
        """
794
 
 
795
 
        #######################################
796
 
        # skippd, not working yet
797
 
        return
798
 
        
799
 
        self.doMerge(['aaa', 'bbb', 'ccc'],
800
 
                     ['aaa', 'ddd', 'ccc'],
801
 
                     ['aaa', 'ccc'],
802
 
                     ['<<<<<<<<', 'aaa', '=======', '>>>>>>>', 'ccc'])
803
 
 
804
 
 
805
651
class JoinWeavesTests(TestBase):
806
652
    def setUp(self):
807
653
        super(JoinWeavesTests, self).setUp()
808
654
        self.weave1 = Weave()
809
655
        self.lines1 = ['hello\n']
810
656
        self.lines3 = ['hello\n', 'cruel\n', 'world\n']
811
 
        self.weave1.add('v1', [], self.lines1)
812
 
        self.weave1.add('v2', [0], ['hello\n', 'world\n'])
813
 
        self.weave1.add('v3', [1], self.lines3)
814
 
        
815
 
    def test_join_empty(self):
816
 
        """Join two empty weaves."""
817
 
        eq = self.assertEqual
818
 
        w1 = Weave()
819
 
        w2 = Weave()
820
 
        w1.join(w2)
821
 
        eq(w1.numversions(), 0)
822
 
        
823
 
    def test_join_empty_to_nonempty(self):
824
 
        """Join empty weave onto nonempty."""
825
 
        self.weave1.join(Weave())
826
 
        self.assertEqual(len(self.weave1), 3)
827
 
 
828
 
    def test_join_unrelated(self):
829
 
        """Join two weaves with no history in common."""
830
 
        wb = Weave()
831
 
        wb.add('b1', [], ['line from b\n'])
832
 
        w1 = self.weave1
833
 
        w1.join(wb)
834
 
        eq = self.assertEqual
835
 
        eq(len(w1), 4)
836
 
        eq(sorted(list(w1.iter_names())),
837
 
           ['b1', 'v1', 'v2', 'v3'])
838
 
 
839
 
    def test_join_related(self):
840
 
        wa = self.weave1.copy()
841
 
        wb = self.weave1.copy()
842
 
        wa.add('a1', ['v3'], ['hello\n', 'sweet\n', 'world\n'])
843
 
        wb.add('b1', ['v3'], ['hello\n', 'pale blue\n', 'world\n'])
844
 
        eq = self.assertEquals
845
 
        eq(len(wa), 4)
846
 
        eq(len(wb), 4)
847
 
        wa.join(wb)
848
 
        eq(len(wa), 5)
849
 
        eq(wa.get_lines('b1'),
850
 
           ['hello\n', 'pale blue\n', 'world\n'])
851
 
 
852
 
    def test_join_parent_disagreement(self):
853
 
        """Cannot join weaves with different parents for a version."""
854
 
        wa = Weave()
855
 
        wb = Weave()
856
 
        wa.add('v1', [], ['hello\n'])
857
 
        wb.add('v0', [], [])
858
 
        wb.add('v1', ['v0'], ['hello\n'])
859
 
        self.assertRaises(WeaveError,
860
 
                          wa.join, wb)
861
 
 
862
 
    def test_join_text_disagreement(self):
863
 
        """Cannot join weaves with different texts for a version."""
864
 
        wa = Weave()
865
 
        wb = Weave()
866
 
        wa.add('v1', [], ['hello\n'])
867
 
        wb.add('v1', [], ['not\n', 'hello\n'])
868
 
        self.assertRaises(WeaveError,
869
 
                          wa.join, wb)
870
 
 
871
 
    def test_join_unordered(self):
872
 
        """Join weaves where indexes differ.
873
 
        
874
 
        The source weave contains a different version at index 0."""
875
 
        wa = self.weave1.copy()
876
 
        wb = Weave()
877
 
        wb.add('x1', [], ['line from x1\n'])
878
 
        wb.add('v1', [], ['hello\n'])
879
 
        wb.add('v2', ['v1'], ['hello\n', 'world\n'])
880
 
        wa.join(wb)
881
 
        eq = self.assertEquals
882
 
        eq(sorted(wa.iter_names()), ['v1', 'v2', 'v3', 'x1',])
883
 
        eq(wa.get_text('x1'), 'line from x1\n')
 
657
        self.weave1.add_lines('v1', [], self.lines1)
 
658
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
659
        self.weave1.add_lines('v3', ['v2'], self.lines3)
 
660
 
 
661
    def test_written_detection(self):
 
662
        # Test detection of weave file corruption.
 
663
        #
 
664
        # Make sure that we can detect if a weave file has
 
665
        # been corrupted. This doesn't test all forms of corruption,
 
666
        # but it at least helps verify the data you get, is what you want.
 
667
        from cStringIO import StringIO
 
668
 
 
669
        w = Weave()
 
670
        w.add_lines('v1', [], ['hello\n'])
 
671
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
672
 
 
673
        tmpf = StringIO()
 
674
        write_weave(w, tmpf)
 
675
 
 
676
        # Because we are corrupting, we need to make sure we have the exact text
 
677
        self.assertEquals('# bzr weave file v5\n'
 
678
                          'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
679
                          'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
680
                          'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n',
 
681
                          tmpf.getvalue())
 
682
 
 
683
        # Change a single letter
 
684
        tmpf = StringIO('# bzr weave file v5\n'
 
685
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
686
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
687
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
 
688
 
 
689
        w = read_weave(tmpf)
 
690
 
 
691
        self.assertEqual('hello\n', w.get_text('v1'))
 
692
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
693
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
694
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
695
 
 
696
        # Change the sha checksum
 
697
        tmpf = StringIO('# bzr weave file v5\n'
 
698
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
699
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
700
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
 
701
 
 
702
        w = read_weave(tmpf)
 
703
 
 
704
        self.assertEqual('hello\n', w.get_text('v1'))
 
705
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
706
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
707
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
708
 
 
709
 
 
710
class TestWeave(TestCase):
 
711
 
 
712
    def test_allow_reserved_false(self):
 
713
        w = Weave('name', allow_reserved=False)
 
714
        # Add lines is checked at the WeaveFile level, not at the Weave level
 
715
        w.add_lines('name:', [], TEXT_1)
 
716
        # But get_lines is checked at this level
 
717
        self.assertRaises(errors.ReservedId, w.get_lines, 'name:')
 
718
 
 
719
    def test_allow_reserved_true(self):
 
720
        w = Weave('name', allow_reserved=True)
 
721
        w.add_lines('name:', [], TEXT_1)
 
722
        self.assertEqual(TEXT_1, w.get_lines('name:'))
 
723
 
 
724
 
 
725
class InstrumentedWeave(Weave):
 
726
    """Keep track of how many times functions are called."""
 
727
 
 
728
    def __init__(self, weave_name=None):
 
729
        self._extract_count = 0
 
730
        Weave.__init__(self, weave_name=weave_name)
 
731
 
 
732
    def _extract(self, versions):
 
733
        self._extract_count += 1
 
734
        return Weave._extract(self, versions)
 
735
 
 
736
 
 
737
class TestNeedsReweave(TestCase):
 
738
    """Internal corner cases for when reweave is needed."""
 
739
 
 
740
    def test_compatible_parents(self):
 
741
        w1 = Weave('a')
 
742
        my_parents = set([1, 2, 3])
 
743
        # subsets are ok
 
744
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
 
745
        # same sets
 
746
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
 
747
        # same empty corner case
 
748
        self.assertTrue(w1._compatible_parents(set(), set()))
 
749
        # other cannot contain stuff my_parents does not
 
750
        self.assertFalse(w1._compatible_parents(set(), set([1])))
 
751
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
 
752
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
 
753
 
 
754
 
 
755
class TestWeaveFile(TestCaseInTempDir):
 
756
 
 
757
    def test_empty_file(self):
 
758
        f = open('empty.weave', 'wb+')
 
759
        try:
 
760
            self.assertRaises(errors.WeaveFormatError,
 
761
                              read_weave, f)
 
762
        finally:
 
763
            f.close()