get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 137670,
    "url": "http://patches.dpdk.org/api/patches/137670/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20240301105522.79870-6-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": "<20240301105522.79870-6-juraj.linkes@pantheon.tech>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20240301105522.79870-6-juraj.linkes@pantheon.tech",
    "date": "2024-03-01T10:55:20",
    "name": "[v4,5/7] dts: block all test cases when earlier setup fails",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "2a2c2636d0ea25197855e76ab1c1f132f86968cd",
    "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/20240301105522.79870-6-juraj.linkes@pantheon.tech/mbox/",
    "series": [
        {
            "id": 31329,
            "url": "http://patches.dpdk.org/api/series/31329/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=31329",
            "date": "2024-03-01T10:55:15",
            "name": "test case blocking and logging",
            "version": 4,
            "mbox": "http://patches.dpdk.org/series/31329/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/137670/comments/",
    "check": "success",
    "checks": "http://patches.dpdk.org/api/patches/137670/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 D96E543BB1;\n\tFri,  1 Mar 2024 11:56:07 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 22C2A433E0;\n\tFri,  1 Mar 2024 11:55:34 +0100 (CET)",
            "from mail-ed1-f49.google.com (mail-ed1-f49.google.com\n [209.85.208.49]) by mails.dpdk.org (Postfix) with ESMTP id 8ADA8433C1\n for <dev@dpdk.org>; Fri,  1 Mar 2024 11:55:31 +0100 (CET)",
            "by mail-ed1-f49.google.com with SMTP id\n 4fb4d7f45d1cf-563cb3ba9daso2657016a12.3\n for <dev@dpdk.org>; Fri, 01 Mar 2024 02:55:31 -0800 (PST)",
            "from jlinkes-PT-Latitude-5530.pantheon.local ([84.245.120.62])\n by smtp.gmail.com with ESMTPSA id\n f12-20020a056402194c00b0056661ec3f24sm1461734edz.81.2024.03.01.02.55.29\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Fri, 01 Mar 2024 02:55:30 -0800 (PST)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=pantheon.tech; s=google; t=1709290531; x=1709895331; darn=dpdk.org;\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=lgi8/GLgdFdN8/MgBNoep+IflRBRy9l2DQ/JD62PMuA=;\n b=kHYKFg9/ioZgsgFR8t1+dEGYkaicWJR1pCuE+yQ2plUm6J0XV9XQBeJspLiF3fkQ+w\n WmMSZewiviTrg11RUxz6HANMadwXKZcEgWXpBRlvFV/nOpNpwMPUis0F658tuspp+YJq\n LClXcY7m28VCkWOAEPVkSplRkIrwpzAAEBksM1zjON+B8PS31R9Vu0CftIA3K6sxQl/O\n Ysqtbn2W90j93MS7+91tXKQKVh2n74AxvYwI0kEiWHcHDqntvMvo67PIOcOhxsGBvZXN\n hp1rFsRgpUgtzhYGNEnVh8mPyj8T1dGR0MKtSo6iH6X6aU+xN16ddoqOs0sGVHwcBCzY\n GH6A==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1709290531; x=1709895331;\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=lgi8/GLgdFdN8/MgBNoep+IflRBRy9l2DQ/JD62PMuA=;\n b=Is2LU9Oi33r6PPH95izkGN19t7J9ptLya5Cz+mfvEI9or47CjLoEOsdd/j+P6v8oce\n NbaDKHPBEawUH4UUTPzJFbTcZh08glcjwCE/0W8B0vKtB4Q/xZYDTaC7Jmv8orIalwZF\n Yv1K9Fm9vvyXqvV59yuRGeeejSfEGc5f5UEpdM9vq/JOB1J/rP1p0qwUCBQ/3AbV/o8N\n EBTYXIAIL/ci6b87NRomtZ9Xq9CuQepHDATiVWxO75b5UwONDCKcBvu6tz1X8HvRkOOh\n n7o5iShh5x6z5ju0Cg9IT4Zz4eEEk7M4mERkuY7H6mWxPNRnpmouyip0GUf2CloPs1xR\n h6mA==",
        "X-Gm-Message-State": "AOJu0YwDarXk/LCw2KXn+ARCzSwcBwuNaTr5V8NB/VZ5pEB7ur6hIeeI\n /z/Prg+FTIoKEuqd1FQMkrhv6cPI3coHsNzJZXcMfphfiqbcU73kruaOZvKHbfQ=",
        "X-Google-Smtp-Source": "\n AGHT+IHSP9A8ld+US0+z5q4B3dH51hQn8BVU3zQd4J42JqzpdxPlMjGSoqdoUOpQvqeMmH/F2tC5Mw==",
        "X-Received": "by 2002:a05:6402:5245:b0:566:dede:1f82 with SMTP id\n t5-20020a056402524500b00566dede1f82mr732645edd.29.1709290531072;\n Fri, 01 Mar 2024 02:55:31 -0800 (PST)",
        "From": "=?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "To": "thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu,\n probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com,\n npratte@iol.unh.edu",
        "Cc": "dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= <juraj.linkes@pantheon.tech>",
        "Subject": "[PATCH v4 5/7] dts: block all test cases when earlier setup fails",
        "Date": "Fri,  1 Mar 2024 11:55:20 +0100",
        "Message-Id": "<20240301105522.79870-6-juraj.linkes@pantheon.tech>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20240301105522.79870-1-juraj.linkes@pantheon.tech>",
        "References": "<20231220103331.60888-1-juraj.linkes@pantheon.tech>\n <20240301105522.79870-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": "In case of a failure before a test suite, the child results will be\nrecursively recorded as blocked, giving us a full report which was\nmissing previously.\n\nSigned-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>\n---\n dts/framework/runner.py      |  21 ++--\n dts/framework/test_result.py | 186 +++++++++++++++++++++++++----------\n 2 files changed, 148 insertions(+), 59 deletions(-)",
    "diff": "diff --git a/dts/framework/runner.py b/dts/framework/runner.py\nindex 5f6bcbbb86..864015c350 100644\n--- a/dts/framework/runner.py\n+++ b/dts/framework/runner.py\n@@ -60,13 +60,15 @@ class DTSRunner:\n     Each setup or teardown of each stage is recorded in a :class:`~framework.test_result.DTSResult`\n     or one of its subclasses. The test case results are also recorded.\n \n-    If an error occurs, the current stage is aborted, the error is recorded and the run continues in\n-    the next iteration of the same stage. The return code is the highest `severity` of all\n+    If an error occurs, the current stage is aborted, the error is recorded, everything in\n+    the inner stages is marked as blocked and the run continues in the next iteration\n+    of the same stage. The return code is the highest `severity` of all\n     :class:`~.framework.exception.DTSError`\\s.\n \n     Example:\n-        An error occurs in a build target setup. The current build target is aborted and the run\n-        continues with the next build target. If the errored build target was the last one in the\n+        An error occurs in a build target setup. The current build target is aborted,\n+        all test suites and their test cases are marked as blocked and the run continues\n+        with the next build target. If the errored build target was the last one in the\n         given execution, the next execution begins.\n     \"\"\"\n \n@@ -100,6 +102,10 @@ def run(self):\n         test case within the test suite is set up, executed and torn down. After all test cases\n         have been executed, the test suite is torn down and the next build target will be tested.\n \n+        In order to properly mark test suites and test cases as blocked in case of a failure,\n+        we need to have discovered which test suites and test cases to run before any failures\n+        happen. The discovery happens at the earliest point at the start of each execution.\n+\n         All the nested steps look like this:\n \n             #. Execution setup\n@@ -134,7 +140,7 @@ def run(self):\n                 self._logger.info(\n                     f\"Running execution with SUT '{execution.system_under_test_node.name}'.\"\n                 )\n-                execution_result = self._result.add_execution(execution.system_under_test_node)\n+                execution_result = self._result.add_execution(execution)\n                 # we don't want to modify the original config, so create a copy\n                 execution_test_suites = list(execution.test_suites)\n                 if not execution.skip_smoke_tests:\n@@ -143,6 +149,7 @@ def run(self):\n                     test_suites_with_cases = self._get_test_suites_with_cases(\n                         execution_test_suites, execution.func, execution.perf\n                     )\n+                    execution_result.test_suites_with_cases = test_suites_with_cases\n                 except Exception as e:\n                     self._logger.exception(\n                         f\"Invalid test suite configuration found: \" f\"{execution_test_suites}.\"\n@@ -493,9 +500,7 @@ def _run_test_suites(\n         \"\"\"\n         end_build_target = False\n         for test_suite_with_cases in test_suites_with_cases:\n-            test_suite_result = build_target_result.add_test_suite(\n-                test_suite_with_cases.test_suite_class.__name__\n-            )\n+            test_suite_result = build_target_result.add_test_suite(test_suite_with_cases)\n             try:\n                 self._run_test_suite(sut_node, tg_node, test_suite_result, test_suite_with_cases)\n             except BlockingTestSuiteError as e:\ndiff --git a/dts/framework/test_result.py b/dts/framework/test_result.py\nindex abdbafab10..eedb2d20ee 100644\n--- a/dts/framework/test_result.py\n+++ b/dts/framework/test_result.py\n@@ -37,7 +37,7 @@\n     BuildTargetInfo,\n     Compiler,\n     CPUType,\n-    NodeConfiguration,\n+    ExecutionConfiguration,\n     NodeInfo,\n     TestSuiteConfig,\n )\n@@ -88,6 +88,8 @@ class Result(Enum):\n     ERROR = auto()\n     #:\n     SKIP = auto()\n+    #:\n+    BLOCK = auto()\n \n     def __bool__(self) -> bool:\n         \"\"\"Only PASS is True.\"\"\"\n@@ -141,21 +143,26 @@ class BaseResult(object):\n     Attributes:\n         setup_result: The result of the setup of the particular stage.\n         teardown_result: The results of the teardown of the particular stage.\n+        child_results: The results of the descendants in the results hierarchy.\n     \"\"\"\n \n     setup_result: FixtureResult\n     teardown_result: FixtureResult\n-    _inner_results: MutableSequence[\"BaseResult\"]\n+    child_results: MutableSequence[\"BaseResult\"]\n \n     def __init__(self):\n         \"\"\"Initialize the constructor.\"\"\"\n         self.setup_result = FixtureResult()\n         self.teardown_result = FixtureResult()\n-        self._inner_results = []\n+        self.child_results = []\n \n     def update_setup(self, result: Result, error: Exception | None = None) -> None:\n         \"\"\"Store the setup result.\n \n+        If the result is :attr:`~Result.BLOCK`, :attr:`~Result.ERROR` or :attr:`~Result.FAIL`,\n+        then the corresponding child results in result hierarchy\n+        are also marked with :attr:`~Result.BLOCK`.\n+\n         Args:\n             result: The result of the setup.\n             error: The error that occurred in case of a failure.\n@@ -163,6 +170,16 @@ def update_setup(self, result: Result, error: Exception | None = None) -> None:\n         self.setup_result.result = result\n         self.setup_result.error = error\n \n+        if result in [Result.BLOCK, Result.ERROR, Result.FAIL]:\n+            self.update_teardown(Result.BLOCK)\n+            self._block_result()\n+\n+    def _block_result(self) -> None:\n+        r\"\"\"Mark the result as :attr:`~Result.BLOCK`\\ed.\n+\n+        The blocking of child results should be done in overloaded methods.\n+        \"\"\"\n+\n     def update_teardown(self, result: Result, error: Exception | None = None) -> None:\n         \"\"\"Store the teardown result.\n \n@@ -181,10 +198,8 @@ def _get_setup_teardown_errors(self) -> list[Exception]:\n             errors.append(self.teardown_result.error)\n         return errors\n \n-    def _get_inner_errors(self) -> list[Exception]:\n-        return [\n-            error for inner_result in self._inner_results for error in inner_result.get_errors()\n-        ]\n+    def _get_child_errors(self) -> list[Exception]:\n+        return [error for child_result in self.child_results for error in child_result.get_errors()]\n \n     def get_errors(self) -> list[Exception]:\n         \"\"\"Compile errors from the whole result hierarchy.\n@@ -192,7 +207,7 @@ def get_errors(self) -> list[Exception]:\n         Returns:\n             The errors from setup, teardown and all errors found in the whole result hierarchy.\n         \"\"\"\n-        return self._get_setup_teardown_errors() + self._get_inner_errors()\n+        return self._get_setup_teardown_errors() + self._get_child_errors()\n \n     def add_stats(self, statistics: \"Statistics\") -> None:\n         \"\"\"Collate stats from the whole result hierarchy.\n@@ -200,8 +215,8 @@ def add_stats(self, statistics: \"Statistics\") -> None:\n         Args:\n             statistics: The :class:`Statistics` object where the stats will be collated.\n         \"\"\"\n-        for inner_result in self._inner_results:\n-            inner_result.add_stats(statistics)\n+        for child_result in self.child_results:\n+            child_result.add_stats(statistics)\n \n \n class DTSResult(BaseResult):\n@@ -242,18 +257,18 @@ def __init__(self, logger: DTSLOG):\n         self._stats_result = None\n         self._stats_filename = os.path.join(SETTINGS.output_dir, \"statistics.txt\")\n \n-    def add_execution(self, sut_node: NodeConfiguration) -> \"ExecutionResult\":\n-        \"\"\"Add and return the inner result (execution).\n+    def add_execution(self, execution: ExecutionConfiguration) -> \"ExecutionResult\":\n+        \"\"\"Add and return the child result (execution).\n \n         Args:\n-            sut_node: The SUT node's test run configuration.\n+            execution: The execution's test run configuration.\n \n         Returns:\n             The execution's result.\n         \"\"\"\n-        execution_result = ExecutionResult(sut_node)\n-        self._inner_results.append(execution_result)\n-        return execution_result\n+        result = ExecutionResult(execution)\n+        self.child_results.append(result)\n+        return result\n \n     def add_error(self, error: Exception) -> None:\n         \"\"\"Record an error that occurred outside any execution.\n@@ -266,8 +281,8 @@ def add_error(self, error: Exception) -> None:\n     def process(self) -> None:\n         \"\"\"Process the data after a whole DTS run.\n \n-        The data is added to inner objects during runtime and this object is not updated\n-        at that time. This requires us to process the inner data after it's all been gathered.\n+        The data is added to child objects during runtime and this object is not updated\n+        at that time. This requires us to process the child data after it's all been gathered.\n \n         The processing gathers all errors and the statistics of test case results.\n         \"\"\"\n@@ -305,28 +320,30 @@ class ExecutionResult(BaseResult):\n     The internal list stores the results of all build targets in a given execution.\n \n     Attributes:\n-        sut_node: The SUT node used in the execution.\n         sut_os_name: The operating system of the SUT node.\n         sut_os_version: The operating system version of the SUT node.\n         sut_kernel_version: The operating system kernel version of the SUT node.\n     \"\"\"\n \n-    sut_node: NodeConfiguration\n     sut_os_name: str\n     sut_os_version: str\n     sut_kernel_version: str\n+    _config: ExecutionConfiguration\n+    _parent_result: DTSResult\n+    _test_suites_with_cases: list[TestSuiteWithCases]\n \n-    def __init__(self, sut_node: NodeConfiguration):\n-        \"\"\"Extend the constructor with the `sut_node`'s config.\n+    def __init__(self, execution: ExecutionConfiguration):\n+        \"\"\"Extend the constructor with the execution's config and DTSResult.\n \n         Args:\n-            sut_node: The SUT node's test run configuration used in the execution.\n+            execution: The execution's test run configuration.\n         \"\"\"\n         super(ExecutionResult, self).__init__()\n-        self.sut_node = sut_node\n+        self._config = execution\n+        self._test_suites_with_cases = []\n \n     def add_build_target(self, build_target: BuildTargetConfiguration) -> \"BuildTargetResult\":\n-        \"\"\"Add and return the inner result (build target).\n+        \"\"\"Add and return the child result (build target).\n \n         Args:\n             build_target: The build target's test run configuration.\n@@ -334,9 +351,34 @@ def add_build_target(self, build_target: BuildTargetConfiguration) -> \"BuildTarg\n         Returns:\n             The build target's result.\n         \"\"\"\n-        build_target_result = BuildTargetResult(build_target)\n-        self._inner_results.append(build_target_result)\n-        return build_target_result\n+        result = BuildTargetResult(\n+            self._test_suites_with_cases,\n+            build_target,\n+        )\n+        self.child_results.append(result)\n+        return result\n+\n+    @property\n+    def test_suites_with_cases(self) -> list[TestSuiteWithCases]:\n+        \"\"\"The test suites with test cases to be executed in this execution.\n+\n+        The test suites can only be assigned once.\n+\n+        Returns:\n+            The list of test suites with test cases. If an error occurs between\n+            the initialization of :class:`ExecutionResult` and assigning test cases to the instance,\n+            return an empty list, representing that we don't know what to execute.\n+        \"\"\"\n+        return self._test_suites_with_cases\n+\n+    @test_suites_with_cases.setter\n+    def test_suites_with_cases(self, test_suites_with_cases: list[TestSuiteWithCases]) -> None:\n+        if self._test_suites_with_cases:\n+            raise ValueError(\n+                \"Attempted to assign test suites to an execution result \"\n+                \"which already has test suites.\"\n+            )\n+        self._test_suites_with_cases = test_suites_with_cases\n \n     def add_sut_info(self, sut_info: NodeInfo) -> None:\n         \"\"\"Add SUT information gathered at runtime.\n@@ -348,6 +390,12 @@ def add_sut_info(self, sut_info: NodeInfo) -> None:\n         self.sut_os_version = sut_info.os_version\n         self.sut_kernel_version = sut_info.kernel_version\n \n+    def _block_result(self) -> None:\n+        r\"\"\"Mark the result as :attr:`~Result.BLOCK`\\ed.\"\"\"\n+        for build_target in self._config.build_targets:\n+            child_result = self.add_build_target(build_target)\n+            child_result.update_setup(Result.BLOCK)\n+\n \n class BuildTargetResult(BaseResult):\n     \"\"\"The build target specific result.\n@@ -369,11 +417,17 @@ class BuildTargetResult(BaseResult):\n     compiler: Compiler\n     compiler_version: str | None\n     dpdk_version: str | None\n+    _test_suites_with_cases: list[TestSuiteWithCases]\n \n-    def __init__(self, build_target: BuildTargetConfiguration):\n-        \"\"\"Extend the constructor with the `build_target`'s build target config.\n+    def __init__(\n+        self,\n+        test_suites_with_cases: list[TestSuiteWithCases],\n+        build_target: BuildTargetConfiguration,\n+    ):\n+        \"\"\"Extend the constructor with the build target's config and ExecutionResult.\n \n         Args:\n+            test_suites_with_cases: The test suites with test cases to be run in this build target.\n             build_target: The build target's test run configuration.\n         \"\"\"\n         super(BuildTargetResult, self).__init__()\n@@ -383,6 +437,23 @@ def __init__(self, build_target: BuildTargetConfiguration):\n         self.compiler = build_target.compiler\n         self.compiler_version = None\n         self.dpdk_version = None\n+        self._test_suites_with_cases = test_suites_with_cases\n+\n+    def add_test_suite(\n+        self,\n+        test_suite_with_cases: TestSuiteWithCases,\n+    ) -> \"TestSuiteResult\":\n+        \"\"\"Add and return the child result (test suite).\n+\n+        Args:\n+            test_suite_with_cases: The test suite with test cases.\n+\n+        Returns:\n+            The test suite's result.\n+        \"\"\"\n+        result = TestSuiteResult(test_suite_with_cases)\n+        self.child_results.append(result)\n+        return result\n \n     def add_build_target_info(self, versions: BuildTargetInfo) -> None:\n         \"\"\"Add information about the build target gathered at runtime.\n@@ -393,15 +464,11 @@ def add_build_target_info(self, versions: BuildTargetInfo) -> None:\n         self.compiler_version = versions.compiler_version\n         self.dpdk_version = versions.dpdk_version\n \n-    def add_test_suite(self, test_suite_name: str) -> \"TestSuiteResult\":\n-        \"\"\"Add and return the inner result (test suite).\n-\n-        Returns:\n-            The test suite's result.\n-        \"\"\"\n-        test_suite_result = TestSuiteResult(test_suite_name)\n-        self._inner_results.append(test_suite_result)\n-        return test_suite_result\n+    def _block_result(self) -> None:\n+        r\"\"\"Mark the result as :attr:`~Result.BLOCK`\\ed.\"\"\"\n+        for test_suite_with_cases in self._test_suites_with_cases:\n+            child_result = self.add_test_suite(test_suite_with_cases)\n+            child_result.update_setup(Result.BLOCK)\n \n \n class TestSuiteResult(BaseResult):\n@@ -410,29 +477,42 @@ class TestSuiteResult(BaseResult):\n     The internal list stores the results of all test cases in a given test suite.\n \n     Attributes:\n-        suite_name: The test suite name.\n+        test_suite_name: The test suite name.\n     \"\"\"\n \n-    suite_name: str\n+    test_suite_name: str\n+    _test_suite_with_cases: TestSuiteWithCases\n+    _parent_result: BuildTargetResult\n+    _child_configs: list[str]\n \n-    def __init__(self, suite_name: str):\n-        \"\"\"Extend the constructor with `suite_name`.\n+    def __init__(self, test_suite_with_cases: TestSuiteWithCases):\n+        \"\"\"Extend the constructor with test suite's config and BuildTargetResult.\n \n         Args:\n-            suite_name: The test suite's name.\n+            test_suite_with_cases: The test suite with test cases.\n         \"\"\"\n         super(TestSuiteResult, self).__init__()\n-        self.suite_name = suite_name\n+        self.test_suite_name = test_suite_with_cases.test_suite_class.__name__\n+        self._test_suite_with_cases = test_suite_with_cases\n \n     def add_test_case(self, test_case_name: str) -> \"TestCaseResult\":\n-        \"\"\"Add and return the inner result (test case).\n+        \"\"\"Add and return the child result (test case).\n+\n+        Args:\n+            test_case_name: The name of the test case.\n \n         Returns:\n             The test case's result.\n         \"\"\"\n-        test_case_result = TestCaseResult(test_case_name)\n-        self._inner_results.append(test_case_result)\n-        return test_case_result\n+        result = TestCaseResult(test_case_name)\n+        self.child_results.append(result)\n+        return result\n+\n+    def _block_result(self) -> None:\n+        r\"\"\"Mark the result as :attr:`~Result.BLOCK`\\ed.\"\"\"\n+        for test_case_method in self._test_suite_with_cases.test_cases:\n+            child_result = self.add_test_case(test_case_method.__name__)\n+            child_result.update_setup(Result.BLOCK)\n \n \n class TestCaseResult(BaseResult, FixtureResult):\n@@ -449,7 +529,7 @@ class TestCaseResult(BaseResult, FixtureResult):\n     test_case_name: str\n \n     def __init__(self, test_case_name: str):\n-        \"\"\"Extend the constructor with `test_case_name`.\n+        \"\"\"Extend the constructor with test case's name and TestSuiteResult.\n \n         Args:\n             test_case_name: The test case's name.\n@@ -470,7 +550,7 @@ def update(self, result: Result, error: Exception | None = None) -> None:\n         self.result = result\n         self.error = error\n \n-    def _get_inner_errors(self) -> list[Exception]:\n+    def _get_child_errors(self) -> list[Exception]:\n         if self.error:\n             return [self.error]\n         return []\n@@ -486,6 +566,10 @@ def add_stats(self, statistics: \"Statistics\") -> None:\n         \"\"\"\n         statistics += self.result\n \n+    def _block_result(self) -> None:\n+        r\"\"\"Mark the result as :attr:`~Result.BLOCK`\\ed.\"\"\"\n+        self.update(Result.BLOCK)\n+\n     def __bool__(self) -> bool:\n         \"\"\"The test case passed only if setup, teardown and the test case itself passed.\"\"\"\n         return bool(self.setup_result) and bool(self.teardown_result) and bool(self.result)\n",
    "prefixes": [
        "v4",
        "5/7"
    ]
}