From 1d6c19a5c76ec34087ff5fda3a89ceef5cabcb87 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 8 Oct 2026 22:48:33 +0000 Subject: [PATCH 1/2] ApplicationTesting: download artifacts the application tests read ('download_artifacts') New inputs 'download_artifacts' - a glob of artifact names, empty by default - and 'artifacts_directory' - 'artifacts' by default. After the wheel is installed, the matching artifacts are downloaded, each into a subdirectory named after the artifact, so application tests can read what other jobs of the pipeline wrote, e.g. the reports of example jobs. '_Checking_JobTemplates.yml' uploads an example report from a new job 'ExampleReport' and lets 'AppTesting' download 'Example-*'; a new app test reads it, and skips where no artifacts were downloaded. Co-Authored-By: Patrick Lehmann --- .github/workflows/ApplicationTesting.yml | 17 +++++++++ .github/workflows/_Checking_JobTemplates.yml | 17 +++++++++ .../Testing/ApplicationTesting.rst | 37 ++++++++++++++++++- tests/app/AppTest.py | 12 ++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ApplicationTesting.yml b/.github/workflows/ApplicationTesting.yml index 381d5461..d4820df6 100644 --- a/.github/workflows/ApplicationTesting.yml +++ b/.github/workflows/ApplicationTesting.yml @@ -34,6 +34,16 @@ on: required: false default: '' type: string + download_artifacts: + description: 'Glob of the names of artifacts to download before the tests run, e.g. ''Example-*''.' + required: false + default: '' + type: string + artifacts_directory: + description: 'Directory the artifacts are downloaded into, each into a subdirectory named after the artifact.' + required: false + default: 'artifacts' + type: string apt: description: 'Ubuntu dependencies to be installed through apt.' required: false @@ -251,6 +261,13 @@ jobs: run: | python -m pip install -v --disable-pip-version-check (Get-Item .\install\*.whl).FullName + - name: 📥 Download artifacts '${{ inputs.download_artifacts }}' + if: inputs.download_artifacts != '' + uses: pyTooling/download-artifact@v8 + with: + pattern: ${{ inputs.download_artifacts }} + path: ${{ inputs.artifacts_directory }} + # Run pytests - name: ✅ Run application tests (Ubuntu/macOS) diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index 71857f79..ef985ea2 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -175,6 +175,21 @@ jobs: wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} package_name: ${{ needs.UnitTestingParams.outputs.package_fullname }} + ExampleReport: + name: 📄 Example report for the application tests + runs-on: ubuntu-26.04 + steps: + - name: 📄 Write an example report + run: | + mkdir -p report + printf -- '%s\n' 'Report of an example job.' > report/Example.txt + + - name: 📤 Upload the example report as artifact + uses: pyTooling/upload-artifact@v7 + with: + name: Example-Report + path: report/Example.txt + AppTesting: uses: pyTooling/Actions/.github/workflows/ApplicationTesting.yml@dev needs: @@ -182,9 +197,11 @@ jobs: - AppTestingParams - UnitTestingParams - Package + - ExampleReport with: jobs: ${{ needs.AppTestingParams.outputs.python_jobs }} wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} + download_artifacts: 'Example-*' apptest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} apptest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).apptesting_xml }} diff --git a/doc/JobTemplate/Testing/ApplicationTesting.rst b/doc/JobTemplate/Testing/ApplicationTesting.rst index 2dceb5b1..fddb8f38 100644 --- a/doc/JobTemplate/Testing/ApplicationTesting.rst +++ b/doc/JobTemplate/Testing/ApplicationTesting.rst @@ -41,13 +41,16 @@ Configuration options for :term:`pytest` should be given via section ``[tool.pyt :ref:`JOBTMPL/ApplicationTesting/Input/ubuntu_before_script`. 5. Download the Python package artifact containing the wheel (:ref:`JOBTMPL/ApplicationTesting/Input/wheel`). 6. Install the wheel using :term:`pip`. - 7. Run the application tests using :term:`pytest` (:ref:`JOBTMPL/ApplicationTesting/Input/apptest_directory`, + 7. Download further artifacts the tests read, if requested + (:ref:`JOBTMPL/ApplicationTesting/Input/download_artifacts`, + :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_directory`). + 8. Run the application tests using :term:`pytest` (:ref:`JOBTMPL/ApplicationTesting/Input/apptest_directory`, :ref:`JOBTMPL/ApplicationTesting/Input/tests_directory`, :ref:`JOBTMPL/ApplicationTesting/Input/root_directory`), in parallel if :file:`pytest-xdist` is installed (:ref:`JOBTMPL/ApplicationTesting/Input/parallel`). The environment variable ``GITHUB_TOKEN`` holds the job's token, so a program reading GitHub's REST API authenticates and isn't limited to 60 requests per hour per IP address. - 8. Upload the test report summary as an artifact (:ref:`JOBTMPL/ApplicationTesting/Input/apptest_xml_artifact`, + 9. Upload the test report summary as an artifact (:ref:`JOBTMPL/ApplicationTesting/Input/apptest_xml_artifact`, :ref:`JOBTMPL/ApplicationTesting/Input/apptest_report_xml`). .. note:: @@ -136,6 +139,10 @@ Parameter Summary +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ | :ref:`JOBTMPL/ApplicationTesting/Input/wheel` | no | string | ``''`` | +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ +| :ref:`JOBTMPL/ApplicationTesting/Input/download_artifacts` | no | string | ``''`` | ++-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ +| :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_directory` | no | string | ``'artifacts'`` | ++-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ | :ref:`JOBTMPL/ApplicationTesting/Input/apt` | no | string | ``''`` | +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ | :ref:`JOBTMPL/ApplicationTesting/Input/brew` | no | string | ``''`` | @@ -221,6 +228,32 @@ wheel Produced by :ref:`JOBTMPL/Package`. If empty, no package is downloaded and the tests run against whatever is installed in the environment. +.. _JOBTMPL/ApplicationTesting/Input/download_artifacts: + +download_artifacts +================== + +:Type: string +:Required: no +:Default Value: ``''`` +:Possible Values: A glob pattern of artifact names, e.g. ``'Example-*'``. An empty string disables the download. +:Description: Artifacts to download before the application tests run, e.g. the reports example jobs wrote, which the + tests read with the installed package. Each artifact is extracted into a subdirectory of + :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_directory` named after the artifact. |br| + The jobs uploading them must be listed in the calling job's ``needs``. + +.. _JOBTMPL/ApplicationTesting/Input/artifacts_directory: + +artifacts_directory +=================== + +:Type: string +:Required: no +:Default Value: ``'artifacts'`` +:Possible Values: Any valid directory path, relative to the repository's root. +:Description: Directory the artifacts of :ref:`JOBTMPL/ApplicationTesting/Input/download_artifacts` are downloaded + into. + .. _JOBTMPL/ApplicationTesting/Input/apt: apt diff --git a/tests/app/AppTest.py b/tests/app/AppTest.py index 14b051a9..25373658 100644 --- a/tests/app/AppTest.py +++ b/tests/app/AppTest.py @@ -30,6 +30,7 @@ # from importlib.metadata import version as get_version from json import loads as json_loads, JSONDecodeError +from pathlib import Path from re import IGNORECASE, MULTILINE from shutil import which from subprocess import CompletedProcess, run as subprocess_run, TimeoutExpired @@ -107,3 +108,14 @@ class Basic(Testcase): def test_NoArguments(self) -> None: result = self.RunEntrypoint() self.assertExitCode(result, 0) + + +class DownloadedArtifacts(TestCase): + """``ApplicationTesting.yml`` downloads the artifacts ``download_artifacts`` names into ``artifacts//``.""" + + def test_ExampleReport(self) -> None: + directory = Path("artifacts") + if not directory.exists(): + self.skipTest("No artifacts downloaded: the calling pipeline sets no 'download_artifacts'.") + + self.assertEqual("Report of an example job.\n", (directory / "Example-Report" / "Example.txt").read_text()) From 2aab8912cec8fd40d357f4562d68962534979f18 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 9 Oct 2026 11:47:51 +0000 Subject: [PATCH 2/2] ApplicationTesting: input 'download_artifacts' is named 'artifacts_pattern' Like 'unittest_artifacts_pattern' (PublishTestResults) and 'coverage_artifacts_pattern' (PublishCoverageResults). Co-Authored-By: Patrick Lehmann --- .github/workflows/ApplicationTesting.yml | 8 ++++---- .github/workflows/_Checking_JobTemplates.yml | 2 +- doc/JobTemplate/Testing/ApplicationTesting.rst | 12 ++++++------ tests/app/AppTest.py | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ApplicationTesting.yml b/.github/workflows/ApplicationTesting.yml index d4820df6..32e6e3e3 100644 --- a/.github/workflows/ApplicationTesting.yml +++ b/.github/workflows/ApplicationTesting.yml @@ -34,7 +34,7 @@ on: required: false default: '' type: string - download_artifacts: + artifacts_pattern: description: 'Glob of the names of artifacts to download before the tests run, e.g. ''Example-*''.' required: false default: '' @@ -261,11 +261,11 @@ jobs: run: | python -m pip install -v --disable-pip-version-check (Get-Item .\install\*.whl).FullName - - name: 📥 Download artifacts '${{ inputs.download_artifacts }}' - if: inputs.download_artifacts != '' + - name: 📥 Download artifacts '${{ inputs.artifacts_pattern }}' + if: inputs.artifacts_pattern != '' uses: pyTooling/download-artifact@v8 with: - pattern: ${{ inputs.download_artifacts }} + pattern: ${{ inputs.artifacts_pattern }} path: ${{ inputs.artifacts_directory }} # Run pytests diff --git a/.github/workflows/_Checking_JobTemplates.yml b/.github/workflows/_Checking_JobTemplates.yml index ef985ea2..d3839b6f 100644 --- a/.github/workflows/_Checking_JobTemplates.yml +++ b/.github/workflows/_Checking_JobTemplates.yml @@ -201,7 +201,7 @@ jobs: with: jobs: ${{ needs.AppTestingParams.outputs.python_jobs }} wheel: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).package_all }} - download_artifacts: 'Example-*' + artifacts_pattern: 'Example-*' apptest_report_xml: ${{ needs.ConfigParams.outputs.unittest_report_xml }} apptest_xml_artifact: ${{ fromJson(needs.UnitTestingParams.outputs.artifact_names).apptesting_xml }} diff --git a/doc/JobTemplate/Testing/ApplicationTesting.rst b/doc/JobTemplate/Testing/ApplicationTesting.rst index fddb8f38..0a03f8d4 100644 --- a/doc/JobTemplate/Testing/ApplicationTesting.rst +++ b/doc/JobTemplate/Testing/ApplicationTesting.rst @@ -42,7 +42,7 @@ Configuration options for :term:`pytest` should be given via section ``[tool.pyt 5. Download the Python package artifact containing the wheel (:ref:`JOBTMPL/ApplicationTesting/Input/wheel`). 6. Install the wheel using :term:`pip`. 7. Download further artifacts the tests read, if requested - (:ref:`JOBTMPL/ApplicationTesting/Input/download_artifacts`, + (:ref:`JOBTMPL/ApplicationTesting/Input/artifacts_pattern`, :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_directory`). 8. Run the application tests using :term:`pytest` (:ref:`JOBTMPL/ApplicationTesting/Input/apptest_directory`, :ref:`JOBTMPL/ApplicationTesting/Input/tests_directory`, @@ -139,7 +139,7 @@ Parameter Summary +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ | :ref:`JOBTMPL/ApplicationTesting/Input/wheel` | no | string | ``''`` | +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ -| :ref:`JOBTMPL/ApplicationTesting/Input/download_artifacts` | no | string | ``''`` | +| :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_pattern` | no | string | ``''`` | +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ | :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_directory` | no | string | ``'artifacts'`` | +-------------------------------------------------------------------+----------+---------------+------------------------------------------------------------------------------------------------------------------------------+ @@ -228,10 +228,10 @@ wheel Produced by :ref:`JOBTMPL/Package`. If empty, no package is downloaded and the tests run against whatever is installed in the environment. -.. _JOBTMPL/ApplicationTesting/Input/download_artifacts: +.. _JOBTMPL/ApplicationTesting/Input/artifacts_pattern: -download_artifacts -================== +artifacts_pattern +================= :Type: string :Required: no @@ -251,7 +251,7 @@ artifacts_directory :Required: no :Default Value: ``'artifacts'`` :Possible Values: Any valid directory path, relative to the repository's root. -:Description: Directory the artifacts of :ref:`JOBTMPL/ApplicationTesting/Input/download_artifacts` are downloaded +:Description: Directory the artifacts of :ref:`JOBTMPL/ApplicationTesting/Input/artifacts_pattern` are downloaded into. .. _JOBTMPL/ApplicationTesting/Input/apt: diff --git a/tests/app/AppTest.py b/tests/app/AppTest.py index 25373658..54585b27 100644 --- a/tests/app/AppTest.py +++ b/tests/app/AppTest.py @@ -111,11 +111,11 @@ def test_NoArguments(self) -> None: class DownloadedArtifacts(TestCase): - """``ApplicationTesting.yml`` downloads the artifacts ``download_artifacts`` names into ``artifacts//``.""" + """``ApplicationTesting.yml`` downloads the artifacts ``artifacts_pattern`` names into ``artifacts//``.""" def test_ExampleReport(self) -> None: directory = Path("artifacts") if not directory.exists(): - self.skipTest("No artifacts downloaded: the calling pipeline sets no 'download_artifacts'.") + self.skipTest("No artifacts downloaded: the calling pipeline sets no 'artifacts_pattern'.") self.assertEqual("Report of an example job.\n", (directory / "Example-Report" / "Example.txt").read_text())