/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
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
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
21
import bzrlib
22
import bzrlib.errors as errors
1563.2.11 by Robert Collins
Consolidate reweave and join as we have no separate usage, make reweave tests apply to all versionedfile implementations and deprecate the old reweave apis.
23
from bzrlib.errors import (
24
                           RevisionNotPresent, 
25
                           RevisionAlreadyPresent,
26
                           WeaveParentMismatch
27
                           )
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
28
from bzrlib.knit import KnitVersionedFile, \
29
     KnitAnnotateFactory
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
30
from bzrlib.tests import TestCaseWithTransport
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
31
from bzrlib.trace import mutter
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
32
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
33
from bzrlib.transport.memory import MemoryTransport
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
34
import bzrlib.versionedfile as versionedfile
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
35
from bzrlib.weave import WeaveFile
36
from bzrlib.weavefile import read_weave
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
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'])
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
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'])
1563.2.18 by Robert Collins
get knit repositories really using knits for text storage.
58
            self.assertEqual(2, len(f))
59
            self.assertEqual(2, f.num_versions())
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
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)
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
68
69
    def test_ancestry(self):
70
        f = self.get_file()
71
        f.add_lines('r0', [], ['a\n', 'b\n'])
72
        f.add_lines('r1', ['r0'], ['b\n', 'c\n'])
73
        f.add_lines('r2', ['r0'], ['b\n', 'c\n'])
74
        f.add_lines('r3', ['r2'], ['b\n', 'c\n'])
75
        f.add_lines('rM', ['r1', 'r2'], ['b\n', 'c\n'])
76
        versions = set(f.get_ancestry(['rM']))
77
        self.assertEquals(versions, set(['rM', 'r2', 'r1', 'r0']))
78
79
        self.assertRaises(RevisionNotPresent,
80
            f.get_ancestry, ['rM', 'rX'])
1563.2.7 by Robert Collins
add versioned file clear_cache entry.
81
        
82
    def test_clear_cache(self):
83
        f = self.get_file()
84
        # on a new file it should not error
85
        f.clear_cache()
86
        # and after adding content, doing a clear_cache and a get should work.
87
        f.add_lines('0', [], ['a'])
88
        f.clear_cache()
89
        self.assertEqual(['a'], f.get_lines('0'))
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
90
91
    def test_clone_text(self):
92
        f = self.get_file()
93
        f.add_lines('r0', [], ['a\n', 'b\n'])
1563.2.5 by Robert Collins
Remove unused transaction references from knit.py and the versionedfile interface.
94
        f.clone_text('r1', 'r0', ['r0'])
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
95
        def verify_file(f):
96
            self.assertEquals(f.get_lines('r1'), f.get_lines('r0'))
97
            self.assertEquals(f.get_lines('r1'), ['a\n', 'b\n'])
98
            self.assertEquals(f.get_parents('r1'), ['r0'])
99
    
100
            self.assertRaises(RevisionNotPresent,
101
                f.clone_text, 'r2', 'rX', [])
102
            self.assertRaises(RevisionAlreadyPresent,
103
                f.clone_text, 'r1', 'r0', [])
104
        verify_file(f)
105
        verify_file(self.reopen_file())
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
106
1563.2.13 by Robert Collins
InterVersionedFile implemented.
107
    def test_create_empty(self):
108
        f = self.get_file()
109
        f.add_lines('0', [], ['a\n'])
110
        new_f = f.create_empty('t', MemoryTransport())
111
        # smoke test, specific types should check it is honoured correctly for
112
        # non type attributes
113
        self.assertEqual([], new_f.versions())
114
        self.assertTrue(isinstance(new_f, f.__class__))
115
1563.2.15 by Robert Collins
remove the weavestore assumptions about the number and nature of files it manages.
116
    def test_copy_to(self):
117
        f = self.get_file()
118
        f.add_lines('0', [], ['a\n'])
119
        t = MemoryTransport()
120
        f.copy_to('foo', t)
121
        for suffix in f.__class__.get_suffixes():
122
            self.assertTrue(t.has('foo' + suffix))
123
124
    def test_get_suffixes(self):
125
        f = self.get_file()
126
        # should be the same
127
        self.assertEqual(f.__class__.get_suffixes(), f.__class__.get_suffixes())
128
        # and should be a list
129
        self.assertTrue(isinstance(f.__class__.get_suffixes(), list))
130
1563.2.13 by Robert Collins
InterVersionedFile implemented.
131
    def test_get_graph(self):
132
        f = self.get_file()
133
        f.add_lines('v1', [], ['hello\n'])
134
        f.add_lines('v2', ['v1'], ['hello\n', 'world\n'])
135
        f.add_lines('v3', ['v2'], ['hello\n', 'cruel\n', 'world\n'])
