/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
1
# Copyright (C) 2005, 2006 by Canonical Ltd
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
19
20
import difflib
21
22
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
23
from bzrlib.errors import KnitError, RevisionAlreadyPresent, NoSuchFile
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
24
from bzrlib.knit import (
25
    KnitVersionedFile,
26
    KnitPlainFactory,
27
    KnitAnnotateFactory,
28
    WeaveToKnit)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
29
from bzrlib.osutils import split_lines
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
30
from bzrlib.tests import TestCaseWithTransport
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
31
from bzrlib.transport import TransportLogger, get_transport
1563.2.13 by Robert Collins
InterVersionedFile implemented.
32
from bzrlib.transport.memory import MemoryTransport
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
33
from bzrlib.weave import Weave
34
35
36
class KnitTests(TestCaseWithTransport):
37
    """Class containing knit test helper routines."""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
38
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
39
    def make_test_knit(self, annotate=False, delay_create=False):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
40
        if not annotate:
41
            factory = KnitPlainFactory()
42
        else:
43
            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
44
        return KnitVersionedFile('test', get_transport('.'), access_mode='w',
45
                                 factory=factory, create=True,
46
                                 delay_create=delay_create)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
47
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
48
49
class BasicKnitTests(KnitTests):
50
51
    def add_stock_one_and_one_a(self, k):
52
        k.add_lines('text-1', [], split_lines(TEXT_1))
53
        k.add_lines('text-1a', ['text-1'], split_lines(TEXT_1A))
54
55
    def test_knit_constructor(self):
56
        """Construct empty k"""
57
        self.make_test_knit()
58
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
59
    def test_knit_add(self):
60
        """Store one text in knit and retrieve"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
61
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
62
        k.add_lines('text-1', [], split_lines(TEXT_1))
63
        self.assertTrue(k.has_version('text-1'))
64
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
65
66
    def test_knit_reload(self):
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
67
        # 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.
68
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
69
        k.add_lines('text-1', [], split_lines(TEXT_1))
70
        del k
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
71
        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.
72
        self.assertTrue(k2.has_version('text-1'))
73
        self.assertEqualDiff(''.join(k2.get_lines('text-1')), TEXT_1)
74
75
    def test_knit_several(self):
76
        """Store several texts in a knit"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
77
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
78
        k.add_lines('text-1', [], split_lines(TEXT_1))
79
        k.add_lines('text-2', [], split_lines(TEXT_2))
80
        self.assertEqualDiff(''.join(k.get_lines('text-1')), TEXT_1)
81
        self.assertEqualDiff(''.join(k.get_lines('text-2')), TEXT_2)
82
        
83
    def test_repeated_add(self):
84
        """Knit traps attempt to replace existing version"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
85
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
86
        k.add_lines('text-1', [], split_lines(TEXT_1))
87
        self.assertRaises(RevisionAlreadyPresent, 
88
                k.add_lines,
89
                'text-1', [], split_lines(TEXT_1))
90
91
    def test_empty(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
92
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
93
        k.add_lines('text-1', [], [])
94
        self.assertEquals(k.get_lines('text-1'), [])
95
96
    def test_incomplete(self):
97
        """Test if texts without a ending line-end can be inserted and
98
        extracted."""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
99
        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.
100
        k.add_lines('text-1', [], ['a\n',    'b'  ])
101
        k.add_lines('text-2', ['text-1'], ['a\rb\n', 'b\n'])
1666.1.6 by Robert Collins
Make knit the default format.
102
        # reopening ensures maximum room for confusion
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
103
        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.
104
        self.assertEquals(k.get_lines('text-1'), ['a\n',    'b'  ])
105
        self.assertEquals(k.get_lines('text-2'), ['a\rb\n', 'b\n'])
106
107
    def test_delta(self):
108
        """Expression of knit delta as lines"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
109
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
110
        td = list(line_delta(TEXT_1.splitlines(True),
111
                             TEXT_1A.splitlines(True)))
112
        self.assertEqualDiff(''.join(td), delta_1_1a)
113
        out = apply_line_delta(TEXT_1.splitlines(True), td)
