/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_versionedfile.py

add ghost aware apis to knits.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 by Canonical Ltd
 
2
#
 
3
# Authors:
 
4
#   Johan Rydberg <jrydberg@gnu.org>
 
5
#
 
6
# This program is free software; you can redistribute it and/or modify
 
7
# it under the terms of the GNU General Public License as published by
 
8
# the Free Software Foundation; either version 2 of the License, or
 
9
# (at your option) any later version.
 
10
 
 
11
# This program is distributed in the hope that it will be useful,
 
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
# GNU General Public License for more details.
 
15
 
 
16
# You should have received a copy of the GNU General Public License
 
17
# along with this program; if not, write to the Free Software
 
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 
 
20
 
 
21
import bzrlib
 
22
import bzrlib.errors as errors
 
23
from bzrlib.errors import (
 
24
                           RevisionNotPresent, 
 
25
                           RevisionAlreadyPresent,
 
26
                           WeaveParentMismatch
 
27
                           )
 
28
from bzrlib.knit import KnitVersionedFile, \
 
29
     KnitAnnotateFactory
 
30
from bzrlib.tests import TestCaseWithTransport
 
31
from bzrlib.trace import mutter
 
32
from bzrlib.transport import get_transport
 
33
from bzrlib.transport.memory import MemoryTransport
 
34
import bzrlib.versionedfile as versionedfile
 
35
from bzrlib.weave import WeaveFile
 
36
from bzrlib.weavefile import read_weave
 
37
 
 
38
 
 
39
class VersionedFileTestMixIn(object):
 
40
    """A mixin test class for testing VersionedFiles.
 
41
 
 
42
    This is not an adaptor-style test at this point because
 
43
    theres no dynamic substitution of versioned file implementations,
 
44
    they are strictly controlled by their owning repositories.
 
45
    """
 
46
 
 
47
    def test_add(self):
 
48
        f = self.get_file()
 
49
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
50
        f.add_lines('r1', ['r0'], ['b\n', 'c\n'])
 
51
        def verify_file(f):
 
52
            versions = f.versions()
 
53
            self.assertTrue('r0' in versions)
 
54
            self.assertTrue('r1' in versions)
 
55
            self.assertEquals(f.get_lines('r0'), ['a\n', 'b\n'])
 
56
            self.assertEquals(f.get_text('r0'), 'a\nb\n')
 
57
            self.assertEquals(f.get_lines('r1'), ['b\n', 'c\n'])
 
58
            self.assertEqual(2, len(f))
 
59
            self.assertEqual(2, f.num_versions())
 
60
    
 
61
            self.assertRaises(RevisionNotPresent,
 
62
                f.add_lines, 'r2', ['foo'], [])
 
63
            self.assertRaises(RevisionAlreadyPresent,
 
64
                f.add_lines, 'r1', [], [])
 
65
        verify_file(f)
 
66
        f = self.reopen_file()
 
67
        verify_file(f)
 
68
 
 
69
    def test_ancestry(self):
 
70
        f = self.get_file()
 
71
        self.assertEqual([], f.get_ancestry([]))
 
72
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
73
        f.add_lines('r1', ['r0'], ['b\n', 'c\n'])
 
74
        f.add_lines('r2', ['r0'], ['b\n', 'c\n'])
 
75
        f.add_lines('r3', ['r2'], ['b\n', 'c\n'])
 
76
        f.add_lines('rM', ['r1', 'r2'], ['b\n', 'c\n'])
 
77
        self.assertEqual([], f.get_ancestry([]))
 
78
        versions = f.get_ancestry(['rM'])
 
79
        # there are some possibilities:
 
80
        # r0 r1 r2 rM r3
 
81
        # r0 r1 r2 r3 rM
 
82
        # etc
 
83
        # so we check indexes
 
84
        r0 = versions.index('r0')
 
85
        r1 = versions.index('r1')
 
86
        r2 = versions.index('r2')
 
87
        self.assertFalse('r3' in versions)
 
88
        rM = versions.index('rM')
 
