diff --git a/.github/workflows/CompletePipeline.yml b/.github/workflows/CompletePipeline.yml index 94ffd4f..7a65928 100644 --- a/.github/workflows/CompletePipeline.yml +++ b/.github/workflows/CompletePipeline.yml @@ -135,13 +135,16 @@ jobs: UnitTesting: uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@dev needs: + - ConfigParams - UnitTestingParams with: jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} requirements: "-r tests/unit/requirements.txt" # pacboy: "msys/git python-lxml:p" - unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} - coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} + unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }} + unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }} + unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} + coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} StaticTypeCheck: uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@dev diff --git a/.github/workflows/ExtractConfiguration.yml b/.github/workflows/ExtractConfiguration.yml index cf23938..2299ab3 100644 --- a/.github/workflows/ExtractConfiguration.yml +++ b/.github/workflows/ExtractConfiguration.yml @@ -59,18 +59,33 @@ on: mypy_prepare_command: description: "" value: ${{ jobs.Extract.outputs.mypy_prepare_command }} + unittest_report_xml_directory: + description: "" + value: ${{ jobs.Extract.outputs.unittest_report_xml_directory }} + unittest_report_xml_filename: + description: "" + value: ${{ jobs.Extract.outputs.unittest_report_xml_filename }} + unittest_report_xml: + description: "" + value: ${{ jobs.Extract.outputs.unittest_report_xml }} coverage_report_html_directory: description: "" value: ${{ jobs.Extract.outputs.coverage_report_html_directory }} coverage_report_xml_directory: description: "" value: ${{ jobs.Extract.outputs.coverage_report_xml_directory }} + coverage_report_xml_filename: + description: "" + value: ${{ jobs.Extract.outputs.coverage_report_xml_filename }} coverage_report_xml: description: "" value: ${{ jobs.Extract.outputs.coverage_report_xml }} coverage_report_json_directory: description: "" value: ${{ jobs.Extract.outputs.coverage_report_json_directory }} + coverage_report_json_filename: + description: "" + value: ${{ jobs.Extract.outputs.coverage_report_json_filename }} coverage_report_json: description: "" value: ${{ jobs.Extract.outputs.coverage_report_json }} @@ -83,10 +98,15 @@ jobs: package_fullname: ${{ steps.getPackageName.outputs.package_fullname }} package_directory: ${{ steps.getPackageName.outputs.package_directory }} mypy_prepare_command: ${{ steps.getPackageName.outputs.mypy_prepare_command }} + unittest_report_xml_directory: ${{ steps.getVariables.outputs.unittest_report_xml_directory }} + unittest_report_xml_filename: ${{ steps.getVariables.outputs.unittest_report_xml_filename }} + unittest_report_xml: ${{ steps.getVariables.outputs.unittest_report_xml }} coverage_report_html_directory: ${{ steps.getVariables.outputs.coverage_report_html_directory }} coverage_report_xml_directory: ${{ steps.getVariables.outputs.coverage_report_xml_directory }} + coverage_report_xml_filename: ${{ steps.getVariables.outputs.coverage_report_xml_filename }} coverage_report_xml: ${{ steps.getVariables.outputs.coverage_report_xml }} coverage_report_json_directory: ${{ steps.getVariables.outputs.coverage_report_json_directory }} + coverage_report_json_filename: ${{ steps.getVariables.outputs.coverage_report_json_filename }} coverage_report_json: ${{ steps.getVariables.outputs.coverage_report_json }} steps: @@ -144,10 +164,11 @@ jobs: from tomli import load as tomli_load - htmlDirectory = Path("htmlcov") - xmlFile = Path("./coverage.xml") - jsonFile = Path("./coverage.json") - coverageRC = "${{ inputs.coverage_config }}".strip() + coverageHTMLDirectory = Path("htmlcov") + unittestXMLFile = Path("./unittest.xml") + coverageXMLFile = Path("./coverage.xml") + coverageJSONFile = Path("./coverage.json") + coverageRC = "${{ inputs.coverage_config }}".strip() # Read output paths from 'pyproject.toml' file if coverageRC == "pyproject.toml": @@ -156,9 +177,10 @@ jobs: with pyProjectFile.open("rb") as file: pyProjectSettings = tomli_load(file) - htmlDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"]) - xmlFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"]) - jsonFile = Path(pyProjectSettings["tool"]["coverage"]["json"]["output"]) + unittestXMLFile = Path(pyProjectSettings["tool"]["pytest"]["xml_path"]) + coverageHTMLDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"]) + coverageXMLFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"]) + coverageJSONFile= Path(pyProjectSettings["tool"]["coverage"]["json"]["output"]) else: print(f"File '{pyProjectFile}' not found.") print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.") @@ -171,9 +193,9 @@ jobs: with coverageRCFile.open("rb") as file: coverageRCSettings = tomli_load(file) - htmlDirectory = Path(coverageRCSettings["html"]["directory"]) - xmlFile = Path(coverageRCSettings["xml"]["output"]) - jsonFile = Path(coverageRCSettings["json"]["output"]) + coverageHTMLDirectory = Path(coverageRCSettings["html"]["directory"]) + coverageXMLFile = Path(coverageRCSettings["xml"]["output"]) + coverageJSONFile = Path(coverageRCSettings["json"]["output"]) else: print(f"File '{coverageRCFile}' not found.") print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.") @@ -184,11 +206,16 @@ jobs: print(f"GITHUB_OUTPUT: {github_output}") with github_output.open("a+", encoding="utf-8") as f: f.write(dedent(f"""\ - coverage_report_html_directory={htmlDirectory.as_posix()} - coverage_report_xml_directory={xmlFile.parent.as_posix()} - coverage_report_xml={xmlFile.as_posix()} - coverage_report_json_directory={jsonFile.parent.as_posix()} - coverage_report_json={jsonFile.as_posix()} + coverage_report_html_directory={coverageHTMLDirectory.as_posix()} + unittest_report_xml_directory={unittestXMLFile.parent.as_posix()} + unittest_report_xml_filename={unittestXMLFile.name} + unittest_report_xml={unittestXMLFile.as_posix()} + coverage_report_xml_directory={coverageXMLFile.parent.as_posix()} + coverage_report_xml_filename={coverageXMLFile.name} + coverage_report_xml={coverageXMLFile.as_posix()} + coverage_report_json_directory={coverageJSONFile.parent.as_posix()} + coverage_report_json_filename={coverageJSONFile.name} + coverage_report_json={coverageJSONFile.as_posix()} """)) - print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}\n json={jsonFile}") + print(f"DEBUG:\n unittest xml={unittestXMLFile}\n coverage html={coverageHTMLDirectory}\n coverage xml={coverageXMLFile}\n coverage json={coverageJSONFile}") diff --git a/.github/workflows/UnitTesting.yml b/.github/workflows/UnitTesting.yml index ed57f5c..0b2c189 100644 --- a/.github/workflows/UnitTesting.yml +++ b/.github/workflows/UnitTesting.yml @@ -94,6 +94,16 @@ on: required: false default: 'unit' type: string + unittest_report_xml_directory: + description: 'Path where to save the unittest summary report XML.' + required: false + default: 'report/unit' + type: string + unittest_report_xml_filename: + description: 'Filename of the unittest summary report XML.' + required: false + default: 'TestReportSummary.xml' + type: string coverage_config: description: 'Path to the .coveragerc file. Use pyproject.toml by default.' required: false @@ -323,69 +333,6 @@ jobs: if: matrix.system == 'msys2' && matrix.runtime == 'UCRT64' && inputs.ucrt64_before_script != '' run: ${{ inputs.ucrt64_before_script }} -# Read pyproject.toml - - - name: 🔁 Extract configurations from pyproject.toml - id: getVariables - shell: python - run: | - from os import getenv - from pathlib import Path - from sys import version - from textwrap import dedent - - print(f"Python: {version}") - - from tomli import load as tomli_load - - htmlDirectory = Path("htmlcov") - xmlFile = Path("./coverage.xml") - jsonFile = Path("./coverage.json") - coverageRC = "${{ inputs.coverage_config }}".strip() - - # Read output paths from 'pyproject.toml' file - if coverageRC == "pyproject.toml": - pyProjectFile = Path("pyproject.toml") - if pyProjectFile.exists(): - with pyProjectFile.open("rb") as file: - pyProjectSettings = tomli_load(file) - - htmlDirectory = Path(pyProjectSettings["tool"]["coverage"]["html"]["directory"]) - xmlFile = Path(pyProjectSettings["tool"]["coverage"]["xml"]["output"]) - jsonFile = Path(pyProjectSettings["tool"]["coverage"]["json"]["output"]) - else: - print(f"File '{pyProjectFile}' not found.") - print(f"::error title=FileNotFoundError::File '{pyProjectFile}' not found.") - exit(1) - - # Read output paths from '.coveragerc' file - elif len(coverageRC) > 0: - coverageRCFile = Path(coverageRC) - if coverageRCFile.exists(): - with coverageRCFile.open("rb") as file: - coverageRCSettings = tomli_load(file) - - htmlDirectory = Path(coverageRCSettings["html"]["directory"]) - xmlFile = Path(coverageRCSettings["xml"]["output"]) - jsonFile = Path(coverageRCSettings["json"]["output"]) - else: - print(f"File '{coverageRCFile}' not found.") - print(f"::error title=FileNotFoundError::File '{coverageRCFile}' not found.") - exit(1) - - # Write jobs to special file - github_output = Path(getenv("GITHUB_OUTPUT")) - print(f"GITHUB_OUTPUT: {github_output}") - with github_output.open("a+", encoding="utf-8") as f: - f.write(dedent(f"""\ - unittest_report_html_directory={htmlDirectory} - coverage_report_html_directory={htmlDirectory.as_posix()} - coverage_report_xml={xmlFile} - coverage_report_json={jsonFile} - """)) - - print(f"DEBUG:\n html={htmlDirectory}\n xml={xmlFile}\n json={jsonFile}") - # Run pytests - name: ✅ Run unit tests (Ubuntu/macOS) @@ -395,7 +342,7 @@ jobs: export PYTHONPATH=$(pwd) cd "${{ inputs.root_directory || '.' }}" - [ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=report/unit/TestReportSummary.xml' || unset PYTEST_ARGS + [ -n '${{ inputs.unittest_xml_artifact }}' ] && PYTEST_ARGS='--junitxml=${{ inputs.unittest_report_xml_directory }}/${{ inputs.unittest_report_xml_filename }}' || unset PYTEST_ARGS if [ -n '${{ inputs.coverage_config }}' ]; then printf "%s\n" "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}" coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }} @@ -411,7 +358,7 @@ jobs: $env:PYTHONPATH = (Get-Location).ToString() cd "${{ inputs.root_directory || '.' }}" - $PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=report/unit/TestReportSummary.xml" } else { "" } + $PYTEST_ARGS = if ("${{ inputs.unittest_xml_artifact }}") { "--junitxml=${{ inputs.unittest_report_xml_directory }}/${{ inputs.unittest_report_xml_filename }}" } else { "" } if ("${{ inputs.coverage_config }}") { Write-Host "coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }}" coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -raP $PYTEST_ARGS --color=yes ${{ inputs.tests_directory || '.' }}/${{ inputs.unittest_directory }} @@ -439,14 +386,14 @@ jobs: # Upload artifacts - - name: 📤 Upload 'TestReportSummary.xml' artifact + - name: 📤 Upload '${{ inputs.unittest_report_xml_filename }}' artifact if: inputs.unittest_xml_artifact != '' continue-on-error: true uses: pyTooling/upload-artifact@v4 with: name: ${{ inputs.unittest_xml_artifact }}-${{ matrix.system }}-${{ matrix.runtime }}-${{ matrix.python }} - working-directory: report/unit - path: TestReportSummary.xml + working-directory: ${{ inputs.unittest_report_xml_directory }} + path: ${{ inputs.unittest_report_xml_filename }} if-no-files-found: error retention-days: 1 diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 53ec96b..fb5762c 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -27,11 +27,14 @@ jobs: UnitTesting: uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@dev needs: + - ConfigParams - UnitTestingParams with: jobs: ${{ needs.UnitTestingParams.outputs.python_jobs }} - unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} - unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }} + unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }} + unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }} + unittest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_xml }} + unittest_html_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).unittesting_html }} # coverage_sqlite_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_sqlite }} # coverage_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_xml }} # coverage_json_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).codecoverage_json }} @@ -40,17 +43,20 @@ jobs: PlatformTesting: uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@dev needs: + - ConfigParams - PlatformTestingParams with: jobs: ${{ needs.PlatformTestingParams.outputs.python_jobs }} # tests_directory: "" unittest_directory: platform - unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }} - unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }} - coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }} - coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }} - coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }} - coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }} + unittest_report_xml_directory: ${{ needs.ConfigParams.outputs.unittest_report_xml_directory }} + unittest_report_xml_filename: ${{ needs.ConfigParams.outputs.unittest_report_xml_filename }} + unittest_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_xml }} + unittest_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).unittesting_html }} + coverage_sqlite_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_sqlite }} + coverage_xml_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_xml }} + coverage_json_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_json }} + coverage_html_artifact: ${{ fromJson(needs.PlatformTestingParams.outputs.artifact_names).codecoverage_html }} # Coverage: # uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@dev diff --git a/pyproject.toml b/pyproject.toml index f76c66f..ea7bdb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,9 @@ show_error_codes = true namespace_packages = true html_report = "report/typing" +[tool.pytest] +junit_xml = "report/unit/TestReportSummary.xml" + [tool.pytest.ini_options] addopts = "--tb=native" # Don't set 'python_classes = *' otherwise, pytest doesn't search for classes @@ -30,6 +33,7 @@ filterwarnings = [ "error::DeprecationWarning", "error::PendingDeprecationWarning" ] +junit_logging = "all" [tool.interrogate] color = true