1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
# test_server.py -- Compatibility tests for git server.
# Copyright (C) 2010 Google, Inc.
#
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
# Dulwich is dual-licensed under the Apache License, Version 2.0 and the GNU
# General Public License as published 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.
#
"""Compatibility tests between Dulwich and the cgit server.
Warning: these tests should be fairly stable, but when writing/debugging new
tests, deadlocks may freeze the test process such that it cannot be
Ctrl-C'ed. On POSIX systems, you can kill the tests with Ctrl-Z, "kill %".
"""
import os
import shutil
import sys
import tempfile
import threading
from dulwich.object_format import SHA256
from dulwich.objects import Blob, Commit, Tree
from dulwich.repo import Repo
from dulwich.server import DictBackend, TCPGitServer
from .. import skipIf
from .server_utils import NoSideBand64kReceivePackHandler, ServerTests
from .utils import CompatTestCase, require_git_version, run_git_or_fail
@skipIf(sys.platform == "win32", "Broken on windows, with very long fail time.")
class GitServerTestCase(ServerTests, CompatTestCase):
"""Tests for client/server compatibility.
This server test case does not use side-band-64k in git-receive-pack.
"""
protocol = "git"
def _handlers(self):
return {b"git-receive-pack": NoSideBand64kReceivePackHandler}
def _check_server(self, dul_server, repo) -> None:
from dulwich.protocol import Protocol
receive_pack_handler_cls = dul_server.handlers[b"git-receive-pack"]
# Create a handler instance to check capabilities
proto = Protocol(lambda x: b"", lambda x: None)
try:
handler = receive_pack_handler_cls(
dul_server.backend,
[b"/"],
proto,
)
caps = handler.capabilities()
self.assertNotIn(b"side-band-64k", caps)
finally:
proto.close()
def _start_server(self, repo):
backend = DictBackend({b"/": repo})
dul_server = TCPGitServer(backend, b"localhost", 0, handlers=self._handlers())
self._check_server(dul_server, repo)
# Start server in a thread
server_thread = threading.Thread(target=dul_server.serve)
server_thread.daemon = True # Make thread daemon so it dies with main thread
server_thread.start()
# Add cleanup in the correct order - shutdown first, then close
def cleanup_server():
dul_server.shutdown()
# Give thread a moment to exit cleanly before closing socket
server_thread.join(timeout=1.0)
dul_server.server_close()
self.addCleanup(cleanup_server)
self._server = dul_server
_, port = self._server.socket.getsockname()
return port
@skipIf(sys.platform == "win32", "Broken on windows, with very long fail time.")
class GitServerSideBand64kTestCase(GitServerTestCase):
"""Tests for client/server compatibility with side-band-64k support."""
# side-band-64k in git-receive-pack was introduced in git 1.7.0.2
min_git_version = (1, 7, 0, 2)
def setUp(self) -> None:
super().setUp()
# side-band-64k is broken in the windows client.
# https://github.com/msysgit/git/issues/101
# Fix has landed for the 1.9.3 release.
if os.name == "nt":
require_git_version((1, 9, 3))
def _handlers(self) -> None:
return None # default handlers include side-band-64k
def _check_server(self, server, repo) -> None:
from dulwich.protocol import Protocol
receive_pack_handler_cls = server.handlers[b"git-receive-pack"]
# Create a handler instance to check capabilities
proto = Protocol(lambda x: b"", lambda x: None)
try:
handler = receive_pack_handler_cls(
server.backend,
[b"/"],
proto,
)
caps = handler.capabilities()
self.assertIn(b"side-band-64k", caps)
finally:
proto.close()
@skipIf(sys.platform == "win32", "Broken on windows, with very long fail time.")
class GitServerSHA256TestCase(CompatTestCase):
"""Tests for SHA-256 repository server compatibility with git client."""
protocol = "git"
# SHA-256 support was introduced in git 2.29.0
min_git_version = (2, 29, 0)
def setUp(self) -> None:
super().setUp()
require_git_version(self.min_git_version)
def _start_server(self, repo):
backend = DictBackend({b"/": repo})
dul_server = TCPGitServer(backend, b"localhost", 0)
# Start server in a thread
server_thread = threading.Thread(target=dul_server.serve)
server_thread.daemon = True
server_thread.start()
# Add cleanup - shutdown first, then close
def cleanup_server():
dul_server.shutdown()
# Give thread a moment to exit cleanly before closing socket
server_thread.join(timeout=1.0)
dul_server.server_close()
self.addCleanup(cleanup_server)
self._server = dul_server
_, port = self._server.socket.getsockname()
return port
def url(self, port) -> str:
return f"{self.protocol}://localhost:{port}/"
def test_clone_sha256_repo_from_dulwich_server(self) -> None:
"""Test that git client can clone SHA-256 repo from dulwich server."""
# Create SHA-256 repository with dulwich
repo_path = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, repo_path)
source_repo = Repo.init(repo_path, mkdir=False, object_format="sha256")
self.addCleanup(source_repo.close)
# Create test content
blob = Blob.from_string(b"Test SHA-256 content from dulwich server")
tree = Tree()
tree.add(b"test.txt", 0o100644, blob.get_id(SHA256))
commit = Commit()
commit.tree = tree.get_id(SHA256)
commit.author = commit.committer = b"Test User <test@example.com>"
commit.commit_time = commit.author_time = 1234567890
commit.commit_timezone = commit.author_timezone = 0
commit.message = b"Test SHA-256 commit"
# Add objects to repo
source_repo.object_store.add_object(blob)
source_repo.object_store.add_object(tree)
source_repo.object_store.add_object(commit)
# Set master ref
source_repo.refs[b"refs/heads/master"] = commit.get_id(SHA256)
# Start dulwich server
port = self._start_server(source_repo)
# Clone with git client
clone_path = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, clone_path)
clone_dir = os.path.join(clone_path, "cloned_repo")
run_git_or_fail(["clone", self.url(port), clone_dir], cwd=clone_path)
# Verify cloned repo is SHA-256
cloned_repo = Repo(clone_dir)
self.addCleanup(cloned_repo.close)
self.assertEqual(cloned_repo.object_format, SHA256)
# Verify object format config
output = run_git_or_fail(
["config", "--get", "extensions.objectformat"], cwd=clone_dir
)
self.assertEqual(output.strip(), b"sha256")
# Verify commit was cloned
cloned_head = cloned_repo.refs[b"refs/heads/master"]
self.assertEqual(len(cloned_head), 64) # SHA-256 length
self.assertEqual(cloned_head, commit.get_id(SHA256))
# Verify git can read the commit
log_output = run_git_or_fail(["log", "--format=%s", "-n", "1"], cwd=clone_dir)
self.assertEqual(log_output.strip(), b"Test SHA-256 commit")
|