/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for Knit data structure"""
18
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
19
from cStringIO import StringIO
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
20
import difflib
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
21
import gzip
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
22
import sys
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
23
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
24
from bzrlib import (
25
    errors,
2484.1.5 by John Arbash Meinel
Simplistic implementations of custom parsers for options and parents
26
    generate_ids,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
27
    knit,
3350.8.12 by Robert Collins
Stacked make_mpdiffs.
28
    multiparent,
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
29
    osutils,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
30
    pack,
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
31
    )
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
32
from bzrlib.errors import (
33
    RevisionAlreadyPresent,
34
    KnitHeaderError,
35
    RevisionNotPresent,
36
    NoSuchFile,
37
    )
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
38
from bzrlib.index import *
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
39
from bzrlib.knit import (
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
40
    AnnotatedKnitContent,
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
41
    KnitContent,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
42
    KnitSequenceMatcher,
43
    KnitVersionedFiles,
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
44
    PlainKnitContent,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
45
    _DirectPackAccess,
46
    _KndxIndex,
47
    _KnitGraphIndex,
48
    _KnitKeyAccess,
49
    make_file_factory,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
50
    )
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
51
from bzrlib.tests import (
52
    Feature,
3350.8.8 by Robert Collins
Stacking and knits don't play nice for annotation yet.
53
    KnownFailure,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
54
    TestCase,
55
    TestCaseWithMemoryTransport,
56
    TestCaseWithTransport,
3787.1.1 by Robert Collins
Embed the failed text in sha1 knit errors.
57
    TestNotApplicable,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
58
    )
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
59
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
60
from bzrlib.transport.memory import MemoryTransport
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
61
from bzrlib.tuned_gzip import GzipFile
3350.8.2 by Robert Collins
stacked get_parent_map.
62
from bzrlib.versionedfile import (
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
63
    AbsentContentFactory,
3350.8.2 by Robert Collins
stacked get_parent_map.
64
    ConstantMapper,
65
    RecordingVersionedFilesDecorator,
66
    )
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
67
68
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
69
class _CompiledKnitFeature(Feature):
70
71
    def _probe(self):
72
        try:
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
73
            import bzrlib._knit_load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
74
        except ImportError:
75
            return False
76
        return True
77
78
    def feature_name(self):
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
79
        return 'bzrlib._knit_load_data_c'
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
80
81
CompiledKnitFeature = _CompiledKnitFeature()
82
83
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
84
class KnitContentTestsMixin(object):
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
85
86
    def test_constructor(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
87
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
88
89
    def test_text(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
90
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
91
        self.assertEqual(content.text(), [])
92
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
93
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
94
        self.assertEqual(content.text(), ["text1", "text2"])
95
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
96
    def test_copy(self):
97
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
98
        copy = content.copy()
99
        self.assertIsInstance(copy, content.__class__)
100
        self.assertEqual(copy.annotate(), content.annotate())
101
102
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
103
        """Assert that the derived matching blocks match real output"""
104
        source_lines = source.splitlines(True)
105
        target_lines = target.splitlines(True)
106
        def nl(line):
107
            if noeol and not line.endswith('\n'):
108
                return line + '\n'
109
            else:
110
                return line
111
        source_content = self._make_content([(None, nl(l)) for l in source_lines])
112
        target_content = self._make_content([(None, nl(l)) for l in target_lines])
113
        line_delta = source_content.line_delta(target_content)
114
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
115
            source_lines, target_lines))
116
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
117
        matcher_blocks = list(list(matcher.get_matching_blocks()))
118
        self.assertEqual(matcher_blocks, delta_blocks)
119
120
    def test_get_line_delta_blocks(self):
121
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
122
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
123
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
124
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
125
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
126
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
127
        self.assertDerivedBlocksEqual(TEXT_1A, '')
128
        self.assertDerivedBlocksEqual('', TEXT_1A)
129
        self.assertDerivedBlocksEqual('', '')
130
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
131
132
    def test_get_line_delta_blocks_noeol(self):
133
        """Handle historical knit deltas safely
134
135
        Some existing knit deltas don't consider the last line to differ
136
        when the only difference whether it has a final newline.
137
138
        New knit deltas appear to always consider the last line to differ
139
        in this case.
140
        """
141
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
142
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
143
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
144
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
145
146
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
147
TEXT_1 = """\
148
Banana cup cakes:
149
150
- bananas
151
- eggs
152
- broken tea cups
153
"""
154
155
TEXT_1A = """\
156
Banana cup cake recipe
157
(serves 6)
158
159
- bananas
160
- eggs
161
- broken tea cups
162
- self-raising flour
163
"""
164
165
TEXT_1B = """\
166
Banana cup cake recipe
167
168
- bananas (do not use plantains!!!)
169
- broken tea cups
170
- flour
171
"""
172
173
delta_1_1a = """\
174
0,1,2
175
Banana cup cake recipe
176
(serves 6)
177
5,5,1
178
- self-raising flour
179
"""
180
181
TEXT_2 = """\
182
Boeuf bourguignon
183
184
- beef
185
- red wine
186
- small onions
187
- carrot
188
- mushrooms
189
"""
190
191
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
192
class TestPlainKnitContent(TestCase, KnitContentTestsMixin):
193
194
    def _make_content(self, lines):
195
        annotated_content = AnnotatedKnitContent(lines)
196
        return PlainKnitContent(annotated_content.text(), 'bogus')
197
198
    def test_annotate(self):
199
        content = self._make_content([])
200
        self.assertEqual(content.annotate(), [])
201
202
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
203
        self.assertEqual(content.annotate(),
204
            [("bogus", "text1"), ("bogus", "text2")])
205
206
    def test_line_delta(self):
207
        content1 = self._make_content([("", "a"), ("", "b")])
208
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
209
        self.assertEqual(content1.line_delta(content2),
210
            [(1, 2, 2, ["a", "c"])])
211
212
    def test_line_delta_iter(self):
213
        content1 = self._make_content([("", "a"), ("", "b")])
214
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
215
        it = content1.line_delta_iter(content2)
216
        self.assertEqual(it.next(), (1, 2, 2, ["a", "c"]))
217
        self.assertRaises(StopIteration, it.next)
218
219
220
class TestAnnotatedKnitContent(TestCase, KnitContentTestsMixin):
221
222
    def _make_content(self, lines):
223
        return AnnotatedKnitContent(lines)
224
225
    def test_annotate(self):
226
        content = self._make_content([])
227
        self.assertEqual(content.annotate(), [])
228
229
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
230
        self.assertEqual(content.annotate(),
231
            [("origin1", "text1"), ("origin2", "text2")])
232
233
    def test_line_delta(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
234
        content1 = self._make_content([("", "a"), ("", "b")])
235
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
236
        self.assertEqual(content1.line_delta(content2),
237
            [(1, 2, 2, [("", "a"), ("", "c")])])
238
239
    def test_line_delta_iter(self):
2794.1.2 by Robert Collins
Nuke versioned file add/get delta support, allowing easy simplification of unannotated Content, reducing memory copies and friction during commit on unannotated texts.
240
        content1 = self._make_content([("", "a"), ("", "b")])
241
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
242
        it = content1.line_delta_iter(content2)
243
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
244
        self.assertRaises(StopIteration, it.next)
245
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
246
247
class MockTransport(object):
248
249
    def __init__(self, file_lines=None):
250
        self.file_lines = file_lines
251
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
252
        # We have no base directory for the MockTransport
253
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
254
255
    def get(self, filename):
256
        if self.file_lines is None:
257
            raise NoSuchFile(filename)
258
        else:
259
            return StringIO("\n".join(self.file_lines))
260
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
261
    def readv(self, relpath, offsets):
262
        fp = self.get(relpath)
263
        for offset, size in offsets:
264
            fp.seek(offset)
265
            yield offset, fp.read(size)
266
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
267
    def __getattr__(self, name):
268
        def queue_call(*args, **kwargs):
269
            self.calls.append((name, args, kwargs))
270
        return queue_call
271
272
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
273
class MockReadvFailingTransport(MockTransport):
274
    """Fail in the middle of a readv() result.
275
3789.2.3 by John Arbash Meinel
Change the mocking a bit, so we can be sure it is failing at the right time.
276
    This Transport will successfully yield the first two requested hunks, but
