get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/122197/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 122197,
    "url": "http://patches.dpdk.org/api/patches/122197/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230117154906.860916-10-juraj.linkes@pantheon.tech/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230117154906.860916-10-juraj.linkes@pantheon.tech>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230117154906.860916-10-juraj.linkes@pantheon.tech",
    "date": "2023-01-17T15:49:05",
    "name": "[v3,09/10] dts: add test suite config and runner",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "08c268752362062a1422e57bffbdf314ad39dcee",
    "submitter": {
        "id": 1626,
        "url": "http://patches.dpdk.org/api/people/1626/?format=api",
        "name": "Juraj Linkeš",
        "email": "juraj.linkes@pantheon.tech"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20230117154906.860916-10-juraj.linkes@pantheon.tech/mbox/",
    "series": [
        {
            "id": 26579,
            "url": "http://patches.dpdk.org/api/series/26579/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=26579",
            "date": "2023-01-17T15:48:57",
            "name": "dts: add hello world testcase",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/26579/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/122197/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/122197/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 550B7423FE;\n\tTue, 17 Jan 2023 16:50:27 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 5D80742D7A;\n\tTue, 17 Jan 2023 16:49:25 +0100 (CET)",
            "from lb.pantheon.sk (lb.pantheon.sk [46.229.239.20])\n by mails.dpdk.org (Postfix) with ESMTP id F157042D5B\n for <dev@dpdk.org>; Tue, 17 Jan 2023 16:49:21 +0100 (CET)",
            "from localhost (localhost [127.0.0.1])\n by lb.pantheon.sk (Postfix) with ESMTP id 52B881D812A;\n Tue, 17 Jan 2023 16:49:21 +0100 (CET)",
            "from lb.pantheon.sk ([127.0.0.1])\n by localhost (lb.pantheon.sk [127.0.0.1]) (amavisd-new, port 10024)\n with ESMTP id jkvt_cmHLYr2; Tue, 17 Jan 2023 16:49:20 +0100 (CET)",
            "from entguard.lab.pantheon.local (unknown [46.229.239.141])\n by lb.pantheon.sk (Postfix) with ESMTP id 0213C1D811C;\n Tue, 17 Jan 2023 16:49:13 +0100 (CET)"
        ],
        "X-Virus-Scanned": "amavisd-new at siecit.sk",
        "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, ohilyard@iol.unh.edu,\n lijuan.tu@intel.com, bruce.richardson@intel.com",
        "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "Subject": "[PATCH v3 09/10] dts: add test suite config and runner",
        "Date": "Tue, 17 Jan 2023 15:49:05 +0000",
        "Message-Id": "<20230117154906.860916-10-juraj.linkes@pantheon.tech>",
        "X-Mailer": "git-send-email 2.25.1",
        "In-Reply-To": "<20230117154906.860916-1-juraj.linkes@pantheon.tech>",
        "References": "<20221114165438.1133783-1-juraj.linkes@pantheon.tech>\n <20230117154906.860916-1-juraj.linkes@pantheon.tech>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "The config allows users to specify which test suites and test cases\nwithin test suites to run.\nAlso add test suite running capabilities to dts runner.\n\nSigned-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>\n---\n dts/conf.yaml                              |  2 ++\n dts/framework/config/__init__.py           | 29 +++++++++++++++-\n dts/framework/config/conf_yaml_schema.json | 40 ++++++++++++++++++++++\n dts/framework/dts.py                       | 19 ++++++++++\n dts/framework/test_suite.py                | 24 ++++++++++++-\n 5 files changed, 112 insertions(+), 2 deletions(-)",
    "diff": "diff --git a/dts/conf.yaml b/dts/conf.yaml\nindex 2111d537cf..2c6ec84282 100644\n--- a/dts/conf.yaml\n+++ b/dts/conf.yaml\n@@ -10,6 +10,8 @@ executions:\n         compiler_wrapper: ccache\n     perf: false\n     func: true\n+    test_suites:\n+      - hello_world\n     system_under_test: \"SUT 1\"\n nodes:\n   - name: \"SUT 1\"\ndiff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py\nindex ce3f20f6a9..058fbf58db 100644\n--- a/dts/framework/config/__init__.py\n+++ b/dts/framework/config/__init__.py\n@@ -12,7 +12,7 @@\n import pathlib\n from dataclasses import dataclass\n from enum import Enum, auto, unique\n-from typing import Any\n+from typing import Any, TypedDict\n \n import warlock  # type: ignore\n import yaml\n@@ -116,11 +116,34 @@ def from_dict(d: dict) -> \"BuildTargetConfiguration\":\n         )\n \n \n+class TestSuiteConfigDict(TypedDict):\n+    suite: str\n+    cases: list[str]\n+\n+\n+@dataclass(slots=True, frozen=True)\n+class TestSuiteConfig:\n+    test_suite: str\n+    test_cases: list[str]\n+\n+    @staticmethod\n+    def from_dict(\n+        entry: str | TestSuiteConfigDict,\n+    ) -> \"TestSuiteConfig\":\n+        if isinstance(entry, str):\n+            return TestSuiteConfig(test_suite=entry, test_cases=[])\n+        elif isinstance(entry, dict):\n+            return TestSuiteConfig(test_suite=entry[\"suite\"], test_cases=entry[\"cases\"])\n+        else:\n+            raise TypeError(f\"{type(entry)} is not valid for a test suite config.\")\n+\n+\n @dataclass(slots=True, frozen=True)\n class ExecutionConfiguration:\n     build_targets: list[BuildTargetConfiguration]\n     perf: bool\n     func: bool\n+    test_suites: list[TestSuiteConfig]\n     system_under_test: NodeConfiguration\n \n     @staticmethod\n@@ -128,6 +151,9 @@ def from_dict(d: dict, node_map: dict) -> \"ExecutionConfiguration\":\n         build_targets: list[BuildTargetConfiguration] = list(\n             map(BuildTargetConfiguration.from_dict, d[\"build_targets\"])\n         )\n+        test_suites: list[TestSuiteConfig] = list(\n+            map(TestSuiteConfig.from_dict, d[\"test_suites\"])\n+        )\n         sut_name = d[\"system_under_test\"]\n         assert sut_name in node_map, f\"Unknown SUT {sut_name} in execution {d}\"\n \n@@ -135,6 +161,7 @@ def from_dict(d: dict, node_map: dict) -> \"ExecutionConfiguration\":\n             build_targets=build_targets,\n             perf=d[\"perf\"],\n             func=d[\"func\"],\n+            test_suites=test_suites,\n             system_under_test=node_map[sut_name],\n         )\n \ndiff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json\nindex abf15ebea8..c4a9e75251 100644\n--- a/dts/framework/config/conf_yaml_schema.json\n+++ b/dts/framework/config/conf_yaml_schema.json\n@@ -75,6 +75,32 @@\n         \"cpu\",\n         \"compiler\"\n       ]\n+    },\n+    \"test_suite\": {\n+      \"type\": \"string\",\n+      \"enum\": [\n+        \"hello_world\"\n+      ]\n+    },\n+    \"test_target\": {\n+      \"type\": \"object\",\n+      \"properties\": {\n+        \"suite\": {\n+          \"$ref\": \"#/definitions/test_suite\"\n+        },\n+        \"cases\": {\n+          \"type\": \"array\",\n+          \"description\": \"If specified, only this subset of test suite's test cases will be run. Unknown test cases will be silently ignored.\",\n+          \"items\": {\n+            \"type\": \"string\"\n+          },\n+          \"minimum\": 1\n+        }\n+      },\n+      \"required\": [\n+        \"suite\"\n+      ],\n+      \"additionalProperties\": false\n     }\n   },\n   \"type\": \"object\",\n@@ -150,6 +176,19 @@\n             \"type\": \"boolean\",\n             \"description\": \"Enable functional testing.\"\n           },\n+          \"test_suites\": {\n+            \"type\": \"array\",\n+            \"items\": {\n+              \"oneOf\": [\n+                {\n+                  \"$ref\": \"#/definitions/test_suite\"\n+                },\n+                {\n+                  \"$ref\": \"#/definitions/test_target\"\n+                }\n+              ]\n+            }\n+          },\n           \"system_under_test\": {\n             \"$ref\": \"#/definitions/node_name\"\n           }\n@@ -159,6 +198,7 @@\n           \"build_targets\",\n           \"perf\",\n           \"func\",\n+          \"test_suites\",\n           \"system_under_test\"\n         ]\n       },\ndiff --git a/dts/framework/dts.py b/dts/framework/dts.py\nindex 6ea7c6e736..f98000450f 100644\n--- a/dts/framework/dts.py\n+++ b/dts/framework/dts.py\n@@ -8,6 +8,7 @@\n from .config import CONFIGURATION, BuildTargetConfiguration, ExecutionConfiguration\n from .exception import DTSError, ErrorSeverity\n from .logger import DTSLOG, getLogger\n+from .test_suite import get_test_suites\n from .testbed_model import SutNode\n from .utils import check_dts_python_version\n \n@@ -132,6 +133,24 @@ def _run_suites(\n     with possibly only a subset of test cases.\n     If no subset is specified, run all test cases.\n     \"\"\"\n+    for test_suite_config in execution.test_suites:\n+        try:\n+            full_suite_path = f\"tests.TestSuite_{test_suite_config.test_suite}\"\n+            test_suite_classes = get_test_suites(full_suite_path)\n+            suites_str = \", \".join((x.__name__ for x in test_suite_classes))\n+            dts_logger.debug(\n+                f\"Found test suites '{suites_str}' in '{full_suite_path}'.\"\n+            )\n+        except Exception as e:\n+            dts_logger.exception(\"An error occurred when searching for test suites.\")\n+            errors.append(e)\n+\n+        else:\n+            for test_suite_class in test_suite_classes:\n+                test_suite = test_suite_class(\n+                    sut_node, test_suite_config.test_cases, execution.func, errors\n+                )\n+                test_suite.run()\n \n \n def _exit_dts() -> None:\ndiff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py\nindex 0972a70c14..0cbedee478 100644\n--- a/dts/framework/test_suite.py\n+++ b/dts/framework/test_suite.py\n@@ -6,12 +6,13 @@\n Base class for creating DTS test cases.\n \"\"\"\n \n+import importlib\n import inspect\n import re\n from collections.abc import MutableSequence\n from types import MethodType\n \n-from .exception import SSHTimeoutError, TestCaseVerifyError\n+from .exception import ConfigurationError, SSHTimeoutError, TestCaseVerifyError\n from .logger import DTSLOG, getLogger\n from .settings import SETTINGS\n from .testbed_model import SutNode\n@@ -226,3 +227,24 @@ def _execute_test_case(self, test_case_method: MethodType) -> bool:\n             raise KeyboardInterrupt(\"Stop DTS\")\n \n         return result\n+\n+\n+def get_test_suites(testsuite_module_path: str) -> list[type[TestSuite]]:\n+    def is_test_suite(object) -> bool:\n+        try:\n+            if issubclass(object, TestSuite) and object is not TestSuite:\n+                return True\n+        except TypeError:\n+            return False\n+        return False\n+\n+    try:\n+        testcase_module = importlib.import_module(testsuite_module_path)\n+    except ModuleNotFoundError as e:\n+        raise ConfigurationError(\n+            f\"Testsuite '{testsuite_module_path}' not found.\"\n+        ) from e\n+    return [\n+        test_suite_class\n+        for _, test_suite_class in inspect.getmembers(testcase_module, is_test_suite)\n+    ]\n",
    "prefixes": [
        "v3",
        "09/10"
    ]
}