Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / dulwich / tests / test_object_store.py @ 959

History | View | Annotate | Download (16.5 KB)

1
# test_object_store.py -- tests for object_store.py
2
# Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
3
#
4
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
5
# General Public License as public by the Free Software Foundation; version 2.0
6
# or (at your option) any later version. You can redistribute it and/or
7
# modify it under the terms of either of these two licenses.
8
#
9
# Unless required by applicable law or agreed to in writing, software
10
# distributed under the License is distributed on an "AS IS" BASIS,
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
# See the License for the specific language governing permissions and
13
# limitations under the License.
14
#
15
# You should have received a copy of the licenses; if not, see
16
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
17
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
18
# License, Version 2.0.
19
#
20

    
21
"""Tests for the object store interface."""
22

    
23

    
24
from contextlib import closing
25
from io import BytesIO
26
import os
27
import shutil
28
import tempfile
29

    
30
from dulwich.index import (
31
    commit_tree,
32
    )
33
from dulwich.errors import (
34
    NotTreeError,
35
    )
36
from dulwich.objects import (
37
    sha_to_hex,
38
    Blob,
39
    Tree,
40
    TreeEntry,
41
    )
42
from dulwich.object_store import (
43
    DiskObjectStore,
44
    MemoryObjectStore,
45
    ObjectStoreGraphWalker,
46
    tree_lookup_path,
47
    )
48
from dulwich.pack import (
49
    REF_DELTA,
50
    write_pack_objects,
51
    )
52
from dulwich.tests import (
53
    TestCase,
54
    )
55
from dulwich.tests.utils import (
56
    make_object,
57
    make_tag,
58
    build_pack,
59
    )
60

    
61

    
62
testobject = make_object(Blob, data=b"yummy data")
63

    
64

    
65
class ObjectStoreTests(object):
66

    
67
    def test_determine_wants_all(self):
68
        self.assertEqual([b"1" * 40],
69
            self.store.determine_wants_all({b"refs/heads/foo": b"1" * 40}))
70

    
71
    def test_determine_wants_all_zero(self):
72
        self.assertEqual([],
73
            self.store.determine_wants_all({b"refs/heads/foo": b"0" * 40}))
74

    
75
    def test_iter(self):
76
        self.assertEqual([], list(self.store))
77

    
78
    def test_get_nonexistant(self):
79
        self.assertRaises(KeyError, lambda: self.store[b"a" * 40])
80

    
81
    def test_contains_nonexistant(self):
82
        self.assertFalse((b"a" * 40) in self.store)
83

    
84
    def test_add_objects_empty(self):
85
        self.store.add_objects([])
86

    
87
    def test_add_commit(self):
88
        # TODO: Argh, no way to construct Git commit objects without
89
        # access to a serialized form.
90
        self.store.add_objects([])
91

    
92
    def test_store_resilience(self):
93
        """Test if updating an existing stored object doesn't erase the
94
        object from the store.
95
        """
96
        test_object = make_object(Blob, data=b'data')
97

    
98
        self.store.add_object(test_object)
99
        test_object_id = test_object.id
100
        test_object.data = test_object.data + b'update'
101
        stored_test_object = self.store[test_object_id]
102

    
103
        self.assertNotEqual(test_object.id, stored_test_object.id)
104
        self.assertEqual(stored_test_object.id, test_object_id)
105

    
106
    def test_add_object(self):
107
        self.store.add_object(testobject)
108
        self.assertEqual(set([testobject.id]), set(self.store))
109
        self.assertTrue(testobject.id in self.store)
110
        r = self.store[testobject.id]
111
        self.assertEqual(r, testobject)
112

    
113
    def test_add_objects(self):
114
        data = [(testobject, "mypath")]
115
        self.store.add_objects(data)
116
        self.assertEqual(set([testobject.id]), set(self.store))
117
        self.assertTrue(testobject.id in self.store)
118
        r = self.store[testobject.id]
119
        self.assertEqual(r, testobject)
120

    
121
    def test_tree_changes(self):
122
        blob_a1 = make_object(Blob, data=b'a1')
123
        blob_a2 = make_object(Blob, data=b'a2')