277
    raise NoSuchFile for the rest.
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
278
    """
279
280
    def readv(self, relpath, offsets):
3789.2.3 by John Arbash Meinel
Change the mocking a bit, so we can be sure it is failing at the right time.
281
        count = 0
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
282
        for result in MockTransport.readv(self, relpath, offsets):
3789.2.3 by John Arbash Meinel
Change the mocking a bit, so we can be sure it is failing at the right time.
283
            count += 1
284
            # we use 2 because the first offset is the pack header, the second
285
            # is the first actual content requset
286
            if count > 2:
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
287
                raise errors.NoSuchFile(relpath)
288
            yield result
289
290
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
291
class KnitRecordAccessTestsMixin(object):
292
    """Tests for getting and putting knit records."""
293
294
    def test_add_raw_records(self):
295
        """Add_raw_records adds records retrievable later."""
296
        access = self.get_access()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
297
        memos = access.add_raw_records([('key', 10)], '1234567890')
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
298
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
299
 
300
    def test_add_several_raw_records(self):
301
        """add_raw_records with many records and read some back."""
302
        access = self.get_access()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
303
        memos = access.add_raw_records([('key', 10), ('key2', 2), ('key3', 5)],
304
            '12345678901234567')
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
305
        self.assertEqual(['1234567890', '12', '34567'],
306
            list(access.get_raw_records(memos)))
307
        self.assertEqual(['1234567890'],
308
            list(access.get_raw_records(memos[0:1])))
309
        self.assertEqual(['12'],
310
            list(access.get_raw_records(memos[1:2])))
311
        self.assertEqual(['34567'],
312
            list(access.get_raw_records(memos[2:3])))
313
        self.assertEqual(['1234567890', '34567'],
314
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
315
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
316
317
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
318
    """Tests for the .kndx implementation."""
319
320
    def get_access(self):
321
        """Get a .knit style access instance."""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
322
        mapper = ConstantMapper("foo")
323
        access = _KnitKeyAccess(self.get_transport(), mapper)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
324
        return access
3789.2.6 by John Arbash Meinel
Make _DirectPackAccess.reload_or_raise maintain the logic.
325
326
327
class _TestException(Exception):
328
    """Just an exception for local tests to use."""
329
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
330
331
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
332
    """Tests for the pack based access."""
333
334
    def get_access(self):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
335
        return self._get_access()[0]
336
337
    def _get_access(self, packname='packfile', index='FOO'):
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
338
        transport = self.get_transport()
339
        def write_data(bytes):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
340
            transport.append_bytes(packname, bytes)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
341
        writer = pack.ContainerWriter(write_data)
342
        writer.begin()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
343
        access = _DirectPackAccess({})
344
        access.set_writer(writer, index, (transport, packname))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
345
        return access, writer
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
346
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
347
    def make_pack_file(self):
348
        """Create a pack file with 2 records."""
349
        access, writer = self._get_access(packname='packname', index='foo')
350
        memos = []
351
        memos.extend(access.add_raw_records([('key1', 10)], '1234567890'))
352
        memos.extend(access.add_raw_records([('key2', 5)], '12345'))
353
        writer.end()
354
        return memos
355
3789.2.6 by John Arbash Meinel
Make _DirectPackAccess.reload_or_raise maintain the logic.
356
    def make_reload_func(self, return_val=True):
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
357
        reload_called = [0]
358
        def reload():
359
            reload_called[0] += 1
3789.2.6 by John Arbash Meinel
Make _DirectPackAccess.reload_or_raise maintain the logic.
360
            return return_val
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
361
        return reload_called, reload
362
3789.2.6 by John Arbash Meinel
Make _DirectPackAccess.reload_or_raise maintain the logic.
363
    def make_retry_exception(self):
364
        # We raise a real exception so that sys.exc_info() is properly
365
        # populated
366
        try:
367
            raise _TestException('foobar')
368
        except _TestException, e:
369
            retry_exc = errors.RetryWithNewPacks(reload_occurred=False,
370
                                                 exc_info=sys.exc_info())
371
        return retry_exc
372
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
373
    def test_read_from_several_packs(self):
374
        access, writer = self._get_access()
375
        memos = []
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
376
        memos.extend(access.add_raw_records([('key', 10)], '1234567890'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
377
        writer.end()
378
        access, writer = self._get_access('pack2', 'FOOBAR')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
379
        memos.extend(access.add_raw_records([('key', 5)], '12345'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
380
        writer.end()
381
        access, writer = self._get_access('pack3', 'BAZ')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
382
        memos.extend(access.add_raw_records([('key', 5)], 'alpha'))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
383
        writer.end()
384
        transport = self.get_transport()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
385
        access = _DirectPackAccess({"FOO":(transport, 'packfile'),
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
386
            "FOOBAR":(transport, 'pack2'),
387
            "BAZ":(transport, 'pack3')})
388
        self.assertEqual(['1234567890', '12345', 'alpha'],
389
            list(access.get_raw_records(memos)))
390
        self.assertEqual(['1234567890'],
391
            list(access.get_raw_records(memos[0:1])))
392
        self.assertEqual(['12345'],
393
            list(access.get_raw_records(memos[1:2])))
394
        self.assertEqual(['alpha'],
395
            list(access.get_raw_records(memos[2:3])))
396
        self.assertEqual(['1234567890', 'alpha'],
397
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
398
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
399
    def test_set_writer(self):
400
        """The writer should be settable post construction."""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
401
        access = _DirectPackAccess({})
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
402
        transport = self.get_transport()
403
        packname = 'packfile'
404
        index = 'foo'
405
        def write_data(bytes):
406
            transport.append_bytes(packname, bytes)
407
        writer = pack.ContainerWriter(write_data)
408
        writer.begin()
409
        access.set_writer(writer, index, (transport, packname))
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
410
        memos = access.add_raw_records([('key', 10)], '1234567890')
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
411
        writer.end()
412
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
413
3789.2.1 by John Arbash Meinel
_DirectPackAccess can now raise RetryWithNewPacks when we think something has happened.
414
    def test_missing_index_raises_retry(self):
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
415
        memos = self.make_pack_file()
3789.2.1 by John Arbash Meinel
_DirectPackAccess can now raise RetryWithNewPacks when we think something has happened.
416
        transport = self.get_transport()
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
417
        reload_called, reload_func = self.make_reload_func()
418
        # Note that the index key has changed from 'foo' to 'bar'
419
        access = _DirectPackAccess({'bar':(transport, 'packname')},
420
                                   reload_func=reload_func)
3789.2.1 by John Arbash Meinel
_DirectPackAccess can now raise RetryWithNewPacks when we think something has happened.
421
        e = self.assertListRaises(errors.RetryWithNewPacks,
422
                                  access.get_raw_records, memos)
423
        # Because a key was passed in which does not match our index list, we
424
        # assume that the listing was already reloaded
425
        self.assertTrue(e.reload_occurred)
426
        self.assertIsInstance(e.exc_info, tuple)
427
        self.assertIs(e.exc_info[0], KeyError)
428
        self.assertIsInstance(e.exc_info[1], KeyError)
429
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
430
    def test_missing_index_raises_key_error_with_no_reload(self):
431
        memos = self.make_pack_file()
432
        transport = self.get_transport()
433
        # Note that the index key has changed from 'foo' to 'bar'
434
        access = _DirectPackAccess({'bar':(transport, 'packname')})
435
        e = self.assertListRaises(KeyError, access.get_raw_records, memos)
436
3789.2.1 by John Arbash Meinel
_DirectPackAccess can now raise RetryWithNewPacks when we think something has happened.
437
    def test_missing_file_raises_retry(self):
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
438
        memos = self.make_pack_file()
439
        transport = self.get_transport()
440
        reload_called, reload_func = self.make_reload_func()
441
        # Note that the 'filename' has been changed to 'different-packname'
442
        access = _DirectPackAccess({'foo':(transport, 'different-packname')},
443
                                   reload_func=reload_func)
444
        e = self.assertListRaises(errors.RetryWithNewPacks,
445
                                  access.get_raw_records, memos)
446
        # The file has gone missing, so we assume we need to reload
447
        self.assertFalse(e.reload_occurred)
448
        self.assertIsInstance(e.exc_info, tuple)
449
        self.assertIs(e.exc_info[0], errors.NoSuchFile)
450
        self.assertIsInstance(e.exc_info[1], errors.NoSuchFile)
451
        self.assertEqual('different-packname', e.exc_info[1].path)
452
453
    def test_missing_file_raises_no_such_file_with_no_reload(self):
454
        memos = self.make_pack_file()
455
        transport = self.get_transport()
456
        # Note that the 'filename' has been changed to 'different-packname'
3789.2.1 by John Arbash Meinel
_DirectPackAccess can now raise RetryWithNewPacks when we think something has happened.
457
        access = _DirectPackAccess({'foo':(transport, 'different-packname')})
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
458
        e = self.assertListRaises(errors.NoSuchFile,
3789.2.1 by John Arbash Meinel
_DirectPackAccess can now raise RetryWithNewPacks when we think something has happened.
459
                                  access.get_raw_records, memos)
460
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
461
    def test_failing_readv_raises_retry(self):
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
462
        memos = self.make_pack_file()
463
        transport = self.get_transport()
464
        failing_transport = MockReadvFailingTransport(
465
                                [transport.get_bytes('packname')])
466
        reload_called, reload_func = self.make_reload_func()
467
        access = _DirectPackAccess({'foo':(failing_transport, 'packname')},
468
                                   reload_func=reload_func)
469
        # Asking for a single record will not trigger the Mock failure
470
        self.assertEqual(['1234567890'],
471
            list(access.get_raw_records(memos[:1])))
472
        self.assertEqual(['12345'],
473
            list(access.get_raw_records(memos[1:2])))
474
        # A multiple offset readv() will fail mid-way through
475
        e = self.assertListRaises(errors.RetryWithNewPacks,
476
                                  access.get_raw_records, memos)
477
        # The file has gone missing, so we assume we need to reload
478
        self.assertFalse(e.reload_occurred)
479
        self.assertIsInstance(e.exc_info, tuple)
480
        self.assertIs(e.exc_info[0], errors.NoSuchFile)
481
        self.assertIsInstance(e.exc_info[1], errors.NoSuchFile)
482
        self.assertEqual('packname', e.exc_info[1].path)
483
484
    def test_failing_readv_raises_no_such_file_with_no_reload(self):
485
        memos = self.make_pack_file()
486
        transport = self.get_transport()
487
        failing_transport = MockReadvFailingTransport(
488
                                [transport.get_bytes('packname')])
489
        reload_called, reload_func = self.make_reload_func()
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
490
        access = _DirectPackAccess({'foo':(failing_transport, 'packname')})
3789.2.3 by John Arbash Meinel
Change the mocking a bit, so we can be sure it is failing at the right time.
491
        # Asking for a single record will not trigger the Mock failure
492
        self.assertEqual(['1234567890'],
493
            list(access.get_raw_records(memos[:1])))
494
        self.assertEqual(['12345'],
495
            list(access.get_raw_records(memos[1:2])))
496
        # A multiple offset readv() will fail mid-way through
3789.2.5 by John Arbash Meinel
Change _DirectPackAccess to only raise Retry when _reload_func is defined.
497
        e = self.assertListRaises(errors.NoSuchFile,
3789.2.2 by John Arbash Meinel
Test that a readv() failing after yielding data will still raise Retry
498
                                  access.get_raw_records, memos)
499
3789.2.6 by John Arbash Meinel
Make _DirectPackAccess.reload_or_raise maintain the logic.
500
    def test_reload_or_raise_no_reload(self):
501
        access = _DirectPackAccess({}, reload_func=None)
502
        retry_exc = self.make_retry_exception()
503
        # Without a reload_func, we will just re-raise the original exception
504
        self.assertRaises(_TestException, access.reload_or_raise, retry_exc)
505
506
    def test_reload_or_raise_reload_changed(self):
507
        reload_called, reload_func = self.make_reload_func(return_val=True)
508
        access = _DirectPackAccess({}, reload_func=reload_func)
509
        retry_exc = self.make_retry_exception()
510
        access.reload_or_raise(retry_exc)
511
        self.assertEqual([1], reload_called)
512
        retry_exc.reload_occurred=True
513
        access.reload_or_raise(retry_exc)
514
        self.assertEqual([2], reload_called)
515
516
    def test_reload_or_raise_reload_no_change(self):
517
        reload_called, reload_func = self.make_reload_func(return_val=False)
518
        access = _DirectPackAccess({}, reload_func=reload_func)
519
        retry_exc = self.make_retry_exception()
520
        # If reload_occurred is False, then we consider it an error to have
521
        # reload_func() return False (no changes).
522
        self.assertRaises(_TestException, access.reload_or_raise, retry_exc)
523
        self.assertEqual([1], reload_called)
524
        retry_exc.reload_occurred=True
525
        # If reload_occurred is True, then we assume nothing changed because
526
        # it had changed earlier, but didn't change again
527
        access.reload_or_raise(retry_exc)
528
        self.assertEqual([2], reload_called)
529
3789.2.7 by John Arbash Meinel
Document what to do next
530
    # TODO: Test that KnitVersionedFiles handles RetryWithNewPacks exceptions
531
    #       and calls reload_or_raise appropriately
532
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
533
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
534
class LowLevelKnitDataTests(TestCase):
535
536
    def create_gz_content(self, text):
537
        sio = StringIO()
538
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
539
        gz_file.write(text)
540
        gz_file.close()
541
        return sio.getvalue()
542
3789.2.4 by John Arbash Meinel
Add a multiple-record test, though it isn't quite what we want for the readv tests.
543
    def make_multiple_records(self):
544
        """Create the content for multiple records."""
545
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
546
        total_txt = []
547
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
548
                                        'foo\n'
549
                                        'bar\n'
550
                                        'end rev-id-1\n'
551
                                        % (sha1sum,))
552
        record_1 = (0, len(gz_txt), sha1sum)
553
        total_txt.append(gz_txt)
554
        sha1sum = osutils.sha('baz\n').hexdigest()
555
        gz_txt = self.create_gz_content('version rev-id-2 1 %s\n'
556
                                        'baz\n'
557
                                        'end rev-id-2\n'
558
                                        % (sha1sum,))
559
        record_2 = (record_1[1], len(gz_txt), sha1sum)
560
        total_txt.append(gz_txt)
561
        return total_txt, record_1, record_2
562
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
563
    def test_valid_knit_data(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
564
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
565
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
566
                                        'foo\n'
567
                                        'bar\n'
568
                                        'end rev-id-1\n'
569
                                        % (sha1sum,))
570
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
571
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
572
        knit = KnitVersionedFiles(None, access)
573
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
574
575
        contents = list(knit._read_records_iter(records))
576
        self.assertEqual([(('rev-id-1',), ['foo\n', 'bar\n'],
577
            '4e48e2c9a3d2ca8a708cb0cc545700544efb5021')], contents)
578
579
        raw_contents = list(knit._read_records_iter_raw(records))
580
        self.assertEqual([(('rev-id-1',), gz_txt, sha1sum)], raw_contents)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
581
3789.2.4 by John Arbash Meinel
Add a multiple-record test, though it isn't quite what we want for the readv tests.
582
    def test_multiple_records_valid(self):
583
        total_txt, record_1, record_2 = self.make_multiple_records()
584
        transport = MockTransport([''.join(total_txt)])
585
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
586
        knit = KnitVersionedFiles(None, access)
587
        records = [(('rev-id-1',), (('rev-id-1',), record_1[0], record_1[1])),
588
                   (('rev-id-2',), (('rev-id-2',), record_2[0], record_2[1]))]
589
590
        contents = list(knit._read_records_iter(records))
591
        self.assertEqual([(('rev-id-1',), ['foo\n', 'bar\n'], record_1[2]),
592
                          (('rev-id-2',), ['baz\n'], record_2[2])],
593
                         contents)
594
595
        raw_contents = list(knit._read_records_iter_raw(records))
596
        self.assertEqual([(('rev-id-1',), total_txt[0], record_1[2]),
597
                          (('rev-id-2',), total_txt[1], record_2[2])],
598
                         raw_contents)
599
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
600
    def test_not_enough_lines(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
601
        sha1sum = osutils.sha('foo\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
602
        # record says 2 lines data says 1
603
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
604
                                        'foo\n'
605
                                        'end rev-id-1\n'
606
                                        % (sha1sum,))
607
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
608
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
609
        knit = KnitVersionedFiles(None, access)
610
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
611
        self.assertRaises(errors.KnitCorrupt, list,
612
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
613
614
        # read_records_iter_raw won't detect that sort of mismatch/corruption
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
615
        raw_contents = list(knit._read_records_iter_raw(records))
616
        self.assertEqual([(('rev-id-1',),  gz_txt, sha1sum)], raw_contents)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
617
618
    def test_too_many_lines(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
619
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
620
        # record says 1 lines data says 2
621
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
622
                                        'foo\n'
623
                                        'bar\n'
624
                                        'end rev-id-1\n'
625
                                        % (sha1sum,))
626
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
627
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
628
        knit = KnitVersionedFiles(None, access)
629
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
630
        self.assertRaises(errors.KnitCorrupt, list,
631
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
632
633
        # read_records_iter_raw won't detect that sort of mismatch/corruption
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
634
        raw_contents = list(knit._read_records_iter_raw(records))
635
        self.assertEqual([(('rev-id-1',), gz_txt, sha1sum)], raw_contents)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
636
637
    def test_mismatched_version_id(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
638
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
639
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
640
                                        'foo\n'
641
                                        'bar\n'
642
                                        'end rev-id-1\n'
643
                                        % (sha1sum,))
644
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
645
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
646
        knit = KnitVersionedFiles(None, access)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
647
        # We are asking for rev-id-2, but the data is rev-id-1
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
648
        records = [(('rev-id-2',), (('rev-id-2',), 0, len(gz_txt)))]
649
        self.assertRaises(errors.KnitCorrupt, list,
650
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
651
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
652
        # read_records_iter_raw detects mismatches in the header
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
653
        self.assertRaises(errors.KnitCorrupt, list,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
654
            knit._read_records_iter_raw(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
655
656
    def test_uncompressed_data(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
657
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
658
        txt = ('version rev-id-1 2 %s\n'
659
               'foo\n'
660
               'bar\n'
661
               'end rev-id-1\n'
662
               % (sha1sum,))
663
        transport = MockTransport([txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
664
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
665
        knit = KnitVersionedFiles(None, access)
666
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
667
668
        # We don't have valid gzip data ==> corrupt
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
669
        self.assertRaises(errors.KnitCorrupt, list,
670
            knit._read_records_iter(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
671
672
        # read_records_iter_raw will notice the bad data
673
        self.assertRaises(errors.KnitCorrupt, list,
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
674
            knit._read_records_iter_raw(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
675
676
    def test_corrupted_data(self):
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
677
        sha1sum = osutils.sha('foo\nbar\n').hexdigest()
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
678
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
679
                                        'foo\n'
680
                                        'bar\n'
681
                                        'end rev-id-1\n'
682
                                        % (sha1sum,))
683
        # Change 2 bytes in the middle to \xff
684
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
685
        transport = MockTransport([gz_txt])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
686
        access = _KnitKeyAccess(transport, ConstantMapper('filename'))
687
        knit = KnitVersionedFiles(None, access)
688
        records = [(('rev-id-1',), (('rev-id-1',), 0, len(gz_txt)))]
689
        self.assertRaises(errors.KnitCorrupt, list,
690
            knit._read_records_iter(records))
691
        # read_records_iter_raw will barf on bad gz data
692
        self.assertRaises(errors.KnitCorrupt, list,
693
            knit._read_records_iter_raw(records))
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
694
695
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
696
class LowLevelKnitIndexTests(TestCase):
697
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
698
    def get_knit_index(self, transport, name, mode):
699
        mapper = ConstantMapper(name)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
700
        orig = knit._load_data
701
        def reset():
702
            knit._load_data = orig
703
        self.addCleanup(reset)
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
704
        from bzrlib._knit_load_data_py import _load_data_py
705
        knit._load_data = _load_data_py
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
706
        allow_writes = lambda: 'w' in mode
707
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
708
709
    def test_create_file(self):
710
        transport = MockTransport()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
711
        index = self.get_knit_index(transport, "filename", "w")
712
        index.keys()
713
        call = transport.calls.pop(0)
714
        # call[1][1] is a StringIO - we can't test it by simple equality.
715
        self.assertEqual('put_file_non_atomic', call[0])
716
        self.assertEqual('filename.kndx', call[1][0])
717
        # With no history, _KndxIndex writes a new index:
718
        self.assertEqual(_KndxIndex.HEADER,
719
            call[1][1].getvalue())
720
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
721
722
    def test_read_utf8_version_id(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
723
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
724
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
725
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
726
            _KndxIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
727
            '%s option 0 1 :' % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
728
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
729
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
730
        # _KndxIndex is a private class, and deals in utf8 revision_ids, not
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
731
        # Unicode revision_ids.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
732
        self.assertEqual({(utf8_revision_id,):()},
733
            index.get_parent_map(index.keys()))
734
        self.assertFalse((unicode_revision_id,) in index.keys())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
735
736
    def test_read_utf8_parents(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
737
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
738
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
739
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
740
            _KndxIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
741
            "version option 0 1 .%s :" % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
742
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
743
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
744
        self.assertEqual({("version",):((utf8_revision_id,),)},
745
            index.get_parent_map(index.keys()))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
746
747
    def test_read_ignore_corrupted_lines(self):
748
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
749
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
750
            "corrupted",
751
            "corrupted options 0 1 .b .c ",
752
            "version options 0 1 :"
753
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
754
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
755
        self.assertEqual(1, len(index.keys()))
756
        self.assertEqual(set([("version",)]), index.keys())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
757
758
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
759
        transport = MockTransport(['not a bzr knit index header\n'])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
760
        index = self.get_knit_index(transport, "filename", "r")
761
        self.assertRaises(KnitHeaderError, index.keys)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
762
763
    def test_read_duplicate_entries(self):
764
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
765
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
766
            "parent options 0 1 :",
767
            "version options1 0 1 0 :",
768
            "version options2 1 2 .other :",
769
            "version options3 3 4 0 .other :"
770
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
771
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
772
        self.assertEqual(2, len(index.keys()))
2592.3.8 by Robert Collins
Remove unneeded pulib method lookup on private class _KnitIndex.
773
        # check that the index used is the first one written. (Specific
774
        # to KnitIndex style indices.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
775
        self.assertEqual("1", index._dictionary_compress([("version",)]))
776
        self.assertEqual((("version",), 3, 4), index.get_position(("version",)))
777
        self.assertEqual(["options3"], index.get_options(("version",)))
778
        self.assertEqual({("version",):(("parent",), ("other",))},
779
            index.get_parent_map([("version",)]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
780
781
    def test_read_compressed_parents(self):
782
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
783
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
784
            "a option 0 1 :",
785
            "b option 0 1 0 :",
786
            "c option 0 1 1 0 :",
787
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
788
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
789
        self.assertEqual({("b",):(("a",),), ("c",):(("b",), ("a",))},
790
            index.get_parent_map([("b",), ("c",)]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
791
792
    def test_write_utf8_version_id(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
793
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
794
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
795
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
796
            _KndxIndex.HEADER
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
797
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
798
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
799
        index.add_records([
800
            ((utf8_revision_id,), ["option"], ((utf8_revision_id,), 0, 1), [])])
801
        call = transport.calls.pop(0)
802
        # call[1][1] is a StringIO - we can't test it by simple equality.
803
        self.assertEqual('put_file_non_atomic', call[0])
804
        self.assertEqual('filename.kndx', call[1][0])
805
        # With no history, _KndxIndex writes a new index:
806
        self.assertEqual(_KndxIndex.HEADER +
807
            "\n%s option 0 1  :" % (utf8_revision_id,),
808
            call[1][1].getvalue())
809
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
810
811
    def test_write_utf8_parents(self):
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
812
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
813
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
814
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
815
            _KndxIndex.HEADER
816
            ])
817
        index = self.get_knit_index(transport, "filename", "r")
818
        index.add_records([
819
            (("version",), ["option"], (("version",), 0, 1), [(utf8_revision_id,)])])
820
        call = transport.calls.pop(0)
821
        # call[1][1] is a StringIO - we can't test it by simple equality.
822
        self.assertEqual('put_file_non_atomic', call[0])
823
        self.assertEqual('filename.kndx', call[1][0])
824
        # With no history, _KndxIndex writes a new index:
825
        self.assertEqual(_KndxIndex.HEADER +
826
            "\nversion option 0 1 .%s :" % (utf8_revision_id,),
827
            call[1][1].getvalue())
828
        self.assertEqual({'create_parent_dir': True}, call[2])
829
830
    def test_keys(self):
831
        transport = MockTransport([
832
            _KndxIndex.HEADER
833
            ])
834
        index = self.get_knit_index(transport, "filename", "r")
835
836
        self.assertEqual(set(), index.keys())
837
838
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
839
        self.assertEqual(set([("a",)]), index.keys())
840
841
        index.add_records([(("a",), ["option"], (("a",), 0, 1), [])])
842
        self.assertEqual(set([("a",)]), index.keys())
843
844
        index.add_records([(("b",), ["option"], (("b",), 0, 1), [])])
845
        self.assertEqual(set([("a",), ("b",)]), index.keys())
846
847
    def add_a_b(self, index, random_id=None):
848
        kwargs = {}
849
        if random_id is not None:
850
            kwargs["random_id"] = random_id
851
        index.add_records([
852
            (("a",), ["option"], (("a",), 0, 1), [("b",)]),
853
            (("a",), ["opt"], (("a",), 1, 2), [("c",)]),
854
            (("b",), ["option"], (("b",), 2, 3), [("a",)])
855
            ], **kwargs)
856
857
    def assertIndexIsAB(self, index):
858
        self.assertEqual({
859
            ('a',): (('c',),),
860
            ('b',): (('a',),),
861
            },
862
            index.get_parent_map(index.keys()))
863
        self.assertEqual((("a",), 1, 2), index.get_position(("a",)))
864
        self.assertEqual((("b",), 2, 3), index.get_position(("b",)))
865
        self.assertEqual(["opt"], index.get_options(("a",)))
866
867
    def test_add_versions(self):
868
        transport = MockTransport([
869
            _KndxIndex.HEADER
870
            ])
871
        index = self.get_knit_index(transport, "filename", "r")
872
873
        self.add_a_b(index)
874
        call = transport.calls.pop(0)
875
        # call[1][1] is a StringIO - we can't test it by simple equality.
876
        self.assertEqual('put_file_non_atomic', call[0])
877
        self.assertEqual('filename.kndx', call[1][0])
878
        # With no history, _KndxIndex writes a new index:
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
879
        self.assertEqual(
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
880
            _KndxIndex.HEADER +
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
881
            "\na option 0 1 .b :"
882
            "\na opt 1 2 .c :"
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
883
            "\nb option 2 3 0 :",
884
            call[1][1].getvalue())
885
        self.assertEqual({'create_parent_dir': True}, call[2])
886
        self.assertIndexIsAB(index)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
887
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
888
    def test_add_versions_random_id_is_accepted(self):
889
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
890
            _KndxIndex.HEADER
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
891
            ])
892
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
893
        self.add_a_b(index, random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
894
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
895
    def test_delay_create_and_add_versions(self):
896
        transport = MockTransport()
897
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
898
        index = self.get_knit_index(transport, "filename", "w")
899
        # dir_mode=0777)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
900
        self.assertEqual([], transport.calls)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
901
        self.add_a_b(index)
902
        #self.assertEqual(
903
        #[    {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
904
        #    kwargs)
905
        # Two calls: one during which we load the existing index (and when its
906
        # missing create it), then a second where we write the contents out.
907
        self.assertEqual(2, len(transport.calls))
908
        call = transport.calls.pop(0)
909
        self.assertEqual('put_file_non_atomic', call[0])
910
        self.assertEqual('filename.kndx', call[1][0])
911
        # With no history, _KndxIndex writes a new index:
912
        self.assertEqual(_KndxIndex.HEADER, call[1][1].getvalue())
913
        self.assertEqual({'create_parent_dir': True}, call[2])
914
        call = transport.calls.pop(0)
915
        # call[1][1] is a StringIO - we can't test it by simple equality.
916
        self.assertEqual('put_file_non_atomic', call[0])
917
        self.assertEqual('filename.kndx', call[1][0])
918
        # With no history, _KndxIndex writes a new index:
919
        self.assertEqual(
920
            _KndxIndex.HEADER +
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
921
            "\na option 0 1 .b :"
922
            "\na opt 1 2 .c :"
923
            "\nb option 2 3 0 :",
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
924
            call[1][1].getvalue())
925
        self.assertEqual({'create_parent_dir': True}, call[2])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
926
927
    def test_get_position(self):
928
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
929
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
930
            "a option 0 1 :",
931
            "b option 1 2 :"
932
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
933
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
934
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
935
        self.assertEqual((("a",), 0, 1), index.get_position(("a",)))
936
        self.assertEqual((("b",), 1, 2), index.get_position(("b",)))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
937
938
    def test_get_method(self):
939
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
940
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
941
            "a fulltext,unknown 0 1 :",
942
            "b unknown,line-delta 1 2 :",
943
            "c bad 3 4 :"
944
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
945
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
946
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
947
        self.assertEqual("fulltext", index.get_method("a"))
948
        self.assertEqual("line-delta", index.get_method("b"))
949
        self.assertRaises(errors.KnitIndexUnknownMethod, index.get_method, "c")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
950
951
    def test_get_options(self):
952
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
953
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
954
            "a opt1 0 1 :",
955
            "b opt2,opt3 1 2 :"
956
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
957
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
958
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
959
        self.assertEqual(["opt1"], index.get_options("a"))
960
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
961
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
962
    def test_get_parent_map(self):
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
963
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
964
            _KndxIndex.HEADER,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
965
            "a option 0 1 :",
966
            "b option 1 2 0 .c :",
967
            "c option 1 2 1 0 .e :"
968
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
969
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
970
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
971
        self.assertEqual({
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
972
            ("a",):(),
973
            ("b",):(("a",), ("c",)),
974
            ("c",):(("b",), ("a",), ("e",)),
975
            }, index.get_parent_map(index.keys()))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
976
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
977
    def test_impossible_parent(self):
978
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
979
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
980
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
981
            "a option 0 1 :",
982
            "b option 0 1 4 :"  # We don't have a 4th record
983
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
984
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
985
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
986
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
987
        except TypeError, e:
988
            if (str(e) == ('exceptions must be strings, classes, or instances,'
989
                           ' not exceptions.IndexError')
990
                and sys.version_info[0:2] >= (2,5)):
991
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
992
                                  ' raising new style exceptions with python'
993
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
994
            else:
995
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
996
997
    def test_corrupted_parent(self):
998
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
999
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
1000
            "a option 0 1 :",
1001
            "b option 0 1 :",
1002
            "c option 0 1 1v :", # Can't have a parent of '1v'
1003
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1004
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
1005
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1006
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
1007
        except TypeError, e:
1008
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1009
                           ' not exceptions.ValueError')
1010
                and sys.version_info[0:2] >= (2,5)):
1011
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1012
                                  ' raising new style exceptions with python'
1013
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
1014
            else:
1015
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
1016
1017
    def test_corrupted_parent_in_list(self):
1018
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1019
            _KndxIndex.HEADER,
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
1020
            "a option 0 1 :",
1021
            "b option 0 1 :",
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
1022
            "c option 0 1 1 v :", # Can't have a parent of 'v'
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
1023
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1024
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
1025
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1026
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
1027
        except TypeError, e:
1028
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1029
                           ' not exceptions.ValueError')
1030
                and sys.version_info[0:2] >= (2,5)):
1031
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1032
                                  ' raising new style exceptions with python'
1033
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
1034
            else:
1035
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
1036
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1037
    def test_invalid_position(self):
1038
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1039
            _KndxIndex.HEADER,
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1040
            "a option 1v 1 :",
1041
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1042
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1043
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1044
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1045
        except TypeError, e:
1046
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1047
                           ' not exceptions.ValueError')
1048
                and sys.version_info[0:2] >= (2,5)):
1049
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1050
                                  ' raising new style exceptions with python'
1051
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
1052
            else:
1053
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1054
1055
    def test_invalid_size(self):
1056
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1057
            _KndxIndex.HEADER,
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1058
            "a option 1 1v :",
1059
            ])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1060
        index = self.get_knit_index(transport, 'filename', 'r')
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1061
        try:
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1062
            self.assertRaises(errors.KnitCorrupt, index.keys)
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1063
        except TypeError, e:
1064
            if (str(e) == ('exceptions must be strings, classes, or instances,'
1065
                           ' not exceptions.ValueError')
1066
                and sys.version_info[0:2] >= (2,5)):
1067
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
1068
                                  ' raising new style exceptions with python'
1069
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
1070
            else:
1071
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
1072
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1073
    def test_short_line(self):
1074
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1075
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1076
            "a option 0 10  :",
1077
            "b option 10 10 0", # This line isn't terminated, ignored
1078
            ])
1079
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1080
        self.assertEqual(set([('a',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1081
1082
    def test_skip_incomplete_record(self):
1083
        # A line with bogus data should just be skipped
1084
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1085
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1086
            "a option 0 10  :",
1087
            "b option 10 10 0", # This line isn't terminated, ignored
1088
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1089
            ])
1090
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1091
        self.assertEqual(set([('a',), ('c',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1092
1093
    def test_trailing_characters(self):
1094
        # A line with bogus data should just be skipped
1095
        transport = MockTransport([
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1096
            _KndxIndex.HEADER,
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1097
            "a option 0 10  :",
1098
            "b option 10 10 0 :a", # This line has extra trailing characters
1099
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1100
            ])
1101
        index = self.get_knit_index(transport, "filename", "r")
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1102
        self.assertEqual(set([('a',), ('c',)]), index.keys())
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
1103
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
1104
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1105
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
1106
1107
    _test_needs_features = [CompiledKnitFeature]
1108
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1109
    def get_knit_index(self, transport, name, mode):
1110
        mapper = ConstantMapper(name)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1111
        orig = knit._load_data
1112
        def reset():
1113
            knit._load_data = orig
1114
        self.addCleanup(reset)
2484.1.12 by John Arbash Meinel
Switch the layout to use a matching _knit_load_data_py.py and _knit_load_data_c.pyx
1115
        from bzrlib._knit_load_data_c import _load_data_c
1116
        knit._load_data = _load_data_c
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1117
        allow_writes = lambda: mode == 'w'
1118
        return _KndxIndex(transport, mapper, lambda:None, allow_writes, lambda:True)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1119
1120
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1121
class KnitTests(TestCaseWithTransport):
1122
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1123
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1124
    def make_test_knit(self, annotate=False, name='test'):
1125
        mapper = ConstantMapper(name)
1126
        return make_file_factory(annotate, mapper)(self.get_transport())
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
1127
1128
3787.1.1 by Robert Collins
Embed the failed text in sha1 knit errors.
1129
class TestBadShaError(KnitTests):
1130
    """Tests for handling of sha errors."""
1131
1132
    def test_exception_has_text(self):
1133
        # having the failed text included in the error allows for recovery.
1134
        source = self.make_test_knit()
1135
        target = self.make_test_knit(name="target")
1136
        if not source._max_delta_chain:
1137
            raise TestNotApplicable(
1138
                "cannot get delta-caused sha failures without deltas.")
1139
        # create a basis
1140
        basis = ('basis',)
1141
        broken = ('broken',)
1142
        source.add_lines(basis, (), ['foo\n'])
1143
        source.add_lines(broken, (basis,), ['foo\n', 'bar\n'])
1144
        # Seed target with a bad basis text
1145
        target.add_lines(basis, (), ['gam\n'])
1146
        target.insert_record_stream(
1147
            source.get_record_stream([broken], 'unordered', False))
1148
        err = self.assertRaises(errors.KnitCorrupt,
1149
            target.get_record_stream([broken], 'unordered', True).next)
1150
        self.assertEqual(['gam\n', 'bar\n'], err.content)
3787.1.2 by Robert Collins
Ensure SHA1KnitCorrupt formats ok.
1151
        # Test for formatting with live data
1152
        self.assertStartsWith(str(err), "Knit ")
3787.1.1 by Robert Collins
Embed the failed text in sha1 knit errors.
1153
1154
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1155
class TestKnitIndex(KnitTests):
1156
1157
    def test_add_versions_dictionary_compresses(self):
1158
        """Adding versions to the index should update the lookup dict"""
1159
        knit = self.make_test_knit()
1160
        idx = knit._index
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1161
        idx.add_records([(('a-1',), ['fulltext'], (('a-1',), 0, 0), [])])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1162
        self.check_file_contents('test.kndx',
1163
            '# bzr knit index 8\n'
1164
            '\n'
1165
            'a-1 fulltext 0 0  :'
1166
            )
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1167
        idx.add_records([
1168
            (('a-2',), ['fulltext'], (('a-2',), 0, 0), [('a-1',)]),
1169
            (('a-3',), ['fulltext'], (('a-3',), 0, 0), [('a-2',)]),
1170
            ])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1171
        self.check_file_contents('test.kndx',
1172
            '# bzr knit index 8\n'
1173
            '\n'
1174
            'a-1 fulltext 0 0  :\n'
1175
            'a-2 fulltext 0 0 0 :\n'
1176
            'a-3 fulltext 0 0 1 :'
1177
            )
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1178
        self.assertEqual(set([('a-3',), ('a-1',), ('a-2',)]), idx.keys())
1179
        self.assertEqual({
1180
            ('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False)),
1181
            ('a-2',): ((('a-2',), 0, 0), None, (('a-1',),), ('fulltext', False)),
1182
            ('a-3',): ((('a-3',), 0, 0), None, (('a-2',),), ('fulltext', False)),
1183
            }, idx.get_build_details(idx.keys()))
1184
        self.assertEqual({('a-1',):(),
1185
            ('a-2',):(('a-1',),),
1186
            ('a-3',):(('a-2',),),},
1187
            idx.get_parent_map(idx.keys()))
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1188
1189
    def test_add_versions_fails_clean(self):
1190
        """If add_versions fails in the middle, it restores a pristine state.