89
        self.assertTrue(r0 < r1)
 
90
        self.assertTrue(r0 < r2)
 
91
        self.assertTrue(r1 < rM)
 
92
        self.assertTrue(r2 < rM)
 
93
 
 
94
        self.assertRaises(RevisionNotPresent,
 
95
            f.get_ancestry, ['rM', 'rX'])
 
96
        
 
97
    def test_clear_cache(self):
 
98
        f = self.get_file()
 
99
        # on a new file it should not error
 
100
        f.clear_cache()
 
101
        # and after adding content, doing a clear_cache and a get should work.
 
102
        f.add_lines('0', [], ['a'])
 
103
        f.clear_cache()
 
104
        self.assertEqual(['a'], f.get_lines('0'))
 
105
 
 
106
    def test_clone_text(self):
 
107
        f = self.get_file()
 
108
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
109
        f.clone_text('r1', 'r0', ['r0'])
 
110
        def verify_file(f):
 
111
            self.assertEquals(f.get_lines('r1'), f.get_lines('r0'))
 
112
            self.assertEquals(f.get_lines('r1'), ['a\n', 'b\n'])
 
113
            self.assertEquals(f.get_parents('r1'), ['r0'])
 
114
    
 
115
            self.assertRaises(RevisionNotPresent,
 
116
                f.clone_text, 'r2', 'rX', [])
 
117
            self.assertRaises(RevisionAlreadyPresent,
 
118
                f.clone_text, 'r1', 'r0', [])
 
119
        verify_file(f)
 
120
        verify_file(self.reopen_file())
 
121
 
 
122
    def test_create_empty(self):
 
123
        f = self.get_file()
 
124
        f.add_lines('0', [], ['a\n'])
 
125
        new_f = f.create_empty('t', MemoryTransport())
 
126
        # smoke test, specific types should check it is honoured correctly for
 
127
        # non type attributes
 
128
        self.assertEqual([], new_f.versions())
 
129
        self.assertTrue(isinstance(new_f, f.__class__))
 
130
 
 
131
    def test_copy_to(self):
 
132
        f = self.get_file()
 
133
        f.add_lines('0', [], ['a\n'])
 
134
        t = MemoryTransport()
 
135
        f.copy_to('foo', t)
 
136
        for suffix in f.__class__.get_suffixes():
 
137
            self.assertTrue(t.has('foo' + suffix))
 
138
 
 
139
    def test_get_suffixes(self):
 
140
        f = self.get_file()
 
141
        # should be the same
 
142
        self.assertEqual(f.__class__.get_suffixes(), f.__class__.get_suffixes())
 
143
        # and should be a list
 
144
        self.assertTrue(isinstance(f.__class__.get_suffixes(), list))
 
145
 
 
146
    def test_get_graph(self):
 
147
        f = self.get_file()
 
148
        f.add_lines('v1', [], ['hello\n'])
 
149
        f.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
 
150
        f.add_lines('v3', ['v2'], ['hello\n', 'cruel\n', 'world\n'])
 
151
        self.assertEqual({'v1': [],
 
152
                          'v2': ['v1'],
 
153
                          'v3': ['v2']},
 
154
                         f.get_graph())
 
155
 
 
156
    def test_get_parents(self):
 
157
        f = self.get_file()
 
158
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
159
        f.add_lines('r1', [], ['a\n', 'b\n'])
 
160
        f.add_lines('r2', [], ['a\n', 'b\n'])
 
161
        f.add_lines('r3', [], ['a\n', 'b\n'])
 
162
        f.add_lines('m', ['r0', 'r1', 'r2', 'r3'], ['a\n', 'b\n'])
 
163
        self.assertEquals(f.get_parents('m'), ['r0', 'r1', 'r2', 'r3'])
 
164
 
 
165
        self.assertRaises(RevisionNotPresent,
 
166
            f.get_parents, 'y')
 
167
 
 
168
    def test_annotate(self):
 
169
        f = self.get_file()
 
