From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from dpdk.org (dpdk.org [92.243.14.124])
	by inbox.dpdk.org (Postfix) with ESMTP id A194DA059F;
	Sat, 11 Apr 2020 16:17:04 +0200 (CEST)
Received: from [92.243.14.124] (localhost [127.0.0.1])
	by dpdk.org (Postfix) with ESMTP id 6A9051C294;
	Sat, 11 Apr 2020 16:15:25 +0200 (CEST)
Received: from mx0b-0016f401.pphosted.com (mx0a-0016f401.pphosted.com
 [67.231.148.174]) by dpdk.org (Postfix) with ESMTP id 972741C23D
 for <dev@dpdk.org>; Sat, 11 Apr 2020 16:15:21 +0200 (CEST)
Received: from pps.filterd (m0045849.ppops.net [127.0.0.1])
 by mx0a-0016f401.pphosted.com (8.16.0.42/8.16.0.42) with SMTP id
 03BEAM5h016872; Sat, 11 Apr 2020 07:15:19 -0700
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=marvell.com;
 h=from : to : cc :
 subject : date : message-id : in-reply-to : references : mime-version :
 content-transfer-encoding : content-type; s=pfpt0818;
 bh=MQlHpOQFwKOHh+7MnrRz17+qrILQ8DHl+NGvUU8Zh2M=;
 b=fcVshKTvm7ZWBSl1ncun2QJQ+yVAcrRfPVlWLDK68wVD68ZtgpAJmY8Tm9FWozfRp82z
 MkhhsaTVOO3+RILluxGr62sy1757nCXUPN7kkeNYDrjlDrnl8poTdo11UHAFCe1Q7+nT
 Gu7/uyj+Sz15r9OGMNSjLpgw8vbybTo91MERtcln6N5l2TT+aznRopXYK9aCmRv4/uBH
 3k4MPzanhbDbxAdxLmS2lC0ZazHlaORv/8hhhZbjSXtkFG9Z+EGDMvB/a61VzJsKJY8s
 4C0Lv0pTbY/P9U5oumKOfUJqBeuAD6f+FJ54xG65cRXbBE5mueGwbRJ4yLgDjT2uspvw 9g== 
Received: from sc-exch04.marvell.com ([199.233.58.184])
 by mx0a-0016f401.pphosted.com with ESMTP id 30bb8q8n5p-1
 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-SHA384 bits=256 verify=NOT);
 Sat, 11 Apr 2020 07:15:19 -0700
Received: from DC5-EXCH02.marvell.com (10.69.176.39) by SC-EXCH04.marvell.com
 (10.93.176.84) with Microsoft SMTP Server (TLS) id 15.0.1497.2;
 Sat, 11 Apr 2020 07:15:17 -0700
Received: from maili.marvell.com (10.69.176.80) by DC5-EXCH02.marvell.com
 (10.69.176.39) with Microsoft SMTP Server id 15.0.1497.2 via Frontend
 Transport; Sat, 11 Apr 2020 07:15:17 -0700
Received: from jerin-lab.marvell.com (jerin-lab.marvell.com [10.28.34.14])
 by maili.marvell.com (Postfix) with ESMTP id BAC6A3F7045;
 Sat, 11 Apr 2020 07:15:14 -0700 (PDT)
From: <jerinj@marvell.com>
To: 
CC: <dev@dpdk.org>, <thomas@monjalon.net>, <david.marchand@redhat.com>,
 <mdr@ashroe.eu>, <mattias.ronnblom@ericsson.com>,
 <kirankumark@marvell.com>, <pbhagavatula@marvell.com>,
 <ndabilpuram@marvell.com>, <xiao.w.wang@intel.com>, <amo@semihalf.com>
Date: Sat, 11 Apr 2020 19:44:12 +0530
Message-ID: <20200411141428.1987768-14-jerinj@marvell.com>
X-Mailer: git-send-email 2.25.1
In-Reply-To: <20200411141428.1987768-1-jerinj@marvell.com>
References: <20200405085613.1336841-1-jerinj@marvell.com>
 <20200411141428.1987768-1-jerinj@marvell.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Content-Type: text/plain