1191
1192
        Any modifications that are made to the index are reset if all versions
1193
        cannot be added.
1194
        """
1195
        # This cheats a little bit by passing in a generator which will
1196
        # raise an exception before the processing finishes
1197
        # Other possibilities would be to have an version with the wrong number
1198
        # of entries, or to make the backing transport unable to write any
1199
        # files.
1200
1201
        knit = self.make_test_knit()
1202
        idx = knit._index
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1203
        idx.add_records([(('a-1',), ['fulltext'], (('a-1',), 0, 0), [])])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1204
1205
        class StopEarly(Exception):
1206
            pass
1207
1208
        def generate_failure():
1209
            """Add some entries and then raise an exception"""
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1210
            yield (('a-2',), ['fulltext'], (None, 0, 0), ('a-1',))
1211
            yield (('a-3',), ['fulltext'], (None, 0, 0), ('a-2',))
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1212
            raise StopEarly()
1213
1214
        # Assert the pre-condition
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1215
        def assertA1Only():
1216
            self.assertEqual(set([('a-1',)]), set(idx.keys()))
1217
            self.assertEqual(
1218
                {('a-1',): ((('a-1',), 0, 0), None, (), ('fulltext', False))},
1219
                idx.get_build_details([('a-1',)]))
1220
            self.assertEqual({('a-1',):()}, idx.get_parent_map(idx.keys()))
1221
1222
        assertA1Only()
1223
        self.assertRaises(StopEarly, idx.add_records, generate_failure())
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1224
        # And it shouldn't be modified
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1225
        assertA1Only()
2171.1.1 by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt.
1226
1227
    def test_knit_index_ignores_empty_files(self):
1228
        # There was a race condition in older bzr, where a ^C at the right time
1229
        # could leave an empty .kndx file, which bzr would later claim was a
1230
        # corrupted file since the header was not present. In reality, the file
1231
        # just wasn't created, so it should be ignored.
1232
        t = get_transport('.')
1233
        t.put_bytes('test.kndx', '')
1234
1235
        knit = self.make_test_knit()
1236
1237
    def test_knit_index_checks_header(self):
1238
        t = get_transport('.')
1239
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1240
        k = self.make_test_knit()
1241
        self.assertRaises(KnitHeaderError, k.keys)
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1242
1243
1244
class TestGraphIndexKnit(KnitTests):
1245
    """Tests for knits using a GraphIndex rather than a KnitIndex."""
1246
1247
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1248
        builder = GraphIndexBuilder(ref_lists)
1249
        for node, references, value in nodes:
1250
            builder.add_node(node, references, value)
1251
        stream = builder.finish()
1252
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1253
        size = trans.put_file(name, stream)
1254
        return GraphIndex(trans, name, size)
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1255
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1256
    def two_graph_index(self, deltas=False, catch_adds=False):
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1257
        """Build a two-graph index.
