~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/test_weave.py

  • Committer: Robert Collins
  • Date: 2005-10-07 01:11:04 UTC
  • mto: This revision was merged to the branch mainline in revision 1420.
  • Revision ID: robertc@robertcollins.net-20051007011104-45d7605fb8b1080b
clean up pyc files in make clean

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python2.4
 
2
 
1
3
# Copyright (C) 2005 by Canonical Ltd
2
4
 
3
5
# This program is free software; you can redistribute it and/or modify
15
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
18
 
17
19
 
18
 
 
19
 
 
20
 
"""test case for knit/weave algorithm"""
21
 
 
22
 
 
23
 
from testsweet import TestBase
24
 
from knit import Knit, VerInfo
 
20
# TODO: tests regarding version names
 
21
 
 
22
 
 
23
 
 
24
"""test suite for weave algorithm"""
 
25
 
 
26
from pprint import pformat
 
27
 
 
28
from bzrlib.weave import Weave, WeaveFormatError, WeaveError
 
29
from bzrlib.weavefile import write_weave, read_weave
 
30
from bzrlib.selftest import TestCase
 
31
from bzrlib.osutils import sha_string
25
32
 
26
33
 
27
34
# texts for use in testing
30
37
          "A second line"]
31
38
 
32
39
 
 
40
 
 
41
class TestBase(TestCase):
 
42
    def check_read_write(self, k):
 
43
        """Check the weave k can be written & re-read."""
 
44
        from tempfile import TemporaryFile
 
45
        tf = TemporaryFile()
 
46
 
 
47
        write_weave(k, tf)
 
48
        tf.seek(0)
 
49
        k2 = read_weave(tf)
 
50
 
 
51
        if k != k2:
 
52
            tf.seek(0)
 
53
            self.log('serialized weave:')
 
54
            self.log(tf.read())
 
55
 
 
56
            self.log('')
 
57
            self.log('parents: %s' % (k._parents == k2._parents))
 
58
            self.log('         %r' % k._parents)
 
59
            self.log('         %r' % k2._parents)
 
60
            self.log('')
 
61
 
 
62
            
 
63
            self.fail('read/write check failed')
 
64
        
 
65
        
 
66
 
 
67
 
33
68
class Easy(TestBase):
34
69
    def runTest(self):
35
 
        k = Knit()
 
70
        k = Weave()
36
71
 
37
72
 
38
73
class StoreText(TestBase):
39
74
    """Store and retrieve a simple text."""
40
75
    def runTest(self):
41
 
        k = Knit()
42
 
        idx = k.add([], TEXT_0)
 
76
        k = Weave()
 
77
        idx = k.add('text0', [], TEXT_0)
43
78
        self.assertEqual(k.get(idx), TEXT_0)
44
79
        self.assertEqual(idx, 0)
45
80
 
47
82
 
48
83
class AnnotateOne(TestBase):
49
84
    def runTest(self):
50
 
        k = Knit()
51
 
        k.add([], TEXT_0)
 
85
        k = Weave()
 
86
        k.add('text0', [], TEXT_0)
52
87
        self.assertEqual(k.annotate(0),
53
88
                         [(0, TEXT_0[0])])
54
89
 
55
90
 
56
91
class StoreTwo(TestBase):
57
92
    def runTest(self):
58
 
        k = Knit()
 
93
        k = Weave()
59
94
 
60
 
        idx = k.add([], TEXT_0)
 
95
        idx = k.add('text0', [], TEXT_0)
61
96
        self.assertEqual(idx, 0)
62
97
 
63
 
        idx = k.add([], TEXT_1)
 
98
        idx = k.add('text1', [], TEXT_1)
64
99
        self.assertEqual(idx, 1)
65
100
 
66
101
        self.assertEqual(k.get(0), TEXT_0)
67
102
        self.assertEqual(k.get(1), TEXT_1)
68
103
 
69
 
        k.dump(self.TEST_LOG)