X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:6.0.138, 18.0.676
 definitions=2020-04-11_04:2020-04-09,
 2020-04-11 signatures=0
Subject: [dpdk-dev]  [PATCH v5 13/29] graph: add unit test case
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org
Sender: "dev" <dev-bounces@dpdk.org>

From: Kiran Kumar K <kirankumark@marvell.com>

Adding the unit test to test the functionality of node and graph APIs.
Testing includes registering a node, cloning a node, creating a graph,
perform graph walk, collecting stats and all node and graph debug APIs.

example command to test:
echo "graph_autotest" | sudo ./build/app/test/dpdk-test -c 0x30

Signed-off-by: Kiran Kumar K <kirankumark@marvell.com>
Signed-off-by: Nithin Dabilpuram <ndabilpuram@marvell.com>
---
 app/test/Makefile     |   6 +
 app/test/meson.build  |   6 +-
 app/test/test_graph.c | 819 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 830 insertions(+), 1 deletion(-)
 create mode 100644 app/test/test_graph.c

diff --git a/app/test/Makefile b/app/test/Makefile
index 1f080d162..ce2e08e12 100644
--- a/app/test/Makefile
+++ b/app/test/Makefile
@@ -221,6 +221,10 @@ SRCS-y += test_event_timer_adapter.c
 SRCS-y += test_event_crypto_adapter.c
 endif
 
+ifeq ($(CONFIG_RTE_LIBRTE_GRAPH), y)
+SRCS-y += test_graph.c
+endif
+
 ifeq ($(CONFIG_RTE_LIBRTE_RAWDEV),y)
 SRCS-y += test_rawdev.c
 endif
@@ -240,6 +244,8 @@ endif
 CFLAGS += -DALLOW_EXPERIMENTAL_API
 
 CFLAGS += -O3
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+CFLAGS += -fno-strict-aliasing
 CFLAGS += $(WERROR_FLAGS)
 
 LDLIBS += -lm
