/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,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
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
    )
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
37
from bzrlib.index import *
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
38
from bzrlib.knit import (
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
39
    KnitContent,
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
40
    KnitGraphIndex,
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
41
    KnitVersionedFile,
42
    KnitPlainFactory,
43
    KnitAnnotateFactory,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
44
    _KnitAccess,
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
45
    _KnitData,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
46
    _KnitIndex,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
47
    _PackAccess,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
48
    WeaveToKnit,
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
49
    KnitSequenceMatcher,
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
50
    )
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
51
from bzrlib.osutils import split_lines
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
52
from bzrlib.tests import (
53
    Feature,
54
    TestCase,
55
    TestCaseWithMemoryTransport,
56
    TestCaseWithTransport,
57
    )
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
58
from bzrlib.transport import TransportLogger, get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
59
from bzrlib.transport.memory import MemoryTransport
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
60
from bzrlib.weave import Weave
61
62
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
63
class _CompiledKnitFeature(Feature):
64
65
    def _probe(self):
66
        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
67
            import bzrlib._knit_load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
68
        except ImportError:
69
            return False
70
        return True
71
72
    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
73
        return 'bzrlib._knit_load_data_c'
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
74
75
CompiledKnitFeature = _CompiledKnitFeature()
76
77
2151.1.1 by John Arbash Meinel
(Dmitry Vasiliev) Tune KnitContent and add tests
78
class KnitContentTests(TestCase):
79
80
    def test_constructor(self):
81
        content = KnitContent([])
82
83
    def test_text(self):
84
        content = KnitContent([])
85
        self.assertEqual(content.text(), [])
86
87
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
88
        self.assertEqual(content.text(), ["text1", "text2"])
89
90
    def test_annotate(self):
91
        content = KnitContent([])
92
        self.assertEqual(content.annotate(), [])
93
94
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
95
        self.assertEqual(content.annotate(),
96
            [("origin1", "text1"), ("origin2", "text2")])
97
98
    def test_annotate_iter(self):
99
        content = KnitContent([])
100
        it = content.annotate_iter()
101
        self.assertRaises(StopIteration, it.next)
102
103
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
104
        it = content.annotate_iter()
105
        self.assertEqual(it.next(), ("origin1", "text1"))
106
        self.assertEqual(it.next(), ("origin2", "text2"))
107
        self.assertRaises(StopIteration, it.next)
108
109
    def test_copy(self):
110
        content = KnitContent([("origin1", "text1"), ("origin2", "text2")])
111
        copy = content.copy()
112
        self.assertIsInstance(copy, KnitContent)
113
        self.assertEqual(copy.annotate(),
114
            [("origin1", "text1"), ("origin2", "text2")])
115
116
    def test_line_delta(self):
117
        content1 = KnitContent([("", "a"), ("", "b")])
118
        content2 = KnitContent([("", "a"), ("", "a"), ("", "c")])
119
        self.assertEqual(content1.line_delta(content2),
120
            [(1, 2, 2, [("", "a"), ("", "c")])])
121
122
    def test_line_delta_iter(self):
123
        content1 = KnitContent([("", "a"), ("", "b")])
124
        content2 = KnitContent([("", "a"), ("", "a"), ("", "c")])
125
        it = content1.line_delta_iter(content2)
126
        self.assertEqual(it.next(), (1, 2, 2, [("", "a"), ("", "c")]))
127
        self.assertRaises(StopIteration, it.next)
128
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
129
130
class MockTransport(object):
131
132
    def __init__(self, file_lines=None):
133
        self.file_lines = file_lines
134
        self.calls = []
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
135
        # We have no base directory for the MockTransport
136
        self.base = ''
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
137
138
    def get(self, filename):
139
        if self.file_lines is None:
140
            raise NoSuchFile(filename)
141
        else:
142
            return StringIO("\n".join(self.file_lines))
143
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
144
    def readv(self, relpath, offsets):
145
        fp = self.get(relpath)
146
        for offset, size in offsets:
147
            fp.seek(offset)
148
            yield offset, fp.read(size)
149
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
150
    def __getattr__(self, name):
151
        def queue_call(*args, **kwargs):
152
            self.calls.append((name, args, kwargs))
153
        return queue_call
154
155
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
156
class KnitRecordAccessTestsMixin(object):
157
    """Tests for getting and putting knit records."""
158
159
    def assertAccessExists(self, access):
160
        """Ensure the data area for access has been initialised/exists."""
161
        raise NotImplementedError(self.assertAccessExists)
162
163
    def test_add_raw_records(self):
164
        """Add_raw_records adds records retrievable later."""
165
        access = self.get_access()
166
        memos = access.add_raw_records([10], '1234567890')
167
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
168
 
169
    def test_add_several_raw_records(self):
170
        """add_raw_records with many records and read some back."""
171
        access = self.get_access()
172
        memos = access.add_raw_records([10, 2, 5], '12345678901234567')
173
        self.assertEqual(['1234567890', '12', '34567'],
174
            list(access.get_raw_records(memos)))
175
        self.assertEqual(['1234567890'],
176
            list(access.get_raw_records(memos[0:1])))
177
        self.assertEqual(['12'],
178
            list(access.get_raw_records(memos[1:2])))
179
        self.assertEqual(['34567'],
180
            list(access.get_raw_records(memos[2:3])))
181
        self.assertEqual(['1234567890', '34567'],
182
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
183
184
    def test_create(self):
185
        """create() should make a file on disk."""
186
        access = self.get_access()
187
        access.create()
188
        self.assertAccessExists(access)
189
190
    def test_open_file(self):
191
        """open_file never errors."""
192
        access = self.get_access()
193
        access.open_file()
194
195
196
class TestKnitKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
197
    """Tests for the .kndx implementation."""
198
199
    def assertAccessExists(self, access):
200
        self.assertNotEqual(None, access.open_file())
201
202
    def get_access(self):
203
        """Get a .knit style access instance."""
204
        access = _KnitAccess(self.get_transport(), "foo.knit", None, None,
205
            False, False)
206
        return access
207
    
208
209
class TestPackKnitAccess(TestCaseWithMemoryTransport, KnitRecordAccessTestsMixin):
210
    """Tests for the pack based access."""
211
212
    def assertAccessExists(self, access):
213
        # as pack based access has no backing unless an index maps data, this
214
        # is a no-op.
215
        pass
216
217
    def get_access(self):
218
        return self._get_access()[0]
219
220
    def _get_access(self, packname='packfile', index='FOO'):
221
        transport = self.get_transport()
222
        def write_data(bytes):
223
            transport.append_bytes(packname, bytes)
224
        writer = pack.ContainerWriter(write_data)
225
        writer.begin()
226
        indices = {index:(transport, packname)}
227
        access = _PackAccess(indices, writer=(writer, index))
228
        return access, writer
229
230
    def test_read_from_several_packs(self):
231
        access, writer = self._get_access()
232
        memos = []
233
        memos.extend(access.add_raw_records([10], '1234567890'))
234
        writer.end()
235
        access, writer = self._get_access('pack2', 'FOOBAR')
236
        memos.extend(access.add_raw_records([5], '12345'))
237
        writer.end()
238
        access, writer = self._get_access('pack3', 'BAZ')
239
        memos.extend(access.add_raw_records([5], 'alpha'))
240
        writer.end()
241
        transport = self.get_transport()
242
        access = _PackAccess({"FOO":(transport, 'packfile'),
243
            "FOOBAR":(transport, 'pack2'),
244
            "BAZ":(transport, 'pack3')})
245
        self.assertEqual(['1234567890', '12345', 'alpha'],
246
            list(access.get_raw_records(memos)))
247
        self.assertEqual(['1234567890'],
248
            list(access.get_raw_records(memos[0:1])))
249
        self.assertEqual(['12345'],
250
            list(access.get_raw_records(memos[1:2])))
251
        self.assertEqual(['alpha'],
252
            list(access.get_raw_records(memos[2:3])))
253
        self.assertEqual(['1234567890', 'alpha'],
254
            list(access.get_raw_records(memos[0:1] + memos[2:3])))
255
256
    def test_set_writer(self):
257
        """The writer should be settable post construction."""
258
        access = _PackAccess({})
259
        transport = self.get_transport()
260
        packname = 'packfile'
261
        index = 'foo'
262
        def write_data(bytes):
263
            transport.append_bytes(packname, bytes)
264
        writer = pack.ContainerWriter(write_data)
265
        writer.begin()
266
        access.set_writer(writer, index, (transport, packname))
267
        memos = access.add_raw_records([10], '1234567890')
268
        writer.end()
269
        self.assertEqual(['1234567890'], list(access.get_raw_records(memos)))
270
271
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
272
class LowLevelKnitDataTests(TestCase):
273
274
    def create_gz_content(self, text):
275
        sio = StringIO()
276
        gz_file = gzip.GzipFile(mode='wb', fileobj=sio)
277
        gz_file.write(text)
278
        gz_file.close()
279
        return sio.getvalue()
280
281
    def test_valid_knit_data(self):
282
        sha1sum = sha.new('foo\nbar\n').hexdigest()
283
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
284
                                        'foo\n'
285
                                        'bar\n'
286
                                        'end rev-id-1\n'
287
                                        % (sha1sum,))
288
        transport = MockTransport([gz_txt])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
289
        access = _KnitAccess(transport, 'filename', None, None, False, False)
290
        data = _KnitData(access=access)
291
        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.
292
293
        contents = data.read_records(records)
294
        self.assertEqual({'rev-id-1':(['foo\n', 'bar\n'], sha1sum)}, contents)
295
296
        raw_contents = list(data.read_records_iter_raw(records))
297
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
298
299
    def test_not_enough_lines(self):
300
        sha1sum = sha.new('foo\n').hexdigest()
301
        # record says 2 lines data says 1
302
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
303
                                        'foo\n'
304
                                        'end rev-id-1\n'
305
                                        % (sha1sum,))
306
        transport = MockTransport([gz_txt])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
307
        access = _KnitAccess(transport, 'filename', None, None, False, False)
308
        data = _KnitData(access=access)
309
        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.
310
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
311
312
        # read_records_iter_raw won't detect that sort of mismatch/corruption
313
        raw_contents = list(data.read_records_iter_raw(records))
314
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
315
316
    def test_too_many_lines(self):
317
        sha1sum = sha.new('foo\nbar\n').hexdigest()
318
        # record says 1 lines data says 2
319
        gz_txt = self.create_gz_content('version rev-id-1 1 %s\n'
320
                                        'foo\n'
321
                                        'bar\n'
322
                                        'end rev-id-1\n'
323
                                        % (sha1sum,))
324
        transport = MockTransport([gz_txt])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
325
        access = _KnitAccess(transport, 'filename', None, None, False, False)
326
        data = _KnitData(access=access)
327
        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.
328
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
329
330
        # read_records_iter_raw won't detect that sort of mismatch/corruption
331
        raw_contents = list(data.read_records_iter_raw(records))
332
        self.assertEqual([('rev-id-1', gz_txt)], raw_contents)
333
334
    def test_mismatched_version_id(self):
335
        sha1sum = sha.new('foo\nbar\n').hexdigest()
336
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
337
                                        'foo\n'
338
                                        'bar\n'
339
                                        'end rev-id-1\n'
340
                                        % (sha1sum,))
341
        transport = MockTransport([gz_txt])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
342
        access = _KnitAccess(transport, 'filename', None, None, False, False)
343
        data = _KnitData(access=access)
2329.1.1 by John Arbash Meinel
Update _KnitData parser to raise more helpful errors when it detects corruption.
344
        # We are asking for rev-id-2, but the data is rev-id-1
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
345
        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.