70
 
 
71
 
 
72
 
 
73
 
class Delta1(TestBase):
74
 
    """Detection of changes prior to inserting new revision."""
 
104
 
 
105
 
 
106
class AddWithGivenSha(TestBase):
75
107
    def runTest(self):
76
 
        from pprint import pformat
77
 
 
78
 
        k = Knit()
79
 
        k.add([], ['line 1'])
80
 
 
81
 
        changes = list(k._delta(set([0]),
82
 
                                ['line 1',
83
 
                                 'new line']))
84
 
 
85
 
        self.log('raw changes: ' + pformat(changes))
86
 
 
87
 
        # should be one inserted line after line 0q
88
 
        self.assertEquals(changes,
89
 
                          [(1, 1, ['new line'])])
90
 
 
91
 
        changes = k._delta(set([0]),
92
 
                           ['top line',
93
 
                            'line 1'])
94
 
        
95
 
        self.assertEquals(list(changes),
96
 
                          [(0, 0, ['top line'])])
 
108
        """Add with caller-supplied SHA-1"""
 
109
        k = Weave()
 
110
 
 
111
        t = 'text0'
 
112
        k.add('text0', [], [t], sha1=sha_string(t))
97
113
 
98
114
 
99
115
 
100
116
class InvalidAdd(TestBase):
101
117
    """Try to use invalid version number during add."""
102
118
    def runTest(self):
103
 
        k = Knit()
 
119
        k = Weave()
104
120
 
105
121
        self.assertRaises(IndexError,
106
122
                          k.add,
 
123
                          'text0',
107
124
                          [69],
108
125
                          ['new text!'])
109
126
 
110
127
 
 
128
class RepeatedAdd(TestBase):
 
129
    """Add the same version twice; harmless."""
 
130
    def runTest(self):
 
131
        k = Weave()
 
132
        idx = k.add('text0', [], TEXT_0)
 
133
        idx2 = k.add('text0', [], TEXT_0)
 
134
        self.assertEqual(idx, idx2)
 
135
 
 
136
 
 
137
 
 
138
class InvalidRepeatedAdd(TestBase):
 
139
    def runTest(self):
 
140
        k = Weave()
 
141
        idx = k.add('text0', [], TEXT_0)
 
142
        self.assertRaises(WeaveError,
 
143
                          k.add,
 
144
                          'text0',
 
145
                          [],
 
146
                          ['not the same text'])
 
147
        self.assertRaises(WeaveError,
 
148
                          k.add,
 
149
                          'text0',
 
150
                          [12],         # not the right parents
 
151
                          TEXT_0)
 
152
        
 
153
 
 
154
 
111
155
class InsertLines(TestBase):
112
156
    """Store a revision that adds one line to the original.
113
157
 
114
158
    Look at the annotations to make sure that the first line is matched
115
159
    and not stored repeatedly."""
116
160
    def runTest(self):
117
 
        k = Knit()
 
161
        k = Weave()
118
162
 
119
 
        k.add([], ['line 1'])
120
 
        k.add([0], ['line 1', 'line 2'])
 
163
        k.add('text0', [], ['line 1'])
 
164
        k.add('text1', [0], ['line 1', 'line 2'])
121
165
 
122
166
        self.assertEqual(k.annotate(0),
123
167
                         [(0, 'line 1')])
130
174
                         [(0, 'line 1'),
131
175
                          (1, 'line 2')])
132
176
 
133
 
        k.add([0], ['line 1', 'diverged line'])
 
177
        k.add('text2', [0], ['line 1', 'diverged line'])
134
178
 
135
179
        self.assertEqual(k.annotate(2),
136
180
                         [(0, 'line 1'),
137
181
                          (2, 'diverged line')])
138
182
 
139
 
        k.add([0, 1],
140
 
              ['line 1', 'middle line', 'line 2'])
 
183
        text3 = ['line 1', 'middle line', 'line 2']
 
