From 21a47ef305c45eb67f321f6794d420320933ab47 Mon Sep 17 00:00:00 2001 From: gcattan Date: Thu, 4 Aug 2022 19:32:29 +0200 Subject: [PATCH] Gc/pytest utils (#11) * add test for parse_inputs * install python package (dev) * typo * :art: Format Python code with psf/black (#10) Co-authored-by: gcattan * test sample size * typo * fix test sample size * :art: Format Python code with psf/black (#12) Co-authored-by: gcattan * add test on sample size * fix assert for test_sample_greater_size_than_list * fix test_sample_greater_size_than_list * :art: Format Python code with psf/black (#13) Co-authored-by: gcattan * test skeleton for test_git * :art: Format Python code with psf/black (#14) Co-authored-by: gcattan * finish test common and start test for git * import datetime missing * typeerror: Integer required * return absolute month difference * typo * :art: Format Python code with psf/black (#17) Co-authored-by: gcattan * test if code runs wihtin a py.test session * typo * print traces * test_branch_is_old: GREEN * test_branch_are_coupled: GREEN * change are_coupled -> contains * test_git pass * :art: Format Python code with psf/black (#18) Co-authored-by: gcattan * test for is_empty_body * typo * test is_test_commit: GREEN * test for not_a_squased commit: GREEN * test if commit contains bad words: GREEN * test count coupled : GREEN! * test count_old_branches : GREEN * test process logs: GREEN * tes overall OK * some helpfull logs * :art: Format Python code with psf/black (#19) Co-authored-by: gcattan * Update run-pytest.yml * missing "-r" option in "is_old" * Update run-pytest.yml Co-authored-by: gcattan Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: gcattan --- .github/workflows/run-pytest.yml | 8 ++- example/git-quality-check.py | 12 ++--- .../indicators/commits/__init__.py | 2 +- .../indicators/commits/count_bad_words.py | 9 ---- .../commits/does_contain_bad_words.py | 9 ++++ .../indicators/counters/count_coupled.py | 10 ++-- .../indicators/counters/process_logs.py | 1 + git_quality_check/utils/__init__.py | 11 +++- git_quality_check/utils/common.py | 38 +++++++++---- git_quality_check/utils/git.py | 7 +-- .../commits/test_count_bad_words.py | 5 -- .../commits/test_does_contain_bad_words.py | 10 ++++ .../indicators/commits/test_is_empty_body.py | 14 ++++- .../indicators/commits/test_is_test_commit.py | 11 +++- .../commits/test_not_a_squashed_commit.py | 5 +- .../indicators/counters/test_count_coupled.py | 5 +- .../counters/test_count_old_branches.py | 4 +- .../indicators/counters/test_process_logs.py | 17 +++++- tests/scoring/test_overall.py | 5 +- tests/utils/test_common.py | 54 +++++++++++++++++-- tests/utils/test_git.py | 45 +++++++++++++++- 21 files changed, 223 insertions(+), 59 deletions(-) delete mode 100644 git_quality_check/indicators/commits/count_bad_words.py create mode 100644 git_quality_check/indicators/commits/does_contain_bad_words.py delete mode 100644 tests/indicators/commits/test_count_bad_words.py create mode 100644 tests/indicators/commits/test_does_contain_bad_words.py diff --git a/.github/workflows/run-pytest.yml b/.github/workflows/run-pytest.yml index 2a33e9c..4ebf0b6 100644 --- a/.github/workflows/run-pytest.yml +++ b/.github/workflows/run-pytest.yml @@ -9,15 +9,19 @@ jobs: max-parallel: 5 steps: - uses: actions/checkout@v2 - - name: Set up Python 3.10 + - name: Set up Python 3.9 uses: actions/setup-python@v2 with: - python-version: "3.10" + python-version: "3.9" - name: Add conda to system path run: | # $CONDA is an environment variable pointing to the root of the miniconda directory echo $CONDA/bin >> $GITHUB_PATH + - name: Install git repo + run: | + python setup.py develop - name: Test with pytest run: | conda install pytest + git fetch pytest diff --git a/example/git-quality-check.py b/example/git-quality-check.py index 57000c7..318a7ba 100644 --- a/example/git-quality-check.py +++ b/example/git-quality-check.py @@ -16,7 +16,7 @@ from git_quality_check.indicators.commits import ( is_empty_body, not_a_squashed_commit, - count_bad_words, + does_contain_bad_words, is_test_commit, ) @@ -31,17 +31,17 @@ branches = git_all_branches() bad_commit_index = process_logs( - logs, [not_a_squashed_commit, is_empty_body, count_bad_words(bad_words)] + logs, [not_a_squashed_commit, is_empty_body, does_contain_bad_words(bad_words)] ) test_index = process_logs(logs, [is_test_commit]) old_branches_index = count_old_branches(branches) coupling_index = count_coupled(branches, main_branches) - print(bad_commit_index) - print(test_index) - print(old_branches_index) - print(coupling_index) + print("Percent of bad commits: ", bad_commit_index) + print("Percent of test commits: ", test_index) + print("Percent of old branches: ", old_branches_index) + print("Percent of coupled branches", coupling_index) overall = compute_score( bad_commit_index, test_index, old_branches_index, coupling_index diff --git a/git_quality_check/indicators/commits/__init__.py b/git_quality_check/indicators/commits/__init__.py index 6dac9b2..247cb72 100644 --- a/git_quality_check/indicators/commits/__init__.py +++ b/git_quality_check/indicators/commits/__init__.py @@ -1,4 +1,4 @@ from .is_empty_body import is_empty_body from .not_a_squashed_commit import not_a_squashed_commit -from .count_bad_words import count_bad_words +from .does_contain_bad_words import does_contain_bad_words from .is_test_commit import is_test_commit diff --git a/git_quality_check/indicators/commits/count_bad_words.py b/git_quality_check/indicators/commits/count_bad_words.py deleted file mode 100644 index 59da32e..0000000 --- a/git_quality_check/indicators/commits/count_bad_words.py +++ /dev/null @@ -1,9 +0,0 @@ -def count_bad_words(bad_words: list[str]): - def _count_bad_words(log: str): - counter = 0 - for word in bad_words: - if word in log.lower().split(): - counter += 1 - return counter - - return _count_bad_words diff --git a/git_quality_check/indicators/commits/does_contain_bad_words.py b/git_quality_check/indicators/commits/does_contain_bad_words.py new file mode 100644 index 0000000..ff35f4d --- /dev/null +++ b/git_quality_check/indicators/commits/does_contain_bad_words.py @@ -0,0 +1,9 @@ +def does_contain_bad_words(bad_words: list[str]): + def _does_contain_bad_words(log: str): + counter = 0 + for word in bad_words: + if word.lower() in log.lower().split(): + counter += 1 + return 1 if counter > 0 else 0 + + return _does_contain_bad_words diff --git a/git_quality_check/indicators/counters/count_coupled.py b/git_quality_check/indicators/counters/count_coupled.py index 0b7b665..4a7f06c 100644 --- a/git_quality_check/indicators/counters/count_coupled.py +++ b/git_quality_check/indicators/counters/count_coupled.py @@ -1,4 +1,4 @@ -from git_quality_check.utils import sample, are_coupled +from git_quality_check.utils import sample, contains def count_coupled(branches, main_branches): @@ -6,7 +6,11 @@ def count_coupled(branches, main_branches): branches.extend(main_branches) count += len(main_branches) counter = 0 + max_counter = 0 for bA in branches: for bB in branches: - counter += 1 if are_coupled(bA, bB) else 0 - return counter / count * 100 + if not bA == bB: + print(bA, bB, contains(bA, bB)) + counter += 1 if contains(bA, bB) else 0 + max_counter += 1 + return counter / max_counter * 100 diff --git a/git_quality_check/indicators/counters/process_logs.py b/git_quality_check/indicators/counters/process_logs.py index b8963bf..1bef944 100644 --- a/git_quality_check/indicators/counters/process_logs.py +++ b/git_quality_check/indicators/counters/process_logs.py @@ -4,5 +4,6 @@ def process_logs(logs: list[str], functions: list[str:int]): for i in range(len(logs)): log = logs[i] for function in functions: + # a same commit can be marked twice if it failes two functions counter += function(log) return counter / count * 100 diff --git a/git_quality_check/utils/__init__.py b/git_quality_check/utils/__init__.py index 808a56d..1ee9f34 100644 --- a/git_quality_check/utils/__init__.py +++ b/git_quality_check/utils/__init__.py @@ -2,9 +2,16 @@ is_valid_log, remove_header, is_old, - are_coupled, + contains, git_logs, git_all_branches, ) -from .common import sample, set_output, format_number, parse_inputs +from .common import ( + sample, + set_output, + format_number, + parse_inputs, + BADWORDS, + MAINBRANCHES, +) diff --git a/git_quality_check/utils/common.py b/git_quality_check/utils/common.py index 68a2ec4..3a77930 100644 --- a/git_quality_check/utils/common.py +++ b/git_quality_check/utils/common.py @@ -1,17 +1,26 @@ import random import os -from datetime import datetime +from datetime import date, datetime + + +BADWORDS = "INPUT_BADWORDS" +MAINBRANCHES = "INPUT_MAINBRANCHES" def parse_inputs(): + """ + " Gets inputs from environment variables: + " - bad_words: A list of words that should be avoided in commit messages. + " - main_branches: The list of main branches (e.g. master, main or develop) + """ bad_words = [] main_branches = [] try: - bad_words = os.environ["INPUT_BADWORDS"].split(", ") + bad_words = os.environ[BADWORDS].split(", ") except: bad_words = ["WIP", "work in progress", "in progress", "TODO"] try: - main_branches = os.environ["INPUT_MAINBRANCHES"].split(", ") + main_branches = os.environ[MAINBRANCHES].split(", ") except: main_branches = ["origin/develop", "origin/master"] return bad_words, main_branches @@ -25,11 +34,22 @@ def get_date(): return datetime.today() -def sample(li: list[str], min: int): +def sample(li: list[str], n: int): + """ + " Computes a sample of size 'n' from a list: + " - if the size of the list is equal or lower than n, returns the list + " - if the size of the list is greater than n, returns a sample of n. + " (some elements might be repetead) + " + " li: the list + " n: expected number of elements in the sample + " + " Returns the tuple (sample, ) + """ count = len(li) - if count > min: - li = random.sample(li, min) - count = min + if count > n: + li = random.sample(li, n) + count = n return (li, count) @@ -41,5 +61,5 @@ def set_output(output: str): print(f"::set-output name=score::{output}") -def diff_month(d1, d2): - return (d1.year - d2.year) * 12 + d1.month - d2.month +def diff_month(d1: date, d2: date): + return abs((d1.year - d2.year) * 12 + d1.month - d2.month) diff --git a/git_quality_check/utils/git.py b/git_quality_check/utils/git.py index 447fb5a..a95d85a 100644 --- a/git_quality_check/utils/git.py +++ b/git_quality_check/utils/git.py @@ -1,4 +1,5 @@ import subprocess +import sys from datetime import datetime from .common import get_date, diff_month, strip @@ -30,7 +31,7 @@ def git_all_branches(): return [strip(r) for r in ret if not strip(r) == ""] -def are_coupled(branchA: str, branchB: str): +def contains(branchA: str, branchB: str): if not is_well_formed_branch(branchA) or not is_well_formed_branch(branchB): return False if branchA == branchB: @@ -53,7 +54,7 @@ def is_well_formed_branch(branch: str): def git_get_branch_date(branch: str): if not is_well_formed_branch(branch): return None - ret = run_git(["log", "-n", "1", '--date=format:"%Y-%m-%d"', branch]).split( + ret = run_git(["log", "-n", "1", '--date=format:"%Y-%m-%d"', branch, "-r"]).split( "Date: " )[1] ret = ret.replace('"', "").split("-") @@ -64,7 +65,7 @@ def git_get_branch_date(branch: str): def remove_first_line(log: str): - if is_valid_log: + if is_valid_log(log): try: eol = log.index("\n") log = log[eol + 1 :] diff --git a/tests/indicators/commits/test_count_bad_words.py b/tests/indicators/commits/test_count_bad_words.py deleted file mode 100644 index b12774c..0000000 --- a/tests/indicators/commits/test_count_bad_words.py +++ /dev/null @@ -1,5 +0,0 @@ -import pytest - - -def test(): - assert True == False diff --git a/tests/indicators/commits/test_does_contain_bad_words.py b/tests/indicators/commits/test_does_contain_bad_words.py new file mode 100644 index 0000000..963e6f7 --- /dev/null +++ b/tests/indicators/commits/test_does_contain_bad_words.py @@ -0,0 +1,10 @@ +from git_quality_check.indicators.commits import does_contain_bad_words + + +def test(): + bad_words = ["bad", "word"] + counter = does_contain_bad_words(bad_words) + log = "I am a very bad bad word" + assert counter(log) == 1 + log = "I am a good log" + assert counter(log) == 0 diff --git a/tests/indicators/commits/test_is_empty_body.py b/tests/indicators/commits/test_is_empty_body.py index b12774c..9ca8492 100644 --- a/tests/indicators/commits/test_is_empty_body.py +++ b/tests/indicators/commits/test_is_empty_body.py @@ -1,5 +1,15 @@ import pytest +from git_quality_check.indicators.commits import is_empty_body -def test(): - assert True == False +@pytest.mark.parametrize("log", ["", "commit 1234455"]) +def test_is_empty_body(log): + assert is_empty_body(log) == 1 + + +def test_is_not_empty_body(): + log = """commit 7a1e2a6a76e6967bde14e95900996ca17811de47 (origin/gc/pytest) + Author: gcattan + Date: 2022-04-17 + remove dependencies""" + assert is_empty_body(log) == 0 diff --git a/tests/indicators/commits/test_is_test_commit.py b/tests/indicators/commits/test_is_test_commit.py index b12774c..0696f45 100644 --- a/tests/indicators/commits/test_is_test_commit.py +++ b/tests/indicators/commits/test_is_test_commit.py @@ -1,5 +1,12 @@ import pytest +from git_quality_check.indicators.commits import is_test_commit -def test(): - assert True == False +@pytest.mark.parametrize("log", ["test", "testing", "test it", "I am testing it"]) +def test_is_test_commit(log): + assert is_test_commit(log) == 1 + + +def test_is_not_test_commit(): + log = """Tset""" + assert is_test_commit(log) == 0 diff --git a/tests/indicators/commits/test_not_a_squashed_commit.py b/tests/indicators/commits/test_not_a_squashed_commit.py index b12774c..d6c49d9 100644 --- a/tests/indicators/commits/test_not_a_squashed_commit.py +++ b/tests/indicators/commits/test_not_a_squashed_commit.py @@ -1,5 +1,6 @@ -import pytest +from git_quality_check.indicators.commits import not_a_squashed_commit def test(): - assert True == False + assert not_a_squashed_commit("PR(#7)") == False + assert not_a_squashed_commit("I am not squased") == True diff --git a/tests/indicators/counters/test_count_coupled.py b/tests/indicators/counters/test_count_coupled.py index b12774c..ec9b221 100644 --- a/tests/indicators/counters/test_count_coupled.py +++ b/tests/indicators/counters/test_count_coupled.py @@ -1,5 +1,6 @@ -import pytest +from git_quality_check.indicators.counters import count_coupled def test(): - assert True == False + assert count_coupled(["origin/update-readme"], ["origin/master"]) == 50 + assert count_coupled(["origin/test-action"], ["origin/formatter"]) == 0 diff --git a/tests/indicators/counters/test_count_old_branches.py b/tests/indicators/counters/test_count_old_branches.py index b12774c..ef22697 100644 --- a/tests/indicators/counters/test_count_old_branches.py +++ b/tests/indicators/counters/test_count_old_branches.py @@ -1,5 +1,5 @@ -import pytest +from git_quality_check.indicators.counters import count_old_branches def test(): - assert True == False + assert count_old_branches(["origin/gc/pytest_utils", "origin/update-readme"]) == 50 diff --git a/tests/indicators/counters/test_process_logs.py b/tests/indicators/counters/test_process_logs.py index b12774c..bfb79f5 100644 --- a/tests/indicators/counters/test_process_logs.py +++ b/tests/indicators/counters/test_process_logs.py @@ -1,5 +1,18 @@ -import pytest +from git_quality_check.indicators.counters import process_logs +from git_quality_check.indicators.commits import not_a_squashed_commit, is_empty_body def test(): - assert True == False + logs = [ + """Commit 1234 + Author: + Date: + Test""", + """Commit 1234 + Author: + Date: + Merge (#5)""", + "", + ] + marked_commits = process_logs(logs, [not_a_squashed_commit, is_empty_body]) + assert marked_commits == 50 diff --git a/tests/scoring/test_overall.py b/tests/scoring/test_overall.py index b12774c..a4fad7f 100644 --- a/tests/scoring/test_overall.py +++ b/tests/scoring/test_overall.py @@ -1,5 +1,6 @@ -import pytest +from git_quality_check.scoring import compute_score def test(): - assert True == False + assert compute_score(100, 0, 100, 100) == 0 + assert compute_score(0, 100, 0, 0) == 100 diff --git a/tests/utils/test_common.py b/tests/utils/test_common.py index b12774c..225a94c 100644 --- a/tests/utils/test_common.py +++ b/tests/utils/test_common.py @@ -1,5 +1,53 @@ -import pytest +import os +from git_quality_check.utils.common import ( + BADWORDS, + MAINBRANCHES, + parse_inputs, + sample, + diff_month, +) +from datetime import datetime -def test(): - assert True == False +def test_parse_inputs(): + os.environ[BADWORDS] = "WIP, todo" + os.environ[MAINBRANCHES] = "origin/develop, origin/main" + bad_words, main_branches = parse_inputs() + assert bad_words[0] == "WIP" + assert bad_words[1] == "todo" + assert main_branches[0] == "origin/develop" + assert main_branches[1] == "origin/main" + + +def test_sample_same_size_than_list(): + li = [3, 10, 6, 15, 20] + size = len(li) + sample_list, count = sample(li, size) + assert count == size + for i in range(size): + assert li[i] == sample_list[i] + + +def test_sample_greater_size_than_list(): + li = [3, 10, 6, 15, 20] + size = len(li) + sample_list, count = sample(li, size + 1) + assert count == size + for i in range(size): + assert li[i] == sample_list[i] + + +def test_sample_lower_size_than_list(): + li = [3, 10, 6, 15, 20] + size = len(li) - 1 + sample_list, count = sample(li, size) + assert count == size + for i in range(size): + assert sample_list[i] in li + + +def test_diff_months(): + date1 = datetime(2022, 8, 1) + date2 = datetime(2023, 8, 1) + assert diff_month(date1, date2) == 12 + assert diff_month(date2, date1) == 12 diff --git a/tests/utils/test_git.py b/tests/utils/test_git.py index b12774c..f1764c3 100644 --- a/tests/utils/test_git.py +++ b/tests/utils/test_git.py @@ -1,5 +1,46 @@ import pytest +from datetime import datetime +from git_quality_check.utils.git import ( + is_old, + git_all_branches, + contains, + git_get_branch_date, + remove_first_line, +) -def test(): - assert True == False +def test_branch_is_old(): + assert is_old("origin/gc/pytest") == True + assert is_old("origin/gc/pytest_utils") == False + + +def test_branch_contains(): + assert contains("origin/gc/test-action", "origin/gc/pytest") == False + assert contains("origin/formatter", "origin/master") == True + assert contains("origin/master", "origin/formatter") == False + + +def test_git_all_branches(): + branches = git_all_branches() + assert len(branches) > 0 + assert "origin/master" in branches + + +def test_git_get_branch_date(): + date = git_get_branch_date("origin/formatter") + assert date == datetime(2022, 4, 1) + + +def test_remove_first_line(): + commit = """commit 7a1e2a6a76e6967bde14e95900996ca17811de47 (origin/gc/pytest) + Author: gcattan + Date: 2022-04-17 + remove dependencies""" + commit = remove_first_line(commit) + commit = remove_first_line(commit) + commit = remove_first_line(commit) + assert commit == " remove dependencies" + commit = remove_first_line(commit) + assert commit == "" + commit = remove_first_line(commit) + assert commit == ""