124
        blob_b = make_object(Blob, data=b'b')
125
        for blob in [blob_a1, blob_a2, blob_b]:
126
            self.store.add_object(blob)
127

    
128
        blobs_1 = [(b'a', blob_a1.id, 0o100644), (b'b', blob_b.id, 0o100644)]
129
        tree1_id = commit_tree(self.store, blobs_1)
130
        blobs_2 = [(b'a', blob_a2.id, 0o100644), (b'b', blob_b.id, 0o100644)]
131
        tree2_id = commit_tree(self.store, blobs_2)
132
        change_a = ((b'a', b'a'), (0o100644, 0o100644), (blob_a1.id, blob_a2.id))
133
        self.assertEqual([change_a],
134
                          list(self.store.tree_changes(tree1_id, tree2_id)))
135
        self.assertEqual(
136
            [change_a, ((b'b', b'b'), (0o100644, 0o100644), (blob_b.id, blob_b.id))],
137
            list(self.store.tree_changes(tree1_id, tree2_id,
138
                                         want_unchanged=True)))
139

    
140
    def test_iter_tree_contents(self):
141
        blob_a = make_object(Blob, data=b'a')
142
        blob_b = make_object(Blob, data=b'b')
143
        blob_c = make_object(Blob, data=b'c')
144
        for blob in [blob_a, blob_b, blob_c]:
145
            self.store.add_object(blob)
146

    
147
        blobs = [
148
            (b'a', blob_a.id, 0o100644),
149
            (b'ad/b', blob_b.id, 0o100644),
150
            (b'ad/bd/c', blob_c.id, 0o100755),
151
            (b'ad/c', blob_c.id, 0o100644),
152
            (b'c', blob_c.id, 0o100644),
153
        ]
154
        tree_id = commit_tree(self.store, blobs)
155
        self.assertEqual([TreeEntry(p, m, h) for (p, h, m) in blobs],
156
                          list(self.store.iter_tree_contents(tree_id)))
157

    
158
    def test_iter_tree_contents_include_trees(self):
159
        blob_a = make_object(Blob, data=b'a')
160
        blob_b = make_object(Blob, data=b'b')
161
        blob_c = make_object(Blob, data=b'c')
162
        for blob in [blob_a, blob_b, blob_c]:
163
            self.store.add_object(blob)
164

    
165
        blobs = [
166
          (b'a', blob_a.id, 0o100644),
167
          (b'ad/b', blob_b.id, 0o100644),
168
          (b'ad/bd/c', blob_c.id, 0o100755),
169
          ]
170
        tree_id = commit_tree(self.store, blobs)
171
        tree = self.store[tree_id]
172
        tree_ad = self.store[tree[b'ad'][1]]
173
        tree_bd = self.store[tree_ad[b'bd'][1]]
174

    
175
        expected = [
176
          TreeEntry(b'', 0o040000, tree_id),
177
          TreeEntry(b'a', 0o100644, blob_a.id),
178
          TreeEntry(b'ad', 0o040000, tree_ad.id),
179
          TreeEntry(b'ad/b', 0o100644, blob_b.id),
180
          TreeEntry(b'ad/bd', 0o040000, tree_bd.id),
181
          TreeEntry(b'ad/bd/c', 0o100755, blob_c.id),
182
          ]
183
        actual = self.store.iter_tree_contents(tree_id, include_trees=True)
184
        self.assertEqual(expected, list(actual))
185

    
186
    def make_tag(self, name, obj):
187
        tag = make_tag(obj, name=name)
188
        self.store.add_object(tag)
189
        return tag
190

    
191
    def test_peel_sha(self):
192
        self.store.add_object(testobject)
193
        tag1 = self.make_tag(b'1', testobject)
194
        tag2 = self.make_tag(b'2', testobject)
195
        tag3 = self.make_tag(b'3', testobject)
196
        for obj in [testobject, tag1, tag2, tag3]:
197
            self.assertEqual(testobject, self.store.peel_sha(obj.id))
198

    
199
    def test_get_raw(self):
200
        self.store.add_object(testobject)
201
        self.assertEqual((Blob.type_num, b'yummy data'),
202
                         self.store.get_raw(testobject.id))