184
        k.add('text3',
 
185
              [0, 1],
 
186
              text3)
 
187
 
 
188
        # self.log("changes to text3: " + pformat(list(k._delta(set([0, 1]), text3))))
 
189
 
 
190
        self.log("k._weave=" + pformat(k._weave))
141
191
 
142
192
        self.assertEqual(k.annotate(3),
143
193
                         [(0, 'line 1'),
144
194
                          (3, 'middle line'),
145
195
                          (1, 'line 2')])
146
196
 
 
197
        # now multiple insertions at different places
 
198
        k.add('text4',
 
199
              [0, 1, 3],
 
200
              ['line 1', 'aaa', 'middle line', 'bbb', 'line 2', 'ccc'])
 
201
 
 
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
 
 
210
 
 
211
 
 
212
class DeleteLines(TestBase):
 
213
    """Deletion of lines from existing text.
 
214
 
 
215
    Try various texts all based on a common ancestor."""
 
216
    def runTest(self):
 
217
        k = Weave()
 
218
 
 
219
        base_text = ['one', 'two', 'three', 'four']
 
220
 
 
221
        k.add('text0', [], base_text)
 
222
        
 
223
        texts = [['one', 'two', 'three'],
 
224
                 ['two', 'three', 'four'],
 
225
                 ['one', 'four'],
 
226
                 ['one', 'two', 'three', 'four'],
 
227
                 ]
 
228
 
 
229
        i = 1
 
230
        for t in texts:
 
231
            ver = k.add('text%d' % i,
 
232
                        [0], t)
 
233
            i += 1
 
234
 
 
235
        self.log('final weave:')
 
236
        self.log('k._weave=' + pformat(k._weave))
 
237
 
 
238
        for i in range(len(texts)):
 
239
            self.assertEqual(k.get(i+1),
 
240
                             texts[i])
 
241
            
 
242
 
 
243
 
 
244
 
 
245
class SuicideDelete(TestBase):
 
246
    """Invalid weave which tries to add and delete simultaneously."""
 
247
    def runTest(self):
 
248
        k = Weave()
 
249
 
 
250
        k._parents = [(),
 
251
                ]
 
252
        k._weave = [('{', 0),
 
253
                'first line',
 
254
                ('[', 0),
 
255
                'deleted in 0',
 
256
                (']', 0),
 
257
                ('}', 0),
 
258
                ]
 
259
        ################################### SKIPPED
 
260
        # Weave.get doesn't trap this anymore
 
261
        return 
 
262
 
 
263
        self.assertRaises(WeaveFormatError,
 
264
                          k.get,
 
265
                          0)        
 
266
 
 
267
 
 
268
 
 
269
class CannedDelete(TestBase):
 
270
    """Unpack canned weave with deleted lines."""
 
271
    def runTest(self):
 
272
        k = Weave()
 
273
 
 
274
        k._parents = [(),
 
275
                frozenset([0]),
 
276
                ]
 
277
        k._weave = [('{', 0),
 
278
                'first line',
 
279
                ('[', 1),
 
280
                'line to be deleted',
 
281
                (']', 1),
 
282
                'last line',
 
283
                ('}', 0),
 
284
                ]
 
285
 
 
286
        self.assertEqual(k.get(0),
 
287
                         ['first line',
 
288
                          'line to be deleted',
 
289
                          'last line',
 
290
                          ])
 
291
 
 
292
        self.assertEqual(k.get(1),
 
293
                         ['first line',
 
294
                          'last line',
 
295
                          ])
 
296
 
 
297
 
 
298
 
 
299
class CannedReplacement(TestBase):
 
300
    """Unpack canned weave with deleted lines."""
 
301
    def runTest(self):
 
302
        k = Weave()
 
303
 
 
304
        k._parents = [frozenset(),
 
305
                frozenset([0]),
 
306
                ]
 
