get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 124469,
    "url": "http://patches.dpdk.org/api/patches/124469/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230223152840.634183-9-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": "<20230223152840.634183-9-juraj.linkes@pantheon.tech>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230223152840.634183-9-juraj.linkes@pantheon.tech",
    "date": "2023-02-23T15:28:38",
    "name": "[v5,08/10] dts: add test suite config and runner",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "36835256dcc4860d28124dc7a37cf3ab32449daf",
    "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/20230223152840.634183-9-juraj.linkes@pantheon.tech/mbox/",
    "series": [
        {
            "id": 27159,
            "url": "http://patches.dpdk.org/api/series/27159/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=27159",
            "date": "2023-02-23T15:28:30",
            "name": "dts: add hello world testcase",
            "version": 5,
            "mbox": "http://patches.dpdk.org/series/27159/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/124469/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/124469/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 1A56941D52;\n\tThu, 23 Feb 2023 16:29:57 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id B35F043063;\n\tThu, 23 Feb 2023 16:29:02 +0100 (CET)",
            "from mail-ed1-f45.google.com (mail-ed1-f45.google.com\n [209.85.208.45]) by mails.dpdk.org (Postfix) with ESMTP id 25FBF42F94\n for <dev@dpdk.org>; Thu, 23 Feb 2023 16:28:54 +0100 (CET)",
            "by mail-ed1-f45.google.com with SMTP id o12so43884288edb.9\n for <dev@dpdk.org>; Thu, 23 Feb 2023 07:28:54 -0800 (PST)",
            "from localhost.localdomain ([84.245.121.112])\n by smtp.gmail.com with ESMTPSA id\n r6-20020a50c006000000b004af6a8617ffsm1158892edb.46.2023.02.23.07.28.52\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 23 Feb 2023 07:28:53 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon-tech.20210112.gappssmtp.com; s=20210112;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=a6Yg1HAeHLjoNVENaWlot1vgqykBX7d+YQMwrK1auvg=;\n b=fiYm/M0OWWRyuVAL2JUrEv34K1bWhJ1KH0VZpVIe5z/8R/yhJMme/Ccod6L9NOSI9Z\n mwQkCkNSNR/oFWuJleINBjo/Kw8ncOKmvUk81opecJjzBych/Dq4SmAM8HST6xX9Pyar\n nRlScAegEAHbCfhEjohj6MJ9D7qgeTbEh7I1rYnyGVWjjkiRmmw7+3CuicnBG57UmAVN\n PVOI+nLCe5GZg5y13M0sMSYeE8LFkeJmN8TOWNeF8jSy4MGv8v2LD4/rWymFrptjjvjz\n u+K8lUjzulZbbI2L6iQYueikqxzGFDruQLpEAP1FEmLvS5hsD3YiI+5hLzhK6RmXTKRv\n mYbA==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20210112;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc\n :subject:date:message-id:reply-to;\n bh=a6Yg1HAeHLjoNVENaWlot1vgqykBX7d+YQMwrK1auvg=;\n b=opQ1V3wciA7o0P8OTz0n6iqJzX/rML0AOyb5mPG38Io6Jggbor7lGqKqvIMM6Id/Uh\n bsExX6i76JRG9JmNDtmqPCu09tkRQHARGo7EKBBgfpL/BHpnLcRQwcz56KtO1z25NE47\n K6KKtk0BkFGlPkxBn4lM66128Mp3Bej8Qj5bp0XQJDHWN0povtW+8MRK129oK5DHvDEa\n /NppCWBNn3PPS/xRsnReILwyCb3OjHqIpvta4JvXjs2PJyF9paZJ/UBVwZ7QDkNCVazM\n O3gzxaVZ4H4MTWEOfE4QVl2GTuMAiQ4ZmCRBCE5XBFUB07b/+wnD0ukTnE8+Ro7aDfeB\n dVeA==",
        "X-Gm-Message-State": "AO0yUKVmfIrBDrtIE3v3hsV3LZbais2sGJZxlqr6kwItJzK8/KESxTFb\n q49p28omVQyUVru6lY71VGqMCQ==",
        "X-Google-Smtp-Source": "\n AK7set8nI+KpMRke3uWv3841rIMBlLIk5jHLAeDjTHDOrlvxKCPv9Vjie/c4q1KerLpwHtNqja2Qig==",
        "X-Received": "by 2002:a05:6402:6ca:b0:4ac:b32d:3dab with SMTP id\n n10-20020a05640206ca00b004acb32d3dabmr9527047edy.29.1677166133757;\n Thu, 23 Feb 2023 07:28:53 -0800 (PST)",
        "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, lijuan.tu@intel.com,\n bruce.richardson@intel.com, probb@iol.unh.edu",
        "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "Subject": "[PATCH v5 08/10] dts: add test suite config and runner",
        "Date": "Thu, 23 Feb 2023 16:28:38 +0100",
        "Message-Id": "<20230223152840.634183-9-juraj.linkes@pantheon.tech>",
        "X-Mailer": "git-send-email 2.30.2",
        "In-Reply-To": "<20230223152840.634183-1-juraj.linkes@pantheon.tech>",
        "References": "<20230213152846.284191-1-juraj.linkes@pantheon.tech>\n <20230223152840.634183-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 75e33e8ccf..a9bd8a3ecf 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 544fceca6a..ebb0823ff5 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@@ -128,11 +128,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@@ -140,6 +163,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@@ -147,6 +173,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 878ca3aec2..ca2d4a1ef2 100644\n--- a/dts/framework/config/conf_yaml_schema.json\n+++ b/dts/framework/config/conf_yaml_schema.json\n@@ -93,6 +93,32 @@\n       \"required\": [\n         \"amount\"\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@@ -172,6 +198,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@@ -181,6 +220,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 9002d43297..12bf3b6420 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\"Test suite '{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": [
        "v5",
        "08/10"
    ]
}