136
        self.assertEqual({'v1': [],
137
                          'v2': ['v1'],
138
                          'v3': ['v2']},
139
                         f.get_graph())
140
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
141
    def test_get_parents(self):
142
        f = self.get_file()
143
        f.add_lines('r0', [], ['a\n', 'b\n'])
144
        f.add_lines('r1', [], ['a\n', 'b\n'])
145
        f.add_lines('r2', [], ['a\n', 'b\n'])
146
        f.add_lines('r3', [], ['a\n', 'b\n'])
147
        f.add_lines('m', ['r0', 'r1', 'r2', 'r3'], ['a\n', 'b\n'])
148
        self.assertEquals(f.get_parents('m'), ['r0', 'r1', 'r2', 'r3'])
149
150
        self.assertRaises(RevisionNotPresent,
151
            f.get_parents, 'y')
152
153
    def test_annotate(self):
154
        f = self.get_file()
155
        f.add_lines('r0', [], ['a\n', 'b\n'])
156
        f.add_lines('r1', ['r0'], ['c\n', 'b\n'])
157
        origins = f.annotate('r1')
158
        self.assertEquals(origins[0][0], 'r1')
159
        self.assertEquals(origins[1][0], 'r0')
160
161
        self.assertRaises(RevisionNotPresent,
162
            f.annotate, 'foo')
163
164
    def test_walk(self):
165
        f = self.get_file('1')
166
        f.add_lines('r0', [], ['a\n', 'b\n'])
167
        f.add_lines('r1', ['r0'], ['c\n', 'b\n'])
168
        f.add_lines('rX', ['r1'], ['d\n', 'b\n'])
169
        f.add_lines('rY', ['r1'], ['c\n', 'e\n'])
170
171
        lines = {}
172
        for lineno, insert, dset, text in f.walk(['rX', 'rY']):
173
            lines[text] = (insert, dset)
174
175
        self.assertTrue(lines['a\n'], ('r0', set(['r1'])))
176
        self.assertTrue(lines['b\n'], ('r0', set(['rY'])))
177
        self.assertTrue(lines['c\n'], ('r1', set(['rX'])))
178
        self.assertTrue(lines['d\n'], ('rX', set([])))
179
        self.assertTrue(lines['e\n'], ('rY', set([])))
180
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
181
    def test_detection(self):
182
        # Test weaves detect corruption.
183
        #
184
        # Weaves contain a checksum of their texts.
185
        # When a text is extracted, this checksum should be
186
        # verified.
187
188
        w = self.get_file_corrupted_text()
189
190
        self.assertEqual('hello\n', w.get_text('v1'))
191
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
192
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
193
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
194
195
        w = self.get_file_corrupted_checksum()
196
197
        self.assertEqual('hello\n', w.get_text('v1'))
198
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_text, 'v2')
199
        self.assertRaises(errors.WeaveInvalidChecksum, w.get_lines, 'v2')
200
        self.assertRaises(errors.WeaveInvalidChecksum, w.check)
201
202
    def get_file_corrupted_text(self):
203
        """Return a versioned file with corrupt text but valid metadata."""
204
        raise NotImplementedError(self.get_file_corrupted_text)
205
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
206
    def reopen_file(self, name='foo'):
207
        """Open the versioned file from disk again."""
208
        raise NotImplementedError(self.reopen_file)