203

    
204
    def test_close(self):
205
        # For now, just check that close doesn't barf.
206
        self.store.add_object(testobject)
207
        self.store.close()
208

    
209

    
210
class MemoryObjectStoreTests(ObjectStoreTests, TestCase):
211

    
212
    def setUp(self):
213
        TestCase.setUp(self)
214
        self.store = MemoryObjectStore()
215

    
216
    def test_add_pack(self):
217
        o = MemoryObjectStore()
218
        f, commit, abort = o.add_pack()
219
        try:
220
            b = make_object(Blob, data=b"more yummy data")
221
            write_pack_objects(f, [(b, None)])
222
        except:
223
            abort()
224
            raise
225
        else:
226
            commit()
227

    
228
    def test_add_pack_emtpy(self):
229
        o = MemoryObjectStore()
230
        f, commit, abort = o.add_pack()
231
        commit()
232

    
233
    def test_add_thin_pack(self):
234
        o = MemoryObjectStore()
235
        blob = make_object(Blob, data=b'yummy data')
236
        o.add_object(blob)
237

    
238
        f = BytesIO()
239
        entries = build_pack(f, [
240
            (REF_DELTA, (blob.id, b'more yummy data')),
241
            ], store=o)
242
        o.add_thin_pack(f.read, None)
243
        packed_blob_sha = sha_to_hex(entries[0][3])
244
        self.assertEqual((Blob.type_num, b'more yummy data'),
245
                         o.get_raw(packed_blob_sha))
246

    
247

    
248
    def test_add_thin_pack_empty(self):
249
        o = MemoryObjectStore()
250

    
251
        f = BytesIO()
252
        entries = build_pack(f, [], store=o)
253
        self.assertEqual([], entries)
254
        o.add_thin_pack(f.read, None)
255

    
256

    
257
class PackBasedObjectStoreTests(ObjectStoreTests):
258

    
259
    def tearDown(self):
260
        for pack in self.store.packs:
261
            pack.close()
262

    
263
    def test_empty_packs(self):
264
        self.assertEqual([], list(self.store.packs))
265

    
266
    def test_pack_loose_objects(self):
267
        b1 = make_object(Blob, data=b"yummy data")
268
        self.store.add_object(b1)
269
        b2 = make_object(Blob, data=b"more yummy data")
270
        self.store.add_object(b2)
271
        self.assertEqual([], list(self.store.packs))
272
        self.assertEqual(2, self.store.pack_loose_objects())
273
        self.assertNotEqual([], list(self.store.packs))
274
        self.assertEqual(0, self.store.pack_loose_objects())
275

    
276

    
277
class DiskObjectStoreTests(PackBasedObjectStoreTests, TestCase):
278

    
279
    def setUp(self):
280
        TestCase.setUp(self)
281
        self.store_dir = tempfile.mkdtemp()
282
        self.addCleanup(shutil.rmtree, self.store_dir)
283
        self.store = DiskObjectStore.init(self.store_dir)
284

    
285
    def tearDown(self):
286
        TestCase.tearDown(self)
287
        PackBasedObjectStoreTests.tearDown(self)
288

    
289
    def test_alternates(self):
290
        alternate_dir = tempfile.mkdtemp()
291
        self.addCleanup(shutil.rmtree, alternate_dir)
292
        alternate_store = DiskObjectStore(alternate_dir)
293
        b2 = make_object(Blob, data=b"yummy data")
294
        alternate_store.add_object(b2)
295
        store = DiskObjectStore(self.store_dir)
296
        self.assertRaises(KeyError, store.__getitem__, b2.id)
297
        store.add_alternate_path(alternate_dir)
298
        self.assertIn(b2.id, store)
299
        self.assertEqual(b2, store[b2.id])
300

    
301
    def test_add_alternate_path(self):
302
        store = DiskObjectStore(self.store_dir)
303
        self.assertEqual([], list(store._read_alternate_paths()))
304
        store.add_alternate_path("/foo/path")
305
        self.assertEqual(["/foo/path"], list(store._read_alternate_paths()))
306
        store.add_alternate_path("/bar/path")