1258
1259
        :param deltas: If true, use underlying indices with two node-ref
1260
            lists and 'parent' set to a delta-compressed against tail.
1261
        """
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1262
        # build a complex graph across several indices.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1263
        if deltas:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1264
            # delta compression inn the index
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1265
            index1 = self.make_g_index('1', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1266
                (('tip', ), 'N0 100', ([('parent', )], [], )),
1267
                (('tail', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1268
            index2 = self.make_g_index('2', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1269
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], [('tail', )])),
1270
                (('separate', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1271
        else:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1272
            # just blob location and graph in the index.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1273
            index1 = self.make_g_index('1', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1274
                (('tip', ), 'N0 100', ([('parent', )], )),
1275
                (('tail', ), '', ([], ))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1276
            index2 = self.make_g_index('2', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1277
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], )),
1278
                (('separate', ), '', ([], ))])
2592.3.2 by Robert Collins
Implement a get_graph for a new KnitGraphIndex that will implement a KnitIndex on top of the GraphIndex API.
1279
        combined_index = CombinedGraphIndex([index1, index2])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1280
        if catch_adds:
1281
            self.combined_index = combined_index
1282
            self.caught_entries = []
1283
            add_callback = self.catch_add
1284
        else:
1285
            add_callback = None
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1286
        return _KnitGraphIndex(combined_index, lambda:True, deltas=deltas,
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1287
            add_callback=add_callback)
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
1288
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1289
    def test_keys(self):
1290
        index = self.two_graph_index()
1291
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
1292
            set(index.keys()))
2592.3.9 by Robert Collins
Implement KnitGraphIndex.has_version.
1293
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
1294
    def test_get_position(self):
1295
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1296
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position(('tip',)))
1297
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position(('parent',)))
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
1298
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1299
    def test_get_method_deltas(self):
1300
        index = self.two_graph_index(deltas=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1301
        self.assertEqual('fulltext', index.get_method(('tip',)))
1302
        self.assertEqual('line-delta', index.get_method(('parent',)))
2592.3.11 by Robert Collins
Implement KnitGraphIndex.get_method.
1303
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1304
    def test_get_method_no_deltas(self):
1305
        # check that the parent-history lookup is ignored with deltas=False.
1306
        index = self.two_graph_index(deltas=False)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1307
        self.assertEqual('fulltext', index.get_method(('tip',)))
1308
        self.assertEqual('fulltext', index.get_method(('parent',)))
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
1309
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1310
    def test_get_options_deltas(self):
1311
        index = self.two_graph_index(deltas=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1312
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1313
        self.assertEqual(['line-delta'], index.get_options(('parent',)))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1314
1315
    def test_get_options_no_deltas(self):
1316
        # check that the parent-history lookup is ignored with deltas=False.
1317
        index = self.two_graph_index(deltas=False)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1318
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1319
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
1320
1321
    def test_get_parent_map(self):
1322
        index = self.two_graph_index()
1323
        self.assertEqual({('parent',):(('tail',), ('ghost',))},
1324
            index.get_parent_map([('parent',), ('ghost',)]))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
1325
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1326
    def catch_add(self, entries):
1327
        self.caught_entries.append(entries)
1328
1329
    def test_add_no_callback_errors(self):
1330
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1331
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1332
            [(('new',), 'fulltext,no-eol', (None, 50, 60), ['separate'])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1333
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1334
    def test_add_version_smoke(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1335
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1336
        index.add_records([(('new',), 'fulltext,no-eol', (None, 50, 60),
1337
            [('separate',)])])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1338
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1339
            self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1340
1341
    def test_add_version_delta_not_delta_index(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1342
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1343
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1344
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1345
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1346
1347
    def test_add_version_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1348
        index = self.two_graph_index(catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1349
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1350
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
1351
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [('parent',)])])
1352
        # position/length are ignored (because each pack could have fulltext or
1353
        # delta, and be at a different position.
1354
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100),
1355
            [('parent',)])])
1356
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000),
1357
            [('parent',)])])
1358
        # but neither should have added data:
1359
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1360
        
1361
    def test_add_version_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1362
        index = self.two_graph_index(deltas=True, catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1363
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1364
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1365
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1366
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1367
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
1368
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1369
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1370
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1371
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1372
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1373
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1374
        
1375
    def test_add_versions_nodeltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1376
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1377
        index.add_records([
1378
                (('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)]),
1379
                (('new2',), 'fulltext', (None, 0, 6), [('new',)]),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1380
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1381
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
1382
            (('new2', ), ' 0 6', ((('new',),),))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1383
            sorted(self.caught_entries[0]))
1384
        self.assertEqual(1, len(self.caught_entries))
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1385
1386
    def test_add_versions_deltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1387
        index = self.two_graph_index(deltas=True, catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1388
        index.add_records([
1389
                (('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)]),
1390
                (('new2',), 'line-delta', (None, 0, 6), [('new',)]),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1391
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1392
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
1393
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1394
            sorted(self.caught_entries[0]))
1395
        self.assertEqual(1, len(self.caught_entries))
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1396
1397
    def test_add_versions_delta_not_delta_index(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1398
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1399
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1400
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1401
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1402
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1403
    def test_add_versions_random_id_accepted(self):
1404
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1405
        index.add_records([], random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1406
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1407
    def test_add_versions_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1408
        index = self.two_graph_index(catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1409
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1410
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100),
1411
            [('parent',)])])
1412
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100),
1413
            [('parent',)])])
1414
        # position/length are ignored (because each pack could have fulltext or
1415
        # delta, and be at a different position.
1416
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100),
1417
            [('parent',)])])
1418
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000),
1419
            [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1420
        # but neither should have added data.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1421
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1422
        
1423
    def test_add_versions_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1424
        index = self.two_graph_index(deltas=True, catch_adds=True)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1425
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1426
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1427
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
1428
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1429
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [('parent',)])])
1430
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1431
            [(('tip',), 'fulltext', (None, 0, 100), [('parent',)])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1432
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1433
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1434
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
1435
        # change options in the second record
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1436
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1437
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)]),
1438
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
1439
        self.assertEqual([], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1440
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1441
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1442
class TestNoParentsGraphIndexKnit(KnitTests):
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1443
    """Tests for knits using _KnitGraphIndex with no parents."""
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1444
1445
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1446
        builder = GraphIndexBuilder(ref_lists)
1447
        for node, references in nodes:
1448
            builder.add_node(node, references)
1449
        stream = builder.finish()
1450
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
1451
        size = trans.put_file(name, stream)
1452
        return GraphIndex(trans, name, size)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1453
1454
    def test_parents_deltas_incompatible(self):
1455
        index = CombinedGraphIndex([])
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1456
        self.assertRaises(errors.KnitError, _KnitGraphIndex, lambda:True,
1457
            index, deltas=True, parents=False)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1458
1459
    def two_graph_index(self, catch_adds=False):
1460
        """Build a two-graph index.
