DPDK patches and discussions
 help / color / mirror / Atom feed
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.


  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).