307
        self.assertEqual(
308
            ["/foo/path", "/bar/path"],
309
            list(store._read_alternate_paths()))
310

    
311
    def test_rel_alternative_path(self):
312
        alternate_dir = tempfile.mkdtemp()
313
        self.addCleanup(shutil.rmtree, alternate_dir)
314
        alternate_store = DiskObjectStore(alternate_dir)
315
        b2 = make_object(Blob, data=b"yummy data")
316
        alternate_store.add_object(b2)
317
        store = DiskObjectStore(self.store_dir)
318
        self.assertRaises(KeyError, store.__getitem__, b2.id)
319
        store.add_alternate_path(os.path.relpath(alternate_dir, self.store_dir))
320
        self.assertEqual(list(alternate_store), list(store.alternates[0]))
321
        self.assertIn(b2.id, store)
322
        self.assertEqual(b2, store[b2.id])
323

    
324
    def test_pack_dir(self):
325
        o = DiskObjectStore(self.store_dir)
326
        self.assertEqual(os.path.join(self.store_dir, "pack"), o.pack_dir)
327

    
328
    def test_add_pack(self):
329
        o = DiskObjectStore(self.store_dir)
330
        f, commit, abort = o.add_pack()
331
        try:
332
            b = make_object(Blob, data=b"more yummy data")
333
            write_pack_objects(f, [(b, None)])
334
        except:
335
            abort()
336
            raise
337
        else:
338
            commit()
339

    
340
    def test_add_thin_pack(self):
341
        o = DiskObjectStore(self.store_dir)
342
        try:
343
            blob = make_object(Blob, data=b'yummy data')
344
            o.add_object(blob)
345

    
346
            f = BytesIO()
347
            entries = build_pack(f, [
348
              (REF_DELTA, (blob.id, b'more yummy data')),
349
              ], store=o)
350

    
351
            with o.add_thin_pack(f.read, None) as pack:
352
                packed_blob_sha = sha_to_hex(entries[0][3])
353
                pack.check_length_and_checksum()
354
                self.assertEqual(sorted([blob.id, packed_blob_sha]), list(pack))
355
                self.assertTrue(o.contains_packed(packed_blob_sha))
356
                self.assertTrue(o.contains_packed(blob.id))
357
                self.assertEqual((Blob.type_num, b'more yummy data'),
358
                                 o.get_raw(packed_blob_sha))
359
        finally:
360
            o.close()
361

    
362
    def test_add_thin_pack_empty(self):
363
        with closing(DiskObjectStore(self.store_dir)) as o:
364
            f = BytesIO()
365
            entries = build_pack(f, [], store=o)
366
            self.assertEqual([], entries)
367
            o.add_thin_pack(f.read, None)
368

    
369

    
370
class TreeLookupPathTests(TestCase):
371

    
372
    def setUp(self):
373
        TestCase.setUp(self)
374
        self.store = MemoryObjectStore()
375
        blob_a = make_object(Blob, data=b'a')
376
        blob_b = make_object(Blob, data=b'b')
377
        blob_c = make_object(Blob, data=b'c')
378
        for blob in [blob_a, blob_b, blob_c]:
379
            self.store.add_object(blob)
380

    
381
        blobs = [
382
          (b'a', blob_a.id, 0o100644),
383
          (b'ad/b', blob_b.id, 0o100644),
384
          (b'ad/bd/c', blob_c.id, 0o100755),
385
          (b'ad/c', blob_c.id, 0o100644),
386
          (b'c', blob_c.id, 0o100644),
387
          ]
388
        self.tree_id = commit_tree(self.store, blobs)
389

    
390
    def get_object(self, sha):
391
        return self.store[sha]
392

    
393
    def test_lookup_blob(self):
394
        o_id = tree_lookup_path(self.get_object, self.tree_id, b'a')[1]
395
        self.assertTrue(isinstance(self.store[o_id], Blob))
396

    
397
    def test_lookup_tree(self):
398
        o_id = tree_lookup_path(self.get_object, self.tree_id, b'ad')[1]
399
        self.assertTrue(isinstance(self.store[o_id], Tree))