170
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
171
        f.add_lines('r1', ['r0'], ['c\n', 'b\n'])
 
172
        origins = f.annotate('r1')
 
173
        self.assertEquals(origins[0][0], 'r1')
 
174
        self.assertEquals(origins[1][0], 'r0')
 
175
 
 
176
        self.assertRaises(RevisionNotPresent,
 
177
            f.annotate, 'foo')
 
178
 
 
179
    def test_walk(self):
 
180
        # tests that walk returns all the inclusions for the requested
 
181
        # revisions as well as the revisions changes themselves.
 
182
        f = self.get_file('1')
 
183
        f.add_lines('r0', [], ['a\n', 'b\n'])
 
184
        f.add_lines('r1', ['r0'], ['c\n', 'b\n'])
 
185
        f.add_lines('rX', ['r1'], ['d\n', 'b\n'])
 
186
        f.add_lines('rY', ['r1'], ['c\n', 'e\n'])
 
187
 
 
188
        lines = {}
 
189
        for lineno, insert, dset, text in f.walk(['rX', 'rY']):
 
190
            lines[text] = (insert, dset)
 
191
 
 
192
        self.assertTrue(lines['a\n'], ('r0', set(['r1'])))
 
193
        self.assertTrue(lines['b\n'], ('r0', set(['rY'])))
 
194
        self.assertTrue(lines['c\n'], ('r1', set(['rX'])))
 
195
        self.assertTrue(lines['d\n'], ('rX', set([])))
 
196
        self.assertTrue(lines['e\n'], ('rY', set([])))
 
197
 
 
198
    def test_detection(self):
 
199
        # Test weaves detect corruption.
 
200
        #
 
201
        # Weaves contain a checksum of their texts.
 
202
        # When a text is extracted, this checksum should be
 
203
        # verified.
 
204
 
 
205
        w = self.get_file_corrupted_text()
 
206
 
 
207
        self.assertEqual('hello\n', w.get_text('v1'))
 
208
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
209
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
210
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
211
 
 
212
        w = self.get_file_corrupted_checksum()
 
213
 
 
214
        self.assertEqual('hello\n', w.get_text('v1'))
 
215
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
 
216
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
 
217
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
 
218
 
 
219
    def get_file_corrupted_text(self):
 
220
        """Return a versioned file with corrupt text but valid metadata."""
 
221
        raise NotImplementedError(self.get_file_corrupted_text)
 
222
 
 
223
    def reopen_file(self, name='foo'):
 
224
        """Open the versioned file from disk again."""
 
225
        raise NotImplementedError(self.reopen_file)
 
226
 
 
227
    def test_iter_lines_added_or_present_in_versions(self):
 
228
        # test that we get at least an equalset of the lines added by
 
229
        # versions in the weave 
 
230
        # the ordering here is to make a tree so that dumb searches have
 
231
        # more changes to muck up.
 
232
        vf = self.get_file()
 
233
        # add a base to get included
 
234
        vf.add_lines('base', [], ['base\n'])
 
235
        # add a ancestor to be included on one side
 
236
        vf.add_lines('lancestor', [], ['lancestor\n'])
 
237
        # add a ancestor to be included on the other side
 
238
        vf.add_lines('rancestor', ['base'], ['rancestor\n'])
 
239
        # add a child of rancestor with no eofile-nl
 
240
        vf.add_lines('child', ['rancestor'], ['base\n', 'child\n'])
 
241
        # add a child of lancestor and base to join the two roots
 
242
        vf.add_lines('otherchild',
 
243
                     ['lancestor', 'base'],
 
244
                     ['base\n', 'lancestor\n', 'otherchild\n'])
 
245
        def iter_with_versions(versions):
 
246
            # now we need to see what lines are returned, and how often.
 
247
            lines = {'base\n':0,
 
248
                     'lancestor\n':0,
 
249
                     'rancestor\n':0,
 
250
                     'child\n':0,
 
251
                     'otherchild\n':0,
 
252
                     }
 
253
            # iterate over the lines
 
