/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
22
import sha
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
23
import sys
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
24
2196.2.5 by John Arbash Meinel
Add an exception class when the knit index storage method is unknown, and properly test for it
25
from bzrlib import (
26
    errors,
2484.1.5 by John Arbash Meinel
Simplistic implementations of custom parsers for options and parents
27
    generate_ids,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
28
    knit,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
29
    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
30
    )
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
31
from bzrlib.errors import (
32
    RevisionAlreadyPresent,
33
    KnitHeaderError,
34
    RevisionNotPresent,
35
    NoSuchFile,
36
    )
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
37
from bzrlib.index import *
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
38
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.
39
    AnnotatedKnitContent,
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
40
    DATA_SUFFIX,
41
    INDEX_SUFFIX,
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
42
    KnitContent,
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.
43
    KnitGraphIndex,
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
44
    KnitVersionedFile,
45
    KnitPlainFactory,
46
    KnitAnnotateFactory,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
47
    _KnitAccess,
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
48
    _KnitData,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
49
    _KnitIndex,
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
50
    make_file_knit,
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
51
    _PackAccess,
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.
52
    PlainKnitContent,
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
53
    _StreamAccess,
54
    _StreamIndex,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
55
    WeaveToKnit,
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
56
    KnitSequenceMatcher,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
57
    )
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
58
from bzrlib.osutils import split_lines
3316.2.13 by Robert Collins
* ``VersionedFile.annotate_iter`` is deprecated. While in principal this
59
from bzrlib.symbol_versioning import one_four
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
60
from bzrlib.tests import (
61
    Feature,
62
    TestCase,
63
    TestCaseWithMemoryTransport,
64
    TestCaseWithTransport,
65
    )
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
66
from bzrlib.transport import get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
67
from bzrlib.transport.memory import MemoryTransport
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
68
from bzrlib.tuned_gzip import GzipFile
2535.3.15 by Andrew Bennetts
Add KnitVersionedFile.get_stream_as_bytes, start smart implementation of RemoteRepository.get_data_stream.
69
from bzrlib.util import bencode
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
70
from bzrlib.weave import Weave
71
72
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
73
class _CompiledKnitFeature(Feature):
74
75
    def _probe(self):
76
        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
77
            import bzrlib._knit_load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
78
        except ImportError:
79
            return False
80
        return True
81
82
    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
83
        return 'bzrlib._knit_load_data_c'
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
84
85
CompiledKnitFeature = _CompiledKnitFeature()
86
87
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.
88
class KnitContentTestsMixin(object):
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
89
90
    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.
91
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
92
93
    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.
94
        content = self._make_content([])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
95
        self.assertEqual(content.text(), [])
96
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.
97
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
98
        self.assertEqual(content.text(), ["text1", "text2"])
99
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.
100
    def test_copy(self):
101
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
102
        copy = content.copy()
103
        self.assertIsInstance(copy, content.__class__)
104
        self.assertEqual(copy.annotate(), content.annotate())
105
106
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
107
        """Assert that the derived matching blocks match real output"""
108
        source_lines = source.splitlines(True)
109
        target_lines = target.splitlines(True)
110
        def nl(line):
111
            if noeol and not line.endswith('\n'):
112
                return line + '\n'
113
            else:
114
                return line
115
        source_content = self._make_content([(None, nl(l)) for l in source_lines])
116
        target_content = self._make_content([(None, nl(l)) for l in target_lines])
117
        line_delta = source_content.line_delta(target_content)
118
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
119
            source_lines, target_lines))
120
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
121
        matcher_blocks = list(list(matcher.get_matching_blocks()))
122
        self.assertEqual(matcher_blocks, delta_blocks)
123
124
    def test_get_line_delta_blocks(self):
125
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
126
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
127
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
128
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
129
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
130
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
131
        self.assertDerivedBlocksEqual(TEXT_1A, '')
132
        self.assertDerivedBlocksEqual('', TEXT_1A)
133
        self.assertDerivedBlocksEqual('', '')
134
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
135
136
    def test_get_line_delta_blocks_noeol(self):
137
        """Handle historical knit deltas safely
138
139
        Some existing knit deltas don't consider the last line to differ
140
        when the only difference whether it has a final newline.
141
142
        New knit deltas appear to always consider the last line to differ
143
        in this case.
144
        """
145
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
146
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
147
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
148
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
149
150
151
class TestPlainKnitContent(TestCase, KnitContentTestsMixin):
152
153
    def _make_content(self, lines):
154
        annotated_content = AnnotatedKnitContent(lines)
155
        return PlainKnitContent(annotated_content.text(), 'bogus')
156
157
    def test_annotate(self):
158
        content = self._make_content([])
159
        self.assertEqual(content.annotate(), [])
160
161
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
162
        self.assertEqual(content.annotate(),
163
            [("bogus", "text1"), ("bogus", "text2")])
164
165
    def test_line_delta(self):
166
        content1 = self._make_content([("", "a"), ("", "b")])
167
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
168
        self.assertEqual(content1.line_delta(content2),
169
            [(1, 2, 2, ["a", "c"])])
170
171
    def test_line_delta_iter(self):
172
        content1 = self._make_content([("", "a"), ("", "b")])
173
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
174
        it = content1.line_delta_iter(content2)
175
        self.assertEqual(it.next(), (1, 2, 2, ["a", "c"]))
176
        self.assertRaises(StopIteration, it.next)
177
178
179
class TestAnnotatedKnitContent(TestCase, KnitContentTestsMixin):
180
181
    def _make_content(self, lines):
182
        return AnnotatedKnitContent(lines)
183
184
    def test_annotate(self):
185
        content = self._make_content([])
186
        self.assertEqual(content.annotate(), [])
187
188
        content = self._make_content([("origin1", "text1"), ("origin2", "text2")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
189
        self.assertEqual(content.annotate(),
190
            [("origin1", "text1"), ("origin2", "text2")])
191
192
    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.
193
        content1 = self._make_content([("", "a"), ("", "b")])
194
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
195
        self.assertEqual(content1.line_delta(content2),
196
            [(1, 2, 2, [("", "a"), ("", "c")])])
197
198
    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.
199
        content1 = self._make_content([("", "a"), ("", "b")])
200
        content2 = self._make_content([("", "a"), ("", "a"), ("", "c")])
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
201
        it = content1.line_delta_iter(content2)
202
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
203
        self.assertRaises(StopIteration, it.next)
204
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
205
206
class MockTransport(object):
207
208
    def __init__(self, file_lines=None):
209
        self.file_lines = file_lines
210
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
211
        # We have no base directory for the MockTransport
212
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
213
214
    def get(self, filename):
215
        if self.file_lines is None:
216
            raise NoSuchFile(filename)
217
        else:
218
            return StringIO("\n".join(self.file_lines))
219
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
220
    def readv(self, relpath, offsets):
221
        fp = self.get(relpath)
222
        for offset, size in offsets:
223
            fp.seek(offset)
224
            yield offset, fp.read(size)
225
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
226
    def __getattr__(self, name):
227
        def queue_call(*args, **kwargs):
228
            self.calls.append((name, args, kwargs))
229
        return queue_call
230
231
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
232
class KnitRecordAccessTestsMixin(object):
233
    """Tests for getting and putting knit records."""
234
235
    def assertAccessExists(self, access):
236
        """Ensure the data area for access has been initialised/exists."""
237
        raise NotImplementedError(self.assertAccessExists)
238
239
    def test_add_raw_records(self):
240
        """Add_raw_records adds records retrievable later."""
241
        access = self.get_access()
242
        memos = access.add_raw_records([10], '1234567890')
243
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
244
 
245
    def test_add_several_raw_records(self):
246
        """add_raw_records with many records and read some back."""
247
        access = self.get_access()
248
        memos = access.add_raw_records([10, 2, 5], '12345678901234567')
249
        self.assertEqual(['1234567890', '12', '34567'],
250
            list(access.get_raw_records(memos)))
251
        self.assertEqual(['1234567890'],
252
            list(access.get_raw_records(memos[0:1])))
253
        self.assertEqual(['12'],
254
            list(access.get_raw_records(memos[1:2])))
255
        self.assertEqual(['34567'],
256
            list(access.get_raw_records(memos[2:3])))
257
        self.assertEqual(['1234567890', '34567'],
258
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
259
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
260
    def test_create(self):
261
        """create() should make a file on disk."""
262
        access = self.get_access()
263
        access.create()
264
        self.assertAccessExists(access)
265
266
    def test_open_file(self):
267
        """open_file never errors."""
268
        access = self.get_access()
269
        access.open_file()
270
271
272
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
273
    """Tests for the .kndx implementation."""
274
275
    def assertAccessExists(self, access):
276
        self.assertNotEqual(None, access.open_file())
277
278
    def get_access(self):
279
        """Get a .knit style access instance."""
280
        access = _KnitAccess(self.get_transport(), "foo.knit", None, None,
281
            False, False)
282
        return access
283
    
284
285
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
286
    """Tests for the pack based access."""
287
288
    def assertAccessExists(self, access):
289
        # as pack based access has no backing unless an index maps data, this
290
        # is a no-op.
291
        pass
292
293
    def get_access(self):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
294
        return self._get_access()[0]
295
296
    def _get_access(self, packname='packfile', index='FOO'):
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
297
        transport = self.get_transport()
298
        def write_data(bytes):
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
299
            transport.append_bytes(packname, bytes)
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
300
        writer = pack.ContainerWriter(write_data)
301
        writer.begin()
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
302
        indices = {index:(transport, packname)}
303
        access = _PackAccess(indices, writer=(writer, index))
304
        return access, writer
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
305
2592.3.67 by Robert Collins
More tests for bzrlib.knit._PackAccess.
306
    def test_read_from_several_packs(self):
307
        access, writer = self._get_access()
308
        memos = []
309
        memos.extend(access.add_raw_records([10], '1234567890'))
310
        writer.end()
311
        access, writer = self._get_access('pack2', 'FOOBAR')
312
        memos.extend(access.add_raw_records([5], '12345'))
313
        writer.end()
314
        access, writer = self._get_access('pack3', 'BAZ')
315
        memos.extend(access.add_raw_records([5], 'alpha'))
316
        writer.end()
317
        transport = self.get_transport()
318
        access = _PackAccess({"FOO":(transport, 'packfile'),
319
            "FOOBAR":(transport, 'pack2'),
320
            "BAZ":(transport, 'pack3')})
321
        self.assertEqual(['1234567890', '12345', 'alpha'],
322
            list(access.get_raw_records(memos)))
323
        self.assertEqual(['1234567890'],
324
            list(access.get_raw_records(memos[0:1])))
325
        self.assertEqual(['12345'],
326
            list(access.get_raw_records(memos[1:2])))
327
        self.assertEqual(['alpha'],
328
            list(access.get_raw_records(memos[2:3])))
329
        self.assertEqual(['1234567890', 'alpha'],
330
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
331
2592.3.70 by Robert Collins
Allow setting a writer after creating a knit._PackAccess object.
332
    def test_set_writer(self):
333
        """The writer should be settable post construction."""
334
        access = _PackAccess({})
335
        transport = self.get_transport()
336
        packname = 'packfile'
337
        index = 'foo'
338
        def write_data(bytes):
339
            transport.append_bytes(packname, bytes)
340
        writer = pack.ContainerWriter(write_data)
341
        writer.begin()
342
        access.set_writer(writer, index, (transport, packname))
343
        memos = access.add_raw_records([10], '1234567890')
344
        writer.end()
345
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
346
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
347
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
348
class LowLevelKnitDataTests(TestCase):
349
350
    def create_gz_content(self, text):
351
        sio = StringIO()
352
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
353
        gz_file.write(text)
354
        gz_file.close()
355
        return sio.getvalue()
356
357
    def test_valid_knit_data(self):
358
        sha1sum = sha.new('foo\nbar\n').hexdigest()
359
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
360
                                        'foo\n'
361
                                        'bar\n'
362
                                        'end rev-id-1\n'
363
                                        % (sha1sum,))
364
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
365
        access = _KnitAccess(transport, 'filename', None, None, False, False)
366
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
367
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
368
369
        contents = data.read_records(records)
370
        self.assertEqual({'rev-id-1':(['foo\n', 'bar\n'], sha1sum)}, contents)
371
372
        raw_contents = list(data.read_records_iter_raw(records))
373
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
374
375
    def test_not_enough_lines(self):
376
        sha1sum = sha.new('foo\n').hexdigest()
377
        # record says 2 lines data says 1
378
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
379
                                        'foo\n'
380
                                        'end rev-id-1\n'
381
                                        % (sha1sum,))
382
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
383
        access = _KnitAccess(transport, 'filename', None, None, False, False)
384
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
385
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
386
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
387
388
        # read_records_iter_raw won't detect that sort of mismatch/corruption
389
        raw_contents = list(data.read_records_iter_raw(records))
390
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
391
392
    def test_too_many_lines(self):
393
        sha1sum = sha.new('foo\nbar\n').hexdigest()
394
        # record says 1 lines data says 2
395
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
396
                                        'foo\n'
397
                                        'bar\n'
398
                                        'end rev-id-1\n'
399
                                        % (sha1sum,))
400
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
401
        access = _KnitAccess(transport, 'filename', None, None, False, False)
402
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
403
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
404
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
405
406
        # read_records_iter_raw won't detect that sort of mismatch/corruption
407
        raw_contents = list(data.read_records_iter_raw(records))
408
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
409
410
    def test_mismatched_version_id(self):
411
        sha1sum = sha.new('foo\nbar\n').hexdigest()
412
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
413
                                        'foo\n'
414
                                        'bar\n'
415
                                        'end rev-id-1\n'
416
                                        % (sha1sum,))
417
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
418
        access = _KnitAccess(transport, 'filename', None, None, False, False)
419
        data = _KnitData(access=access)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