209
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
210
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
211
class TestWeave(TestCaseWithTransport, VersionedFileTestMixIn):
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
212
213
    def get_file(self, name='foo'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
214
        return WeaveFile(name, get_transport(self.get_url('.')))
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
215
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
216
    def get_file_corrupted_text(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
217
        w = WeaveFile('foo', get_transport(self.get_url('.')))
1563.2.13 by Robert Collins
InterVersionedFile implemented.
218
        w.add_lines('v1', [], ['hello\n'])
219
        w.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
220
        
221
        # We are going to invasively corrupt the text
222
        # Make sure the internals of weave are the same
223
        self.assertEqual([('{', 0)
224
                        , 'hello\n'
225
                        , ('}', None)
226
                        , ('{', 1)
227
                        , 'there\n'
228
                        , ('}', None)
229
                        ], w._weave)
230
        
231
        self.assertEqual(['f572d396fae9206628714fb2ce00f72e94f2258f'
232
                        , '90f265c6e75f1c8f9ab76dcf85528352c5f215ef'
233
                        ], w._sha1s)
234
        w.check()
235
        
236
        # Corrupted
237
        w._weave[4] = 'There\n'
238
        return w
239
240
    def get_file_corrupted_checksum(self):
241
        w = self.get_file_corrupted_text()
242
        # Corrected
243
        w._weave[4] = 'there\n'
244
        self.assertEqual('hello\nthere\n', w.get_text('v2'))
245
        
246
        #Invalid checksum, first digit changed
247
        w._sha1s[1] =  'f0f265c6e75f1c8f9ab76dcf85528352c5f215ef'
248
        return w
249
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
250
    def reopen_file(self, name='foo'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
251
        return WeaveFile(name, get_transport(self.get_url('.')))
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
252
1563.2.1 by Robert Collins
Merge in a variation of the versionedfile api from versioned-file.
253
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
254
class TestKnit(TestCaseWithTransport, VersionedFileTestMixIn):
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
255
256
    def get_file(self, name='foo'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
257
        return KnitVersionedFile(name, get_transport(self.get_url('.')),
258
                                 delta=True)
1563.2.6 by Robert Collins
Start check tests for knits (pending), and remove dead code.
259
260
    def get_file_corrupted_text(self):
261
        knit = self.get_file()
262
        knit.add_lines('v1', [], ['hello\n'])
263
        knit.add_lines('v2', ['v1'], ['hello\n', 'there\n'])
264
        return knit
1563.2.9 by Robert Collins
Update versionedfile api tests to ensure that data is available after every operation.
265
266
    def reopen_file(self, name='foo'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
267
        return KnitVersionedFile(name, get_transport(self.get_url('.')), delta=True)
1563.2.10 by Robert Collins
Change weave store to be a versioned store, using WeaveFiles which maintain integrity without needing explicit 'put' operations.
268
269
    def test_detection(self):
270
        print "TODO for merging: create a corrupted knit."
1563.2.19 by Robert Collins
stub out a check for knits.
271
        knit = self.get_file()
272
        knit.check()
1563.2.12 by Robert Collins
Checkpointing: created InterObject to factor out common inter object worker code, added InterVersionedFile and tests to allow making join work between any versionedfile.
273
274
275
class InterString(versionedfile.InterVersionedFile):
276
    """An inter-versionedfile optimised code path for strings.
277
278
    This is for use during testing where we use strings as versionedfiles
279
    so that none of the default regsitered interversionedfile classes will
280
    match - which lets us test the match logic.
281
    """
282
283
    @staticmethod
284
    def is_compatible(source, target):
285
        """InterString is compatible with strings-as-versionedfiles."""
286
        return isinstance(source, str) and isinstance(target, str)
287
288
289
# TODO this and the InterRepository core logic should be consolidatable
290
# if we make the registry a separate class though we still need to 
291
# test the behaviour in the active registry to catch failure-to-handle-
292
# stange-objects
293
class TestInterVersionedFile(TestCaseWithTransport):
294
295
    def test_get_default_inter_versionedfile(self):
296
        # test that the InterVersionedFile.get(a, b) probes
297
        # for a class where is_compatible(a, b) returns
298
        # true and returns a default interversionedfile otherwise.
299
        # This also tests that the default registered optimised interversionedfile
300
        # classes do not barf inappropriately when a surprising versionedfile type
301
        # is handed to them.
302
        dummy_a = "VersionedFile 1."
303
        dummy_b = "VersionedFile 2."
304
        self.assertGetsDefaultInterVersionedFile(dummy_a, dummy_b)
305
306
    def assertGetsDefaultInterVersionedFile(self, a, b):
307
        """Asserts that InterVersionedFile.get(a, b) -> the default."""
308
        inter = versionedfile.InterVersionedFile.get(a, b)
309
        self.assertEqual(versionedfile.InterVersionedFile,
310
                         inter.__class__)
311
        self.assertEqual(a, inter.source)
312
        self.assertEqual(b, inter.target)
313
314
    def test_register_inter_versionedfile_class(self):
315
        # test that a optimised code path provider - a
316
        # InterVersionedFile subclass can be registered and unregistered
317
        # and that it is correctly selected when given a versionedfile
318
        # pair that it returns true on for the is_compatible static method
319
        # check
320
        dummy_a = "VersionedFile 1."
321
        dummy_b = "VersionedFile 2."
322
        versionedfile.InterVersionedFile.register_optimiser(InterString)
323
        try:
324
            # we should get the default for something InterString returns False
325
            # to
326
            self.assertFalse(InterString.is_compatible(dummy_a, None))
327
            self.assertGetsDefaultInterVersionedFile(dummy_a, None)
328
            # and we should get an InterString for a pair it 'likes'
329
            self.assertTrue(InterString.is_compatible(dummy_a, dummy_b))
330
            inter = versionedfile.InterVersionedFile.get(dummy_a, dummy_b)
331
            self.assertEqual(InterString, inter.__class__)
332
            self.assertEqual(dummy_a, inter.source)
333
            self.assertEqual(dummy_b, inter.target)
334
        finally:
335
            versionedfile.InterVersionedFile.unregister_optimiser(InterString)
336
        # now we should get the default InterVersionedFile object again.
337
        self.assertGetsDefaultInterVersionedFile(dummy_a, dummy_b)
338
339