114
        self.assertEqualDiff(''.join(out), TEXT_1A)
115
116
    def test_add_with_parents(self):
117
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
118
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
119
        self.add_stock_one_and_one_a(k)
120
        self.assertEquals(k.get_parents('text-1'), [])
121
        self.assertEquals(k.get_parents('text-1a'), ['text-1'])
122
123
    def test_ancestry(self):
124
        """Store in knit with parents"""
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
125
        k = self.make_test_knit()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
126
        self.add_stock_one_and_one_a(k)
127
        self.assertEquals(set(k.get_ancestry(['text-1a'])), set(['text-1a', 'text-1']))
128
129
    def test_add_delta(self):
130
        """Store in knit with parents"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
131
        k = KnitVersionedFile('test', get_transport('.'), factory=KnitPlainFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
132
            delta=True, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
133
        self.add_stock_one_and_one_a(k)
1596.2.7 by Robert Collins
Remove the requirement for reannotation in knit joins.
134
        k.clear_cache()
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
135
        self.assertEqualDiff(''.join(k.get_lines('text-1a')), TEXT_1A)
136
137
    def test_annotate(self):
138
        """Annotations"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
139
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
140
            delta=True, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
141
        self.insert_and_test_small_annotate(k)
142
143
    def insert_and_test_small_annotate(self, k):
144
        """test annotation with k works correctly."""
145
        k.add_lines('text-1', [], ['a\n', 'b\n'])
146
        k.add_lines('text-2', ['text-1'], ['a\n', 'c\n'])
147
148
        origins = k.annotate('text-2')
149
        self.assertEquals(origins[0], ('text-1', 'a\n'))
150
        self.assertEquals(origins[1], ('text-2', 'c\n'))
151
152
    def test_annotate_fulltext(self):
153
        """Annotations"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
154
        k = KnitVersionedFile('knit', get_transport('.'), factory=KnitAnnotateFactory(),
1563.2.25 by Robert Collins
Merge in upstream.
155
            delta=False, create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
156
        self.insert_and_test_small_annotate(k)
157
158
    def test_annotate_merge_1(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
159
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
160
        k.add_lines('text-a1', [], ['a\n', 'b\n'])
161
        k.add_lines('text-a2', [], ['d\n', 'c\n'])
162
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['d\n', 'b\n'])
163
        origins = k.annotate('text-am')
164
        self.assertEquals(origins[0], ('text-a2', 'd\n'))
165
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
166
167
    def test_annotate_merge_2(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
168
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
169
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
170
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
171
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['a\n', 'y\n', 'c\n'])
172
        origins = k.annotate('text-am')
173
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
174
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
175
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
176
177
    def test_annotate_merge_9(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
178
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
179
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
180
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
181
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'c\n'])
182
        origins = k.annotate('text-am')
183
        self.assertEquals(origins[0], ('text-am', 'k\n'))
184
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
185
        self.assertEquals(origins[2], ('text-a1', 'c\n'))
186
187
    def test_annotate_merge_3(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
188
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
189
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
190
        k.add_lines('text-a2', [] ,['x\n', 'y\n', 'z\n'])
191
        k.add_lines('text-am', ['text-a1', 'text-a2'], ['k\n', 'y\n', 'z\n'])
192
        origins = k.annotate('text-am')
193
        self.assertEquals(origins[0], ('text-am', 'k\n'))
194
        self.assertEquals(origins[1], ('text-a2', 'y\n'))
195
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
196
197
    def test_annotate_merge_4(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
198
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
199
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
200
        k.add_lines('text-a2', [], ['x\n', 'y\n', 'z\n'])
201
        k.add_lines('text-a3', ['text-a1'], ['a\n', 'b\n', 'p\n'])
202
        k.add_lines('text-am', ['text-a2', 'text-a3'], ['a\n', 'b\n', 'z\n'])
203
        origins = k.annotate('text-am')
204
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
205
        self.assertEquals(origins[1], ('text-a1', 'b\n'))
206
        self.assertEquals(origins[2], ('text-a2', 'z\n'))
207
208
    def test_annotate_merge_5(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
209
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
210
        k.add_lines('text-a1', [], ['a\n', 'b\n', 'c\n'])
211
        k.add_lines('text-a2', [], ['d\n', 'e\n', 'f\n'])
212
        k.add_lines('text-a3', [], ['x\n', 'y\n', 'z\n'])
213
        k.add_lines('text-am',
214
                    ['text-a1', 'text-a2', 'text-a3'],
215
                    ['a\n', 'e\n', 'z\n'])
216
        origins = k.annotate('text-am')
217
        self.assertEquals(origins[0], ('text-a1', 'a\n'))
218
        self.assertEquals(origins[1], ('text-a2', 'e\n'))
219
        self.assertEquals(origins[2], ('text-a3', 'z\n'))
220
221
    def test_annotate_file_cherry_pick(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
222
        k = self.make_test_knit(True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
223
        k.add_lines('text-1', [], ['a\n', 'b\n', 'c\n'])
224
        k.add_lines('text-2', ['text-1'], ['d\n', 'e\n', 'f\n'])
225
        k.add_lines('text-3', ['text-2', 'text-1'], ['a\n', 'b\n', 'c\n'])
226
        origins = k.annotate('text-3')
227
        self.assertEquals(origins[0], ('text-1', 'a\n'))
228
        self.assertEquals(origins[1], ('text-1', 'b\n'))
229
        self.assertEquals(origins[2], ('text-1', 'c\n'))
230
231
    def test_knit_join(self):
232
        """Store in knit with parents"""
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
233
        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.
234
        k1.add_lines('text-a', [], split_lines(TEXT_1))
235
        k1.add_lines('text-b', ['text-a'], split_lines(TEXT_1))
236
237
        k1.add_lines('text-c', [], split_lines(TEXT_1))
238
        k1.add_lines('text-d', ['text-c'], split_lines(TEXT_1))
239
240
        k1.add_lines('text-m', ['text-b', 'text-d'], split_lines(TEXT_1))
241
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
242
        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.
243
        count = k2.join(k1, version_ids=['text-m'])
244
        self.assertEquals(count, 5)
245
        self.assertTrue(k2.has_version('text-a'))
246
        self.assertTrue(k2.has_version('text-c'))
247
248
    def test_reannotate(self):
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
249
        k1 = KnitVersionedFile('knit1', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
250
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
251
        # 0
252
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
253
        # 1
254
        k1.add_lines('text-b', ['text-a'], ['a\n', 'c\n'])
255
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
256
        k2 = KnitVersionedFile('test2', get_transport('.'),
1563.2.25 by Robert Collins
Merge in upstream.
257
                               factory=KnitAnnotateFactory(), create=True)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
258
        k2.join(k1, version_ids=['text-b'])
259
260
        # 2
261
        k1.add_lines('text-X', ['text-b'], ['a\n', 'b\n'])
262
        # 2
263
        k2.add_lines('text-c', ['text-b'], ['z\n', 'c\n'])
264
        # 3
265
        k2.add_lines('text-Y', ['text-b'], ['b\n', 'c\n'])
266
267
        # test-c will have index 3
268
        k1.join(k2, version_ids=['text-c'])
269
270
        lines = k1.get_lines('text-c')
271
        self.assertEquals(lines, ['z\n', 'c\n'])
272
273
        origins = k1.annotate('text-c')
1594.2.24 by Robert Collins
Make use of the transaction finalisation warning support to implement in-knit caching.
274
        self.assertEquals(origins[0], ('text-c', 'z\n'))
275
        self.assertEquals(origins[1], ('text-b', 'c\n'))
276
1756.3.4 by Aaron Bentley
Fix bug getting texts when line deltas were reused
277
    def test_get_line_delta_texts(self):
278
        """Make sure we can call get_texts on text with reused line deltas"""
279
        k1 = KnitVersionedFile('test1', get_transport('.'), 
280
                               factory=KnitPlainFactory(), create=True)
281
        for t in range(3):
282
            if t == 0:
283
                parents = []
284
            else:
285
                parents = ['%d' % (t-1)]
286
            k1.add_lines('%d' % t, parents, ['hello\n'] * t)
287
        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.
288
        
289
    def test_iter_lines_reads_in_order(self):
290
        t = MemoryTransport()
291
        instrumented_t = TransportLogger(t)
292
        k1 = KnitVersionedFile('id', instrumented_t, create=True, delta=True)
293
        self.assertEqual([('id.kndx',)], instrumented_t._calls)
294
        # add texts with no required ordering
295
        k1.add_lines('base', [], ['text\n'])
296
        k1.add_lines('base2', [], ['text2\n'])
297
        k1.clear_cache()
298
        instrumented_t._calls = []
299
        # request a last-first iteration
300
        results = list(k1.iter_lines_added_or_present_in_versions(['base2', 'base']))
1628.1.2 by Robert Collins
More knit micro-optimisations.
301
        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.
302
        self.assertEqual(['text\n', 'text2\n'], results)
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
303
1563.2.13 by Robert Collins
InterVersionedFile implemented.
304
    def test_create_empty_annotated(self):
1563.2.16 by Robert Collins
Change WeaveStore into VersionedFileStore and make its versoined file class parameterisable.
305
        k1 = self.make_test_knit(True)
1563.2.13 by Robert Collins
InterVersionedFile implemented.
306
        # 0
307
        k1.add_lines('text-a', [], ['a\n', 'b\n'])
308
        k2 = k1.create_empty('t', MemoryTransport())
309
        self.assertTrue(isinstance(k2.factory, KnitAnnotateFactory))
310
        self.assertEqual(k1.delta, k2.delta)
311
        # the generic test checks for empty content and file class
312
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
313
    def test_knit_format(self):
314
        # this tests that a new knit index file has the expected content
315
        # and that is writes the data we expect as records are added.
316
        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
317
        # Now knit files are not created until we first add data to them
1666.1.6 by Robert Collins
Make knit the default format.
318
        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.
319
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
320
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
321
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
322
            "\n"
323
            "revid fulltext 0 84 .a_ghost :",
324
            'test.kndx')
325
        knit.add_lines_with_ghosts('revid2', ['revid'], ['a\n'])
326
        self.assertFileEqual(
1666.1.6 by Robert Collins
Make knit the default format.
327
            "# bzr knit index 8\n"
1641.1.2 by Robert Collins
Change knit index files to be robust in the presence of partial writes.
328
            "\nrevid fulltext 0 84 .a_ghost :"
329
            "\nrevid2 line-delta 84 82 0 :",
330
            'test.kndx')
331
        # 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.
332
        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.
333
        self.assertEqual(['revid', 'revid2'], knit.versions())
334
        # write a short write to the file and ensure that its ignored
335
        indexfile = file('test.kndx', 'at')
336
        indexfile.write('\nrevid3 line-delta 166 82 1 2 3 4 5 .phwoar:demo ')
337
        indexfile.close()
338
        # 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.
339
        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.
340
        self.assertEqual(['revid', 'revid2'], knit.versions())
341
        # and add a revision with the same id the failed write had
342
        knit.add_lines('revid3', ['revid2'], ['a\n'])
343
        # and when reading it revid3 should now appear.
1685.1.39 by John Arbash Meinel
Updating test_knit to not instantiate a LocalTransport directly.
344
        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.
345
        self.assertEqual(['revid', 'revid2', 'revid3'], knit.versions())
346
        self.assertEqual(['revid2'], knit.get_parents('revid3'))
347
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
348
    def test_delay_create(self):
349
        """Test that passing delay_create=True creates files late"""
350
        knit = self.make_test_knit(annotate=True, delay_create=True)
351
        self.failIfExists('test.knit')
352
        self.failIfExists('test.kndx')
353
        knit.add_lines_with_ghosts('revid', ['a_ghost'], ['a\n'])
354
        self.failUnlessExists('test.knit')
355
        self.assertFileEqual(
356
            "# bzr knit index 8\n"
357
            "\n"
358
            "revid fulltext 0 84 .a_ghost :",
359
            'test.kndx')
360
1946.2.2 by John Arbash Meinel
test delay_create does the right thing
361
    def test_create_parent_dir(self):
362
        """create_parent_dir can create knits in nonexistant dirs"""
363
        # Has no effect if we don't set 'delay_create'
364
        trans = get_transport('.')
365
        self.assertRaises(NoSuchFile, KnitVersionedFile, 'dir/test',
366
                          trans, access_mode='w', factory=None,
367
                          create=True, create_parent_dir=True)
368
        # Nothing should have changed yet
369
        knit = KnitVersionedFile('dir/test', trans, access_mode='w',
370
                                 factory=None, create=True,
371
                                 create_parent_dir=True,
372
                                 delay_create=True)
373
        self.failIfExists('dir/test.knit')
374
        self.failIfExists('dir/test.kndx')
375
        self.failIfExists('dir')
376
        knit.add_lines('revid', [], ['a\n'])
377
        self.failUnlessExists('dir')
378
        self.failUnlessExists('dir/test.knit')
379
        self.assertFileEqual(
380
            "# bzr knit index 8\n"
381
            "\n"
382
            "revid fulltext 0 84  :",
383
            'dir/test.kndx')
384
1664.2.1 by Aaron Bentley
Start work on plan_merge test
385
    def test_plan_merge(self):
386
        my_knit = self.make_test_knit(annotate=True)
387
        my_knit.add_lines('text1', [], split_lines(TEXT_1))
388
        my_knit.add_lines('text1a', ['text1'], split_lines(TEXT_1A))
389
        my_knit.add_lines('text1b', ['text1'], split_lines(TEXT_1B))
1664.2.3 by Aaron Bentley
Add failing test case
390
        plan = list(my_knit.plan_merge('text1a', 'text1b'))
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
391
        for plan_line, expected_line in zip(plan, AB_MERGE):
392
            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.
393
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
394
395
TEXT_1 = """\
396
Banana cup cakes:
397
398
- bananas
399
- eggs
400
- broken tea cups
401
"""
402
403
TEXT_1A = """\
404
Banana cup cake recipe
405
(serves 6)
406
407
- bananas
408
- eggs
409
- broken tea cups
410
- self-raising flour
411
"""
412
1664.2.1 by Aaron Bentley
Start work on plan_merge test
413
TEXT_1B = """\
414
Banana cup cake recipe
415
416
- bananas (do not use plantains!!!)
417
- broken tea cups
418
- flour
419
"""
420
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
421
delta_1_1a = """\
422
0,1,2
423
Banana cup cake recipe
424
(serves 6)
425
5,5,1
426
- self-raising flour
427
"""
428
429
TEXT_2 = """\
430
Boeuf bourguignon
431
432
- beef
433
- red wine
434
- small onions
435
- carrot
436
- mushrooms
437
"""
438
1664.2.3 by Aaron Bentley
Add failing test case
439
AB_MERGE_TEXT="""unchanged|Banana cup cake recipe
440
new-a|(serves 6)
441
unchanged|
442
killed-b|- bananas
443
killed-b|- eggs
444
new-b|- bananas (do not use plantains!!!)
445
unchanged|- broken tea cups
446
new-a|- self-raising flour
1664.2.6 by Aaron Bentley
Got plan-merge passing tests
447
new-b|- flour
448
"""
1664.2.3 by Aaron Bentley
Add failing test case
449
AB_MERGE=[tuple(l.split('|')) for l in AB_MERGE_TEXT.splitlines(True)]
450
451
1563.2.4 by Robert Collins
First cut at including the knit implementation of versioned_file.
452
def line_delta(from_lines, to_lines):
453
    """Generate line-based delta from one text to another"""
454
    s = difflib.SequenceMatcher(None, from_lines, to_lines)
455
    for op in s.get_opcodes():
456
        if op[0] == 'equal':
457
            continue
458
        yield '%d,%d,%d\n' % (op[1], op[2], op[4]-op[3])
459
        for i in range(op[3], op[4]):
460
            yield to_lines[i]
461
462
463
def apply_line_delta(basis_lines, delta_lines):
464
    """Apply a line-based perfect diff
