From 6f07679a93331c0bfedccd5489a01d47c9a5ad9c Mon Sep 17 00:00:00 2001 From: smd1121 <2087868649@qq.com> Date: Wed, 3 Jan 2024 21:17:31 +0800 Subject: [PATCH] [feat][test] add tests on cat-file & hash-object --- .github/workflows/test.yml | 28 +++++++++++++++ .gitignore | 1 + xgit/test/__init__.py | 0 xgit/test/requirements.txt | 3 ++ xgit/test/test_cat_file.py | 46 ++++++++++++++++++++++++ xgit/test/test_hash_object.py | 47 ++++++++++++++++++++++++ xgit/test/test_utils.py | 67 +++++++++++++++++++++++++++++++++++ 7 files changed, 192 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 xgit/test/__init__.py create mode 100644 xgit/test/requirements.txt create mode 100644 xgit/test/test_cat_file.py create mode 100644 xgit/test/test_hash_object.py create mode 100644 xgit/test/test_utils.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9d05f4d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: test + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + pip install -r xgit/test/requirements.txt + pip install . + + - name: Config Git + run: | + git config --global user.email "you@example.com" + git config --global user.name "Your Name" + + - name: Run tests + run: | + cd xgit + coverage run -m pytest . + coverage report -m diff --git a/.gitignore b/.gitignore index e865cf1..0d83276 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ __pycache__ .mypy_cache *.egg-info/ +.pytest_cache/ diff --git a/xgit/test/__init__.py b/xgit/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/xgit/test/requirements.txt b/xgit/test/requirements.txt new file mode 100644 index 0000000..0b98267 --- /dev/null +++ b/xgit/test/requirements.txt @@ -0,0 +1,3 @@ +loguru +pytest +coverage diff --git a/xgit/test/test_cat_file.py b/xgit/test/test_cat_file.py new file mode 100644 index 0000000..303429e --- /dev/null +++ b/xgit/test/test_cat_file.py @@ -0,0 +1,46 @@ +import subprocess +from pathlib import Path + +from typer.testing import CliRunner + +from xgit.test.test_utils import gen_random_sha, check_same_output, gen_random_string, temp_git_workspace + +runner = CliRunner() + + +def test_cat_blob(): + with temp_git_workspace() as dir: + file1 = Path(dir) / "test" + with open(file1, "w", encoding="utf-8") as f: + f.write(gen_random_string()) + + file2 = Path(dir) / "a" / "b" + file2.parent.mkdir(exist_ok=True, parents=True) + file2.touch() + + subprocess.run(["git", "add", "."], check=True) + subprocess.run(["git", "commit", "-m", "test"], check=True) + + to_test = ["-p", "-e", "-t", "-s", "blob"] + + # on tree object + sha = subprocess.run( + ["git", "rev-parse", "HEAD^{tree}"], check=True, capture_output=True, text=True + ).stdout.strip() + for i in to_test: + assert check_same_output(["cat-file", i, sha]) + + # on contents (including blob object) + objs = subprocess.run( + ["git", "ls-tree", "-r", "HEAD^{tree}"], check=True, capture_output=True, text=True + ).stdout.strip() + obj_lst = objs.split("\n") + for obj in obj_lst: + sha = obj.split()[2] + for i in to_test: + assert check_same_output(["cat-file", i, sha]) + + # on not exist object + random_sha = gen_random_sha() + for i in to_test: + assert check_same_output(["cat-file", i, random_sha]) diff --git a/xgit/test/test_hash_object.py b/xgit/test/test_hash_object.py new file mode 100644 index 0000000..d29c9b7 --- /dev/null +++ b/xgit/test/test_hash_object.py @@ -0,0 +1,47 @@ +import tempfile +import subprocess + +from typer.testing import CliRunner + +from xgit.cli import app +from xgit.test.test_utils import gen_random_string, temp_xgit_workspace + +runner = CliRunner() + + +def test_hash_value(): + content = gen_random_string() + + with tempfile.NamedTemporaryFile() as f: + with open(f.name, "w", encoding="utf-8") as f: + f.write(content) + + expected_by_file = subprocess.run( + ["git", "hash-object", f.name], check=True, capture_output=True, text=True + ).stdout + result_by_file = runner.invoke(app, ["hash-object", f.name]).stdout + assert result_by_file == expected_by_file + + expected_by_stdin = subprocess.run( + ["git", "hash-object", "--stdin"], input=content, check=True, capture_output=True, text=True + ).stdout + result_by_stdin = runner.invoke(app, ["hash-object", "--stdin"], input=content).stdout + assert result_by_stdin == expected_by_stdin + + +def test_hash_object_write(): + content = gen_random_string() + + with temp_xgit_workspace(): + with tempfile.NamedTemporaryFile() as f: + with open(f.name, "w", encoding="utf-8") as f: + f.write(content) + + result = runner.invoke(app, ["hash-object", "-w", f.name]) + object_id = result.stdout.strip() + + read_by_git = subprocess.run( + ["git", "cat-file", "blob", object_id], check=True, capture_output=True, text=True + ).stdout + + assert content == read_by_git diff --git a/xgit/test/test_utils.py b/xgit/test/test_utils.py new file mode 100644 index 0000000..c02da08 --- /dev/null +++ b/xgit/test/test_utils.py @@ -0,0 +1,67 @@ +import os +import random +import string +import tempfile +import contextlib +import subprocess + +from loguru import logger +from typer.testing import CliRunner + +from xgit.cli import app + +runner = CliRunner(mix_stderr=False) + + +@contextlib.contextmanager +def temp_git_workspace(use_xgit: bool = False): + original_dir = os.getcwd() + with tempfile.TemporaryDirectory() as dir: + try: + os.chdir(dir) + git = "xgit" if use_xgit else "git" + subprocess.run([git, "init", dir], check=True) + yield dir + finally: + os.chdir(original_dir) + + +@contextlib.contextmanager +def temp_xgit_workspace(): + with temp_git_workspace(use_xgit=True) as dir: + yield dir + + +def gen_random_string(len: int = 1000): + # 关于 `\r` 的事情有点脏,我不想管。有关换行符的事情,关心的朋友可以搜索 autocrlf + # 具体来说,如果不去掉 `\r`,那么在下面测试的一些情况中(尤其是有和 stdout 交互的情况中) `\r` 会变成 `\n` + # 这会导致内容不一致,进而导致 SHA 不正确。 + # 如果您感兴趣,可以把下面的 `.replace` 去掉试试! + # 如果有更正确的方案,也请您告诉我~ + alphabet = string.octdigits + string.whitespace.replace("\r", "") + return "".join(random.choice(alphabet) for _ in range(len)) + + +def gen_random_sha(): + return "".join(random.choice(string.hexdigits) for _ in range(40)) + + +def check_same_output(cmd: list[str]) -> bool: + git_result = subprocess.run(["git"] + cmd, capture_output=True, check=False) + xgit_result = runner.invoke(app, cmd) + xgit_stdout = xgit_result.stdout.encode() + + if git_result.returncode != xgit_result.exit_code: + logger.info(f"cmd: {cmd}") + logger.info(f"exit_code not equal: {git_result.returncode} != {xgit_result.exit_code}") + return False + + if git_result.returncode != 0: + return True + + if git_result.stdout != xgit_stdout: + logger.info(f"cmd: {cmd}") + logger.info(f"stdout not equal: {git_result.stdout} != {xgit_stdout}") + return False + + return True