From: Stephen Hemminger <stephen@networkplumber.org>
To: Lukas Sismis <sismis@dyna-nic.com>
Cc: dev@dpdk.org
Subject: Re: [PATCH v1 4/5] test: add flow parser unit tests
Date: Tue, 6 Jan 2026 08:37:09 -0800 [thread overview]
Message-ID: <20260106083709.3751c70c@phoenix.local> (raw)
In-Reply-To: <20260105213025.749383-5-sismis@dyna-nic.com>
On Mon, 5 Jan 2026 22:30:22 +0100
Lukas Sismis <sismis@dyna-nic.com> wrote:
> Add comprehensive unit tests for the flow parser library.
>
> Tests cover:
> - flow_parser_autotest: Tests rte_flow_parser_parse() with various
> flow command strings including create, destroy, validate, query,
> list, and isolate commands. Verifies parsed patterns, actions,
> and attributes match expected values.
>
> - flow_parser_helpers_autotest: Tests the standalone helper functions
> parse_attr_str(), parse_pattern_str(), and parse_actions_str().
> Verifies parsing of attributes, patterns with various item types,
> and action sequences.
>
> Run with: dpdk-test flow_parser_autotest
> dpdk-test flow_parser_helpers_autotest
>
> Signed-off-by: Lukas Sismis <sismis@dyna-nic.com>
> ---
> app/test/meson.build | 1 +
> app/test/test_flow_parser.c | 226 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 227 insertions(+)
> create mode 100644 app/test/test_flow_parser.c
>
> diff --git a/app/test/meson.build b/app/test/meson.build
> index efec42a6bf..03cf8aa70c 100644
> --- a/app/test/meson.build
> +++ b/app/test/meson.build
> @@ -88,6 +88,7 @@ source_file_deps = {
> 'test_fib6_perf.c': ['fib'],
> 'test_fib_perf.c': ['net', 'fib'],
> 'test_flow_classify.c': ['net', 'acl', 'table', 'ethdev', 'flow_classify'],
> + 'test_flow_parser.c': ['flow_parser', 'cmdline', 'ethdev'],
> 'test_func_reentrancy.c': ['hash', 'lpm'],
> 'test_graph.c': ['graph'],
> 'test_graph_feature_arc.c': ['graph'],
> diff --git a/app/test/test_flow_parser.c b/app/test/test_flow_parser.c
> new file mode 100644
> index 0000000000..1e65762d9f
> --- /dev/null
> +++ b/app/test/test_flow_parser.c
> @@ -0,0 +1,226 @@
> +/* SPDX-License-Identifier: BSD-3-Clause */
> +
> +#include <stdint.h>
> +#include <string.h>
> +
> +#include <rte_flow.h>
> +#include <rte_flow_parser.h>
> +
> +#include "test.h"
> +
> +static int
> +test_flow_parser_command_mapping(void)
> +{
> + static const char *create_cmd =
> + "flow create 0 ingress pattern eth / end "
> + "actions drop / end";
> + static const char *list_cmd = "flow list 0";
> + uint8_t outbuf[4096];
> + struct rte_flow_parser_output *out = (void *)outbuf;
> + int ret;
> +
> + ret = rte_flow_parser_init(NULL);
> + if (ret != 0)
> + return TEST_FAILED;
> +
> + /* Test flow create command parsing */
> + memset(outbuf, 0, sizeof(outbuf));
> + ret = rte_flow_parser_parse(create_cmd, out, sizeof(outbuf));
> + if (ret != 0) {
> + printf("flow create parse failed: %d\n", ret);
> + return TEST_FAILED;
> + }
> + if (out->command != RTE_FLOW_PARSER_CMD_CREATE) {
> + printf("expected CREATE command, got %d\n", out->command);
> + return TEST_FAILED;
> + }
> + if (out->port != 0) {
> + printf("expected port 0, got %u\n", out->port);
> + return TEST_FAILED;
> + }
> + /* pattern: eth / end = 2 items */
> + if (out->args.vc.pattern_n != 2) {
> + printf("expected 2 pattern items, got %u\n",
> + out->args.vc.pattern_n);
> + return TEST_FAILED;
> + }
> + if (out->args.vc.pattern[0].type != RTE_FLOW_ITEM_TYPE_ETH) {
> + printf("expected ETH pattern, got %d\n",
> + out->args.vc.pattern[0].type);
> + return TEST_FAILED;
> + }
> + if (out->args.vc.pattern[1].type != RTE_FLOW_ITEM_TYPE_END) {
> + printf("expected END pattern, got %d\n",
> + out->args.vc.pattern[1].type);
> + return TEST_FAILED;
> + }
> + /* actions: drop / end = 2 items */
> + if (out->args.vc.actions_n != 2) {
> + printf("expected 2 action items, got %u\n",
> + out->args.vc.actions_n);
> + return TEST_FAILED;
> + }
> + if (out->args.vc.actions[0].type != RTE_FLOW_ACTION_TYPE_DROP) {
> + printf("expected DROP action, got %d\n",
> + out->args.vc.actions[0].type);
> + return TEST_FAILED;
> + }
> + if (out->args.vc.actions[1].type != RTE_FLOW_ACTION_TYPE_END) {
> + printf("expected END action, got %d\n",
> + out->args.vc.actions[1].type);
> + return TEST_FAILED;
> + }
> + /* ingress attribute */
> + if (out->args.vc.attr.ingress != 1 || out->args.vc.attr.egress != 0) {
> + printf("expected ingress=1 egress=0\n");
> + return TEST_FAILED;
> + }
> +
> + /* Test flow list command parsing */
> + memset(outbuf, 0, sizeof(outbuf));
> + ret = rte_flow_parser_parse(list_cmd, out, sizeof(outbuf));
> + if (ret != 0 ||
> + out->command != RTE_FLOW_PARSER_CMD_LIST ||
> + out->port != 0)
> + return TEST_FAILED;
> +
> + return TEST_SUCCESS;
> +}
> +
> +static int
> +test_flow_parser_lightweight_helpers(void)
> +{
> + const struct rte_flow_item *pattern = NULL;
> + const struct rte_flow_action *actions = NULL;
> + const struct rte_flow_action_queue *queue_conf;
> + const struct rte_flow_action_mark *mark_conf;
> + struct rte_flow_attr attr;
> + uint32_t pattern_n = 0;
> + uint32_t actions_n = 0;
> + int ret;
> +
> + ret = rte_flow_parser_init(NULL);
> + if (ret != 0)
> + return TEST_FAILED;
> +
> + /* Test attribute parsing */
> + memset(&attr, 0, sizeof(attr));
> + ret = rte_flow_parser_parse_attr_str("ingress group 1 priority 5", &attr);
> + if (ret != 0) {
> + printf("attr parse failed: %d\n", ret);
> + return TEST_FAILED;
> + }
> + if (attr.group != 1 || attr.priority != 5 ||
> + attr.ingress != 1 || attr.egress != 0) {
> + printf("attr mismatch: group=%u priority=%u ingress=%u egress=%u\n",
> + attr.group, attr.priority, attr.ingress, attr.egress);
> + return TEST_FAILED;
> + }
> +
> + /* Test pattern parsing: eth / ipv4 / end = 3 items */
> + ret = rte_flow_parser_parse_pattern_str("eth / ipv4 / end",
> + &pattern, &pattern_n);
> + if (ret != 0) {
> + printf("pattern parse failed: %d\n", ret);
> + return TEST_FAILED;
> + }
> + if (pattern_n != 3) {
> + printf("expected 3 pattern items, got %u\n", pattern_n);
> + return TEST_FAILED;
> + }
> + if (pattern[0].type != RTE_FLOW_ITEM_TYPE_ETH) {
> + printf("pattern[0] expected ETH, got %d\n", pattern[0].type);
> + return TEST_FAILED;
> + }
> + if (pattern[1].type != RTE_FLOW_ITEM_TYPE_IPV4) {
> + printf("pattern[1] expected IPV4, got %d\n", pattern[1].type);
> + return TEST_FAILED;
> + }
> + if (pattern[2].type != RTE_FLOW_ITEM_TYPE_END) {
> + printf("pattern[2] expected END, got %d\n", pattern[2].type);
> + return TEST_FAILED;
> + }
> +
> + /* Test actions parsing with config values: queue index 3 / end = 2 items */
> + ret = rte_flow_parser_parse_actions_str("queue index 3 / end",
> + &actions, &actions_n);
> + if (ret != 0) {
> + printf("actions parse failed: %d\n", ret);
> + return TEST_FAILED;
> + }
> + if (actions_n != 2) {
> + printf("expected 2 action items, got %u\n", actions_n);
> + return TEST_FAILED;
> + }
> + if (actions[0].type != RTE_FLOW_ACTION_TYPE_QUEUE) {
> + printf("actions[0] expected QUEUE, got %d\n", actions[0].type);
> + return TEST_FAILED;
> + }
> + queue_conf = actions[0].conf;
> + if (queue_conf == NULL || queue_conf->index != 3) {
> + printf("queue index expected 3, got %u\n",
> + queue_conf ? queue_conf->index : 0);
> + return TEST_FAILED;
> + }
> + if (actions[1].type != RTE_FLOW_ACTION_TYPE_END) {
> + printf("actions[1] expected END, got %d\n", actions[1].type);
> + return TEST_FAILED;
> + }
> +
> + /* Test multiple actions: mark id 42 / drop / end = 3 items */
> + ret = rte_flow_parser_parse_actions_str("mark id 42 / drop / end",
> + &actions, &actions_n);
> + if (ret != 0) {
> + printf("multi-action parse failed: %d\n", ret);
> + return TEST_FAILED;
> + }
> + if (actions_n != 3) {
> + printf("expected 3 action items, got %u\n", actions_n);
> + return TEST_FAILED;
> + }
> + if (actions[0].type != RTE_FLOW_ACTION_TYPE_MARK) {
> + printf("actions[0] expected MARK, got %d\n", actions[0].type);
> + return TEST_FAILED;
> + }
> + mark_conf = actions[0].conf;
> + if (mark_conf == NULL || mark_conf->id != 42) {
> + printf("mark id expected 42, got %u\n",
> + mark_conf ? mark_conf->id : 0);
> + return TEST_FAILED;
> + }
> + if (actions[1].type != RTE_FLOW_ACTION_TYPE_DROP) {
> + printf("actions[1] expected DROP, got %d\n", actions[1].type);
> + return TEST_FAILED;
> + }
> + if (actions[2].type != RTE_FLOW_ACTION_TYPE_END) {
> + printf("actions[2] expected END, got %d\n", actions[2].type);
> + return TEST_FAILED;
> + }
> +
> + /* Test complex pattern: eth / ipv4 / tcp / end = 4 items */
> + ret = rte_flow_parser_parse_pattern_str("eth / ipv4 / tcp / end",
> + &pattern, &pattern_n);
> + if (ret != 0) {
> + printf("complex pattern parse failed: %d\n", ret);
> + return TEST_FAILED;
> + }
> + if (pattern_n != 4) {
> + printf("expected 4 pattern items, got %u\n", pattern_n);
> + return TEST_FAILED;
> + }
> + if (pattern[0].type != RTE_FLOW_ITEM_TYPE_ETH ||
> + pattern[1].type != RTE_FLOW_ITEM_TYPE_IPV4 ||
> + pattern[2].type != RTE_FLOW_ITEM_TYPE_TCP ||
> + pattern[3].type != RTE_FLOW_ITEM_TYPE_END) {
> + printf("complex pattern type mismatch\n");
> + return TEST_FAILED;
> + }
> +
> + return TEST_SUCCESS;
> +}
> +
> +REGISTER_FAST_TEST(flow_parser_autotest, true, true,
> + test_flow_parser_command_mapping);
> +
> +REGISTER_FAST_TEST(flow_parser_helpers_autotest, true, true,
> + test_flow_parser_lightweight_helpers);
Test looks good.
My preference is for tests to use the macros like TEST_ASSERT() in test.h
since then it makes fixing them easier.
Also if there are multiple tests uses unit test runner instead of one big
test or registering multiple tests.
next prev parent reply other threads:[~2026-01-06 16:37 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-05 21:30 [PATCH v1 0/5] flow_parser: add shared parser library Lukas Sismis
2026-01-05 21:30 ` [PATCH v1 1/5] " Lukas Sismis
2026-01-06 16:35 ` Stephen Hemminger
2026-01-05 21:30 ` [PATCH v1 2/5] app/testpmd: integrate shared flow " Lukas Sismis
2026-01-05 21:30 ` [PATCH v1 3/5] examples/flow_parsing: add flow parser demo Lukas Sismis
2026-01-05 21:30 ` [PATCH v1 4/5] test: add flow parser unit tests Lukas Sismis
2026-01-06 16:37 ` Stephen Hemminger [this message]
2026-01-05 21:30 ` [PATCH v1 5/5] doc: add flow parser library maintainers Lukas Sismis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260106083709.3751c70c@phoenix.local \
--to=stephen@networkplumber.org \
--cc=dev@dpdk.org \
--cc=sismis@dyna-nic.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).