254
            for line in vf.iter_lines_added_or_present_in_versions(versions):
 
255
                lines[line] += 1
 
256
            return lines
 
257
        lines = iter_with_versions(['child', 'otherchild'])
 
258
        # we must see child and otherchild
 
259
        self.assertTrue(lines['child\n'] > 0)
 
260
        self.assertTrue(lines['otherchild\n'] > 0)
 
261
        # we dont care if we got more than that.
 
262
        
 
263
        # test all lines
 
264
        lines = iter_with_versions(None)
 
265
        # all lines must be seen at least once
 
266
        self.assertTrue(lines['base\n'] > 0)
 
267
        self.assertTrue(lines['lancestor\n'] > 0)
 
268
        self.assertTrue(lines['rancestor\n'] > 0)
 
269
        self.assertTrue(lines['child\n'] > 0)
 
270
        self.assertTrue(lines['otherchild\n'] > 0)
 
271
 
 
272
    def test_fix_parents(self):
 
273
        # some versioned files allow incorrect parents to be corrected after
 
274
        # insertion - this may not fix ancestry..
 
275
        # if they do not supported, they just do not implement it.
 
276
        # we test this as an interface test to ensure that those that *do*
 
277
        # implementent it get it right.
 
278
        vf = self.get_file()
 
279
        vf.add_lines('notbase', [], [])
 
280
        vf.add_lines('base', [], [])
 
281
        try:
 
282
            vf.fix_parents('notbase', ['base'])
 
283
        except NotImplementedError:
 
284
            return
 
285
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
286
        # open again, check it stuck.
 
287
        vf = self.get_file()
 
288
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
289
 
 
290
    def test_fix_parents_with_ghosts(self):
 
291
        # when fixing parents, ghosts that are listed should not be ghosts
 
292
        # anymore.
 
293
        vf = self.get_file()
 
294
 
 
295
        try:
 
296
            vf.add_lines_with_ghosts('notbase', ['base', 'stillghost'], [])
 
297
        except NotImplementedError:
 
298
            return
 
299
        vf.add_lines('base', [], [])
 
300
        vf.fix_parents('notbase', ['base', 'stillghost'])
 
301
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
302
        # open again, check it stuck.
 
303
        vf = self.get_file()
 
304
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
305
        # and check the ghosts
 
306
        self.assertEqual(['base', 'stillghost'],
 
307
                         vf.get_parents_with_ghosts('notbase'))
 
308
 
 
309
    def test_add_lines_with_ghosts(self):
 
310
        # some versioned file formats allow lines to be added with parent
 
311
        # information that is > than that in the format. Formats that do
 
312
        # not support this need to raise NotImplementedError on the
 
313
        # add_lines_with_ghosts api.
 
314
        vf = self.get_file()
 
315
        # add a revision with ghost parents
 
316
        try:
 
317
            vf.add_lines_with_ghosts('notbase', ['base'], [])
 
318
        except NotImplementedError:
 
319
            # check the other ghost apis are also not implemented
 
320
            self.assertRaises(NotImplementedError, vf.has_ghost, 'foo')
 
321
            self.assertRaises(NotImplementedError, vf.get_ancestry_with_ghosts, ['foo'])
 
322
            self.assertRaises(NotImplementedError, vf.get_parents_with_ghosts, 'foo')
 
323
            self.assertRaises(NotImplementedError, vf.get_graph_with_ghosts)
 
324
            return
 
325
        # test key graph related apis: getncestry, _graph, get_parents
 
326
        # has_version
 
327
        # - these are ghost unaware and must not be reflect ghosts
 
328
        self.assertEqual(['notbase'], vf.get_ancestry('notbase'))
 
329
        self.assertEqual([], vf.get_parents('notbase'))
 
330
        self.assertEqual({'notbase':[]}, vf.get_graph())
 
331
        self.assertFalse(vf.has_version('base'))
 
332
        # we have _with_ghost apis to give us ghost information.
 
333
        self.assertEqual(['base', 'notbase'], vf.get_ancestry_with_ghosts(['notbase']))
 