346
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
347
348
        # read_records_iter_raw will notice if we request the wrong version.
349
        self.assertRaises(errors.KnitCorrupt, list,
350
                          data.read_records_iter_raw(records))
351
352
    def test_uncompressed_data(self):
353
        sha1sum = sha.new('foo\nbar\n').hexdigest()
354
        txt = ('version rev-id-1 2 %s\n'
355
               'foo\n'
356
               'bar\n'
357
               'end rev-id-1\n'
358
               % (sha1sum,))
359
        transport = MockTransport([txt])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
360
        access = _KnitAccess(transport, 'filename', None, None, False, False)
361
        data = _KnitData(access=access)
362
        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.
363
364
        # We don't have valid gzip data ==> corrupt
365
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
366
367
        # read_records_iter_raw will notice the bad data
368
        self.assertRaises(errors.KnitCorrupt, list,
369
                          data.read_records_iter_raw(records))
370
371
    def test_corrupted_data(self):
372
        sha1sum = sha.new('foo\nbar\n').hexdigest()
373
        gz_txt = self.create_gz_content('version rev-id-1 2 %s\n'
374
                                        'foo\n'
375
                                        'bar\n'
376
                                        'end rev-id-1\n'
377
                                        % (sha1sum,))
378
        # Change 2 bytes in the middle to \xff
379
        gz_txt = gz_txt[:10] + '\xff\xff' + gz_txt[12:]
380
        transport = MockTransport([gz_txt])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
381
        access = _KnitAccess(transport, 'filename', None, None, False, False)
382
        data = _KnitData(access=access)
383
        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.
384
385
        self.assertRaises(errors.KnitCorrupt, data.read_records, records)
386
387
        # read_records_iter_raw will notice if we request the wrong version.
388
        self.assertRaises(errors.KnitCorrupt, list,
389
                          data.read_records_iter_raw(records))
390
391
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
392
class LowLevelKnitIndexTests(TestCase):
393
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
394
    def get_knit_index(self, *args, **kwargs):
395
        orig = knit._load_data
396
        def reset():
397
            knit._load_data = orig
398
        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
399
        from bzrlib._knit_load_data_py import _load_data_py
400
        knit._load_data = _load_data_py
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
401
        return _KnitIndex(*args, **kwargs)
402
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
403
    def test_no_such_file(self):
404
        transport = MockTransport()
405
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
406
        self.assertRaises(NoSuchFile, self.get_knit_index,
407
                          transport, "filename", "r")
408
        self.assertRaises(NoSuchFile, self.get_knit_index,
409
                          transport, "filename", "w", create=False)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
410
411
    def test_create_file(self):
412
        transport = MockTransport()
413
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
414
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
415
            file_mode="wb", create=True)
416
        self.assertEqual(
417
                ("put_bytes_non_atomic",
418
                    ("filename", index.HEADER), {"mode": "wb"}),
419
                transport.calls.pop(0))
420
421
    def test_delay_create_file(self):
422
        transport = MockTransport()
423
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
424
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
425
            create=True, file_mode="wb", create_parent_dir=True,
426
            delay_create=True, dir_mode=0777)
427
        self.assertEqual([], transport.calls)
428
429
        index.add_versions([])
430
        name, (filename, f), kwargs = transport.calls.pop(0)
431
        self.assertEqual("put_file_non_atomic", name)
432
        self.assertEqual(
433
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
434
            kwargs)
435
        self.assertEqual("filename", filename)
436
        self.assertEqual(index.HEADER, f.read())
437
438
        index.add_versions([])
439
        self.assertEqual(("append_bytes", ("filename", ""), {}),
440
            transport.calls.pop(0))
441
442
    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
