import subprocess
import pytest
from .conftest import GIT2CPP_TEST_WASM, get_filemode_lines, set_filemode
xsimd_url = "https://github.com/xtensor-stack/xsimd.git"
xtl_url = "https://github.com/xtensor-stack/xtl.git"
def test_clone(git2cpp_path, tmp_path, run_in_tmp_path):
clone_cmd = [git2cpp_path, "clone", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
assert (tmp_path / "xtl").exists()
assert (tmp_path / "xtl/include").exists()
def test_clone_is_bare(git2cpp_path, tmp_path, run_in_tmp_path):
clone_cmd = [git2cpp_path, "clone", "--bare", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
assert (tmp_path / "xtl").is_dir()
status_cmd = [git2cpp_path, "status"]
p_status = subprocess.run(status_cmd, capture_output=True, cwd=tmp_path / "xtl", text=True)
assert p_status.returncode != 0
assert "This operation is not allowed against bare repositories" in p_status.stderr
branch_cmd = [git2cpp_path, "branch"]
p_branch = subprocess.run(branch_cmd, capture_output=True, cwd=tmp_path / "xtl", text=True)
assert p_branch.returncode == 0
assert p_branch.stdout.strip() == "* master"
def test_clone_shallow(git2cpp_path, tmp_path, run_in_tmp_path):
clone_cmd = [git2cpp_path, "clone", "--depth", "1", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
assert (tmp_path / "xtl").exists()
xtl_path = tmp_path / "xtl"
cmd_log = [git2cpp_path, "log"]
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
assert p_log.returncode == 0
assert p_log.stdout.count("Author") == 1
@pytest.mark.parametrize("protocol", ["http", "https"])
def test_clone_private_repo(git2cpp_path, tmp_path, run_in_tmp_path, private_test_repo, protocol):
# Succeeds with correct credentials.
# Note that http succeeds by redirecting to https.
username = "abc" # Can be any non-empty string.
password = private_test_repo["token"]
input = f"{username}\n{password}\n"
repo_path = tmp_path / private_test_repo["repo_name"]
url = private_test_repo["https_url" if protocol == "https" else "http_url"]
clone_cmd = [git2cpp_path, "clone", url]
p_clone = subprocess.run(clone_cmd, capture_output=True, text=True, input=input)
assert p_clone.returncode == 0
assert repo_path.exists()
# Single request for username and password.
assert p_clone.stdout.count("Username:") == 1
assert p_clone.stdout.count("Password:") == 1
status_cmd = [git2cpp_path, "status"]
p_status = subprocess.run(status_cmd, cwd=repo_path, capture_output=True, text=True)
assert p_status.returncode == 0
assert "On branch main" in p_status.stdout
assert "Your branch is up to date with 'origin/main'" in p_status.stdout
def test_clone_private_repo_fails_then_succeeds(
git2cpp_path, tmp_path, run_in_tmp_path, private_test_repo
):
# Fails with wrong credentials, then succeeds with correct ones.
username = "xyz" # Can be any non-empty string.
password = private_test_repo["token"]
input = f"wrong1\nwrong2\n{username}\n{password}"
repo_path = tmp_path / private_test_repo["repo_name"]
clone_cmd = [git2cpp_path, "clone", private_test_repo["https_url"]]
p_clone = subprocess.run(clone_cmd, capture_output=True, text=True, input=input)
assert p_clone.returncode == 0
assert repo_path.exists()
# Two requests for username and password.
assert p_clone.stdout.count("Username:") == 2
assert p_clone.stdout.count("Password:") == 2
status_cmd = [git2cpp_path, "status"]
p_status = subprocess.run(status_cmd, cwd=repo_path, capture_output=True, text=True)
assert p_status.returncode == 0
assert "On branch main" in p_status.stdout
assert "Your branch is up to date with 'origin/main'" in p_status.stdout
def test_clone_private_repo_fails_on_no_username(
git2cpp_path, tmp_path, run_in_tmp_path, private_test_repo
):
input = "\n"
repo_path = tmp_path / private_test_repo["repo_name"]
clone_cmd = [git2cpp_path, "clone", private_test_repo["https_url"]]
p_clone = subprocess.run(clone_cmd, capture_output=True, text=True, input=input)
assert p_clone.returncode != 0
assert "No username specified" in p_clone.stderr
assert not repo_path.exists()
assert p_clone.stdout.count("Username:") == 1
assert p_clone.stdout.count("Password:") == 0
def test_clone_private_repo_fails_on_no_password(
git2cpp_path, tmp_path, run_in_tmp_path, private_test_repo
):
input = "username\n\n" # Note no password between the \n
repo_path = tmp_path / private_test_repo["repo_name"]
clone_cmd = [git2cpp_path, "clone", private_test_repo["https_url"]]
p_clone = subprocess.run(clone_cmd, capture_output=True, text=True, input=input)
assert p_clone.returncode != 0
assert "No password specified" in p_clone.stderr
assert not repo_path.exists()
assert p_clone.stdout.count("Username:") == 1
assert p_clone.stdout.count("Password:") == 1
def test_clone_private_repo_fails_with_no_credential_callback(
git2cpp_path, tmp_path, run_in_tmp_path, disable_credential_callback
):
clone_cmd = [git2cpp_path, "clone", "https://github.com/QuantStack/git2cpp-test-private"]
p_clone = subprocess.run(clone_cmd, capture_output=True, text=True)
assert p_clone.returncode != 0
assert "Cloning into 'git2cpp-test-private'..." in p_clone.stdout
assert "error: remote authentication required but no callback set" in p_clone.stderr
@pytest.mark.parametrize("protocol", ["http", "https"])
def test_clone_gitlab(git2cpp_path, tmp_path, run_in_tmp_path, protocol):
repo_url = f"{protocol}://gitlab.quantstack.net/ianthomas23_group/cockle-playground"
clone_cmd = [git2cpp_path, "clone", repo_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
repo_path = tmp_path / "cockle-playground"
assert repo_path.is_dir()
assert (repo_path / "src").is_dir()
status_cmd = [git2cpp_path, "status"]
p_status = subprocess.run(status_cmd, capture_output=True, cwd=repo_path, text=True)
assert p_status.returncode == 0
assert "On branch main" in p_status.stdout
assert "Your branch is up to date with 'origin/main'" in p_status.stdout
@pytest.mark.skipif(not GIT2CPP_TEST_WASM, reason="Only test in WebAssembly")
def test_clone_timeout(git2cpp_path, tmp_path, run_in_tmp_path):
# Set very short timeout.
subprocess.run(["export", "GIT_HTTP_TIMEOUT=0.001"], check=True)
# Check timeout is set.
check_env_cmd = ["env"]
p_check_env = subprocess.run(check_env_cmd, capture_output=True, cwd=tmp_path, text=True)
assert "GIT_HTTP_TIMEOUT=0.001" in p_check_env.stdout
# Clone fails with timeout.
clone_cmd = [git2cpp_path, "clone", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode != 0
assert "network request timed out connecting to" in p_clone.stderr
assert (
"set a longer timeout in seconds using the environment variable GIT_HTTP_TIMEOUT"
in p_clone.stderr
)
assert "the current value is 0.001 seconds" in p_clone.stderr
# Set more reasonable timeout.
subprocess.run(["export", "GIT_HTTP_TIMEOUT=10"], check=True)
# Check timeout is set.
p_check_env = subprocess.run(check_env_cmd, capture_output=True, cwd=tmp_path, text=True)
assert "GIT_HTTP_TIMEOUT=10" in p_check_env.stdout
# Clone succeeds.
clone_cmd = [git2cpp_path, "clone", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
assert (tmp_path / "xtl").exists()
assert (tmp_path / "xtl/include").exists()
@pytest.mark.skipif(not GIT2CPP_TEST_WASM, reason="Only test in WebAssembly")
def test_clone_negative_timeout_ignored(git2cpp_path, tmp_path, run_in_tmp_path):
# Set negative timeout.
subprocess.run(["export", "GIT_HTTP_TIMEOUT=-1"], check=True)
# Check timeout is set.
check_env_cmd = ["env"]
p_check_env = subprocess.run(check_env_cmd, capture_output=True, cwd=tmp_path, text=True)
assert "GIT_HTTP_TIMEOUT=-1" in p_check_env.stdout
# Clone succeeds, ignoring invalid timeout.
clone_cmd = [git2cpp_path, "clone", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
assert (tmp_path / "xtl").exists()
assert (tmp_path / "xtl/include").exists()
assert (
"environment variable GIT_HTTP_TIMEOUT must be a positive number of seconds"
in p_clone.stdout
)
def test_clone_large_repo(git2cpp_path, tmp_path, run_in_tmp_path):
clone_cmd = [git2cpp_path, "clone", xsimd_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
assert (tmp_path / "xsimd").exists()
assert (tmp_path / "xsimd/include").exists()
assert (tmp_path / "xsimd/xsimdConfig.cmake.in").exists()
def test_clone_filemode(git2cpp_path, tmp_path, run_in_tmp_path):
clone_cmd = [git2cpp_path, "clone", xtl_url]
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
assert p_clone.returncode == 0
xtl_path = tmp_path / "xtl"
filemode_lines = get_filemode_lines(git2cpp_path, xtl_path)
if GIT2CPP_TEST_WASM:
# In WebAssembly core.filemode=false is set by an in-memory config override that has higher
# priority than the repository's config file, so is listed after it.
assert filemode_lines == ["core.filemode=true", "core.filemode=false"]
else:
assert filemode_lines == ["core.filemode=true"]
# Setting writes to the repository's config file, not the in-memory override.
set_filemode(git2cpp_path, xtl_path, "true")
filemode_lines = get_filemode_lines(git2cpp_path, xtl_path)
if GIT2CPP_TEST_WASM:
assert filemode_lines == ["core.filemode=true", "core.filemode=false"]
else:
assert filemode_lines == ["core.filemode=true"]
set_filemode(git2cpp_path, xtl_path, "false")
filemode_lines = get_filemode_lines(git2cpp_path, xtl_path)
if GIT2CPP_TEST_WASM:
assert filemode_lines == ["core.filemode=false", "core.filemode=false"]
else:
assert filemode_lines == ["core.filemode=false"]