From patchwork Wed Aug 21 14:53:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143234 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 315F045830; Wed, 21 Aug 2024 16:53:21 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B45F0410DF; Wed, 21 Aug 2024 16:53:20 +0200 (CEST) Received: from mail-ej1-f45.google.com (mail-ej1-f45.google.com [209.85.218.45]) by mails.dpdk.org (Postfix) with ESMTP id 89D7D4003C for ; Wed, 21 Aug 2024 16:53:19 +0200 (CEST) Received: by mail-ej1-f45.google.com with SMTP id a640c23a62f3a-a80eab3945eso693544666b.1 for ; Wed, 21 Aug 2024 07:53:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724251999; x=1724856799; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=bCLp+AlnSzegj0a7lHUmvftrvl+gfkJ5N6jHY03IdbE=; b=dO+bOiq7NvqaHULAf3k61n3GExDxTjBjD3qTwpyVB90uTo09LPnTUp1eHktL6g8VeX jHeH1hh4Az1UbNRwP8exBf0JKZCkIvHyqaJUtjO8DbtSaTkzCBMAXHcuGecXkU49QpvG yBWmPh2RI/XhGdEdRuvg+S0M+RJEb4cBNYWYMPb4Ec9lvnSumilcsLCS+MYS+9igeEHz qd48yc+3O26ZDh8oeFaeoMqqtqEyWzFu5ayvv1CWvvW4lOT/OJSKQW5lOsn58qC0nsvl dMOs37q+sgy7ei3wtEsW+IrV5Teh+yZ3dRSEZhDSxVGvSzlf1mDWHLXm7/n+6M7sZ87Y XC0g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724251999; x=1724856799; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=bCLp+AlnSzegj0a7lHUmvftrvl+gfkJ5N6jHY03IdbE=; b=mwXO0mhDb3pe4+HUg0T90pjrdaI3CiXevg5vkGytP5R+XBwnZyy6wL4eBjcS8e6zmJ M7cBR4ZysMAyTKiiB7/kf7g+dzLLxTQgcJUgrMOLMZjr4f6UsAmxFEN2pa7peaOxaXrI M4S1M2t7CHAYvYJHgnO7+FgpLtQQ3CGBdhT831rPrUbGUu/B68I+j6dIrQEQCsbI+lVO yduCuWJd+vl3+/KNNiRATvdM0yKh4q7A37EScYydSQTUW8H0XIUZcMOottXs2O8/YO76 g9wbOLNwTfGSZDrKkEb8a64zhrLPxyS7UCthZRxxmXaUIk0fyyHEXqopkvc7bIJl8cdg ELxA== X-Gm-Message-State: AOJu0Yz7F3d+AlxUbXDBFVW9mTi74r4fzMBkFFlQJuCboEyp3WFsB2kL E54jJBQm++FTedovFTq7ZE/R9wmGMQV4N6hDLQ2m+zvXGwuSqVhBUhFjkTkDqn0= X-Google-Smtp-Source: AGHT+IHGCWCVb5N0ba1ZD1df/dz/lwsJ1fllVi0u/MymFmkpwt/Sm2SbCeQBaV6l7ZiqoVm+3YwSpg== X-Received: by 2002:a17:907:9491:b0:a7a:b643:654f with SMTP id a640c23a62f3a-a866f2d6256mr176088666b.15.1724251998846; Wed, 21 Aug 2024 07:53:18 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:18 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 01/12] dts: fix default device error handling mode Date: Wed, 21 Aug 2024 16:53:04 +0200 Message-Id: <20240821145315.97974-2-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The device_error_handling_mode of testpmd port may not be present, e.g. in VM ports. Fixes: 61d5bc9bf974 ("dts: add port info command to testpmd shell") Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx Reviewed-by: Nicholas Pratte --- dts/framework/remote_session/testpmd_shell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py index a5347b07dc..b4ad253020 100644 --- a/dts/framework/remote_session/testpmd_shell.py +++ b/dts/framework/remote_session/testpmd_shell.py @@ -465,8 +465,8 @@ class TestPmdPort(TextParser): metadata=DeviceCapabilitiesFlag.make_parser(), ) #: - device_error_handling_mode: DeviceErrorHandlingMode = field( - metadata=DeviceErrorHandlingMode.make_parser() + device_error_handling_mode: DeviceErrorHandlingMode | None = field( + default=None, metadata=DeviceErrorHandlingMode.make_parser() ) #: device_private_info: str | None = field( From patchwork Wed Aug 21 14:53:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143235 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id D436345830; Wed, 21 Aug 2024 16:53:33 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 05EF94113C; Wed, 21 Aug 2024 16:53:23 +0200 (CEST) Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) by mails.dpdk.org (Postfix) with ESMTP id 7E4AC4003C for ; Wed, 21 Aug 2024 16:53:20 +0200 (CEST) Received: by mail-ej1-f54.google.com with SMTP id a640c23a62f3a-a7a9cf7d3f3so865986366b.1 for ; Wed, 21 Aug 2024 07:53:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252000; x=1724856800; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=S5VWo4Gx8OSxpZfUPX176vfPt0kJSf620dJ+bgadidc=; b=aWIKOCNTVyORyBHcRzQA55b2luwltFvUcEBYhc2uKjy7dYvGssd0NpTTgKAHBquE21 YDaSn+L9Imcy7EmW7+LUo/RlLuSGPjMQC1ma1oJ2zNYC9n2VxyorQ9fJm5vpUjEL1Fxx EG4kaAUmtvQ/O9Tfj8UH2T0e77F0B8G0tLupluv1bJMdvC6RmtYcxe6sCH90K2uopAi0 qxhibaDXZIecBSy0o+9EXQ8MrHQgudv6I3y3f7qF8lb5DdGsaZxR353ROM+k9CYJIiZN Hrt6Y8J9wMlQMx1Xy1x8Fn0EpxmCKZZJetvsM+wu8G55v2DRk8/D4znZQe1jOgu/XTA0 7gDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252000; x=1724856800; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S5VWo4Gx8OSxpZfUPX176vfPt0kJSf620dJ+bgadidc=; b=Ir7cjndVoiB4y/854hajRwFKohjTAEC8TpZ6yFjDlCcky5CjQdQ8S7PBjy8hKShDnc c+yEsYvaP5ZDsWAjLiGOV+xk0SDHqd/WU+sOhVM2VKlyghEVpVWWqOAxjaCkBrY/50bl qVRH9RP2L+Hz4t/24QE0mmLL0zdVvk2fiBg7I8RMsfTsUTMQbcZbMaDXhnwemRQ6yqqE MI4R3RICYhx8PNGl9YqRZ195y0MpjBPaLvxwP6c33g+I7hDt5ObbHUn5X3dWBf8oT5lT HAmq/aUnQFQy2pQxj/NIVcjX0NQiXGHNqcqwAd9z12djTt0eyB2Df/2QIV3gAOLkP/yZ ozHg== X-Gm-Message-State: AOJu0YxyUxGxXgRzbJK7CLdSDRYJZuUXYKS9ybkpB5aErkOynLjiQNOq AF1F30R+pLwSYKimP++Qi+XpZGnNx1ijpMYX9RV1x3aJ3jxJ5mBxgoKcFptyB+k= X-Google-Smtp-Source: AGHT+IEJ/U9NbA6Vdq3oHJ+cUL98I8vby/L/lQnbersSGQhZMPfAOXnNbcKo1TmgWr4qzgjQWoEqIQ== X-Received: by 2002:a17:907:2d07:b0:a86:667a:8f95 with SMTP id a640c23a62f3a-a866f2aa0e9mr135786266b.22.1724251999904; Wed, 21 Aug 2024 07:53:19 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:19 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 02/12] dts: add the aenum dependency Date: Wed, 21 Aug 2024 16:53:05 +0200 Message-Id: <20240821145315.97974-3-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Regular Python enumerations create only one instance for members with the same value, such as: class MyEnum(Enum): foo = 1 bar = 1 MyEnum.foo and MyEnum.bar are aliases that return the same instance. DTS needs to return different instances in the above scenario so that we can map capabilities with different names to the same function that retrieves the capabilities. Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx Reviewed-by: Nicholas Pratte --- dts/poetry.lock | 14 +++++++++++++- dts/pyproject.toml | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/dts/poetry.lock b/dts/poetry.lock index 2dd8bad498..cf5f6569c6 100644 --- a/dts/poetry.lock +++ b/dts/poetry.lock @@ -1,5 +1,17 @@ # This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +[[package]] +name = "aenum" +version = "3.1.15" +description = "Advanced Enumerations (compatible with Python's stdlib Enum), NamedTuples, and NamedConstants" +optional = false +python-versions = "*" +files = [ + {file = "aenum-3.1.15-py2-none-any.whl", hash = "sha256:27b1710b9d084de6e2e695dab78fe9f269de924b51ae2850170ee7e1ca6288a5"}, + {file = "aenum-3.1.15-py3-none-any.whl", hash = "sha256:e0dfaeea4c2bd362144b87377e2c61d91958c5ed0b4daf89cb6f45ae23af6288"}, + {file = "aenum-3.1.15.tar.gz", hash = "sha256:8cbd76cd18c4f870ff39b24284d3ea028fbe8731a58df3aa581e434c575b9559"}, +] + [[package]] name = "alabaster" version = "0.7.13" @@ -1350,4 +1362,4 @@ jsonschema = ">=4,<5" [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "6db17f96cb31fb463b0b0a31dff9c02aa72641e0bffd8a610033fe2324006c43" +content-hash = "6f20ce05310df93fed1d392160d1653ae5de5c6f260a5865eb3c6111a7c2b394" diff --git a/dts/pyproject.toml b/dts/pyproject.toml index 38281f0e39..6e347852cc 100644 --- a/dts/pyproject.toml +++ b/dts/pyproject.toml @@ -26,6 +26,7 @@ fabric = "^2.7.1" scapy = "^2.5.0" pydocstyle = "6.1.1" typing-extensions = "^4.11.0" +aenum = "^3.1.15" [tool.poetry.group.dev.dependencies] mypy = "^1.10.0" From patchwork Wed Aug 21 14:53:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143236 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id A7C6645830; Wed, 21 Aug 2024 16:53:40 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 346FC41141; Wed, 21 Aug 2024 16:53:24 +0200 (CEST) Received: from mail-ed1-f49.google.com (mail-ed1-f49.google.com [209.85.208.49]) by mails.dpdk.org (Postfix) with ESMTP id C2A2D410F6 for ; Wed, 21 Aug 2024 16:53:21 +0200 (CEST) Received: by mail-ed1-f49.google.com with SMTP id 4fb4d7f45d1cf-5becc379f3fso5591416a12.3 for ; Wed, 21 Aug 2024 07:53:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252001; x=1724856801; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=v/Nzhk+D3Z56sHNqT90h0elAGnx78Ej490vp0qMFOXk=; b=v9OUNE8rE/rnBT/7VoFj0ja/UvoWHsU/qIHL0+KARlbfwsX8chfyP8DaMKdS4MnPg1 8nmfeJope+Nx4ep1OH/W1ryUVaIrohI4gaTl4ADJm38wx9uCJIICNCo5BAeC0Av5afEz CUn9rJ43K9NuIZ7FaV8qlVrHfwMUc3q8U90EuwddsEky1UzFzneOZxsidiYbN8eBNp6K LsU0QtHCecCy4vobJwThVTbQ9cQ4mdbSJ8VxqTZsJ0LN/TkErtWvf3UGDYOfyZv2X39J dYhZ+gglHoc5XYQAHGZan+pkxnzGgXDGZxbeGrklEaxpejZOpxl0ZdhWqs/SRQPD7QO+ VtGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252001; x=1724856801; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=v/Nzhk+D3Z56sHNqT90h0elAGnx78Ej490vp0qMFOXk=; b=ioUh6Rq1Jf8VbtkyXyntc1OOWjJ/Vf3zMnzhw71MBMdwJYGpYvVHegC950199P1XU/ ZWaxNMAdOoGS9s4b48c8L4gmxVoc9YaJPbYTih2qHFSpVVEyGpYRQVuu6c4+gUXLOg3d oFOO5826uYWhcJ/VbIpZmelztb3nEP0568OhKlQ6l4Yxq4JaIERO9LMB/zzImjlJZTFW SBhDf5D/K1brQ72Mv0seFyraaJbF7ZyWPkLrupYhIDL3rpllaAzkyxV5TA8QWXRnAMfa bWnWC7l3r2Dju5dVkKO61VCZ/7uh4PtOYWILy+/8E3LlW6goEGbApBndxGuj2H/ziJoH /9Jw== X-Gm-Message-State: AOJu0YxvpMlbj0KrZl8Q6fvVCDa1fBPQ95qo7VAHTwydtarmPyfXdh1t Js716I7DUn5R+p3D6QVcpBcMjFJQBPuk6KhRJ07PGJJGyHWzjxZ/CUwJj6d8960= X-Google-Smtp-Source: AGHT+IGeobAaObuxeCVHvxiHwj2Yeln6iikNKoZ4ghPUqvwhW4gWs1oXMGvWQv6ssOoKoGgqcohuEQ== X-Received: by 2002:a17:907:9815:b0:a7a:bd5a:1eb2 with SMTP id a640c23a62f3a-a866f8f46a9mr151082566b.54.1724252001033; Wed, 21 Aug 2024 07:53:21 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:20 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 03/12] dts: add test case decorators Date: Wed, 21 Aug 2024 16:53:06 +0200 Message-Id: <20240821145315.97974-4-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add decorators for functional and performance test cases. These decorators add attributes to the decorated test cases. With the addition of decorators, we change the test case discovery mechanism from looking at test case names according to a regex to simply checking an attribute of the function added with one of the decorators. The decorators allow us to add further variables to test cases. Also move the test case filtering to TestSuite while changing the mechanism to separate the logic in a more sensible manner. Bugzilla ID: 1460 Signed-off-by: Juraj Linkeš Reviewed-by: Dean Marx Reviewed-by: Nicholas Pratte --- dts/framework/runner.py | 93 ++++------------ dts/framework/test_result.py | 5 +- dts/framework/test_suite.py | 125 +++++++++++++++++++++- dts/tests/TestSuite_hello_world.py | 8 +- dts/tests/TestSuite_os_udp.py | 3 +- dts/tests/TestSuite_pmd_buffer_scatter.py | 3 +- dts/tests/TestSuite_smoke_tests.py | 6 +- 7 files changed, 160 insertions(+), 83 deletions(-) diff --git a/dts/framework/runner.py b/dts/framework/runner.py index 6b6f6a05f5..525f119ab6 100644 --- a/dts/framework/runner.py +++ b/dts/framework/runner.py @@ -20,11 +20,10 @@ import importlib import inspect import os -import re import sys from pathlib import Path -from types import FunctionType -from typing import Iterable, Sequence +from types import MethodType +from typing import Iterable from framework.testbed_model.sut_node import SutNode from framework.testbed_model.tg_node import TGNode @@ -53,7 +52,7 @@ TestSuiteResult, TestSuiteWithCases, ) -from .test_suite import TestSuite +from .test_suite import TestCase, TestSuite class DTSRunner: @@ -232,9 +231,9 @@ def _get_test_suites_with_cases( for test_suite_config in test_suite_configs: test_suite_class = self._get_test_suite_class(test_suite_config.test_suite) - test_cases = [] - func_test_cases, perf_test_cases = self._filter_test_cases( - test_suite_class, test_suite_config.test_cases + test_cases: list[type[TestCase]] = [] + func_test_cases, perf_test_cases = test_suite_class.get_test_cases( + test_suite_config.test_cases ) if func: test_cases.extend(func_test_cases) @@ -309,57 +308,6 @@ def is_test_suite(object) -> bool: f"Couldn't find any valid test suites in {test_suite_module.__name__}." ) - def _filter_test_cases( - self, test_suite_class: type[TestSuite], test_cases_to_run: Sequence[str] - ) -> tuple[list[FunctionType], list[FunctionType]]: - """Filter `test_cases_to_run` from `test_suite_class`. - - There are two rounds of filtering if `test_cases_to_run` is not empty. - The first filters `test_cases_to_run` from all methods of `test_suite_class`. - Then the methods are separated into functional and performance test cases. - If a method matches neither the functional nor performance name prefix, it's an error. - - Args: - test_suite_class: The class of the test suite. - test_cases_to_run: Test case names to filter from `test_suite_class`. - If empty, return all matching test cases. - - Returns: - A list of test case methods that should be executed. - - Raises: - ConfigurationError: If a test case from `test_cases_to_run` is not found - or it doesn't match either the functional nor performance name prefix. - """ - func_test_cases = [] - perf_test_cases = [] - name_method_tuples = inspect.getmembers(test_suite_class, inspect.isfunction) - if test_cases_to_run: - name_method_tuples = [ - (name, method) for name, method in name_method_tuples if name in test_cases_to_run - ] - if len(name_method_tuples) < len(test_cases_to_run): - missing_test_cases = set(test_cases_to_run) - { - name for name, _ in name_method_tuples - } - raise ConfigurationError( - f"Test cases {missing_test_cases} not found among methods " - f"of {test_suite_class.__name__}." - ) - - for test_case_name, test_case_method in name_method_tuples: - if re.match(self._func_test_case_regex, test_case_name): - func_test_cases.append(test_case_method) - elif re.match(self._perf_test_case_regex, test_case_name): - perf_test_cases.append(test_case_method) - elif test_cases_to_run: - raise ConfigurationError( - f"Method '{test_case_name}' matches neither " - f"a functional nor a performance test case name." - ) - - return func_test_cases, perf_test_cases - def _connect_nodes_and_run_test_run( self, sut_nodes: dict[str, SutNode], @@ -607,7 +555,7 @@ def _run_test_suite( def _execute_test_suite( self, test_suite: TestSuite, - test_cases: Iterable[FunctionType], + test_cases: Iterable[type[TestCase]], test_suite_result: TestSuiteResult, ) -> None: """Execute all `test_cases` in `test_suite`. @@ -618,29 +566,29 @@ def _execute_test_suite( Args: test_suite: The test suite object. - test_cases: The list of test case methods. + test_cases: The list of test case functions. test_suite_result: The test suite level result object associated with the current test suite. """ self._logger.set_stage(DtsStage.test_suite) - for test_case_method in test_cases: - test_case_name = test_case_method.__name__ + for test_case in test_cases: + test_case_name = test_case.__name__ test_case_result = test_suite_result.add_test_case(test_case_name) all_attempts = SETTINGS.re_run + 1 attempt_nr = 1 - self._run_test_case(test_suite, test_case_method, test_case_result) + self._run_test_case(test_suite, test_case, test_case_result) while not test_case_result and attempt_nr < all_attempts: attempt_nr += 1 self._logger.info( f"Re-running FAILED test case '{test_case_name}'. " f"Attempt number {attempt_nr} out of {all_attempts}." ) - self._run_test_case(test_suite, test_case_method, test_case_result) + self._run_test_case(test_suite, test_case, test_case_result) def _run_test_case( self, test_suite: TestSuite, - test_case_method: FunctionType, + test_case: type[TestCase], test_case_result: TestCaseResult, ) -> None: """Setup, execute and teardown `test_case_method` from `test_suite`. @@ -649,11 +597,11 @@ def _run_test_case( Args: test_suite: The test suite object. - test_case_method: The test case method. + test_case: The test case function. test_case_result: The test case level result object associated with the current test case. """ - test_case_name = test_case_method.__name__ + test_case_name = test_case.__name__ try: # run set_up function for each case @@ -668,7 +616,7 @@ def _run_test_case( else: # run test case if setup was successful - self._execute_test_case(test_suite, test_case_method, test_case_result) + self._execute_test_case(test_suite, test_case, test_case_result) finally: try: @@ -686,21 +634,22 @@ def _run_test_case( def _execute_test_case( self, test_suite: TestSuite, - test_case_method: FunctionType, + test_case: type[TestCase], test_case_result: TestCaseResult, ) -> None: """Execute `test_case_method` from `test_suite`, record the result and handle failures. Args: test_suite: The test suite object. - test_case_method: The test case method. + test_case: The test case function. test_case_result: The test case level result object associated with the current test case. """ - test_case_name = test_case_method.__name__ + test_case_name = test_case.__name__ try: self._logger.info(f"Starting test case execution: {test_case_name}") - test_case_method(test_suite) + # Explicit method binding is required, otherwise mypy complains + MethodType(test_case, test_suite)() test_case_result.update(Result.PASS) self._logger.info(f"Test case execution PASSED: {test_case_name}") diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py index 5694a2482b..b1ca584523 100644 --- a/dts/framework/test_result.py +++ b/dts/framework/test_result.py @@ -27,7 +27,6 @@ from collections.abc import MutableSequence from dataclasses import dataclass from enum import Enum, auto -from types import FunctionType from typing import Union from .config import ( @@ -44,7 +43,7 @@ from .exception import DTSError, ErrorSeverity from .logger import DTSLogger from .settings import SETTINGS -from .test_suite import TestSuite +from .test_suite import TestCase, TestSuite @dataclass(slots=True, frozen=True) @@ -63,7 +62,7 @@ class is to hold a subset of test cases (which could be all test cases) because """ test_suite_class: type[TestSuite] - test_cases: list[FunctionType] + test_cases: list[type[TestCase]] def create_config(self) -> TestSuiteConfig: """Generate a :class:`TestSuiteConfig` from the stored test suite with test cases. diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py index 694b2eba65..b4ee0f9039 100644 --- a/dts/framework/test_suite.py +++ b/dts/framework/test_suite.py @@ -13,8 +13,11 @@ * Test case verification. """ +import inspect +from collections.abc import Callable, Sequence +from enum import Enum, auto from ipaddress import IPv4Interface, IPv6Interface, ip_interface -from typing import ClassVar, Union +from typing import ClassVar, Protocol, TypeVar, Union, cast from scapy.layers.inet import IP # type: ignore[import-untyped] from scapy.layers.l2 import Ether # type: ignore[import-untyped] @@ -27,7 +30,7 @@ PacketFilteringConfig, ) -from .exception import TestCaseVerifyError +from .exception import ConfigurationError, TestCaseVerifyError from .logger import DTSLogger, get_dts_logger from .utils import get_packet_summaries @@ -120,6 +123,68 @@ def _process_links(self) -> None: ): self._port_links.append(PortLink(sut_port=sut_port, tg_port=tg_port)) + @classmethod + def get_test_cases( + cls, test_case_sublist: Sequence[str] | None = None + ) -> tuple[set[type["TestCase"]], set[type["TestCase"]]]: + """Filter `test_case_subset` from this class. + + Test cases are regular (or bound) methods decorated with :func:`func_test` + or :func:`perf_test`. + + Args: + test_case_sublist: Test case names to filter from this class. + If empty or :data:`None`, return all test cases. + + Returns: + The filtered test case functions. This method returns functions as opposed to methods, + as methods are bound to instances and this method only has access to the class. + + Raises: + ConfigurationError: If a test case from `test_case_subset` is not found. + """ + + def is_test_case(function: Callable) -> bool: + if inspect.isfunction(function): + # TestCase is not used at runtime, so we can't use isinstance() with `function`. + # But function.test_type exists. + if hasattr(function, "test_type"): + return isinstance(function.test_type, TestCaseType) + return False + + if test_case_sublist is None: + test_case_sublist = [] + + # the copy is needed so that the condition "elif test_case_sublist" doesn't + # change mid-cycle + test_case_sublist_copy = list(test_case_sublist) + func_test_cases = set() + perf_test_cases = set() + + for test_case_name, test_case_function in inspect.getmembers(cls, is_test_case): + if test_case_name in test_case_sublist_copy: + # if test_case_sublist_copy is non-empty, remove the found test case + # so that we can look at the remainder at the end + test_case_sublist_copy.remove(test_case_name) + elif test_case_sublist: + # if the original list is not empty (meaning we're filtering test cases), + # we're dealing with a test case we would've + # removed in the other branch; since we didn't, we don't want to run it + continue + + match test_case_function.test_type: + case TestCaseType.PERFORMANCE: + perf_test_cases.add(test_case_function) + case TestCaseType.FUNCTIONAL: + func_test_cases.add(test_case_function) + + if test_case_sublist_copy: + raise ConfigurationError( + f"Test cases {test_case_sublist_copy} not found among functions of {cls.__name__}." + ) + + return func_test_cases, perf_test_cases + def set_up_suite(self) -> None: """Set up test fixtures common to all test cases. @@ -365,3 +430,59 @@ def _verify_l3_packet(self, received_packet: IP, expected_packet: IP) -> bool: if received_packet.src != expected_packet.src or received_packet.dst != expected_packet.dst: return False return True + + +#: The generic type for a method of an instance of TestSuite +TestSuiteMethodType = TypeVar("TestSuiteMethodType", bound=Callable[[TestSuite], None]) + + +class TestCaseType(Enum): + """The types of test cases.""" + + #: + FUNCTIONAL = auto() + #: + PERFORMANCE = auto() + + +class TestCase(Protocol[TestSuiteMethodType]): + """Definition of the test case type for static type checking purposes. + + The type is applied to test case functions through a decorator, which casts the decorated + test case function to :class:`TestCase` and sets common variables. + """ + + #: + test_type: ClassVar[TestCaseType] + #: necessary for mypy so that it can treat this class as the function it's shadowing + __call__: TestSuiteMethodType + + @classmethod + def make_decorator( + cls, test_case_type: TestCaseType + ) -> Callable[[TestSuiteMethodType], type["TestCase"]]: + """Create a decorator for test suites. + + The decorator casts the decorated function as :class:`TestCase`, + sets it as `test_case_type` + and initializes common variables defined in :class:`RequiresCapabilities`. + + Args: + test_case_type: Either a functional or performance test case. + + Returns: + The decorator of a functional or performance test case. + """ + + def _decorator(func: TestSuiteMethodType) -> type[TestCase]: + test_case = cast(type[TestCase], func) + test_case.test_type = test_case_type + return test_case + + return _decorator + + +#: The decorator for functional test cases. +func_test: Callable = TestCase.make_decorator(TestCaseType.FUNCTIONAL) +#: The decorator for performance test cases. +perf_test: Callable = TestCase.make_decorator(TestCaseType.PERFORMANCE) diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py index d958f99030..16d064ffeb 100644 --- a/dts/tests/TestSuite_hello_world.py +++ b/dts/tests/TestSuite_hello_world.py @@ -8,7 +8,7 @@ """ from framework.remote_session.dpdk_shell import compute_eal_params -from framework.test_suite import TestSuite +from framework.test_suite import TestSuite, func_test from framework.testbed_model.cpu import ( LogicalCoreCount, LogicalCoreCountFilter, @@ -27,7 +27,8 @@ def set_up_suite(self) -> None: """ self.app_helloworld_path = self.sut_node.build_dpdk_app("helloworld") - def test_hello_world_single_core(self) -> None: + @func_test + def hello_world_single_core(self) -> None: """Single core test case. Steps: @@ -46,7 +47,8 @@ def test_hello_world_single_core(self) -> None: f"helloworld didn't start on lcore{lcores[0]}", ) - def test_hello_world_all_cores(self) -> None: + @func_test + def hello_world_all_cores(self) -> None: """All cores test case. Steps: diff --git a/dts/tests/TestSuite_os_udp.py b/dts/tests/TestSuite_os_udp.py index a78bd74139..beaa5f425d 100644 --- a/dts/tests/TestSuite_os_udp.py +++ b/dts/tests/TestSuite_os_udp.py @@ -10,7 +10,7 @@ from scapy.layers.inet import IP, UDP # type: ignore[import-untyped] from scapy.layers.l2 import Ether # type: ignore[import-untyped] -from framework.test_suite import TestSuite +from framework.test_suite import TestSuite, func_test class TestOsUdp(TestSuite): @@ -26,6 +26,7 @@ def set_up_suite(self) -> None: self.sut_node.bind_ports_to_driver(for_dpdk=False) self.configure_testbed_ipv4() + @func_test def test_os_udp(self) -> None: """Basic UDP IPv4 traffic test case. diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py index 0d8e101e5c..020fb0ab62 100644 --- a/dts/tests/TestSuite_pmd_buffer_scatter.py +++ b/dts/tests/TestSuite_pmd_buffer_scatter.py @@ -24,7 +24,7 @@ from framework.params.testpmd import SimpleForwardingModes from framework.remote_session.testpmd_shell import TestPmdShell -from framework.test_suite import TestSuite +from framework.test_suite import TestSuite, func_test class TestPmdBufferScatter(TestSuite): @@ -123,6 +123,7 @@ def pmd_scatter(self, mbsize: int) -> None: f"{offset}.", ) + @func_test def test_scatter_mbuf_2048(self) -> None: """Run the :meth:`pmd_scatter` test with `mbsize` set to 2048.""" self.pmd_scatter(mbsize=2048) diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py index c0b0e6bb00..94f90d9327 100644 --- a/dts/tests/TestSuite_smoke_tests.py +++ b/dts/tests/TestSuite_smoke_tests.py @@ -17,7 +17,7 @@ from framework.config import PortConfig from framework.remote_session.testpmd_shell import TestPmdShell from framework.settings import SETTINGS -from framework.test_suite import TestSuite +from framework.test_suite import TestSuite, func_test from framework.utils import REGEX_FOR_PCI_ADDRESS @@ -47,6 +47,7 @@ def set_up_suite(self) -> None: self.dpdk_build_dir_path = self.sut_node.remote_dpdk_build_dir self.nics_in_node = self.sut_node.config.ports + @func_test def test_unit_tests(self) -> None: """DPDK meson ``fast-tests`` unit tests. @@ -63,6 +64,7 @@ def test_unit_tests(self) -> None: privileged=True, ) + @func_test def test_driver_tests(self) -> None: """DPDK meson ``driver-tests`` unit tests. @@ -91,6 +93,7 @@ def test_driver_tests(self) -> None: privileged=True, ) + @func_test def test_devices_listed_in_testpmd(self) -> None: """Testpmd device discovery. @@ -108,6 +111,7 @@ def test_devices_listed_in_testpmd(self) -> None: "please check your configuration", ) + @func_test def test_device_bound_to_driver(self) -> None: """Device driver in OS. From patchwork Wed Aug 21 14:53:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143237 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 9FD7E45830; Wed, 21 Aug 2024 16:53:49 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id CC5E041611; Wed, 21 Aug 2024 16:53:26 +0200 (CEST) Received: from mail-ed1-f53.google.com (mail-ed1-f53.google.com [209.85.208.53]) by mails.dpdk.org (Postfix) with ESMTP id DF1254111B for ; Wed, 21 Aug 2024 16:53:22 +0200 (CEST) Received: by mail-ed1-f53.google.com with SMTP id 4fb4d7f45d1cf-5bec4e00978so6110672a12.0 for ; Wed, 21 Aug 2024 07:53:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252002; x=1724856802; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xLJGrZT/Vkt94m1c7pc/cyz1oXmi9UGnpHjBfDc+9Xs=; b=u5pNtVt+lvwbYINicnRveQ29TrqPitrB2iTUpUPSJ2UPEgP2uqrSulu2DVhej8womD 82hCIIAiipBkM0fqpmZNJONfIIK7N+E5bnSc/CPgAsi/5XES6Yhhw6WBXRwfoexVAjDs bMpwSgptVfJrwziizDdPVZI6HhJchFAeIE4UvzPlkLT5W7EGB+Wa+ksA6unMAkHHEEO7 6C2BSmwbKCB9+Btoqd6cgqq3KhAjgWdNb9+SvlBQvYFTNOarYOW6lHTk8xQA2sATwK0L 4zwZzX5z3fOAyXSqZRn1PIN9oZiSOJLB09Bfk0wXceSZmttnZ4k6Krv1I580nJKIDN44 kFMw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252002; x=1724856802; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xLJGrZT/Vkt94m1c7pc/cyz1oXmi9UGnpHjBfDc+9Xs=; b=TrCaWW4O4nqIV9ensRVt54FlB4DuvMUa6uXLccGjK+EQ+k6viPQxyxUR2TpLvjyumX YxJxyoQQHi8j78CEyuO4VcxQaICtfmsr/LRSfMMSJHnm/KFfGrKfwoWu8B43aMP1uIu4 SEoFDkZlMcD54/ynw3DWe5h/u8agCcfYX62nH3VJX33qKpsowREsjRLQL61aiREw2hS7 2w6aWa7h4JjvVSoAS7c/7is5U75/JkzWOmsxH4+bO5ICQ5VcaNNFpUjlVOR3elLQz/DI xY4Cqri4guZhIg9QxWKWYDS5OIEYCKAAwH4UZ7FKyTyYYptfbYAQocpUqQpb0GIrOLZo oHAA== X-Gm-Message-State: AOJu0Yz60YqPlsrFyZxlzECjOqn7lztA6okqoYkmcdF4TKwL5cjSfa9T JWHXGftB+4jw1cpnMsFXQiyP7foAwXuC6v9hv2HcD+0KUiCx6TWaOtwF0/Ltd7o= X-Google-Smtp-Source: AGHT+IED2ZRXwC1pJGrRRymkF1xa6lTdG0/Ta8uT3OTXU9+Ufnq/+lK6tCfnAidEWIyhy0uDuWzabA== X-Received: by 2002:a17:907:d3d5:b0:a86:6fb3:fda5 with SMTP id a640c23a62f3a-a866fb40780mr181190966b.32.1724252002167; Wed, 21 Aug 2024 07:53:22 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:21 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 04/12] dts: add mechanism to skip test cases or suites Date: Wed, 21 Aug 2024 16:53:07 +0200 Message-Id: <20240821145315.97974-5-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org If a test case is not relevant to the testing environment (such as when a NIC doesn't support a tested feature), the framework should skip it. The mechanism is a skeleton without actual logic that would set a test case or suite to be skipped. The mechanism uses a protocol to extend test suites and test cases with additional attributes that track whether the test case or suite should be skipped the reason for skipping it. Also update the results module with the new SKIP result. Signed-off-by: Juraj Linkeš Reviewed-by: Dean Marx --- dts/framework/runner.py | 34 +++++++--- dts/framework/test_result.py | 77 ++++++++++++++--------- dts/framework/test_suite.py | 7 ++- dts/framework/testbed_model/capability.py | 28 +++++++++ 4 files changed, 109 insertions(+), 37 deletions(-) create mode 100644 dts/framework/testbed_model/capability.py diff --git a/dts/framework/runner.py b/dts/framework/runner.py index 525f119ab6..55357ea1fe 100644 --- a/dts/framework/runner.py +++ b/dts/framework/runner.py @@ -477,7 +477,20 @@ def _run_test_suites( for test_suite_with_cases in test_suites_with_cases: test_suite_result = build_target_result.add_test_suite(test_suite_with_cases) try: - self._run_test_suite(sut_node, tg_node, test_suite_result, test_suite_with_cases) + if not test_suite_with_cases.skip: + self._run_test_suite( + sut_node, + tg_node, + test_suite_result, + test_suite_with_cases, + ) + else: + self._logger.info( + f"Test suite execution SKIPPED: " + f"'{test_suite_with_cases.test_suite_class.__name__}'. Reason: " + f"{test_suite_with_cases.test_suite_class.skip_reason}" + ) + test_suite_result.update_setup(Result.SKIP) except BlockingTestSuiteError as e: self._logger.exception( f"An error occurred within {test_suite_with_cases.test_suite_class.__name__}. " @@ -576,14 +589,21 @@ def _execute_test_suite( test_case_result = test_suite_result.add_test_case(test_case_name) all_attempts = SETTINGS.re_run + 1 attempt_nr = 1 - self._run_test_case(test_suite, test_case, test_case_result) - while not test_case_result and attempt_nr < all_attempts: - attempt_nr += 1 + if not test_case.skip: + self._run_test_case(test_suite, test_case, test_case_result) + while not test_case_result and attempt_nr < all_attempts: + attempt_nr += 1 + self._logger.info( + f"Re-running FAILED test case '{test_case_name}'. " + f"Attempt number {attempt_nr} out of {all_attempts}." + ) + self._run_test_case(test_suite, test_case, test_case_result) + else: self._logger.info( - f"Re-running FAILED test case '{test_case_name}'. " - f"Attempt number {attempt_nr} out of {all_attempts}." + f"Test case execution SKIPPED: {test_case_name}. Reason: " + f"{test_case.skip_reason}" ) - self._run_test_case(test_suite, test_case, test_case_result) + test_case_result.update_setup(Result.SKIP) def _run_test_case( self, diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py index b1ca584523..306b100bc6 100644 --- a/dts/framework/test_result.py +++ b/dts/framework/test_result.py @@ -75,6 +75,20 @@ def create_config(self) -> TestSuiteConfig: test_cases=[test_case.__name__ for test_case in self.test_cases], ) + @property + def skip(self) -> bool: + """Skip the test suite if all test cases or the suite itself are to be skipped. + + Returns: + :data:`True` if the test suite should be skipped, :data:`False` otherwise. + """ + all_test_cases_skipped = True + for test_case in self.test_cases: + if not test_case.skip: + all_test_cases_skipped = False + break + return all_test_cases_skipped or self.test_suite_class.skip + class Result(Enum): """The possible states that a setup, a teardown or a test case may end up in.""" @@ -86,12 +100,12 @@ class Result(Enum): #: ERROR = auto() #: - SKIP = auto() - #: BLOCK = auto() + #: + SKIP = auto() def __bool__(self) -> bool: - """Only PASS is True.""" + """Only :attr:`PASS` is True.""" return self is self.PASS @@ -169,12 +183,13 @@ def update_setup(self, result: Result, error: Exception | None = None) -> None: self.setup_result.result = result self.setup_result.error = error - if result in [Result.BLOCK, Result.ERROR, Result.FAIL]: - self.update_teardown(Result.BLOCK) - self._block_result() + if result != Result.PASS: + result_to_mark = Result.BLOCK if result != Result.SKIP else Result.SKIP + self.update_teardown(result_to_mark) + self._mark_results(result_to_mark) - def _block_result(self) -> None: - r"""Mark the result as :attr:`~Result.BLOCK`\ed. + def _mark_results(self, result) -> None: + """Mark the result as well as the child result as `result`. The blocking of child results should be done in overloaded methods. """ @@ -391,11 +406,11 @@ def add_sut_info(self, sut_info: NodeInfo) -> None: self.sut_os_version = sut_info.os_version self.sut_kernel_version = sut_info.kernel_version - def _block_result(self) -> None: - r"""Mark the result as :attr:`~Result.BLOCK`\ed.""" + def _mark_results(self, result) -> None: + """Mark the result as well as the child result as `result`.""" for build_target in self._config.build_targets: child_result = self.add_build_target(build_target) - child_result.update_setup(Result.BLOCK) + child_result.update_setup(result) class BuildTargetResult(BaseResult): @@ -465,11 +480,11 @@ def add_build_target_info(self, versions: BuildTargetInfo) -> None: self.compiler_version = versions.compiler_version self.dpdk_version = versions.dpdk_version - def _block_result(self) -> None: - r"""Mark the result as :attr:`~Result.BLOCK`\ed.""" + def _mark_results(self, result) -> None: + """Mark the result as well as the child result as `result`.""" for test_suite_with_cases in self._test_suites_with_cases: child_result = self.add_test_suite(test_suite_with_cases) - child_result.update_setup(Result.BLOCK) + child_result.update_setup(result) class TestSuiteResult(BaseResult): @@ -509,11 +524,11 @@ def add_test_case(self, test_case_name: str) -> "TestCaseResult": self.child_results.append(result) return result - def _block_result(self) -> None: - r"""Mark the result as :attr:`~Result.BLOCK`\ed.""" + def _mark_results(self, result) -> None: + """Mark the result as well as the child result as `result`.""" for test_case_method in self._test_suite_with_cases.test_cases: child_result = self.add_test_case(test_case_method.__name__) - child_result.update_setup(Result.BLOCK) + child_result.update_setup(result) class TestCaseResult(BaseResult, FixtureResult): @@ -567,9 +582,9 @@ def add_stats(self, statistics: "Statistics") -> None: """ statistics += self.result - def _block_result(self) -> None: - r"""Mark the result as :attr:`~Result.BLOCK`\ed.""" - self.update(Result.BLOCK) + def _mark_results(self, result) -> None: + r"""Mark the result as `result`.""" + self.update(result) def __bool__(self) -> bool: """The test case passed only if setup, teardown and the test case itself passed.""" @@ -583,7 +598,8 @@ class Statistics(dict): The data are stored in the following keys: - * **PASS RATE** (:class:`int`) -- The FAIL/PASS ratio of all test cases. + * **PASS RATE** (:class:`int`) -- The :attr:`~Result.FAIL`/:attr:`~Result.PASS` ratio + of all test cases. * **DPDK VERSION** (:class:`str`) -- The tested DPDK version. """ @@ -600,22 +616,27 @@ def __init__(self, dpdk_version: str | None): self["DPDK VERSION"] = dpdk_version def __iadd__(self, other: Result) -> "Statistics": - """Add a Result to the final count. + """Add a :class:`Result` to the final count. + + :attr:`~Result.SKIP` is not taken into account Example: - stats: Statistics = Statistics() # empty Statistics - stats += Result.PASS # add a Result to `stats` + stats: Statistics = Statistics() # empty :class:`Statistics` + stats += Result.PASS # add a :class:`Result` to `stats` Args: - other: The Result to add to this statistics object. + other: The :class:`Result` to add to this statistics object. Returns: The modified statistics object. """ self[other.name] += 1 - self["PASS RATE"] = ( - float(self[Result.PASS.name]) * 100 / sum(self[result.name] for result in Result) - ) + if other != Result.SKIP: + self["PASS RATE"] = ( + float(self[Result.PASS.name]) + * 100 + / sum([self[result.name] for result in Result if result != Result.SKIP]) + ) return self def __str__(self) -> str: diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py index b4ee0f9039..c59fc9c6e6 100644 --- a/dts/framework/test_suite.py +++ b/dts/framework/test_suite.py @@ -23,6 +23,7 @@ from scapy.layers.l2 import Ether # type: ignore[import-untyped] from scapy.packet import Packet, Padding # type: ignore[import-untyped] +from framework.testbed_model.capability import TestProtocol from framework.testbed_model.port import Port, PortLink from framework.testbed_model.sut_node import SutNode from framework.testbed_model.tg_node import TGNode @@ -35,7 +36,7 @@ from .utils import get_packet_summaries -class TestSuite: +class TestSuite(TestProtocol): """The base class with building blocks needed by most test cases. * Test suite setup/cleanup methods to override, @@ -445,7 +446,7 @@ class TestCaseType(Enum): PERFORMANCE = auto() -class TestCase(Protocol[TestSuiteMethodType]): +class TestCase(TestProtocol, Protocol[TestSuiteMethodType]): """Definition of the test case type for static type checking purposes. The type is applied to test case functions through a decorator, which casts the decorated @@ -476,6 +477,8 @@ def make_decorator( def _decorator(func: TestSuiteMethodType) -> type[TestCase]: test_case = cast(type[TestCase], func) + test_case.skip = cls.skip + test_case.skip_reason = cls.skip_reason test_case.test_type = test_case_type return test_case diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py new file mode 100644 index 0000000000..662f691a0e --- /dev/null +++ b/dts/framework/testbed_model/capability.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 PANTHEON.tech s.r.o. + +"""Testbed capabilities. + +This module provides a protocol that defines the common attributes of test cases and suites. +""" + +from collections.abc import Sequence +from typing import ClassVar, Protocol + + +class TestProtocol(Protocol): + """Common test suite and test case attributes.""" + + #: Whether to skip the test case or suite. + skip: ClassVar[bool] = False + #: The reason for skipping the test case or suite. + skip_reason: ClassVar[str] = "" + + @classmethod + def get_test_cases(cls, test_case_sublist: Sequence[str] | None = None) -> tuple[set, set]: + """Get test cases. Should be implemented by subclasses containing test cases. + + Raises: + NotImplementedError: The subclass does not implement the method. + """ + raise NotImplementedError() From patchwork Wed Aug 21 14:53:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143238 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 438E445830; Wed, 21 Aug 2024 16:53:57 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 05E8A4114B; Wed, 21 Aug 2024 16:53:28 +0200 (CEST) Received: from mail-lj1-f182.google.com (mail-lj1-f182.google.com [209.85.208.182]) by mails.dpdk.org (Postfix) with ESMTP id 459CB41143 for ; Wed, 21 Aug 2024 16:53:24 +0200 (CEST) Received: by mail-lj1-f182.google.com with SMTP id 38308e7fff4ca-2f3f163e379so27539581fa.3 for ; Wed, 21 Aug 2024 07:53:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252004; x=1724856804; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=hYJEbeWbSMr0BoQOmMotFUlNe9bt88+mvzsiHxqUkAs=; b=b0FdxZIiIp/LpMHt/YNb6po8RHqGz+jexFpA5Jzl2ukb8C8gf3VCwvGydm8SJCmwtN O7tf7Ylv3VGj9NcaGZig83F77lViI6n8sGIQKMAaEDJK4QIDhCvmP5aJWZ+NDqackVsb JCAutCjmIAfBvjIu6sfO61HlT85cgMZ0GCGZCEFpA70JrG0HvigOpnJBMhRJP7CZYtni SW8wY9Swgs/p4OMcBri9ngR+FzW8OYONkQYHlEPI/SNhuE8kTrak3AYA281lGMS/psCm q//g/HqVuYR832W180LOgFqIjidbg1wgwpFzP8VewZsjxMtUT+EaKkTWu19ZLzWujJ3X DI+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252004; x=1724856804; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hYJEbeWbSMr0BoQOmMotFUlNe9bt88+mvzsiHxqUkAs=; b=cWrKd3WOlwjzRGHQhlDMj0XrvuoBSi3nt+KUjcUPn7orvhCnA07k4ZqMNnWA7HlDeF V7pQf6WMserFU/58deIM//rbEiW0XhfrUbDNMm6KT/VKr7TLEjXMc2kx4q6FNTb+kmCo wVzxvrDzRX5tj8SbrP3z2ZzRATJjFIlgDtJAZwAGTzWSl6ZUgdgX4HsHnminewX5OhV8 SD5GvqC0cieWkR742vflkoRLmiwcBQuvawySyF2OHe4GUQD+RVgImOHUtwac6KahLKIm +EhLK5Wn64hNu6fiXXd7L9NqQcgejixgjigFqIvGpqrPXG8KZ3JMLZ9H1srWKGPIcDZM 6N2w== X-Gm-Message-State: AOJu0YzO2eVH6H2TKqgwH3E1Y85uiH3wlkwsT+QwZmzp+NQnHpx5EMEZ I2hwT1pqdJWpSRWLkPHLfrNDr4Q+pN7a20MuxoLiNltTnAUs7yMD84hNhuRWvWk= X-Google-Smtp-Source: AGHT+IH5ICBRkJ99Dlk/YFNtu//ILV/B1oo98U/yaMpF360lmpjmvErjUi9hA/zzlpNU+Bh132G7og== X-Received: by 2002:a2e:7019:0:b0:2ec:5685:f061 with SMTP id 38308e7fff4ca-2f3f887b324mr17096561fa.21.1724252003456; Wed, 21 Aug 2024 07:53:23 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:23 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 05/12] dts: add support for simpler topologies Date: Wed, 21 Aug 2024 16:53:08 +0200 Message-Id: <20240821145315.97974-6-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org We currently assume there are two links between the SUT and TG nodes, but that's too strict, even for some of the already existing test cases. Add support for topologies with less than two links. For topologies with no links, dummy ports are used. The expectation is that test suites or cases that don't require any links won't be using methods that use ports. Any test suites or cases requiring links will be skipped in topologies with no links, but this feature is not implemented in this commit. Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx --- dts/framework/runner.py | 6 +- dts/framework/test_suite.py | 32 +++---- dts/framework/testbed_model/node.py | 2 +- dts/framework/testbed_model/port.py | 4 +- dts/framework/testbed_model/topology.py | 101 ++++++++++++++++++++++ dts/tests/TestSuite_pmd_buffer_scatter.py | 2 +- 6 files changed, 120 insertions(+), 27 deletions(-) create mode 100644 dts/framework/testbed_model/topology.py diff --git a/dts/framework/runner.py b/dts/framework/runner.py index 55357ea1fe..48ae9cc215 100644 --- a/dts/framework/runner.py +++ b/dts/framework/runner.py @@ -53,6 +53,7 @@ TestSuiteWithCases, ) from .test_suite import TestCase, TestSuite +from .testbed_model.topology import Topology class DTSRunner: @@ -474,6 +475,7 @@ def _run_test_suites( test_suites_with_cases: The test suites with test cases to run. """ end_build_target = False + topology = Topology(sut_node.ports, tg_node.ports) for test_suite_with_cases in test_suites_with_cases: test_suite_result = build_target_result.add_test_suite(test_suite_with_cases) try: @@ -481,6 +483,7 @@ def _run_test_suites( self._run_test_suite( sut_node, tg_node, + topology, test_suite_result, test_suite_with_cases, ) @@ -506,6 +509,7 @@ def _run_test_suite( self, sut_node: SutNode, tg_node: TGNode, + topology: Topology, test_suite_result: TestSuiteResult, test_suite_with_cases: TestSuiteWithCases, ) -> None: @@ -533,7 +537,7 @@ def _run_test_suite( self._logger.set_stage( DtsStage.test_suite_setup, Path(SETTINGS.output_dir, test_suite_name) ) - test_suite = test_suite_with_cases.test_suite_class(sut_node, tg_node) + test_suite = test_suite_with_cases.test_suite_class(sut_node, tg_node, topology) try: self._logger.info(f"Starting test suite setup: {test_suite_name}") test_suite.set_up_suite() diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py index c59fc9c6e6..56f153bda6 100644 --- a/dts/framework/test_suite.py +++ b/dts/framework/test_suite.py @@ -24,9 +24,10 @@ from scapy.packet import Packet, Padding # type: ignore[import-untyped] from framework.testbed_model.capability import TestProtocol -from framework.testbed_model.port import Port, PortLink +from framework.testbed_model.port import Port from framework.testbed_model.sut_node import SutNode from framework.testbed_model.tg_node import TGNode +from framework.testbed_model.topology import Topology, TopologyType from framework.testbed_model.traffic_generator.capturing_traffic_generator import ( PacketFilteringConfig, ) @@ -72,7 +73,7 @@ class TestSuite(TestProtocol): #: will block the execution of all subsequent test suites in the current build target. is_blocking: ClassVar[bool] = False _logger: DTSLogger - _port_links: list[PortLink] + _topology_type: TopologyType _sut_port_ingress: Port _sut_port_egress: Port _sut_ip_address_ingress: Union[IPv4Interface, IPv6Interface] @@ -86,6 +87,7 @@ def __init__( self, sut_node: SutNode, tg_node: TGNode, + topology: Topology, ): """Initialize the test suite testbed information and basic configuration. @@ -95,35 +97,21 @@ def __init__( Args: sut_node: The SUT node where the test suite will run. tg_node: The TG node where the test suite will run. + topology: The topology where the test suite will run. """ self.sut_node = sut_node self.tg_node = tg_node self._logger = get_dts_logger(self.__class__.__name__) - self._port_links = [] - self._process_links() - self._sut_port_ingress, self._tg_port_egress = ( - self._port_links[0].sut_port, - self._port_links[0].tg_port, - ) - self._sut_port_egress, self._tg_port_ingress = ( - self._port_links[1].sut_port, - self._port_links[1].tg_port, - ) + self._topology_type = topology.type + self._tg_port_egress = topology.tg_port_egress + self._sut_port_ingress = topology.sut_port_ingress + self._sut_port_egress = topology.sut_port_egress + self._tg_port_ingress = topology.tg_port_ingress self._sut_ip_address_ingress = ip_interface("192.168.100.2/24") self._sut_ip_address_egress = ip_interface("192.168.101.2/24") self._tg_ip_address_egress = ip_interface("192.168.100.3/24") self._tg_ip_address_ingress = ip_interface("192.168.101.3/24") - def _process_links(self) -> None: - """Construct links between SUT and TG ports.""" - for sut_port in self.sut_node.ports: - for tg_port in self.tg_node.ports: - if (sut_port.identifier, sut_port.peer) == ( - tg_port.peer, - tg_port.identifier, - ): - self._port_links.append(PortLink(sut_port=sut_port, tg_port=tg_port)) - @classmethod def get_test_cases( cls, test_case_sublist: Sequence[str] | None = None diff --git a/dts/framework/testbed_model/node.py b/dts/framework/testbed_model/node.py index 12a40170ac..51443cd71f 100644 --- a/dts/framework/testbed_model/node.py +++ b/dts/framework/testbed_model/node.py @@ -90,7 +90,7 @@ def __init__(self, node_config: NodeConfiguration): self._init_ports() def _init_ports(self) -> None: - self.ports = [Port(self.name, port_config) for port_config in self.config.ports] + self.ports = [Port(port_config) for port_config in self.config.ports] self.main_session.update_ports(self.ports) for port in self.ports: self.configure_port_state(port) diff --git a/dts/framework/testbed_model/port.py b/dts/framework/testbed_model/port.py index 817405bea4..82c84cf4f8 100644 --- a/dts/framework/testbed_model/port.py +++ b/dts/framework/testbed_model/port.py @@ -54,7 +54,7 @@ class Port: mac_address: str = "" logical_name: str = "" - def __init__(self, node_name: str, config: PortConfig): + def __init__(self, config: PortConfig): """Initialize the port from `node_name` and `config`. Args: @@ -62,7 +62,7 @@ def __init__(self, node_name: str, config: PortConfig): config: The test run configuration of the port. """ self.identifier = PortIdentifier( - node=node_name, + node=config.node, pci=config.pci, ) self.os_driver = config.os_driver diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py new file mode 100644 index 0000000000..19632ee890 --- /dev/null +++ b/dts/framework/testbed_model/topology.py @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright(c) 2024 PANTHEON.tech s.r.o. + +"""Testbed topology representation. + +A topology of a testbed captures what links are available between the testbed's nodes. +The link information then implies what type of topology is available. +""" + +from dataclasses import dataclass +from enum import IntEnum +from typing import Iterable + +from framework.config import PortConfig + +from .port import Port + + +class TopologyType(IntEnum): + """Supported topology types.""" + + #: A topology with no Traffic Generator. + no_link = 0 + #: A topology with one physical link between the SUT node and the TG node. + one_link = 1 + #: A topology with two physical links between the Sut node and the TG node. + two_links = 2 + + +class Topology: + """Testbed topology. + + The topology contains ports processed into ingress and egress ports. + It's assumed that port0 of the SUT node is connected to port0 of the TG node and so on. + If there are no ports on a node, dummy ports (ports with no actual values) are stored. + If there is only one link available, the ports of this link are stored + as both ingress and egress ports. + + The dummy ports shouldn't be used. It's up to :class:`~framework.runner.DTSRunner` + to ensure no test case or suite requiring actual links is executed + when the topology prohibits it and up to the developers to make sure that test cases + not requiring any links don't use any ports. Otherwise, the underlying methods + using the ports will fail. + + Attributes: + type: The type of the topology. + tg_port_egress: The egress port of the TG node. + sut_port_ingress: The ingress port of the SUT node. + sut_port_egress: The egress port of the SUT node. + tg_port_ingress: The ingress port of the TG node. + """ + + type: TopologyType + tg_port_egress: Port + sut_port_ingress: Port + sut_port_egress: Port + tg_port_ingress: Port + + def __init__(self, sut_ports: Iterable[Port], tg_ports: Iterable[Port]): + """Create the topology from `sut_ports` and `tg_ports`. + + Args: + sut_ports: The SUT node's ports. + tg_ports: The TG node's ports. + """ + port_links = [] + for sut_port in sut_ports: + for tg_port in tg_ports: + if (sut_port.identifier, sut_port.peer) == ( + tg_port.peer, + tg_port.identifier, + ): + port_links.append(PortLink(sut_port=sut_port, tg_port=tg_port)) + + self.type = TopologyType(len(port_links)) + dummy_port = Port(PortConfig("", "", "", "", "", "")) + self.tg_port_egress = dummy_port + self.sut_port_ingress = dummy_port + self.sut_port_egress = dummy_port + self.tg_port_ingress = dummy_port + if self.type > TopologyType.no_link: + self.tg_port_egress = port_links[0].tg_port + self.sut_port_ingress = port_links[0].sut_port + self.sut_port_egress = self.sut_port_ingress + self.tg_port_ingress = self.tg_port_egress + if self.type > TopologyType.one_link: + self.sut_port_egress = port_links[1].sut_port + self.tg_port_ingress = port_links[1].tg_port + + +@dataclass(slots=True, frozen=True) +class PortLink: + """The physical, cabled connection between the ports. + + Attributes: + sut_port: The port on the SUT node connected to `tg_port`. + tg_port: The port on the TG node connected to `sut_port`. + """ + + sut_port: Port + tg_port: Port diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py index 020fb0ab62..178a40385e 100644 --- a/dts/tests/TestSuite_pmd_buffer_scatter.py +++ b/dts/tests/TestSuite_pmd_buffer_scatter.py @@ -58,7 +58,7 @@ def set_up_suite(self) -> None: to support larger packet sizes. """ self.verify( - len(self._port_links) > 1, + self._topology_type > 1, "There must be at least two port links to run the scatter test suite", ) From patchwork Wed Aug 21 14:53:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143239 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 8AE4545830; Wed, 21 Aug 2024 16:54:04 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 34D3342759; Wed, 21 Aug 2024 16:53:29 +0200 (CEST) Received: from mail-ed1-f54.google.com (mail-ed1-f54.google.com [209.85.208.54]) by mails.dpdk.org (Postfix) with ESMTP id B332D411F3 for ; Wed, 21 Aug 2024 16:53:25 +0200 (CEST) Received: by mail-ed1-f54.google.com with SMTP id 4fb4d7f45d1cf-5bec78c3f85so6215216a12.1 for ; Wed, 21 Aug 2024 07:53:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252005; x=1724856805; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=hCqVS2m2INp/6NcDjRm0craXL99gKfi6g/uL3HEqK9I=; b=EEctc258fPqJyO/y8ZwACykNVUYcGb/P4P7WPezrm/iDb61ZAL0d8YMPWv6HQ8aFuW HJyLDZhGArjz+hkcCIrRdz5a+94fwOe8cHwf+YuaERBUW7oWaBflNL/MCfZszfQocaL5 vs3yC72J/90Ur6BDxzIpmSlXWuDlZm/jc15YcsYlzZvDEDd9pRBa1meOAY7Qu76D+G3f rJgIawzvuh2T/0Lvg1hybShr3yUSxwt64PYQKMgvsGG6OUHwFbhf2JQzIjtCQQoqrbjN 46640uTbdqMJJqDgb4vZ6qCvbAYKkrt1TvYmUtpTg7sXlU8Q99XEd9A0Fn7BD3tCz+IG SiXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252005; x=1724856805; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hCqVS2m2INp/6NcDjRm0craXL99gKfi6g/uL3HEqK9I=; b=Ld3xYjXGv2B8j+qOYZkuAKtICic5mOM9Db7B+pGA6BAAZpoAfW0go7U63acJ0L5sFz X1ASSDGqJFFxmP4RV0EpJ2DItVqGY8fsKp9qxKk/8G8NgctLcI622/n7Ca9f4uSuOfY6 05kKS8recoqvSMzL1FBl/UiJL6oEY0/O4y5Ra2PlkMpQR55g6ILXujQL0Lh7gNb9nXe8 DOHU78FDWExXzcNqISKain01TMM4qQzwF517+d/35eZggNcWxDUfgXa9Lg6nlODVIQYR 8a0ktOuraDhntfizzQUU9o32gduuV++1bmLJFdKOd9nOALbEGY07yZaYbY8okZiXqvSd ua7Q== X-Gm-Message-State: AOJu0YwTyLRTTi/oW1GSJPJDaERE14G7cNs6zIfs9vdsRaKesDV/9rHj VsayuGsM9Wo8Cq0f+VQfM2vkD2M+HFqwS9KN/pXHdRp7HXPARYfpEg1sQeSgqN4= X-Google-Smtp-Source: AGHT+IG1nMep6EcRV+0BYu8W6Jfn2LEmEJCwSHseowELg93cxs9N/Aj8gZYgSmENIT6nIpvRjyIxSA== X-Received: by 2002:a17:907:86a3:b0:a86:67e7:c740 with SMTP id a640c23a62f3a-a866f2930ffmr170308566b.17.1724252005185; Wed, 21 Aug 2024 07:53:25 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:24 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 06/12] dst: add basic capability support Date: Wed, 21 Aug 2024 16:53:09 +0200 Message-Id: <20240821145315.97974-7-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org A test case or suite may require certain capabilities to be present in the tested environment. Add the basic infrastructure for checking the support status of capabilities: * The Capability ABC defining the common capability API * Extension of the TestProtocol with required capabilities (each test suite or case stores the capabilities it requires) * Integration with the runner which calls the new APIs to get which capabilities are supported. Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx --- dts/framework/runner.py | 26 +++++ dts/framework/test_result.py | 38 ++++++- dts/framework/test_suite.py | 1 + dts/framework/testbed_model/capability.py | 117 +++++++++++++++++++++- 4 files changed, 179 insertions(+), 3 deletions(-) diff --git a/dts/framework/runner.py b/dts/framework/runner.py index 48ae9cc215..43bb2bc830 100644 --- a/dts/framework/runner.py +++ b/dts/framework/runner.py @@ -25,6 +25,7 @@ from types import MethodType from typing import Iterable +from framework.testbed_model.capability import Capability, get_supported_capabilities from framework.testbed_model.sut_node import SutNode from framework.testbed_model.tg_node import TGNode @@ -452,6 +453,21 @@ def _run_build_target( self._logger.exception("Build target teardown failed.") build_target_result.update_teardown(Result.FAIL, e) + def _get_supported_capabilities( + self, + sut_node: SutNode, + topology_config: Topology, + test_suites_with_cases: Iterable[TestSuiteWithCases], + ) -> set[Capability]: + + capabilities_to_check = set() + for test_suite_with_cases in test_suites_with_cases: + capabilities_to_check.update(test_suite_with_cases.required_capabilities) + + self._logger.debug(f"Found capabilities to check: {capabilities_to_check}") + + return get_supported_capabilities(sut_node, topology_config, capabilities_to_check) + def _run_test_suites( self, sut_node: SutNode, @@ -464,6 +480,12 @@ def _run_test_suites( The method assumes the build target we're testing has already been built on the SUT node. The current build target thus corresponds to the current DPDK build present on the SUT node. + Before running any suites, the method determines whether they should be skipped + by inspecting any required capabilities the test suite needs and comparing those + to capabilities supported by the tested environment. If all capabilities are supported, + the suite is run. If all test cases in a test suite would be skipped, the whole test suite + is skipped (the setup and teardown is not run). + If a blocking test suite (such as the smoke test suite) fails, the rest of the test suites in the current build target won't be executed. @@ -476,7 +498,11 @@ def _run_test_suites( """ end_build_target = False topology = Topology(sut_node.ports, tg_node.ports) + supported_capabilities = self._get_supported_capabilities( + sut_node, topology, test_suites_with_cases + ) for test_suite_with_cases in test_suites_with_cases: + test_suite_with_cases.mark_skip_unsupported(supported_capabilities) test_suite_result = build_target_result.add_test_suite(test_suite_with_cases) try: if not test_suite_with_cases.skip: diff --git a/dts/framework/test_result.py b/dts/framework/test_result.py index 306b100bc6..b4b58ef348 100644 --- a/dts/framework/test_result.py +++ b/dts/framework/test_result.py @@ -25,10 +25,12 @@ import os.path from collections.abc import MutableSequence -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum, auto from typing import Union +from framework.testbed_model.capability import Capability + from .config import ( OS, Architecture, @@ -63,6 +65,12 @@ class is to hold a subset of test cases (which could be all test cases) because test_suite_class: type[TestSuite] test_cases: list[type[TestCase]] + required_capabilities: set[Capability] = field(default_factory=set, init=False) + + def __post_init__(self): + """Gather the required capabilities of the test suite and all test cases.""" + for test_object in [self.test_suite_class] + self.test_cases: + self.required_capabilities.update(test_object.required_capabilities) def create_config(self) -> TestSuiteConfig: """Generate a :class:`TestSuiteConfig` from the stored test suite with test cases. @@ -75,6 +83,34 @@ def create_config(self) -> TestSuiteConfig: test_cases=[test_case.__name__ for test_case in self.test_cases], ) + def mark_skip_unsupported(self, supported_capabilities: set[Capability]) -> None: + """Mark the test suite and test cases to be skipped. + + The mark is applied if object to be skipped requires any capabilities and at least one of + them is not among `supported_capabilities`. + + Args: + supported_capabilities: The supported capabilities. + """ + for test_object in [self.test_suite_class, *self.test_cases]: + capabilities_not_supported = test_object.required_capabilities - supported_capabilities + if capabilities_not_supported: + test_object.skip = True + capability_str = ( + "capability" if len(capabilities_not_supported) == 1 else "capabilities" + ) + test_object.skip_reason = ( + f"Required {capability_str} '{capabilities_not_supported}' not found." + ) + if not self.test_suite_class.skip: + if all(test_case.skip for test_case in self.test_cases): + self.test_suite_class.skip = True + + self.test_suite_class.skip_reason = ( + "All test cases are marked to be skipped with reasons: " + f"{' '.join(test_case.skip_reason for test_case in self.test_cases)}" + ) + @property def skip(self) -> bool: """Skip the test suite if all test cases or the suite itself are to be skipped. diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py index 56f153bda6..5c393ce8bf 100644 --- a/dts/framework/test_suite.py +++ b/dts/framework/test_suite.py @@ -467,6 +467,7 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]: test_case = cast(type[TestCase], func) test_case.skip = cls.skip test_case.skip_reason = cls.skip_reason + test_case.required_capabilities = set() test_case.test_type = test_case_type return test_case diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py index 662f691a0e..8899f07f76 100644 --- a/dts/framework/testbed_model/capability.py +++ b/dts/framework/testbed_model/capability.py @@ -3,11 +3,97 @@ """Testbed capabilities. -This module provides a protocol that defines the common attributes of test cases and suites. +This module provides a protocol that defines the common attributes of test cases and suites +and support for test environment capabilities. """ +from abc import ABC, abstractmethod from collections.abc import Sequence -from typing import ClassVar, Protocol +from typing import Callable, ClassVar, Protocol + +from typing_extensions import Self + +from .sut_node import SutNode +from .topology import Topology + + +class Capability(ABC): + """The base class for various capabilities. + + The same capability should always be represented by the same object, + meaning the same capability required by different test cases or suites + should point to the same object. + + Example: + ``test_case1`` and ``test_case2`` each require ``capability1`` + and in both instances, ``capability1`` should point to the same capability object. + + It is up to the subclasses how they implement this. + + The instances are used in sets so they must be hashable. + """ + + #: A set storing the capabilities whose support should be checked. + capabilities_to_check: ClassVar[set[Self]] = set() + + def register_to_check(self) -> Callable[[SutNode, "Topology"], set[Self]]: + """Register the capability to be checked for support. + + Returns: + The callback function that checks the support of capabilities of the particular subclass + which should be called after all capabilities have been registered. + """ + if not type(self).capabilities_to_check: + type(self).capabilities_to_check = set() + type(self).capabilities_to_check.add(self) + return type(self)._get_and_reset + + def add_to_required(self, test_case_or_suite: type["TestProtocol"]) -> None: + """Add the capability instance to the required test case or suite's capabilities. + + Args: + test_case_or_suite: The test case or suite among whose required capabilities + to add this instance. + """ + if not test_case_or_suite.required_capabilities: + test_case_or_suite.required_capabilities = set() + self._preprocess_required(test_case_or_suite) + test_case_or_suite.required_capabilities.add(self) + + def _preprocess_required(self, test_case_or_suite: type["TestProtocol"]) -> None: + """An optional method that modifies the required capabilities.""" + + @classmethod + def _get_and_reset(cls, sut_node: SutNode, topology: "Topology") -> set[Self]: + """The callback method to be called after all capabilities have been registered. + + Not only does this method check the support of capabilities, + but it also reset the internal set of registered capabilities + so that the "register, then get support" workflow works in subsequent test runs. + """ + supported_capabilities = cls.get_supported_capabilities(sut_node, topology) + cls.capabilities_to_check = set() + return supported_capabilities + + @classmethod + @abstractmethod + def get_supported_capabilities(cls, sut_node: SutNode, topology: "Topology") -> set[Self]: + """Get the support status of each registered capability. + + Each subclass must implement this method and return the subset of supported capabilities + of :attr:`capabilities_to_check`. + + Args: + sut_node: The SUT node of the current test run. + topology: The topology of the current test run. + + Returns: + The supported capabilities. + """ + + @abstractmethod + def __hash__(self) -> int: + """The subclasses must be hashable so that they can be stored in sets.""" class TestProtocol(Protocol): @@ -17,6 +103,8 @@ class TestProtocol(Protocol): skip: ClassVar[bool] = False #: The reason for skipping the test case or suite. skip_reason: ClassVar[str] = "" + #: The capabilities the test case or suite requires in order to be executed. + required_capabilities: ClassVar[set[Capability]] = set() @classmethod def get_test_cases(cls, test_case_sublist: Sequence[str] | None = None) -> tuple[set, set]: @@ -26,3 +114,28 @@ def get_test_cases(cls, test_case_sublist: Sequence[str] | None = None) -> tuple NotImplementedError: The subclass does not implement the method. """ raise NotImplementedError() + + +def get_supported_capabilities( + sut_node: SutNode, + topology_config: Topology, + capabilities_to_check: set[Capability], +) -> set[Capability]: + """Probe the environment for `capabilities_to_check` and return the supported ones. + + Args: + sut_node: The SUT node to check for capabilities. + topology_config: The topology config to check for capabilities. + capabilities_to_check: The capabilities to check. + + Returns: + The capabilities supported by the environment. + """ + callbacks = set() + for capability_to_check in capabilities_to_check: + callbacks.add(capability_to_check.register_to_check()) + supported_capabilities = set() + for callback in callbacks: + supported_capabilities.update(callback(sut_node, topology_config)) + + return supported_capabilities From patchwork Wed Aug 21 14:53:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143240 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 4120E45830; Wed, 21 Aug 2024 16:54:13 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id B3C4A4278E; Wed, 21 Aug 2024 16:53:30 +0200 (CEST) Received: from mail-ej1-f54.google.com (mail-ej1-f54.google.com [209.85.218.54]) by mails.dpdk.org (Postfix) with ESMTP id ADB80415D7 for ; Wed, 21 Aug 2024 16:53:26 +0200 (CEST) Received: by mail-ej1-f54.google.com with SMTP id a640c23a62f3a-a7de4364ca8so747267166b.2 for ; Wed, 21 Aug 2024 07:53:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252006; x=1724856806; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Bac+s+7Xg+cl3PKbrjUDDqu6SNMsuzsAzsw7hSoypkM=; b=JkkLx6XiGUAApxT0tvLn+OI7zTncrWtWxKJSoVpgILAe0nREft0yezui8ixakqANvB dHuIAiyLx4RfBJT+4nBm2dGrzHFBrhz/gDjQsU5MzJH053rdvbX5j9/F15VqWP5emFHd EZcUxeEGjdeSeeSlv2EGHjDwU0ojOaKpP41VY4offvT1nCqWiRtva7I7NLwqkVMgvPzm 9IkraKjPEQRmETT0flXMvEsgMRJmHpwx/3la1Heg1yh8uDQWcX47qJtcsJ/xRr6yceiY ZrDHhupLOJCS/ggkjg7TNFoBikIGP/9t7Xn4H44jsolG2PoEd7rpobSoFkM3xt8bmMpj KT2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252006; x=1724856806; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Bac+s+7Xg+cl3PKbrjUDDqu6SNMsuzsAzsw7hSoypkM=; b=sQD1GOeBRKN7qstmfmxrfhoLPcpHXwnxG8ZkMe26HJu7KsPkZepbLv5tTbSfv0DvyS 46mLxOyVdJq/NAd2pEfI4KpyFlplIHGhzvlUI7FX6lazb0PeFBIN3Av7avH07e/cIoxo dFEgQZU+s+ksb9t/eTbpHinnBYddWAC13jOdyHxWvz8EVMPnBIfv0b/WJ1oeDlWEpXyw EmoGN/L5fP74OLbV4vU08d8yIKfQOd5ISMtX640C/VmF/BPrwhZwfRPshku2Zg823RZR twfe5zLenWll394VRvqd0oBLg/XBAQY0K21y4lO5dA8lvjK+o9EkTc4pLoZvsXqTrNug HQ/A== X-Gm-Message-State: AOJu0YwTFdT8bZpDVHXX/M/gv/VZRzZ+itWPPqvlylMpPkJ8zAzgWG9S JmfxyDiaPjqrK2kBRTtNdTbGWxqmf8WHleV8vboKi4CqYOPJhAJVuP9SkjEE0GV5iPHtfiVlNgj jxAM= X-Google-Smtp-Source: AGHT+IEfAe1L0oYj1Ts96w68VNjcUwN7OVDbZJGTxKCCSvTrDEIA3mlDkzaJ7XHGypBKDtxvpNOp6w== X-Received: by 2002:a17:907:2d8f:b0:a86:82e2:8c64 with SMTP id a640c23a62f3a-a8682e28e67mr86241266b.35.1724252006210; Wed, 21 Aug 2024 07:53:26 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:26 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 07/12] dts: add testpmd port information caching Date: Wed, 21 Aug 2024 16:53:10 +0200 Message-Id: <20240821145315.97974-8-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org When using port information multiple times in a testpmd shell instance lifespan, it's desirable to not get the information each time, so caching is added. In case the information changes, there's a way to force the update. Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx --- dts/framework/remote_session/testpmd_shell.py | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py index b4ad253020..f0bcc918e5 100644 --- a/dts/framework/remote_session/testpmd_shell.py +++ b/dts/framework/remote_session/testpmd_shell.py @@ -654,6 +654,7 @@ class TestPmdShell(DPDKShell): """ _app_params: TestPmdParams + _ports: list[TestPmdPort] | None #: The path to the testpmd executable. path: ClassVar[PurePath] = PurePath("app", "dpdk-testpmd") @@ -686,6 +687,21 @@ def __init__( TestPmdParams(**app_params), name, ) + self._ports = None + + @property + def ports(self) -> list[TestPmdPort]: + """The ports of the instance. + + This caches the ports returned by :meth:`show_port_info_all`. + To force an update of port information, execute :meth:`show_port_info_all` or + :meth:`show_port_info`. + + Returns: The list of known testpmd ports. + """ + if self._ports is None: + return self.show_port_info_all() + return self._ports def start(self, verify: bool = True) -> None: """Start packet forwarding with the current configuration. @@ -872,7 +888,8 @@ def show_port_info_all(self) -> list[TestPmdPort]: # executed on a pseudo-terminal created by paramiko on the remote node, lines end with CRLF. # Therefore we also need to take the carriage return into account. iter = re.finditer(r"\*{21}.*?[\r\n]{4}", output + "\r\n", re.S) - return [TestPmdPort.parse(block.group(0)) for block in iter] + self._ports = [TestPmdPort.parse(block.group(0)) for block in iter] + return self._ports def show_port_info(self, port_id: int) -> TestPmdPort: """Returns the given port information. @@ -890,7 +907,16 @@ def show_port_info(self, port_id: int) -> TestPmdPort: if output.startswith("Invalid port"): raise InteractiveCommandExecutionError("invalid port given") - return TestPmdPort.parse(output) + port = TestPmdPort.parse(output) + self._update_port(port) + return port + + def _update_port(self, port: TestPmdPort) -> None: + if self._ports: + self._ports = [ + existing_port if port.id != existing_port.id else port + for existing_port in self._ports + ] def show_port_stats_all(self) -> list[TestPmdPortStats]: """Returns the statistics of all the ports. From patchwork Wed Aug 21 14:53:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143241 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 88C4245830; Wed, 21 Aug 2024 16:54:19 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id EEDE642791; Wed, 21 Aug 2024 16:53:31 +0200 (CEST) Received: from mail-ej1-f47.google.com (mail-ej1-f47.google.com [209.85.218.47]) by mails.dpdk.org (Postfix) with ESMTP id E4451411F3 for ; Wed, 21 Aug 2024 16:53:27 +0200 (CEST) Received: by mail-ej1-f47.google.com with SMTP id a640c23a62f3a-a7a81bd549eso565032566b.3 for ; Wed, 21 Aug 2024 07:53:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252007; x=1724856807; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=YlDITddmCLhYO9g28jHSyYFulx8QtBbP2D5HW2GgbBk=; b=G17lFe9nt/WK5V4vbVHlqgtcAwnO8hHO8W4zZ1MdF2ffZIaYQA6sW69X5YfMydQFGJ fMo6v/IEhjl5g820IVFUYxeOrNczjyiTBDhf05nNWqir1Gs5kULLa1sWAR64lgVhzgSy nu9ORena7fHeIAfDJaIc5bTCvGT2zWFssGR5yx5Saz3FhNi66i9A3iEftS8lavxAR3CP nsRoAuaFZx/v6kP1V/Bppqe1Jbjf/rnIZYIhI2oCmIT3uggbkbqhikZ6vjKmdV0fRRA3 6s0z0bvK1ms2qmQtUwVC2b5nozkfBKQJRKlbXP3w6SIrC0SfrHoqJRg/veTcrbcX1YXy PoEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252007; x=1724856807; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YlDITddmCLhYO9g28jHSyYFulx8QtBbP2D5HW2GgbBk=; b=KGV4r8zeItV/2hJc9jQjnHQW0ULTDEfW0yhomvk5hzp6qc3QiO4tpHGbtHUmn9GmCn DbLxlvky4UsRMrEgcycg3MgnOvJfg3XHg5/5ls742jTXBnX5hljuP5ldnEZr4L+f9d21 61WRcTbiXHor3eaWUaP/ILW6OhCkxEWNt4XImy8j/yZLrppIS06xCeItVoNsIgEW8SRa 4bDW22waJl5VypWu7PDlp/QCbBjjC+O1Y5Yo0cPddiM0SsvYUgQaG6oUZzMhDjDxIoy0 KZJqz5E1pf7yhkab78/k0r8xttfiJ09ozL8dgwAtUGUNsacs7hyfK6nXDP50ErwbP7cB grog== X-Gm-Message-State: AOJu0YzIaW6GaUyJyH4VFG+zTXh9/KERyNGddloRvauSU+rju53/QGFL cNgZhXFNQ7qow/mpsnRGQtlKu3BuJu3DoS+jElLIJxCbfjQ2S+A96OaJKpGVx5s= X-Google-Smtp-Source: AGHT+IGh138EvHWh1/nNWRdqs0qAoJp9mKD6Moq0D6YGhCgPj14odYjFbQmQZb4fKUhk3uIa//cRTg== X-Received: by 2002:a17:907:6d23:b0:a77:e1fb:7dec with SMTP id a640c23a62f3a-a866f27b16fmr183088566b.17.1724252007283; Wed, 21 Aug 2024 07:53:27 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:27 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 08/12] dts: add NIC capability support Date: Wed, 21 Aug 2024 16:53:11 +0200 Message-Id: <20240821145315.97974-9-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Some test cases or suites may be testing a NIC feature that is not supported on all NICs, so add support for marking test cases or suites as requiring NIC capabilities. The marking is done with a decorator, which populates the internal required_capabilities attribute of TestProtocol. The NIC capability itself is a wrapper around the NicCapability defined in testpmd_shell. The reason is twofold: 1. Enums cannot be extended and the class implements the methods of its abstract base superclass, 2. The class also stores an optional decorator function which is used before/after capability retrieval. This is needed because some capabilities may be advertised differently under different configuration. The decorator API is designed to be simple to use. The arguments passed to it are all from the testpmd shell. Everything else (even the actual capability object creation) is done internally. Signed-off-by: Juraj Linkeš Reviewed-by: Dean Marx --- Depends-on: patch-142276 ("dts: add methods for modifying MTU to testpmd shell") --- dts/framework/remote_session/testpmd_shell.py | 178 ++++++++++++++++- dts/framework/testbed_model/capability.py | 180 +++++++++++++++++- dts/tests/TestSuite_pmd_buffer_scatter.py | 2 + 3 files changed, 356 insertions(+), 4 deletions(-) diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py index f0bcc918e5..48c31124d1 100644 --- a/dts/framework/remote_session/testpmd_shell.py +++ b/dts/framework/remote_session/testpmd_shell.py @@ -16,11 +16,17 @@ import re import time -from collections.abc import Callable +from collections.abc import Callable, MutableSet from dataclasses import dataclass, field from enum import Flag, auto +from functools import partial from pathlib import PurePath -from typing import ClassVar +from typing import TYPE_CHECKING, Any, ClassVar, TypeAlias + +if TYPE_CHECKING: + from enum import Enum as NoAliasEnum +else: + from aenum import NoAliasEnum from typing_extensions import Self, TypeVarTuple, Unpack @@ -34,6 +40,16 @@ from framework.testbed_model.sut_node import SutNode from framework.utils import StrEnum +TestPmdShellCapabilityMethod: TypeAlias = Callable[ + ["TestPmdShell", MutableSet["NicCapability"], MutableSet["NicCapability"]], None +] + +TestPmdShellSimpleMethod: TypeAlias = Callable[["TestPmdShell"], Any] + +TestPmdShellDecoratedMethod: TypeAlias = Callable[["TestPmdShell"], None] + +TestPmdShellDecorator: TypeAlias = Callable[[TestPmdShellSimpleMethod], TestPmdShellDecoratedMethod] + class TestPmdDevice: """The data of a device that testpmd can recognize. @@ -377,6 +393,71 @@ def _validate(info: str): return TextParser.wrap(TextParser.find(r"Device private info:\s+([\s\S]+)"), _validate) +class RxQueueState(StrEnum): + """RX queue states.""" + + #: + stopped = auto() + #: + started = auto() + #: + hairpin = auto() + #: + unknown = auto() + + @classmethod + def make_parser(cls) -> ParserFn: + """Makes a parser function. + + Returns: + ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a + parser function that makes an instance of this enum from text. + """ + return TextParser.wrap(TextParser.find(r"Rx queue state: ([^\r\n]+)"), cls) + + +@dataclass +class TestPmdRxqInfo(TextParser): + """Representation of testpmd's ``show rxq info `` command.""" + + #: + port_id: int = field(metadata=TextParser.find_int(r"Infos for port (\d+)\b ?, RX queue \d+\b")) + #: + queue_id: int = field(metadata=TextParser.find_int(r"Infos for port \d+\b ?, RX queue (\d+)\b")) + #: Mempool used by that queue + mempool: str = field(metadata=TextParser.find(r"Mempool: ([^\r\n]+)")) + #: Ring prefetch threshold + rx_prefetch_threshold: int = field( + metadata=TextParser.find_int(r"RX prefetch threshold: (\d+)\b") + ) + #: Ring host threshold + rx_host_threshold: int = field(metadata=TextParser.find_int(r"RX host threshold: (\d+)\b")) + #: Ring writeback threshold + rx_writeback_threshold: int = field( + metadata=TextParser.find_int(r"RX writeback threshold: (\d+)\b") + ) + #: Drives the freeing of Rx descriptors + rx_free_threshold: int = field(metadata=TextParser.find_int(r"RX free threshold: (\d+)\b")) + #: Drop packets if no descriptors are available + rx_drop_packets: bool = field(metadata=TextParser.find(r"RX drop packets: on")) + #: Do not start queue with rte_eth_dev_start() + rx_deferred_start: bool = field(metadata=TextParser.find(r"RX deferred start: on")) + #: Scattered packets Rx enabled + rx_scattered_packets: bool = field(metadata=TextParser.find(r"RX scattered packets: on")) + #: The state of the queue + rx_queue_state: str = field(metadata=RxQueueState.make_parser()) + #: Configured number of RXDs + number_of_rxds: int = field(metadata=TextParser.find_int(r"Number of RXDs: (\d+)\b")) + #: Hardware receive buffer size + rx_buffer_size: int | None = field( + default=None, metadata=TextParser.find_int(r"RX buffer size: (\d+)\b") + ) + #: Burst mode information + burst_mode: str | None = field( + default=None, metadata=TextParser.find(r"Burst mode: ([^\r\n]+)") + ) + + @dataclass class TestPmdPort(TextParser): """Dataclass representing the result of testpmd's ``show port info`` command.""" @@ -962,3 +1043,96 @@ def _close(self) -> None: self.stop() self.send_command("quit", "Bye...") return super()._close() + + """ + ====== Capability retrieval methods ====== + """ + + def get_capabilities_rxq_info( + self, + supported_capabilities: MutableSet["NicCapability"], + unsupported_capabilities: MutableSet["NicCapability"], + ) -> None: + """Get all rxq capabilities and divide them into supported and unsupported. + + Args: + supported_capabilities: Supported capabilities will be added to this set. + unsupported_capabilities: Unsupported capabilities will be added to this set. + """ + self._logger.debug("Getting rxq capabilities.") + command = f"show rxq info {self.ports[0].id} 0" + rxq_info = TestPmdRxqInfo.parse(self.send_command(command)) + if rxq_info.rx_scattered_packets: + supported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) + else: + unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) + + """ + ====== Decorator methods ====== + """ + + @staticmethod + def config_mtu_9000(testpmd_method: TestPmdShellSimpleMethod) -> TestPmdShellDecoratedMethod: + """Configure MTU to 9000 on all ports, run `testpmd_method`, then revert. + + Args: + testpmd_method: The method to decorate. + + Returns: + The method decorated with setting and reverting MTU. + """ + + def wrapper(testpmd_shell: Self): + original_mtus = [] + for port in testpmd_shell.ports: + original_mtus.append((port.id, port.mtu)) + testpmd_shell.set_port_mtu(port_id=port.id, mtu=9000, verify=False) + testpmd_method(testpmd_shell) + for port_id, mtu in original_mtus: + testpmd_shell.set_port_mtu(port_id=port_id, mtu=mtu if mtu else 1500, verify=False) + + return wrapper + + +class NicCapability(NoAliasEnum): + """A mapping between capability names and the associated :class:`TestPmdShell` methods. + + The :class:`TestPmdShell` method executes the command that checks + whether the capability is supported. + + The signature of each :class:`TestPmdShell` capability checking method must be:: + + fn(self, supported_capabilities: MutableSet, unsupported_capabilities: MutableSet) -> None + + The method must get whether a capability is supported or not from a testpmd command. + If multiple capabilities can be obtained from a testpmd command, each should be obtained + in the method. These capabilities should then be added to `supported_capabilities` or + `unsupported_capabilities` based on their support. + + The two dictionaries are shared across all capability discovery function calls in a given + test run so that we don't call the same function multiple times, + e.g. when we find a :attr:`scattered_rx` in :meth:`TestPmdShell.get_capabilities_rxq`, we don't + go looking for it again if a different test case also needs it. + """ + + #: Scattered packets Rx enabled. + SCATTERED_RX_ENABLED: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rxq_info + ) + + def __call__( + self, + testpmd_shell: TestPmdShell, + supported_capabilities: MutableSet[Self], + unsupported_capabilities: MutableSet[Self], + ) -> None: + """Execute the associated capability retrieval function. + + Args: + testpmd_shell: :class:`TestPmdShell` object to which the function will be bound to. + supported_capabilities: The dictionary storing the supported capabilities + of a given test run. + unsupported_capabilities: The dictionary storing the unsupported capabilities + of a given test run. + """ + self.value(testpmd_shell, supported_capabilities, unsupported_capabilities) diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py index 8899f07f76..9a79e6ebb3 100644 --- a/dts/framework/testbed_model/capability.py +++ b/dts/framework/testbed_model/capability.py @@ -5,14 +5,40 @@ This module provides a protocol that defines the common attributes of test cases and suites and support for test environment capabilities. + +Many test cases are testing features not available on all hardware. + +The module also allows developers to mark test cases or suites a requiring certain +hardware capabilities with the :func:`requires` decorator. + +Example: + .. code:: python + + from framework.test_suite import TestSuite, func_test + from framework.testbed_model.capability import NicCapability, requires + class TestPmdBufferScatter(TestSuite): + # only the test case requires the scattered_rx capability + # other test cases may not require it + @requires(NicCapability.scattered_rx) + @func_test + def test_scatter_mbuf_2048(self): """ from abc import ABC, abstractmethod -from collections.abc import Sequence -from typing import Callable, ClassVar, Protocol +from collections.abc import MutableSet, Sequence +from dataclasses import dataclass +from typing import Callable, ClassVar, Protocol, Tuple from typing_extensions import Self +from framework.logger import get_dts_logger +from framework.remote_session.testpmd_shell import ( + NicCapability, + TestPmdShell, + TestPmdShellDecorator, + TestPmdShellSimpleMethod, +) + from .sut_node import SutNode from .topology import Topology @@ -96,6 +122,128 @@ def __hash__(self) -> int: """The subclasses must be hashable so that they can be stored in sets.""" +@dataclass +class DecoratedNicCapability(Capability): + """A wrapper around :class:`~framework.remote_session.testpmd_shell.NicCapability`. + + Some NIC capabilities are only present or listed as supported only under certain conditions, + such as when a particular configuration is in place. This is achieved by allowing users to pass + a decorator function that decorates the function that gets the support status of the capability. + + New instances should be created with the :meth:`create_unique` class method to ensure + there are no duplicate instances. + + Attributes: + nic_capability: The NIC capability that partly defines each instance. + capability_decorator: The decorator function that will be passed the function associated + with `nic_capability` when discovering the support status of the capability. + Each instance is defined by `capability_decorator` along with `nic_capability`. + """ + + nic_capability: NicCapability + capability_decorator: TestPmdShellDecorator | None + _unique_capabilities: ClassVar[ + dict[Tuple[NicCapability, TestPmdShellDecorator | None], Self] + ] = {} + + @classmethod + def get_unique( + cls, nic_capability: NicCapability, decorator_fn: TestPmdShellDecorator | None + ) -> "DecoratedNicCapability": + """Get the capability uniquely identified by `nic_capability` and `decorator_fn`. + + Args: + nic_capability: The NIC capability. + decorator_fn: The function that will be passed the function associated + with `nic_capability` when discovering the support status of the capability. + + Returns: + The capability uniquely identified by `nic_capability` and `decorator_fn`. + """ + if (nic_capability, decorator_fn) not in cls._unique_capabilities: + cls._unique_capabilities[(nic_capability, decorator_fn)] = cls( + nic_capability, decorator_fn + ) + return cls._unique_capabilities[(nic_capability, decorator_fn)] + + @classmethod + def get_supported_capabilities( + cls, sut_node: SutNode, topology: "Topology" + ) -> set["DecoratedNicCapability"]: + """Overrides :meth:`~Capability.get_supported_capabilities`. + + The capabilities are first sorted by decorators, then reduced into a single function which + is then passed to the decorator. This way we only execute each decorator only once. + """ + supported_conditional_capabilities: set["DecoratedNicCapability"] = set() + logger = get_dts_logger(f"{sut_node.name}.{cls.__name__}") + if topology.type is Topology.type.no_link: + logger.debug( + "No links available in the current topology, not getting NIC capabilities." + ) + return supported_conditional_capabilities + logger.debug( + f"Checking which NIC capabilities from {cls.capabilities_to_check} are supported." + ) + if cls.capabilities_to_check: + capabilities_to_check_map = cls._get_decorated_capabilities_map() + with TestPmdShell(sut_node, privileged=True) as testpmd_shell: + for conditional_capability_fn, capabilities in capabilities_to_check_map.items(): + supported_capabilities: set[NicCapability] = set() + unsupported_capabilities: set[NicCapability] = set() + capability_fn = cls._reduce_capabilities( + capabilities, supported_capabilities, unsupported_capabilities + ) + if conditional_capability_fn: + capability_fn = conditional_capability_fn(capability_fn) + capability_fn(testpmd_shell) + for supported_capability in supported_capabilities: + for capability in capabilities: + if supported_capability == capability.nic_capability: + supported_conditional_capabilities.add(capability) + + logger.debug(f"Found supported capabilities {supported_conditional_capabilities}.") + return supported_conditional_capabilities + + @classmethod + def _get_decorated_capabilities_map( + cls, + ) -> dict[TestPmdShellDecorator | None, set["DecoratedNicCapability"]]: + capabilities_map: dict[TestPmdShellDecorator | None, set["DecoratedNicCapability"]] = {} + for capability in cls.capabilities_to_check: + if capability.capability_decorator not in capabilities_map: + capabilities_map[capability.capability_decorator] = set() + capabilities_map[capability.capability_decorator].add(capability) + + return capabilities_map + + @classmethod + def _reduce_capabilities( + cls, + capabilities: set["DecoratedNicCapability"], + supported_capabilities: MutableSet, + unsupported_capabilities: MutableSet, + ) -> TestPmdShellSimpleMethod: + def reduced_fn(testpmd_shell: TestPmdShell) -> None: + for capability in capabilities: + capability.nic_capability( + testpmd_shell, supported_capabilities, unsupported_capabilities + ) + + return reduced_fn + + def __hash__(self) -> int: + """Instances are identified by :attr:`nic_capability` and :attr:`capability_decorator`.""" + return hash((self.nic_capability, self.capability_decorator)) + + def __repr__(self) -> str: + """Easy to read string of :attr:`nic_capability` and :attr:`capability_decorator`.""" + condition_fn_name = "" + if self.capability_decorator: + condition_fn_name = f"{self.capability_decorator.__qualname__}|" + return f"{condition_fn_name}{self.nic_capability}" + + class TestProtocol(Protocol): """Common test suite and test case attributes.""" @@ -116,6 +264,34 @@ def get_test_cases(cls, test_case_sublist: Sequence[str] | None = None) -> tuple raise NotImplementedError() +def requires( + *nic_capabilities: NicCapability, + decorator_fn: TestPmdShellDecorator | None = None, +) -> Callable[[type[TestProtocol]], type[TestProtocol]]: + """A decorator that adds the required capabilities to a test case or test suite. + + Args: + nic_capabilities: The NIC capabilities that are required by the test case or test suite. + decorator_fn: The decorator function that will be used when getting + NIC capability support status. + topology_type: The topology type the test suite or case requires. + + Returns: + The decorated test case or test suite. + """ + + def add_required_capability(test_case_or_suite: type[TestProtocol]) -> type[TestProtocol]: + for nic_capability in nic_capabilities: + decorated_nic_capability = DecoratedNicCapability.get_unique( + nic_capability, decorator_fn + ) + decorated_nic_capability.add_to_required(test_case_or_suite) + + return test_case_or_suite + + return add_required_capability + + def get_supported_capabilities( sut_node: SutNode, topology_config: Topology, diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py index 178a40385e..713549a5b2 100644 --- a/dts/tests/TestSuite_pmd_buffer_scatter.py +++ b/dts/tests/TestSuite_pmd_buffer_scatter.py @@ -25,6 +25,7 @@ from framework.params.testpmd import SimpleForwardingModes from framework.remote_session.testpmd_shell import TestPmdShell from framework.test_suite import TestSuite, func_test +from framework.testbed_model.capability import NicCapability, requires class TestPmdBufferScatter(TestSuite): @@ -123,6 +124,7 @@ def pmd_scatter(self, mbsize: int) -> None: f"{offset}.", ) + @requires(NicCapability.SCATTERED_RX_ENABLED, decorator_fn=TestPmdShell.config_mtu_9000) @func_test def test_scatter_mbuf_2048(self) -> None: """Run the :meth:`pmd_scatter` test with `mbsize` set to 2048.""" From patchwork Wed Aug 21 14:53:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143242 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 7404945830; Wed, 21 Aug 2024 16:54:24 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 219144279A; Wed, 21 Aug 2024 16:53:33 +0200 (CEST) Received: from mail-ed1-f44.google.com (mail-ed1-f44.google.com [209.85.208.44]) by mails.dpdk.org (Postfix) with ESMTP id 0C7B242708 for ; Wed, 21 Aug 2024 16:53:29 +0200 (CEST) Received: by mail-ed1-f44.google.com with SMTP id 4fb4d7f45d1cf-58ef19aa69dso6787691a12.3 for ; Wed, 21 Aug 2024 07:53:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252008; x=1724856808; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=6dEa5DybgN+IHLWNuLB7WEceGA3zoWar+mYhWVTGPTM=; b=G8hcvREFbXlXSIy22iEJV9LOVhV8yuXy677mHYCFCEv/PPEG39tzF8EOG7Ea/7zsid H8S4LtlJNUOyIM6yiBvfAyj7XNd+mKSim3zxeJ87J0tbtj8gtnFXJqDq6UfiXCrZTGta jlW9A0WQA4BdJdFWmBspk+PV+gjcoJD++MMnWSEog+X2I2hANB3spM1cpV4i1wJgvKTg XMmUBNCRiGkhgsaaPn3nxSAP0UMRHL+vzvCjNc/cMuai6fh4Yf10KnYfef5a3oFwxyIu UeEYD5wE6LAx4qbjaV3YdOYnTuIFxbPJ51YTmi/BhKI3gtgvUOId7s+m+d60S3Vgs/gU 2dOw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252008; x=1724856808; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=6dEa5DybgN+IHLWNuLB7WEceGA3zoWar+mYhWVTGPTM=; b=QDSKX+DSjdVjygWPL4D/taB5u3aeIo/XEjiUOl3k1oNZidM1m20P+TiTb8/NH/6ciT WA70mGxbYJ8TCuT9y8BLcSIbXRE94T2IaDQIuurc5oiWvai8q0Mis98jySl82teGJjYP LMYH4mfrdOrJjoVqN0dr8fy3IcCeXYHRz2uJJQzXCEyKpfi7LPxe5ZkirGhv60oHXb2F SI6iNSw3W/ScmvD8XRP1YBuzim5c4Ensjw0iYaPA7ljAMGZNZTWUkfbVcnrN0rYn3iZ+ Izz0R4R4RPAAN31X5TtAOxwBzAn7ZANUrwxWR/7HTWVei9LGef/oCYskFP+10awpe7dd s7mw== X-Gm-Message-State: AOJu0YwQ216aMMVj6PdEcO2WB/ARDLdxTvTjdQhhTnEQVHcw97J6Uhv7 07sHcNhFLWTJJs7dkKcumK9pvBYO/0nKBkvZZ9HkVchXwgRrLg4CdbvQ78uZXRE= X-Google-Smtp-Source: AGHT+IGTacqw6l8ro5LaKDOnrlZjduclkSOewa521cbcX4tEXOqH7dmcizLiaWlgzEQ9IC4/3r+/aA== X-Received: by 2002:a17:907:e255:b0:a7a:a212:be48 with SMTP id a640c23a62f3a-a866f9b31e6mr212768466b.56.1724252008405; Wed, 21 Aug 2024 07:53:28 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:28 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 09/12] dts: add topology capability Date: Wed, 21 Aug 2024 16:53:12 +0200 Message-Id: <20240821145315.97974-10-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add support for marking test cases as requiring a certain topology. The default topology is a two link topology and the other supported topologies are one link and no link topologies. The TestProtocol of test suites and cases is extended with the topology type each test suite or case requires. Each test case starts out as requiring a two link topology and can be marked as requiring as topology directly (by decorating the test case) or through its test suite. If a test suite is decorated as requiring a certain topology, all its test cases are marked as such. If both test suite and a test case are decorated as requiring a topology, the test case cannot require a more complex topology than the whole suite (but it can require a less complex one). If a test suite is not decorated, this has no effect on required test case topology. Since the default topology is defined as a reference to one of the actual topologies, the NoAliasEnum from the aenum package is utilized, which removes the aliasing of Enums so that TopologyType.two_links and TopologyType.default are distinct. This is needed to distinguish between a user passed value and the default value being used (which is used when a test suite is or isn't decorated). Signed-off-by: Juraj Linkeš Reviewed-by: Dean Marx --- dts/framework/test_suite.py | 6 +- dts/framework/testbed_model/capability.py | 182 +++++++++++++++++++++- dts/framework/testbed_model/topology.py | 35 ++++- dts/tests/TestSuite_hello_world.py | 2 + dts/tests/TestSuite_pmd_buffer_scatter.py | 8 +- dts/tests/TestSuite_smoke_tests.py | 2 + 6 files changed, 217 insertions(+), 18 deletions(-) diff --git a/dts/framework/test_suite.py b/dts/framework/test_suite.py index 5c393ce8bf..51f49bd601 100644 --- a/dts/framework/test_suite.py +++ b/dts/framework/test_suite.py @@ -27,7 +27,7 @@ from framework.testbed_model.port import Port from framework.testbed_model.sut_node import SutNode from framework.testbed_model.tg_node import TGNode -from framework.testbed_model.topology import Topology, TopologyType +from framework.testbed_model.topology import Topology from framework.testbed_model.traffic_generator.capturing_traffic_generator import ( PacketFilteringConfig, ) @@ -73,7 +73,6 @@ class TestSuite(TestProtocol): #: will block the execution of all subsequent test suites in the current build target. is_blocking: ClassVar[bool] = False _logger: DTSLogger - _topology_type: TopologyType _sut_port_ingress: Port _sut_port_egress: Port _sut_ip_address_ingress: Union[IPv4Interface, IPv6Interface] @@ -102,7 +101,6 @@ def __init__( self.sut_node = sut_node self.tg_node = tg_node self._logger = get_dts_logger(self.__class__.__name__) - self._topology_type = topology.type self._tg_port_egress = topology.tg_port_egress self._sut_port_ingress = topology.sut_port_ingress self._sut_port_egress = topology.sut_port_egress @@ -468,6 +466,8 @@ def _decorator(func: TestSuiteMethodType) -> type[TestCase]: test_case.skip = cls.skip test_case.skip_reason = cls.skip_reason test_case.required_capabilities = set() + test_case.topology_type = cls.topology_type + test_case.topology_type.add_to_required(test_case) test_case.test_type = test_case_type return test_case diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py index 9a79e6ebb3..998efa95d2 100644 --- a/dts/framework/testbed_model/capability.py +++ b/dts/framework/testbed_model/capability.py @@ -7,11 +7,29 @@ and support for test environment capabilities. Many test cases are testing features not available on all hardware. +On the other hand, some test cases or suites may not need the most complex topology available. -The module also allows developers to mark test cases or suites a requiring certain -hardware capabilities with the :func:`requires` decorator. +The module allows developers to mark test cases or suites a requiring certain hardware capabilities +or a particular topology with the :func:`requires` decorator. + +There are differences between hardware and topology capabilities: + + * Hardware capabilities are assumed to not be required when not specified. + * However, some topology is always available, so each test case or suite is assigned + a default topology if no topology is specified in the decorator. + +Examples: + .. code:: python + + from framework.test_suite import TestSuite, func_test + from framework.testbed_model.capability import TopologyType, requires + # The whole test suite (each test case within) doesn't require any links. + @requires(topology_type=TopologyType.no_link) + @func_test + class TestHelloWorld(TestSuite): + def hello_world_single_core(self): + ... -Example: .. code:: python from framework.test_suite import TestSuite, func_test @@ -24,6 +42,7 @@ class TestPmdBufferScatter(TestSuite): def test_scatter_mbuf_2048(self): """ +import inspect from abc import ABC, abstractmethod from collections.abc import MutableSet, Sequence from dataclasses import dataclass @@ -31,6 +50,7 @@ def test_scatter_mbuf_2048(self): from typing_extensions import Self +from framework.exception import ConfigurationError from framework.logger import get_dts_logger from framework.remote_session.testpmd_shell import ( NicCapability, @@ -40,7 +60,7 @@ def test_scatter_mbuf_2048(self): ) from .sut_node import SutNode -from .topology import Topology +from .topology import Topology, TopologyType class Capability(ABC): @@ -244,6 +264,154 @@ def __repr__(self) -> str: return f"{condition_fn_name}{self.nic_capability}" +@dataclass +class TopologyCapability(Capability): + """A wrapper around :class:`~.topology.TopologyType`. + + Each test case must be assigned a topology. It could be done explicitly; + the implicit default is :attr:`~.topology.TopologyType.default`, which this class defines + as equal to :attr:`~.topology.TopologyType.two_links`. + + Test case topology may be set by setting the topology for the whole suite. + The priority in which topology is set is as follows: + + #. The topology set using the :func:`requires` decorator with a test case, + #. The topology set using the :func:`requires` decorator with a test suite, + #. The default topology if the decorator is not used. + + The default topology of test suite (i.e. when not using the decorator + or not setting the topology with the decorator) does not affect the topology of test cases. + + New instances should be created with the :meth:`create_unique` class method to ensure + there are no duplicate instances. + + Attributes: + topology_type: The topology type that defines each instance. + """ + + topology_type: TopologyType + + _unique_capabilities: ClassVar[dict[str, Self]] = {} + + def _preprocess_required(self, test_case_or_suite: type["TestProtocol"]) -> None: + test_case_or_suite.required_capabilities.discard(test_case_or_suite.topology_type) + test_case_or_suite.topology_type = self + + @classmethod + def get_unique(cls, topology_type: TopologyType) -> "TopologyCapability": + """Get the capability uniquely identified by `topology_type`. + + Args: + topology_type: The topology type. + + Returns: + The capability uniquely identified by `topology_type`. + """ + if topology_type.name not in cls._unique_capabilities: + cls._unique_capabilities[topology_type.name] = cls(topology_type) + return cls._unique_capabilities[topology_type.name] + + @classmethod + def get_supported_capabilities( + cls, sut_node: SutNode, topology: "Topology" + ) -> set["TopologyCapability"]: + """Overrides :meth:`~OSSession.get_supported_capabilities`.""" + supported_capabilities = set() + topology_capability = cls.get_unique(topology.type) + for topology_type in TopologyType: + candidate_topology_type = cls.get_unique(topology_type) + if candidate_topology_type <= topology_capability: + supported_capabilities.add(candidate_topology_type) + return supported_capabilities + + def set_required(self, test_case_or_suite: type["TestProtocol"]) -> None: + """The logic for setting the required topology of a test case or suite. + + Decorators are applied on methods of a class first, then on the class. + This means we have to modify test case topologies when processing the test suite topologies. + At that point, the test case topologies have been set by the :func:`requires` decorator. + The test suite topology only affects the test case topologies + if not :attr:`~.topology.TopologyType.default`. + """ + if inspect.isclass(test_case_or_suite): + if self.topology_type is not TopologyType.default: + self.add_to_required(test_case_or_suite) + func_test_cases, perf_test_cases = test_case_or_suite.get_test_cases() + for test_case in func_test_cases | perf_test_cases: + if test_case.topology_type.topology_type is TopologyType.default: + # test case topology has not been set, use the one set by the test suite + self.add_to_required(test_case) + elif test_case.topology_type > test_case_or_suite.topology_type: + raise ConfigurationError( + "The required topology type of a test case " + f"({test_case.__name__}|{test_case.topology_type}) " + "cannot be more complex than that of a suite " + f"({test_case_or_suite.__name__}|{test_case_or_suite.topology_type})." + ) + else: + self.add_to_required(test_case_or_suite) + + def __eq__(self, other) -> bool: + """Compare the :attr:`~TopologyCapability.topology_type`s. + + Args: + other: The object to compare with. + + Returns: + :data:`True` if the topology types are the same. + """ + return self.topology_type == other.topology_type + + def __lt__(self, other) -> bool: + """Compare the :attr:`~TopologyCapability.topology_type`s. + + Args: + other: The object to compare with. + + Returns: + :data:`True` if the instance's topology type is less complex than the compared object's. + """ + return self.topology_type < other.topology_type + + def __gt__(self, other) -> bool: + """Compare the :attr:`~TopologyCapability.topology_type`s. + + Args: + other: The object to compare with. + + Returns: + :data:`True` if the instance's topology type is more complex than the compared object's. + """ + return other < self + + def __le__(self, other) -> bool: + """Compare the :attr:`~TopologyCapability.topology_type`s. + + Args: + other: The object to compare with. + + Returns: + :data:`True` if the instance's topology type is less complex or equal than + the compared object's. + """ + return not self > other + + def __hash__(self): + """Each instance is identified by :attr:`topology_type`.""" + return self.topology_type.__hash__() + + def __str__(self): + """Easy to read string of class and name of :attr:`topology_type`.""" + name = self.topology_type.name + if self.topology_type is TopologyType.default: + name = TopologyType.get_from_value(self.topology_type.value) + return f"{type(self.topology_type).__name__}.{name}" + + def __repr__(self): + """Easy to read string of class and name of :attr:`topology_type`.""" + return self.__str__() + + class TestProtocol(Protocol): """Common test suite and test case attributes.""" @@ -251,6 +419,8 @@ class TestProtocol(Protocol): skip: ClassVar[bool] = False #: The reason for skipping the test case or suite. skip_reason: ClassVar[str] = "" + #: The topology type of the test case or suite. + topology_type: ClassVar[TopologyCapability] = TopologyCapability(TopologyType.default) #: The capabilities the test case or suite requires in order to be executed. required_capabilities: ClassVar[set[Capability]] = set() @@ -267,6 +437,7 @@ def get_test_cases(cls, test_case_sublist: Sequence[str] | None = None) -> tuple def requires( *nic_capabilities: NicCapability, decorator_fn: TestPmdShellDecorator | None = None, + topology_type: TopologyType = TopologyType.default, ) -> Callable[[type[TestProtocol]], type[TestProtocol]]: """A decorator that adds the required capabilities to a test case or test suite. @@ -287,6 +458,9 @@ def add_required_capability(test_case_or_suite: type[TestProtocol]) -> type[Test ) decorated_nic_capability.add_to_required(test_case_or_suite) + topology_capability = TopologyCapability.get_unique(topology_type) + topology_capability.set_required(test_case_or_suite) + return test_case_or_suite return add_required_capability diff --git a/dts/framework/testbed_model/topology.py b/dts/framework/testbed_model/topology.py index 19632ee890..712d252e44 100644 --- a/dts/framework/testbed_model/topology.py +++ b/dts/framework/testbed_model/topology.py @@ -8,15 +8,20 @@ """ from dataclasses import dataclass -from enum import IntEnum -from typing import Iterable +from typing import TYPE_CHECKING, Iterable + +if TYPE_CHECKING: + from enum import Enum as NoAliasEnum +else: + from aenum import NoAliasEnum from framework.config import PortConfig +from framework.exception import ConfigurationError from .port import Port -class TopologyType(IntEnum): +class TopologyType(int, NoAliasEnum): """Supported topology types.""" #: A topology with no Traffic Generator. @@ -25,6 +30,28 @@ class TopologyType(IntEnum): one_link = 1 #: A topology with two physical links between the Sut node and the TG node. two_links = 2 + #: The default topology required by test cases if not specified otherwise. + default = 2 + + @classmethod + def get_from_value(cls, value: int) -> "TopologyType": + """Get the corresponding instance from value. + + :class:`Enum`s that don't allow aliases don't know which instance should be returned + as there could be multiple valid instances. Except for the :attr:`default` value, + :class:`TopologyType` is a regular Enum. When creating an instance from value, we're + not interested in the default, since we already know the value, allowing us to remove + the ambiguity. + """ + match value: + case 0: + return TopologyType.no_link + case 1: + return TopologyType.one_link + case 2: + return TopologyType.two_links + case _: + raise ConfigurationError("More than two links in a topology are not supported.") class Topology: @@ -72,7 +99,7 @@ def __init__(self, sut_ports: Iterable[Port], tg_ports: Iterable[Port]): ): port_links.append(PortLink(sut_port=sut_port, tg_port=tg_port)) - self.type = TopologyType(len(port_links)) + self.type = TopologyType.get_from_value(len(port_links)) dummy_port = Port(PortConfig("", "", "", "", "", "")) self.tg_port_egress = dummy_port self.sut_port_ingress = dummy_port diff --git a/dts/tests/TestSuite_hello_world.py b/dts/tests/TestSuite_hello_world.py index 16d064ffeb..734f006026 100644 --- a/dts/tests/TestSuite_hello_world.py +++ b/dts/tests/TestSuite_hello_world.py @@ -9,6 +9,7 @@ from framework.remote_session.dpdk_shell import compute_eal_params from framework.test_suite import TestSuite, func_test +from framework.testbed_model.capability import TopologyType, requires from framework.testbed_model.cpu import ( LogicalCoreCount, LogicalCoreCountFilter, @@ -16,6 +17,7 @@ ) +@requires(topology_type=TopologyType.no_link) class TestHelloWorld(TestSuite): """DPDK hello world app test suite.""" diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py index 713549a5b2..89ece2ef56 100644 --- a/dts/tests/TestSuite_pmd_buffer_scatter.py +++ b/dts/tests/TestSuite_pmd_buffer_scatter.py @@ -54,15 +54,9 @@ def set_up_suite(self) -> None: """Set up the test suite. Setup: - Verify that we have at least 2 port links in the current test run - and increase the MTU of both ports on the traffic generator to 9000 + Increase the MTU of both ports on the traffic generator to 9000 to support larger packet sizes. """ - self.verify( - self._topology_type > 1, - "There must be at least two port links to run the scatter test suite", - ) - self.tg_node.main_session.configure_port_mtu(9000, self._tg_port_egress) self.tg_node.main_session.configure_port_mtu(9000, self._tg_port_ingress) diff --git a/dts/tests/TestSuite_smoke_tests.py b/dts/tests/TestSuite_smoke_tests.py index 94f90d9327..5f953a190f 100644 --- a/dts/tests/TestSuite_smoke_tests.py +++ b/dts/tests/TestSuite_smoke_tests.py @@ -18,9 +18,11 @@ from framework.remote_session.testpmd_shell import TestPmdShell from framework.settings import SETTINGS from framework.test_suite import TestSuite, func_test +from framework.testbed_model.capability import TopologyType, requires from framework.utils import REGEX_FOR_PCI_ADDRESS +@requires(topology_type=TopologyType.no_link) class TestSmokeTests(TestSuite): """DPDK and infrastructure smoke test suite. From patchwork Wed Aug 21 14:53:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143243 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id A9EF245830; Wed, 21 Aug 2024 16:54:33 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id C65D5427A3; Wed, 21 Aug 2024 16:53:34 +0200 (CEST) Received: from mail-wm1-f48.google.com (mail-wm1-f48.google.com [209.85.128.48]) by mails.dpdk.org (Postfix) with ESMTP id DA9164275B for ; Wed, 21 Aug 2024 16:53:29 +0200 (CEST) Received: by mail-wm1-f48.google.com with SMTP id 5b1f17b1804b1-42ab99fb45dso20230505e9.1 for ; Wed, 21 Aug 2024 07:53:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252009; x=1724856809; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=CoOdQJTPOm/TbWwt+Z3UyMpbv/OfppIAUnsOVMjGCLA=; b=lZrsw9oMhHA+ok5jLZWgrzc33/P5NVV40K3mxukGdgn5/5pNXgwxAOAKi0BWxMqYu6 Xj6VjU3gzd8OUjVfNxgm8nfTwNkoLW5upfX2TvJp4c4T6do2yldBdN5bJkDHiwX0UEvm aeH8o3BkJvIYBKBJTlmxmj24/y9nHrWZo+/ANP9YrwGG3kBb3yKU9D4De/seZ5pUQt21 zvoQskLUsImOhY/kMichLTMr8fSK1TE5m7ISs+1KljfANSRhZrRVb3Gj/b9vsDI+sWO8 osP/hHGOa5+ldGHRIvpTOttEg5eJMYjtDU8kiEVWaEa77pnP0trQSNVykhpIcES7ZzlO Qc6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252009; x=1724856809; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CoOdQJTPOm/TbWwt+Z3UyMpbv/OfppIAUnsOVMjGCLA=; b=KqF6smEb/JdO5TacIRxCrQD2m/bZelY7Q3xdvO3hmZdEH1tpYIeaML5sl1F6EqOpC/ RYNQ24DSe5iZ7xtVxM6I+54uJwSmHn8Mr1M40n1vWjTYPX7m/+X5LDJKewgz5PSRS2HS hciw71hT3Vd8cLmmdspigNslM3+iIs6aMU/zmQVNWD+bMRofq/jLrPdXEXYgKMvzebM8 KtlEkKUubYck0QogXTxdgukDAoG3DaSKpdDEM/N8IPx0w4uY6v5P+IIRvwfPqhowFm3y J2hyC8vhfy5IoKYLW4IOwtayZt45cJIy5hRe4Bg8TzAAsrN+mVxxidjADbXzitBLGoUM NGMQ== X-Gm-Message-State: AOJu0YwQxJPveT6UHQZMerLi896JBz4ZeTvAjusZBEqgxpYQcd7ZxvDq 0O5+9P6j5gm5uiOCBQrM9oJo9dmas/4qAfcAVxYGNgL25MoPrpcwzN+MXzY4DU4= X-Google-Smtp-Source: AGHT+IHKlcud4HJjqVlFaDHDM8FcdAMbEpvZsYd/z7s7heMM5qZJFN801hRGkH9MeObGUR/yv/4qZg== X-Received: by 2002:a5d:404b:0:b0:371:8bce:7a7c with SMTP id ffacd0b85a97d-372fd57f14cmr2140123f8f.13.1724252009470; Wed, 21 Aug 2024 07:53:29 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:29 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 10/12] doc: add DTS capability doc sources Date: Wed, 21 Aug 2024 16:53:13 +0200 Message-Id: <20240821145315.97974-11-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add new files to generate DTS API documentation from. Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx --- doc/api/dts/framework.testbed_model.capability.rst | 6 ++++++ doc/api/dts/framework.testbed_model.rst | 2 ++ doc/api/dts/framework.testbed_model.topology.rst | 6 ++++++ 3 files changed, 14 insertions(+) create mode 100644 doc/api/dts/framework.testbed_model.capability.rst create mode 100644 doc/api/dts/framework.testbed_model.topology.rst diff --git a/doc/api/dts/framework.testbed_model.capability.rst b/doc/api/dts/framework.testbed_model.capability.rst new file mode 100644 index 0000000000..326fed9104 --- /dev/null +++ b/doc/api/dts/framework.testbed_model.capability.rst @@ -0,0 +1,6 @@ +capability - Testbed Capabilities +================================= + +.. automodule:: framework.testbed_model.capability + :members: + :show-inheritance: diff --git a/doc/api/dts/framework.testbed_model.rst b/doc/api/dts/framework.testbed_model.rst index 4b024e47e6..e1f9543b28 100644 --- a/doc/api/dts/framework.testbed_model.rst +++ b/doc/api/dts/framework.testbed_model.rst @@ -21,6 +21,8 @@ testbed\_model - Testbed Modelling Package framework.testbed_model.node framework.testbed_model.sut_node framework.testbed_model.tg_node + framework.testbed_model.capability framework.testbed_model.cpu framework.testbed_model.port + framework.testbed_model.topology framework.testbed_model.virtual_device diff --git a/doc/api/dts/framework.testbed_model.topology.rst b/doc/api/dts/framework.testbed_model.topology.rst new file mode 100644 index 0000000000..f3d9d1f418 --- /dev/null +++ b/doc/api/dts/framework.testbed_model.topology.rst @@ -0,0 +1,6 @@ +topology - Testbed Topology +=========================== + +.. automodule:: framework.testbed_model.topology + :members: + :show-inheritance: From patchwork Wed Aug 21 14:53:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143244 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 3B39545830; Wed, 21 Aug 2024 16:54:40 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id D71EF427A6; Wed, 21 Aug 2024 16:53:35 +0200 (CEST) Received: from mail-ej1-f52.google.com (mail-ej1-f52.google.com [209.85.218.52]) by mails.dpdk.org (Postfix) with ESMTP id 2350E41149 for ; Wed, 21 Aug 2024 16:53:31 +0200 (CEST) Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-a868831216cso11218066b.3 for ; Wed, 21 Aug 2024 07:53:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252011; x=1724856811; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=mf5Eieawx7Ytw5lbkNtmBzZN7VVuziBpj7jK81+BPAU=; b=U1NEebCJPGVoyZNVwAvTsZ2tb8xf8XE8Hbu5LA/jrVHwdm0/6FNl5PvPvgTBYtMRj9 Pu5YY3NWsFntJfKy4Kc8XjfsriyJ/PDq/hjHIC7ds06MpDVSlgHK/L9oBozu2z/fMsU1 1iNSP2MahGesAx30Rm0igrVXNipOhhKBhjNtCANUE528WsUqpFt16vTVZ9aQEpmjx/yi wLhY6eYeqN+6Yo12GiL54D2ZCoQ61gAGec1W49fHxHtA6IHR6F9DOxuv4V6leDxof4Cb RSoc1DsvN0MbDGuFGfv/GsNVBKO9txSVFgtcJlgNHGrU25X3AAEhQWgOd1+XNpRjfB2M Frhw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252011; x=1724856811; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=mf5Eieawx7Ytw5lbkNtmBzZN7VVuziBpj7jK81+BPAU=; b=h4tISN3A+7oRX1m5MzlezgWYrj+t1DTHXLsVqbEDTO1ojIQp1JR1mjrkoX0z/G648+ E49u+wzv/x0tSiovQ/7owo+5t9q2LrMs2PB4P+4DXWD9VJw+tkSyzCzbKGRZPVk3vJx8 czP5go9VXClrHfPNXQ1UrbTNNoQfP9xZ04sl5dlA4WEVAR94RhNa0x10sPrY8WtYllbi 6VaZdIt0hiPmLsE7gKKohgCetFvk9RAw60Vjgk1fUNdok9lEBgBgrimQWeNB2lt/LW48 k0DAhn+R3ZilPhPF+jKz+JFOax0rCWAAowwQLkq1Z/0iDZlhXx0CFUsJ3X9ED0I06+6d gJWQ== X-Gm-Message-State: AOJu0YxTVjJPZ4lqsv5F/kaipo9TvxEwnds7PY0EJMTtToHJFwxoGZGb LX7FRZa+lyhNYAPKfuVfekw+IEdKy88HUfxFRAgEjm0BP66XfhcVceXaYDrhzRw= X-Google-Smtp-Source: AGHT+IGXhXxFi79KnbTALGIAISWgs+pYkB0ww7UVrVQdrTQZmW9WMDkX8qjYm+kCJbXLqGrAZ5XkRw== X-Received: by 2002:a17:907:80b:b0:a79:82c1:a5b2 with SMTP id a640c23a62f3a-a866f11dad6mr170429366b.9.1724252010537; Wed, 21 Aug 2024 07:53:30 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:30 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 11/12] dts: add Rx offload capabilities Date: Wed, 21 Aug 2024 16:53:14 +0200 Message-Id: <20240821145315.97974-12-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org The scatter Rx offload capability is needed for the pmd_buffer_scatter test suite. The command that retrieves the capability is: show port rx_offload capabilities The command also retrieves a lot of other capabilities (RX_OFFLOAD_*) which are all added into a Flag. The Flag members correspond to NIC capability names so a convenience function that looks for the supported Flags in a testpmd output is also added. The NIC capability names (mentioned above) are copy-pasted from the Flag. Dynamic addition of Enum members runs into problems with typing (mypy doesn't know about the members) and documentation generation (Sphinx doesn't know about the members). Signed-off-by: Juraj Linkeš Reviewed-by: Dean Marx --- dts/framework/remote_session/testpmd_shell.py | 213 ++++++++++++++++++ dts/tests/TestSuite_pmd_buffer_scatter.py | 1 + 2 files changed, 214 insertions(+) diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py index 48c31124d1..f83569669e 100644 --- a/dts/framework/remote_session/testpmd_shell.py +++ b/dts/framework/remote_session/testpmd_shell.py @@ -659,6 +659,103 @@ class TestPmdPortStats(TextParser): tx_bps: int = field(metadata=TextParser.find_int(r"Tx-bps:\s+(\d+)")) +class RxOffloadCapability(Flag): + """Rx offload capabilities of a device.""" + + #: + RX_OFFLOAD_VLAN_STRIP = auto() + #: Device supports L3 checksum offload. + RX_OFFLOAD_IPV4_CKSUM = auto() + #: Device supports L4 checksum offload. + RX_OFFLOAD_UDP_CKSUM = auto() + #: Device supports L4 checksum offload. + RX_OFFLOAD_TCP_CKSUM = auto() + #: Device supports Large Receive Offload. + RX_OFFLOAD_TCP_LRO = auto() + #: Device supports QinQ (queue in queue) offload. + RX_OFFLOAD_QINQ_STRIP = auto() + #: Device supports inner packet L3 checksum. + RX_OFFLOAD_OUTER_IPV4_CKSUM = auto() + #: Device supports MACsec. + RX_OFFLOAD_MACSEC_STRIP = auto() + #: Device supports filtering of a VLAN Tag identifier. + RX_OFFLOAD_VLAN_FILTER = 1 << 9 + #: Device supports VLAN offload. + RX_OFFLOAD_VLAN_EXTEND = auto() + #: Device supports receiving segmented mbufs. + RX_OFFLOAD_SCATTER = 1 << 13 + #: Device supports Timestamp. + RX_OFFLOAD_TIMESTAMP = auto() + #: Device supports crypto processing while packet is received in NIC. + RX_OFFLOAD_SECURITY = auto() + #: Device supports CRC stripping. + RX_OFFLOAD_KEEP_CRC = auto() + #: Device supports L4 checksum offload. + RX_OFFLOAD_SCTP_CKSUM = auto() + #: Device supports inner packet L4 checksum. + RX_OFFLOAD_OUTER_UDP_CKSUM = auto() + #: Device supports RSS hashing. + RX_OFFLOAD_RSS_HASH = auto() + #: Device supports + RX_OFFLOAD_BUFFER_SPLIT = auto() + #: Device supports all checksum capabilities. + RX_OFFLOAD_CHECKSUM = RX_OFFLOAD_IPV4_CKSUM | RX_OFFLOAD_UDP_CKSUM | RX_OFFLOAD_TCP_CKSUM + #: Device supports all VLAN capabilities. + RX_OFFLOAD_VLAN = ( + RX_OFFLOAD_VLAN_STRIP + | RX_OFFLOAD_VLAN_FILTER + | RX_OFFLOAD_VLAN_EXTEND + | RX_OFFLOAD_QINQ_STRIP + ) + + @classmethod + def from_string(cls, line: str) -> Self: + """Make an instance from a string containing the flag names separated with a space. + + Args: + line: The line to parse. + + Returns: + A new instance containing all found flags. + """ + flag = cls(0) + for flag_name in line.split(): + flag |= cls[f"RX_OFFLOAD_{flag_name}"] + return flag + + @classmethod + def make_parser(cls, per_port: bool) -> ParserFn: + """Make a parser function. + + Args: + per_port: If :data:`True`, will return capabilities per port. If :data:`False`, + will return capabilities per queue. + + Returns: + ParserFn: A dictionary for the `dataclasses.field` metadata argument containing a + parser function that makes an instance of this flag from text. + """ + granularity = "Port" if per_port else "Queue" + return TextParser.wrap( + TextParser.find(rf"Per {granularity}\s+:(.*)$", re.MULTILINE), + cls.from_string, + ) + + +@dataclass +class RxOffloadCapabilities(TextParser): + """The result of testpmd's ``show port rx_offload capabilities`` command.""" + + #: + port_id: int = field( + metadata=TextParser.find_int(r"Rx Offloading Capabilities of port (\d+) :") + ) + #: Per-queue Rx offload capabilities. + per_queue: RxOffloadCapability = field(metadata=RxOffloadCapability.make_parser(False)) + #: Capabilities other than per-queue Rx offload capabilities. + per_port: RxOffloadCapability = field(metadata=RxOffloadCapability.make_parser(True)) + + T = TypeVarTuple("T") # type: ignore[misc] @@ -1048,6 +1145,42 @@ def _close(self) -> None: ====== Capability retrieval methods ====== """ + def get_capabilities_rx_offload( + self, + supported_capabilities: MutableSet["NicCapability"], + unsupported_capabilities: MutableSet["NicCapability"], + ) -> None: + """Get all rx offload capabilities and divide them into supported and unsupported. + + Args: + supported_capabilities: Supported capabilities will be added to this set. + unsupported_capabilities: Unsupported capabilities will be added to this set. + """ + self._logger.debug("Getting rx offload capabilities.") + command = f"show port {self.ports[0].id} rx_offload capabilities" + rx_offload_capabilities_out = self.send_command(command) + rx_offload_capabilities = RxOffloadCapabilities.parse(rx_offload_capabilities_out) + self._update_capabilities_from_flag( + supported_capabilities, + unsupported_capabilities, + RxOffloadCapability, + rx_offload_capabilities.per_port | rx_offload_capabilities.per_queue, + ) + + def _update_capabilities_from_flag( + self, + supported_capabilities: MutableSet["NicCapability"], + unsupported_capabilities: MutableSet["NicCapability"], + flag_class: type[Flag], + supported_flags: Flag, + ) -> None: + """Divide all flags from `flag_class` into supported and unsupported.""" + for flag in flag_class: + if flag in supported_flags: + supported_capabilities.add(NicCapability[str(flag.name)]) + else: + unsupported_capabilities.add(NicCapability[str(flag.name)]) + def get_capabilities_rxq_info( self, supported_capabilities: MutableSet["NicCapability"], @@ -1119,6 +1252,86 @@ class NicCapability(NoAliasEnum): SCATTERED_RX_ENABLED: TestPmdShellCapabilityMethod = partial( TestPmdShell.get_capabilities_rxq_info ) + #: + RX_OFFLOAD_VLAN_STRIP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports L3 checksum offload. + RX_OFFLOAD_IPV4_CKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports L4 checksum offload. + RX_OFFLOAD_UDP_CKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports L4 checksum offload. + RX_OFFLOAD_TCP_CKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports Large Receive Offload. + RX_OFFLOAD_TCP_LRO: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports QinQ (queue in queue) offload. + RX_OFFLOAD_QINQ_STRIP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports inner packet L3 checksum. + RX_OFFLOAD_OUTER_IPV4_CKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports MACsec. + RX_OFFLOAD_MACSEC_STRIP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports filtering of a VLAN Tag identifier. + RX_OFFLOAD_VLAN_FILTER: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports VLAN offload. + RX_OFFLOAD_VLAN_EXTEND: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports receiving segmented mbufs. + RX_OFFLOAD_SCATTER: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports Timestamp. + RX_OFFLOAD_TIMESTAMP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports crypto processing while packet is received in NIC. + RX_OFFLOAD_SECURITY: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports CRC stripping. + RX_OFFLOAD_KEEP_CRC: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports L4 checksum offload. + RX_OFFLOAD_SCTP_CKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports inner packet L4 checksum. + RX_OFFLOAD_OUTER_UDP_CKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports RSS hashing. + RX_OFFLOAD_RSS_HASH: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports scatter Rx packets to segmented mbufs. + RX_OFFLOAD_BUFFER_SPLIT: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports all checksum capabilities. + RX_OFFLOAD_CHECKSUM: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) + #: Device supports all VLAN capabilities. + RX_OFFLOAD_VLAN: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_rx_offload + ) def __call__( self, diff --git a/dts/tests/TestSuite_pmd_buffer_scatter.py b/dts/tests/TestSuite_pmd_buffer_scatter.py index 89ece2ef56..64c48b0793 100644 --- a/dts/tests/TestSuite_pmd_buffer_scatter.py +++ b/dts/tests/TestSuite_pmd_buffer_scatter.py @@ -28,6 +28,7 @@ from framework.testbed_model.capability import NicCapability, requires +@requires(NicCapability.RX_OFFLOAD_SCATTER) class TestPmdBufferScatter(TestSuite): """DPDK PMD packet scattering test suite. From patchwork Wed Aug 21 14:53:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Juraj_Linke=C5=A1?= X-Patchwork-Id: 143245 X-Patchwork-Delegate: juraj.linkes@pantheon.tech Return-Path: 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]) by inbox.dpdk.org (Postfix) with ESMTP id 8EB7245830; Wed, 21 Aug 2024 16:54:46 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 017C3427A8; Wed, 21 Aug 2024 16:53:37 +0200 (CEST) Received: from mail-ed1-f46.google.com (mail-ed1-f46.google.com [209.85.208.46]) by mails.dpdk.org (Postfix) with ESMTP id 2CD4A42795 for ; Wed, 21 Aug 2024 16:53:32 +0200 (CEST) Received: by mail-ed1-f46.google.com with SMTP id 4fb4d7f45d1cf-5bed83489c3so5666087a12.3 for ; Wed, 21 Aug 2024 07:53:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pantheon.tech; s=google; t=1724252012; x=1724856812; darn=dpdk.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=cqcX3MhGJftS+hwVm4zY15+W82fGO9gj7oygWcfEn1c=; b=G/BSCkWzFjDdu4P+hOltyxy5Xau7JxT39e0chfyeBEdul23U4zfA0U8GhAXHj0ACqC dJ5Jy3/or0sLnxuxVnyVDM1HbIL83LMVwLEzfH2p3og91iPn4QOoO7HzOCYH8sI/jBVI Tj8EntAyI5R29dv8aMrU3D1srQNow1FO2QnVUhIuiJh1DnAoB78rtsG6i9HwWFYvhMQd NR4+M/lzT/CWu3nSQ8vXBi0miXInVsRSMVzkGv2fVzD0gTuJSPuq1+A8lfp3PKm8uwqo t9Ojj/JfKG29YfpkO/tVZA7cemZ0hHlfOCVb2pl42bBHEC1WOs0utofpOhJSOAq8Ces+ GEsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724252012; x=1724856812; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=cqcX3MhGJftS+hwVm4zY15+W82fGO9gj7oygWcfEn1c=; b=K4eyy/D6cM3k1uWYXZDjndqGYnB9mIK13qsxBWODCWH0zQyERojhOVROEdXYuYTwxv KgmSW2xpq2BH55fwzuoInA1isVVK1dbBt+E2WnHdnqVdG6HV/F5ubY7lZzUFNFg7Xint 1xmw/LcWKbGGz/rLQ4dOr525+ATHcfIuzpyCTQk7e0HKnDjiiUkHEHOQRmTsHZrjER19 DApumHA3U32CCHjUYdzHIqVvbLUOMhkdUX0i2YcE3XOyp2T2fETtjU5B+bTpEJwSTb5k F7DTlWfZtT2BsTGW/JVwf8TCZqyauEZTa6vyuNWNUWI+HR4F+auQFS3ileah6D01nY6m IXuw== X-Gm-Message-State: AOJu0Yz/a/cJUS1LrUZYaA0vljUUi7eiEvIhuNPw5a2Edp8Co7EEUS68 HHGBbsgcC3VQ2AJt0FwMeBpJnKoR2kgs0EQPq15ntfABkKGngQ5cjGQ4UWF+FTLx6CVXJjnkNm7 YFrc= X-Google-Smtp-Source: AGHT+IHc1ftkiP1Oj9w/6xqV1nL9dDwASZsN/wggFDTDLQVZhYXz2Qmwq9PzDEcRQEs8MilvT1GrQg== X-Received: by 2002:a17:906:bc27:b0:a7a:ab1a:2d65 with SMTP id a640c23a62f3a-a866f9c83f1mr203891766b.67.1724252011600; Wed, 21 Aug 2024 07:53:31 -0700 (PDT) Received: from localhost.localdomain ([84.245.121.236]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a8680c4725csm52434866b.91.2024.08.21.07.53.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 21 Aug 2024 07:53:31 -0700 (PDT) From: =?utf-8?q?Juraj_Linke=C5=A1?= To: thomas@monjalon.net, Honnappa.Nagarahalli@arm.com, jspewock@iol.unh.edu, probb@iol.unh.edu, paul.szczepanek@arm.com, Luca.Vizzarro@arm.com, npratte@iol.unh.edu, dmarx@iol.unh.edu, alex.chapman@arm.com Cc: dev@dpdk.org, =?utf-8?q?Juraj_Linke=C5=A1?= Subject: [PATCH v3 12/12] dts: add NIC capabilities from show port info Date: Wed, 21 Aug 2024 16:53:15 +0200 Message-Id: <20240821145315.97974-13-juraj.linkes@pantheon.tech> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240821145315.97974-1-juraj.linkes@pantheon.tech> References: <20240301155416.96960-1-juraj.linkes@pantheon.tech> <20240821145315.97974-1-juraj.linkes@pantheon.tech> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Add the capabilities advertised by the testpmd command "show port info" so that test cases may be marked as requiring those capabilities: RUNTIME_RX_QUEUE_SETUP RUNTIME_TX_QUEUE_SETUP RXQ_SHARE FLOW_RULE_KEEP FLOW_SHARED_OBJECT_KEEP These names are copy pasted from the existing DeviceCapabilitiesFlag class. Dynamic addition of Enum members runs into problems with typing (mypy doesn't know about the members) and documentation generation (Sphinx doesn't know about the members). Signed-off-by: Juraj Linkeš Reviewed-by: Jeremy Spewock Reviewed-by: Dean Marx --- dts/framework/remote_session/testpmd_shell.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/dts/framework/remote_session/testpmd_shell.py b/dts/framework/remote_session/testpmd_shell.py index f83569669e..166ffc827e 100644 --- a/dts/framework/remote_session/testpmd_shell.py +++ b/dts/framework/remote_session/testpmd_shell.py @@ -1200,6 +1200,24 @@ def get_capabilities_rxq_info( else: unsupported_capabilities.add(NicCapability.SCATTERED_RX_ENABLED) + def get_capabilities_show_port_info( + self, + supported_capabilities: MutableSet["NicCapability"], + unsupported_capabilities: MutableSet["NicCapability"], + ) -> None: + """Get all capabilities from show port info and divide them into supported and unsupported. + + Args: + supported_capabilities: Supported capabilities will be added to this set. + unsupported_capabilities: Unsupported capabilities will be added to this set. + """ + self._update_capabilities_from_flag( + supported_capabilities, + unsupported_capabilities, + DeviceCapabilitiesFlag, + self.ports[0].device_capabilities, + ) + """ ====== Decorator methods ====== """ @@ -1332,6 +1350,24 @@ class NicCapability(NoAliasEnum): RX_OFFLOAD_VLAN: TestPmdShellCapabilityMethod = partial( TestPmdShell.get_capabilities_rx_offload ) + #: Device supports Rx queue setup after device started. + RUNTIME_RX_QUEUE_SETUP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_show_port_info + ) + #: Device supports Tx queue setup after device started. + RUNTIME_TX_QUEUE_SETUP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_show_port_info + ) + #: Device supports shared Rx queue among ports within Rx domain and switch domain. + RXQ_SHARE: TestPmdShellCapabilityMethod = partial(TestPmdShell.get_capabilities_show_port_info) + #: Device supports keeping flow rules across restart. + FLOW_RULE_KEEP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_show_port_info + ) + #: Device supports keeping shared flow objects across restart. + FLOW_SHARED_OBJECT_KEEP: TestPmdShellCapabilityMethod = partial( + TestPmdShell.get_capabilities_show_port_info + ) def __call__( self,