~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_weave.py

  • Committer: Martin Packman
  • Date: 2012-02-01 13:24:42 UTC
  • mto: (6437.23.4 2.5)
  • mto: This revision was merged to the branch mainline in revision 6462.
  • Revision ID: martin.packman@canonical.com-20120201132442-ela7jc4mxv4b058o
Treat path for .bzr.log as unicode

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-2009, 2011 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
30
32
from bzrlib.weavefile import write_weave, read_weave
31
 
from bzrlib.selftest 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
64
64
 
65
65
class WeaveContains(TestBase):
66
66
    """Weave __contains__ operator"""
 
67
 
67
68
    def runTest(self):
68
 
        k = Weave()
 
69
        k = Weave(get_scope=lambda:None)
69
70
        self.assertFalse('foo' in k)
70
 
        k.add('foo', [], TEXT_1)
 
71
        k.add_lines('foo', [], TEXT_1)
71
72
        self.assertTrue('foo' in k)
72
73
 
73
74
 
74
75
class Easy(TestBase):
75
 
    def runTest(self):
76
 
        k = Weave()
77
 
 
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
 
 
 
76
 
 
77
    def runTest(self):
 
78
        k = Weave()
87
79
 
88
80
 
89
81
class AnnotateOne(TestBase):
90
 
    def runTest(self):
91
 
        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
 
 
83
    def runTest(self):
 
84
        k = Weave()
 
85
        k.add_lines('text0', [], TEXT_0)
 
86
        self.assertEqual(k.annotate('text0'),
 
87
                         [('text0', TEXT_0[0])])
120
88
 
121
89
 
122
90
class InvalidAdd(TestBase):
123
91
    """Try to use invalid version number during add."""
 
92
 
124
93
    def runTest(self):
125
94
        k = Weave()
126
95
 
