# test_objects.py -- tests for objects.py
# Copyright (C) 2007 James Westby <jw+debian@jameswestby.net>
#
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
# General Public License as public by the Free Software Foundation; version 2.0
# or (at your option) any later version. You can redistribute it and/or
# modify it under the terms of either of these two licenses.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# You should have received a copy of the licenses; if not, see
# <http://www.gnu.org/licenses/> for a copy of the GNU General Public License
# and <http://www.apache.org/licenses/LICENSE-2.0> for a copy of the Apache
# License, Version 2.0.
#
"""Tests for git base objects."""
# TODO: Round-trip parse-serialize-parse and serialize-parse-serialize tests.
from io import BytesIO
import datetime
from itertools import (
permutations,
)
import os
import stat
import warnings
from contextlib import contextmanager
from dulwich.errors import (
ObjectFormatException,
)
from dulwich.objects import (
Blob,
Tree,
Commit,
ShaFile,
Tag,
TreeEntry,
format_timezone,
hex_to_sha,
sha_to_hex,
hex_to_filename,
check_hexsha,
check_identity,
object_class,
parse_timezone,
pretty_format_tree_entry,
parse_tree,
_parse_tree_py,
sorted_tree_items,
_sorted_tree_items_py,
MAX_TIME
)
from dulwich.tests import (
TestCase,
)
from dulwich.tests.utils import (
make_commit,
make_object,
functest_builder,
ext_functest_builder,
)
a_sha = b'6f670c0fb53f9463760b7295fbb814e965fb20c8'
b_sha = b'2969be3e8ee1c0222396a5611407e4769f14e54b'
c_sha = b'954a536f7819d40e6f637f849ee187dd10066349'
tree_sha = b'70c190eb48fa8bbb50ddc692a17b44cb781af7f6'
tag_sha = b'71033db03a03c6a36721efcf1968dd8f8e0cf023'
class TestHexToSha(TestCase):
def test_simple(self):
self.assertEqual(b'\xab\xcd' * 10, hex_to_sha(b'abcd' * 10))
def test_reverse(self):
self.assertEqual(b'abcd' * 10, sha_to_hex(b'\xab\xcd' * 10))
class BlobReadTests(TestCase):
"""Test decompression of blobs"""
def get_sha_file(self, cls, base, sha):
dir = os.path.join(os.path.dirname(__file__), 'data', base)
return cls.from_path(hex_to_filename(dir, sha))
def get_blob(self, sha):
"""Return the blob named sha from the test data dir"""
return self.get_sha_file(Blob, 'blobs', sha)
def get_tree(self, sha):
return self.get_sha_file(Tree, 'trees', sha)
def get_tag(self, sha):
return self.get_sha_file(Tag, 'tags', sha)
def commit(self, sha):
return self.get_sha_file(Commit, 'commits', sha)
def test_decompress_simple_blob(self):
b = self.get_blob(a_sha)
self.assertEqual(b.data, b'test 1\n')
self.assertEqual(b.sha().hexdigest().encode('ascii'), a_sha)
def test_hash(self):
b = self.get_blob(a_sha)
self.assertEqual(hash(b.id), hash(b))
def test_parse_empty_blob_object(self):
sha = b'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
b = self.get_blob(sha)
self.assertEqual(b.data, b'')
self.assertEqual(b.id, sha)
self.assertEqual(b.sha().hexdigest().encode('ascii'), sha)
def test_create_blob_from_string(self):
string = b'test 2\n'
b = Blob.from_string(string)
self.assertEqual(b.data, string)
self.assertEqual(b.sha().hexdigest().encode('ascii'), b_sha)
def test_legacy_from_file(self):
b1 = Blob.from_string(b'foo')
b_raw = b1.as_legacy_object()
b2 = b1.from_file(BytesIO(b_raw))
self.assertEqual(b1, b2)
def test_chunks(self):
string = b'test 5\n'
b = Blob.from_string(string)
self.assertEqual([string], b.chunked)
def test_splitlines(self):
for case in [
[],
[b'foo\nbar\n'],
[b'bl\na', b'blie'],
[b'bl\na', b'blie', b'bloe\n'],
[b'', b'bl\na', b'blie', b'bloe\n'],
[b'', b'', b'', b'bla\n'],
[b'', b'', b'', b'bla\n', b''],
[b'bl', b'', b'a\naaa'],
[b'a\naaa', b'a'],
]:
b = Blob()
b.chunked = case
self.assertEqual(b.data.splitlines(True), b.splitlines())
def test_set_chunks(self):
b = Blob()
b.chunked = [b'te', b'st', b' 5\n']
self.assertEqual(b'test 5\n', b.data)
b.chunked = [b'te', b'st', b' 6\n']
self.assertEqual(b'test 6\n', b.as_raw_string())
self.assertEqual(b'test 6\n', bytes(b))
def test_parse_legacy_blob(self):
string = b'test 3\n'
b = self.get_blob(c_sha)
self.assertEqual(b.data, string)
self.assertEqual(b.sha().hexdigest().encode('ascii'), c_sha)
def test_eq(self):
blob1 = self.get_blob(a_sha)
blob2 = self.get_blob(a_sha)
self.assertEqual(blob1, blob2)
def test_read_tree_from_file(self):
t = self.get_tree(tree_sha)
self.assertEqual(t.items()[0], (b'a', 33188, a_sha))
self.assertEqual(t.items()[1], (b'b', 33188, b_sha))
def test_read_tree_from_file_parse_count(self):
old_deserialize = Tree._deserialize
def reset_deserialize():
Tree._deserialize = old_deserialize
self.addCleanup(reset_deserialize)
self.deserialize_count = 0
def counting_deserialize(*args, **kwargs):
self.deserialize_count += 1
return old_deserialize(*args, **kwargs)
Tree._deserialize = counting_deserialize
t = self.get_tree(tree_sha)
self.assertEqual(t.items()[0], (b'a', 33188, a_sha))
self.assertEqual(t.items()[1], (b'b', 33188, b_sha))
self.assertEqual(self.deserialize_count, 1)
def test_read_tag_from_file(self):
t = self.get_tag(tag_sha)
self.assertEqual(t.object,
(Commit, b'51b668fd5bf7061b7d6fa525f88803e6cfadaa51'))
self.assertEqual(t.name, b'signed')
self.assertEqual(t.tagger, b'Ali Sabil <ali.sabil@gmail.com>')
self.assertEqual(t.tag_time, 1231203091)
self.assertEqual(
t.message,
b'This is a signed tag\n'
)
self.assertEqual(
t.signature,
b'-----BEGIN PGP SIGNATURE-----\n'
b'Version: GnuPG v1.4.9 (GNU/Linux)\n'
b'\n'
b'iEYEABECAAYFAkliqx8ACgkQqSMmLy9u/'
b'kcx5ACfakZ9NnPl02tOyYP6pkBoEkU1\n'
b'5EcAn0UFgokaSvS371Ym/4W9iJj6vh3h\n'
b'=ql7y\n'
b'-----END PGP SIGNATURE-----\n')
def test_read_commit_from_file(self):
sha = b'60dacdc733de308bb77bb76ce0fb0f9b44c9769e'
c = self.commit(sha)
self.assertEqual(c.tree, tree_sha)
self.assertEqual(c.parents,
[b'0d89f20333fbb1d2f3a94da77f4981373d8f4310'])
self.assertEqual(c.author,
b'James Westby <jw+debian@jameswestby.net>')
self.assertEqual(c.committer,
b'James Westby <jw+debian@jameswestby.net>')
self.assertEqual(c.commit_time, 1174759230)
self.assertEqual(c.commit_timezone, 0)
self.assertEqual(c.author_timezone, 0)
self.assertEqual(c.message, b'Test commit\n')
def test_read_commit_no_parents(self):
sha = b'0d89f20333fbb1d2f3a94da77f4981373d8f4310'
c = self.commit(sha)
self.assertEqual(c.tree, b'90182552c4a85a45ec2a835cadc3451bebdfe870')
self.assertEqual(c.parents, [])
self.assertEqual(c.author,
b'James Westby <jw+debian@jameswestby.net>')
self.assertEqual(c.committer,
b'James Westby <jw+debian@jameswestby.net>')
self.assertEqual(c.commit_time, 1174758034)
self.assertEqual(c.commit_timezone, 0)
self.assertEqual(c.author_timezone, 0)
self.assertEqual(c.message, b'Test commit\n')
def test_read_commit_two_parents(self):
sha = b'5dac377bdded4c9aeb8dff595f0faeebcc8498cc'
c = self.commit(sha)
self.assertEqual(c.tree, b'd80c186a03f423a81b39df39dc87fd269736ca86')
self.assertEqual(c.parents,
[b'ab64bbdcc51b170d21588e5c5d391ee5c0c96dfd',
b'4cffe90e0a41ad3f5190079d7c8f036bde29cbe6'])
self.assertEqual(c.author,
b'James Westby <jw+debian@jameswestby.net>')
self.assertEqual(c.committer,
b'James Westby <jw+debian@jameswestby.net>')
self.assertEqual(c.commit_time, 1174773719)
self.assertEqual(c.commit_timezone, 0)
self.assertEqual(c.author_timezone, 0)
self.assertEqual(c.message, b'Merge ../b\n')
def test_stub_sha(self):
sha = b'5' * 40
c = make_commit(id=sha, message=b'foo')
self.assertTrue(isinstance(c, Commit))
self.assertEqual(sha, c.id)
self.assertNotEqual(sha, c.sha())
class ShaFileCheckTests(TestCase):
def assertCheckFails(self, cls, data):
obj = cls()
def do_check():
obj.set_raw_string(data)
obj.check()
self.assertRaises(ObjectFormatException, do_check)
def assertCheckSucceeds(self, cls, data):
obj = cls()
obj.set_raw_string(data)
self.assertEqual(None, obj.check())
small_buffer_zlib_object = (
b'\x48\x89\x15\xcc\x31\x0e\xc2\x30\x0c\x40\x51\xe6'
b'\x9c\xc2\x3b\xaa\x64\x37\xc4\xc1\x12\x42\x5c\xc5'
b'\x49\xac\x52\xd4\x92\xaa\x78\xe1\xf6\x94\xed\xeb'
b'\x0d\xdf\x75\x02\xa2\x7c\xea\xe5\x65\xd5\x81\x8b'
b'\x9a\x61\xba\xa0\xa9\x08\x36\xc9\x4c\x1a\xad\x88'
b'\x16\xba\x46\xc4\xa8\x99\x6a\x64\xe1\xe0\xdf\xcd'
b'\xa0\xf6\x75\x9d\x3d\xf8\xf1\xd0\x77\xdb\xfb\xdc'
b'\x86\xa3\x87\xf1\x2f\x93\xed\x00\xb7\xc7\xd2\xab'
b'\x2e\xcf\xfe\xf1\x3b\x50\xa4\x91\x53\x12\x24\x38'
b'\x23\x21\x86\xf0\x03\x2f\x91\x24\x52'
)
class ShaFileTests(TestCase):
def test_deflated_smaller_window_buffer(self):
# zlib on some systems uses smaller buffers,
# resulting in a different header.
# See https://github.com/libgit2/libgit2/pull/464
sf = ShaFile.from_file(BytesIO(small_buffer_zlib_object))
self.assertEqual(sf.type_name, b'tag')
self.assertEqual(sf.tagger, b' <@localhost>')
class CommitSerializationTests(TestCase):
def make_commit(self, **kwargs):
attrs = {'tree': b'd80c186a03f423a81b39df39dc87fd269736ca86',
'parents': [b'ab64bbdcc51b170d21588e5c5d391ee5c0c96dfd',
b'4cffe90e0a41ad3f5190079d7c8f036bde29cbe6'],
'author': b'James Westby <jw+debian@jameswestby.net>',
'committer': b'James Westby <jw+debian@jameswestby.net>',
'commit_time': 1174773719,
'author_time': 1174773719,
'commit_timezone': 0,
'author_timezone': 0,
'message': b'Merge ../b\n'}
attrs.update(kwargs)
return make_commit(**attrs)
def test_encoding(self):
c = self.make_commit(encoding=b'iso8859-1')
self.assertTrue(b'encoding iso8859-1\n' in c.as_raw_string())
def test_short_timestamp(self):
c = self.make_commit(commit_time=30)
c1 = Commit()
c1.set_raw_string(c.as_raw_string())
self.assertEqual(30, c1.commit_time)
def test_full_tree(self):
c = self.make_commit(commit_time=30)
t = Tree()
t.add(b'data-x', 0o644, Blob().id)
c.tree = t
c1 = Commit()
c1.set_raw_string(c.as_raw_string())
Loading ...