diff --git a/app/test/meson.build b/app/test/meson.build
index 351d29cb6..3cf850584 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -51,6 +51,7 @@ test_sources = files('commands.c',
 	'test_fib6_perf.c',
 	'test_func_reentrancy.c',
 	'test_flow_classify.c',
+	'test_graph.c',
 	'test_hash.c',
 	'test_hash_functions.c',
 	'test_hash_multiwriter.c',
@@ -151,7 +152,8 @@ test_deps = ['acl',
 	'rib',
 	'ring',
 	'stack',
-	'timer'
+	'timer',
+	'graph'
 ]
 
 # Each test is marked with flag true/false
@@ -362,6 +364,8 @@ endif
 
 # specify -D_GNU_SOURCE unconditionally
 cflags += '-D_GNU_SOURCE'
+# Strict-aliasing rules are violated by uint8_t[] to context size casts.
+cflags += '-fno-strict-aliasing'
 
 test_dep_objs = []
 if dpdk_conf.has('RTE_LIBRTE_COMPRESSDEV')
diff --git a/app/test/test_graph.c b/app/test/test_graph.c
new file mode 100644
index 000000000..f5c957f82
--- /dev/null
+++ b/app/test/test_graph.c
@@ -0,0 +1,819 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2020 Marvell International Ltd.
+ */
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_errno.h>
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_mbuf.h>
+
+#include "test.h"
+
+static uint16_t test_node_worker_source(struct rte_graph *graph,
+					struct rte_node *node, void **objs,
+					uint16_t nb_objs);
+
+static uint16_t test_node0_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node1_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node2_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+static uint16_t test_node3_worker(struct rte_graph *graph,
+				  struct rte_node *node, void **objs,
+				  uint16_t nb_objs);
+
+#define MBUFF_SIZE 512
+#define MAX_NODES  4
+
+static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
+static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
+static rte_graph_t graph_id;
+static uint64_t obj_stats[MAX_NODES + 1];
+static uint64_t fn_calls[MAX_NODES + 1];
+
+const char *node_patterns[] = {
+	"test_node_source1",	   "test_node00",
+	"test_node00-test_node11", "test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+const char *node_names[] = {
+	"test_node00",
+	"test_node00-test_node11",
+	"test_node00-test_node22",
+	"test_node00-test_node33",
+};
+
+struct test_node_register {
+	char name[RTE_NODE_NAMESIZE];
+	rte_node_process_t process;
+	uint16_t nb_edges;
+	const char *next_nodes[MAX_NODES];
+};
+
+typedef struct {
+	uint32_t idx;
+	struct test_node_register node;
+} test_node_t;
+
+typedef struct {
+	test_node_t test_node[MAX_NODES];
+} test_main_t;
+
+static test_main_t test_main = {
+	.test_node = {
+		{
+			.node = {
+					.name = "test_node00",
+					.process = test_node0_worker,
+					.nb_edges = 2,
+					.next_nodes = {"test_node00-"
+						       "test_node11",
+						       "test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node11",
+					.process = test_node1_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node22"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node22",
+					.process = test_node2_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00-"
+						       "test_node33"},
+				},
+		},
+		{
+			.node = {
+					.name = "test_node33",
+					.process = test_node3_worker,
+					.nb_edges = 1,
+					.next_nodes = {"test_node00"},
+				},
+		},
+	},
+};
+
+static int
+node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	RTE_SET_USED(graph);
+	*(uint32_t *)node->ctx = node->id;
+
+	return 0;
+}
+
+static struct rte_node_register test_node_source = {
+	.name = "test_node_source1",
+	.process = test_node_worker_source,
+	.flags = RTE_NODE_SOURCE_F,
+	.nb_edges = 2,
+	.init = node_init,
+	.next_nodes = {"test_node00", "test_node00-test_node11"},
+};
+RTE_NODE_REGISTER(test_node_source);
+
+static struct rte_node_register test_node0 = {
+	.name = "test_node00",
+	.process = test_node0_worker,
+	.init = node_init,
+};
+RTE_NODE_REGISTER(test_node0);
+
+uint16_t
+test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
+			void **objs, uint16_t nb_objs)
+{
+	uint32_t obj_node0 = rand() % 100, obj_node1;
+	test_main_t *tm = &test_main;
+	struct rte_mbuf *data;
+	void **next_stream;
+	rte_node_t next;
+	uint32_t i;
+
+	RTE_SET_USED(objs);
+	nb_objs = RTE_GRAPH_BURST_SIZE;
+
+	/* Prepare stream for next node 0 */
+	obj_node0 = nb_objs * obj_node0 * 0.01;
+	next = 0;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[0][i];
+		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][i];
+	}
+	rte_node_next_stream_put(graph, node, next, obj_node0);
+
+	/* Prepare stream for next node 1 */
+	obj_node1 = nb_objs - obj_node0;
+	next = 1;
+	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
+	for (i = 0; i < obj_node1; i++) {
+		data = &mbuf[0][obj_node0 + i];
+		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
+		if ((i + 1) == obj_node1)
+			data->udata64 |= (1 << 16);
+		next_stream[i] = &mbuf[0][obj_node0 + i];
+	}
+
+	rte_node_next_stream_put(graph, node, next, obj_node1);
+	obj_stats[0] += nb_objs;
+	fn_calls[0] += 1;
+	return nb_objs;
+}
+
+uint16_t
+test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+
+	if (*(uint32_t *)node->ctx == test_node0.id) {
+		uint32_t obj_node0 = rand() % 100, obj_node1;
+		struct rte_mbuf *data;
+		uint8_t second_pass = 0;
+		uint32_t count = 0;
+		uint32_t i;
+
+		obj_stats[1] += nb_objs;
+		fn_calls[1] += 1;
+
+		for (i = 0; i < nb_objs; i++) {
+			data = (struct rte_mbuf *)objs[i];
+			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
+				printf("Data idx miss match at node 0, expected"
+				       " = %u got = %u\n",
+				       tm->test_node[0].idx,
+				       (uint32_t)(data->udata64 >> 32));
+				goto end;
+			}
+
+			if ((data->udata64 & 0xffff) != (i - count)) {
+				printf("Expected buff count miss match at "
+				       "node 0\n");
+				goto end;
+			}
+
+			if (data->udata64 & (0x1 << 16))
+				count = i + 1;
+			if (data->udata64 & (0x1 << 17))
+				second_pass = 1;
+		}
+
+		if (count != i) {
+			printf("Count mismatch at node 0\n");
+			goto end;
+		}
+
+		obj_node0 = nb_objs * obj_node0 * 0.01;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[1][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[1].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
+				 obj_node0);
+
+		obj_node1 = nb_objs - obj_node0;
+		for (i = 0; i < obj_node1; i++) {
+			data = &mbuf[1][obj_node0 + i];
+			data->udata64 =
+				((uint64_t)tm->test_node[2].idx << 32) | i;
+			if ((i + 1) == obj_node1)
+				data->udata64 |= (1 << 16);
+			if (second_pass)
+				data->udata64 |= (1 << 17);
+		}
+		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
+				 obj_node1);
+
+	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
+		test_node1_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
+		test_node2_worker(graph, node, objs, nb_objs);
+	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
+		test_node3_worker(graph, node, objs, nb_objs);
+	} else {
+		printf("Unexpected node context\n");
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	uint32_t obj_node0 = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t i;
+
+	obj_stats[2] += nb_objs;
+	fn_calls[2] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
+			printf("Data idx miss match at node 1, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[1].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 1\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 1\n");
+		goto end;
+	}
+
+	obj_node0 = nb_objs;
+	for (i = 0; i < obj_node0; i++) {
+		data = &mbuf[2][i];
+		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
+		if ((i + 1) == obj_node0)
+			data->udata64 |= (1 << 16);
+		if (second_pass)
+			data->udata64 |= (1 << 17);
+	}
+	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[3] += nb_objs;
+	fn_calls[3] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
+			printf("Data idx miss match at node 2, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[2].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 2\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 2\n");
+		goto end;
+	}
+
+	if (!second_pass) {
+		obj_node0 = nb_objs;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[3][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[3].idx << 32) | i;
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+uint16_t
+test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
+		  uint16_t nb_objs)
+{
+	test_main_t *tm = &test_main;
+	uint8_t second_pass = 0;
+	struct rte_mbuf *data;
+	uint32_t count = 0;
+	uint32_t obj_node0;
+	uint32_t i;
+
+	obj_stats[4] += nb_objs;
+	fn_calls[4] += 1;
+	for (i = 0; i < nb_objs; i++) {
+		data = (struct rte_mbuf *)objs[i];
+		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
+			printf("Data idx miss match at node 3, expected = %u"
+			       " got = %u\n",
+			       tm->test_node[3].idx,
+			       (uint32_t)(data->udata64 >> 32));
+			goto end;
+		}
+
+		if ((data->udata64 & 0xffff) != (i - count)) {
+			printf("Expected buff count miss match at node 3\n");
+			goto end;
+		}
+
+		if (data->udata64 & (0x1 << 16))
+			count = i + 1;
+		if (data->udata64 & (0x1 << 17))
+			second_pass = 1;
+	}
+
+	if (count != i) {
+		printf("Count mismatch at node 3\n");
+		goto end;
+	}
+
+	if (second_pass) {
+		printf("Unexpected buffers are at node 3\n");
+		goto end;
+	} else {
+		obj_node0 = nb_objs * 2;
+		for (i = 0; i < obj_node0; i++) {
+			data = &mbuf[4][i];
+			data->udata64 =
+				((uint64_t)tm->test_node[0].idx << 32) | i;
+			data->udata64 |= (1 << 17);
+			if ((i + 1) == obj_node0)
+				data->udata64 |= (1 << 16);
+		}
+		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
+				 obj_node0);
+	}
+
+end:
+	return nb_objs;
+}
+
+static int
+test_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	int i;
+
+	/* Verify the name with ID */
+	for (i = 1; i < MAX_NODES; i++) {
+		char *name = rte_node_id_to_name(tm->test_node[i].idx);
+		if (strcmp(name, node_names[i]) != 0) {
+			printf("Test node name verify by ID = %d failed "
+			       "Expected = %s, got %s\n",
+			       i, node_names[i], name);
+			return -1;
+		}
+	}
+
+	/* Verify by name */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t idx = rte_node_from_name(node_names[i]);
+		if (idx != tm->test_node[i].idx) {
+			printf("Test node ID verify by name = %s failed "
+			       "Expected = %d, got %d\n",
+			       node_names[i], tm->test_node[i].idx, idx);
+			return -1;
+		}
+	}
+
+	/* Verify edge count */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+	}
+
+	/* Verify edge names */
+	for (i = 1; i < MAX_NODES; i++) {
+		uint32_t j, count;
+		char **next_edges;
+
+		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
+		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
+			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+		next_edges = malloc(count);
+		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
+			       tm->test_node[i].node.name,
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		for (j = 0; j < count; j++) {
+			if (strcmp(next_edges[j],
+				   tm->test_node[i].node.next_nodes[j]) != 0) {
+				printf("Edge name miss match, expected = %s got = %s\n",
+				       tm->test_node[i].node.next_nodes[j],
+				       next_edges[j]);
+				return -1;
+			}
+		}
+		free(next_edges);
+	}
+
+	return 0;
+}
+
+static int
+test_node_clone(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id, dummy_id;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	tm->test_node[0].idx = node_id;
+
+	/* Clone with same name, should fail */
+	dummy_id = rte_node_clone(node_id, "test_node00");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid id when clone with same name, Expecting fail\n");
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		tm->test_node[i].idx =
+			rte_node_clone(node_id, tm->test_node[i].node.name);
+		if (rte_node_is_invalid(tm->test_node[i].idx)) {
+			printf("Got invalid node id\n");
+			return -1;
+		}
+	}
+
+	/* Clone from cloned node should fail */
+	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
+	if (!rte_node_is_invalid(dummy_id)) {
+		printf("Got valid node id when cloning from cloned node, expected fail\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+test_update_edges(void)
+{
+	test_main_t *tm = &test_main;
+	uint32_t node_id;
+	uint16_t count;
+	int i;
+
+	node_id = rte_node_from_name("test_node00");
+	count = rte_node_edge_update(node_id, 0,
+				     tm->test_node[0].node.next_nodes,
+				     tm->test_node[0].node.nb_edges);
+	if (count != tm->test_node[0].node.nb_edges) {
+		printf("Update edges failed expected: %d got = %d\n",
+		       tm->test_node[0].node.nb_edges, count);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_NODES; i++) {
+		count = rte_node_edge_update(tm->test_node[i].idx, 0,
+					     tm->test_node[i].node.next_nodes,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Update edges failed expected: %d got = %d\n",
+			       tm->test_node[i].node.nb_edges, count);
+			return -1;
+		}
+
+		count = rte_node_edge_shrink(tm->test_node[i].idx,
+					     tm->test_node[i].node.nb_edges);
+		if (count != tm->test_node[i].node.nb_edges) {
+			printf("Shrink edges failed\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+test_create_graph(void)
+{
+	static const char *node_patterns_dummy[] = {
+		"test_node_source1",	   "test_node00",
+		"test_node00-test_node11", "test_node00-test_node22",
+		"test_node00-test_node33", "test_node00-dummy_node",
+	};
+	struct rte_graph_param gconf = {
+		.socket_id = SOCKET_ID_ANY,
+		.nb_node_patterns = 6,
+		.node_patterns = node_patterns_dummy,
+	};
+	uint32_t dummy_node_id;
+	uint32_t node_id;
+
+	node_id = rte_node_from_name("test_node00");
+	dummy_node_id = rte_node_clone(node_id, "dummy_node");
+	if (rte_node_is_invalid(dummy_node_id)) {
+		printf("Got invalid node id\n");
+		return -1;
+	}
+
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id != RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation success with isolated node, expected graph creation fail\n");
+		return -1;
+	}
+
+	gconf.nb_node_patterns = 5;
+	gconf.node_patterns = node_patterns;
+	graph_id = rte_graph_create("worker0", &gconf);
+	if (graph_id == RTE_GRAPH_ID_INVALID) {
+		printf("Graph creation failed with error = %d\n", rte_errno);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+test_graph_walk(void)
+{
+	struct rte_graph *graph = rte_graph_lookup("worker0");
+	int i;
+
+	if (!graph) {
+		printf("Graph lookup failed\n");
+		return -1;
+	}
+
+	for (i = 0; i < 5; i++)
+		rte_graph_walk(graph);
+	return 0;
+}
+
+static int
+test_graph_lookup_functions(void)
+{
+	test_main_t *tm = &test_main;
+	struct rte_node *node;
+	int i;
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	for (i = 0; i < MAX_NODES; i++) {
+		node = rte_graph_node_get_by_name("worker0", node_names[i]);
+		if (!node) {
+			printf("rte_graph_node_get, failed for node = %d\n",
+			       tm->test_node[i].idx);
+			return -1;
+		}
+
+		if (tm->test_node[i].idx != node->id) {
+			printf("Node id didn't match, expected = %d got = %d\n",
+			       tm->test_node[i].idx, node->id);
+			return 0;
+		}
+
+		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
+			printf("Node name didn't match, expected = %s got %s\n",
+			       node_names[i], node->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int
+graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
+			 const struct rte_graph_cluster_node_stats *st)
+{
+	int i;
+
+	RTE_SET_USED(is_first);
+	RTE_SET_USED(is_last);
+	RTE_SET_USED(cookie);
+
+	for (i = 0; i < MAX_NODES + 1; i++) {
+		rte_node_t id = rte_node_from_name(node_patterns[i]);
+		if (id == st->id) {
+			if (obj_stats[i] != st->objs) {
+				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
+				       node_patterns[i], obj_stats[i],
+				       st->objs);
+				return -1;
+			}
+
+			if (fn_calls[i] != st->calls) {
+				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
+				       node_patterns[i], fn_calls[i],
+				       st->calls);
+				return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+test_print_stats(void)
+{
+	struct rte_graph_cluster_stats_param s_param;
+	struct rte_graph_cluster_stats *stats;
+	const char *pattern = "worker0";
+
+	if (!rte_graph_has_stats_feature())
+		return 0;
+
+	/* Prepare stats object */
+	memset(&s_param, 0, sizeof(s_param));
+	s_param.f = stdout;
+	s_param.socket_id = SOCKET_ID_ANY;
+	s_param.graph_patterns = &pattern;
+	s_param.nb_graph_patterns = 1;
+	s_param.fn = graph_cluster_stats_cb_t;
+
+	stats = rte_graph_cluster_stats_create(&s_param);
+	if (stats == NULL) {
+		printf("Unable to get stats\n");
+		return -1;
+	}
+	/* Clear screen and move to top left */
+	rte_graph_cluster_stats_get(stats, 0);
+	rte_graph_cluster_stats_destroy(stats);
+
+	return 0;
+}
+
+static int
+graph_setup(void)
+{
+	int i, j;
+
+	for (i = 0; i <= MAX_NODES; i++) {
+		for (j = 0; j < MBUFF_SIZE; j++)
+			mbuf_p[i][j] = &mbuf[i][j];
+	}
+	if (test_node_clone()) {
+		printf("test_node_clone: fail\n");
+		return -1;
+	}
+	printf("test_node_clone: pass\n");
+
+	return 0;
+}
+
+static void
+graph_teardown(void)
+{
+	int id;
+
+	id = rte_graph_destroy(rte_graph_from_name("worker0"));
+	if (id)
+		printf("Graph Destroy failed\n");
+}
+
+static struct unit_test_suite graph_testsuite = {
+	.suite_name = "Graph library test suite",
+	.setup = graph_setup,
+	.teardown = graph_teardown,
+	.unit_test_cases = {
+		TEST_CASE(test_update_edges),
+		TEST_CASE(test_lookup_functions),
+		TEST_CASE(test_create_graph),
+		TEST_CASE(test_graph_lookup_functions),
+		TEST_CASE(test_graph_walk),
+		TEST_CASE(test_print_stats),
+		TEST_CASES_END(), /**< NULL terminate unit test array */
+	},
+};
+
+static int
+graph_autotest_fn(void)
+{
+	return unit_test_suite_runner(&graph_testsuite);
+}
+
+REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
+
+static int
+test_node_list_dump(void)
+{
+	rte_node_list_dump(stdout);
+
+	return TEST_SUCCESS;
+}
+REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
+
-- 
2.25.1