Skip to content

feat: support codspeed #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
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
44 changes: 44 additions & 0 deletions .github/workflows/codspeed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CodSpeed

permissions:
contents: read

on:
push:
branches:
- "main"
paths:
- "commit_check/**"
- "tests/**"
- ".github/workflows/codspeed.yml"
- "pyproject.toml"
pull_request:
branches:
- "main"
paths:
- "commit_check/**"
- "tests/**"
- ".github/workflows/codspeed.yml"
- "pyproject.toml"
# `workflow_dispatch` allows CodSpeed to trigger backtest
# performance analysis in order to generate initial data.
workflow_dispatch:

jobs:
benchmarks:
name: Run benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install dependencies
run: pip install -e .[test]

- name: Run benchmarks
uses: CodSpeedHQ/action@v3
with:
token: ${{ secrets.CODSPEED_TOKEN }}
run: pytest tests/ --codspeed
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ tracker = "https://github.com/commit-check/commit-check/issues"

[project.optional-dependencies]
dev = ['nox']
test = ['coverage', 'pytest', 'pytest-mock']
test = ['coverage', 'pytest', 'pytest-mock', 'pytest-codspeed']
docs = ['sphinx-immaterial', 'sphinx-autobuild']

[tool.setuptools]
Expand Down
12 changes: 12 additions & 0 deletions tests/author_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from commit_check import PASS, FAIL
from commit_check.author import check_author

Expand All @@ -11,6 +12,7 @@ class TestAuthorName:
fake_author_value_an = "fake_author_name"
fake_accented_author_value_an = "fáké_áúthór_námé"