400
        o_id = tree_lookup_path(self.get_object, self.tree_id, b'ad/bd')[1]
401
        self.assertTrue(isinstance(self.store[o_id], Tree))
402
        o_id = tree_lookup_path(self.get_object, self.tree_id, b'ad/bd/')[1]
403
        self.assertTrue(isinstance(self.store[o_id], Tree))
404

    
405
    def test_lookup_nonexistent(self):
406
        self.assertRaises(KeyError, tree_lookup_path, self.get_object, self.tree_id, b'j')
407

    
408
    def test_lookup_not_tree(self):
409
        self.assertRaises(NotTreeError, tree_lookup_path, self.get_object, self.tree_id, b'ad/b/j')
410

    
411

    
412
class ObjectStoreGraphWalkerTests(TestCase):
413

    
414
    def get_walker(self, heads, parent_map):
415
        new_parent_map = dict([
416
            (k * 40, [(p * 40) for p in ps]) for (k, ps) in parent_map.items()])
417
        return ObjectStoreGraphWalker([x * 40 for x in heads],
418
            new_parent_map.__getitem__)
419

    
420
    def test_ack_invalid_value(self):
421
        gw = self.get_walker([], {})
422
        self.assertRaises(ValueError, gw.ack, "tooshort")
423

    
424
    def test_empty(self):
425
        gw = self.get_walker([], {})
426
        self.assertIs(None, next(gw))
427
        gw.ack(b"a" * 40)
428
        self.assertIs(None, next(gw))
429

    
430
    def test_descends(self):
431
        gw = self.get_walker([b"a"], {b"a": [b"b"], b"b": []})
432
        self.assertEqual(b"a" * 40, next(gw))
433
        self.assertEqual(b"b" * 40, next(gw))
434

    
435
    def test_present(self):
436
        gw = self.get_walker([b"a"], {b"a": [b"b"], b"b": []})
437
        gw.ack(b"a" * 40)
438
        self.assertIs(None, next(gw))
439

    
440
    def test_parent_present(self):
441
        gw = self.get_walker([b"a"], {b"a": [b"b"], b"b": []})
442
        self.assertEqual(b"a" * 40, next(gw))
443
        gw.ack(b"a" * 40)
444
        self.assertIs(None, next(gw))
445

    
446
    def test_child_ack_later(self):
447
        gw = self.get_walker([b"a"], {b"a": [b"b"], b"b": [b"c"], b"c": []})
448
        self.assertEqual(b"a" * 40, next(gw))
449
        self.assertEqual(b"b" * 40, next(gw))
450
        gw.ack(b"a" * 40)
451
        self.assertIs(None, next(gw))
452

    
453
    def test_only_once(self):
454
        # a  b
455
        # |  |
456
        # c  d
457
        # \ /
458
        #  e
459
        gw = self.get_walker([b"a", b"b"], {
460
                b"a": [b"c"],
461
                b"b": [b"d"],
462
                b"c": [b"e"],
463
                b"d": [b"e"],
464
                b"e": [],
465
                })
466
        walk = []
467
        acked = False
468
        walk.append(next(gw))
469
        walk.append(next(gw))
470
        # A branch (a, c) or (b, d) may be done after 2 steps or 3 depending on
471
        # the order walked: 3-step walks include (a, b, c) and (b, a, d), etc.
472
        if walk == [b"a" * 40, b"c" * 40] or walk == [b"b" * 40, b"d" * 40]:
473
          gw.ack(walk[0])
474
          acked = True
475

    
476
        walk.append(next(gw))
477
        if not acked and walk[2] == b"c" * 40:
478
          gw.ack(b"a" * 40)
479
        elif not acked and walk[2] == b"d" * 40:
480
          gw.ack(b"b" * 40)
481
        walk.append(next(gw))
482
        self.assertIs(None, next(gw))
483

    
484
        self.assertEqual([b"a" * 40, b"b" * 40, b"c" * 40, b"d" * 40], sorted(walk))
485
        self.assertLess(walk.index(b"a" * 40), walk.index(b"c" * 40))
486
        self.assertLess(walk.index(b"b" * 40), walk.index(b"d" * 40))