334
        self.assertEqual(['base'], vf.get_parents_with_ghosts('notbase'))
 
335
        self.assertEqual({'notbase':['base']}, vf.get_graph_with_ghosts())
 
336
        self.assertTrue(vf.has_ghost('base'))
 
337
        # if we add something that is a ghost of another, it should correct the
 
338
        # results of the prior apis
 
339
        vf.add_lines('base', [], [])
 
340
        self.assertEqual(['base', 'notbase'], vf.get_ancestry(['notbase']))
 
341
        self.assertEqual(['base'], vf.get_parents('notbase'))
 
342
        self.assertEqual({'base':[],
 
343
                          'notbase':['base'],
 
344
                          },
 
345
                         vf.get_graph())
 
346
        self.assertTrue(vf.has_version('base'))
 
347
        # we have _with_ghost apis to give us ghost information.
 
348
        self.assertEqual(['base', 'notbase'], vf.get_ancestry_with_ghosts(['notbase']))
 
349
        self.assertEqual(['base'], vf.get_parents_with_ghosts('notbase'))
 
350
        self.assertEqual({'base':[],
 
351
                          'notbase':['base'],
 
352
                          },
 
353
                         vf.get_graph_with_ghosts())
 
354
        self.assertFalse(vf.has_ghost('base'))
 
355
 
 
356
 
 
357
class TestWeave(TestCaseWithTransport, VersionedFileTestMixIn):
 
358
 
 
359
    def get_file(self, name='foo'):
 
360
        return WeaveFile(name, get_transport(self.get_url('.')), create=True)
 
361
 
 
362
    def get_file_corrupted_text(self):
 
363
        w = WeaveFile('foo', get_transport(self.get_url('.')), create=True)
 
364
        w.add_lines('v1', [], ['hello\n'])
 
365
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
366
        
 
367
        # We are going to invasively corrupt the text
 
368
        # Make sure the internals of weave are the same
 
369
        self.assertEqual([('{', 0)
 
370
                        , 'hello\n'
 
371
                        , ('}', None)
 
372
                        , ('{', 1)
 
373
                        , 'there\n'
 
374
                        , ('}', None)
 
375
                        ], w._weave)
 
376
        
 
377
        self.assertEqual(['f572d396fae9206628714fb2ce00f72e94f2258f'
 
378
                        , '90f265c6e75f1c8f9ab76dcf85528352c5f215ef'
 
379
                        ], w._sha1s)
 
380
        w.check()
 
381
        
 
382
        # Corrupted
 
383
        w._weave[4] = 'There\n'
 
384
        return w
 
385
 
 
386
    def get_file_corrupted_checksum(self):
 
387
        w = self.get_file_corrupted_text()
 
388
        # Corrected
 
389
        w._weave[4] = 'there\n'
 
390
        self.assertEqual('hello\nthere\n', w.get_text('v2'))
 
391
        
 
392
        #Invalid checksum, first digit changed
 
393
        w._sha1s[1] =  'f0f265c6e75f1c8f9ab76dcf85528352c5f215ef'
 
394
        return w
 
395
 
 
396
    def reopen_file(self, name='foo'):
 
397
        return WeaveFile(name, get_transport(self.get_url('.')))
 
398
 
 
399
    def test_no_implicit_create(self):
 
400
        self.assertRaises(errors.NoSuchFile,
 
401
                          WeaveFile,
 
402
                          'foo',
 
403
                          get_transport(self.get_url('.')))
 
404
 
 
405
 
 
406
class TestKnit(TestCaseWithTransport, VersionedFileTestMixIn):
 
407
 
 
408
    def get_file(self, name='foo'):
 
409
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
 
410
                                 delta=True, create=True)
 
411
 
 
412
    def get_file_corrupted_text(self):
 
413
        knit = self.get_file()
 
414
        knit.add_lines('v1', [], ['hello\n'])
 
415
        knit.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
 
416
        return knit
 
417
 
 
418
    def reopen_file(self, name='foo'):
 
