Hi Koushik,
Apologies for the long wait. To start off, could you please address the
AI Code Review as it raised a few concerns:
http://mails.dpdk.org/archives/test-report/2026-August/1029040.html
The patch will also require rebasing.
On 11/08/2026 19:55, Koushik Bhargav Nimoji wrote:
diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py
index 21faa55dc1..13ed8f671c 100644
--- a/dts/framework/test_result.py
+++ b/dts/framework/test_result.py
@@ -117,6 +117,7 @@ class ResultNode(BaseModel):
label: str
children: list[Union["ResultNode", ResultLeaf]] =
Field(default_factory=list)
parent: Union["ResultNode", None] = None
+ ports: list[dict[str, str]] | None = None
This attribute lacks the appropriate documentation. Also unless I missed
something, I am not sure how this belongs to this patch. Can you please
clarify?
def add_child(self, label: str) -> "ResultNode":
"""Creates and append a child node to the model.
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index c20a0fa885..b23930b953 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -115,7 +115,7 @@
from framework.remote_session.dpdk import DPDKBuildEnvironment,
DPDKRuntimeEnvironment
from framework.settings import SETTINGS
from framework.test_result import Result, ResultNode, TestRunResult
-from framework.test_suite import BaseConfig, TestCase, TestCaseType, TestSuite
+from framework.test_suite import BaseConfig, TestCase, TestCaseType,
TestSuite, TestSuiteType
from framework.testbed_model.capability import (
Capability,
get_supported_capabilities,
@@ -375,11 +375,6 @@ def next(self) -> State | None:
ctx.topology.bind_cryptodevs("dpdk")
ctx.topology.configure_ports("sut", "dpdk")
- if ctx.func_tg and ctx.topology.type is not LinkTopology.NO_LINK:
- ctx.func_tg.setup(ctx.topology)
- if ctx.perf_tg and ctx.topology.type is not LinkTopology.NO_LINK:
- ctx.perf_tg.setup(ctx.topology)
-
self.result.ports = [
port.to_dict() for port in ctx.topology.sut_ports +
ctx.topology.tg_ports
]
@@ -506,12 +501,34 @@ class TestSuiteSetup(TestSuiteState):
"""Test suite setup."""
logger_name: ClassVar[str] = "test_suite_setup"
+ test_run: TestRun
Unsure why this is necessary, given it's already inherited by
TestSuiteState.
def before(self) -> None:
"""Hook before the state is processed."""
super().before()
self.logger.set_custom_log_file(self.test_suite.name)
+ ctx = self.test_run.ctx
+ testsuite_type = self.test_suite.testsuite_type
nit: stick to the snake case, so test_suite_type
+ if (
+ ctx.topology.type is not LinkTopology.NO_LINK
+ and testsuite_type is not TestSuiteType.CRYPTO
+ ):
+ if testsuite_type == TestSuiteType.FUNC:
+ required_tg, other_tg = ctx.func_tg, ctx.perf_tg
+ else:
+ required_tg, other_tg = ctx.perf_tg, ctx.func_tg
+
+ if other_tg and other_tg.is_setup:
+ other_tg.teardown()
+
+ if testsuite_type == TestSuiteType.FUNC and ctx.tg_node:
+ for port in ctx.topology.tg_ports:
+ ctx.tg_node.main_session.bind_ports_to_driver([port],
port.config.os_driver)
I am a bit confused why this happens here. The driver to be bound to
should be governed by the TG that uses such ports. Could be os_driver,
could be dpdk_driver, regardless of Scapy and TRex.
+
+ if required_tg and not required_tg.is_setup:
+ required_tg.setup(ctx.topology)
+
@property
def description(self) -> str:
"""State description."""
diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py
index 69ce26040a..554fe3a475 100644
--- a/dts/framework/test_suite.py
+++ b/dts/framework/test_suite.py
@@ -45,6 +45,17 @@ class BaseConfig(FrozenModel):
"""Base for a custom test suite configuration."""
+class TestSuiteType(Enum):
+ """Enum to specify test suite type."""
+
+ FUNC = auto()
+ PERF = auto()
+ CRYPTO = auto()
+
+
+TestSuiteClassType = TypeVar("TestSuiteClassType", bound=type["TestSuite"])
+
+
class TestSuite(TestProtocol):
"""The base class with building blocks needed by most test cases.
@@ -85,6 +96,30 @@ class TestSuite(TestProtocol):
_sut_ip_address_egress: Union[IPv4Interface, IPv6Interface]
_tg_ip_address_ingress: Union[IPv4Interface, IPv6Interface]
_tg_ip_address_egress: Union[IPv4Interface, IPv6Interface]
+ testsuite_type: ClassVar[TestSuiteType]
nit: snake_case
+
+ @classmethod
+ def make_decorator(
+ cls, testsuite_type: TestSuiteType
nit: snake_case
+ ) -> Callable[[TestSuiteClassType], TestSuiteClassType]:
+ """Creates a decorator for test suites.
+
+ The decorator casts the decorated function as :class:`TestSuite`,
+ sets it as `testsuite_type` and initializes a variable to
+ determine the type of test suite.
+
+ Args:
+ testsuite_type: A functional, performance, or cryptodev test suite.
nit: snake_case
+
+ Returns:
+ The decorator of a functional, performance, or cryptodev test
suite.
+ """
+
+ def _decorator(testsuite_cls: TestSuiteClassType) ->
TestSuiteClassType:
+ testsuite_cls.testsuite_type = testsuite_type
+ return testsuite_cls
nit: snake_case
+
+ return _decorator
So the above function doesn't really use cls, making it a @static_method
instead of a @class_method. Given static_method is discouraged, it
ultimately means that this method should rather be a module-level
function. But I do understand that we'd also prefer to have consistency
and this is already done for TestCase. So I am ok with either.
def __init__(self, config: BaseConfig) -> None:
"""Initialize the test suite testbed information and basic
configuration.
@@ -270,6 +305,11 @@ def _adjust_addresses(self, packets: list[Packet], expected:
bool = False) -> li
return ret_packets
+#: Test suite type decorators
+func_suite: Callable[[Any], type["TestSuite"]] =
TestSuite.make_decorator(TestSuiteType.FUNC)
+perf_suite: Callable[[Any], type["TestSuite"]] =
TestSuite.make_decorator(TestSuiteType.PERF)
+crypto_suite: Callable[[Any], type["TestSuite"]] =
TestSuite.make_decorator(TestSuiteType.CRYPTO)
+
#: The generic type for a method of an instance of TestSuite
TestSuiteMethodType = TypeVar("TestSuiteMethodType",
bound=Callable[[TestSuite], None])
Many thanks,
Luca