Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 210 additions & 51 deletions tests/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,30 @@
import os
import pathlib
import textwrap
from typing import Callable

import pytest

from pytest_mock import MockerFixture

from libvcs import exc
from libvcs.git import GitRemote, convert_pip_url as git_convert_pip_url, extract_status
from libvcs.git import (
GitRemote,
GitRepo,
convert_pip_url as git_convert_pip_url,
extract_status,
)
from libvcs.shortcuts import create_repo_from_pip_url
from libvcs.util import run, which

if not which("git"):
pytestmark = pytest.mark.skip(reason="git is not available")


RepoTestFactory = Callable[..., GitRepo]
RepoTestFactoryLazyKwargs = Callable[..., dict]


@pytest.fixture(autouse=True, scope="module")
def gitconfig(user_path: pathlib.Path):
gitconfig = user_path / ".gitconfig"
Expand All @@ -37,7 +49,31 @@ def gitconfig_default(monkeypatch: pytest.MonkeyPatch, user_path: pathlib.Path):
monkeypatch.setenv("HOME", str(user_path))


def test_repo_git_obtain_initial_commit_repo(tmp_path: pathlib.Path):
@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda bare_repo_dir, tmp_path, **kwargs: {
"url": f"file://{bare_repo_dir}",
"repo_dir": tmp_path / "obtaining a bare repo",
},
],
[
create_repo_from_pip_url,
lambda bare_repo_dir, tmp_path, **kwargs: {
"pip_url": f"git+file://{bare_repo_dir}",
"repo_dir": tmp_path / "obtaining a bare repo",
},
],
],
)
def test_repo_git_obtain_initial_commit_repo(
tmp_path: pathlib.Path,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
"""initial commit repos return 'initial'.

note: this behaviors differently from git(1)'s use of the word "bare".
Expand All @@ -48,26 +84,39 @@ def test_repo_git_obtain_initial_commit_repo(tmp_path: pathlib.Path):
run(["git", "init", repo_name], cwd=tmp_path)

bare_repo_dir = tmp_path / repo_name

git_repo = create_repo_from_pip_url(
**{
"pip_url": f"git+file://{bare_repo_dir}",
"repo_dir": tmp_path / "obtaining a bare repo",
}
)
git_repo: GitRepo = constructor(**lazy_constructor_options(**locals()))

git_repo.obtain()
assert git_repo.get_revision() == "initial"


def test_repo_git_obtain_full(tmp_path: pathlib.Path, git_remote):
git_repo = create_repo_from_pip_url(
**{
"pip_url": f"git+file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
}
)

@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda git_remote, tmp_path, **kwargs: {
"url": f"file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
},
],
[
create_repo_from_pip_url,
lambda git_remote, tmp_path, **kwargs: {
"pip_url": f"git+file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
},
],
],
)
def test_repo_git_obtain_full(
tmp_path: pathlib.Path,
git_remote,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
git_repo: GitRepo = constructor(**lazy_constructor_options(**locals()))
git_repo.obtain()

test_repo_revision = run(["git", "rev-parse", "HEAD"], cwd=git_remote)
Expand All @@ -76,14 +125,34 @@ def test_repo_git_obtain_full(tmp_path: pathlib.Path, git_remote):
assert os.path.exists(tmp_path / "myrepo")


def test_repo_update_handle_cases(tmp_path: pathlib.Path, git_remote, mocker):
git_repo = create_repo_from_pip_url(
**{
"pip_url": f"git+file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
}
)

@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda git_remote, tmp_path, **kwargs: {
"url": f"file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
},
],
[
create_repo_from_pip_url,
lambda git_remote, tmp_path, **kwargs: {
"pip_url": f"git+file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
},
],
],
)
def test_repo_update_handle_cases(
tmp_path: pathlib.Path,
git_remote: pathlib.Path,
mocker: MockerFixture,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
git_repo: GitRepo = constructor(**lazy_constructor_options(**locals()))
git_repo.obtain() # clone initial repo
mocka = mocker.spy(git_repo, "run")
git_repo.update_repo()
Expand All @@ -98,7 +167,35 @@ def test_repo_update_handle_cases(tmp_path: pathlib.Path, git_remote, mocker):
assert mocker.call(["symbolic-ref", "--short", "HEAD"]) not in mocka.mock_calls


def test_progress_callback(tmp_path: pathlib.Path, git_remote, mocker):
@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda git_remote, tmp_path, progress_callback, **kwargs: {
"url": f"file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
"progress_callback": progress_callback,
},
],
[
create_repo_from_pip_url,
lambda git_remote, tmp_path, progress_callback, **kwargs: {
"pip_url": f"git+file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
"progress_callback": progress_callback,
},
],
],
)
def test_progress_callback(
tmp_path: pathlib.Path,
git_remote: pathlib.Path,
mocker: MockerFixture,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
def progress_callback_spy(output, timestamp):
assert isinstance(output, str)
assert isinstance(timestamp, datetime.datetime)
Expand All @@ -110,27 +207,43 @@ def progress_callback_spy(output, timestamp):
run(["git", "rev-parse", "HEAD"], cwd=git_remote)

# create a new repo with the repo as a remote
git_repo = create_repo_from_pip_url(
**{
"pip_url": f"git+file://{git_remote}",
"repo_dir": tmp_path / "myrepo",
"progress_callback": progress_callback,
}
)
git_repo: GitRepo = constructor(**lazy_constructor_options(**locals()))
git_repo.obtain()

assert progress_callback.called


def test_remotes(repos_path, git_remote):
@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda git_remote, repos_path, repo_name, **kwargs: {
"url": f"file://{git_remote}",
"repo_dir": repos_path / repo_name,
},
],
[
create_repo_from_pip_url,
lambda git_remote, repos_path, repo_name, **kwargs: {
"pip_url": f"git+file://{git_remote}",
"repo_dir": repos_path / repo_name,
},
],
],
)
def test_remotes(
repos_path: pathlib.Path,
git_remote: pathlib.Path,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
repo_name = "myrepo"
remote_name = "myremote"
remote_url = "https://localhost/my/git/repo.git"

git_repo = create_repo_from_pip_url(
pip_url=f"git+file://{git_remote}",
repo_dir=repos_path / repo_name,
)
git_repo: GitRepo = constructor(**lazy_constructor_options(**locals()))
git_repo.obtain()
git_repo.set_remote(name=remote_name, url=remote_url)

Expand Down Expand Up @@ -160,17 +273,39 @@ def test_git_get_url_and_rev_from_pip_url():
assert rev == "eucalyptus"


def test_remotes_preserves_git_ssh(repos_path, git_remote):
@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda git_remote, repo_dir, **kwargs: {
"url": f"file://{git_remote}",
"repo_dir": str(repo_dir),
},
],
[
create_repo_from_pip_url,
lambda git_remote, repo_dir, **kwargs: {
"pip_url": f"git+file://{git_remote}",
"repo_dir": repo_dir,
},
],
],
)
def test_remotes_preserves_git_ssh(
repos_path: pathlib.Path,
git_remote: pathlib.Path,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
# Regression test for #14
repo_name = "myexamplegit"
repo_dir = repos_path / repo_name
remote_name = "myremote"
remote_url = "git+ssh://[email protected]/tony/AlgoXY.git"
git_repo: GitRepo = constructor(**lazy_constructor_options(**locals()))

git_repo = create_repo_from_pip_url(
pip_url=f"git+file://{git_remote}",
repo_dir=repo_dir,
)
git_repo.obtain()
git_repo.set_remote(name=remote_name, url=remote_url)

Expand All @@ -180,7 +315,31 @@ def test_remotes_preserves_git_ssh(repos_path, git_remote):
)


def test_private_ssh_format(pip_url_kwargs):
@pytest.mark.parametrize(
# Postpone evaluation of options so fixture variables can interpolate
"constructor,lazy_constructor_options",
[
[
GitRepo,
lambda bare_repo_dir, tmp_path, **kwargs: {
"url": f"file://{bare_repo_dir}",
"repo_dir": tmp_path / "obtaining a bare repo",
},
],
[
create_repo_from_pip_url,
lambda bare_repo_dir, tmp_path, **kwargs: {
"pip_url": f"git+file://{bare_repo_dir}",
"repo_dir": tmp_path / "obtaining a bare repo",
},
],
],
)
def test_private_ssh_format(
pip_url_kwargs: dict,
constructor: RepoTestFactory,
lazy_constructor_options: RepoTestFactoryLazyKwargs,
):
pip_url_kwargs.update(
**{"pip_url": "git+ssh://github.com:/tmp/omg/private_ssh_repo"}
)
Expand All @@ -190,14 +349,14 @@ def test_private_ssh_format(pip_url_kwargs):
excinfo.match(r"is malformatted")


def test_ls_remotes(git_repo):
def test_ls_remotes(git_repo: GitRepo):
remotes = git_repo.remotes()

assert "origin" in remotes
assert "origin" in git_repo.remotes(flat=True)


def test_get_remotes(git_repo):
def test_get_remotes(git_repo: GitRepo):
assert "origin" in git_repo.remotes()


Expand All @@ -207,7 +366,7 @@ def test_get_remotes(git_repo):
["myrepo", "file:///apples"],
],
)
def test_set_remote(git_repo, repo_name, new_repo_url):
def test_set_remote(git_repo: GitRepo, repo_name: str, new_repo_url: str):
mynewremote = git_repo.set_remote(name=repo_name, url="file:///")

assert "file:///" in mynewremote, "set_remote returns remote"
Expand All @@ -229,13 +388,13 @@ def test_set_remote(git_repo, repo_name, new_repo_url):
), "Running remove_set should overwrite previous remote"


def test_get_git_version(git_repo):
def test_get_git_version(git_repo: GitRepo):
expected_version = git_repo.run(["--version"]).replace("git version ", "")
assert git_repo.get_git_version()
assert expected_version == git_repo.get_git_version()


def test_get_current_remote_name(git_repo):
def test_get_current_remote_name(git_repo: GitRepo):
assert git_repo.get_current_remote_name() == "origin"

new_branch = "another-branch-with-no-upstream"
Expand Down Expand Up @@ -328,7 +487,7 @@ def test_extract_status():
],
],
)
def test_extract_status_b(fixture, expected_result):
def test_extract_status_b(fixture: str, expected_result: dict):
assert (
extract_status(textwrap.dedent(fixture))._asdict().items()
>= expected_result.items()
Expand Down Expand Up @@ -378,7 +537,7 @@ def test_extract_status_b(fixture, expected_result):
],
],
)
def test_extract_status_c(fixture, expected_result):
def test_extract_status_c(fixture: str, expected_result: dict):
assert (
expected_result.items()
<= extract_status(textwrap.dedent(fixture))._asdict().items()
Expand Down