307
        k._weave = [('{', 0),
 
308
                'first line',
 
309
                ('[', 1),
 
310
                'line to be deleted',
 
311
                (']', 1),
 
312
                ('{', 1),
 
313
                'replacement line',                
 
314
                ('}', 1),
 
315
                'last line',
 
316
                ('}', 0),
 
317
                ]
 
318
 
 
319
        self.assertEqual(k.get(0),
 
320
                         ['first line',
 
321
                          'line to be deleted',
 
322
                          'last line',
 
323
                          ])
 
324
 
 
325
        self.assertEqual(k.get(1),
 
326
                         ['first line',
 
327
                          'replacement line',
 
328
                          'last line',
 
329
                          ])
 
330
 
 
331
 
 
332
 
 
333
class BadWeave(TestBase):
 
334
    """Test that we trap an insert which should not occur."""
 
335
    def runTest(self):
 
336
        k = Weave()
 
337
 
 
338
        k._parents = [frozenset(),
 
339
                ]
 
340
        k._weave = ['bad line',
 
341
                ('{', 0),
 
342
                'foo {',
 
343
                ('{', 1),
 
344
                '  added in version 1',
 
345
                ('{', 2),
 
346
                '  added in v2',
 
347
                ('}', 2),
 
348
                '  also from v1',
 
349
                ('}', 1),
 
350
                '}',
 
351
                ('}', 0)]
 
352
 
 
353
        ################################### SKIPPED
 
354
        # Weave.get doesn't trap this anymore
 
355
        return 
 
356
 
 
357
 
 
358
        self.assertRaises(WeaveFormatError,
 
359
                          k.get,
 
360
                          0)
 
361
 
 
362
 
 
363
class BadInsert(TestBase):
 
364
    """Test that we trap an insert which should not occur."""
 
365
    def runTest(self):
 
366
        k = Weave()
 
367
 
 
368
        k._parents = [frozenset(),
 
369
                frozenset([0]),
 
370
                frozenset([0]),
 
371
                frozenset([0,1,2]),
 
372
                ]
 
373
        k._weave = [('{', 0),
 
374
                'foo {',
 
375
                ('{', 1),
 
376
                '  added in version 1',
 
377
                ('{', 1),
 
378
                '  more in 1',
 
379
                ('}', 1),
 
380
                ('}', 1),
 
381
                ('}', 0)]
 
382
 
 
383
 
 
384
        # this is not currently enforced by get
 
385
        return  ##########################################
 
386
 
 
387
        self.assertRaises(WeaveFormatError,
 
388
                          k.get,
 
389
                          0)
 
390
 
 
391
        self.assertRaises(WeaveFormatError,
 
392
                          k.get,
 
393
                          1)
 
394
 
 
395
 
 
396
class InsertNested(TestBase):
 
397
    """Insertion with nested instructions."""
 
398
    def runTest(self):
 
399
        k = Weave()
 
400
 
 
401
        k._parents = [frozenset(),
 
402
                frozenset([0]),
 
403
                frozenset([0]),
 
404
                frozenset([0,1,2]),
 
405
                ]
 
406
        k._weave = [('{', 0),
 
407
                'foo {',
 
408
                ('{', 1),
 
409
                '  added in version 1',
 
410
                ('{', 2),
 
411
                '  added in v2',
 
412
                ('}', 2),
 
413
                '  also from v1',
 
414
                ('}', 1),
 
415
                '}',
 
416
                ('}', 0)]
 
417
 
 
418
        self.assertEqual(k.get(0),
 
419
                         ['foo {',
 
420
                          '}'])
 
421
 
 
422
        self.assertEqual(k.get(1),
 
423
                         ['foo {',
 
424
                          '  added in version 1',
 
425
                          '  also from v1',
 
426
                          '}'])
 
427
                       
 
428
        self.assertEqual(k.get(2),
 
429
                         ['foo {',
 
430
                          '  added in v2',
 
431
                          '}'])
 