420
        # We are asking for rev-id-2, but the data is rev-id-1
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
421
        records = [('rev-id-2', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
422
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
423
424
        # read_records_iter_raw will notice if we request the wrong version.
425
        self.assertRaises(errors.KnitCorrupt, list,
426
                          data.read_records_iter_raw(records))
427
428
    def test_uncompressed_data(self):
429
        sha1sum = sha.new('foo\nbar\n').hexdigest()
430
        txt = ('version rev-id-1 2 %s\n'
431
               'foo\n'
432
               'bar\n'
433
               'end rev-id-1\n'
434
               % (sha1sum,))
435
        transport = MockTransport([txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
436
        access = _KnitAccess(transport, 'filename', None, None, False, False)
437
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
438
        records = [('rev-id-1', (None, 0, len(txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
439
440
        # We don't have valid gzip data ==> corrupt
441
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
442
443
        # read_records_iter_raw will notice the bad data
444
        self.assertRaises(errors.KnitCorrupt, list,
445
                          data.read_records_iter_raw(records))
446
447
    def test_corrupted_data(self):
448
        sha1sum = sha.new('foo\nbar\n').hexdigest()
449
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
450
                                        'foo\n'
451
                                        'bar\n'
452
                                        'end rev-id-1\n'
453
                                        % (sha1sum,))
454
        # Change 2 bytes in the middle to \xff
455
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
456
        transport = MockTransport([gz_txt])
2592.3.66 by Robert Collins
Allow adaption of KnitData to pack files.
457
        access = _KnitAccess(transport, 'filename', None, None, False, False)
458
        data = _KnitData(access=access)
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
459
        records = [('rev-id-1', (None, 0, len(gz_txt)))]
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
460
461
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
462
463
        # read_records_iter_raw will notice if we request the wrong version.
464
        self.assertRaises(errors.KnitCorrupt, list,
465
                          data.read_records_iter_raw(records))
466
467
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
468
class LowLevelKnitIndexTests(TestCase):
469
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
470
    def get_knit_index(self, *args, **kwargs):
471
        orig = knit._load_data
472
        def reset():
473
            knit._load_data = orig
474
        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
475
        from bzrlib._knit_load_data_py import _load_data_py
476
        knit._load_data = _load_data_py
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
477
        return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
478
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
479
    def test_no_such_file(self):
480
        transport = MockTransport()
481
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
482
        self.assertRaises(NoSuchFile, self.get_knit_index,
483
                          transport, "filename", "r")
484
        self.assertRaises(NoSuchFile, self.get_knit_index,
485
                          transport, "filename", "w", create=False)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
486
487
    def test_create_file(self):
488
        transport = MockTransport()
489
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
490
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
491
            file_mode="wb", create=True)
492
        self.assertEqual(
493
                ("put_bytes_non_atomic",
494
                    ("filename", index.HEADER), {"mode": "wb"}),
495
                transport.calls.pop(0))
496
497
    def test_delay_create_file(self):
498
        transport = MockTransport()
499
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
500
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
501
            create=True, file_mode="wb", create_parent_dir=True,
502
            delay_create=True, dir_mode=0777)
503
        self.assertEqual([], transport.calls)
504
505
        index.add_versions([])
506
        name, (filename, f), kwargs = transport.calls.pop(0)
507
        self.assertEqual("put_file_non_atomic", name)
508
        self.assertEqual(
509
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
510
            kwargs)
511
        self.assertEqual("filename", filename)
512
        self.assertEqual(index.HEADER, f.read())
513
514
        index.add_versions([])
515
        self.assertEqual(("append_bytes", ("filename", ""), {}),
516
            transport.calls.pop(0))
517
518
    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
519
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
520
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
521
        transport = MockTransport([
522
            _KnitIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
523
            '%s option 0 1 :' % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
524
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
525
        index = self.get_knit_index(transport, "filename", "r")
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
526
        # _KnitIndex is a private class, and deals in utf8 revision_ids, not
527
        # Unicode revision_ids.
528
        self.assertTrue(index.has_version(utf8_revision_id))
529
        self.assertFalse(index.has_version(unicode_revision_id))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
530
531
    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
532
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
533
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
534
        transport = MockTransport([
535
            _KnitIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
536
            "version option 0 1 .%s :" % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
537
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
538
        index = self.get_knit_index(transport, "filename", "r")
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
539
        self.assertEqual((utf8_revision_id,),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
540
            index.get_parents_with_ghosts("version"))
541
542
    def test_read_ignore_corrupted_lines(self):
543
        transport = MockTransport([
544
            _KnitIndex.HEADER,
545
            "corrupted",
546
            "corrupted options 0 1 .b .c ",
547
            "version options 0 1 :"
548
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
549
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
550
        self.assertEqual(1, index.num_versions())
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
551
        self.assertTrue(index.has_version("version"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
552
553
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
554
        transport = MockTransport(['not a bzr knit index header\n'])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
555
        self.assertRaises(KnitHeaderError,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
556
            self.get_knit_index, transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
557
558
    def test_read_duplicate_entries(self):
559
        transport = MockTransport([
560
            _KnitIndex.HEADER,
561
            "parent options 0 1 :",
562
            "version options1 0 1 0 :",
563
            "version options2 1 2 .other :",
564
            "version options3 3 4 0 .other :"
565
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
566
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
567
        self.assertEqual(2, index.num_versions())
2592.3.8 by Robert Collins
Remove unneeded pulib method lookup on private class _KnitIndex.
568
        # check that the index used is the first one written. (Specific
569
        # to KnitIndex style indices.
570
        self.assertEqual("1", index._version_list_to_index(["version"]))
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
571
        self.assertEqual((None, 3, 4), index.get_position("version"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
572
        self.assertEqual(["options3"], index.get_options("version"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
573
        self.assertEqual(("parent", "other"),
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
574
            index.get_parents_with_ghosts("version"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
575
576
    def test_read_compressed_parents(self):
577
        transport = MockTransport([
578
            _KnitIndex.HEADER,
579
            "a option 0 1 :",
580
            "b option 0 1 0 :",
581
            "c option 0 1 1 0 :",
582
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
583
        index = self.get_knit_index(transport, "filename", "r")
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
584
        self.assertEqual({"b":("a",), "c":("b", "a")},
585
            index.get_parent_map(["b", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
586
587
    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
588
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
589
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
590
        transport = MockTransport([
591
            _KnitIndex.HEADER
592
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
593
        index = self.get_knit_index(transport, "filename", "r")
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
594
        index.add_version(utf8_revision_id, ["option"], (None, 0, 1), [])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
595
        self.assertEqual(("append_bytes", ("filename",
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
596
            "\n%s option 0 1  :" % (utf8_revision_id,)),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
597
            {}),
598
            transport.calls.pop(0))
599
600
    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
601
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
602
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
603
        transport = MockTransport([
604
            _KnitIndex.HEADER
605
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
606
        index = self.get_knit_index(transport, "filename", "r")
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
607
        index.add_version("version", ["option"], (None, 0, 1), [utf8_revision_id])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
608
        self.assertEqual(("append_bytes", ("filename",
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
609
            "\nversion option 0 1 .%s :" % (utf8_revision_id,)),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
610
            {}),
611
            transport.calls.pop(0))
612
613
    def test_get_ancestry(self):
614
        transport = MockTransport([
615
            _KnitIndex.HEADER,
616
            "a option 0 1 :",
617
            "b option 0 1 0 .e :",
618
            "c option 0 1 1 0 :",
619
            "d option 0 1 2 .f :"
620
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
621
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
622
623
        self.assertEqual([], index.get_ancestry([]))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
624
        self.assertEqual(["a"], index.get_ancestry(["a"]))
625
        self.assertEqual(["a", "b"], index.get_ancestry(["b"]))
626
        self.assertEqual(["a", "b", "c"], index.get_ancestry(["c"]))
627
        self.assertEqual(["a", "b", "c", "d"], index.get_ancestry(["d"]))
628
        self.assertEqual(["a", "b"], index.get_ancestry(["a", "b"]))
629
        self.assertEqual(["a", "b", "c"], index.get_ancestry(["a", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
630
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
631
        self.assertRaises(RevisionNotPresent, index.get_ancestry, ["e"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
632
633
    def test_get_ancestry_with_ghosts(self):
634
        transport = MockTransport([
635
            _KnitIndex.HEADER,
636
            "a option 0 1 :",
637
            "b option 0 1 0 .e :",
638
            "c option 0 1 0 .f .g :",
639
            "d option 0 1 2 .h .j .k :"
640
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
641
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
642
643
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
644
        self.assertEqual(["a"], index.get_ancestry_with_ghosts(["a"]))
645
        self.assertEqual(["a", "e", "b"],
646
            index.get_ancestry_with_ghosts(["b"]))
647
        self.assertEqual(["a", "g", "f", "c"],
648
            index.get_ancestry_with_ghosts(["c"]))
649
        self.assertEqual(["a", "g", "f", "c", "k", "j", "h", "d"],
650
            index.get_ancestry_with_ghosts(["d"]))
651
        self.assertEqual(["a", "e", "b"],
652
            index.get_ancestry_with_ghosts(["a", "b"]))
653
        self.assertEqual(["a", "g", "f", "c"],
654
            index.get_ancestry_with_ghosts(["a", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
655
        self.assertEqual(
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
656
            ["a", "g", "f", "c", "e", "b", "k", "j", "h", "d"],
657
            index.get_ancestry_with_ghosts(["b", "d"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
658
659
        self.assertRaises(RevisionNotPresent,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
660
            index.get_ancestry_with_ghosts, ["e"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
661
662
    def test_num_versions(self):
663
        transport = MockTransport([
664
            _KnitIndex.HEADER
665
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
666
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
667
668
        self.assertEqual(0, index.num_versions())
669
        self.assertEqual(0, len(index))
670
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
671
        index.add_version("a", ["option"], (None, 0, 1), [])
672
        self.assertEqual(1, index.num_versions())
673
        self.assertEqual(1, len(index))
674
675
        index.add_version("a", ["option2"], (None, 1, 2), [])
676
        self.assertEqual(1, index.num_versions())
677
        self.assertEqual(1, len(index))
678
679
        index.add_version("b", ["option"], (None, 0, 1), [])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
680
        self.assertEqual(2, index.num_versions())
681
        self.assertEqual(2, len(index))
682
683
    def test_get_versions(self):
684
        transport = MockTransport([
685
            _KnitIndex.HEADER
686
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
687
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
688
689
        self.assertEqual([], index.get_versions())
690
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
691
        index.add_version("a", ["option"], (None, 0, 1), [])
692
        self.assertEqual(["a"], index.get_versions())
693
694
        index.add_version("a", ["option"], (None, 0, 1), [])
695
        self.assertEqual(["a"], index.get_versions())
696
697
        index.add_version("b", ["option"], (None, 0, 1), [])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
698
        self.assertEqual(["a", "b"], index.get_versions())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
699
700
    def test_add_version(self):
701
        transport = MockTransport([
702
            _KnitIndex.HEADER
703
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
704
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
705
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
706
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
707
        self.assertEqual(("append_bytes",
708
            ("filename", "\na option 0 1 .b :"),
709
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
710
        self.assertTrue(index.has_version("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
711
        self.assertEqual(1, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
712
        self.assertEqual((None, 0, 1), index.get_position("a"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
713
        self.assertEqual(["option"], index.get_options("a"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
714
        self.assertEqual(("b",), index.get_parents_with_ghosts("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
715
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
716
        index.add_version("a", ["opt"], (None, 1, 2), ["c"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
717
        self.assertEqual(("append_bytes",
718
            ("filename", "\na opt 1 2 .c :"),
719
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
720
        self.assertTrue(index.has_version("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
721
        self.assertEqual(1, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
722
        self.assertEqual((None, 1, 2), index.get_position("a"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
723
        self.assertEqual(["opt"], index.get_options("a"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
724
        self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
725
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
726
        index.add_version("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
727
        self.assertEqual(("append_bytes",
728
            ("filename", "\nb option 2 3 0 :"),
729
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
730
        self.assertTrue(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
731
        self.assertEqual(2, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
732
        self.assertEqual((None, 2, 3), index.get_position("b"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
733
        self.assertEqual(["option"], index.get_options("b"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
734
        self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
735
736
    def test_add_versions(self):
737
        transport = MockTransport([
738
            _KnitIndex.HEADER
739
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
740
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
741
742
        index.add_versions([
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
743
            ("a", ["option"], (None, 0, 1), ["b"]),
744
            ("a", ["opt"], (None, 1, 2), ["c"]),
745
            ("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
746
            ])
747
        self.assertEqual(("append_bytes", ("filename",
748
            "\na option 0 1 .b :"
749
            "\na opt 1 2 .c :"
750
            "\nb option 2 3 0 :"
751
            ), {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
752
        self.assertTrue(index.has_version("a"))
753
        self.assertTrue(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
754
        self.assertEqual(2, index.num_versions())
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
755
        self.assertEqual((None, 1, 2), index.get_position("a"))
756
        self.assertEqual((None, 2, 3), index.get_position("b"))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
757
        self.assertEqual(["opt"], index.get_options("a"))
758
        self.assertEqual(["option"], index.get_options("b"))
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
759
        self.assertEqual(("c",), index.get_parents_with_ghosts("a"))
760
        self.assertEqual(("a",), index.get_parents_with_ghosts("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
761
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
762
    def test_add_versions_random_id_is_accepted(self):
763
        transport = MockTransport([
764
            _KnitIndex.HEADER
765
            ])
766
        index = self.get_knit_index(transport, "filename", "r")
767
768
        index.add_versions([
769
            ("a", ["option"], (None, 0, 1), ["b"]),
770
            ("a", ["opt"], (None, 1, 2), ["c"]),
771
            ("b", ["option"], (None, 2, 3), ["a"])
772
            ], random_id=True)
773
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
774
    def test_delay_create_and_add_versions(self):
775
        transport = MockTransport()
776
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
777
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
778
            create=True, file_mode="wb", create_parent_dir=True,
779
            delay_create=True, dir_mode=0777)
780
        self.assertEqual([], transport.calls)
781
782
        index.add_versions([
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
783
            ("a", ["option"], (None, 0, 1), ["b"]),
784
            ("a", ["opt"], (None, 1, 2), ["c"]),
785
            ("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
786
            ])
787
        name, (filename, f), kwargs = transport.calls.pop(0)
788
        self.assertEqual("put_file_non_atomic", name)
789
        self.assertEqual(
790
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
791
            kwargs)
792
        self.assertEqual("filename", filename)
793
        self.assertEqual(
794
            index.HEADER +
795
            "\na option 0 1 .b :"
796
            "\na opt 1 2 .c :"
797
            "\nb option 2 3 0 :",
798
            f.read())
799
800
    def test_has_version(self):
801
        transport = MockTransport([
802
            _KnitIndex.HEADER,
803
            "a option 0 1 :"
804
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
805
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
806
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
807
        self.assertTrue(index.has_version("a"))
808
        self.assertFalse(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
809
810
    def test_get_position(self):
811
        transport = MockTransport([
812
            _KnitIndex.HEADER,
813
            "a option 0 1 :",
814
            "b option 1 2 :"
815
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
816
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
817
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
818
        self.assertEqual((None, 0, 1), index.get_position("a"))
819
        self.assertEqual((None, 1, 2), index.get_position("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
820
821
    def test_get_method(self):
822
        transport = MockTransport([
823
            _KnitIndex.HEADER,
824
            "a fulltext,unknown 0 1 :",
825
            "b unknown,line-delta 1 2 :",
826
            "c bad 3 4 :"
827
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
828
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
829
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
830
        self.assertEqual("fulltext", index.get_method("a"))
831
        self.assertEqual("line-delta", index.get_method("b"))
832
        self.assertRaises(errors.KnitIndexUnknownMethod, index.get_method, "c")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
833
834
    def test_get_options(self):
835
        transport = MockTransport([
836
            _KnitIndex.HEADER,
837
            "a opt1 0 1 :",
838
            "b opt2,opt3 1 2 :"
839
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
840
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
841
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
842
        self.assertEqual(["opt1"], index.get_options("a"))
843
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
844
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
845
    def test_get_parent_map(self):
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
846
        transport = MockTransport([
847
            _KnitIndex.HEADER,
848
            "a option 0 1 :",
849
            "b option 1 2 0 .c :",
850
            "c option 1 2 1 0 .e :"
851
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
852
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
853
3287.5.6 by Robert Collins
Remove _KnitIndex.get_parents.
854
        self.assertEqual({
855
            "a":(),
856
            "b":("a", "c"),
857
            "c":("b", "a", "e"),
858
            }, index.get_parent_map(["a", "b", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
859
860
    def test_get_parents_with_ghosts(self):
861
        transport = MockTransport([
862
            _KnitIndex.HEADER,
863
            "a option 0 1 :",
864
            "b option 1 2 0 .c :",
865
            "c option 1 2 1 0 .e :"
866
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
867
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
868
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
869
        self.assertEqual((), index.get_parents_with_ghosts("a"))
870
        self.assertEqual(("a", "c"), index.get_parents_with_ghosts("b"))
871
        self.assertEqual(("b", "a", "e"),
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
872
            index.get_parents_with_ghosts("c"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
873
874
    def test_check_versions_present(self):
875
        transport = MockTransport([
876
            _KnitIndex.HEADER,
877
            "a option 0 1 :",
878
            "b option 0 1 :"
879
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
880
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
881
882
        check = index.check_versions_present
883
884
        check([])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
885
        check(["a"])
886
        check(["b"])
887
        check(["a", "b"])
888
        self.assertRaises(RevisionNotPresent, check, ["c"])
889
        self.assertRaises(RevisionNotPresent, check, ["a", "b", "c"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
890
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
891
    def test_impossible_parent(self):
892
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
893
        transport = MockTransport([
894
            _KnitIndex.HEADER,
895
            "a option 0 1 :",
896
            "b option 0 1 4 :"  # We don't have a 4th record
897
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
898
        try:
899
            self.assertRaises(errors.KnitCorrupt,
900
                              self.get_knit_index, transport, 'filename', 'r')
901
        except TypeError, e:
902
            if (str(e) == ('exceptions must be strings, classes, or instances,'
903
                           ' not exceptions.IndexError')
904
                and sys.version_info[0:2] >= (2,5)):
905
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
906
                                  ' raising new style exceptions with python'
907
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
908
            else:
909
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
910
911
    def test_corrupted_parent(self):
912
        transport = MockTransport([
913
            _KnitIndex.HEADER,
914
            "a option 0 1 :",
915
            "b option 0 1 :",
916
            "c option 0 1 1v :", # Can't have a parent of '1v'
917
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
918
        try:
919
            self.assertRaises(errors.KnitCorrupt,
920
                              self.get_knit_index, transport, 'filename', 'r')
921
        except TypeError, e:
922
            if (str(e) == ('exceptions must be strings, classes, or instances,'
923
                           ' not exceptions.ValueError')
924
                and sys.version_info[0:2] >= (2,5)):
925
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
926
                                  ' raising new style exceptions with python'
927
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
928
            else:
929
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
930
931
    def test_corrupted_parent_in_list(self):
932
        transport = MockTransport([
933
            _KnitIndex.HEADER,
934
            "a option 0 1 :",
935
            "b option 0 1 :",
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
936
            "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.
937
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
938
        try:
939
            self.assertRaises(errors.KnitCorrupt,
940
                              self.get_knit_index, transport, 'filename', 'r')
941
        except TypeError, e:
942
            if (str(e) == ('exceptions must be strings, classes, or instances,'
943
                           ' not exceptions.ValueError')
944
                and sys.version_info[0:2] >= (2,5)):
945
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
946
                                  ' raising new style exceptions with python'
947
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
948
            else:
949
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
950
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
951
    def test_invalid_position(self):
952
        transport = MockTransport([
953
            _KnitIndex.HEADER,
954
            "a option 1v 1 :",
955
            ])
956
        try:
957
            self.assertRaises(errors.KnitCorrupt,
958
                              self.get_knit_index, transport, 'filename', 'r')
959
        except TypeError, e:
960
            if (str(e) == ('exceptions must be strings, classes, or instances,'
961
                           ' not exceptions.ValueError')
962
                and sys.version_info[0:2] >= (2,5)):
963
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
964
                                  ' raising new style exceptions with python'
965
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
966
            else:
967
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
968
969
    def test_invalid_size(self):
970
        transport = MockTransport([
971
            _KnitIndex.HEADER,
972
            "a option 1 1v :",
973
            ])
974
        try:
975
            self.assertRaises(errors.KnitCorrupt,
976
                              self.get_knit_index, transport, 'filename', 'r')
977
        except TypeError, e:
978
            if (str(e) == ('exceptions must be strings, classes, or instances,'
979
                           ' not exceptions.ValueError')
980
                and sys.version_info[0:2] >= (2,5)):
981
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
982
                                  ' raising new style exceptions with python'
983
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
984
            else:
985
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
986
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
987
    def test_short_line(self):
988
        transport = MockTransport([
989
            _KnitIndex.HEADER,
990
            "a option 0 10  :",
991
            "b option 10 10 0", # This line isn't terminated, ignored
992
            ])
993
        index = self.get_knit_index(transport, "filename", "r")
994
        self.assertEqual(['a'], index.get_versions())
995
996
    def test_skip_incomplete_record(self):
997
        # A line with bogus data should just be skipped
998
        transport = MockTransport([
999
            _KnitIndex.HEADER,
1000
            "a option 0 10  :",
1001
            "b option 10 10 0", # This line isn't terminated, ignored
1002
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1003
            ])
1004
        index = self.get_knit_index(transport, "filename", "r")
1005
        self.assertEqual(['a', 'c'], index.get_versions())
1006
1007
    def test_trailing_characters(self):
1008
        # A line with bogus data should just be skipped
1009
        transport = MockTransport([
1010
            _KnitIndex.HEADER,
1011
            "a option 0 10  :",
1012
            "b option 10 10 0 :a", # This line has extra trailing characters
1013
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
1014
            ])
1015
        index = self.get_knit_index(transport, "filename", "r")
1016
        self.assertEqual(['a', 'c'], index.get_versions())
1017
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
1018
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1019
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
1020
1021
    _test_needs_features = [CompiledKnitFeature]
1022
1023
    def get_knit_index(self, *args, **kwargs):
1024
        orig = knit._load_data
1025
        def reset():
1026
            knit._load_data = orig
1027
        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
1028
        from bzrlib._knit_load_data_c import _load_data_c
1029
        knit._load_data = _load_data_c
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1030
        return _KnitIndex(get_scope=lambda:None, *args, **kwargs)
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
1031
1032
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1033
class KnitTests(TestCaseWithTransport):
1034
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1035
2535.3.27 by Andrew Bennetts
Merge from bzr.dev.
1036
    def make_test_knit(self, annotate=False, delay_create=False, index=None,
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1037
                       name='test', delta=True, access_mode='w'):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1038
        if not annotate:
1039
            factory = KnitPlainFactory()
1040
        else:
1041
            factory = None
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1042
        if index is None:
1043
            index = _KnitIndex(get_transport('.'), name + INDEX_SUFFIX,
1044
                access_mode, create=True, file_mode=None,
1045
                create_parent_dir=False, delay_create=delay_create,
1046
                dir_mode=None, get_scope=lambda:None)
1047
        access = _KnitAccess(get_transport('.'), name + DATA_SUFFIX, None,
1048
            None, delay_create, False)
1049
        return KnitVersionedFile(name, get_transport('.'), factory=factory,
1050
            create=True, delay_create=delay_create, index=index,
1051
            access_method=access, delta=delta)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1052
2535.3.53 by Andrew Bennetts
Remove get_stream_as_bytes from KnitVersionedFile's API, make it a function in knitrepo.py instead.
1053
    def assertRecordContentEqual(self, knit, version_id, candidate_content):
1054
        """Assert that some raw record content matches the raw record content
1055
        for a particular version_id in the given knit.
1056
        """
1057
        index_memo = knit._index.get_position(version_id)
1058
        record = (version_id, index_memo)
1059
        [(_, expected_content)] = list(knit._data.read_records_iter_raw([record]))
1060
        self.assertEqual(expected_content, candidate_content)
1061
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1062
1063
class BasicKnitTests(KnitTests):
1064
1065
    def add_stock_one_and_one_a(self, k):
1066
        k.add_lines('text-1', [], split_lines(TEXT_1))
1067
        k.add_lines('text-1a', ['text-1'], split_lines(TEXT_1A))
1068
1069
    def test_knit_constructor(self):
1070
        """Construct empty k"""
1071
        self.make_test_knit()
1072
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
1073
    def test_make_explicit_index(self):
1074
        """We can supply an index to use."""
1075
        knit = KnitVersionedFile('test', get_transport('.'),
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1076
            index='strangelove', access_method="a")
2592.3.1 by Robert Collins
Allow giving KnitVersionedFile an index object to use rather than implicitly creating one.
1077
        self.assertEqual(knit._index, 'strangelove')
1078
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1079
    def test_knit_add(self):
1080
        """Store one text in knit and retrieve"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1081
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1082
        k.add_lines('text-1', [], split_lines(TEXT_1))
1083
        self.assertTrue(k.has_version('text-1'))
1084
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
1085
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1086
    def test_newline_empty_lines(self):
1087
        # ensure that ["\n"] round trips ok.
1088
        knit = self.make_test_knit()
1089
        knit.add_lines('a', [], ["\n"])
1090
        knit.add_lines_with_ghosts('b', [], ["\n"])
1091
        self.assertEqual(["\n"], knit.get_lines('a'))
1092
        self.assertEqual(["\n"], knit.get_lines('b'))
1093
        self.assertEqual(['fulltext'], knit._index.get_options('a'))
1094
        self.assertEqual(['fulltext'], knit._index.get_options('b'))
1095
        knit.add_lines('c', ['a'], ["\n"])
1096
        knit.add_lines_with_ghosts('d', ['b'], ["\n"])
1097
        self.assertEqual(["\n"], knit.get_lines('c'))
1098
        self.assertEqual(["\n"], knit.get_lines('d'))
1099
        self.assertEqual(['line-delta'], knit._index.get_options('c'))
1100
        self.assertEqual(['line-delta'], knit._index.get_options('d'))
1101
1102
    def test_empty_lines(self):
1103
        # bizarrely, [] is not listed as having no-eol. 
1104
        knit = self.make_test_knit()
1105
        knit.add_lines('a', [], [])
1106
        knit.add_lines_with_ghosts('b', [], [])
1107
        self.assertEqual([], knit.get_lines('a'))
1108
        self.assertEqual([], knit.get_lines('b'))
1109
        self.assertEqual(['fulltext'], knit._index.get_options('a'))
1110
        self.assertEqual(['fulltext'], knit._index.get_options('b'))
1111
        knit.add_lines('c', ['a'], [])
1112
        knit.add_lines_with_ghosts('d', ['b'], [])
1113
        self.assertEqual([], knit.get_lines('c'))
1114
        self.assertEqual([], knit.get_lines('d'))
1115
        self.assertEqual(['line-delta'], knit._index.get_options('c'))
1116
        self.assertEqual(['line-delta'], knit._index.get_options('d'))
1117
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1118
    def test_knit_reload(self):
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1119
        # test that the content in a reloaded knit is correct
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1120
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1121
        k.add_lines('text-1', [], split_lines(TEXT_1))
1122
        del k
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1123
        k2 = make_file_knit('test', get_transport('.'), access_mode='r',
1124
            factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1125
        self.assertTrue(k2.has_version('text-1'))
1126
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
1127
1128
    def test_knit_several(self):
1129
        """Store several texts in a knit"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1130
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1131
        k.add_lines('text-1', [], split_lines(TEXT_1))
1132
        k.add_lines('text-2', [], split_lines(TEXT_2))
1133
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
1134
        self.assertEqualDiff(''.join(k.get_lines('text-2')), TEXT_2)
1135
        
1136
    def test_repeated_add(self):
1137
        """Knit traps attempt to replace existing version"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1138
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1139
        k.add_lines('text-1', [], split_lines(TEXT_1))
1140
        self.assertRaises(RevisionAlreadyPresent, 
1141
                k.add_lines,
1142
                'text-1', [], split_lines(TEXT_1))
1143
1144
    def test_empty(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1145
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1146
        k.add_lines('text-1', [], [])
1147
        self.assertEquals(k.get_lines('text-1'), [])
1148
1149
    def test_incomplete(self):
1150
        """Test if texts without a ending line-end can be inserted and
1151
        extracted."""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1152
        k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1153
        k.add_lines('text-1', [], ['a\n',    'b'  ])
1154
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1666.1.6 by Robert Collins
Make knit the default format.
1155
        # reopening ensures maximum room for confusion
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1156
        k = make_file_knit('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1157
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
1158
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
1159
1160
    def test_delta(self):
1161
        """Expression of knit delta as lines"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1162
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1163
        td = list(line_delta(TEXT_1.splitlines(True),
1164
                             TEXT_1A.splitlines(True)))
1165
        self.assertEqualDiff(''.join(td), delta_1_1a)
1166
        out = apply_line_delta(TEXT_1.splitlines(True), td)
1167
        self.assertEqualDiff(''.join(out), TEXT_1A)
1168
1169
    def test_add_with_parents(self):
1170
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1171
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1172
        self.add_stock_one_and_one_a(k)
3287.5.2 by Robert Collins
Deprecate VersionedFile.get_parents, breaking pulling from a ghost containing knit or pack repository to weaves, which improves correctness and allows simplification of core code.
1173
        self.assertEqual({'text-1':(), 'text-1a':('text-1',)},
1174
            k.get_parent_map(['text-1', 'text-1a']))
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1175
1176
    def test_ancestry(self):
1177
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1178
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1179
        self.add_stock_one_and_one_a(k)
1180
        self.assertEquals(set(k.get_ancestry(['text-1a'])), set(['text-1a', 'text-1']))
1181
1182
    def test_add_delta(self):
1183
        """Store in knit with parents"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1184
        k = self.make_test_knit(annotate=False)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1185
        self.add_stock_one_and_one_a(k)
1186
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1187
2592.3.21 by Robert Collins
Test using a KnitGraphIndex for storage.
1188
    def test_add_delta_knit_graph_index(self):
1189
        """Does adding work with a KnitGraphIndex."""
1190
        index = InMemoryGraphIndex(2)
1191
        knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
1192
            deltas=True)
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1193
        k = self.make_test_knit(annotate=True, index=knit_index)
2592.3.21 by Robert Collins
Test using a KnitGraphIndex for storage.
1194
        self.add_stock_one_and_one_a(k)
1195
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1196
        # check the index had the right data added.
1197
        self.assertEqual(set([
2624.2.14 by Robert Collins
Add source index to the index iteration API to allow mapping back to the origin of retrieved data.
1198
            (index, ('text-1', ), ' 0 127', ((), ())),
1199
            (index, ('text-1a', ), ' 127 140', ((('text-1', ),), (('text-1', ),))),
2592.3.21 by Robert Collins
Test using a KnitGraphIndex for storage.
1200
            ]), set(index.iter_all_entries()))
1201
        # we should not have a .kndx file
1202
        self.assertFalse(get_transport('.').has('test.kndx'))
1203
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1204
    def test_annotate(self):
1205
        """Annotations"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1206
        k = self.make_test_knit(annotate=True, name='knit')
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1207
        self.insert_and_test_small_annotate(k)
1208
1209
    def insert_and_test_small_annotate(self, k):
1210
        """test annotation with k works correctly."""
1211
        k.add_lines('text-1', [], ['a\n', 'b\n'])
1212
        k.add_lines('text-2', ['text-1'], ['a\n', 'c\n'])
1213
1214
        origins = k.annotate('text-2')
1215
        self.assertEquals(origins[0], ('text-1', 'a\n'))
1216
        self.assertEquals(origins[1], ('text-2', 'c\n'))
1217
1218
    def test_annotate_fulltext(self):
1219
        """Annotations"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1220
        k = self.make_test_knit(annotate=True, name='knit', delta=False)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1221
        self.insert_and_test_small_annotate(k)
1222
1223
    def test_annotate_merge_1(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1224
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1225
        k.add_lines('text-a1', [], ['a\n', 'b\n'])
1226
        k.add_lines('text-a2', [], ['d\n', 'c\n'])
1227
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['d\n', 'b\n'])
1228
        origins = k.annotate('text-am')
1229
        self.assertEquals(origins[0], ('text-a2', 'd\n'))
1230
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
1231
1232
    def test_annotate_merge_2(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1233
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1234
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1235
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1236
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['a\n', 'y\n', 'c\n'])
1237
        origins = k.annotate('text-am')
1238
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1239
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1240
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
1241
1242
    def test_annotate_merge_9(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1243
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1244
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1245
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1246
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'c\n'])
1247
        origins = k.annotate('text-am')
1248
        self.assertEquals(origins[0], ('text-am', 'k\n'))
1249
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1250
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
1251
1252
    def test_annotate_merge_3(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1253
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1254
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1255
        k.add_lines('text-a2', [] ,['x\n', 'y\n', 'z\n'])
1256
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'z\n'])
1257
        origins = k.annotate('text-am')
1258
        self.assertEquals(origins[0], ('text-am', 'k\n'))
1259
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1260
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
1261
1262
    def test_annotate_merge_4(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1263
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1264
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1265
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1266
        k.add_lines('text-a3', ['text-a1'], ['a\n', 'b\n', 'p\n'])
1267
        k.add_lines('text-am', ['text-a2', 'text-a3'], ['a\n', 'b\n', 'z\n'])
1268
        origins = k.annotate('text-am')
1269
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1270
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
1271
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
1272
1273
    def test_annotate_merge_5(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1274
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1275
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1276
        k.add_lines('text-a2', [], ['d\n', 'e\n', 'f\n'])
1277
        k.add_lines('text-a3', [], ['x\n', 'y\n', 'z\n'])
1278
        k.add_lines('text-am',
1279
                    ['text-a1', 'text-a2', 'text-a3'],
1280
                    ['a\n', 'e\n', 'z\n'])
1281
        origins = k.annotate('text-am')
1282
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1283
        self.assertEquals(origins[1], ('text-a2', 'e\n'))
1284
        self.assertEquals(origins[2], ('text-a3', 'z\n'))
1285
1286
    def test_annotate_file_cherry_pick(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1287
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1288
        k.add_lines('text-1', [], ['a\n', 'b\n', 'c\n'])
1289
        k.add_lines('text-2', ['text-1'], ['d\n', 'e\n', 'f\n'])
1290
        k.add_lines('text-3', ['text-2', 'text-1'], ['a\n', 'b\n', 'c\n'])
1291
        origins = k.annotate('text-3')
1292
        self.assertEquals(origins[0], ('text-1', 'a\n'))
1293
        self.assertEquals(origins[1], ('text-1', 'b\n'))
1294
        self.assertEquals(origins[2], ('text-1', 'c\n'))
1295
2851.4.6 by Ian Clatworthy
review tweaks
1296
    def _test_join_with_factories(self, k1_factory, k2_factory):
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1297
        k1 = make_file_knit('test1', get_transport('.'), factory=k1_factory, create=True)
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1298
        k1.add_lines('text-a', [], ['a1\n', 'a2\n', 'a3\n'])
1299
        k1.add_lines('text-b', ['text-a'], ['a1\n', 'b2\n', 'a3\n'])
1300
        k1.add_lines('text-c', [], ['c1\n', 'c2\n', 'c3\n'])
1301
        k1.add_lines('text-d', ['text-c'], ['c1\n', 'd2\n', 'd3\n'])
1302
        k1.add_lines('text-m', ['text-b', 'text-d'], ['a1\n', 'b2\n', 'd3\n'])
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1303
        k2 = make_file_knit('test2', get_transport('.'), factory=k2_factory, create=True)
2851.4.6 by Ian Clatworthy
review tweaks
1304
        count = k2.join(k1, version_ids=['text-m'])
1305
        self.assertEquals(count, 5)
1306
        self.assertTrue(k2.has_version('text-a'))
1307
        self.assertTrue(k2.has_version('text-c'))
1308
        origins = k2.annotate('text-m')
1309
        self.assertEquals(origins[0], ('text-a', 'a1\n'))
1310
        self.assertEquals(origins[1], ('text-b', 'b2\n'))
1311
        self.assertEquals(origins[2], ('text-d', 'd3\n'))
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1312
1313
    def test_knit_join_plain_to_plain(self):
1314
        """Test joining a plain knit with a plain knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1315
        self._test_join_with_factories(KnitPlainFactory(), KnitPlainFactory())
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1316
1317
    def test_knit_join_anno_to_anno(self):
1318
        """Test joining an annotated knit with an annotated knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1319
        self._test_join_with_factories(None, None)
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1320
1321
    def test_knit_join_anno_to_plain(self):
1322
        """Test joining an annotated knit with a plain knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1323
        self._test_join_with_factories(None, KnitPlainFactory())
2851.4.1 by Ian Clatworthy
Support joining plain knits to annotated knits and vice versa
1324
1325
    def test_knit_join_plain_to_anno(self):
1326
        """Test joining a plain knit with an annotated knit."""
2851.4.6 by Ian Clatworthy
review tweaks
1327
        self._test_join_with_factories(KnitPlainFactory(), None)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1328
1329
    def test_reannotate(self):
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1330
        k1 = make_file_knit('knit1', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
1331
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1332
        # 0
1333
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
1334
        # 1
1335
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
1336
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1337
        k2 = make_file_knit('test2', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
1338
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1339
        k2.join(k1, version_ids=['text-b'])
1340
1341
        # 2
1342
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
1343
        # 2
1344
        k2.add_lines('text-c', ['text-b'], ['z\n', 'c\n'])
1345
        # 3
1346
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
1347
1348
        # test-c will have index 3
1349
        k1.join(k2, version_ids=['text-c'])
1350
1351
        lines = k1.get_lines('text-c')
1352
        self.assertEquals(lines, ['z\n', 'c\n'])
1353
1354
        origins = k1.annotate('text-c')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
1355
        self.assertEquals(origins[0], ('text-c', 'z\n'))
1356
        self.assertEquals(origins[1], ('text-b', 'c\n'))
1357
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
1358
    def test_get_line_delta_texts(self):
1359
        """Make sure we can call get_texts on text with reused line deltas"""
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1360
        k1 = make_file_knit('test1', get_transport('.'),
1361
            factory=KnitPlainFactory(), create=True)
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
1362
        for t in range(3):
1363
            if t == 0:
1364
                parents = []
1365
            else:
1366
                parents = ['%d' % (t-1)]
1367
            k1.add_lines('%d' % t, parents, ['hello\n'] * t)
1368
        k1.get_texts(('%d' % t) for t in range(3))
1594.3.1 by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit.
1369
        
1370
    def test_iter_lines_reads_in_order(self):
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1371
        instrumented_t = get_transport('trace+memory:///')
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1372
        k1 = make_file_knit('id', instrumented_t, create=True, delta=True)
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1373
        self.assertEqual([('get', 'id.kndx',)], instrumented_t._activity)
1594.3.1 by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit.
1374
        # add texts with no required ordering
1375
        k1.add_lines('base', [], ['text\n'])
1376
        k1.add_lines('base2', [], ['text2\n'])
2745.5.4 by Robert Collins
Review feedback.
1377
        # clear the logged activity, but preserve the list instance in case of
1378
        # clones pointing at it.
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1379
        del instrumented_t._activity[:]
1594.3.1 by Robert Collins
Merge transaction finalisation and ensure iter_lines_added_or_present in knits does a old-to-new read in the knit.
1380
        # request a last-first iteration
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1381
        results = list(k1.iter_lines_added_or_present_in_versions(
1382
            ['base2', 'base']))
2745.5.6 by Robert Collins
Fix knit test fallout from final readv api change.
1383
        self.assertEqual(
1384
            [('readv', 'id.knit', [(0, 87), (87, 89)], False, None)],
2745.5.3 by Robert Collins
* Move transport logging into a new transport class
1385
            instrumented_t._activity)
2975.3.1 by Robert Collins
Change (without backwards compatibility) the
1386
        self.assertEqual([('text\n', 'base'), ('text2\n', 'base2')], results)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1387
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1388
    def test_knit_format(self):
1389
        # this tests that a new knit index file has the expected content
1390
        # and that is writes the data we expect as records are added.
1391
        knit = self.make_test_knit(True)
1946.2.1 by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories
1392
        # Now knit files are not created until we first add data to them
1666.1.6 by Robert Collins
Make knit the default format.
1393
        self.assertFileEqual("# bzr knit index 8\n", 'test.kndx')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1394
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
1395
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
1396
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1397
            "\n"
1398
            "revid fulltext 0 84 .a_ghost :",
1399
            'test.kndx')
1400
        knit.add_lines_with_ghosts('revid2', ['revid'], ['a\n'])
1401
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
1402
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1403
            "\nrevid fulltext 0 84 .a_ghost :"
1404
            "\nrevid2 line-delta 84 82 0 :",
1405
            'test.kndx')
1406
        # we should be able to load this file again
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1407
        knit = make_file_knit('test', get_transport('.'), access_mode='r')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1408
        self.assertEqual(['revid', 'revid2'], knit.versions())
1409
        # write a short write to the file and ensure that its ignored
2484.1.23 by John Arbash Meinel
When we append a new line, don't use text mode
1410
        indexfile = file('test.kndx', 'ab')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1411
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1412
        indexfile.close()
1413
        # we should be able to load this file again
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1414
        knit = make_file_knit('test', get_transport('.'), access_mode='w')
1654.1.5 by Robert Collins
Merge partial index write support for knits, adding a test case per review comments.
1415
        self.assertEqual(['revid', 'revid2'], knit.versions())
1416
        # and add a revision with the same id the failed write had
1417
        knit.add_lines('revid3', ['revid2'], ['a\n'])
1418
        # and when reading it revid3 should now appear.
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1419
        knit = make_file_knit('test', get_transport('.'), access_mode='r')
1654.1.5 by Robert Collins
Merge partial index write support for knits, adding a test case per review comments.
1420
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
3287.5.2 by Robert Collins
Deprecate VersionedFile.get_parents, breaking pulling from a ghost containing knit or pack repository to weaves, which improves correctness and allows simplification of core code.
1421
        self.assertEqual({'revid3':('revid2',)}, knit.get_parent_map(['revid3']))
1654.1.5 by Robert Collins
Merge partial index write support for knits, adding a test case per review comments.
1422
1946.2.1 by John Arbash Meinel
2 changes to knits. Delay creating the .knit or .kndx file until we have actually tried to write data. Because of this, we must allow the Knit to create the prefix directories
1423
    def test_delay_create(self):
1424
        """Test that passing delay_create=True creates files late"""
1425
        knit = self.make_test_knit(annotate=True, delay_create=True)
1426
        self.failIfExists('test.knit')
1427
        self.failIfExists('test.kndx')
1428
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
1429
        self.failUnlessExists('test.knit')
1430
        self.assertFileEqual(
1431
            "# bzr knit index 8\n"
1432
            "\n"
1433
            "revid fulltext 0 84 .a_ghost :",
1434
            'test.kndx')
1435
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1436
    def test_create_parent_dir(self):
1437
        """create_parent_dir can create knits in nonexistant dirs"""
1438
        # Has no effect if we don't set 'delay_create'
1439
        trans = get_transport('.')
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1440
        self.assertRaises(NoSuchFile, make_file_knit, 'dir/test',
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1441
                          trans, access_mode='w', factory=None,
1442
                          create=True, create_parent_dir=True)
1443
        # Nothing should have changed yet
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1444
        knit = make_file_knit('dir/test', trans, access_mode='w',
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1445
                                 factory=None, create=True,
1446
                                 create_parent_dir=True,
1447
                                 delay_create=True)
1448
        self.failIfExists('dir/test.knit')
1449
        self.failIfExists('dir/test.kndx')
1450
        self.failIfExists('dir')
1451
        knit.add_lines('revid', [], ['a\n'])
1452
        self.failUnlessExists('dir')
1453
        self.failUnlessExists('dir/test.knit')
1454
        self.assertFileEqual(
1455
            "# bzr knit index 8\n"
1456
            "\n"
1457
            "revid fulltext 0 84  :",
1458
            'dir/test.kndx')
1459
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1460
    def test_create_mode_700(self):
1461
        trans = get_transport('.')
1462
        if not trans._can_roundtrip_unix_modebits():
1463
            # Can't roundtrip, so no need to run this test
1464
            return
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1465
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1466
            create=True, create_parent_dir=True, delay_create=True,
1467
            file_mode=0600, dir_mode=0700)
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1468
        knit.add_lines('revid', [], ['a\n'])
1469
        self.assertTransportMode(trans, 'dir', 0700)
1470
        self.assertTransportMode(trans, 'dir/test.knit', 0600)
1471
        self.assertTransportMode(trans, 'dir/test.kndx', 0600)
1472
1473
    def test_create_mode_770(self):
1474
        trans = get_transport('.')
1475
        if not trans._can_roundtrip_unix_modebits():
1476
            # Can't roundtrip, so no need to run this test
1477
            return
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1478
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1479
            create=True, create_parent_dir=True, delay_create=True,
1480
            file_mode=0660, dir_mode=0770)
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1481
        knit.add_lines('revid', [], ['a\n'])
1482
        self.assertTransportMode(trans, 'dir', 0770)
1483
        self.assertTransportMode(trans, 'dir/test.knit', 0660)
1484
        self.assertTransportMode(trans, 'dir/test.kndx', 0660)
1485
1486
    def test_create_mode_777(self):
1487
        trans = get_transport('.')
1488
        if not trans._can_roundtrip_unix_modebits():
1489
            # Can't roundtrip, so no need to run this test
1490
            return
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
1491
        knit = make_file_knit('dir/test', trans, access_mode='w', factory=None,
1492
            create=True, create_parent_dir=True, delay_create=True,
1493
            file_mode=0666, dir_mode=0777)
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1494
        knit.add_lines('revid', [], ['a\n'])
1495
        self.assertTransportMode(trans, 'dir', 0777)
1496
        self.assertTransportMode(trans, 'dir/test.knit', 0666)
1497
        self.assertTransportMode(trans, 'dir/test.kndx', 0666)
1498
1664.2.1 by Aaron Bentley
Start work on plan_merge test
1499
    def test_plan_merge(self):
1500
        my_knit = self.make_test_knit(annotate=True)
1501
        my_knit.add_lines('text1', [], split_lines(TEXT_1))
1502
        my_knit.add_lines('text1a', ['text1'], split_lines(TEXT_1A))
1503
        my_knit.add_lines('text1b', ['text1'], split_lines(TEXT_1B))
1664.2.3 by Aaron Bentley
Add failing test case
1504
        plan = list(my_knit.plan_merge('text1a', 'text1b'))
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
1505
        for plan_line, expected_line in zip(plan, AB_MERGE):
1506
            self.assertEqual(plan_line, expected_line)
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1507
3370.1.4 by Andrew Bennetts
Organise data stream tests in test_knit slightly better.
1508
1509
class GetDataStreamTests(KnitTests):
1510
    """Tests for get_data_stream."""
1511
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1512
    def test_get_stream_empty(self):
1513
        """Get a data stream for an empty knit file."""
1514
        k1 = self.make_test_knit()
1515
        format, data_list, reader_callable = k1.get_data_stream([])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1516
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1517
        self.assertEqual([], data_list)
1518
        content = reader_callable(None)
1519
        self.assertEqual('', content)
1520
        self.assertIsInstance(content, str)
1521
1522
    def test_get_stream_one_version(self):
1523
        """Get a data stream for a single record out of a knit containing just
1524
        one record.
1525
        """
1526
        k1 = self.make_test_knit()
1527
        test_data = [
1528
            ('text-a', [], TEXT_1),
1529
            ]
1530
        expected_data_list = [
1531
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1532
            ('text-a', ['fulltext'], 122, ()),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1533
           ]
1534
        for version_id, parents, lines in test_data:
1535
            k1.add_lines(version_id, parents, split_lines(lines))
1536
1537
        format, data_list, reader_callable = k1.get_data_stream(['text-a'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1538
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1539
        self.assertEqual(expected_data_list, data_list)
1540
        # There's only one record in the knit, so the content should be the
1541
        # entire knit data file's contents.
2535.3.36 by Andrew Bennetts
Merge bzr.dev
1542
        self.assertEqual(k1.transport.get_bytes(k1._data._access._filename),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1543
                         reader_callable(None))
1544
        
1545
    def test_get_stream_get_one_version_of_many(self):
1546
        """Get a data stream for just one version out of a knit containing many
1547
        versions.
1548
        """
1549
        k1 = self.make_test_knit()
1550
        # Insert the same data as test_knit_join, as they seem to cover a range
1551
        # of cases (no parents, one parent, multiple parents).
1552
        test_data = [
1553
            ('text-a', [], TEXT_1),
1554
            ('text-b', ['text-a'], TEXT_1),
1555
            ('text-c', [], TEXT_1),
1556
            ('text-d', ['text-c'], TEXT_1),
1557
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1558
            ]
1559
        expected_data_list = [
1560
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1561
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1562
            ]
1563
        for version_id, parents, lines in test_data:
1564
            k1.add_lines(version_id, parents, split_lines(lines))
1565
1566
        format, data_list, reader_callable = k1.get_data_stream(['text-m'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1567
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1568
        self.assertEqual(expected_data_list, data_list)
1569
        self.assertRecordContentEqual(k1, 'text-m', reader_callable(None))
1570
        
3023.2.2 by Martin Pool
Fix KnitVersionedFile.get_data_stream to not assume .versions() is sorted. (lp:165106)
1571
    def test_get_data_stream_unordered_index(self):
1572
        """Get a data stream when the knit index reports versions out of order.
1573
1574
        https://bugs.launchpad.net/bzr/+bug/164637
1575
        """
1576
        k1 = self.make_test_knit()
1577
        test_data = [
1578
            ('text-a', [], TEXT_1),
1579
            ('text-b', ['text-a'], TEXT_1),
1580
            ('text-c', [], TEXT_1),
1581
            ('text-d', ['text-c'], TEXT_1),
1582
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1583
            ]
1584
        for version_id, parents, lines in test_data:
1585
            k1.add_lines(version_id, parents, split_lines(lines))
1586
        # monkey-patch versions method to return out of order, as if coming
1587
        # from multiple independently indexed packs
1588
        original_versions = k1.versions
1589
        k1.versions = lambda: reversed(original_versions())
1590
        expected_data_list = [
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1591
            ('text-a', ['fulltext'], 122, ()),
1592
            ('text-b', ['line-delta'], 84, ('text-a',))]
3023.2.2 by Martin Pool
Fix KnitVersionedFile.get_data_stream to not assume .versions() is sorted. (lp:165106)
1593
        # now check the fulltext is first and the delta second
1594
        format, data_list, _ = k1.get_data_stream(['text-a', 'text-b'])
1595
        self.assertEqual('knit-plain', format)
1596
        self.assertEqual(expected_data_list, data_list)
1597
        # and that's true if we ask for them in the opposite order too
1598
        format, data_list, _ = k1.get_data_stream(['text-b', 'text-a'])
1599
        self.assertEqual(expected_data_list, data_list)
1600
        # also try requesting more versions
1601
        format, data_list, _ = k1.get_data_stream([
1602
            'text-m', 'text-b', 'text-a'])
1603
        self.assertEqual([
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1604
            ('text-a', ['fulltext'], 122, ()),
1605
            ('text-b', ['line-delta'], 84, ('text-a',)),
1606
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
3023.2.2 by Martin Pool
Fix KnitVersionedFile.get_data_stream to not assume .versions() is sorted. (lp:165106)
1607
            ], data_list)
1608
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1609
    def test_get_stream_ghost_parent(self):
1610
        """Get a data stream for a version with a ghost parent."""
1611
        k1 = self.make_test_knit()
1612
        # Test data
1613
        k1.add_lines('text-a', [], split_lines(TEXT_1))
1614
        k1.add_lines_with_ghosts('text-b', ['text-a', 'text-ghost'],
1615
                                 split_lines(TEXT_1))
1616
        # Expected data
1617
        expected_data_list = [
1618
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1619
            ('text-b', ['line-delta'], 84, ('text-a', 'text-ghost')),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1620
            ]
1621
        
1622
        format, data_list, reader_callable = k1.get_data_stream(['text-b'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1623
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1624
        self.assertEqual(expected_data_list, data_list)
1625
        self.assertRecordContentEqual(k1, 'text-b', reader_callable(None))
1626
    
1627
    def test_get_stream_get_multiple_records(self):
1628
        """Get a stream for multiple records of a knit."""
1629
        k1 = self.make_test_knit()
1630
        # Insert the same data as test_knit_join, as they seem to cover a range
1631
        # of cases (no parents, one parent, multiple parents).
1632
        test_data = [
1633
            ('text-a', [], TEXT_1),
1634
            ('text-b', ['text-a'], TEXT_1),
1635
            ('text-c', [], TEXT_1),
1636
            ('text-d', ['text-c'], TEXT_1),
1637
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1638
            ]
3023.2.3 by Martin Pool
Update tests for new ordering of results from get_data_stream - the order is not defined by the interface, but is stable
1639
        for version_id, parents, lines in test_data:
1640
            k1.add_lines(version_id, parents, split_lines(lines))
1641
1642
        # This test is actually a bit strict as the order in which they're
1643
        # returned is not defined.  This matches the current (deterministic)
1644
        # behaviour.
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1645
        expected_data_list = [
1646
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1647
            ('text-d', ['line-delta'], 84, ('text-c',)),
1648
            ('text-b', ['line-delta'], 84, ('text-a',)),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1649
            ]
1650
        # Note that even though we request the revision IDs in a particular
1651
        # order, the data stream may return them in any order it likes.  In this
1652
        # case, they'll be in the order they were inserted into the knit.
1653
        format, data_list, reader_callable = k1.get_data_stream(
1654
            ['text-d', 'text-b'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1655
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1656
        self.assertEqual(expected_data_list, data_list)
3023.2.3 by Martin Pool
Update tests for new ordering of results from get_data_stream - the order is not defined by the interface, but is stable
1657
        # must match order they're returned
1658
        self.assertRecordContentEqual(k1, 'text-d', reader_callable(84))
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1659
        self.assertRecordContentEqual(k1, 'text-b', reader_callable(84))
1660
        self.assertEqual('', reader_callable(None),
1661
                         "There should be no more bytes left to read.")
1662
1663
    def test_get_stream_all(self):
1664
        """Get a data stream for all the records in a knit.
1665
1666
        This exercises fulltext records, line-delta records, records with
1667
        various numbers of parents, and reading multiple records out of the
1668
        callable.  These cases ought to all be exercised individually by the
1669
        other test_get_stream_* tests; this test is basically just paranoia.
1670
        """
1671
        k1 = self.make_test_knit()
1672
        # Insert the same data as test_knit_join, as they seem to cover a range
1673
        # of cases (no parents, one parent, multiple parents).
1674
        test_data = [
1675
            ('text-a', [], TEXT_1),
1676
            ('text-b', ['text-a'], TEXT_1),
1677
            ('text-c', [], TEXT_1),
1678
            ('text-d', ['text-c'], TEXT_1),
1679
            ('text-m', ['text-b', 'text-d'], TEXT_1),
1680
           ]
3023.2.3 by Martin Pool
Update tests for new ordering of results from get_data_stream - the order is not defined by the interface, but is stable
1681
        for version_id, parents, lines in test_data:
1682
            k1.add_lines(version_id, parents, split_lines(lines))
1683
1684
        # This test is actually a bit strict as the order in which they're
1685
        # returned is not defined.  This matches the current (deterministic)
1686
        # behaviour.
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1687
        expected_data_list = [
1688
            # version, options, length, parents
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
1689
            ('text-a', ['fulltext'], 122, ()),
1690
            ('text-b', ['line-delta'], 84, ('text-a',)),
1691
            ('text-m', ['line-delta'], 84, ('text-b', 'text-d')),
1692
            ('text-c', ['fulltext'], 121, ()),
1693
            ('text-d', ['line-delta'], 84, ('text-c',)),
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1694
            ]
1695
        format, data_list, reader_callable = k1.get_data_stream(
1696
            ['text-a', 'text-b', 'text-c', 'text-d', 'text-m'])
2535.3.17 by Andrew Bennetts
[broken] Closer to a working Repository.fetch_revisions smart request.
1697
        self.assertEqual('knit-plain', format)
2535.3.3 by Andrew Bennetts
Add Knit.get_data_stream.
1698
        self.assertEqual(expected_data_list, data_list)
1699
        for version_id, options, length, parents in expected_data_list:
1700
            bytes = reader_callable(length)
1701
            self.assertRecordContentEqual(k1, version_id, bytes)
1702
3370.1.4 by Andrew Bennetts
Organise data stream tests in test_knit slightly better.
1703
1704
class InsertDataStreamTests(KnitTests):
1705
    """Tests for insert_data_stream."""
1706
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1707
    def assertKnitFilesEqual(self, knit1, knit2):
1708
        """Assert that the contents of the index and data files of two knits are
1709
        equal.
1710
        """
1711
        self.assertEqual(
2535.3.36 by Andrew Bennetts
Merge bzr.dev
1712
            knit1.transport.get_bytes(knit1._data._access._filename),
1713
            knit2.transport.get_bytes(knit2._data._access._filename))
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1714
        self.assertEqual(
1715
            knit1.transport.get_bytes(knit1._index._filename),
1716
            knit2.transport.get_bytes(knit2._index._filename))
1717
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1718
    def assertKnitValuesEqual(self, left, right):
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
1719
        """Assert that the texts, annotations and graph of left and right are
1720
        the same.
1721
        """
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1722
        self.assertEqual(set(left.versions()), set(right.versions()))
1723
        for version in left.versions():
1724
            self.assertEqual(left.get_parents_with_ghosts(version),
1725
                right.get_parents_with_ghosts(version))
1726
            self.assertEqual(left.get_lines(version),
1727
                right.get_lines(version))
1728
            self.assertEqual(left.annotate(version),
1729
                right.annotate(version))
1730
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1731
    def test_empty_stream(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1732
        """Inserting a data stream with no records should not put any data into
1733
        the knit.
1734
        """
1735
        k1 = self.make_test_knit()
1736
        k1.insert_data_stream(
1737
            (k1.get_format_signature(), [], lambda ignored: ''))
2535.3.36 by Andrew Bennetts
Merge bzr.dev
1738
        self.assertEqual('', k1.transport.get_bytes(k1._data._access._filename),
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1739
                         "The .knit should be completely empty.")
1740
        self.assertEqual(k1._index.HEADER,
1741
                         k1.transport.get_bytes(k1._index._filename),
1742
                         "The .kndx should have nothing apart from the header.")
1743
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1744
    def test_one_record(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1745
        """Inserting a data stream with one record from a knit with one record
1746
        results in byte-identical files.
1747
        """
1748
        source = self.make_test_knit(name='source')
1749
        source.add_lines('text-a', [], split_lines(TEXT_1))
1750
        data_stream = source.get_data_stream(['text-a'])
1751
        target = self.make_test_knit(name='target')
1752
        target.insert_data_stream(data_stream)
1753
        self.assertKnitFilesEqual(source, target)
1754
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1755
    def test_annotated_stream_into_unannotated_knit(self):
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1756
        """Inserting an annotated datastream to an unannotated knit works."""
1757
        # case one - full texts.
1758
        source = self.make_test_knit(name='source', annotate=True)
1759
        target = self.make_test_knit(name='target', annotate=False)
1760
        source.add_lines('text-a', [], split_lines(TEXT_1))
1761
        target.insert_data_stream(source.get_data_stream(['text-a']))
1762
        self.assertKnitValuesEqual(source, target)
1763
        # case two - deltas.
1764
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_2))
1765
        target.insert_data_stream(source.get_data_stream(['text-b']))
1766
        self.assertKnitValuesEqual(source, target)
1767
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1768
    def test_unannotated_stream_into_annotated_knit(self):
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
1769
        """Inserting an unannotated datastream to an annotated knit works."""
1770
        # case one - full texts.
1771
        source = self.make_test_knit(name='source', annotate=False)
1772
        target = self.make_test_knit(name='target', annotate=True)
1773
        source.add_lines('text-a', [], split_lines(TEXT_1))
1774
        target.insert_data_stream(source.get_data_stream(['text-a']))
1775
        self.assertKnitValuesEqual(source, target)
1776
        # case two - deltas.
1777
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_2))
1778
        target.insert_data_stream(source.get_data_stream(['text-b']))
1779
        self.assertKnitValuesEqual(source, target)
1780
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1781
    def test_records_already_present(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1782
        """Insert a data stream where some records are alreday present in the
1783
        target, and some not.  Only the new records are inserted.
1784
        """
1785
        source = self.make_test_knit(name='source')
1786
        target = self.make_test_knit(name='target')
1787
        # Insert 'text-a' into both source and target
1788
        source.add_lines('text-a', [], split_lines(TEXT_1))
1789
        target.insert_data_stream(source.get_data_stream(['text-a']))
1790
        # Insert 'text-b' into just the source.
1791
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
1792
        # Get a data stream of both text-a and text-b, and insert it.
1793
        data_stream = source.get_data_stream(['text-a', 'text-b'])
1794
        target.insert_data_stream(data_stream)
1795
        # The source and target will now be identical.  This means the text-a
1796
        # record was not added a second time.
1797
        self.assertKnitFilesEqual(source, target)
1798
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1799
    def test_multiple_records(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1800
        """Inserting a data stream of all records from a knit with multiple
1801
        records results in byte-identical files.
1802
        """
1803
        source = self.make_test_knit(name='source')
1804
        source.add_lines('text-a', [], split_lines(TEXT_1))
1805
        source.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
1806
        source.add_lines('text-c', [], split_lines(TEXT_1))
1807
        data_stream = source.get_data_stream(['text-a', 'text-b', 'text-c'])
1808
        
1809
        target = self.make_test_knit(name='target')
1810
        target.insert_data_stream(data_stream)
1811
        
1812
        self.assertKnitFilesEqual(source, target)
1813
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1814
    def test_ghost_parent(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1815
        """Insert a data stream with a record that has a ghost parent."""
1816
        # Make a knit with a record, text-a, that has a ghost parent.
1817
        source = self.make_test_knit(name='source')
1818
        source.add_lines_with_ghosts('text-a', ['text-ghost'],
1819
                                     split_lines(TEXT_1))
1820
        data_stream = source.get_data_stream(['text-a'])
1821
1822
        target = self.make_test_knit(name='target')
1823
        target.insert_data_stream(data_stream)
1824
1825
        self.assertKnitFilesEqual(source, target)
1826
1827
        # The target knit object is in a consistent state, i.e. the record we
1828
        # just added is immediately visible.
1829
        self.assertTrue(target.has_version('text-a'))
3287.6.5 by Robert Collins
Deprecate VersionedFile.has_ghost.
1830
        self.assertFalse(target.has_version('text-ghost'))
1831
        self.assertEqual({'text-a':('text-ghost',)},
1832
            target.get_parent_map(['text-a', 'text-ghost']))
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1833
        self.assertEqual(split_lines(TEXT_1), target.get_lines('text-a'))
1834
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1835
    def test_inconsistent_version_lines(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1836
        """Inserting a data stream which has different content for a version_id
1837
        than already exists in the knit will raise KnitCorrupt.
1838
        """
1839
        source = self.make_test_knit(name='source')
1840
        target = self.make_test_knit(name='target')
1841
        # Insert a different 'text-a' into both source and target
1842
        source.add_lines('text-a', [], split_lines(TEXT_1))
1843
        target.add_lines('text-a', [], split_lines(TEXT_2))
1844
        # Insert a data stream with conflicting content into the target
1845
        data_stream = source.get_data_stream(['text-a'])
1846
        self.assertRaises(
1847
            errors.KnitCorrupt, target.insert_data_stream, data_stream)
1848
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1849
    def test_inconsistent_version_parents(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1850
        """Inserting a data stream which has different parents for a version_id
1851
        than already exists in the knit will raise KnitCorrupt.
1852
        """
1853
        source = self.make_test_knit(name='source')
1854
        target = self.make_test_knit(name='target')
1855
        # Insert a different 'text-a' into both source and target.  They differ
1856
        # only by the parents list, the content is the same.
1857
        source.add_lines_with_ghosts('text-a', [], split_lines(TEXT_1))
1858
        target.add_lines_with_ghosts('text-a', ['a-ghost'], split_lines(TEXT_1))
1859
        # Insert a data stream with conflicting content into the target
1860
        data_stream = source.get_data_stream(['text-a'])
1861
        self.assertRaises(
1862
            errors.KnitCorrupt, target.insert_data_stream, data_stream)
1863
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1864
    def test_unknown_stream_format(self):
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1865
        """A data stream in a different format to the target knit cannot be
1866
        inserted.
1867
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1868
        It will raise KnitDataStreamUnknown because the fallback code will fail
1869
        to make a knit. In future we may need KnitDataStreamIncompatible again,
1870
        for more exotic cases.
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1871
        """
1872
        data_stream = ('fake-format-signature', [], lambda _: '')
1873
        target = self.make_test_knit(name='target')
1874
        self.assertRaises(
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1875
            errors.KnitDataStreamUnknown,
2535.3.4 by Andrew Bennetts
Simple implementation of Knit.insert_data_stream.
1876
            target.insert_data_stream, data_stream)
1877
3370.1.5 by Andrew Bennetts
Rename test methods to be a little clearer after reorganisation.
1878
    def test_bug_208418(self):
3287.7.4 by Andrew Bennetts
Failing test for bug 208418.
1879
        """You can insert a stream with an incompatible format, even when:
1880
          * the stream has a line-delta record,
1881
          * whose parent is in the target, also stored as a line-delta
1882
1883
        See <https://launchpad.net/bugs/208418>.
1884
        """
1885
        base_lines = split_lines(TEXT_1)
1886
        # Make the target
1887
        target = self.make_test_knit(name='target', annotate=True)
1888
        target.add_lines('version-1', [], base_lines)
1889
        target.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1890
        # The second record should be a delta.
1891
        self.assertEqual('line-delta', target._index.get_method('version-2'))
1892
        
1893
        # Make a source, with a different format, but the same data
1894
        source = self.make_test_knit(name='source', annotate=False)
1895
        source.add_lines('version-1', [], base_lines)
1896
        source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1897
        # Now add another record, which should be stored as a delta against
1898
        # version-2.
1899
        source.add_lines('version-3', ['version-2'], base_lines + ['b\n'])
1900
        self.assertEqual('line-delta', source._index.get_method('version-3'))
1901
1902
        # Make a stream of the new version
1903
        data_stream = source.get_data_stream(['version-3'])
1904
        # And insert into the target
1905
        target.insert_data_stream(data_stream)
1906
        # No errors should have been raised.
1907
3370.1.6 by Andrew Bennetts
Add test for line-deltas inserted into a non-delta knit.
1908
    def test_line_delta_record_into_non_delta_knit(self):
1909
        # Make a data stream with a line-delta record
1910
        source = self.make_test_knit(name='source', delta=True)
1911
        base_lines = split_lines(TEXT_1)
1912
        source.add_lines('version-1', [], base_lines)
1913
        source.add_lines('version-2', ['version-1'], base_lines + ['a\n'])
1914
        # The second record should be a delta.
1915
        self.assertEqual('line-delta', source._index.get_method('version-2'))
1916
        data_stream = source.get_data_stream(['version-1', 'version-2'])
1917
1918
        # Insert the stream into a non-delta knit.
1919
        target = self.make_test_knit(name='target', delta=False)
1920
        target.insert_data_stream(data_stream)
1921
        
1922
        # Both versions are fulltexts in the target
1923
        self.assertEqual('fulltext', target._index.get_method('version-1'))
1924
        self.assertEqual('fulltext', target._index.get_method('version-2'))
3360.2.9 by Martin Pool
merge #205156
1925
1926
3370.1.4 by Andrew Bennetts
Organise data stream tests in test_knit slightly better.
1927
class DataStreamTests(KnitTests):
1928
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1929
    def assertMadeStreamKnit(self, source_knit, versions, target_knit):
1930
        """Assert that a knit made from a stream is as expected."""
1931
        a_stream = source_knit.get_data_stream(versions)
1932
        expected_data = a_stream[2](None)
1933
        a_stream = source_knit.get_data_stream(versions)
1934
        a_knit = target_knit._knit_from_datastream(a_stream)
1935
        self.assertEqual(source_knit.factory.__class__,
1936
            a_knit.factory.__class__)
1937
        self.assertIsInstance(a_knit._data._access, _StreamAccess)
1938
        self.assertIsInstance(a_knit._index, _StreamIndex)
1939
        self.assertEqual(a_knit._index.data_list, a_stream[1])
1940
        self.assertEqual(a_knit._data._access.data, expected_data)
1941
        self.assertEqual(a_knit.filename, target_knit.filename)
1942
        self.assertEqual(a_knit.transport, target_knit.transport)
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
1943
        self.assertEqual(a_knit._index, a_knit._data._access.stream_index)
1944
        self.assertEqual(target_knit, a_knit._data._access.backing_knit)
1945
        self.assertIsInstance(a_knit._data._access.orig_factory,
1946
            source_knit.factory.__class__)
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
1947
1948
    def test__knit_from_data_stream_empty(self):
1949
        """Create a knit object from a datastream."""
1950
        annotated = self.make_test_knit(name='source', annotate=True)
1951
        plain = self.make_test_knit(name='target', annotate=False)
1952
        # case 1: annotated source
1953
        self.assertMadeStreamKnit(annotated, [], annotated)
1954
        self.assertMadeStreamKnit(annotated, [], plain)
1955
        # case 2: plain source
1956
        self.assertMadeStreamKnit(plain, [], annotated)
1957
        self.assertMadeStreamKnit(plain, [], plain)
1958
1959
    def test__knit_from_data_stream_unknown_format(self):
1960
        annotated = self.make_test_knit(name='source', annotate=True)
1961
        self.assertRaises(errors.KnitDataStreamUnknown,
1962
            annotated._knit_from_datastream, ("unknown", None, None))
1963
1964
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1965
TEXT_1 = """\
1966
Banana cup cakes:
1967
1968
- bananas
1969
- eggs
1970
- broken tea cups
1971
"""
1972
1973
TEXT_1A = """\
1974
Banana cup cake recipe
1975
(serves 6)
1976
1977
- bananas
1978
- eggs
1979
- broken tea cups
1980
- self-raising flour
1981
"""
1982
1664.2.1 by Aaron Bentley
Start work on plan_merge test
1983
TEXT_1B = """\
1984
Banana cup cake recipe
1985
1986
- bananas (do not use plantains!!!)
1987
- broken tea cups
1988
- flour
1989
"""
1990
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1991
delta_1_1a = """\
1992
0,1,2
1993
Banana cup cake recipe
1994
(serves 6)
1995
5,5,1
1996
- self-raising flour
1997
"""
1998
1999
TEXT_2 = """\
2000
Boeuf bourguignon
2001
2002
- beef
2003
- red wine
2004
- small onions
2005
- carrot
2006
- mushrooms
2007
"""
2008
1664.2.3 by Aaron Bentley
Add failing test case
2009
AB_MERGE_TEXT="""unchanged|Banana cup cake recipe
2010
new-a|(serves 6)
2011
unchanged|
2012
killed-b|- bananas
2013
killed-b|- eggs
2014
new-b|- bananas (do not use plantains!!!)
2015
unchanged|- broken tea cups
2016
new-a|- self-raising flour
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
2017
new-b|- flour
2018
"""
1664.2.3 by Aaron Bentley
Add failing test case
2019
AB_MERGE=[tuple(l.split('|')) for l in AB_MERGE_TEXT.splitlines(True)]
2020
2021
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
2022
def line_delta(from_lines, to_lines):
2023
    """Generate line-based delta from one text to another"""
2024
    s = difflib.SequenceMatcher(None, from_lines, to_lines)
2025
    for op in s.get_opcodes():
2026
        if op[0] == 'equal':
2027
            continue
2028
        yield '%d,%d,%d\n' % (op[1], op[2], op[4]-op[3])
2029
        for i in range(op[3], op[4]):
2030
            yield to_lines[i]
2031
2032
2033
def apply_line_delta(basis_lines, delta_lines):
2034
    """Apply a line-based perfect diff
2035
    
2036
    basis_lines -- text to apply the patch to
2037
    delta_lines -- diff instructions and content
2038
    """
2039
    out = basis_lines[:]
2040
    i = 0
2041
    offset = 0
2042
    while i < len(delta_lines):
2043
        l = delta_lines[i]
2044
        a, b, c = map(long, l.split(','))
2045
        i = i + 1
2046
        out[offset+a:offset+b] = delta_lines[i:i+c]
2047
        i = i + c
2048
        offset = offset + (b - a) + c
2049
    return out
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
2050
2051
2052
class TestWeaveToKnit(KnitTests):
2053
2054
    def test_weave_to_knit_matches(self):
2055
        # check that the WeaveToKnit is_compatible function
2056
        # registers True for a Weave to a Knit.
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
2057
        w = Weave(get_scope=lambda:None)
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
2058
        k = self.make_test_knit()
2059
        self.failUnless(WeaveToKnit.is_compatible(w, k))
2060
        self.failIf(WeaveToKnit.is_compatible(k, w))
2061
        self.failIf(WeaveToKnit.is_compatible(w, w))
2062
        self.failIf(WeaveToKnit.is_compatible(k, k))
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
2063
2064
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2065
class TestKnitIndex(KnitTests):
2066
2067
    def test_add_versions_dictionary_compresses(self):
2068
        """Adding versions to the index should update the lookup dict"""
2069
        knit = self.make_test_knit()
2070
        idx = knit._index
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2071
        idx.add_version('a-1', ['fulltext'], (None, 0, 0), [])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2072
        self.check_file_contents('test.kndx',
2073
            '# bzr knit index 8\n'
2074
            '\n'
2075
            'a-1 fulltext 0 0  :'
2076
            )
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2077
        idx.add_versions([('a-2', ['fulltext'], (None, 0, 0), ['a-1']),
2078
                          ('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
2079
                         ])
2080
        self.check_file_contents('test.kndx',
2081
            '# bzr knit index 8\n'
2082
            '\n'
2083
            'a-1 fulltext 0 0  :\n'
2084
            'a-2 fulltext 0 0 0 :\n'
2085
            'a-3 fulltext 0 0 1 :'
2086
            )
2087
        self.assertEqual(['a-1', 'a-2', 'a-3'], idx._history)
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2088
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0),
2089
                          'a-2':('a-2', ['fulltext'], 0, 0, ('a-1',), 1),
2090
                          'a-3':('a-3', ['fulltext'], 0, 0, ('a-2',), 2),
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2091
                         }, idx._cache)
2092
2093
    def test_add_versions_fails_clean(self):
2094
        """If add_versions fails in the middle, it restores a pristine state.
2095
2096
        Any modifications that are made to the index are reset if all versions
2097
        cannot be added.
2098
        """
2099
        # This cheats a little bit by passing in a generator which will
2100
        # raise an exception before the processing finishes
2101
        # Other possibilities would be to have an version with the wrong number
2102
        # of entries, or to make the backing transport unable to write any
2103
        # files.
2104
2105
        knit = self.make_test_knit()
2106
        idx = knit._index
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2107
        idx.add_version('a-1', ['fulltext'], (None, 0, 0), [])
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2108
2109
        class StopEarly(Exception):
2110
            pass
2111
2112
        def generate_failure():
2113
            """Add some entries and then raise an exception"""
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2114
            yield ('a-2', ['fulltext'], (None, 0, 0), ('a-1',))
2115
            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
2116
            raise StopEarly()
2117
2118
        # Assert the pre-condition
2119
        self.assertEqual(['a-1'], idx._history)
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2120
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
2121
2122
        self.assertRaises(StopEarly, idx.add_versions, generate_failure())
2123
2124
        # And it shouldn't be modified
2125
        self.assertEqual(['a-1'], idx._history)
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2126
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, (), 0)}, idx._cache)
2171.1.1 by John Arbash Meinel
Knit index files should ignore empty indexes rather than consider them corrupt.
2127
2128
    def test_knit_index_ignores_empty_files(self):
2129
        # There was a race condition in older bzr, where a ^C at the right time
2130
        # could leave an empty .kndx file, which bzr would later claim was a
2131
        # corrupted file since the header was not present. In reality, the file
2132
        # just wasn't created, so it should be ignored.
2133
        t = get_transport('.')
2134
        t.put_bytes('test.kndx', '')
2135
2136
        knit = self.make_test_knit()
2137
2138
    def test_knit_index_checks_header(self):
2139
        t = get_transport('.')
2140
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
2141
2196.2.1 by John Arbash Meinel
Merge Dmitry's optimizations and minimize the actual diff.
2142
        self.assertRaises(KnitHeaderError, self.make_test_knit)
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.
2143
2144
2145
class TestGraphIndexKnit(KnitTests):
2146
    """Tests for knits using a GraphIndex rather than a KnitIndex."""
2147
2148
    def make_g_index(self, name, ref_lists=0, nodes=[]):
2149
        builder = GraphIndexBuilder(ref_lists)
2150
        for node, references, value in nodes:
2151
            builder.add_node(node, references, value)
2152
        stream = builder.finish()
2153
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
2154
        size = trans.put_file(name, stream)
2155
        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.
2156
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2157
    def two_graph_index(self, deltas=False, catch_adds=False):
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2158
        """Build a two-graph index.
2159
2160
        :param deltas: If true, use underlying indices with two node-ref
2161
            lists and 'parent' set to a delta-compressed against tail.
2162
        """
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.
2163
        # build a complex graph across several indices.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2164
        if deltas:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2165
            # delta compression inn the index
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2166
            index1 = self.make_g_index('1', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2167
                (('tip', ), 'N0 100', ([('parent', )], [], )),
2168
                (('tail', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2169
            index2 = self.make_g_index('2', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2170
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], [('tail', )])),
2171
                (('separate', ), '', ([], []))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2172
        else:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2173
            # just blob location and graph in the index.
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2174
            index1 = self.make_g_index('1', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2175
                (('tip', ), 'N0 100', ([('parent', )], )),
2176
                (('tail', ), '', ([], ))])
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2177
            index2 = self.make_g_index('2', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2178
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], )),
2179
                (('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.
2180
        combined_index = CombinedGraphIndex([index1, index2])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2181
        if catch_adds:
2182
            self.combined_index = combined_index
2183
            self.caught_entries = []
2184
            add_callback = self.catch_add
2185
        else:
2186
            add_callback = None
2187
        return KnitGraphIndex(combined_index, deltas=deltas,
2188
            add_callback=add_callback)
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
2189
2190
    def test_get_ancestry(self):
2592.3.30 by Robert Collins
Make GraphKnitIndex get_ancestry the same as regular knits.
2191
        # get_ancestry is defined as eliding ghosts, not erroring.
2192
        index = self.two_graph_index()
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
2193
        self.assertEqual([], index.get_ancestry([]))
2194
        self.assertEqual(['separate'], index.get_ancestry(['separate']))
2195
        self.assertEqual(['tail'], index.get_ancestry(['tail']))
2196
        self.assertEqual(['tail', 'parent'], index.get_ancestry(['parent']))
2197
        self.assertEqual(['tail', 'parent', 'tip'], index.get_ancestry(['tip']))
2198
        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
2199
            (['tail', 'parent', 'tip', 'separate'],
2200
             ['separate', 'tail', 'parent', 'tip'],
2201
            ))
2202
        # and without topo_sort
2203
        self.assertEqual(set(['separate']),
2204
            set(index.get_ancestry(['separate'], topo_sorted=False)))
2205
        self.assertEqual(set(['tail']),
2206
            set(index.get_ancestry(['tail'], topo_sorted=False)))
2207
        self.assertEqual(set(['tail', 'parent']),
2208
            set(index.get_ancestry(['parent'], topo_sorted=False)))
2209
        self.assertEqual(set(['tail', 'parent', 'tip']),
2210
            set(index.get_ancestry(['tip'], topo_sorted=False)))
2211
        self.assertEqual(set(['separate', 'tail', 'parent', 'tip']),
2212
            set(index.get_ancestry(['tip', 'separate'])))
2592.3.30 by Robert Collins
Make GraphKnitIndex get_ancestry the same as regular knits.
2213
        # asking for a ghost makes it go boom.
2214
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
2592.3.4 by Robert Collins
Implement get_ancestry/get_ancestry_with_ghosts for KnitGraphIndex.
2215
2216
    def test_get_ancestry_with_ghosts(self):
2217
        index = self.two_graph_index()
2218
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2219
        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
2220
        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
2221
        self.assertTrue(index.get_ancestry_with_ghosts(['parent']) in
2222
            (['tail', 'ghost', 'parent'],
2223
             ['ghost', 'tail', 'parent'],
2224
            ))
2225
        self.assertTrue(index.get_ancestry_with_ghosts(['tip']) in
2226
            (['tail', 'ghost', 'parent', 'tip'],
2227
             ['ghost', 'tail', 'parent', 'tip'],
2228
            ))
2229
        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
2230
            (['tail', 'ghost', 'parent', 'tip', 'separate'],
2231
             ['ghost', 'tail', 'parent', 'tip', 'separate'],
2232
             ['separate', 'tail', 'ghost', 'parent', 'tip'],
2233
             ['separate', 'ghost', 'tail', 'parent', 'tip'],
2234
            ))
2592.3.30 by Robert Collins
Make GraphKnitIndex get_ancestry the same as regular knits.
2235
        # asking for a ghost makes it go boom.
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2236
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
2592.3.5 by Robert Collins
Implement KnitGraphIndex.num_versions.
2237
2238
    def test_num_versions(self):
2239
        index = self.two_graph_index()
2240
        self.assertEqual(4, index.num_versions())
2592.3.6 by Robert Collins
Implement KnitGraphIndex.get_versions.
2241
2242
    def test_get_versions(self):
2243
        index = self.two_graph_index()
2244
        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
2245
            set(index.get_versions()))
2246
2592.3.9 by Robert Collins
Implement KnitGraphIndex.has_version.
2247
    def test_has_version(self):
2248
        index = self.two_graph_index()
2249
        self.assertTrue(index.has_version('tail'))
2250
        self.assertFalse(index.has_version('ghost'))
2251
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
2252
    def test_get_position(self):
2253
        index = self.two_graph_index()
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2254
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
2255
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2592.3.10 by Robert Collins
Implement KnitGraphIndex.get_position.
2256
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2257
    def test_get_method_deltas(self):
2258
        index = self.two_graph_index(deltas=True)
2592.3.11 by Robert Collins
Implement KnitGraphIndex.get_method.
2259
        self.assertEqual('fulltext', index.get_method('tip'))
2260
        self.assertEqual('line-delta', index.get_method('parent'))
2261
2592.3.13 by Robert Collins
Implement KnitGraphIndex.get_method.
2262
    def test_get_method_no_deltas(self):
2263
        # check that the parent-history lookup is ignored with deltas=False.
2264
        index = self.two_graph_index(deltas=False)
2265
        self.assertEqual('fulltext', index.get_method('tip'))
2266
        self.assertEqual('fulltext', index.get_method('parent'))
2267
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2268
    def test_get_options_deltas(self):
2269
        index = self.two_graph_index(deltas=True)
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2270
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2271
        self.assertEqual(['line-delta'], index.get_options('parent'))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2272
2273
    def test_get_options_no_deltas(self):
2274
        # check that the parent-history lookup is ignored with deltas=False.
2275
        index = self.two_graph_index(deltas=False)
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2276
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2277
        self.assertEqual(['fulltext'], index.get_options('parent'))
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2278
2592.3.15 by Robert Collins
Implement KnitGraphIndex.get_parents/get_parents_with_ghosts.
2279
    def test_get_parents_with_ghosts(self):
2280
        index = self.two_graph_index()
2281
        self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
2592.3.43 by Robert Collins
A knit iter_parents API.
2282
        # and errors on ghosts.
2283
        self.assertRaises(errors.RevisionNotPresent,
2284
            index.get_parents_with_ghosts, 'ghost')
2592.3.15 by Robert Collins
Implement KnitGraphIndex.get_parents/get_parents_with_ghosts.
2285
2592.3.16 by Robert Collins
Implement KnitGraphIndex.check_versions_present.
2286
    def test_check_versions_present(self):
2287
        # ghosts should not be considered present
2288
        index = self.two_graph_index()
2289
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2290
            ['ghost'])
2291
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2292
            ['tail', 'ghost'])
2293
        index.check_versions_present(['tail', 'separate'])
2592.3.14 by Robert Collins
Implement KnitGraphIndex.get_options.
2294
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2295
    def catch_add(self, entries):
2296
        self.caught_entries.append(entries)
2297
2298
    def test_add_no_callback_errors(self):
2299
        index = self.two_graph_index()
2300
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2301
            '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.
2302
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2303
    def test_add_version_smoke(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2304
        index = self.two_graph_index(catch_adds=True)
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2305
        index.add_version('new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2306
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2307
            self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2308
2309
    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.
2310
        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.
2311
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2312
            '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.
2313
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2314
2315
    def test_add_version_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2316
        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.
2317
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2318
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
2319
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2320
        # but neither should have added data.
2321
        self.assertEqual([[], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2322
        
2323
    def test_add_version_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2324
        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.
2325
        # change options
2326
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2327
            'tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])
2328
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2329
            'tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])
2330
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2331
            'tip', 'fulltext', (None, 0, 100), ['parent'])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2332
        # position/length
2333
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2334
            'tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2335
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2336
            'tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2337
        # parents
2338
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2339
            'tip', 'fulltext,no-eol', (None, 0, 100), [])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2340
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2341
        
2342
    def test_add_versions_nodeltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2343
        index = self.two_graph_index(catch_adds=True)
2344
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2345
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
2346
                ('new2', 'fulltext', (None, 0, 6), ['new']),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2347
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2348
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
2349
            (('new2', ), ' 0 6', ((('new',),),))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2350
            sorted(self.caught_entries[0]))
2351
        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.
2352
2353
    def test_add_versions_deltas(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2354
        index = self.two_graph_index(deltas=True, catch_adds=True)
2355
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2356
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
2357
                ('new2', 'line-delta', (None, 0, 6), ['new']),
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2358
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2359
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
2360
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2361
            sorted(self.caught_entries[0]))
2362
        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.
2363
2364
    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.
2365
        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.
2366
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2367
            [('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.
2368
        self.assertEqual([], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2369
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
2370
    def test_add_versions_random_id_accepted(self):
2371
        index = self.two_graph_index(catch_adds=True)
2372
        index.add_versions([], random_id=True)
2373
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2374
    def test_add_versions_same_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2375
        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.
2376
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2377
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
2378
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2379
        # but neither should have added data.
2380
        self.assertEqual([[], []], self.caught_entries)
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2381
        
2382
    def test_add_versions_different_dup(self):
2592.3.19 by Robert Collins
Change KnitGraphIndex from returning data to performing a callback on insertions.
2383
        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.
2384
        # change options
2385
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2386
            [('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2387
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2388
            [('tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])])
2389
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2390
            [('tip', 'fulltext', (None, 0, 100), ['parent'])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2391
        # position/length
2392
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2393
            [('tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2394
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2395
            [('tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])])
2592.3.17 by Robert Collins
Add add_version(s) to KnitGraphIndex, completing the required api for KnitVersionedFile.
2396
        # parents
2397
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2398
            [('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.
2399
        # change options in the second record
2400
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2401
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent']),
2402
             ('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.
2403
        self.assertEqual([], self.caught_entries)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2404
2405
class TestNoParentsGraphIndexKnit(KnitTests):
2406
    """Tests for knits using KnitGraphIndex with no parents."""
2407
2408
    def make_g_index(self, name, ref_lists=0, nodes=[]):
2409
        builder = GraphIndexBuilder(ref_lists)
2410
        for node, references in nodes:
2411
            builder.add_node(node, references)
2412
        stream = builder.finish()
2413
        trans = self.get_transport()
2890.2.1 by Robert Collins
* ``bzrlib.index.GraphIndex`` now requires a size parameter to the
2414
        size = trans.put_file(name, stream)
2415
        return GraphIndex(trans, name, size)
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2416
2417
    def test_parents_deltas_incompatible(self):
2418
        index = CombinedGraphIndex([])
2419
        self.assertRaises(errors.KnitError, KnitGraphIndex, index,
2420
            deltas=True, parents=False)
2421
2422
    def two_graph_index(self, catch_adds=False):
2423
        """Build a two-graph index.
2424
2425
        :param deltas: If true, use underlying indices with two node-ref
2426
            lists and 'parent' set to a delta-compressed against tail.
2427
        """
2428
        # put several versions in the index.
2429
        index1 = self.make_g_index('1', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2430
            (('tip', ), 'N0 100'),
2431
            (('tail', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2432
        index2 = self.make_g_index('2', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2433
            (('parent', ), ' 100 78'),
2434
            (('separate', ), '')])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2435
        combined_index = CombinedGraphIndex([index1, index2])
2436
        if catch_adds:
2437
            self.combined_index = combined_index
2438
            self.caught_entries = []
2439
            add_callback = self.catch_add
2440
        else:
2441
            add_callback = None
2442
        return KnitGraphIndex(combined_index, parents=False,
2443
            add_callback=add_callback)
2444
2445
    def test_get_ancestry(self):
2446
        # with no parents, ancestry is always just the key.
2447
        index = self.two_graph_index()
2448
        self.assertEqual([], index.get_ancestry([]))
2449
        self.assertEqual(['separate'], index.get_ancestry(['separate']))
2450
        self.assertEqual(['tail'], index.get_ancestry(['tail']))
2451
        self.assertEqual(['parent'], index.get_ancestry(['parent']))
2452
        self.assertEqual(['tip'], index.get_ancestry(['tip']))
2453
        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
2454
            (['tip', 'separate'],
2455
             ['separate', 'tip'],
2456
            ))
2457
        # asking for a ghost makes it go boom.
2458
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
2459
2460
    def test_get_ancestry_with_ghosts(self):
2461
        index = self.two_graph_index()
2462
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2463
        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
2464
        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
2465
        self.assertEqual(['parent'], index.get_ancestry_with_ghosts(['parent']))
2466
        self.assertEqual(['tip'], index.get_ancestry_with_ghosts(['tip']))
2467
        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
2468
            (['tip', 'separate'],
2469
             ['separate', 'tip'],
2470
            ))
2471
        # asking for a ghost makes it go boom.
2472
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
2473
2474
    def test_num_versions(self):
2475
        index = self.two_graph_index()
2476
        self.assertEqual(4, index.num_versions())
2477
2478
    def test_get_versions(self):
2479
        index = self.two_graph_index()
2480
        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
2481
            set(index.get_versions()))
2482
2483
    def test_has_version(self):
2484
        index = self.two_graph_index()
2485
        self.assertTrue(index.has_version('tail'))
2486
        self.assertFalse(index.has_version('ghost'))
2487
2488
    def test_get_position(self):
2489
        index = self.two_graph_index()
2592.3.71 by Robert Collins
Basic version of knit-based repository operating, many tests failing.
2490
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
2491
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2492
2493
    def test_get_method(self):
2494
        index = self.two_graph_index()
2495
        self.assertEqual('fulltext', index.get_method('tip'))
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2496
        self.assertEqual(['fulltext'], index.get_options('parent'))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2497
2498
    def test_get_options(self):
2499
        index = self.two_graph_index()
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2500
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2501
        self.assertEqual(['fulltext'], index.get_options('parent'))
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2502
2503
    def test_get_parents_with_ghosts(self):
2504
        index = self.two_graph_index()
2505
        self.assertEqual((), index.get_parents_with_ghosts('parent'))
2592.3.43 by Robert Collins
A knit iter_parents API.
2506
        # and errors on ghosts.
2507
        self.assertRaises(errors.RevisionNotPresent,
2508
            index.get_parents_with_ghosts, 'ghost')
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2509
2510
    def test_check_versions_present(self):
2511
        index = self.two_graph_index()
2512
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2513
            ['missing'])
2514
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2515
            ['tail', 'missing'])
2516
        index.check_versions_present(['tail', 'separate'])
2517
2518
    def catch_add(self, entries):
2519
        self.caught_entries.append(entries)
2520
2521
    def test_add_no_callback_errors(self):
2522
        index = self.two_graph_index()
2523
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2524
            'new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2525
2526
    def test_add_version_smoke(self):
2527
        index = self.two_graph_index(catch_adds=True)
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2528
        index.add_version('new', 'fulltext,no-eol', (None, 50, 60), [])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2529
        self.assertEqual([[(('new', ), 'N50 60')]],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2530
            self.caught_entries)
2531
2532
    def test_add_version_delta_not_delta_index(self):
2533
        index = self.two_graph_index(catch_adds=True)
2534
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2535
            'new', 'no-eol,line-delta', (None, 0, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2536
        self.assertEqual([], self.caught_entries)
2537
2538
    def test_add_version_same_dup(self):
2539
        index = self.two_graph_index(catch_adds=True)
2540
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2541
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), [])
2542
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2543
        # but neither should have added data.
2544
        self.assertEqual([[], []], self.caught_entries)
2545
        
2546
    def test_add_version_different_dup(self):
2547
        index = self.two_graph_index(catch_adds=True)
2548
        # change options
2549
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2550
            'tip', 'no-eol,line-delta', (None, 0, 100), [])
2551
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2552
            'tip', 'line-delta,no-eol', (None, 0, 100), [])
2553
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2554
            'tip', 'fulltext', (None, 0, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2555
        # position/length
2556
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2557
            'tip', 'fulltext,no-eol', (None, 50, 100), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2558
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2559
            'tip', 'fulltext,no-eol', (None, 0, 1000), [])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2560
        # parents
2561
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2562
            'tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2563
        self.assertEqual([], self.caught_entries)
2564
        
2565
    def test_add_versions(self):
2566
        index = self.two_graph_index(catch_adds=True)
2567
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2568
                ('new', 'fulltext,no-eol', (None, 50, 60), []),
2569
                ('new2', 'fulltext', (None, 0, 6), []),
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2570
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2571
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2572
            sorted(self.caught_entries[0]))
2573
        self.assertEqual(1, len(self.caught_entries))
2574
2575
    def test_add_versions_delta_not_delta_index(self):
2576
        index = self.two_graph_index(catch_adds=True)
2577
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2578
            [('new', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2579
        self.assertEqual([], self.caught_entries)
2580
2581
    def test_add_versions_parents_not_parents_index(self):
2582
        index = self.two_graph_index(catch_adds=True)
2583
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2584
            [('new', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2585
        self.assertEqual([], self.caught_entries)
2586
2841.2.1 by Robert Collins
* Commit no longer checks for new text keys during insertion when the
2587
    def test_add_versions_random_id_accepted(self):
2588
        index = self.two_graph_index(catch_adds=True)
2589
        index.add_versions([], random_id=True)
2590
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2591
    def test_add_versions_same_dup(self):
2592
        index = self.two_graph_index(catch_adds=True)
2593
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2594
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), [])])
2595
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2596
        # but neither should have added data.
2597
        self.assertEqual([[], []], self.caught_entries)
2598
        
2599
    def test_add_versions_different_dup(self):
2600
        index = self.two_graph_index(catch_adds=True)
2601
        # change options
2602
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2603
            [('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2604
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2605
            [('tip', 'line-delta,no-eol', (None, 0, 100), [])])
2606
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2607
            [('tip', 'fulltext', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2608
        # position/length
2609
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2610
            [('tip', 'fulltext,no-eol', (None, 50, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2611
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2612
            [('tip', 'fulltext,no-eol', (None, 0, 1000), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2613
        # parents
2614
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2615
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2616
        # change options in the second record
2617
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2618
            [('tip', 'fulltext,no-eol', (None, 0, 100), []),
2619
             ('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2592.3.34 by Robert Collins
Rough unfactored support for parentless KnitGraphIndexs.
2620
        self.assertEqual([], self.caught_entries)
2621
3015.2.19 by Robert Collins
Don't include the pack container length in the lengths given by get_data_stream.
2622
class TestPackKnits(KnitTests):
2623
    """Tests that use a _PackAccess and KnitGraphIndex."""
2624
2625
    def test_get_data_stream_packs_ignores_pack_overhead(self):
2626
        # Packs have an encoding overhead that should not be included in the
2627
        # 'size' field of a data stream, because it is not returned by the
2628
        # raw_reading functions - it is why index_memo's are opaque, and
2629
        # get_data_stream was abusing this.
2630
        packname = 'test.pack'
2631
        transport = self.get_transport()
2632
        def write_data(bytes):
2633
            transport.append_bytes(packname, bytes)
2634
        writer = pack.ContainerWriter(write_data)
2635
        writer.begin()
2636
        index = InMemoryGraphIndex(2)
2637
        knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
2638
            deltas=True)
2639
        indices = {index:(transport, packname)}
2640
        access = _PackAccess(indices, writer=(writer, index))
2641
        k = KnitVersionedFile('test', get_transport('.'),
2642
            delta=True, create=True, index=knit_index, access_method=access)
2643
        # insert something into the knit
2644
        k.add_lines('text-1', [], ["foo\n"])
2645
        # get a data stream for it
2646
        stream = k.get_data_stream(['text-1'])
2647
        # if the stream has been incorrectly assembled, we will get a short read
2648
        # reading from the stream (as streams have no trailer)
2649
        expected_length = stream[1][0][2]
2650
        # we use -1 to do the read, so that if a trailer is added this test
2651
        # will fail and we'll adjust it to handle that case correctly, rather
2652
        # than allowing an over-read that is bogus.
2653
        self.assertEqual(expected_length, len(stream[2](-1)))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2654
2655
2656
class Test_StreamIndex(KnitTests):
2657
2658
    def get_index(self, knit, stream):
2659
        """Get a _StreamIndex from knit and stream."""
2660
        return knit._knit_from_datastream(stream)._index
2661
2662
    def assertIndexVersions(self, knit, versions):
2663
        """Check that the _StreamIndex versions are those of the stream."""
2664
        index = self.get_index(knit, knit.get_data_stream(versions))
2665
        self.assertEqual(set(index.get_versions()), set(versions))
2666
        # check we didn't get duplicates
2667
        self.assertEqual(len(index.get_versions()), len(versions))
2668
2669
    def assertIndexAncestry(self, knit, ancestry_versions, versions, result):
2670
        """Check the result of a get_ancestry call on knit."""
2671
        index = self.get_index(knit, knit.get_data_stream(versions))
3052.2.4 by Andrew Bennetts
Some tweaks suggested by John's review.
2672
        self.assertEqual(
2673
            set(result),
2674
            set(index.get_ancestry(ancestry_versions, False)))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2675
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2676
    def assertGetMethod(self, knit, versions, version, result):
2677
        index = self.get_index(knit, knit.get_data_stream(versions))
2678
        self.assertEqual(result, index.get_method(version))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2679
2680
    def assertGetOptions(self, knit, version, options):
2681
        index = self.get_index(knit, knit.get_data_stream(version))
2682
        self.assertEqual(options, index.get_options(version))
2683
2684
    def assertGetPosition(self, knit, versions, version, result):
2685
        index = self.get_index(knit, knit.get_data_stream(versions))
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2686
        if result[1] is None:
2687
            result = (result[0], index, result[2], result[3])
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2688
        self.assertEqual(result, index.get_position(version))
2689
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2690
    def assertGetParentsWithGhosts(self, knit, versions, version, parents):
2691
        index = self.get_index(knit, knit.get_data_stream(versions))
2692
        self.assertEqual(parents, index.get_parents_with_ghosts(version))
2693
2694
    def make_knit_with_4_versions_2_dags(self):
2695
        knit = self.make_test_knit()
2696
        knit.add_lines('a', [], ["foo"])
2697
        knit.add_lines('b', [], [])
2698
        knit.add_lines('c', ['b', 'a'], [])
2699
        knit.add_lines_with_ghosts('d', ['e', 'f'], [])
2700
        return knit
2701
2702
    def test_versions(self):
2703
        """The versions of a StreamIndex are those of the datastream."""
2704
        knit = self.make_knit_with_4_versions_2_dags()
2705
        # ask for most permutations, which catches bugs like falling back to the
2706
        # target knit, or showing ghosts, etc.
2707
        self.assertIndexVersions(knit, [])
2708
        self.assertIndexVersions(knit, ['a'])
2709
        self.assertIndexVersions(knit, ['b'])
2710
        self.assertIndexVersions(knit, ['c'])
2711
        self.assertIndexVersions(knit, ['d'])
2712
        self.assertIndexVersions(knit, ['a', 'b'])
2713
        self.assertIndexVersions(knit, ['b', 'c'])
2714
        self.assertIndexVersions(knit, ['a', 'c'])
2715
        self.assertIndexVersions(knit, ['a', 'b', 'c'])
2716
        self.assertIndexVersions(knit, ['a', 'b', 'c', 'd'])
2717
2718
    def test_construct(self):
2719
        """Constructing a StreamIndex generates index data."""
2720
        data_list = [('text-a', ['fulltext'], 127, []),
2721
            ('text-b', ['option'], 128, ['text-c'])]
3224.1.8 by John Arbash Meinel
Add noeol to the return signature of get_build_details.
2722
        index = _StreamIndex(data_list, None)
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2723
        self.assertEqual({'text-a':(['fulltext'], (0, 127), []),
2724
            'text-b':(['option'], (127, 127 + 128), ['text-c'])},
2725
            index._by_version)
2726
2727
    def test_get_ancestry(self):
2728
        knit = self.make_knit_with_4_versions_2_dags()
2729
        self.assertIndexAncestry(knit, ['a'], ['a'], ['a'])
2730
        self.assertIndexAncestry(knit, ['b'], ['b'], ['b'])
2731
        self.assertIndexAncestry(knit, ['c'], ['c'], ['c'])
2732
        self.assertIndexAncestry(knit, ['c'], ['a', 'b', 'c'],
3052.2.4 by Andrew Bennetts
Some tweaks suggested by John's review.
2733
            set(['a', 'b', 'c']))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2734
        self.assertIndexAncestry(knit, ['c', 'd'], ['a', 'b', 'c', 'd'],
3052.2.4 by Andrew Bennetts
Some tweaks suggested by John's review.
2735
            set(['a', 'b', 'c', 'd']))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2736
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2737
    def test_get_method(self):
2738
        knit = self.make_knit_with_4_versions_2_dags()
2739
        self.assertGetMethod(knit, ['a'], 'a', 'fulltext')
2740
        self.assertGetMethod(knit, ['c'], 'c', 'line-delta')
2741
        # get_method on a basis that is not in the datastream (but in the
2742
        # backing knit) returns 'fulltext', because thats what we'll create as
2743
        # we thunk across.
2744
        self.assertGetMethod(knit, ['c'], 'b', 'fulltext')
2745
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2746
    def test_get_options(self):
2747
        knit = self.make_knit_with_4_versions_2_dags()
2748
        self.assertGetOptions(knit, 'a', ['no-eol', 'fulltext'])
2749
        self.assertGetOptions(knit, 'c', ['line-delta'])
2750
2751
    def test_get_parents_with_ghosts(self):
2752
        knit = self.make_knit_with_4_versions_2_dags()
3287.5.5 by Robert Collins
Refactor internals of knit implementations to implement get_parents_with_ghosts in terms of get_parent_map.
2753
        self.assertGetParentsWithGhosts(knit, ['a'], 'a', ())
2754
        self.assertGetParentsWithGhosts(knit, ['c'], 'c', ('b', 'a'))
2755
        self.assertGetParentsWithGhosts(knit, ['d'], 'd', ('e', 'f'))
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2756
2757
    def test_get_position(self):
2758
        knit = self.make_knit_with_4_versions_2_dags()
3052.2.6 by Andrew Bennetts
Fix typo in comment.
2759
        # get_position returns (thunk_flag, index(can be None), start, end) for
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2760
        # _StreamAccess to use.
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2761
        self.assertGetPosition(knit, ['a'], 'a', (False, None, 0, 78))
2762
        self.assertGetPosition(knit, ['a', 'c'], 'c', (False, None, 78, 156))
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2763
        # get_position on a text that is not in the datastream (but in the
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2764
        # backing knit) returns (True, 'versionid', None, None) - and then the
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2765
        # access object can construct the relevant data as needed.
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2766
        self.assertGetPosition(knit, ['a', 'c'], 'b', (True, 'b', None, None))
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2767
2768
2769
class Test_StreamAccess(KnitTests):
3052.2.2 by Robert Collins
* Operations pulling data from a smart server where the underlying
2770
2771
    def get_index_access(self, knit, stream):
2772
        """Get a _StreamAccess from knit and stream."""
2773
        knit =  knit._knit_from_datastream(stream)
2774
        return knit._index, knit._data._access
2775
2776
    def assertGetRawRecords(self, knit, versions):
2777
        index, access = self.get_index_access(knit,
2778
            knit.get_data_stream(versions))
2779
        # check that every version asked for can be obtained from the resulting
2780
        # access object.
2781
        # batch
2782
        memos = []
2783
        for version in versions:
2784
            memos.append(knit._index.get_position(version))
2785
        original = {}
2786
        for version, data in zip(
2787
            versions, knit._data._access.get_raw_records(memos)):
2788
            original[version] = data
2789
        memos = []
2790
        for version in versions:
2791
            memos.append(index.get_position(version))
2792
        streamed = {}
2793
        for version, data in zip(versions, access.get_raw_records(memos)):
2794
            streamed[version] = data
2795
        self.assertEqual(original, streamed)
2796
        # individually
2797
        for version in versions:
2798
            data = list(access.get_raw_records(
2799
                [index.get_position(version)]))[0]
2800
            self.assertEqual(original[version], data)
2801
2802
    def make_knit_with_two_versions(self):
2803
        knit = self.make_test_knit()
2804
        knit.add_lines('a', [], ["foo"])
2805
        knit.add_lines('b', [], ["bar"])
2806
        return knit
2807
2808
    def test_get_raw_records(self):
2809
        knit = self.make_knit_with_two_versions()
2810
        self.assertGetRawRecords(knit, ['a', 'b'])
2811
        self.assertGetRawRecords(knit, ['a'])
2812
        self.assertGetRawRecords(knit, ['b'])
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2813
    
2814
    def test_get_raw_record_from_backing_knit(self):
2815
        # the thunk layer should create an artificial A on-demand when needed.
2816
        source_knit = self.make_test_knit(name='plain', annotate=False)
2817
        target_knit = self.make_test_knit(name='annotated', annotate=True)
2818
        source_knit.add_lines("A", [], ["Foo\n"])
2819
        # Give the target A, so we can try to thunk across to it.
2820
        target_knit.join(source_knit)
2821
        index, access = self.get_index_access(target_knit,
2822
            source_knit.get_data_stream([]))
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2823
        raw_data = list(access.get_raw_records([(True, "A", None, None)]))[0]
3052.2.3 by Robert Collins
Handle insert_data_stream of an unannotated stream into an annotated knit.
2824
        df = GzipFile(mode='rb', fileobj=StringIO(raw_data))
2825
        self.assertEqual(
2826
            'version A 1 5d36b88bb697a2d778f024048bafabd443d74503\n'
2827
            'Foo\nend A\n',
2828
            df.read())
2829
2830
    def test_asking_for_thunk_stream_is_not_plain_errors(self):
2831
        knit = self.make_test_knit(name='annotated', annotate=True)
2832
        knit.add_lines("A", [], ["Foo\n"])
2833
        index, access = self.get_index_access(knit,
2834
            knit.get_data_stream([]))
2835
        self.assertRaises(errors.KnitCorrupt,
3052.2.5 by Andrew Bennetts
Address the rest of the review comments from John and myself.
2836
            list, access.get_raw_records([(True, "A", None, None)]))
3316.2.3 by Robert Collins
Remove manual notification of transaction finishing on versioned files.
2837
2838
2839
class TestFormatSignatures(KnitTests):
2840
2841
    def test_knit_format_signatures(self):
2842
        """Different formats of knit have different signature strings."""
2843
        knit = self.make_test_knit(name='a', annotate=True)
2844
        self.assertEqual('knit-annotated', knit.get_format_signature())
2845
        knit = self.make_test_knit(name='p', annotate=False)
2846
        self.assertEqual('knit-plain', knit.get_format_signature())