127
 
        self.assertRaises(IndexError,
128
 
                          k.add,
 
96
        self.assertRaises(errors.RevisionNotPresent,
 
97
                          k.add_lines,
129
98
                          'text0',
130
 
                          [69],
 
99
                          ['69'],
131
100
                          ['new text!'])
132
101
 
133
102
 
134
103
class RepeatedAdd(TestBase):
135
104
    """Add the same version twice; harmless."""
136
 
    def runTest(self):
 
105
 
 
106
    def test_duplicate_add(self):
137
107
        k = Weave()
138
 
        idx = k.add('text0', [], TEXT_0)
139
 
        idx2 = k.add('text0', [], TEXT_0)
 
108
        idx = k.add_lines('text0', [], TEXT_0)
 
109
        idx2 = k.add_lines('text0', [], TEXT_0)
140
110
        self.assertEqual(idx, idx2)
141
111
 
142
112
 
143
 
 
144
113
class InvalidRepeatedAdd(TestBase):
 
114
 
145
115
    def runTest(self):
146
116
        k = Weave()
147
 
        idx = k.add('text0', [], TEXT_0)
148
 
        self.assertRaises(WeaveError,
149
 
                          k.add,
 
117
        k.add_lines('basis', [], TEXT_0)
 
118
        idx = k.add_lines('text0', [], TEXT_0)
 
119
        self.assertRaises(errors.RevisionAlreadyPresent,
 
120
                          k.add_lines,
150
121
                          'text0',
151
122
                          [],
152
123
                          ['not the same text'])
153
 
        self.assertRaises(WeaveError,
154
 
                          k.add,
 
124
        self.assertRaises(errors.RevisionAlreadyPresent,
 
125
                          k.add_lines,
155
126
                          'text0',
156
 
                          [12],         # not the right parents
 
127
                          ['basis'],         # not the right parents
157
128
                          TEXT_0)
158
 
        
159
129
 
160
130
 
161
131
class InsertLines(TestBase):
166
136
    def runTest(self):
167
137
        k = Weave()
168
138
 
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),
 
139
        k.add_lines('text0', [], ['line 1'])
 
140
        k.add_lines('text1', ['text0'], ['line 1', 'line 2'])
 
141
 
 
142
        self.assertEqual(k.annotate('text0'),
 
143
                         [('text0', 'line 1')])
 
144
 
 
145
        self.assertEqual(k.get_lines(1),
176
146
                         ['line 1',
177
147
                          'line 2'])
178
148
 
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')])
 
149
        self.assertEqual(k.annotate('text1'),
 
150
                         [('text0', 'line 1'),
 
151
                          ('text1', 'line 2')])
 
152
 
 
153
        k.add_lines('text2', ['text0'], ['line 1', 'diverged line'])
 
154
 
 
155
        self.assertEqual(k.annotate('text2'),
 
156
                         [('text0', 'line 1'),
 
157
                          ('text2', 'diverged line')])
188
158
 
189
159
        text3 = ['line 1', 'middle line', 'line 2']
190
 
        k.add('text3',
191
 
              [0, 1],
 
160
        k.add_lines('text3',
 
161
              ['text0', 'text1'],
192
162
              text3)
193
163
 
194
164
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
195
165
 
196
166
        self.log("k._weave=" + pformat(k._weave))
197
167
 
198
 
        self.assertEqual(k.annotate(3),
199
 
                         [(0, 'line 1'),
200
 
                          (3, 'middle line'),
201
 
                          (1, 'line 2')])
 
168
        self.assertEqual(k.annotate('text3'),
 
169
                         [('text0', 'line 1'),
 
170
                          ('text3', 'middle line'),
 
171
                          ('text1', 'line 2')])
202
172
 
203
173
        # now multiple insertions at different places
204
 
        k.add('text4',
205
 
              [0, 1, 3],
 
174
        k.add_lines('text4',
 
175
              ['text0', 'text1', 'text3'],
206
176
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
207
177
 
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
 
 
 
178
        self.assertEqual(k.annotate('text4'),
 
179
                         [('text0', 'line 1'),
 
180
                          ('text4', 'aaa'),
 
181
                          ('text3', 'middle line'),
 
182
                          ('text4', 'bbb'),
 
183
                          ('text1', 'line 2'),
 
184
                          ('text4', 'ccc')])
216
185
 
217
186
 
218
187
class DeleteLines(TestBase):
224
193
 
225
194
        base_text = ['one', 'two', 'three', 'four']
226
195
 
227
 
        k.add('text0', [], base_text)
228
 
        
 
196
        k.add_lines('text0', [], base_text)
 
197
 
229
198
        texts = [['one', 'two', 'three'],
230
199
                 ['two', 'three', 'four'],
231
200
                 ['one', 'four'],
234
203
 
235
204
        i = 1
236
205
        for t in texts:
237
 
            ver = k.add('text%d' % i,
238
 
                        [0], t)
 
206
            ver = k.add_lines('text%d' % i,
 
207
                        ['text0'], t)
239
208
            i += 1
240
209
 
241
210
        self.log('final weave:')
242
211
        self.log('k._weave=' + pformat(k._weave))
243
212
 
244
213
        for i in range(len(texts)):
245
 
            self.assertEqual(k.get(i+1),
 
214
            self.assertEqual(k.get_lines(i+1),
246
215
                             texts[i])
247
 
            
248
 
 
249
216
 
250
217
 
251
218
class SuicideDelete(TestBase):
264
231
                ]
265
232
        ################################### SKIPPED
266
233
        # Weave.get doesn't trap this anymore
267
 
        return 
 
234
        return
268
235
 
269
236
        self.assertRaises(WeaveFormatError,
270
 
                          k.get,
271
 
                          0)        
272
 
 
 
237
                          k.get_lines,
 
238
                          0)
273
239
 
274
240
 
275
241
class CannedDelete(TestBase):
288
254
                'last line',
289
255
                ('}', 0),
290
256
                ]
 
257
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
258
                  , sha_string('first linelast line')]
291
259
 
292
 
        self.assertEqual(k.get(0),
 
260
        self.assertEqual(k.get_lines(0),
293
261
                         ['first line',
294
262
                          'line to be deleted',
295
263
                          'last line',
296
264
                          ])
297
265
 
298
 
        self.assertEqual(k.get(1),
 
266
        self.assertEqual(k.get_lines(1),
299
267
                         ['first line',
300
268
                          'last line',
301
269
                          ])
302
270
 
303
271
 
304
 
 
305
272
class CannedReplacement(TestBase):
306
273
    """Unpack canned weave with deleted lines."""
307
274
    def runTest(self):
316
283
                'line to be deleted',
317
284
                (']', 1),
318
285
                ('{', 1),
319
 
                'replacement line',                
 
286
                'replacement line',
320
287
                ('}', 1),
321
288
                'last line',
322
289
                ('}', 0),
323
290
                ]
 
291
        k._sha1s = [sha_string('first lineline to be deletedlast line')
 
292
                  , sha_string('first linereplacement linelast line')]
324
293
 
325
 
        self.assertEqual(k.get(0),
 
294
        self.assertEqual(k.get_lines(0),
326
295
                         ['first line',
327
296
                          'line to be deleted',
328
297
                          'last line',
329
298
                          ])
330
299
 
331
 
        self.assertEqual(k.get(1),
 
300
        self.assertEqual(k.get_lines(1),
332
301
                         ['first line',
333
302
                          'replacement line',
334
303
                          'last line',
335
304
                          ])
336
305
 
337
306
 
338
 
 
339
307
class BadWeave(TestBase):
340
308
    """Test that we trap an insert which should not occur."""
341
309
    def runTest(self):
358
326
 
359
327
        ################################### SKIPPED
360
328
        # Weave.get doesn't trap this anymore
361
 
        return 
 
329
        return
362
330
 
363
331
 
364
332
        self.assertRaises(WeaveFormatError,
421
389
                '}',
422
390
                ('}', 0)]
423
391
 
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
 
                         
 
392
        k._sha1s = [sha_string('foo {}')
 
393
                  , sha_string('foo {  added in version 1  also from v1}')
 
394
                  , sha_string('foo {  added in v2}')
 
395
                  , sha_string('foo {  added in version 1  added in v2  also from v1}')
 
396
                  ]
 
397
 
 
398
        self.assertEqual(k.get_lines(0),
 
399
                         ['foo {',
 
400
                          '}'])
 
401
 
 
402
        self.assertEqual(k.get_lines(1),
 
403
                         ['foo {',
 
404
                          '  added in version 1',
 
405
                          '  also from v1',
 
406
                          '}'])
 
407
 
 
408
        self.assertEqual(k.get_lines(2),
 
409
                         ['foo {',
 
410
                          '  added in v2',
 
411
                          '}'])
 
412
 
 
413
        self.assertEqual(k.get_lines(3),
 
414
                         ['foo {',
 
415
                          '  added in version 1',
 
416
                          '  added in v2',
 
417
                          '  also from v1',
 
418
                          '}'])
446
419
 
447
420
 
448
421
class DeleteLines2(TestBase):
453
426
    def runTest(self):
454
427
        k = Weave()
455
428
 
456
 
        k.add('text0', [], ["line the first",
 
429
        k.add_lines('text0', [], ["line the first",
457
430
                   "line 2",
458
431
                   "line 3",
459
432
                   "fine"])
460
433
 
461
 
        self.assertEqual(len(k.get(0)), 4)
 
434
        self.assertEqual(len(k.get_lines(0)), 4)
462
435
 
463
 
        k.add('text1', [0], ["line the first",
 
436
        k.add_lines('text1', ['text0'], ["line the first",
464
437
                   "fine"])
465
438
 
466
 
        self.assertEqual(k.get(1),
 
439
        self.assertEqual(k.get_lines(1),
467
440
                         ["line the first",
468
441
                          "fine"])
469
442
 
470
 
        self.assertEqual(k.annotate(1),
471
 
                         [(0, "line the first"),
472
 
                          (0, "fine")])
473
 
 
 
443
        self.assertEqual(k.annotate('text1'),
 
444
                         [('text0', "line the first"),
 
445
                          ('text0', "fine")])
474
446
 
475
447
 
476
448
class IncludeVersions(TestBase):
494
466
                "second line",
495
467
                ('}', 1)]
496
468
 
497
 
        self.assertEqual(k.get(1),
 
469
        k._sha1s = [sha_string('first line')
 
470
                  , sha_string('first linesecond line')]
 
471
 
 
472
        self.assertEqual(k.get_lines(1),
498
473
                         ["first line",
499
474
                          "second line"])
500
475
 
501
 
        self.assertEqual(k.get(0),
 
476
        self.assertEqual(k.get_lines(0),
502
477
                         ["first line"])
503
478
 
504
479
 
506
481
    """Weave with two diverged texts based on version 0.
507
482
    """
508
483
    def runTest(self):
 
484
        # FIXME make the weave, dont poke at it.
509
485
        k = Weave()
510
486
 
 
487
        k._names = ['0', '1', '2']
 
488
        k._name_map = {'0':0, '1':1, '2':2}
511
489
        k._parents = [frozenset(),
512
490
                frozenset([0]),
513
491
                frozenset([0]),
520
498
                ('}', 1),
521
499
                ('{', 2),
522
500
                "alternative second line",
523
 
                ('}', 2),                
 
501
                ('}', 2),
524
502
                ]
525
503
 
526
 
        self.assertEqual(k.get(0),
 
504
        k._sha1s = [sha_string('first line')
 
505
                  , sha_string('first linesecond line')
 
506
                  , sha_string('first linealternative second line')]
 
507
 
 
508
        self.assertEqual(k.get_lines(0),
527
509
                         ["first line"])
528
510
 
529
 
        self.assertEqual(k.get(1),
 
511
        self.assertEqual(k.get_lines(1),
530
512
                         ["first line",
531
513
                          "second line"])
532
514
 
533
 
        self.assertEqual(k.get(2),
 
515
        self.assertEqual(k.get_lines('2'),
534
516
                         ["first line",
535
517
                          "alternative second line"])
536
518
 
537
 
        self.assertEqual(list(k.inclusions([2])),
538
 
                         [0, 2])
539
 
 
 
519
        self.assertEqual(list(k.get_ancestry(['2'])),
 
520
                         ['0', '2'])
540
521
 
541
522
 
542
523
class ReplaceLine(TestBase):
545
526
 
546
527
        text0 = ['cheddar', 'stilton', 'gruyere']
547
528
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
548
 
        
549
 
        k.add('text0', [], text0)
550
 
        k.add('text1', [0], text1)
 
529
 
 
530
        k.add_lines('text0', [], text0)
 
531
        k.add_lines('text1', ['text0'], text1)
551
532
 
552
533
        self.log('k._weave=' + pformat(k._weave))
553
534
 
554
 
        self.assertEqual(k.get(0), text0)
555
 
        self.assertEqual(k.get(1), text1)
556
 
 
 
535
        self.assertEqual(k.get_lines(0), text0)
 
536
        self.assertEqual(k.get_lines(1), text1)
557
537
 
558
538
 
559
539
class Merge(TestBase):
560
540
    """Storage of versions that merge diverged parents"""
 
541
 
561
542
    def runTest(self):
562
543
        k = Weave()
563
544
 
567
548
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
568
549
                 ]
569
550
 
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])
 
551
        k.add_lines('text0', [], texts[0])
 
552
        k.add_lines('text1', ['text0'], texts[1])
 
553
        k.add_lines('text2', ['text0'], texts[2])
 
554
        k.add_lines('merge', ['text0', 'text1', 'text2'], texts[3])
574
555
 
575
556
        for i, t in enumerate(texts):
576
 
            self.assertEqual(k.get(i), t)
 
557
            self.assertEqual(k.get_lines(i), t)
577
558
 
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'),
 
559
        self.assertEqual(k.annotate('merge'),
 
560
                         [('text0', 'header'),
 
561
                          ('text1', ''),
 
562
                          ('text1', 'line from 1'),
 
563
                          ('merge', 'fixup line'),
 
564
                          ('text2', 'line from 2'),
584
565
                          ])
585
566
 
586
 
        self.assertEqual(list(k.inclusions([3])),
587
 
                         [0, 1, 2, 3])
 
567
        self.assertEqual(list(k.get_ancestry(['merge'])),
 
568
                         ['text0', 'text1', 'text2', 'merge'])
588
569
 
589
570
        self.log('k._weave=' + pformat(k._weave))
590
571
 
601
582
        return  # NOT RUN
602
583
        k = Weave()
603
584
 
604
 
        k.add([], ['aaa', 'bbb'])
605
 
        k.add([0], ['aaa', '111', 'bbb'])
606
 
        k.add([1], ['aaa', '222', 'bbb'])
 
585
        k.add_lines([], ['aaa', 'bbb'])
 
586
        k.add_lines([0], ['aaa', '111', 'bbb'])
 
587
        k.add_lines([1], ['aaa', '222', 'bbb'])
607
588
 
608
589
        merged = k.merge([1, 2])
609
590
 
612
593
                           [['bbb']]])
613
594
 
614
595
 
615
 
 
616
596
class NonConflict(TestBase):
617
597
    """Two descendants insert compatible changes.
618
598
 
621
601
        return  # NOT RUN
622
602
        k = Weave()
623
603
 
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
 
        
 
604
        k.add_lines([], ['aaa', 'bbb'])
 
605
        k.add_lines([0], ['111', 'aaa', 'ccc', 'bbb'])
 
606
        k.add_lines([1], ['aaa', 'ccc', 'bbb', '222'])
655
607
 
656
608
 
657
609
class Khayyam(TestBase):
658
610
    """Test changes to multi-line texts, and read/write"""
659
 
    def runTest(self):
 
611
 
 
612
    def test_multi_line_merge(self):
660
613
        rawtexts = [
661
614
            """A Book of Verses underneath the Bough,
662
615
            A Jug of Wine, a Loaf of Bread, -- and Thou
663
616
            Beside me singing in the Wilderness --
664
617
            Oh, Wilderness were Paradise enow!""",
665
 
            
 
618
 
666
619
            """A Book of Verses underneath the Bough,
667
620
            A Jug of Wine, a Loaf of Bread, -- and Thou
668
621
            Beside me singing in the Wilderness --
688
641
        parents = set()
689
642
        i = 0
690
643
        for t in texts:
691
 
            ver = k.add('text%d' % i,
 
644
            ver = k.add_lines('text%d' % i,
692
645
                        list(parents), t)
693
 
            parents.add(ver)
 
646
            parents.add('text%d' % i)
694
647
            i += 1
695
648
 
696
649
        self.log("k._weave=" + pformat(k._weave))
697
650
 
698
651
        for i, t in enumerate(texts):
699
 
            self.assertEqual(k.get(i), t)
 
652
            self.assertEqual(k.get_lines(i), t)
700
653
 
701
654
        self.check_read_write(k)
702
655
 
703
656
 
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
657
class JoinWeavesTests(TestBase):
 
658
 
806
659
    def setUp(self):
807
660
        super(JoinWeavesTests, self).setUp()
808
661
        self.weave1 = Weave()
809
662
        self.lines1 = ['hello\n']
810
663
        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')
 
664
        self.weave1.add_lines('v1', [], self.lines1)
 
665
        self.weave1.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
666
        self.weave1.add_lines('v3', ['v2'], self.lines3)
 
667
 
 
668
    def test_written_detection(self):
 
669
        # Test detection of weave file corruption.
 
670
        #
 
671
        # Make sure that we can detect if a weave file has
 
672
        # been corrupted. This doesn't test all forms of corruption,
 
673
        # but it at least helps verify the data you get, is what you want.
 
674
        from cStringIO import StringIO
 
675
 
 
676
        w = Weave()
 
677
        w.add_lines('v1', [], ['hello\n'])
 
678
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
679
 
 
680
        tmpf = StringIO()
 
681
        write_weave(w, tmpf)
 
682
 
 
683
        # Because we are corrupting, we need to make sure we have the exact text
 
684
        self.assertEquals('# 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
                          tmpf.getvalue())
 
689
 
 
690
        # Change a single letter
 
691
        tmpf = StringIO('# bzr weave file v5\n'
 
692
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
693
                        'i 0\n1 90f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
694
                        'w\n{ 0\n. hello\n}\n{ 1\n. There\n}\nW\n')
 
695
 
 
696
        w = read_weave(tmpf)
 
697
 
 
698
        self.assertEqual('hello\n', w.get_text('v1'))
 
699
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
700
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
701
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
702
 
 
703
        # Change the sha checksum
 
704
        tmpf = StringIO('# bzr weave file v5\n'
 
705
                        'i\n1 f572d396fae9206628714fb2ce00f72e94f2258f\nn v1\n\n'
 
706
                        'i 0\n1 f0f265c6e75f1c8f9ab76dcf85528352c5f215ef\nn v2\n\n'
 
707
                        'w\n{ 0\n. hello\n}\n{ 1\n. there\n}\nW\n')
 
708
 
 
709
        w = read_weave(tmpf)
 
710
 
 
711
        self.assertEqual('hello\n', w.get_text('v1'))
 
712
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
713
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
714
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
715
 
 
716
 
 
717
class TestWeave(TestCase):
 
718
 
 
719
    def test_allow_reserved_false(self):
 
720
        w = Weave('name', allow_reserved=False)
 
721
        # Add lines is checked at the WeaveFile level, not at the Weave level
 
722
        w.add_lines('name:', [], TEXT_1)
 
723
        # But get_lines is checked at this level
 
724
        self.assertRaises(errors.ReservedId, w.get_lines, 'name:')
 
725
 
 
726
    def test_allow_reserved_true(self):
 
727
        w = Weave('name', allow_reserved=True)
 
728
        w.add_lines('name:', [], TEXT_1)
 
729
        self.assertEqual(TEXT_1, w.get_lines('name:'))
 
730
 
 
731
 
 
732
class InstrumentedWeave(Weave):
 
733
    """Keep track of how many times functions are called."""
 
734
 
 
735
    def __init__(self, weave_name=None):
 
736
        self._extract_count = 0
 
737
        Weave.__init__(self, weave_name=weave_name)
 
738
 
 
739
    def _extract(self, versions):
 
740
        self._extract_count += 1
 
741
        return Weave._extract(self, versions)
 
742
 
 
743
 
 
744
class TestNeedsReweave(TestCase):
 
745
    """Internal corner cases for when reweave is needed."""
 
746
 
 
747
    def test_compatible_parents(self):
 
748
        w1 = Weave('a')
 
749
        my_parents = set([1, 2, 3])
 
750
        # subsets are ok
 
751
        self.assertTrue(w1._compatible_parents(my_parents, set([3])))
 
752
        # same sets
 
753
        self.assertTrue(w1._compatible_parents(my_parents, set(my_parents)))
 
754
        # same empty corner case
 
755
        self.assertTrue(w1._compatible_parents(set(), set()))
 
756
        # other cannot contain stuff my_parents does not
 
757
        self.assertFalse(w1._compatible_parents(set(), set([1])))
 
758
        self.assertFalse(w1._compatible_parents(my_parents, set([1, 2, 3, 4])))
 
759
        self.assertFalse(w1._compatible_parents(my_parents, set([4])))
 
760
 
 
761
 
 
762
class TestWeaveFile(TestCaseInTempDir):
 
763
 
 
764
    def test_empty_file(self):
 
765
        f = open('empty.weave', 'wb+')
 
766
        try:
 
767
            self.assertRaises(errors.WeaveFormatError,
 
768
                              read_weave, f)
 
769
        finally:
 
770
            f.close()