-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
904c9fe
commit 8b3a264
Showing
13 changed files
with
1,024 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
name: Static checks and unit tests | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
test-linux: | ||
|
||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: [3.12] | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Update APT cache | ||
run: sudo apt-get update | ||
- name: Install graphviz for class and package diagrams | ||
run: sudo apt-get install graphviz | ||
- name: Set up Python ${{ matrix.python-version }} | ||
uses: actions/setup-python@v2 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
python -m pip install wheel | ||
pip install -r requirements/test.txt | ||
- name: Static type checking with mypy | ||
run: python dev-tools/dev_tools.py --type | ||
- name: Lint with pylint | ||
run: python dev-tools/dev_tools.py --lint --keep-results | ||
- name: Generate class and package diagrams | ||
run: | | ||
python dev-tools/dev_tools.py --diagram --keep-results | ||
- name: Test with pytest | ||
run: python dev-tools/dev_tools.py --unit "not manual and not windows and not tool" --keep-results | ||
- name: Upload test results and coverage reports | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: test-results-and-coverage-report-linux | ||
path: test-results | ||
# Use always() to always run this step to publish test results when there are test failures | ||
if: ${{ always() }} | ||
|
||
test-windows: | ||
|
||
runs-on: | ||
- self-hosted | ||
- spf-windows-gh-runners | ||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Remove old venv | ||
run: | | ||
if (Test-Path -LiteralPath venv) { | ||
Remove-Item -LiteralPath venv -Recurse | ||
} | ||
- name: Create virtual environment | ||
run: py -3.12 -m venv venv | ||
- name: Install dependencies | ||
run: | | ||
venv\Scripts\python -m pip install --upgrade pip | ||
venv\Scripts\python -m pip install wheel | ||
venv\Scripts\python -m pip install -r requirements/test.txt | ||
- name: Static type checking with mypy | ||
run: venv\Scripts\python dev-tools\dev_tools.py --type | ||
- name: Lint with pylint | ||
run: venv\Scripts\python dev-tools\dev_tools.py --lint --keep-results | ||
- name: Test with pytest | ||
run: venv\Scripts\python dev-tools\dev_tools.py --unit "not manual and not linux and not tool" --keep-results | ||
- name: Upload test results and coverage reports | ||
uses: actions/upload-artifact@v2 | ||
with: | ||
name: test-results-and-coverage-report-windows | ||
path: test-results |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
/venv/ | ||
/venv*/ | ||
/.eggs/ | ||
/.idea/ | ||
/build/ | ||
pytrnsys_process.egg-info/ | ||
__pycache__/ | ||
.coverage | ||
/test-results/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
#!/usr/bin/python3.12 | ||
|
||
"""Helper script for developers for running unit tests, static checkers and much more.""" | ||
|
||
# Run from top-level directory | ||
|
||
import argparse as ap | ||
import pathlib as pl | ||
import shutil as sh | ||
import subprocess as sp | ||
import sys | ||
import sysconfig as sc | ||
import time | ||
import typing as tp | ||
|
||
_SCRIPTS_DIR = pl.Path(sc.get_path("scripts")) | ||
|
||
_SOURCE_DIR_NAMES = ["pytrnsys_process", "tests", "dev-tools"] | ||
|
||
|
||
def main(): | ||
"""Run the tool""" | ||
|
||
arguments = _parse_arguments() | ||
|
||
test_results_dir_path = pl.Path("test-results") | ||
_prepare_test_results_directory(test_results_dir_path, arguments.shallKeepResults) | ||
|
||
_maybe_run_mypy(arguments) | ||
|
||
_maybe_run_pylint(arguments) | ||
|
||
_maybe_run_black(arguments) | ||
|
||
_maybe_create_diagrams(arguments) | ||
|
||
_maybe_run_pytest(arguments, test_results_dir_path) | ||
|
||
|
||
def _parse_arguments() -> ap.Namespace: | ||
parser = ap.ArgumentParser() | ||
|
||
parser.add_argument( | ||
"-k", | ||
"--keep-results", | ||
help="Don't clean test results", | ||
action="store_true", | ||
dest="shallKeepResults", | ||
) | ||
parser.add_argument( | ||
"-s", | ||
"--static-checks", | ||
help="Perform linting and type checking", | ||
action="store_true", | ||
dest="shallPerformStaticChecks", | ||
) | ||
parser.add_argument( | ||
"-l", "--lint", help="Perform linting", type=str, default=None, const="", nargs="?", dest="lintArguments" | ||
) | ||
parser.add_argument( | ||
"-b", | ||
"--black", | ||
help="Check formatting", | ||
type=str, | ||
default=None, | ||
const="--check", | ||
nargs="?", | ||
dest="blackArguments", | ||
) | ||
parser.add_argument( | ||
"-t", | ||
"--type", | ||
help="Perform type checking", | ||
type=str, | ||
default=None, | ||
const="", | ||
nargs="?", | ||
dest="mypyArguments", | ||
) | ||
parser.add_argument( | ||
"-u", | ||
"--unit", | ||
help="Perform unit tests", | ||
type=str, | ||
default=None, | ||
const="", | ||
nargs="?", | ||
dest="pytestMarkersExpression", | ||
) | ||
parser.add_argument( | ||
"-d", | ||
"--diagram", | ||
help="Create package and class diagrams", | ||
nargs="?", | ||
default=None, | ||
const="pdf", | ||
choices=["pdf", "dot"], | ||
dest="diagramsFormat", | ||
) | ||
parser.add_argument( | ||
"-a", | ||
"--all", | ||
help="Perform all checks", | ||
action="store_true", | ||
dest="shallRunAll", | ||
) | ||
arguments = parser.parse_args() | ||
return arguments | ||
|
||
|
||
def _prepare_test_results_directory(test_results_dir_path: pl.Path, shall_keep_results: bool) -> None: | ||
if test_results_dir_path.exists() and not test_results_dir_path.is_dir(): | ||
print("ERROR: `test-results` exists but is not a directory", file=sys.stderr) | ||
sys.exit(2) | ||
|
||
if not shall_keep_results and test_results_dir_path.is_dir(): | ||
sh.rmtree(test_results_dir_path) | ||
|
||
# Sometimes we need to give Windows a bit of time so that it can realize that | ||
# the directory is gone and it allows us to create it again. | ||
time.sleep(1) | ||
|
||
if not test_results_dir_path.is_dir(): | ||
test_results_dir_path.mkdir() | ||
|
||
|
||
def _maybe_run_mypy(arguments): | ||
if arguments.shallRunAll or arguments.shallPerformStaticChecks or arguments.mypyArguments is not None: | ||
cmd = _create_static_checker_command("mypy", "--show-error-codes") | ||
additional_args = arguments.mypyArguments or "" | ||
_print_and_run([*cmd, *additional_args.split()]) | ||
|
||
|
||
def _maybe_run_pylint(arguments): | ||
if arguments.shallRunAll or arguments.shallPerformStaticChecks or arguments.lintArguments is not None: | ||
cmd = _create_static_checker_command("pylint") | ||
additional_args = arguments.lintArguments or "" | ||
|
||
_print_and_run([*cmd, *additional_args.split()]) | ||
|
||
|
||
def _maybe_run_black(arguments): | ||
if arguments.shallRunAll or arguments.shallPerformStaticChecks or arguments.blackArguments is not None: | ||
cmd = _create_static_checker_command("black", "-l 120") | ||
additional_args = "--check" if arguments.blackArguments is None else arguments.blackArguments | ||
|
||
_print_and_run([*cmd, *additional_args.split()]) | ||
|
||
|
||
def _create_static_checker_command(static_checker_name: str, *args: str) -> tp.Sequence[str]: | ||
cmd = [ | ||
f"{_SCRIPTS_DIR / static_checker_name}", | ||
*args, | ||
*_SOURCE_DIR_NAMES, | ||
] | ||
return cmd | ||
|
||
|
||
def _maybe_create_diagrams(arguments): | ||
if arguments.shallRunAll or arguments.diagramsFormat: | ||
diagrams_format = arguments.diagramsFormat if arguments.diagramsFormat else "pdf" | ||
cmd = ( | ||
f"{_SCRIPTS_DIR / 'pyreverse'} -k -o {diagrams_format} -p process_pytrnsys -d test-results process_pytrnsys" | ||
) | ||
_print_and_run(cmd.split()) | ||
|
||
|
||
def _maybe_run_pytest(arguments, test_results_dir_path): | ||
was_called_without_arguments = ( | ||
not arguments.shallPerformStaticChecks | ||
and arguments.mypyArguments is None | ||
and arguments.lintArguments is None | ||
and arguments.blackArguments is None | ||
and arguments.diagramsFormat is None | ||
) | ||
if arguments.shallRunAll or arguments.pytestMarkersExpression is not None or was_called_without_arguments: | ||
_run_unit_tests_with_pytest(arguments, test_results_dir_path) | ||
_run_doctests_with_pytest() | ||
|
||
|
||
def _run_unit_tests_with_pytest(arguments, test_results_dir_path): | ||
marker_expressions = _get_marker_expressions(arguments.pytestMarkersExpression) | ||
additional_args = ["-m", marker_expressions] | ||
cmd = [ | ||
_SCRIPTS_DIR / "pytest", | ||
"-v", | ||
"--cov=process_pytrnsys", | ||
f"--cov-report=html:{test_results_dir_path / 'coverage-html'}", | ||
f"--cov-report=lcov:{test_results_dir_path / 'coverage.lcov'}", | ||
"--cov-report=term", | ||
f"--html={test_results_dir_path / 'report' / 'report.html'}", | ||
] | ||
args = [*cmd, *additional_args, "tests"] | ||
_print_and_run(args) | ||
|
||
|
||
def _run_doctests_with_pytest(): | ||
cmd = [_SCRIPTS_DIR / "pytest", "--doctest-modules"] | ||
_print_and_run(cmd) | ||
|
||
|
||
def _get_marker_expressions(user_supplied_marker_expressions: str) -> str: | ||
hard_coded_marker_expressions = "not tool" | ||
marker_expressions = ( | ||
f"({hard_coded_marker_expressions}) and ({user_supplied_marker_expressions})" | ||
if user_supplied_marker_expressions | ||
else hard_coded_marker_expressions | ||
) | ||
return marker_expressions | ||
|
||
|
||
def _print_and_run(args: tp.Sequence[str]) -> None: | ||
formatted_args = " ".join(str(arg) for arg in args) | ||
print(f"Running '{formatted_args}'...") | ||
sp.run(args, check=True) | ||
print("...DONE.") | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.