@pytest.mark.benchmark
def test_check_author(self, mocker):
# Must call get_commit_info, re.match.
checks = [{
Expand All @@ -30,6 +32,7 @@ def test_check_author(self, mocker):
assert m_get_commit_info.call_count == 1
assert m_re_match.call_count == 1

@pytest.mark.benchmark
def test_check_author_with_accented_letters(self, mocker):
# Must call get_commit_info, re.match.
checks = [{
Expand All @@ -49,6 +52,7 @@ def test_check_author_with_accented_letters(self, mocker):
assert m_get_commit_info.call_count == 1
assert m_re_match.call_count == 1

@pytest.mark.benchmark
def test_check_author_with_empty_checks(self, mocker):
# Must NOT call get_commit_info, re.match. with `checks` param with length 0.
checks = []
Expand All @@ -65,6 +69,7 @@ def test_check_author_with_empty_checks(self, mocker):
assert m_get_commit_info.call_count == 0
assert m_re_match.call_count == 0

@pytest.mark.benchmark
def test_check_author_with_different_check(self, mocker):
# Must NOT call get_commit_info, re.match with not `author_name`.
checks = [{
Expand All @@ -84,6 +89,7 @@ def test_check_author_with_different_check(self, mocker):
assert m_get_commit_info.call_count == 0
assert m_re_match.call_count == 0

@pytest.mark.benchmark
def test_check_author_with_len0_regex(self, mocker, capfd):
# Must NOT call get_commit_info, re.match with `regex` with length 0.
checks = [
Expand All @@ -107,6 +113,7 @@ def test_check_author_with_len0_regex(self, mocker, capfd):
out, _ = capfd.readouterr()
assert "Not found regex for author_name." in out

@pytest.mark.benchmark
def test_check_author_with_result_none(self, mocker):
# Must call print_error_message, print_suggestion when re.match returns NONE.
checks = [{
Expand Down Expand Up @@ -140,6 +147,7 @@ class TestAuthorEmail:
# used by get_commit_info mock
fake_author_value_ae = "fake_author_email"

@pytest.mark.benchmark
def test_check_author(self, mocker):
# Must call get_commit_info, re.match.
checks = [{
Expand All @@ -159,6 +167,7 @@ def test_check_author(self, mocker):
assert m_get_commit_info.call_count == 1
assert m_re_match.call_count == 1

@pytest.mark.benchmark
def test_check_author_with_empty_checks(self, mocker):
# Must NOT call get_commit_info, re.match. with `checks` param with length 0.
checks = []
Expand All @@ -175,6 +184,7 @@ def test_check_author_with_empty_checks(self, mocker):
assert m_get_commit_info.call_count == 0
assert m_re_match.call_count == 0

@pytest.mark.benchmark
def test_check_author_with_different_check(self, mocker):
# Must NOT call get_commit_info, re.match with not `author_email`.
checks = [{
Expand All @@ -194,6 +204,7 @@ def test_check_author_with_different_check(self, mocker):
assert m_get_commit_info.call_count == 0
assert m_re_match.call_count == 0

@pytest.mark.benchmark
def test_check_author_with_len0_regex(self, mocker, capfd):
# Must NOT call get_commit_info, re.match with `regex` with length 0.
checks = [
Expand All @@ -217,6 +228,7 @@ def test_check_author_with_len0_regex(self, mocker, capfd):
out, _ = capfd.readouterr()
assert "Not found regex for author_email." in out

@pytest.mark.benchmark
def test_check_author_with_result_none(self, mocker):
# Must call print_error_message, print_suggestion when re.match returns NONE.
checks = [{
Expand Down
11 changes: 10 additions & 1 deletion tests/branch_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from commit_check import PASS, FAIL
from commit_check.branch import check_branch, check_merge_base

Expand All @@ -7,6 +8,7 @@


class TestCheckBranch:
@pytest.mark.benchmark
def test_check_branch(self, mocker):
# Must call get_branch_name, re.match at once.
checks = [{
Expand All @@ -26,6 +28,7 @@ def test_check_branch(self, mocker):
assert m_get_branch_name.call_count == 1
assert m_re_match.call_count == 1

@pytest.mark.benchmark
def test_check_branch_with_empty_checks(self, mocker):
# Must NOT call get_branch_name, re.match with `checks` param with length 0.
checks = []
Expand All @@ -42,6 +45,7 @@ def test_check_branch_with_empty_checks(self, mocker):
assert m_get_branch_name.call_count == 0
assert m_re_match.call_count == 0

@pytest.mark.benchmark
def test_check_branch_with_different_check(self, mocker):
# Must NOT call get_branch_name, re.match with not `branch`.
checks = [{
Expand All @@ -61,6 +65,7 @@ def test_check_branch_with_different_check(self, mocker):
assert m_get_branch_name.call_count == 0
assert m_re_match.call_count == 0

@pytest.mark.benchmark
def test_check_branch_with_len0_regex(self, mocker, capfd):
# Must NOT call get_branch_name, re.match with `regex` with length 0.
checks = [
Expand All @@ -84,6 +89,7 @@ def test_check_branch_with_len0_regex(self, mocker, capfd):
out, _ = capfd.readouterr()
assert "Not found regex for branch naming." in out

@pytest.mark.benchmark
def test_check_branch_with_result_none(self, mocker):
# Must call print_error_message, print_suggestion when re.match returns NONE.
checks = [{
Expand Down Expand Up @@ -115,14 +121,15 @@ def test_check_branch_with_result_none(self, mocker):


class TestCheckMergeBase:
@pytest.mark.benchmark
def test_check_merge_base_with_empty_checks(self, mocker):
checks = []
m_check_merge = mocker.patch(f"{LOCATION}.check_merge_base")
retval = check_merge_base(checks)
assert retval == PASS
assert m_check_merge.call_count == 0


@pytest.mark.benchmark
def test_check_merge_base_with_empty_regex(self, mocker):
checks = [{
"check": "merge_base",
Expand All @@ -133,6 +140,7 @@ def test_check_merge_base_with_empty_regex(self, mocker):
assert retval == PASS
assert m_check_merge.call_count == 0

@pytest.mark.benchmark
def test_check_merge_base_with_different_check(self, mocker):
checks = [{
"check": "branch",
Expand All @@ -143,6 +151,7 @@ def test_check_merge_base_with_different_check(self, mocker):
assert retval == PASS
assert m_check_merge.call_count == 0

@pytest.mark.benchmark
def test_check_merge_base_fail_with_messages(self, mocker, capfd):
checks = [{
"check": "merge_base",
Expand Down
12 changes: 12 additions & 0 deletions tests/commit_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from commit_check import PASS, FAIL
from commit_check.commit import check_commit_msg, get_default_commit_msg_file, read_commit_msg, check_commit_signoff

Expand All @@ -9,11 +10,13 @@
MSG_FILE = '.git/COMMIT_EDITMSG'


@pytest.mark.benchmark
def test_get_default_commit_msg_file(mocker):
retval = get_default_commit_msg_file()
assert retval == ".git/COMMIT_EDITMSG"


@pytest.mark.benchmark
def test_read_commit_msg_from_existing_file(tmp_path):
# Create a temporary file with a known content
commit_msg_content = "Test commit message content."
Expand All @@ -24,12 +27,14 @@ def test_read_commit_msg_from_existing_file(tmp_path):
assert result == commit_msg_content


@pytest.mark.benchmark
def test_read_commit_msg_file_not_found(mocker):
m_commits_info = mocker.patch('commit_check.util.get_commit_info', return_value='mocked_commits_info')
read_commit_msg("non_existent_file.txt")
assert m_commits_info.call_count == 0


@pytest.mark.benchmark
def test_check_commit_msg_no_commit_msg_file(mocker):
mock_get_default_commit_msg_file = mocker.patch(
"commit_check.commit.get_default_commit_msg_file",
Expand All @@ -49,6 +54,7 @@ def test_check_commit_msg_no_commit_msg_file(mocker):
assert result == 0


@pytest.mark.benchmark
def test_check_commit_with_empty_checks(mocker):
checks = []
m_re_match = mocker.patch(
Expand All @@ -60,6 +66,7 @@ def test_check_commit_with_empty_checks(mocker):
assert m_re_match.call_count == 0


@pytest.mark.benchmark
def test_check_commit_with_different_check(mocker):
checks = [{
"check": "branch",
Expand All @@ -74,6 +81,7 @@ def test_check_commit_with_different_check(mocker):
assert m_re_match.call_count == 0


@pytest.mark.benchmark
def test_check_commit_with_len0_regex(mocker, capfd):
checks = [
{
Expand All @@ -92,6 +100,7 @@ def test_check_commit_with_len0_regex(mocker, capfd):
assert "Not found regex for commit message." in out


@pytest.mark.benchmark
def test_check_commit_with_result_none(mocker):
checks = [{
"check": "message",
Expand All @@ -116,6 +125,7 @@ def test_check_commit_with_result_none(mocker):
assert m_print_suggestion.call_count == 1


@pytest.mark.benchmark
def test_check_commit_signoff(mocker):
checks = [{
"check": "commit_signoff",
Expand All @@ -140,6 +150,7 @@ def test_check_commit_signoff(mocker):
assert m_print_suggestion.call_count == 1


@pytest.mark.benchmark
def test_check_commit_signoff_with_empty_regex(mocker):
checks = [{
"check": "commit_signoff",
Expand All @@ -156,6 +167,7 @@ def test_check_commit_signoff_with_empty_regex(mocker):
assert m_re_match.call_count == 0


@pytest.mark.benchmark
def test_check_commit_signoff_with_empty_checks(mocker):
checks = []
m_re_match = mocker.patch(
Expand Down
5 changes: 5 additions & 0 deletions tests/error_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,31 @@
from commit_check.error import error_handler, log_and_exit


@pytest.mark.benchmark
def test_error_handler_RuntimeError():
with pytest.raises(SystemExit) as exit_info:
with error_handler():
raise RuntimeError("Test error")
assert exit_info.value.code == 1


@pytest.mark.benchmark
def test_error_handler_KeyboardInterrupt():
with pytest.raises(SystemExit) as exit_info:
with error_handler():
raise KeyboardInterrupt
assert exit_info.value.code == 130


@pytest.mark.benchmark
def test_error_handler_unexpected_error():
with pytest.raises(SystemExit) as exit_info:
with error_handler():
raise Exception("Test error")
assert exit_info.value.code == 3


@pytest.mark.benchmark
def test_error_handler_cannot_access(mocker):
with pytest.raises(SystemExit):
store_dir = "/fake/commit-check"
Expand All @@ -50,6 +54,7 @@ def test_error_handler_cannot_access(mocker):
mock_open().write.assert_any_call(f"Failed to write to log at {log_path}\n")


@pytest.mark.benchmark
@pytest.mark.xfail
def test_log_and_exit(monkeypatch):
monkeypatch.setenv("COMMIT_CHECK_HOME", "")
Expand Down
5 changes: 5 additions & 0 deletions tests/main_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


class TestMain:
@pytest.mark.benchmark
@pytest.mark.parametrize("argv, check_commit_call_count, check_branch_call_count, check_author_call_count, check_commit_signoff_call_count, check_merge_base_call_count", [
([CMD, "--message"], 1, 0, 0, 0, 0),
([CMD, "--branch"], 0, 1, 0, 0, 0),
Expand Down Expand Up @@ -53,6 +54,7 @@ def test_main(
assert m_check_commit_signoff.call_count == check_commit_signoff_call_count
assert m_check_merge_base.call_count == check_merge_base_call_count

@pytest.mark.benchmark
def test_main_help(self, mocker, capfd):
mocker.patch(
"commit_check.main.validate_config",
Expand All @@ -78,6 +80,7 @@ def test_main_help(self, mocker, capfd):
stdout, _ = capfd.readouterr()
assert "usage: " in stdout

@pytest.mark.benchmark
def test_main_version(self, mocker):
mocker.patch(
"commit_check.main.validate_config",
Expand All @@ -101,6 +104,7 @@ def test_main_version(self, mocker):
assert m_check_commit_signoff.call_count == 0
assert m_check_merge_base.call_count == 0

@pytest.mark.benchmark
def test_main_validate_config_ret_none(self, mocker):
mocker.patch(
"commit_check.main.validate_config",
Expand All @@ -116,6 +120,7 @@ def test_main_validate_config_ret_none(self, mocker):
assert m_check_commit.call_count == 1
assert m_check_commit.call_args[0][0] == DEFAULT_CONFIG["checks"]

@pytest.mark.benchmark
@pytest.mark.parametrize(
"argv, message_result, branch_result, author_name_result, author_email_result, commit_signoff_result, merge_base_result, final_result",
[
Expand Down
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy