Skip to content

Commit

Permalink
Create MUT infra (#71)
Browse files Browse the repository at this point in the history
Set up infra to run MUT on the CLI source code.
  • Loading branch information
kiransingh99 authored Feb 1, 2023
1 parent 7496dd1 commit 0911e0a
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 129 deletions.
24 changes: 20 additions & 4 deletions .github/workflows/push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ jobs:
steps:
- name: Pull git workspace
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand All @@ -23,10 +21,20 @@ jobs:
steps:
- name: Pull git workspace
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
- name: Check branch name
run: python tools/branch_name.py
code-coverage:
name: Code coverage
runs-on: ubuntu-latest
steps:
- name: Pull git workspace
uses: actions/checkout@v3
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install coverage
- name: Run code coverage
run: tools/mut.py -c
copyright:
name: Copyright notice
runs-on: ubuntu-latest
Expand All @@ -46,6 +54,14 @@ jobs:
uses: actions/checkout@v3
- name: Check for fail commit strings
run: tools/fc_check
mut:
name: MUT
runs-on: ubuntu-latest
steps:
- name: Pull git workspace
uses: actions/checkout@v3
- name: Run MUT
run: tools/mut.py
mypy:
name: Mypy
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ The following packages are required for the mainline code to run:

The following packages are required for the associated infra to run:
* black
* coverage
* mypy
* pylint
* unittest

## Get involved
We'd love to hear your thoughts on this project, and if you have any suggestions or feature requests, or even want to get involved, please do!
Expand Down
Empty file added test/mut/__init__.py
Empty file.
18 changes: 18 additions & 0 deletions test/mut/_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# ------------------------------------------------------------------------------
# _util.py - MUT utilities
#
# February 2023, Gurkiran Singh
#
# Copyright (c) 2023
# All rights reserved.
# ------------------------------------------------------------------------------

"""Utilities for Gurbani Analysis MUT."""

from __future__ import annotations

import unittest


class BaseTest(unittest.TestCase):
"""Base test for all MUT cases."""
10 changes: 3 additions & 7 deletions tools/autoformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,10 @@ class _AutoformatReturnCodes(cmn.ReturnCodes):


def _run_black(args: argparse.Namespace) -> int:
"""
Runs `black` command based on input parameters.
:param args:
Namespace object with args to run black with.
"""Runs `black` command based on input parameters.
:return:
Return code.
:param args: namespace object with args to run black with.
:return: return code.
"""

cmd = [cmn.which_python(), "-m", "black"]
Expand Down
17 changes: 8 additions & 9 deletions tools/branch_name.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ class _BranchNameReturnCodes(cmn.ReturnCodes):


def _check_branch_name_validity(branch_name: str) -> int:
"""
Checks the branch name and handles the following cases by outputting and
"""Checks the branch name and handles the following cases by outputting and
setting an error code:
- valid
- still on main
- invalid (non-main)
:param branch_name: the branch name to test.
:return: return code indicating validity.
"""

if re.fullmatch(r"GA\d+\.[A-Za-z0-9-_.]+", branch_name):
Expand Down Expand Up @@ -76,11 +78,9 @@ def _check_branch_name_validity(branch_name: str) -> int:


def _get_current_branch() -> str:
"""
Queries git to get the current branch name.
"""Queries git to get the current branch name.
:return:
The current branch name
:return: the current branch name.
"""
cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
try:
Expand All @@ -89,9 +89,8 @@ def _get_current_branch() -> str:
print("\nERROR")
print(f"Exception raised running `{' '.join(cmd)}`:")
raise
else:
branch_name = output.stdout.decode("utf-8").strip("\n")
return branch_name
branch_name = output.stdout.decode("utf-8").strip("\n")
return branch_name


def main() -> None:
Expand Down
104 changes: 44 additions & 60 deletions tools/cmn.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,21 @@ class ReturnCodes(enum.IntEnum):

@classmethod
def get_error_codes(cls) -> list[int]:
"""Returns a list of all the known error code values."""
"""Returns a list of all the known error code values.
:return: a list of all the error code values.
"""
return [error.value for error in cls]

@classmethod
def is_ok(cls, value: int) -> bool:
"""A return code is okay if it is 0.
:return: True if the return code does not imply any issues. False
otherwise.
"""
return value == 0


class WinErrorCodes(ReturnCodes):
"""WINError codes."""
Expand All @@ -48,18 +60,14 @@ class WinErrorCodes(ReturnCodes):
def get_all_code_files(
untracked_files: bool = False, root_dir: str = "."
) -> set[str]:
"""
Produces a set of source code files tracked by git, in the given directory.
:param untracked_files:
If True, files not tracked by git will be included in the search.
Eefaults to False.
:param root_dir:
All files must be a subdirectory of this one. Relative to root of ws.
:return:
A set of files filtered with the given settings.
"""Produces a set of source code files tracked by git, in the given
directory.
:param untracked_files: if True, files not tracked by git will be included
in the search. Defaults to False.
:param root_dir: all files must be a subdirectory of this one. Relative to
root of ws.
:return: a set of files filtered with the given settings.
"""

exclude_patterns = {
Expand Down Expand Up @@ -111,18 +119,13 @@ def get_all_code_files(
def get_python_files(
untracked_files: bool = False, root_dir: str = "."
) -> set[str]:
"""
Produces a set of python files in the given directory.
:param untracked_files:
If True, files not tracked by git will be included in the search.
Eefaults to False.
:param root_dir:
All files must be a subdirectory of this one. Relative to root of ws.
"""Produces a set of python files in the given directory.
:return:
A set of python files filtered with the given settings.
:param untracked_files: if True, files not tracked by git will be included
in the search. Defaults to False.
:param root_dir: all files must be a subdirectory of this one. Relative to
root of ws.
:return: a set of python files filtered with the given settings.
"""
unfiltered = get_all_code_files(untracked_files, root_dir)

Expand All @@ -140,28 +143,17 @@ def handle_cli_error(
cmd: Iterable[str],
exc: Optional[Exception] = None,
) -> None:
"""Handles the exception raised due to a failure occurring when running a
CLI. If there's an unknown error code, an error message is printed to log
this. The script then exits with the same return code as received from the
CLI.
:param known_return_codes: enum containing all known error codes.
:param return_code: return code of the failed CLI.
:param cmd: the command ran, as a list of each argument of the command.
:param exc: the original exception, which will get reraised.
:raises Exception: if `exc` was provided, it gets raised again.
"""
Handles the exception raised due to a failure occurring when running a CLI.
If there's an unknown error code, an error message is printed to
log this. The script then exits with the same return code as received from
the CLI.
:param known_return_codes:
Enum containing all known error codes.
:param return_code:
Return code of the failed CLI.
:param cmd:
The command ran, as a list of each argument of the command.
:param exc:
The original exception, which will get reraised.
:raises Exception:
If `exc` was provided, it gets raised again.
"""

if return_code not in known_return_codes.get_error_codes():
print(
"\nAn unexpected error occurred when running the command:"
Expand All @@ -174,24 +166,18 @@ def handle_cli_error(


def handle_missing_package_error(package: str) -> None:
"""
Handler for when a package is missing.
"""Handler for when a package is missing.
:param package:
Name of missing package.
:param package: name of missing package.
"""
print(f"Failed running command. Check `{package}` is installed.")


def month_name_from_num(index: int) -> str:
"""
Converts a number into the corresponding month.
"""Converts a number into the corresponding month.
:param index:
Number between 1 and 12.
:return:
Month name corresponding to input.
:param index: number between 1 and 12.
:return: month name corresponding to input.
"""
months = [
"January",
Expand All @@ -212,11 +198,9 @@ def month_name_from_num(index: int) -> str:


def which_python() -> str:
"""
Determine how to invoke python 3.
"""Determine how to invoke python 3.
:return:
Correct invocation of python on the running computer.
:return: correct invocation of python on the running computer.
"""
options = ["python3", "python"]

Expand Down
62 changes: 29 additions & 33 deletions tools/copyright.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ class _CopyrightCheckReturnCodes(cmn.ReturnCodes):

@dataclass(frozen=True)
class _LineMatchInfo:
"""
Structure to hold data about a given line of a copyright notice.
"""Structure to hold data about a given line of a copyright notice.
Tracks the line number of the notice, the actual line read in from the file
being tested, and the expected regex rule for the line.
Expand All @@ -59,14 +58,10 @@ class _LineMatchInfo:


def _generate_expected(file_path: str) -> Generator[str, None, None]:
"""
Generator that outputs each line of a correctly formatted copyright notice.
:param file_path:
Path to the file to generate an copyright notice for.
"""Generator that outputs each line of a correctly formatted copyright notice.
:yield:
Regex for a copyright notice, output line-by-line.
:param file_path: path to the file to generate an copyright notice for.
:yield: regex for a copyright notice, output line-by-line.
"""
file_name = file_path.split("/")[-1]
notice_start_end = r"^# [-]{78}$"
Expand All @@ -82,35 +77,36 @@ def _generate_expected(file_path: str) -> Generator[str, None, None]:
.strip()
.split("\n")
)
created_month = cmn.month_name_from_num(int(edit_dates[-1][5:7]))
created_year = edit_dates[-1][:4]
last_edited_year = edit_dates[0][:4]
if created_year == last_edited_year:
date_range = created_year
else:
date_range = created_year + " - " + last_edited_year

exp = [
notice_start_end,
rf"^# {file_name} - .+$",
blank_line,
rf"^# {created_month} {created_year}, [A-Za-z -]+$",
blank_line,
rf"^# Copyright \(c\) {date_range}$",
r"^# All rights reserved.$",
notice_start_end,
]

for line in exp:
yield line
if edit_dates[0]:
created_month = cmn.month_name_from_num(int(edit_dates[-1][5:7]))
created_year = edit_dates[-1][:4]
last_edited_year = edit_dates[0][:4]
if created_year == last_edited_year:
date_range = created_year
else:
date_range = created_year + " - " + last_edited_year

exp = [
notice_start_end,
rf"^# {file_name} - .+$",
blank_line,
rf"^# {created_month} {created_year}, [A-Za-z -]+$",
blank_line,
rf"^# Copyright \(c\) {date_range}$",
r"^# All rights reserved.$",
notice_start_end,
]

for line in exp:
yield line


def _run_check(args: argparse.Namespace) -> int:
"""
Runs copyright checks on input parameters.
"""Runs copyright checks on input parameters.
:param args:
Namespace object with args to run check on.
:param args: namespace object with args to run check on.
:return: return code from CLI
"""
failed = {}
line_number = 0
Expand Down
Loading

0 comments on commit 0911e0a

Please sign in to comment.