443
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
444
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
445
        transport = MockTransport([
446
            _KnitIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
447
            '%s option 0 1 :' % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
448
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
449
        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
450
        # _KnitIndex is a private class, and deals in utf8 revision_ids, not
451
        # Unicode revision_ids.
452
        self.assertTrue(index.has_version(utf8_revision_id))
453
        self.assertFalse(index.has_version(unicode_revision_id))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
454
455
    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
456
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
457
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
458
        transport = MockTransport([
459
            _KnitIndex.HEADER,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
460
            "version option 0 1 .%s :" % (utf8_revision_id,)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
461
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
462
        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
463
        self.assertEqual([utf8_revision_id],
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
464
            index.get_parents_with_ghosts("version"))
465
466
    def test_read_ignore_corrupted_lines(self):
467
        transport = MockTransport([
468
            _KnitIndex.HEADER,
469
            "corrupted",
470
            "corrupted options 0 1 .b .c ",
471
            "version options 0 1 :"
472
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
473
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
474
        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
475
        self.assertTrue(index.has_version("version"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
476
477
    def test_read_corrupted_header(self):
2196.2.3 by John Arbash Meinel
Update tests and code to pass after merging bzr.dev
478
        transport = MockTransport(['not a bzr knit index header\n'])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
479
        self.assertRaises(KnitHeaderError,
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
480
            self.get_knit_index, transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
481
482
    def test_read_duplicate_entries(self):
483
        transport = MockTransport([
484
            _KnitIndex.HEADER,
485
            "parent options 0 1 :",
486
            "version options1 0 1 0 :",
487
            "version options2 1 2 .other :",
488
            "version options3 3 4 0 .other :"
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", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
491
        self.assertEqual(2, index.num_versions())
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
492
        # check that the index used is the first one written. (Specific
493
        # to KnitIndex style indices.
494
        self.assertEqual("1", index._version_list_to_index(["version"]))
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
495
        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
496
        self.assertEqual(["options3"], index.get_options("version"))
497
        self.assertEqual(["parent", "other"],
498
            index.get_parents_with_ghosts("version"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
499
500
    def test_read_compressed_parents(self):
501
        transport = MockTransport([
502
            _KnitIndex.HEADER,
503
            "a option 0 1 :",
504
            "b option 0 1 0 :",
505
            "c option 0 1 1 0 :",
506
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
507
        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
508
        self.assertEqual(["a"], index.get_parents("b"))
509
        self.assertEqual(["b", "a"], index.get_parents("c"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
510
511
    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
512
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
513
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
514
        transport = MockTransport([
515
            _KnitIndex.HEADER
516
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
517
        index = self.get_knit_index(transport, "filename", "r")
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
518
        index.add_version(utf8_revision_id, ["option"], (None, 0, 1), [])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
519
        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
520
            "\n%s option 0 1  :" % (utf8_revision_id,)),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
521
            {}),
522
            transport.calls.pop(0))
523
524
    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
525
        unicode_revision_id = u"version-\N{CYRILLIC CAPITAL LETTER A}"
526
        utf8_revision_id = unicode_revision_id.encode('utf-8')
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
527
        transport = MockTransport([
528
            _KnitIndex.HEADER
529
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
530
        index = self.get_knit_index(transport, "filename", "r")
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
531
        index.add_version("version", ["option"], (None, 0, 1), [utf8_revision_id])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
532
        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
533
            "\nversion option 0 1 .%s :" % (utf8_revision_id,)),
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
534
            {}),
535
            transport.calls.pop(0))
536
537
    def test_get_graph(self):
538
        transport = MockTransport()
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
539
        index = self.get_knit_index(transport, "filename", "w", create=True)
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
540
        self.assertEqual([], index.get_graph())
541
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
542
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
543
        self.assertEqual([("a", ["b"])], index.get_graph())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
544
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
545
        index.add_version("c", ["option"], (None, 0, 1), ["d"])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
546
        self.assertEqual([("a", ["b"]), ("c", ["d"])],
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
547
            sorted(index.get_graph()))
548
549
    def test_get_ancestry(self):
550
        transport = MockTransport([
551
            _KnitIndex.HEADER,
552
            "a option 0 1 :",
553
            "b option 0 1 0 .e :",
554
            "c option 0 1 1 0 :",
555
            "d option 0 1 2 .f :"
556
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
557
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
558
559
        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
560
        self.assertEqual(["a"], index.get_ancestry(["a"]))
561
        self.assertEqual(["a", "b"], index.get_ancestry(["b"]))
562
        self.assertEqual(["a", "b", "c"], index.get_ancestry(["c"]))
563
        self.assertEqual(["a", "b", "c", "d"], index.get_ancestry(["d"]))
564
        self.assertEqual(["a", "b"], index.get_ancestry(["a", "b"]))
565
        self.assertEqual(["a", "b", "c"], index.get_ancestry(["a", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
566
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
567
        self.assertRaises(RevisionNotPresent, index.get_ancestry, ["e"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
568
569
    def test_get_ancestry_with_ghosts(self):
570
        transport = MockTransport([
571
            _KnitIndex.HEADER,
572
            "a option 0 1 :",
573
            "b option 0 1 0 .e :",
574
            "c option 0 1 0 .f .g :",
575
            "d option 0 1 2 .h .j .k :"
576
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
577
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
578
579
        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
580
        self.assertEqual(["a"], index.get_ancestry_with_ghosts(["a"]))
581
        self.assertEqual(["a", "e", "b"],
582
            index.get_ancestry_with_ghosts(["b"]))
583
        self.assertEqual(["a", "g", "f", "c"],
584
            index.get_ancestry_with_ghosts(["c"]))
585
        self.assertEqual(["a", "g", "f", "c", "k", "j", "h", "d"],
586
            index.get_ancestry_with_ghosts(["d"]))
587
        self.assertEqual(["a", "e", "b"],
588
            index.get_ancestry_with_ghosts(["a", "b"]))
589
        self.assertEqual(["a", "g", "f", "c"],
590
            index.get_ancestry_with_ghosts(["a", "c"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
591
        self.assertEqual(
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
592
            ["a", "g", "f", "c", "e", "b", "k", "j", "h", "d"],
593
            index.get_ancestry_with_ghosts(["b", "d"]))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
594
595
        self.assertRaises(RevisionNotPresent,
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
596
            index.get_ancestry_with_ghosts, ["e"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
597
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
598
    def test_iter_parents(self):
599
        transport = MockTransport()
600
        index = self.get_knit_index(transport, "filename", "w", create=True)
601
        # no parents
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
602
        index.add_version('r0', ['option'], (None, 0, 1), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
603
        # 1 parent
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
604
        index.add_version('r1', ['option'], (None, 0, 1), ['r0'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
605
        # 2 parents
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
606
        index.add_version('r2', ['option'], (None, 0, 1), ['r1', 'r0'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
607
        # XXX TODO a ghost
608
        # cases: each sample data individually:
609
        self.assertEqual(set([('r0', ())]),
610
            set(index.iter_parents(['r0'])))
611
        self.assertEqual(set([('r1', ('r0', ))]),
612
            set(index.iter_parents(['r1'])))
613
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
614
            set(index.iter_parents(['r2'])))
615
        # no nodes returned for a missing node
616
        self.assertEqual(set(),
617
            set(index.iter_parents(['missing'])))
618
        # 1 node returned with missing nodes skipped
619
        self.assertEqual(set([('r1', ('r0', ))]),
620
            set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
621
        # 2 nodes returned
622
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
623
            set(index.iter_parents(['r0', 'r1'])))
624
        # 2 nodes returned, missing skipped
625
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
626
            set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
627
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
628
    def test_num_versions(self):
629
        transport = MockTransport([
630
            _KnitIndex.HEADER
631
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
632
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
633
634
        self.assertEqual(0, index.num_versions())
635
        self.assertEqual(0, len(index))
636
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
637
        index.add_version("a", ["option"], (None, 0, 1), [])
638
        self.assertEqual(1, index.num_versions())
639
        self.assertEqual(1, len(index))
640
641
        index.add_version("a", ["option2"], (None, 1, 2), [])
642
        self.assertEqual(1, index.num_versions())
643
        self.assertEqual(1, len(index))
644
645
        index.add_version("b", ["option"], (None, 0, 1), [])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
646
        self.assertEqual(2, index.num_versions())
647
        self.assertEqual(2, len(index))
648
649
    def test_get_versions(self):
650
        transport = MockTransport([
651
            _KnitIndex.HEADER
652
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
653
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
654
655
        self.assertEqual([], index.get_versions())
656
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
657
        index.add_version("a", ["option"], (None, 0, 1), [])
658
        self.assertEqual(["a"], index.get_versions())
659
660
        index.add_version("a", ["option"], (None, 0, 1), [])
661
        self.assertEqual(["a"], index.get_versions())
662
663
        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
664
        self.assertEqual(["a", "b"], index.get_versions())
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
665
666
    def test_add_version(self):
667
        transport = MockTransport([
668
            _KnitIndex.HEADER
669
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
670
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
671
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
672
        index.add_version("a", ["option"], (None, 0, 1), ["b"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
673
        self.assertEqual(("append_bytes",
674
            ("filename", "\na option 0 1 .b :"),
675
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
676
        self.assertTrue(index.has_version("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
677
        self.assertEqual(1, index.num_versions())
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
678
        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
679
        self.assertEqual(["option"], index.get_options("a"))
680
        self.assertEqual(["b"], index.get_parents_with_ghosts("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
681
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
682
        index.add_version("a", ["opt"], (None, 1, 2), ["c"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
683
        self.assertEqual(("append_bytes",
684
            ("filename", "\na opt 1 2 .c :"),
685
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
686
        self.assertTrue(index.has_version("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
687
        self.assertEqual(1, index.num_versions())
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
688
        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
689
        self.assertEqual(["opt"], index.get_options("a"))
690
        self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
691
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
692
        index.add_version("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
693
        self.assertEqual(("append_bytes",
694
            ("filename", "\nb option 2 3 0 :"),
695
            {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
696
        self.assertTrue(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
697
        self.assertEqual(2, index.num_versions())
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
698
        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
699
        self.assertEqual(["option"], index.get_options("b"))
700
        self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
701
702
    def test_add_versions(self):
703
        transport = MockTransport([
704
            _KnitIndex.HEADER
705
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
706
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
707
708
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
709
            ("a", ["option"], (None, 0, 1), ["b"]),
710
            ("a", ["opt"], (None, 1, 2), ["c"]),
711
            ("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
712
            ])
713
        self.assertEqual(("append_bytes", ("filename",
714
            "\na option 0 1 .b :"
715
            "\na opt 1 2 .c :"
716
            "\nb option 2 3 0 :"
717
            ), {}), transport.calls.pop(0))
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
718
        self.assertTrue(index.has_version("a"))
719
        self.assertTrue(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
720
        self.assertEqual(2, index.num_versions())
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
721
        self.assertEqual((None, 1, 2), index.get_position("a"))
722
        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
723
        self.assertEqual(["opt"], index.get_options("a"))
724
        self.assertEqual(["option"], index.get_options("b"))
725
        self.assertEqual(["c"], index.get_parents_with_ghosts("a"))
726
        self.assertEqual(["a"], index.get_parents_with_ghosts("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
727
728
    def test_delay_create_and_add_versions(self):
729
        transport = MockTransport()
730
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
731
        index = self.get_knit_index(transport, "filename", "w",
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
732
            create=True, file_mode="wb", create_parent_dir=True,
733
            delay_create=True, dir_mode=0777)
734
        self.assertEqual([], transport.calls)
735
736
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
737
            ("a", ["option"], (None, 0, 1), ["b"]),
738
            ("a", ["opt"], (None, 1, 2), ["c"]),
739
            ("b", ["option"], (None, 2, 3), ["a"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
740
            ])
741
        name, (filename, f), kwargs = transport.calls.pop(0)
742
        self.assertEqual("put_file_non_atomic", name)
743
        self.assertEqual(
744
            {"dir_mode": 0777, "create_parent_dir": True, "mode": "wb"},
745
            kwargs)
746
        self.assertEqual("filename", filename)
747
        self.assertEqual(
748
            index.HEADER +
749
            "\na option 0 1 .b :"
750
            "\na opt 1 2 .c :"
751
            "\nb option 2 3 0 :",
752
            f.read())
753
754
    def test_has_version(self):
755
        transport = MockTransport([
756
            _KnitIndex.HEADER,
757
            "a option 0 1 :"
758
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
759
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
760
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
761
        self.assertTrue(index.has_version("a"))
762
        self.assertFalse(index.has_version("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
763
764
    def test_get_position(self):
765
        transport = MockTransport([
766
            _KnitIndex.HEADER,
767
            "a option 0 1 :",
768
            "b option 1 2 :"
769
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
770
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
771
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
772
        self.assertEqual((None, 0, 1), index.get_position("a"))
773
        self.assertEqual((None, 1, 2), index.get_position("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
774
775
    def test_get_method(self):
776
        transport = MockTransport([
777
            _KnitIndex.HEADER,
778
            "a fulltext,unknown 0 1 :",
779
            "b unknown,line-delta 1 2 :",
780
            "c bad 3 4 :"
781
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
782
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
783
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
784
        self.assertEqual("fulltext", index.get_method("a"))
785
        self.assertEqual("line-delta", index.get_method("b"))
786
        self.assertRaises(errors.KnitIndexUnknownMethod, index.get_method, "c")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
787
788
    def test_get_options(self):
789
        transport = MockTransport([
790
            _KnitIndex.HEADER,
791
            "a opt1 0 1 :",
792
            "b opt2,opt3 1 2 :"
793
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
794
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
795
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
796
        self.assertEqual(["opt1"], index.get_options("a"))
797
        self.assertEqual(["opt2", "opt3"], index.get_options("b"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
798
799
    def test_get_parents(self):
800
        transport = MockTransport([
801
            _KnitIndex.HEADER,
802
            "a option 0 1 :",
803
            "b option 1 2 0 .c :",
804
            "c option 1 2 1 0 .e :"
805
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
806
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
807
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
808
        self.assertEqual([], index.get_parents("a"))
809
        self.assertEqual(["a", "c"], index.get_parents("b"))
810
        self.assertEqual(["b", "a"], index.get_parents("c"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
811
812
    def test_get_parents_with_ghosts(self):
813
        transport = MockTransport([
814
            _KnitIndex.HEADER,
815
            "a option 0 1 :",
816
            "b option 1 2 0 .c :",
817
            "c option 1 2 1 0 .e :"
818
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
819
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
820
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
821
        self.assertEqual([], index.get_parents_with_ghosts("a"))
822
        self.assertEqual(["a", "c"], index.get_parents_with_ghosts("b"))
823
        self.assertEqual(["b", "a", "e"],
824
            index.get_parents_with_ghosts("c"))
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
825
826
    def test_check_versions_present(self):
827
        transport = MockTransport([
828
            _KnitIndex.HEADER,
829
            "a option 0 1 :",
830
            "b option 0 1 :"
831
            ])
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
832
        index = self.get_knit_index(transport, "filename", "r")
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
833
834
        check = index.check_versions_present
835
836
        check([])
2249.5.12 by John Arbash Meinel
Change the APIs for VersionedFile, Store, and some of Repository into utf-8
837
        check(["a"])
838
        check(["b"])
839
        check(["a", "b"])
840
        self.assertRaises(RevisionNotPresent, check, ["c"])
841
        self.assertRaises(RevisionNotPresent, check, ["a", "b", "c"])
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
842
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
843
    def test_impossible_parent(self):
844
        """Test we get KnitCorrupt if the parent couldn't possibly exist."""
845
        transport = MockTransport([
846
            _KnitIndex.HEADER,
847
            "a option 0 1 :",
848
            "b option 0 1 4 :"  # We don't have a 4th record
849
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
850
        try:
851
            self.assertRaises(errors.KnitCorrupt,
852
                              self.get_knit_index, transport, 'filename', 'r')
853
        except TypeError, e:
854
            if (str(e) == ('exceptions must be strings, classes, or instances,'
855
                           ' not exceptions.IndexError')
856
                and sys.version_info[0:2] >= (2,5)):
857
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
858
                                  ' raising new style exceptions with python'
859
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
860
            else:
861
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
862
863
    def test_corrupted_parent(self):
864
        transport = MockTransport([
865
            _KnitIndex.HEADER,
866
            "a option 0 1 :",
867
            "b option 0 1 :",
868
            "c option 0 1 1v :", # Can't have a parent of '1v'
869
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
870
        try:
871
            self.assertRaises(errors.KnitCorrupt,
872
                              self.get_knit_index, transport, 'filename', 'r')
873
        except TypeError, e:
874
            if (str(e) == ('exceptions must be strings, classes, or instances,'
875
                           ' not exceptions.ValueError')
876
                and sys.version_info[0:2] >= (2,5)):
877
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
878
                                  ' raising new style exceptions with python'
879
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
880
            else:
881
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
882
883
    def test_corrupted_parent_in_list(self):
884
        transport = MockTransport([
885
            _KnitIndex.HEADER,
886
            "a option 0 1 :",
887
            "b option 0 1 :",
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
888
            "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.
889
            ])
2484.1.17 by John Arbash Meinel
Workaround for Pyrex <0.9.5 and python >=2.5 incompatibilities.
890
        try:
891
            self.assertRaises(errors.KnitCorrupt,
892
                              self.get_knit_index, transport, 'filename', 'r')
893
        except TypeError, e:
894
            if (str(e) == ('exceptions must be strings, classes, or instances,'
895
                           ' not exceptions.ValueError')
896
                and sys.version_info[0:2] >= (2,5)):
897
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
898
                                  ' raising new style exceptions with python'
899
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
900
            else:
901
                raise
2484.1.13 by John Arbash Meinel
Add a test that KnitCorrupt is raised when parent strings are invalid.
902
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
903
    def test_invalid_position(self):
904
        transport = MockTransport([
905
            _KnitIndex.HEADER,
906
            "a option 1v 1 :",
907
            ])
908
        try:
909
            self.assertRaises(errors.KnitCorrupt,
910
                              self.get_knit_index, transport, 'filename', 'r')
911
        except TypeError, e:
912
            if (str(e) == ('exceptions must be strings, classes, or instances,'
913
                           ' not exceptions.ValueError')
914
                and sys.version_info[0:2] >= (2,5)):
915
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
916
                                  ' raising new style exceptions with python'
917
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
918
            else:
919
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
920
921
    def test_invalid_size(self):
922
        transport = MockTransport([
923
            _KnitIndex.HEADER,
924
            "a option 1 1v :",
925
            ])
926
        try:
927
            self.assertRaises(errors.KnitCorrupt,
928
                              self.get_knit_index, transport, 'filename', 'r')
929
        except TypeError, e:
930
            if (str(e) == ('exceptions must be strings, classes, or instances,'
931
                           ' not exceptions.ValueError')
932
                and sys.version_info[0:2] >= (2,5)):
933
                self.knownFailure('Pyrex <0.9.5 fails with TypeError when'
934
                                  ' raising new style exceptions with python'
935
                                  ' >=2.5')
2484.1.19 by John Arbash Meinel
Don't suppress the TypeError if it doesn't match our requirements.
936
            else:
937
                raise
2484.1.18 by John Arbash Meinel
Test that we properly verify the size and position strings.
938
2484.1.24 by John Arbash Meinel
Add direct tests of how we handle incomplete/'broken' lines
939
    def test_short_line(self):
940
        transport = MockTransport([
941
            _KnitIndex.HEADER,
942
            "a option 0 10  :",
943
            "b option 10 10 0", # This line isn't terminated, ignored
944
            ])
945
        index = self.get_knit_index(transport, "filename", "r")
946
        self.assertEqual(['a'], index.get_versions())
947
948
    def test_skip_incomplete_record(self):
949
        # A line with bogus data should just be skipped
950
        transport = MockTransport([
951
            _KnitIndex.HEADER,
952
            "a option 0 10  :",
953
            "b option 10 10 0", # This line isn't terminated, ignored
954
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
955
            ])
956
        index = self.get_knit_index(transport, "filename", "r")
957
        self.assertEqual(['a', 'c'], index.get_versions())
958
959
    def test_trailing_characters(self):
960
        # A line with bogus data should just be skipped
961
        transport = MockTransport([
962
            _KnitIndex.HEADER,
963
            "a option 0 10  :",
964
            "b option 10 10 0 :a", # This line has extra trailing characters
965
            "c option 20 10 0 :", # Properly terminated, and starts with '\n'
966
            ])
967
        index = self.get_knit_index(transport, "filename", "r")
968
        self.assertEqual(['a', 'c'], index.get_versions())
969
2158.3.1 by Dmitry Vasiliev
KnitIndex tests/fixes/optimizations
970
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
971
class LowLevelKnitIndexTests_c(LowLevelKnitIndexTests):
972
973
    _test_needs_features = [CompiledKnitFeature]
974
975
    def get_knit_index(self, *args, **kwargs):
976
        orig = knit._load_data
977
        def reset():
978
            knit._load_data = orig
979
        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
980
        from bzrlib._knit_load_data_c import _load_data_c
981
        knit._load_data = _load_data_c
2484.1.1 by John Arbash Meinel
Add an initial function to read knit indexes in pyrex.
982
        return _KnitIndex(*args, **kwargs)
983
984
985
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
986
class KnitTests(TestCaseWithTransport):
987
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
988
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
989
    def make_test_knit(self, annotate=False, delay_create=False, index=None):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
990
        if not annotate:
991
            factory = KnitPlainFactory()
992
        else:
993
            factory = None
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
994
        return KnitVersionedFile('test', get_transport('.'), access_mode='w',
995
                                 factory=factory, create=True,
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
996
                                 delay_create=delay_create, index=index)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
997
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
998
999
class BasicKnitTests(KnitTests):
1000
1001
    def add_stock_one_and_one_a(self, k):
1002
        k.add_lines('text-1', [], split_lines(TEXT_1))
1003
        k.add_lines('text-1a', ['text-1'], split_lines(TEXT_1A))
1004
1005
    def test_knit_constructor(self):
1006
        """Construct empty k"""
1007
        self.make_test_knit()
1008
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1009
    def test_make_explicit_index(self):
1010
        """We can supply an index to use."""
1011
        knit = KnitVersionedFile('test', get_transport('.'),
1012
            index='strangelove')
1013
        self.assertEqual(knit._index, 'strangelove')
1014
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1015
    def test_knit_add(self):
1016
        """Store one text in knit and retrieve"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1017
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1018
        k.add_lines('text-1', [], split_lines(TEXT_1))
1019
        self.assertTrue(k.has_version('text-1'))
1020
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
1021
1022
    def test_knit_reload(self):
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1023
        # 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.
1024
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1025
        k.add_lines('text-1', [], split_lines(TEXT_1))
1026
        del k
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1027
        k2 = KnitVersionedFile('test', get_transport('.'), access_mode='r', factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1028
        self.assertTrue(k2.has_version('text-1'))
1029
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
1030
1031
    def test_knit_several(self):
1032
        """Store several texts in a knit"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1033
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1034
        k.add_lines('text-1', [], split_lines(TEXT_1))
1035
        k.add_lines('text-2', [], split_lines(TEXT_2))
1036
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
1037
        self.assertEqualDiff(''.join(k.get_lines('text-2')), TEXT_2)
1038
        
1039
    def test_repeated_add(self):
1040
        """Knit traps attempt to replace existing version"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1041
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1042
        k.add_lines('text-1', [], split_lines(TEXT_1))
1043
        self.assertRaises(RevisionAlreadyPresent, 
1044
                k.add_lines,
1045
                'text-1', [], split_lines(TEXT_1))
1046
1047
    def test_empty(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1048
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1049
        k.add_lines('text-1', [], [])
1050
        self.assertEquals(k.get_lines('text-1'), [])
1051
1052
    def test_incomplete(self):
1053
        """Test if texts without a ending line-end can be inserted and
1054
        extracted."""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1055
        k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1056
        k.add_lines('text-1', [], ['a\n',    'b'  ])
1057
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1666.1.6 by Robert Collins
Make knit the default format.
1058
        # reopening ensures maximum room for confusion
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1059
        k = KnitVersionedFile('test', get_transport('.'), delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1060
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
1061
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
1062
1063
    def test_delta(self):
1064
        """Expression of knit delta as lines"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1065
        k = self.make_test_knit()
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
1066
        KnitContent
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1067
        td = list(line_delta(TEXT_1.splitlines(True),
1068
                             TEXT_1A.splitlines(True)))
1069
        self.assertEqualDiff(''.join(td), delta_1_1a)
1070
        out = apply_line_delta(TEXT_1.splitlines(True), td)
1071
        self.assertEqualDiff(''.join(out), TEXT_1A)
1072
2520.4.47 by Aaron Bentley
Fix get_line_delta_blocks with eol
1073
    def assertDerivedBlocksEqual(self, source, target, noeol=False):
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
1074
        """Assert that the derived matching blocks match real output"""
1075
        source_lines = source.splitlines(True)
1076
        target_lines = target.splitlines(True)
2520.4.47 by Aaron Bentley
Fix get_line_delta_blocks with eol
1077
        def nl(line):
1078
            if noeol and not line.endswith('\n'):
1079
                return line + '\n'
1080
            else:
1081
                return line
1082
        source_content = KnitContent([(None, nl(l)) for l in source_lines])
1083
        target_content = KnitContent([(None, nl(l)) for l in target_lines])
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
1084
        line_delta = source_content.line_delta(target_content)
2520.4.47 by Aaron Bentley
Fix get_line_delta_blocks with eol
1085
        delta_blocks = list(KnitContent.get_line_delta_blocks(line_delta,
1086
            source_lines, target_lines))
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
1087
        matcher = KnitSequenceMatcher(None, source_lines, target_lines)
2520.4.47 by Aaron Bentley
Fix get_line_delta_blocks with eol
1088
        matcher_blocks = list(list(matcher.get_matching_blocks()))
1089
        self.assertEqual(matcher_blocks, delta_blocks)
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
1090
1091
    def test_get_line_delta_blocks(self):
1092
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'q\nc\n')
1093
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1)
1094
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1A)
1095
        self.assertDerivedBlocksEqual(TEXT_1, TEXT_1B)
1096
        self.assertDerivedBlocksEqual(TEXT_1B, TEXT_1A)
1097
        self.assertDerivedBlocksEqual(TEXT_1A, TEXT_1B)
1098
        self.assertDerivedBlocksEqual(TEXT_1A, '')
1099
        self.assertDerivedBlocksEqual('', TEXT_1A)
1100
        self.assertDerivedBlocksEqual('', '')
2520.4.47 by Aaron Bentley
Fix get_line_delta_blocks with eol
1101
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd')
1102
1103
    def test_get_line_delta_blocks_noeol(self):
2520.4.48 by Aaron Bentley
Support getting blocks from knit deltas with no final EOL
1104
        """Handle historical knit deltas safely
1105
1106
        Some existing knit deltas don't consider the last line to differ
1107
        when the only difference whether it has a final newline.
1108
1109
        New knit deltas appear to always consider the last line to differ
1110
        in this case.
1111
        """
2520.4.47 by Aaron Bentley
Fix get_line_delta_blocks with eol
1112
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\nd\n', noeol=True)
1113
        self.assertDerivedBlocksEqual('a\nb\nc\nd\n', 'a\nb\nc', noeol=True)
2520.4.48 by Aaron Bentley
Support getting blocks from knit deltas with no final EOL
1114
        self.assertDerivedBlocksEqual('a\nb\nc\n', 'a\nb\nc', noeol=True)
1115
        self.assertDerivedBlocksEqual('a\nb\nc', 'a\nb\nc\n', noeol=True)
2520.4.41 by Aaron Bentley
Accelerate mpdiff generation
1116
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1117
    def test_add_with_parents(self):
1118
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1119
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1120
        self.add_stock_one_and_one_a(k)
1121
        self.assertEquals(k.get_parents('text-1'), [])
1122
        self.assertEquals(k.get_parents('text-1a'), ['text-1'])
1123
1124
    def test_ancestry(self):
1125
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1126
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1127
        self.add_stock_one_and_one_a(k)
1128
        self.assertEquals(set(k.get_ancestry(['text-1a'])), set(['text-1a', 'text-1']))
1129
1130
    def test_add_delta(self):
1131
        """Store in knit with parents"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1132
        k = KnitVersionedFile('test', get_transport('.'), factory=KnitPlainFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
1133
            delta=True, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1134
        self.add_stock_one_and_one_a(k)
1596.2.7 by Robert Collins
Remove the requirement for reannotation in knit joins.
1135
        k.clear_cache()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1136
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1137
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1138
    def test_add_delta_knit_graph_index(self):
1139
        """Does adding work with a KnitGraphIndex."""
1140
        index = InMemoryGraphIndex(2)
1141
        knit_index = KnitGraphIndex(index, add_callback=index.add_nodes,
1142
            deltas=True)
1143
        k = KnitVersionedFile('test', get_transport('.'),
1144
            delta=True, create=True, index=knit_index)
1145
        self.add_stock_one_and_one_a(k)
1146
        k.clear_cache()
1147
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
1148
        # check the index had the right data added.
1149
        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.
1150
            (index, ('text-1', ), ' 0 127', ((), ())),
1151
            (index, ('text-1a', ), ' 127 140', ((('text-1', ),), (('text-1', ),))),
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1152
            ]), set(index.iter_all_entries()))
1153
        # we should not have a .kndx file
1154
        self.assertFalse(get_transport('.').has('test.kndx'))
1155
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1156
    def test_annotate(self):
1157
        """Annotations"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1158
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
1159
            delta=True, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1160
        self.insert_and_test_small_annotate(k)
1161
1162
    def insert_and_test_small_annotate(self, k):
1163
        """test annotation with k works correctly."""
1164
        k.add_lines('text-1', [], ['a\n', 'b\n'])
1165
        k.add_lines('text-2', ['text-1'], ['a\n', 'c\n'])
1166
1167
        origins = k.annotate('text-2')
1168
        self.assertEquals(origins[0], ('text-1', 'a\n'))
1169
        self.assertEquals(origins[1], ('text-2', 'c\n'))
1170
1171
    def test_annotate_fulltext(self):
1172
        """Annotations"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1173
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
1174
            delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1175
        self.insert_and_test_small_annotate(k)
1176
1177
    def test_annotate_merge_1(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1178
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1179
        k.add_lines('text-a1', [], ['a\n', 'b\n'])
1180
        k.add_lines('text-a2', [], ['d\n', 'c\n'])
1181
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['d\n', 'b\n'])
1182
        origins = k.annotate('text-am')
1183
        self.assertEquals(origins[0], ('text-a2', 'd\n'))
1184
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
1185
1186
    def test_annotate_merge_2(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1187
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1188
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1189
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1190
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['a\n', 'y\n', 'c\n'])
1191
        origins = k.annotate('text-am')
1192
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1193
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1194
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
1195
1196
    def test_annotate_merge_9(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1197
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1198
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1199
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1200
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'c\n'])
1201
        origins = k.annotate('text-am')
1202
        self.assertEquals(origins[0], ('text-am', 'k\n'))
1203
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1204
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
1205
1206
    def test_annotate_merge_3(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1207
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1208
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1209
        k.add_lines('text-a2', [] ,['x\n', 'y\n', 'z\n'])
1210
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'z\n'])
1211
        origins = k.annotate('text-am')
1212
        self.assertEquals(origins[0], ('text-am', 'k\n'))
1213
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
1214
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
1215
1216
    def test_annotate_merge_4(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1217
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1218
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1219
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
1220
        k.add_lines('text-a3', ['text-a1'], ['a\n', 'b\n', 'p\n'])
1221
        k.add_lines('text-am', ['text-a2', 'text-a3'], ['a\n', 'b\n', 'z\n'])
1222
        origins = k.annotate('text-am')
1223
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1224
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
1225
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
1226
1227
    def test_annotate_merge_5(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1228
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1229
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
1230
        k.add_lines('text-a2', [], ['d\n', 'e\n', 'f\n'])
1231
        k.add_lines('text-a3', [], ['x\n', 'y\n', 'z\n'])
1232
        k.add_lines('text-am',
1233
                    ['text-a1', 'text-a2', 'text-a3'],
1234
                    ['a\n', 'e\n', 'z\n'])
1235
        origins = k.annotate('text-am')
1236
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
1237
        self.assertEquals(origins[1], ('text-a2', 'e\n'))
1238
        self.assertEquals(origins[2], ('text-a3', 'z\n'))
1239
1240
    def test_annotate_file_cherry_pick(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1241
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1242
        k.add_lines('text-1', [], ['a\n', 'b\n', 'c\n'])
1243
        k.add_lines('text-2', ['text-1'], ['d\n', 'e\n', 'f\n'])
1244
        k.add_lines('text-3', ['text-2', 'text-1'], ['a\n', 'b\n', 'c\n'])
1245
        origins = k.annotate('text-3')
1246
        self.assertEquals(origins[0], ('text-1', 'a\n'))
1247
        self.assertEquals(origins[1], ('text-1', 'b\n'))
1248
        self.assertEquals(origins[2], ('text-1', 'c\n'))
1249
1250
    def test_knit_join(self):
1251
        """Store in knit with parents"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1252
        k1 = KnitVersionedFile('test1', get_transport('.'), factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1253
        k1.add_lines('text-a', [], split_lines(TEXT_1))
1254
        k1.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
1255
1256
        k1.add_lines('text-c', [], split_lines(TEXT_1))
1257
        k1.add_lines('text-d', ['text-c'], split_lines(TEXT_1))
1258
1259
        k1.add_lines('text-m', ['text-b', 'text-d'], split_lines(TEXT_1))
1260
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1261
        k2 = KnitVersionedFile('test2', get_transport('.'), factory=KnitPlainFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1262
        count = k2.join(k1, version_ids=['text-m'])
1263
        self.assertEquals(count, 5)
1264
        self.assertTrue(k2.has_version('text-a'))
1265
        self.assertTrue(k2.has_version('text-c'))
1266
1267
    def test_reannotate(self):
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1268
        k1 = KnitVersionedFile('knit1', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
1269
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1270
        # 0
1271
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
1272
        # 1
1273
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
1274
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1275
        k2 = KnitVersionedFile('test2', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
1276
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1277
        k2.join(k1, version_ids=['text-b'])
1278
1279
        # 2
1280
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
1281
        # 2
1282
        k2.add_lines('text-c', ['text-b'], ['z\n', 'c\n'])
1283
        # 3
1284
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
1285
1286
        # test-c will have index 3
1287
        k1.join(k2, version_ids=['text-c'])
1288
1289
        lines = k1.get_lines('text-c')
1290
        self.assertEquals(lines, ['z\n', 'c\n'])
1291
1292
        origins = k1.annotate('text-c')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
1293
        self.assertEquals(origins[0], ('text-c', 'z\n'))
1294
        self.assertEquals(origins[1], ('text-b', 'c\n'))
1295
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
1296
    def test_get_line_delta_texts(self):
1297
        """Make sure we can call get_texts on text with reused line deltas"""
1298
        k1 = KnitVersionedFile('test1', get_transport('.'), 
1299
                               factory=KnitPlainFactory(), create=True)
1300
        for t in range(3):
1301
            if t == 0:
1302
                parents = []
1303
            else:
1304
                parents = ['%d' % (t-1)]
1305
            k1.add_lines('%d' % t, parents, ['hello\n'] * t)
1306
        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.
1307
        
1308
    def test_iter_lines_reads_in_order(self):
1309
        t = MemoryTransport()
1310
        instrumented_t = TransportLogger(t)
1311
        k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
1312
        self.assertEqual([('id.kndx',)], instrumented_t._calls)
1313
        # add texts with no required ordering
1314
        k1.add_lines('base', [], ['text\n'])
1315
        k1.add_lines('base2', [], ['text2\n'])
1316
        k1.clear_cache()
1317
        instrumented_t._calls = []
1318
        # request a last-first iteration
1319
        results = list(k1.iter_lines_added_or_present_in_versions(['base2', 'base']))
1628.1.2 by Robert Collins
More knit micro-optimisations.
1320
        self.assertEqual([('id.knit', [(0, 87), (87, 89)])], instrumented_t._calls)
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.
1321
        self.assertEqual(['text\n', 'text2\n'], results)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1322
1563.2.13 by Robert Collins
InterVersionedFile implemented.
1323
    def test_create_empty_annotated(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
1324
        k1 = self.make_test_knit(True)
1563.2.13 by Robert Collins
InterVersionedFile implemented.
1325
        # 0
1326
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
1327
        k2 = k1.create_empty('t', MemoryTransport())
1328
        self.assertTrue(isinstance(k2.factory, KnitAnnotateFactory))
1329
        self.assertEqual(k1.delta, k2.delta)
1330
        # the generic test checks for empty content and file class
1331
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1332
    def test_knit_format(self):
1333
        # this tests that a new knit index file has the expected content
1334
        # and that is writes the data we expect as records are added.
1335
        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
1336
        # Now knit files are not created until we first add data to them
1666.1.6 by Robert Collins
Make knit the default format.
1337
        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.
1338
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
1339
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
1340
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1341
            "\n"
1342
            "revid fulltext 0 84 .a_ghost :",
1343
            'test.kndx')
1344
        knit.add_lines_with_ghosts('revid2', ['revid'], ['a\n'])
1345
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
1346
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1347
            "\nrevid fulltext 0 84 .a_ghost :"
1348
            "\nrevid2 line-delta 84 82 0 :",
1349
            'test.kndx')
1350
        # we should be able to load this file again
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1351
        knit = KnitVersionedFile('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.
1352
        self.assertEqual(['revid', 'revid2'], knit.versions())
1353
        # 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
1354
        indexfile = file('test.kndx', 'ab')
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
1355
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
1356
        indexfile.close()
1357
        # we should be able to load this file again
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1358
        knit = KnitVersionedFile('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.
1359
        self.assertEqual(['revid', 'revid2'], knit.versions())
1360
        # and add a revision with the same id the failed write had
1361
        knit.add_lines('revid3', ['revid2'], ['a\n'])
1362
        # and when reading it revid3 should now appear.
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
1363
        knit = KnitVersionedFile('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.
1364
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
1365
        self.assertEqual(['revid2'], knit.get_parents('revid3'))
1366
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
1367
    def test_delay_create(self):
1368
        """Test that passing delay_create=True creates files late"""
1369
        knit = self.make_test_knit(annotate=True, delay_create=True)
1370
        self.failIfExists('test.knit')
1371
        self.failIfExists('test.kndx')
1372
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
1373
        self.failUnlessExists('test.knit')
1374
        self.assertFileEqual(
1375
            "# bzr knit index 8\n"
1376
            "\n"
1377
            "revid fulltext 0 84 .a_ghost :",
1378
            'test.kndx')
1379
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
1380
    def test_create_parent_dir(self):
1381
        """create_parent_dir can create knits in nonexistant dirs"""
1382
        # Has no effect if we don't set 'delay_create'
1383
        trans = get_transport('.')
1384
        self.assertRaises(NoSuchFile, KnitVersionedFile, 'dir/test',
1385
                          trans, access_mode='w', factory=None,
1386
                          create=True, create_parent_dir=True)
1387
        # Nothing should have changed yet
1388
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1389
                                 factory=None, create=True,
1390
                                 create_parent_dir=True,
1391
                                 delay_create=True)
1392
        self.failIfExists('dir/test.knit')
1393
        self.failIfExists('dir/test.kndx')
1394
        self.failIfExists('dir')
1395
        knit.add_lines('revid', [], ['a\n'])
1396
        self.failUnlessExists('dir')
1397
        self.failUnlessExists('dir/test.knit')
1398
        self.assertFileEqual(
1399
            "# bzr knit index 8\n"
1400
            "\n"
1401
            "revid fulltext 0 84  :",
1402
            'dir/test.kndx')
1403
1946.2.13 by John Arbash Meinel
Test that passing modes does the right thing for knits.
1404
    def test_create_mode_700(self):
1405
        trans = get_transport('.')
1406
        if not trans._can_roundtrip_unix_modebits():
1407
            # Can't roundtrip, so no need to run this test
1408
            return
1409
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1410
                                 factory=None, create=True,
1411
                                 create_parent_dir=True,
1412
                                 delay_create=True,
1413
                                 file_mode=0600,
1414
                                 dir_mode=0700)
1415
        knit.add_lines('revid', [], ['a\n'])
1416
        self.assertTransportMode(trans, 'dir', 0700)
1417
        self.assertTransportMode(trans, 'dir/test.knit', 0600)
1418
        self.assertTransportMode(trans, 'dir/test.kndx', 0600)
1419
1420
    def test_create_mode_770(self):
1421
        trans = get_transport('.')
1422
        if not trans._can_roundtrip_unix_modebits():
1423
            # Can't roundtrip, so no need to run this test
1424
            return
1425
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1426
                                 factory=None, create=True,
1427
                                 create_parent_dir=True,
1428
                                 delay_create=True,
1429
                                 file_mode=0660,
1430
                                 dir_mode=0770)
1431
        knit.add_lines('revid', [], ['a\n'])
1432
        self.assertTransportMode(trans, 'dir', 0770)
1433
        self.assertTransportMode(trans, 'dir/test.knit', 0660)
1434
        self.assertTransportMode(trans, 'dir/test.kndx', 0660)
1435
1436
    def test_create_mode_777(self):
1437
        trans = get_transport('.')
1438
        if not trans._can_roundtrip_unix_modebits():
1439
            # Can't roundtrip, so no need to run this test
1440
            return
1441
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
1442
                                 factory=None, create=True,
1443
                                 create_parent_dir=True,
1444
                                 delay_create=True,
1445
                                 file_mode=0666,
1446
                                 dir_mode=0777)
1447
        knit.add_lines('revid', [], ['a\n'])
1448
        self.assertTransportMode(trans, 'dir', 0777)
1449
        self.assertTransportMode(trans, 'dir/test.knit', 0666)
1450
        self.assertTransportMode(trans, 'dir/test.kndx', 0666)
1451
1664.2.1 by Aaron Bentley
Start work on plan_merge test
1452
    def test_plan_merge(self):
1453
        my_knit = self.make_test_knit(annotate=True)
1454
        my_knit.add_lines('text1', [], split_lines(TEXT_1))
1455
        my_knit.add_lines('text1a', ['text1'], split_lines(TEXT_1A))
1456
        my_knit.add_lines('text1b', ['text1'], split_lines(TEXT_1B))
1664.2.3 by Aaron Bentley
Add failing test case
1457
        plan = list(my_knit.plan_merge('text1a', 'text1b'))
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
1458
        for plan_line, expected_line in zip(plan, AB_MERGE):
1459
            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.
1460
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1461
1462
TEXT_1 = """\
1463
Banana cup cakes:
1464
1465
- bananas
1466
- eggs
1467
- broken tea cups
1468
"""
1469
1470
TEXT_1A = """\
1471
Banana cup cake recipe
1472
(serves 6)
1473
1474
- bananas
1475
- eggs
1476
- broken tea cups
1477
- self-raising flour
1478
"""
1479
1664.2.1 by Aaron Bentley
Start work on plan_merge test
1480
TEXT_1B = """\
1481
Banana cup cake recipe
1482
1483
- bananas (do not use plantains!!!)
1484
- broken tea cups
1485
- flour
1486
"""
1487
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1488
delta_1_1a = """\
1489
0,1,2
1490
Banana cup cake recipe
1491
(serves 6)
1492
5,5,1
1493
- self-raising flour
1494
"""
1495
1496
TEXT_2 = """\
1497
Boeuf bourguignon
1498
1499
- beef
1500
- red wine
1501
- small onions
1502
- carrot
1503
- mushrooms
1504
"""
1505
1664.2.3 by Aaron Bentley
Add failing test case
1506
AB_MERGE_TEXT="""unchanged|Banana cup cake recipe
1507
new-a|(serves 6)
1508
unchanged|
1509
killed-b|- bananas
1510
killed-b|- eggs
1511
new-b|- bananas (do not use plantains!!!)
1512
unchanged|- broken tea cups
1513
new-a|- self-raising flour
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
1514
new-b|- flour
1515
"""
1664.2.3 by Aaron Bentley
Add failing test case
1516
AB_MERGE=[tuple(l.split('|')) for l in AB_MERGE_TEXT.splitlines(True)]
1517
1518
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1519
def line_delta(from_lines, to_lines):
1520
    """Generate line-based delta from one text to another"""
1521
    s = difflib.SequenceMatcher(None, from_lines, to_lines)
1522
    for op in s.get_opcodes():
1523
        if op[0] == 'equal':
1524
            continue
1525
        yield '%d,%d,%d\n' % (op[1], op[2], op[4]-op[3])
1526
        for i in range(op[3], op[4]):
1527
            yield to_lines[i]
1528
1529
1530
def apply_line_delta(basis_lines, delta_lines):
1531
    """Apply a line-based perfect diff
1532
    
1533
    basis_lines -- text to apply the patch to
1534
    delta_lines -- diff instructions and content
1535
    """
1536
    out = basis_lines[:]
1537
    i = 0
1538
    offset = 0
1539
    while i < len(delta_lines):
1540
        l = delta_lines[i]
1541
        a, b, c = map(long, l.split(','))
1542
        i = i + 1
1543
        out[offset+a:offset+b] = delta_lines[i:i+c]
1544
        i = i + c
1545
        offset = offset + (b - a) + c
1546
    return out
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
1547
1548
1549
class TestWeaveToKnit(KnitTests):
1550
1551
    def test_weave_to_knit_matches(self):
1552
        # check that the WeaveToKnit is_compatible function
1553
        # registers True for a Weave to a Knit.
1554
        w = Weave()
1555
        k = self.make_test_knit()
1556
        self.failUnless(WeaveToKnit.is_compatible(w, k))
1557
        self.failIf(WeaveToKnit.is_compatible(k, w))
1558
        self.failIf(WeaveToKnit.is_compatible(w, w))
1559
        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
1560
1561
1562
class TestKnitCaching(KnitTests):
1563
    
1564
    def create_knit(self, cache_add=False):
1565
        k = self.make_test_knit(True)
1566
        if cache_add:
1567
            k.enable_cache()
1568
1569
        k.add_lines('text-1', [], split_lines(TEXT_1))
1570
        k.add_lines('text-2', [], split_lines(TEXT_2))
1571
        return k
1572
1573
    def test_no_caching(self):
1574
        k = self.create_knit()
1575
        # Nothing should be cached without setting 'enable_cache'
1576
        self.assertEqual({}, k._data._cache)
1577
1578
    def test_cache_add_and_clear(self):
1579
        k = self.create_knit(True)
1580
1581
        self.assertEqual(['text-1', 'text-2'], sorted(k._data._cache.keys()))
1582
1583
        k.clear_cache()
1584
        self.assertEqual({}, k._data._cache)
1585
1586
    def test_cache_data_read_raw(self):
1587
        k = self.create_knit()
1588
1589
        # Now cache and read
1590
        k.enable_cache()
1591
1592
        def read_one_raw(version):
1593
            pos_map = k._get_components_positions([version])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1594
            method, index_memo, next = pos_map[version]
1595
            lst = list(k._data.read_records_iter_raw([(version, index_memo)]))
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
1596
            self.assertEqual(1, len(lst))
1597
            return lst[0]
1598
1599
        val = read_one_raw('text-1')
1863.1.8 by John Arbash Meinel
Removing disk-backed-cache
1600
        self.assertEqual({'text-1':val[1]}, k._data._cache)
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
1601
1602
        k.clear_cache()
1603
        # After clear, new reads are not cached
1604
        self.assertEqual({}, k._data._cache)
1605
1606
        val2 = read_one_raw('text-1')
1607
        self.assertEqual(val, val2)
1608
        self.assertEqual({}, k._data._cache)
1609
1610
    def test_cache_data_read(self):
1611
        k = self.create_knit()
1612
1613
        def read_one(version):
1614
            pos_map = k._get_components_positions([version])
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1615
            method, index_memo, next = pos_map[version]
1616
            lst = list(k._data.read_records_iter([(version, index_memo)]))
1863.1.1 by John Arbash Meinel
Allow Versioned files to do caching if explicitly asked, and implement for Knit
1617
            self.assertEqual(1, len(lst))
1618
            return lst[0]
1619
1620
        # Now cache and read
1621
        k.enable_cache()
1622
1623
        val = read_one('text-2')
1624
        self.assertEqual(['text-2'], k._data._cache.keys())
1625
        self.assertEqual('text-2', val[0])
1626
        content, digest = k._data._parse_record('text-2',
1627
                                                k._data._cache['text-2'])
1628
        self.assertEqual(content, val[1])
1629
        self.assertEqual(digest, val[2])
1630
1631
        k.clear_cache()
1632
        self.assertEqual({}, k._data._cache)
1633
1634
        val2 = read_one('text-2')
1635
        self.assertEqual(val, val2)
1636
        self.assertEqual({}, k._data._cache)
1637
1638
    def test_cache_read(self):
1639
        k = self.create_knit()
1640
        k.enable_cache()
1641
1642
        text = k.get_text('text-1')
1643
        self.assertEqual(TEXT_1, text)
1644
        self.assertEqual(['text-1'], k._data._cache.keys())
1645
1646
        k.clear_cache()
1647
        self.assertEqual({}, k._data._cache)
1648
1649
        text = k.get_text('text-1')
1650
        self.assertEqual(TEXT_1, text)
1651
        self.assertEqual({}, k._data._cache)
2102.2.1 by John Arbash Meinel
Fix bug #64789 _KnitIndex.add_versions() should dict compress new revisions
1652
1653
1654
class TestKnitIndex(KnitTests):
1655
1656
    def test_add_versions_dictionary_compresses(self):
1657
        """Adding versions to the index should update the lookup dict"""
1658
        knit = self.make_test_knit()
1659
        idx = knit._index
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1660
        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
1661
        self.check_file_contents('test.kndx',
1662
            '# bzr knit index 8\n'
1663
            '\n'
1664
            'a-1 fulltext 0 0  :'
1665
            )
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1666
        idx.add_versions([('a-2', ['fulltext'], (None, 0, 0), ['a-1']),
1667
                          ('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
1668
                         ])
1669
        self.check_file_contents('test.kndx',
1670
            '# bzr knit index 8\n'
1671
            '\n'
1672
            'a-1 fulltext 0 0  :\n'
1673
            'a-2 fulltext 0 0 0 :\n'
1674
            'a-3 fulltext 0 0 1 :'
1675
            )
1676
        self.assertEqual(['a-1', 'a-2', 'a-3'], idx._history)
1677
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0),
1678
                          'a-2':('a-2', ['fulltext'], 0, 0, ['a-1'], 1),
1679
                          'a-3':('a-3', ['fulltext'], 0, 0, ['a-2'], 2),
1680
                         }, idx._cache)
1681
1682
    def test_add_versions_fails_clean(self):
1683
        """If add_versions fails in the middle, it restores a pristine state.
1684
1685
        Any modifications that are made to the index are reset if all versions
1686
        cannot be added.
1687
        """
1688
        # This cheats a little bit by passing in a generator which will
1689
        # raise an exception before the processing finishes
1690
        # Other possibilities would be to have an version with the wrong number
1691
        # of entries, or to make the backing transport unable to write any
1692
        # files.
1693
1694
        knit = self.make_test_knit()
1695
        idx = knit._index
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1696
        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
1697
1698
        class StopEarly(Exception):
1699
            pass
1700
1701
        def generate_failure():
1702
            """Add some entries and then raise an exception"""
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1703
            yield ('a-2', ['fulltext'], (None, 0, 0), ['a-1'])
1704
            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
1705
            raise StopEarly()
1706
1707
        # Assert the pre-condition
1708
        self.assertEqual(['a-1'], idx._history)
1709
        self.assertEqual({'a-1':('a-1', ['fulltext'], 0, 0, [], 0)}, idx._cache)
1710
1711
        self.assertRaises(StopEarly, idx.add_versions, generate_failure())
1712
1713
        # And it shouldn't be modified
1714
        self.assertEqual(['a-1'], idx._history)
1715
        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.
1716
1717
    def test_knit_index_ignores_empty_files(self):
1718
        # There was a race condition in older bzr, where a ^C at the right time
1719
        # could leave an empty .kndx file, which bzr would later claim was a
1720
        # corrupted file since the header was not present. In reality, the file
1721
        # just wasn't created, so it should be ignored.
1722
        t = get_transport('.')
1723
        t.put_bytes('test.kndx', '')
1724
1725
        knit = self.make_test_knit()
1726
1727
    def test_knit_index_checks_header(self):
1728
        t = get_transport('.')
1729
        t.put_bytes('test.kndx', '# not really a knit header\n\n')
1730
2196.2.1 by John Arbash Meinel
Merge Dmitry's optimizations and minimize the actual diff.
1731
        self.assertRaises(KnitHeaderError, self.make_test_knit)
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1732
1733
1734
class TestGraphIndexKnit(KnitTests):
1735
    """Tests for knits using a GraphIndex rather than a KnitIndex."""
1736
1737
    def make_g_index(self, name, ref_lists=0, nodes=[]):
1738
        builder = GraphIndexBuilder(ref_lists)
1739
        for node, references, value in nodes:
1740
            builder.add_node(node, references, value)
1741
        stream = builder.finish()
1742
        trans = self.get_transport()
1743
        trans.put_file(name, stream)
1744
        return GraphIndex(trans, name)
1745
1746
    def two_graph_index(self, deltas=False, catch_adds=False):
1747
        """Build a two-graph index.
1748
1749
        :param deltas: If true, use underlying indices with two node-ref
1750
            lists and 'parent' set to a delta-compressed against tail.
1751
        """
1752
        # build a complex graph across several indices.
1753
        if deltas:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1754
            # delta compression inn the index
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1755
            index1 = self.make_g_index('1', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1756
                (('tip', ), 'N0 100', ([('parent', )], [], )),
1757
                (('tail', ), '', ([], []))])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1758
            index2 = self.make_g_index('2', 2, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1759
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], [('tail', )])),
1760
                (('separate', ), '', ([], []))])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1761
        else:
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1762
            # just blob location and graph in the index.
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1763
            index1 = self.make_g_index('1', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1764
                (('tip', ), 'N0 100', ([('parent', )], )),
1765
                (('tail', ), '', ([], ))])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1766
            index2 = self.make_g_index('2', 1, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1767
                (('parent', ), ' 100 78', ([('tail', ), ('ghost', )], )),
1768
                (('separate', ), '', ([], ))])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1769
        combined_index = CombinedGraphIndex([index1, index2])
1770
        if catch_adds:
1771
            self.combined_index = combined_index
1772
            self.caught_entries = []
1773
            add_callback = self.catch_add
1774
        else:
1775
            add_callback = None
1776
        return KnitGraphIndex(combined_index, deltas=deltas,
1777
            add_callback=add_callback)
1778
1779
    def test_get_graph(self):
1780
        index = self.two_graph_index()
1781
        self.assertEqual(set([
1782
            ('tip', ('parent', )),
1783
            ('tail', ()),
1784
            ('parent', ('tail', 'ghost')),
1785
            ('separate', ()),
1786
            ]), set(index.get_graph()))
1787
1788
    def test_get_ancestry(self):
1789
        # get_ancestry is defined as eliding ghosts, not erroring.
1790
        index = self.two_graph_index()
1791
        self.assertEqual([], index.get_ancestry([]))
1792
        self.assertEqual(['separate'], index.get_ancestry(['separate']))
1793
        self.assertEqual(['tail'], index.get_ancestry(['tail']))
1794
        self.assertEqual(['tail', 'parent'], index.get_ancestry(['parent']))
1795
        self.assertEqual(['tail', 'parent', 'tip'], index.get_ancestry(['tip']))
1796
        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
1797
            (['tail', 'parent', 'tip', 'separate'],
1798
             ['separate', 'tail', 'parent', 'tip'],
1799
            ))
1800
        # and without topo_sort
1801
        self.assertEqual(set(['separate']),
1802
            set(index.get_ancestry(['separate'], topo_sorted=False)))
1803
        self.assertEqual(set(['tail']),
1804
            set(index.get_ancestry(['tail'], topo_sorted=False)))
1805
        self.assertEqual(set(['tail', 'parent']),
1806
            set(index.get_ancestry(['parent'], topo_sorted=False)))
1807
        self.assertEqual(set(['tail', 'parent', 'tip']),
1808
            set(index.get_ancestry(['tip'], topo_sorted=False)))
1809
        self.assertEqual(set(['separate', 'tail', 'parent', 'tip']),
1810
            set(index.get_ancestry(['tip', 'separate'])))
1811
        # asking for a ghost makes it go boom.
1812
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
1813
1814
    def test_get_ancestry_with_ghosts(self):
1815
        index = self.two_graph_index()
1816
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
1817
        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
1818
        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
1819
        self.assertTrue(index.get_ancestry_with_ghosts(['parent']) in
1820
            (['tail', 'ghost', 'parent'],
1821
             ['ghost', 'tail', 'parent'],
1822
            ))
1823
        self.assertTrue(index.get_ancestry_with_ghosts(['tip']) in
1824
            (['tail', 'ghost', 'parent', 'tip'],
1825
             ['ghost', 'tail', 'parent', 'tip'],
1826
            ))
1827
        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
1828
            (['tail', 'ghost', 'parent', 'tip', 'separate'],
1829
             ['ghost', 'tail', 'parent', 'tip', 'separate'],
1830
             ['separate', 'tail', 'ghost', 'parent', 'tip'],
1831
             ['separate', 'ghost', 'tail', 'parent', 'tip'],
1832
            ))
1833
        # asking for a ghost makes it go boom.
1834
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
1835
1836
    def test_num_versions(self):
1837
        index = self.two_graph_index()
1838
        self.assertEqual(4, index.num_versions())
1839
1840
    def test_get_versions(self):
1841
        index = self.two_graph_index()
1842
        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
1843
            set(index.get_versions()))
1844
1845
    def test_has_version(self):
1846
        index = self.two_graph_index()
1847
        self.assertTrue(index.has_version('tail'))
1848
        self.assertFalse(index.has_version('ghost'))
1849
1850
    def test_get_position(self):
1851
        index = self.two_graph_index()
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1852
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
1853
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1854
1855
    def test_get_method_deltas(self):
1856
        index = self.two_graph_index(deltas=True)
1857
        self.assertEqual('fulltext', index.get_method('tip'))
1858
        self.assertEqual('line-delta', index.get_method('parent'))
1859
1860
    def test_get_method_no_deltas(self):
1861
        # check that the parent-history lookup is ignored with deltas=False.
1862
        index = self.two_graph_index(deltas=False)
1863
        self.assertEqual('fulltext', index.get_method('tip'))
1864
        self.assertEqual('fulltext', index.get_method('parent'))
1865
1866
    def test_get_options_deltas(self):
1867
        index = self.two_graph_index(deltas=True)
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
1868
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
1869
        self.assertEqual(['line-delta'], index.get_options('parent'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1870
1871
    def test_get_options_no_deltas(self):
1872
        # check that the parent-history lookup is ignored with deltas=False.
1873
        index = self.two_graph_index(deltas=False)
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
1874
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
1875
        self.assertEqual(['fulltext'], index.get_options('parent'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1876
1877
    def test_get_parents(self):
1878
        # get_parents ignores ghosts
1879
        index = self.two_graph_index()
1880
        self.assertEqual(('tail', ), index.get_parents('parent'))
1881
        # and errors on ghosts.
1882
        self.assertRaises(errors.RevisionNotPresent,
1883
            index.get_parents, 'ghost')
1884
1885
    def test_get_parents_with_ghosts(self):
1886
        index = self.two_graph_index()
1887
        self.assertEqual(('tail', 'ghost'), index.get_parents_with_ghosts('parent'))
1888
        # and errors on ghosts.
1889
        self.assertRaises(errors.RevisionNotPresent,
1890
            index.get_parents_with_ghosts, 'ghost')
1891
1892
    def test_check_versions_present(self):
1893
        # ghosts should not be considered present
1894
        index = self.two_graph_index()
1895
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
1896
            ['ghost'])
1897
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
1898
            ['tail', 'ghost'])
1899
        index.check_versions_present(['tail', 'separate'])
1900
1901
    def catch_add(self, entries):
1902
        self.caught_entries.append(entries)
1903
1904
    def test_add_no_callback_errors(self):
1905
        index = self.two_graph_index()
1906
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1907
            'new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1908
1909
    def test_add_version_smoke(self):
1910
        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
1911
        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.
1912
        self.assertEqual([[(('new', ), 'N50 60', ((('separate',),),))]],
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1913
            self.caught_entries)
1914
1915
    def test_add_version_delta_not_delta_index(self):
1916
        index = self.two_graph_index(catch_adds=True)
1917
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1918
            'new', 'no-eol,line-delta', (None, 0, 100), ['parent'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1919
        self.assertEqual([], self.caught_entries)
1920
1921
    def test_add_version_same_dup(self):
1922
        index = self.two_graph_index(catch_adds=True)
1923
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1924
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
1925
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1926
        # but neither should have added data.
1927
        self.assertEqual([[], []], self.caught_entries)
1928
        
1929
    def test_add_version_different_dup(self):
1930
        index = self.two_graph_index(deltas=True, catch_adds=True)
1931
        # change options
1932
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1933
            'tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])
1934
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1935
            'tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])
1936
        self.assertRaises(errors.KnitCorrupt, index.add_version,
1937
            'tip', 'fulltext', (None, 0, 100), ['parent'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1938
        # position/length
1939
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1940
            'tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1941
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1942
            'tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1943
        # parents
1944
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1945
            'tip', 'fulltext,no-eol', (None, 0, 100), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1946
        self.assertEqual([], self.caught_entries)
1947
        
1948
    def test_add_versions_nodeltas(self):
1949
        index = self.two_graph_index(catch_adds=True)
1950
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1951
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
1952
                ('new2', 'fulltext', (None, 0, 6), ['new']),
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1953
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1954
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),),)),
1955
            (('new2', ), ' 0 6', ((('new',),),))],
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1956
            sorted(self.caught_entries[0]))
1957
        self.assertEqual(1, len(self.caught_entries))
1958
1959
    def test_add_versions_deltas(self):
1960
        index = self.two_graph_index(deltas=True, catch_adds=True)
1961
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1962
                ('new', 'fulltext,no-eol', (None, 50, 60), ['separate']),
1963
                ('new2', 'line-delta', (None, 0, 6), ['new']),
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1964
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
1965
        self.assertEqual([(('new', ), 'N50 60', ((('separate',),), ())),
1966
            (('new2', ), ' 0 6', ((('new',),), (('new',),), ))],
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1967
            sorted(self.caught_entries[0]))
1968
        self.assertEqual(1, len(self.caught_entries))
1969
1970
    def test_add_versions_delta_not_delta_index(self):
1971
        index = self.two_graph_index(catch_adds=True)
1972
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1973
            [('new', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1974
        self.assertEqual([], self.caught_entries)
1975
1976
    def test_add_versions_same_dup(self):
1977
        index = self.two_graph_index(catch_adds=True)
1978
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1979
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
1980
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1981
        # but neither should have added data.
1982
        self.assertEqual([[], []], self.caught_entries)
1983
        
1984
    def test_add_versions_different_dup(self):
1985
        index = self.two_graph_index(deltas=True, catch_adds=True)
1986
        # change options
1987
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1988
            [('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
1989
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1990
            [('tip', 'line-delta,no-eol', (None, 0, 100), ['parent'])])
1991
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
1992
            [('tip', 'fulltext', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1993
        # position/length
1994
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1995
            [('tip', 'fulltext,no-eol', (None, 50, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1996
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
1997
            [('tip', 'fulltext,no-eol', (None, 0, 1000), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
1998
        # parents
1999
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2000
            [('tip', 'fulltext,no-eol', (None, 0, 100), [])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2001
        # change options in the second record
2002
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2003
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent']),
2004
             ('tip', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2005
        self.assertEqual([], self.caught_entries)
2006
2007
    def test_iter_parents(self):
2008
        index1 = self.make_g_index('1', 1, [
2009
        # no parents
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2010
            (('r0', ), 'N0 100', ([], )),
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2011
        # 1 parent
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2012
            (('r1', ), '', ([('r0', )], ))])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2013
        index2 = self.make_g_index('2', 1, [
2014
        # 2 parents
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2015
            (('r2', ), 'N0 100', ([('r1', ), ('r0', )], )),
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2016
            ])
2017
        combined_index = CombinedGraphIndex([index1, index2])
2018
        index = KnitGraphIndex(combined_index)
2019
        # XXX TODO a ghost
2020
        # cases: each sample data individually:
2021
        self.assertEqual(set([('r0', ())]),
2022
            set(index.iter_parents(['r0'])))
2023
        self.assertEqual(set([('r1', ('r0', ))]),
2024
            set(index.iter_parents(['r1'])))
2025
        self.assertEqual(set([('r2', ('r1', 'r0'))]),
2026
            set(index.iter_parents(['r2'])))
2027
        # no nodes returned for a missing node
2028
        self.assertEqual(set(),
2029
            set(index.iter_parents(['missing'])))
2030
        # 1 node returned with missing nodes skipped
2031
        self.assertEqual(set([('r1', ('r0', ))]),
2032
            set(index.iter_parents(['ghost1', 'r1', 'ghost'])))
2033
        # 2 nodes returned
2034
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2035
            set(index.iter_parents(['r0', 'r1'])))
2036
        # 2 nodes returned, missing skipped
2037
        self.assertEqual(set([('r0', ()), ('r1', ('r0', ))]),
2038
            set(index.iter_parents(['a', 'r0', 'b', 'r1', 'c'])))
2039
2040
2041
class TestNoParentsGraphIndexKnit(KnitTests):
2042
    """Tests for knits using KnitGraphIndex with no parents."""
2043
2044
    def make_g_index(self, name, ref_lists=0, nodes=[]):
2045
        builder = GraphIndexBuilder(ref_lists)
2046
        for node, references in nodes:
2047
            builder.add_node(node, references)
2048
        stream = builder.finish()
2049
        trans = self.get_transport()
2050
        trans.put_file(name, stream)
2051
        return GraphIndex(trans, name)
2052
2053
    def test_parents_deltas_incompatible(self):
2054
        index = CombinedGraphIndex([])
2055
        self.assertRaises(errors.KnitError, KnitGraphIndex, index,
2056
            deltas=True, parents=False)
2057
2058
    def two_graph_index(self, catch_adds=False):
2059
        """Build a two-graph index.
2060
2061
        :param deltas: If true, use underlying indices with two node-ref
2062
            lists and 'parent' set to a delta-compressed against tail.
2063
        """
2064
        # put several versions in the index.
2065
        index1 = self.make_g_index('1', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2066
            (('tip', ), 'N0 100'),
2067
            (('tail', ), '')])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2068
        index2 = self.make_g_index('2', 0, [
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2069
            (('parent', ), ' 100 78'),
2070
            (('separate', ), '')])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2071
        combined_index = CombinedGraphIndex([index1, index2])
2072
        if catch_adds:
2073
            self.combined_index = combined_index
2074
            self.caught_entries = []
2075
            add_callback = self.catch_add
2076
        else:
2077
            add_callback = None
2078
        return KnitGraphIndex(combined_index, parents=False,
2079
            add_callback=add_callback)
2080
2081
    def test_get_graph(self):
2082
        index = self.two_graph_index()
2083
        self.assertEqual(set([
2084
            ('tip', ()),
2085
            ('tail', ()),
2086
            ('parent', ()),
2087
            ('separate', ()),
2088
            ]), set(index.get_graph()))
2089
2090
    def test_get_ancestry(self):
2091
        # with no parents, ancestry is always just the key.
2092
        index = self.two_graph_index()
2093
        self.assertEqual([], index.get_ancestry([]))
2094
        self.assertEqual(['separate'], index.get_ancestry(['separate']))
2095
        self.assertEqual(['tail'], index.get_ancestry(['tail']))
2096
        self.assertEqual(['parent'], index.get_ancestry(['parent']))
2097
        self.assertEqual(['tip'], index.get_ancestry(['tip']))
2098
        self.assertTrue(index.get_ancestry(['tip', 'separate']) in
2099
            (['tip', 'separate'],
2100
             ['separate', 'tip'],
2101
            ))
2102
        # asking for a ghost makes it go boom.
2103
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry, ['ghost'])
2104
2105
    def test_get_ancestry_with_ghosts(self):
2106
        index = self.two_graph_index()
2107
        self.assertEqual([], index.get_ancestry_with_ghosts([]))
2108
        self.assertEqual(['separate'], index.get_ancestry_with_ghosts(['separate']))
2109
        self.assertEqual(['tail'], index.get_ancestry_with_ghosts(['tail']))
2110
        self.assertEqual(['parent'], index.get_ancestry_with_ghosts(['parent']))
2111
        self.assertEqual(['tip'], index.get_ancestry_with_ghosts(['tip']))
2112
        self.assertTrue(index.get_ancestry_with_ghosts(['tip', 'separate']) in
2113
            (['tip', 'separate'],
2114
             ['separate', 'tip'],
2115
            ))
2116
        # asking for a ghost makes it go boom.
2117
        self.assertRaises(errors.RevisionNotPresent, index.get_ancestry_with_ghosts, ['ghost'])
2118
2119
    def test_num_versions(self):
2120
        index = self.two_graph_index()
2121
        self.assertEqual(4, index.num_versions())
2122
2123
    def test_get_versions(self):
2124
        index = self.two_graph_index()
2125
        self.assertEqual(set(['tail', 'tip', 'parent', 'separate']),
2126
            set(index.get_versions()))
2127
2128
    def test_has_version(self):
2129
        index = self.two_graph_index()
2130
        self.assertTrue(index.has_version('tail'))
2131
        self.assertFalse(index.has_version('ghost'))
2132
2133
    def test_get_position(self):
2134
        index = self.two_graph_index()
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2135
        self.assertEqual((index._graph_index._indices[0], 0, 100), index.get_position('tip'))
2136
        self.assertEqual((index._graph_index._indices[1], 100, 78), index.get_position('parent'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2137
2138
    def test_get_method(self):
2139
        index = self.two_graph_index()
2140
        self.assertEqual('fulltext', index.get_method('tip'))
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2141
        self.assertEqual(['fulltext'], index.get_options('parent'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2142
2143
    def test_get_options(self):
2144
        index = self.two_graph_index()
2658.2.1 by Robert Collins
Fix mismatch between KnitGraphIndex and KnitIndex in get_options.
2145
        self.assertEqual(['fulltext', 'no-eol'], index.get_options('tip'))
2146
        self.assertEqual(['fulltext'], index.get_options('parent'))
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2147
2148
    def test_get_parents(self):
2149
        index = self.two_graph_index()
2150
        self.assertEqual((), index.get_parents('parent'))
2151
        # and errors on ghosts.
2152
        self.assertRaises(errors.RevisionNotPresent,
2153
            index.get_parents, 'ghost')
2154
2155
    def test_get_parents_with_ghosts(self):
2156
        index = self.two_graph_index()
2157
        self.assertEqual((), index.get_parents_with_ghosts('parent'))
2158
        # and errors on ghosts.
2159
        self.assertRaises(errors.RevisionNotPresent,
2160
            index.get_parents_with_ghosts, 'ghost')
2161
2162
    def test_check_versions_present(self):
2163
        index = self.two_graph_index()
2164
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2165
            ['missing'])
2166
        self.assertRaises(RevisionNotPresent, index.check_versions_present,
2167
            ['tail', 'missing'])
2168
        index.check_versions_present(['tail', 'separate'])
2169
2170
    def catch_add(self, entries):
2171
        self.caught_entries.append(entries)
2172
2173
    def test_add_no_callback_errors(self):
2174
        index = self.two_graph_index()
2175
        self.assertRaises(errors.ReadOnlyError, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2176
            'new', 'fulltext,no-eol', (None, 50, 60), ['separate'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2177
2178
    def test_add_version_smoke(self):
2179
        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
2180
        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.
2181
        self.assertEqual([[(('new', ), 'N50 60')]],
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2182
            self.caught_entries)
2183
2184
    def test_add_version_delta_not_delta_index(self):
2185
        index = self.two_graph_index(catch_adds=True)
2186
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2187
            'new', 'no-eol,line-delta', (None, 0, 100), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2188
        self.assertEqual([], self.caught_entries)
2189
2190
    def test_add_version_same_dup(self):
2191
        index = self.two_graph_index(catch_adds=True)
2192
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2193
        index.add_version('tip', 'fulltext,no-eol', (None, 0, 100), [])
2194
        index.add_version('tip', 'no-eol,fulltext', (None, 0, 100), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2195
        # but neither should have added data.
2196
        self.assertEqual([[], []], self.caught_entries)
2197
        
2198
    def test_add_version_different_dup(self):
2199
        index = self.two_graph_index(catch_adds=True)
2200
        # change options
2201
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2202
            'tip', 'no-eol,line-delta', (None, 0, 100), [])
2203
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2204
            'tip', 'line-delta,no-eol', (None, 0, 100), [])
2205
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2206
            'tip', 'fulltext', (None, 0, 100), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2207
        # position/length
2208
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2209
            'tip', 'fulltext,no-eol', (None, 50, 100), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2210
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2211
            'tip', 'fulltext,no-eol', (None, 0, 1000), [])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2212
        # parents
2213
        self.assertRaises(errors.KnitCorrupt, index.add_version,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2214
            'tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2215
        self.assertEqual([], self.caught_entries)
2216
        
2217
    def test_add_versions(self):
2218
        index = self.two_graph_index(catch_adds=True)
2219
        index.add_versions([
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2220
                ('new', 'fulltext,no-eol', (None, 50, 60), []),
2221
                ('new2', 'fulltext', (None, 0, 6), []),
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2222
                ])
2624.2.5 by Robert Collins
Change bzrlib.index.Index keys to be 1-tuples, not strings.
2223
        self.assertEqual([(('new', ), 'N50 60'), (('new2', ), ' 0 6')],
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2224
            sorted(self.caught_entries[0]))
2225
        self.assertEqual(1, len(self.caught_entries))
2226
2227
    def test_add_versions_delta_not_delta_index(self):
2228
        index = self.two_graph_index(catch_adds=True)
2229
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2230
            [('new', 'no-eol,line-delta', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2231
        self.assertEqual([], self.caught_entries)
2232
2233
    def test_add_versions_parents_not_parents_index(self):
2234
        index = self.two_graph_index(catch_adds=True)
2235
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2236
            [('new', 'no-eol,fulltext', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2237
        self.assertEqual([], self.caught_entries)
2238
2239
    def test_add_versions_same_dup(self):
2240
        index = self.two_graph_index(catch_adds=True)
2241
        # options can be spelt two different ways
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2242
        index.add_versions([('tip', 'fulltext,no-eol', (None, 0, 100), [])])
2243
        index.add_versions([('tip', 'no-eol,fulltext', (None, 0, 100), [])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2244
        # but neither should have added data.
2245
        self.assertEqual([[], []], self.caught_entries)
2246
        
2247
    def test_add_versions_different_dup(self):
2248
        index = self.two_graph_index(catch_adds=True)
2249
        # change options
2250
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2251
            [('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2252
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2253
            [('tip', 'line-delta,no-eol', (None, 0, 100), [])])
2254
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2255
            [('tip', 'fulltext', (None, 0, 100), [])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2256
        # position/length
2257
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2258
            [('tip', 'fulltext,no-eol', (None, 50, 100), [])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2259
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2260
            [('tip', 'fulltext,no-eol', (None, 0, 1000), [])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2261
        # parents
2262
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2263
            [('tip', 'fulltext,no-eol', (None, 0, 100), ['parent'])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2264
        # change options in the second record
2265
        self.assertRaises(errors.KnitCorrupt, index.add_versions,
2670.2.2 by Robert Collins
* In ``bzrlib.knit`` the internal interface has been altered to use
2266
            [('tip', 'fulltext,no-eol', (None, 0, 100), []),
2267
             ('tip', 'no-eol,line-delta', (None, 0, 100), [])])
2625.8.1 by Robert Collins
LIBRARY API BREAKS:
2268
        self.assertEqual([], self.caught_entries)
2269
2270
    def test_iter_parents(self):
2271
        index = self.two_graph_index()
2272
        self.assertEqual(set([
2273
            ('tip', ()), ('tail', ()), ('parent', ()), ('separate', ())
2274
            ]),
2275
            set(index.iter_parents(['tip', 'tail', 'ghost', 'parent', 'separate'])))
2276
        self.assertEqual(set([('tip', ())]),
2277
            set(index.iter_parents(['tip'])))
2278
        self.assertEqual(set(),
2279
            set(index.iter_parents([])))