432
 
 
433
        self.assertEqual(k.get(3),
 
434
                         ['foo {',
 
435
                          '  added in version 1',
 
436
                          '  added in v2',
 
437
                          '  also from v1',
 
438
                          '}'])
 
439
                         
 
440
 
 
441
 
 
442
class DeleteLines2(TestBase):
 
443
    """Test recording revisions that delete lines.
 
444
 
 
445
    This relies on the weave having a way to represent lines knocked
 
446
    out by a later revision."""
 
447
    def runTest(self):
 
448
        k = Weave()
 
449
 
 
450
        k.add('text0', [], ["line the first",
 
451
                   "line 2",
 
452
                   "line 3",
 
453
                   "fine"])
 
454
 
 
455
        self.assertEqual(len(k.get(0)), 4)
 
456
 
 
457
        k.add('text1', [0], ["line the first",
 
458
                   "fine"])
 
459
 
 
460
        self.assertEqual(k.get(1),
 
461
                         ["line the first",
 
462
                          "fine"])
 
463
 
 
464
        self.assertEqual(k.annotate(1),
 
465
                         [(0, "line the first"),
 
466
                          (0, "fine")])
 
467
 
147
468
 
148
469
 
149
470
class IncludeVersions(TestBase):
150
471
    """Check texts that are stored across multiple revisions.
151
472
 
152
 
    Here we manually create a knit with particular encoding and make
 
473
    Here we manually create a weave with particular encoding and make
153
474
    sure it unpacks properly.
154
475
 
155
476
    Text 0 includes nothing; text 1 includes text 0 and adds some
157
478
    """
158
479
 
159
480
    def runTest(self):
160
 
        k = Knit()
 
481
        k = Weave()
161
482
 
162
 
        k._v = [VerInfo(), VerInfo(included=[0])]
163
 
        k._l = [(0, "first line"),
164
 
                (1, "second line")]
 
483
        k._parents = [frozenset(), frozenset([0])]
 
484
        k._weave = [('{', 0),
 
485
                "first line",
 
486
                ('}', 0),
 
487
                ('{', 1),
 
488
                "second line",
 
489
                ('}', 1)]
165
490
 