419
        return KnitVersionedFile(name, get_transport(self.get_url('.')), delta=True)
 
420
 
 
421
    def test_detection(self):
 
422
        print "TODO for merging: create a corrupted knit."
 
423
        knit = self.get_file()
 
424
        knit.check()
 
425
 
 
426
    def test_no_implicit_create(self):
 
427
        self.assertRaises(errors.NoSuchFile,
 
428
                          KnitVersionedFile,
 
429
                          'foo',
 
430
                          get_transport(self.get_url('.')))
 
431
 
 
432
 
 
433
class InterString(versionedfile.InterVersionedFile):
 
434
    """An inter-versionedfile optimised code path for strings.
 
435
 
 
436
    This is for use during testing where we use strings as versionedfiles
 
437
    so that none of the default regsitered interversionedfile classes will
 
438
    match - which lets us test the match logic.
 
439
    """
 
440
 
 
441
    @staticmethod
 
442
    def is_compatible(source, target):
 
443
        """InterString is compatible with strings-as-versionedfiles."""
 
444
        return isinstance(source, str) and isinstance(target, str)
 
445
 
 
446
 
 
447
# TODO this and the InterRepository core logic should be consolidatable
 
448
# if we make the registry a separate class though we still need to 
 
449
# test the behaviour in the active registry to catch failure-to-handle-
 
450
# stange-objects
 
451
class TestInterVersionedFile(TestCaseWithTransport):
 
452
 
 
453
    def test_get_default_inter_versionedfile(self):
 
454
        # test that the InterVersionedFile.get(a, b) probes
 
455
        # for a class where is_compatible(a, b) returns
 
456
        # true and returns a default interversionedfile otherwise.
 
457
        # This also tests that the default registered optimised interversionedfile
 
458
        # classes do not barf inappropriately when a surprising versionedfile type
 
459
        # is handed to them.
 
460
        dummy_a = "VersionedFile 1."
 
461
        dummy_b = "VersionedFile 2."
 
462
        self.assertGetsDefaultInterVersionedFile(dummy_a, dummy_b)
 
463
 
 
464
    def assertGetsDefaultInterVersionedFile(self, a, b):
 
465
        """Asserts that InterVersionedFile.get(a, b) -> the default."""
 
466
        inter = versionedfile.InterVersionedFile.get(a, b)
 
467
        self.assertEqual(versionedfile.InterVersionedFile,
 
468
                         inter.__class__)
 
469
        self.assertEqual(a, inter.source)
 
470
        self.assertEqual(b, inter.target)
 
471
 
 
472
    def test_register_inter_versionedfile_class(self):
 
473
        # test that a optimised code path provider - a
 
474
        # InterVersionedFile subclass can be registered and unregistered
 
475
        # and that it is correctly selected when given a versionedfile
 
476
        # pair that it returns true on for the is_compatible static method
 
477
        # check
 
478
        dummy_a = "VersionedFile 1."
 
479
        dummy_b = "VersionedFile 2."
 
480
        versionedfile.InterVersionedFile.register_optimiser(InterString)
 
481
        try:
 
482
            # we should get the default for something InterString returns False
 
483
            # to
 
484
            self.assertFalse(InterString.is_compatible(dummy_a, None))
 
485
            self.assertGetsDefaultInterVersionedFile(dummy_a, None)
 
486
            # and we should get an InterString for a pair it 'likes'
 
487
            self.assertTrue(InterString.is_compatible(dummy_a, dummy_b))
 
488
            inter = versionedfile.InterVersionedFile.get(dummy_a, dummy_b)
 
489
            self.assertEqual(InterString, inter.__class__)
 
490
            self.assertEqual(dummy_a, inter.source)
 
491
            self.assertEqual(dummy_b, inter.target)
 
492
        finally:
 
493
            versionedfile.InterVersionedFile.unregister_optimiser(InterString)
 
494
        # now we should get the default InterVersionedFile object again.
 
495
        self.assertGetsDefaultInterVersionedFile(dummy_a, dummy_b)