465
    
466
    basis_lines -- text to apply the patch to
467
    delta_lines -- diff instructions and content
468
    """
469
    out = basis_lines[:]
470
    i = 0
471
    offset = 0
472
    while i < len(delta_lines):
473
        l = delta_lines[i]
474
        a, b, c = map(long, l.split(','))
475
        i = i + 1
476
        out[offset+a:offset+b] = delta_lines[i:i+c]
477
        i = i + c
478
        offset = offset + (b - a) + c
479
    return out
1684.3.3 by Robert Collins
Add a special cased weaves to knit converter.
480
481
482
class TestWeaveToKnit(KnitTests):
483
484
    def test_weave_to_knit_matches(self):
485
        # check that the WeaveToKnit is_compatible function
486
        # registers True for a Weave to a Knit.
487
        w = Weave()
488
        k = self.make_test_knit()
489
        self.failUnless(WeaveToKnit.is_compatible(w, k))
490
        self.failIf(WeaveToKnit.is_compatible(k, w))
491
        self.failIf(WeaveToKnit.is_compatible(w, w))
492
        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
493
494
495
class TestKnitCaching(KnitTests):
496
    
497
    def create_knit(self, cache_add=False):
498
        k = self.make_test_knit(True)
499
        if cache_add:
500
            k.enable_cache()
501
502
        k.add_lines('text-1', [], split_lines(TEXT_1))
503
        k.add_lines('text-2', [], split_lines(TEXT_2))
504
        return k
505
506
    def test_no_caching(self):
507
        k = self.create_knit()
508
        # Nothing should be cached without setting 'enable_cache'
509
        self.assertEqual({}, k._data._cache)
510
511
    def test_cache_add_and_clear(self):
512
        k = self.create_knit(True)
513
514
        self.assertEqual(['text-1', 'text-2'], sorted(k._data._cache.keys()))
515
516
        k.clear_cache()
517
        self.assertEqual({}, k._data._cache)
518
519
    def test_cache_data_read_raw(self):
520
        k = self.create_knit()
521
522
        # Now cache and read
523
        k.enable_cache()
524
525
        def read_one_raw(version):
526
            pos_map = k._get_components_positions([version])
527
            method, pos, size, next = pos_map[version]
528
            lst = list(k._data.read_records_iter_raw([(version, pos, size)]))
529
            self.assertEqual(1, len(lst))
530
            return lst[0]
531
532
        val = read_one_raw('text-1')
1863.1.8 by John Arbash Meinel
Removing disk-backed-cache
533
        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
534
535
        k.clear_cache()
536
        # After clear, new reads are not cached
537
        self.assertEqual({}, k._data._cache)
538
539
        val2 = read_one_raw('text-1')
540
        self.assertEqual(val, val2)
541
        self.assertEqual({}, k._data._cache)
542
543
    def test_cache_data_read(self):
544
        k = self.create_knit()
545
546
        def read_one(version):
547
            pos_map = k._get_components_positions([version])
548
            method, pos, size, next = pos_map[version]
549
            lst = list(k._data.read_records_iter([(version, pos, size)]))
550
            self.assertEqual(1, len(lst))
551
            return lst[0]
552
553
        # Now cache and read
554
        k.enable_cache()
555
556
        val = read_one('text-2')
557
        self.assertEqual(['text-2'], k._data._cache.keys())
558
        self.assertEqual('text-2', val[0])
559
        content, digest = k._data._parse_record('text-2',
560
                                                k._data._cache['text-2'])
561
        self.assertEqual(content, val[1])
562
        self.assertEqual(digest, val[2])
563
564
        k.clear_cache()
565
        self.assertEqual({}, k._data._cache)
566
567
        val2 = read_one('text-2')
568
        self.assertEqual(val, val2)
569
        self.assertEqual({}, k._data._cache)
570
571
    def test_cache_read(self):
572
        k = self.create_knit()
573
        k.enable_cache()
574
575
        text = k.get_text('text-1')
576
        self.assertEqual(TEXT_1, text)
577
        self.assertEqual(['text-1'], k._data._cache.keys())
578
579
        k.clear_cache()
580
        self.assertEqual({}, k._data._cache)
581
582
        text = k.get_text('text-1')
583
        self.assertEqual(TEXT_1, text)
584
        self.assertEqual({}, k._data._cache)