166
491
        self.assertEqual(k.get(1),
167
492
                         ["first line",
170
495
        self.assertEqual(k.get(0),
171
496
                         ["first line"])
172
497
 
173
 
        k.dump(self.TEST_LOG)
174
 
 
175
498
 
176
499
class DivergedIncludes(TestBase):
177
 
    """Knit with two diverged texts based on version 0.
 
500
    """Weave with two diverged texts based on version 0.
178
501
    """
179
502
    def runTest(self):
180
 
        k = Knit()
 
503
        k = Weave()
181
504
 
182
 
        k._v = [VerInfo(),
183
 
                VerInfo(included=[0]),
184
 
                VerInfo(included=[0]),
185
 
                ]
186
 
        k._l = [(0, "first line"),
187
 
                (1, "second line"),
188
 
                (2, "alternative second line"),]
 
505
        k._parents = [frozenset(),
 
506
                frozenset([0]),
 
507
                frozenset([0]),
 
508
                ]
 
509
        k._weave = [('{', 0),
 
510
                "first line",
 
511
                ('}', 0),
 
512
                ('{', 1),
 
513
                "second line",
 
514
                ('}', 1),
 
515
                ('{', 2),
 
516
                "alternative second line",
 
517
                ('}', 2),                
 
518
                ]
189
519
 
190
520
        self.assertEqual(k.get(0),
191
521
                         ["first line"])
198
528
                         ["first line",
199
529
                          "alternative second line"])
200
530
 
201
 
def testknit():
202
 
    import testsweet
203
 
    from unittest import TestSuite, TestLoader
204
 
    import testknit
205
 
 
206
 
    tl = TestLoader()
207
 
    suite = TestSuite()
208
 
    suite.addTest(tl.loadTestsFromModule(testknit))
209
 
    
210
 
    return int(not testsweet.run_suite(suite)) # for shell 0=true
 
531
        self.assertEqual(list(k.inclusions([2])),
 
532
                         [0, 2])
 
533
 
 
534
 
 
535
 
 
536
class ReplaceLine(TestBase):
 
537
    def runTest(self):
 
538
        k = Weave()
 
539
 
 
540
        text0 = ['cheddar', 'stilton', 'gruyere']
 
541
        text1 = ['cheddar', 'blue vein', 'neufchatel', 'chevre']
 
542
        
 
543
        k.add('text0', [], text0)
 
544
        k.add('text1', [0], text1)
 
545
 
 
546
        self.log('k._weave=' + pformat(k._weave))
 
547
 
 
548
        self.assertEqual(k.get(0), text0)
 
549
        self.assertEqual(k.get(1), text1)
 
550
 
 
551
 
 
552
 
 
553
class Merge(TestBase):
 
554
    """Storage of versions that merge diverged parents"""
 
555
    def runTest(self):
 
556
        k = Weave()
 
557
 
 
558
        texts = [['header'],
 
559
                 ['header', '', 'line from 1'],
 
560
                 ['header', '', 'line from 2', 'more from 2'],
 
561
                 ['header', '', 'line from 1', 'fixup line', 'line from 2'],
 
562
                 ]
 
563
 
 
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])
 
568
 
 
569
        for i, t in enumerate(texts):
 
570
            self.assertEqual(k.get(i), t)
 
571
 
 
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'),
 
578
                          ])
 
579
 
 
580
        self.assertEqual(list(k.inclusions([3])),
 
581
                         [0, 1, 2, 3])
 
582
 
 
583
        self.log('k._weave=' + pformat(k._weave))
 
584
 
 
585
        self.check_read_write(k)
 
586
 
 
587
 
 
588
class Conflicts(TestBase):
 
589
    """Test detection of conflicting regions during a merge.
 
590
 
 
591
    A base version is inserted, then two descendents try to
 
592
    insert different lines in the same place.  These should be
 
593
    reported as a possible conflict and forwarded to the user."""
 
594
    def runTest(self):
 
595
        return  # NOT RUN
 
596
        k = Weave()
 
597
 
 
598
        k.add([], ['aaa', 'bbb'])
 
599
        k.add([0], ['aaa', '111', 'bbb'])
 
600
        k.add([1], ['aaa', '222', 'bbb'])
 
601
 
 
602
        merged = k.merge([1, 2])
 
603
 
 
604
        self.assertEquals([[['aaa']],
 
605
                           [['111'], ['222']],
 
606
                           [['bbb']]])
 
607
 
 
608
 
 
609
 
 
610
class NonConflict(TestBase):
 
611
    """Two descendants insert compatible changes.
 
612
 
 
613
    No conflict should be reported."""
 
614
    def runTest(self):
 
615
        return  # NOT RUN
 
616
        k = Weave()
 
617
 
 
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
        
 
649
 
 
650
 
 
651
class Khayyam(TestBase):
 
652
    """Test changes to multi-line texts, and read/write"""
 
653
    def runTest(self):
 
654
        rawtexts = [
 
655
            """A Book of Verses underneath the Bough,
 
656
            A Jug of Wine, a Loaf of Bread, -- and Thou
 
657
            Beside me singing in the Wilderness --
 
658
            Oh, Wilderness were Paradise enow!""",
 
659
            
 
660
            """A Book of Verses underneath the Bough,
 
661
            A Jug of Wine, a Loaf of Bread, -- and Thou
 
662
            Beside me singing in the Wilderness --
 
663
            Oh, Wilderness were Paradise now!""",
 
664
 
 
665
            """A Book of poems underneath the tree,
 
666
            A Jug of Wine, a Loaf of Bread,
 
667
            and Thou
 
668
            Beside me singing in the Wilderness --
 
669
            Oh, Wilderness were Paradise now!
 
670
 
 
671
            -- O. Khayyam""",
 
672
 
 
673
            """A Book of Verses underneath the Bough,
 
674
            A Jug of Wine, a Loaf of Bread,
 
675
            and Thou
 
676
            Beside me singing in the Wilderness --
 
677
            Oh, Wilderness were Paradise now!""",
 
678
            ]
 
679
        texts = [[l.strip() for l in t.split('\n')] for t in rawtexts]
 
680
 
 
681
        k = Weave()
 
682
        parents = set()
 
683
        i = 0
 
684
        for t in texts:
 
685
            ver = k.add('text%d' % i,
 
686
                        list(parents), t)
 
687
            parents.add(ver)
 
688
            i += 1
 
689
 
 
690
        self.log("k._weave=" + pformat(k._weave))
 
691
 
 
692
        for i, t in enumerate(texts):
 
693
            self.assertEqual(k.get(i), t)
 
694
 
 
695
        self.check_read_write(k)
 
696
 
 
697
 
 
698
 
 
699
class MergeCases(TestBase):
 
700
    def doMerge(self, base, a, b, mp):
 
701
        from cStringIO import StringIO
 
702
        from textwrap import dedent
 
703
 
 
704
        def addcrlf(x):
 
705
            return x + '\n'
 
706
        
 
707
        w = Weave()
 
708
        w.add('text0', [], map(addcrlf, base))
 
709
        w.add('text1', [0], map(addcrlf, a))
 
710
        w.add('text2', [0], map(addcrlf, b))
 
711
 
 
712
        self.log('weave is:')
 
713
        tmpf = StringIO()
 
714
        write_weave(w, tmpf)
 
715
        self.log(tmpf.getvalue())
 
716
 
 
717
        self.log('merge plan:')
 
718
        p = list(w.plan_merge(1, 2))
 
719
        for state, line in p:
 
720
            if line:
 
721
                self.log('%12s | %s' % (state, line[:-1]))
 
722
 
 
723
        self.log('merge:')
 
724
        mt = StringIO()
 
725
        mt.writelines(w.weave_merge(p))
 
726
        mt.seek(0)
 
727
        self.log(mt.getvalue())
 
728
 
 
729
        mp = map(addcrlf, mp)
 
730
        self.assertEqual(mt.readlines(), mp)
 
731
        
 
732
        
 
733
    def testOneInsert(self):
 
734
        self.doMerge([],
 
735
                     ['aa'],
 
736
                     [],
 
737
                     ['aa'])
 
738
 
 
739
    def testSeparateInserts(self):
 
740
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
741
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
742
                     ['aaa', 'bbb', 'yyy', 'ccc'],
 
743
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
744
 
 
745
    def testSameInsert(self):
 
746
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
747
                     ['aaa', 'xxx', 'bbb', 'ccc'],
 
748
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'],
 
749
                     ['aaa', 'xxx', 'bbb', 'yyy', 'ccc'])
 
750
 
 
751
    def testOverlappedInsert(self):
 
752
        self.doMerge(['aaa', 'bbb'],
 
753
                     ['aaa', 'xxx', 'yyy', 'bbb'],
 
754
                     ['aaa', 'xxx', 'bbb'],
 
755
                     ['aaa', '<<<<', 'xxx', 'yyy', '====', 'xxx', '>>>>', 'bbb'])
 
756
 
 
757
        # really it ought to reduce this to 
 
758
        # ['aaa', 'xxx', 'yyy', 'bbb']
 
759
 
 
760
 
 
761
    def testClashReplace(self):
 
762
        self.doMerge(['aaa'],
 
763
                     ['xxx'],
 
764
                     ['yyy', 'zzz'],
 
765
                     ['<<<<', 'xxx', '====', 'yyy', 'zzz', '>>>>'])
 
766
 
 
767
    def testNonClashInsert(self):
 
768
        self.doMerge(['aaa'],
 
769
                     ['xxx', 'aaa'],
 
770
                     ['yyy', 'zzz'],
 
771
                     ['<<<<', 'xxx', 'aaa', '====', 'yyy', 'zzz', '>>>>'])
 
772
 
 
773
        self.doMerge(['aaa'],
 
774
                     ['aaa'],
 
775
                     ['yyy', 'zzz'],
 
776
                     ['yyy', 'zzz'])
 
777
 
 
778
 
 
779
    def testDeleteAndModify(self):
 
780
        """Clashing delete and modification.
 
781
 
 
782
        If one side modifies a region and the other deletes it then
 
783
        there should be a conflict with one side blank.
 
784
        """
 
785
 
 
786
        #######################################
 
787
        # skippd, not working yet
 
788
        return
 
789
        
 
790
        self.doMerge(['aaa', 'bbb', 'ccc'],
 
791
                     ['aaa', 'ddd', 'ccc'],
 
792
                     ['aaa', 'ccc'],
 
793
                     ['<<<<', 'aaa', '====', '>>>>', 'ccc'])
 
794
 
 
795
 
 
796
class JoinWeavesTests(TestBase):
 
797
    def setUp(self):
 
798
        super(JoinWeavesTests, self).setUp()
 
799
        self.weave1 = Weave()
 
800
        self.lines1 = ['hello\n']
 
801
        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)
 
805
        
 
806
    def test_join_empty(self):
 
807
        """Join two empty weaves."""
 
808
        eq = self.assertEqual
 
809
        w1 = Weave()
 
810
        w2 = Weave()
 
811
        w1.join(w2)
 
812
        eq(w1.numversions(), 0)
 
813
        
 
814
    def test_join_empty_to_nonempty(self):
 
815
        """Join empty weave onto nonempty."""
 
816
        self.weave1.join(Weave())
 
817
        self.assertEqual(len(self.weave1), 3)
 
818
 
 
819
    def test_join_unrelated(self):
 
820
        """Join two weaves with no history in common."""
 
821
        wb = Weave()
 
822
        wb.add('b1', [], ['line from b\n'])
 
823
        w1 = self.weave1
 
824
        w1.join(wb)
 
825
        eq = self.assertEqual
 
826
        eq(len(w1), 4)
 
827
        eq(sorted(list(w1.iter_names())),
 
828
           ['b1', 'v1', 'v2', 'v3'])
 
829
 
 
830
    def test_join_related(self):
 
831
        wa = self.weave1.copy()
 
832
        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'])
 
835
        eq = self.assertEquals
 
836
        eq(len(wa), 4)
 
837
        eq(len(wb), 4)
 
838
        wa.join(wb)
 
839
        eq(len(wa), 5)
 
840
        eq(wa.get_lines('b1'),
 
841
           ['hello\n', 'pale blue\n', 'world\n'])
 
842
 
 
843
    def test_join_parent_disagreement(self):
 
844
        """Cannot join weaves with different parents for a version."""
 
845
        wa = Weave()
 
846
        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)
 
852
 
 
853
    def test_join_text_disagreement(self):
 
854
        """Cannot join weaves with different texts for a version."""
 
855
        wa = Weave()
 
856
        wb = Weave()
 
857
        wa.add('v1', [], ['hello\n'])
 
858
        wb.add('v1', [], ['not\n', 'hello\n'])
 
859
        self.assertRaises(WeaveError,
 
860
                          wa.join, wb)
 
861
 
 
862
    def test_join_unordered(self):
 
863
        """Join weaves where indexes differ.
 
864
        
 
865
        The source weave contains a different version at index 0."""
 
866
        wa = self.weave1.copy()
 
867
        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')
211
894
 
212
895
 
213
896
if __name__ == '__main__':
214
897
    import sys
215
 
    sys.exit(testknit())
 
898
    import unittest
 
899
    sys.exit(unittest.main())
216
900