1461
1462
        :param deltas: If true, use underlying indices with two node-ref
1463
            lists and 'parent' set to a delta-compressed against tail.
1464
        """
1465
        # put several versions in the index.
1466
        index1 = self.make_g_index('1', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1467
            (('tip', ), 'N0 100'),
1468
            (('tail', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1469
        index2 = self.make_g_index('2', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1470
            (('parent', ), ' 100 78'),
1471
            (('separate', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1472
        combined_index = CombinedGraphIndex([index1, index2])
1473
        if catch_adds:
1474
            self.combined_index = combined_index
1475
            self.caught_entries = []
1476
            add_callback = self.catch_add
1477
        else:
1478
            add_callback = None
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1479
        return _KnitGraphIndex(combined_index, lambda:True, parents=False,
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1480
            add_callback=add_callback)
1481
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1482
    def test_keys(self):
1483
        index = self.two_graph_index()
1484
        self.assertEqual(set([('tail',), ('tip',), ('parent',), ('separate',)]),
1485
            set(index.keys()))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1486
1487
    def test_get_position(self):
1488
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1489
        self.assertEqual((index._graph_index._indices[0], 0, 100),
1490
            index.get_position(('tip',)))
1491
        self.assertEqual((index._graph_index._indices[1], 100, 78),
1492
            index.get_position(('parent',)))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1493
1494
    def test_get_method(self):
1495
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1496
        self.assertEqual('fulltext', index.get_method(('tip',)))
1497
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1498
1499
    def test_get_options(self):
1500
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1501
        self.assertEqual(['fulltext', 'no-eol'], index.get_options(('tip',)))
1502
        self.assertEqual(['fulltext'], index.get_options(('parent',)))
1503
1504
    def test_get_parent_map(self):
1505
        index = self.two_graph_index()
1506
        self.assertEqual({('parent',):None},
1507
            index.get_parent_map([('parent',), ('ghost',)]))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1508
1509
    def catch_add(self, entries):
1510
        self.caught_entries.append(entries)
1511
1512
    def test_add_no_callback_errors(self):
1513
        index = self.two_graph_index()
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1514
        self.assertRaises(errors.ReadOnlyError, index.add_records,
1515
            [(('new',), 'fulltext,no-eol', (None, 50, 60), [('separate',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1516
1517
    def test_add_version_smoke(self):
1518
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1519
        index.add_records([(('new',), 'fulltext,no-eol', (None, 50, 60), [])])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1520
        self.assertEqual([[(('new', ), 'N50 60')]],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1521
            self.caught_entries)
1522
1523
    def test_add_version_delta_not_delta_index(self):
1524
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1525
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1526
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1527
        self.assertEqual([], self.caught_entries)
1528
1529
    def test_add_version_same_dup(self):
1530
        index = self.two_graph_index(catch_adds=True)
1531
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1532
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1533
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [])])
1534
        # position/length are ignored (because each pack could have fulltext or
1535
        # delta, and be at a different position.
1536
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100), [])])
1537
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1538
        # but neither should have added data.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1539
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1540
        
1541
    def test_add_version_different_dup(self):
1542
        index = self.two_graph_index(catch_adds=True)
1543
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1544
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1545
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1546
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1547
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [])])
1548
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1549
            [(('tip',), 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1550
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1551
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1552
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1553
        self.assertEqual([], self.caught_entries)
1554
        
1555
    def test_add_versions(self):
1556
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1557
        index.add_records([
1558
                (('new',), 'fulltext,no-eol', (None, 50, 60), []),
1559
                (('new2',), 'fulltext', (None, 0, 6), []),
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1560
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1561
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1562
            sorted(self.caught_entries[0]))
1563
        self.assertEqual(1, len(self.caught_entries))
1564
1565
    def test_add_versions_delta_not_delta_index(self):
1566
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1567
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1568
            [(('new',), 'no-eol,line-delta', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1569
        self.assertEqual([], self.caught_entries)
1570
1571
    def test_add_versions_parents_not_parents_index(self):
1572
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1573
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1574
            [(('new',), 'no-eol,fulltext', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1575
        self.assertEqual([], self.caught_entries)
1576
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1577
    def test_add_versions_random_id_accepted(self):
1578
        index = self.two_graph_index(catch_adds=True)
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1579
        index.add_records([], random_id=True)
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
1580
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1581
    def test_add_versions_same_dup(self):
1582
        index = self.two_graph_index(catch_adds=True)
1583
        # options can be spelt two different ways
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1584
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 100), [])])
1585
        index.add_records([(('tip',), 'no-eol,fulltext', (None, 0, 100), [])])
1586
        # position/length are ignored (because each pack could have fulltext or
1587
        # delta, and be at a different position.
1588
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 50, 100), [])])
1589
        index.add_records([(('tip',), 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1590
        # but neither should have added data.
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1591
        self.assertEqual([[], [], [], []], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1592
        
1593
    def test_add_versions_different_dup(self):
1594
        index = self.two_graph_index(catch_adds=True)
1595
        # change options
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1596
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1597
            [(('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
1598
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1599
            [(('tip',), 'line-delta,no-eol', (None, 0, 100), [])])
1600
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1601
            [(('tip',), 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1602
        # parents
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1603
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1604
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), [('parent',)])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1605
        # change options in the second record
3350.6.4 by Robert Collins
First cut at pluralised VersionedFiles. Some rather massive API incompatabilities, primarily because of the difficulty of coherence among competing stores.
1606
        self.assertRaises(errors.KnitCorrupt, index.add_records,
1607
            [(('tip',), 'fulltext,no-eol', (None, 0, 100), []),
1608
             (('tip',), 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
1609
        self.assertEqual([], self.caught_entries)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1610
1611
1612
class TestStacking(KnitTests):
1613
1614
    def get_basis_and_test_knit(self):
1615
        basis = self.make_test_knit(name='basis')
3350.8.2 by Robert Collins
stacked get_parent_map.
1616
        basis = RecordingVersionedFilesDecorator(basis)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1617
        test = self.make_test_knit(name='test')
1618
        test.add_fallback_versioned_files(basis)
1619
        return basis, test
1620
1621
    def test_add_fallback_versioned_files(self):
1622
        basis = self.make_test_knit(name='basis')
1623
        test = self.make_test_knit(name='test')
1624
        # It must not error; other tests test that the fallback is referred to
1625
        # when accessing data.
1626
        test.add_fallback_versioned_files(basis)
1627
1628
    def test_add_lines(self):
3350.8.9 by Robert Collins
define behaviour for add_lines with stacked storage.
1629
        # lines added to the test are not added to the basis
1630
        basis, test = self.get_basis_and_test_knit()
1631
        key = ('foo',)
1632
        key_basis = ('bar',)
1633
        key_cross_border = ('quux',)
1634
        key_delta = ('zaphod',)
1635
        test.add_lines(key, (), ['foo\n'])
1636
        self.assertEqual({}, basis.get_parent_map([key]))
1637
        # lines added to the test that reference across the stack do a
1638
        # fulltext.
1639
        basis.add_lines(key_basis, (), ['foo\n'])
1640
        basis.calls = []
1641
        test.add_lines(key_cross_border, (key_basis,), ['foo\n'])
1642
        self.assertEqual('fulltext', test._index.get_method(key_cross_border))
1643
        self.assertEqual([("get_parent_map", set([key_basis]))], basis.calls)
1644
        # Subsequent adds do delta.
3350.8.14 by Robert Collins
Review feedback.
1645
        basis.calls = []
3350.8.9 by Robert Collins
define behaviour for add_lines with stacked storage.
1646
        test.add_lines(key_delta, (key_cross_border,), ['foo\n'])
1647
        self.assertEqual('line-delta', test._index.get_method(key_delta))
1648
        self.assertEqual([], basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1649
1650
    def test_annotate(self):
3350.8.8 by Robert Collins
Stacking and knits don't play nice for annotation yet.
1651
        # annotations from the test knit are answered without asking the basis
1652
        basis, test = self.get_basis_and_test_knit()
1653
        key = ('foo',)
1654
        key_basis = ('bar',)
1655
        key_missing = ('missing',)
1656
        test.add_lines(key, (), ['foo\n'])
1657
        details = test.annotate(key)
1658
        self.assertEqual([(key, 'foo\n')], details)
1659
        self.assertEqual([], basis.calls)
1660
        # But texts that are not in the test knit are looked for in the basis
1661
        # directly.
1662
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
1663
        basis.calls = []
1664
        details = test.annotate(key_basis)
1665
        self.assertEqual([(key_basis, 'foo\n'), (key_basis, 'bar\n')], details)
3350.9.1 by Robert Collins
Redo annotate more simply, using just the public interfaces for VersionedFiles.
1666
        # Not optimised to date:
1667
        # self.assertEqual([("annotate", key_basis)], basis.calls)
1668
        self.assertEqual([('get_parent_map', set([key_basis])),
1669
            ('get_parent_map', set([key_basis])),
1670
            ('get_parent_map', set([key_basis])),
1671
            ('get_record_stream', [key_basis], 'unordered', True)],
1672
            basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1673
1674
    def test_check(self):
3517.4.19 by Martin Pool
Update test for knit.check() to expect it to recurse into fallback vfs
1675
        # At the moment checking a stacked knit does implicitly check the
1676
        # fallback files.  
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1677
        basis, test = self.get_basis_and_test_knit()
1678
        test.check()
1679
1680
    def test_get_parent_map(self):
3350.8.2 by Robert Collins
stacked get_parent_map.
1681
        # parents in the test knit are answered without asking the basis
1682
        basis, test = self.get_basis_and_test_knit()
1683
        key = ('foo',)
1684
        key_basis = ('bar',)
1685
        key_missing = ('missing',)
1686
        test.add_lines(key, (), [])
1687
        parent_map = test.get_parent_map([key])
1688
        self.assertEqual({key: ()}, parent_map)
1689
        self.assertEqual([], basis.calls)
1690
        # But parents that are not in the test knit are looked for in the basis
1691
        basis.add_lines(key_basis, (), [])
1692
        basis.calls = []
1693
        parent_map = test.get_parent_map([key, key_basis, key_missing])
1694
        self.assertEqual({key: (),
1695
            key_basis: ()}, parent_map)
1696
        self.assertEqual([("get_parent_map", set([key_basis, key_missing]))],
1697
            basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1698
3350.8.7 by Robert Collins
get_record_stream for fulltexts working (but note extreme memory use!).
1699
    def test_get_record_stream_unordered_fulltexts(self):
1700
        # records from the test knit are answered without asking the basis:
1701
        basis, test = self.get_basis_and_test_knit()
1702
        key = ('foo',)
1703
        key_basis = ('bar',)
1704
        key_missing = ('missing',)
1705
        test.add_lines(key, (), ['foo\n'])
1706
        records = list(test.get_record_stream([key], 'unordered', True))
1707
        self.assertEqual(1, len(records))
1708
        self.assertEqual([], basis.calls)
1709
        # Missing (from test knit) objects are retrieved from the basis:
1710
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
1711
        basis.calls = []
1712
        records = list(test.get_record_stream([key_basis, key_missing],
1713
            'unordered', True))
1714
        self.assertEqual(2, len(records))
1715
        calls = list(basis.calls)
1716
        for record in records:
1717
            self.assertSubset([record.key], (key_basis, key_missing))
1718
            if record.key == key_missing:
1719
                self.assertIsInstance(record, AbsentContentFactory)
1720
            else:
1721
                reference = list(basis.get_record_stream([key_basis],
1722
                    'unordered', True))[0]
1723
                self.assertEqual(reference.key, record.key)
1724
                self.assertEqual(reference.sha1, record.sha1)
1725
                self.assertEqual(reference.storage_kind, record.storage_kind)
1726
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
1727
                    record.get_bytes_as(record.storage_kind))
1728
                self.assertEqual(reference.get_bytes_as('fulltext'),
1729
                    record.get_bytes_as('fulltext'))
3350.8.14 by Robert Collins
Review feedback.
1730
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.7 by Robert Collins
get_record_stream for fulltexts working (but note extreme memory use!).
1731
        # ask which fallbacks have which parents.
1732
        self.assertEqual([
1733
            ("get_parent_map", set([key_basis, key_missing])),
1734
            ("get_record_stream", [key_basis], 'unordered', True)],
1735
            calls)
1736
1737
    def test_get_record_stream_ordered_fulltexts(self):
1738
        # ordering is preserved down into the fallback store.
1739
        basis, test = self.get_basis_and_test_knit()
1740
        key = ('foo',)
1741
        key_basis = ('bar',)
1742
        key_basis_2 = ('quux',)
1743
        key_missing = ('missing',)
1744
        test.add_lines(key, (key_basis,), ['foo\n'])
1745
        # Missing (from test knit) objects are retrieved from the basis:
1746
        basis.add_lines(key_basis, (key_basis_2,), ['foo\n', 'bar\n'])
1747
        basis.add_lines(key_basis_2, (), ['quux\n'])
1748
        basis.calls = []
1749
        # ask for in non-topological order
1750
        records = list(test.get_record_stream(
1751
            [key, key_basis, key_missing, key_basis_2], 'topological', True))
1752
        self.assertEqual(4, len(records))
1753
        results = []
1754
        for record in records:
1755
            self.assertSubset([record.key],
1756
                (key_basis, key_missing, key_basis_2, key))
1757
            if record.key == key_missing:
1758
                self.assertIsInstance(record, AbsentContentFactory)
1759
            else:
1760
                results.append((record.key, record.sha1, record.storage_kind,
1761
                    record.get_bytes_as('fulltext')))
1762
        calls = list(basis.calls)
1763
        order = [record[0] for record in results]
1764
        self.assertEqual([key_basis_2, key_basis, key], order)
1765
        for result in results:
1766
            if result[0] == key:
1767
                source = test
1768
            else:
1769
                source = basis
1770
            record = source.get_record_stream([result[0]], 'unordered',
1771
                True).next()
1772
            self.assertEqual(record.key, result[0])
1773
            self.assertEqual(record.sha1, result[1])
1774
            self.assertEqual(record.storage_kind, result[2])
1775
            self.assertEqual(record.get_bytes_as('fulltext'), result[3])
3350.8.14 by Robert Collins
Review feedback.
1776
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.7 by Robert Collins
get_record_stream for fulltexts working (but note extreme memory use!).
1777
        # ask which fallbacks have which parents.
1778
        self.assertEqual([
1779
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
1780
            # unordered is asked for by the underlying worker as it still
1781
            # buffers everything while answering - which is a problem!
1782
            ("get_record_stream", [key_basis_2, key_basis], 'unordered', True)],
1783
            calls)
1784
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
1785
    def test_get_record_stream_unordered_deltas(self):
1786
        # records from the test knit are answered without asking the basis:
1787
        basis, test = self.get_basis_and_test_knit()
1788
        key = ('foo',)
1789
        key_basis = ('bar',)
1790
        key_missing = ('missing',)
1791
        test.add_lines(key, (), ['foo\n'])
1792
        records = list(test.get_record_stream([key], 'unordered', False))
1793
        self.assertEqual(1, len(records))
1794
        self.assertEqual([], basis.calls)
1795
        # Missing (from test knit) objects are retrieved from the basis:
1796
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
1797
        basis.calls = []
1798
        records = list(test.get_record_stream([key_basis, key_missing],
1799
            'unordered', False))
1800
        self.assertEqual(2, len(records))
1801
        calls = list(basis.calls)
1802
        for record in records:
1803
            self.assertSubset([record.key], (key_basis, key_missing))
1804
            if record.key == key_missing:
1805
                self.assertIsInstance(record, AbsentContentFactory)
1806
            else:
1807
                reference = list(basis.get_record_stream([key_basis],
1808
                    'unordered', False))[0]
1809
                self.assertEqual(reference.key, record.key)
1810
                self.assertEqual(reference.sha1, record.sha1)
1811
                self.assertEqual(reference.storage_kind, record.storage_kind)
1812
                self.assertEqual(reference.get_bytes_as(reference.storage_kind),
1813
                    record.get_bytes_as(record.storage_kind))
3350.8.14 by Robert Collins
Review feedback.
1814
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
1815
        # ask which fallbacks have which parents.
1816
        self.assertEqual([
1817
            ("get_parent_map", set([key_basis, key_missing])),
1818
            ("get_record_stream", [key_basis], 'unordered', False)],
1819
            calls)
1820
1821
    def test_get_record_stream_ordered_deltas(self):
1822
        # ordering is preserved down into the fallback store.
1823
        basis, test = self.get_basis_and_test_knit()
1824
        key = ('foo',)
1825
        key_basis = ('bar',)
1826
        key_basis_2 = ('quux',)
1827
        key_missing = ('missing',)
1828
        test.add_lines(key, (key_basis,), ['foo\n'])
1829
        # Missing (from test knit) objects are retrieved from the basis:
1830
        basis.add_lines(key_basis, (key_basis_2,), ['foo\n', 'bar\n'])
1831
        basis.add_lines(key_basis_2, (), ['quux\n'])
1832
        basis.calls = []
1833
        # ask for in non-topological order
1834
        records = list(test.get_record_stream(
1835
            [key, key_basis, key_missing, key_basis_2], 'topological', False))
1836
        self.assertEqual(4, len(records))
1837
        results = []
1838
        for record in records:
1839
            self.assertSubset([record.key],
1840
                (key_basis, key_missing, key_basis_2, key))
1841
            if record.key == key_missing:
1842
                self.assertIsInstance(record, AbsentContentFactory)
1843
            else:
1844
                results.append((record.key, record.sha1, record.storage_kind,
1845
                    record.get_bytes_as(record.storage_kind)))
1846
        calls = list(basis.calls)
1847
        order = [record[0] for record in results]
1848
        self.assertEqual([key_basis_2, key_basis, key], order)
1849
        for result in results:
1850
            if result[0] == key:
1851
                source = test
1852
            else:
1853
                source = basis
1854
            record = source.get_record_stream([result[0]], 'unordered',
1855
                False).next()
1856
            self.assertEqual(record.key, result[0])
1857
            self.assertEqual(record.sha1, result[1])
1858
            self.assertEqual(record.storage_kind, result[2])
1859
            self.assertEqual(record.get_bytes_as(record.storage_kind), result[3])
3350.8.14 by Robert Collins
Review feedback.
1860
        # It's not strictly minimal, but it seems reasonable for now for it to
3350.8.6 by Robert Collins
get_record_stream stacking for delta access.
1861
        # ask which fallbacks have which parents.
1862
        self.assertEqual([
1863
            ("get_parent_map", set([key_basis, key_basis_2, key_missing])),
1864
            ("get_record_stream", [key_basis_2, key_basis], 'topological', False)],
1865
            calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1866
1867
    def test_get_sha1s(self):
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
1868
        # sha1's in the test knit are answered without asking the basis
1869
        basis, test = self.get_basis_and_test_knit()
1870
        key = ('foo',)
1871
        key_basis = ('bar',)
1872
        key_missing = ('missing',)
1873
        test.add_lines(key, (), ['foo\n'])
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
1874
        key_sha1sum = osutils.sha('foo\n').hexdigest()
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
1875
        sha1s = test.get_sha1s([key])
1876
        self.assertEqual({key: key_sha1sum}, sha1s)
1877
        self.assertEqual([], basis.calls)
1878
        # But texts that are not in the test knit are looked for in the basis
1879
        # directly (rather than via text reconstruction) so that remote servers
1880
        # etc don't have to answer with full content.
1881
        basis.add_lines(key_basis, (), ['foo\n', 'bar\n'])
3734.2.4 by Vincent Ladeuil
Fix python2.6 deprecation warnings related to hashlib.
1882
        basis_sha1sum = osutils.sha('foo\nbar\n').hexdigest()
3350.8.3 by Robert Collins
VF.get_sha1s needed changing to be stackable.
1883
        basis.calls = []
1884
        sha1s = test.get_sha1s([key, key_missing, key_basis])
1885
        self.assertEqual({key: key_sha1sum,
1886
            key_basis: basis_sha1sum}, sha1s)
1887
        self.assertEqual([("get_sha1s", set([key_basis, key_missing]))],
1888
            basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1889
1890
    def test_insert_record_stream(self):
3350.8.10 by Robert Collins
Stacked insert_record_stream.
1891
        # records are inserted as normal; insert_record_stream builds on
3350.8.14 by Robert Collins
Review feedback.
1892
        # add_lines, so a smoke test should be all that's needed:
3350.8.10 by Robert Collins
Stacked insert_record_stream.
1893
        key = ('foo',)
1894
        key_basis = ('bar',)
1895
        key_delta = ('zaphod',)
1896
        basis, test = self.get_basis_and_test_knit()
1897
        source = self.make_test_knit(name='source')
1898
        basis.add_lines(key_basis, (), ['foo\n'])
1899
        basis.calls = []
1900
        source.add_lines(key_basis, (), ['foo\n'])
1901
        source.add_lines(key_delta, (key_basis,), ['bar\n'])
1902
        stream = source.get_record_stream([key_delta], 'unordered', False)
1903
        test.insert_record_stream(stream)
1904
        self.assertEqual([("get_parent_map", set([key_basis]))],
1905
            basis.calls)
1906
        self.assertEqual({key_delta:(key_basis,)},
1907
            test.get_parent_map([key_delta]))
1908
        self.assertEqual('bar\n', test.get_record_stream([key_delta],
1909
            'unordered', True).next().get_bytes_as('fulltext'))
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1910
1911
    def test_iter_lines_added_or_present_in_keys(self):
3350.8.5 by Robert Collins
Iter_lines_added_or_present_in_keys stacks.
1912
        # Lines from the basis are returned, and lines for a given key are only
1913
        # returned once. 
1914
        key1 = ('foo1',)
1915
        key2 = ('foo2',)
1916
        # all sources are asked for keys:
1917
        basis, test = self.get_basis_and_test_knit()
1918
        basis.add_lines(key1, (), ["foo"])
1919
        basis.calls = []
1920
        lines = list(test.iter_lines_added_or_present_in_keys([key1]))
1921
        self.assertEqual([("foo\n", key1)], lines)
1922
        self.assertEqual([("iter_lines_added_or_present_in_keys", set([key1]))],
1923
            basis.calls)
1924
        # keys in both are not duplicated:
1925
        test.add_lines(key2, (), ["bar\n"])
1926
        basis.add_lines(key2, (), ["bar\n"])
1927
        basis.calls = []
1928
        lines = list(test.iter_lines_added_or_present_in_keys([key2]))
1929
        self.assertEqual([("bar\n", key2)], lines)
1930
        self.assertEqual([], basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1931
1932
    def test_keys(self):
3350.8.4 by Robert Collins
Vf.keys() stacking support.
1933
        key1 = ('foo1',)
1934
        key2 = ('foo2',)
1935
        # all sources are asked for keys:
1936
        basis, test = self.get_basis_and_test_knit()
1937
        keys = test.keys()
1938
        self.assertEqual(set(), set(keys))
1939
        self.assertEqual([("keys",)], basis.calls)
1940
        # keys from a basis are returned:
1941
        basis.add_lines(key1, (), [])
1942
        basis.calls = []
1943
        keys = test.keys()
1944
        self.assertEqual(set([key1]), set(keys))
1945
        self.assertEqual([("keys",)], basis.calls)
1946
        # keys in both are not duplicated:
1947
        test.add_lines(key2, (), [])
1948
        basis.add_lines(key2, (), [])
1949
        basis.calls = []
1950
        keys = test.keys()
1951
        self.assertEqual(2, len(keys))
1952
        self.assertEqual(set([key1, key2]), set(keys))
1953
        self.assertEqual([("keys",)], basis.calls)
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1954
1955
    def test_add_mpdiffs(self):
3350.8.11 by Robert Collins
Stacked add_mpdiffs.
1956
        # records are inserted as normal; add_mpdiff builds on
3350.8.14 by Robert Collins
Review feedback.
1957
        # add_lines, so a smoke test should be all that's needed:
3350.8.11 by Robert Collins
Stacked add_mpdiffs.
1958
        key = ('foo',)
1959
        key_basis = ('bar',)
1960
        key_delta = ('zaphod',)
1961
        basis, test = self.get_basis_and_test_knit()
1962
        source = self.make_test_knit(name='source')
1963
        basis.add_lines(key_basis, (), ['foo\n'])
1964
        basis.calls = []
1965
        source.add_lines(key_basis, (), ['foo\n'])
1966
        source.add_lines(key_delta, (key_basis,), ['bar\n'])
1967
        diffs = source.make_mpdiffs([key_delta])
1968
        test.add_mpdiffs([(key_delta, (key_basis,),
1969
            source.get_sha1s([key_delta])[key_delta], diffs[0])])
1970
        self.assertEqual([("get_parent_map", set([key_basis])),
3350.8.12 by Robert Collins
Stacked make_mpdiffs.
1971
            ('get_record_stream', [key_basis], 'unordered', True),
1972
            ('get_parent_map', set([key_basis]))],
3350.8.11 by Robert Collins
Stacked add_mpdiffs.
1973
            basis.calls)
1974
        self.assertEqual({key_delta:(key_basis,)},
1975
            test.get_parent_map([key_delta]))
1976
        self.assertEqual('bar\n', test.get_record_stream([key_delta],
1977
            'unordered', True).next().get_bytes_as('fulltext'))
3350.8.1 by Robert Collins
KnitVersionedFiles.add_fallback_versioned_files exists.
1978
1979
    def test_make_mpdiffs(self):
3350.8.12 by Robert Collins
Stacked make_mpdiffs.
1980
        # Generating an mpdiff across a stacking boundary should detect parent
1981
        # texts regions.
1982
        key = ('foo',)
1983
        key_left = ('bar',)
1984
        key_right = ('zaphod',)
1985
        basis, test = self.get_basis_and_test_knit()
1986
        basis.add_lines(key_left, (), ['bar\n'])
1987
        basis.add_lines(key_right, (), ['zaphod\n'])
1988
        basis.calls = []
1989
        test.add_lines(key, (key_left, key_right),
1990
            ['bar\n', 'foo\n', 'zaphod\n'])
1991
        diffs = test.make_mpdiffs([key])
1992
        self.assertEqual([
1993
            multiparent.MultiParent([multiparent.ParentText(0, 0, 0, 1),
1994
                multiparent.NewText(['foo\n']),
1995
                multiparent.ParentText(1, 0, 2, 1)])],
1996
            diffs)
1997
        self.assertEqual(4, len(basis.calls))
1998
        self.assertEqual([
1999
            ("get_parent_map", set([key_left, key_right])),
2000
            ("get_parent_map", set([key_left, key_right])),
2001
            ("get_parent_map", set([key_left, key_right])),
2002
            ],
2003
            basis.calls[:3])
3350.8.14 by Robert Collins
Review feedback.
2004
        last_call = basis.calls[3]
2005
        self.assertEqual('get_record_stream', last_call[0])
2006
        self.assertEqual(set([key_left, key_right]), set(last_call[1]))
2007
        self.assertEqual('unordered', last_call[2])
2008
        self.assertEqual(True, last_call[3])