xref: /dpdk/app/test/test_graph.c (revision cc9035ff)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2020 Marvell International Ltd.
3  */
4 #include <assert.h>
5 #include <inttypes.h>
6 #include <signal.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <rte_errno.h>
12 #include <rte_graph.h>
13 #include <rte_graph_worker.h>
14 #include <rte_mbuf.h>
15 #include <rte_random.h>
16 
17 #include "test.h"
18 
19 static uint16_t test_node_worker_source(struct rte_graph *graph,
20 					struct rte_node *node, void **objs,
21 					uint16_t nb_objs);
22 
23 static uint16_t test_node0_worker(struct rte_graph *graph,
24 				  struct rte_node *node, void **objs,
25 				  uint16_t nb_objs);
26 
27 static uint16_t test_node1_worker(struct rte_graph *graph,
28 				  struct rte_node *node, void **objs,
29 				  uint16_t nb_objs);
30 
31 static uint16_t test_node2_worker(struct rte_graph *graph,
32 				  struct rte_node *node, void **objs,
33 				  uint16_t nb_objs);
34 
35 static uint16_t test_node3_worker(struct rte_graph *graph,
36 				  struct rte_node *node, void **objs,
37 				  uint16_t nb_objs);
38 
39 #define MBUFF_SIZE 512
40 #define MAX_NODES  4
41 
42 static struct rte_mbuf mbuf[MAX_NODES + 1][MBUFF_SIZE];
43 static void *mbuf_p[MAX_NODES + 1][MBUFF_SIZE];
44 static rte_graph_t graph_id;
45 static uint64_t obj_stats[MAX_NODES + 1];
46 static uint64_t fn_calls[MAX_NODES + 1];
47 
48 const char *node_patterns[] = {
49 	"test_node_source1",	   "test_node00",
50 	"test_node00-test_node11", "test_node00-test_node22",
51 	"test_node00-test_node33",
52 };
53 
54 const char *node_names[] = {
55 	"test_node00",
56 	"test_node00-test_node11",
57 	"test_node00-test_node22",
58 	"test_node00-test_node33",
59 };
60 
61 struct test_node_register {
62 	char name[RTE_NODE_NAMESIZE];
63 	rte_node_process_t process;
64 	uint16_t nb_edges;
65 	const char *next_nodes[MAX_NODES];
66 };
67 
68 typedef struct {
69 	uint32_t idx;
70 	struct test_node_register node;
71 } test_node_t;
72 
73 typedef struct {
74 	test_node_t test_node[MAX_NODES];
75 } test_main_t;
76 
77 static test_main_t test_main = {
78 	.test_node = {
79 		{
80 			.node = {
81 					.name = "test_node00",
82 					.process = test_node0_worker,
83 					.nb_edges = 2,
84 					.next_nodes = {"test_node00-"
85 						       "test_node11",
86 						       "test_node00-"
87 						       "test_node22"},
88 				},
89 		},
90 		{
91 			.node = {
92 					.name = "test_node11",
93 					.process = test_node1_worker,
94 					.nb_edges = 1,
95 					.next_nodes = {"test_node00-"
96 						       "test_node22"},
97 				},
98 		},
99 		{
100 			.node = {
101 					.name = "test_node22",
102 					.process = test_node2_worker,
103 					.nb_edges = 1,
104 					.next_nodes = {"test_node00-"
105 						       "test_node33"},
106 				},
107 		},
108 		{
109 			.node = {
110 					.name = "test_node33",
111 					.process = test_node3_worker,
112 					.nb_edges = 1,
113 					.next_nodes = {"test_node00"},
114 				},
115 		},
116 	},
117 };
118 
119 static int
120 node_init(const struct rte_graph *graph, struct rte_node *node)
121 {
122 	RTE_SET_USED(graph);
123 	*(uint32_t *)node->ctx = node->id;
124 
125 	return 0;
126 }
127 
128 static struct rte_node_register test_node_source = {
129 	.name = "test_node_source1",
130 	.process = test_node_worker_source,
131 	.flags = RTE_NODE_SOURCE_F,
132 	.nb_edges = 2,
133 	.init = node_init,
134 	.next_nodes = {"test_node00", "test_node00-test_node11"},
135 };
136 RTE_NODE_REGISTER(test_node_source);
137 
138 static struct rte_node_register test_node0 = {
139 	.name = "test_node00",
140 	.process = test_node0_worker,
141 	.init = node_init,
142 };
143 RTE_NODE_REGISTER(test_node0);
144 
145 uint16_t
146 test_node_worker_source(struct rte_graph *graph, struct rte_node *node,
147 			void **objs, uint16_t nb_objs)
148 {
149 	uint32_t obj_node0 = rte_rand() % 100, obj_node1;
150 	test_main_t *tm = &test_main;
151 	struct rte_mbuf *data;
152 	void **next_stream;
153 	rte_node_t next;
154 	uint32_t i;
155 
156 	RTE_SET_USED(objs);
157 	nb_objs = RTE_GRAPH_BURST_SIZE;
158 
159 	/* Prepare stream for next node 0 */
160 	obj_node0 = nb_objs * obj_node0 * 0.01;
161 	next = 0;
162 	next_stream = rte_node_next_stream_get(graph, node, next, obj_node0);
163 	for (i = 0; i < obj_node0; i++) {
164 		data = &mbuf[0][i];
165 		data->udata64 = ((uint64_t)tm->test_node[0].idx << 32) | i;
166 		if ((i + 1) == obj_node0)
167 			data->udata64 |= (1 << 16);
168 		next_stream[i] = &mbuf[0][i];
169 	}
170 	rte_node_next_stream_put(graph, node, next, obj_node0);
171 
172 	/* Prepare stream for next node 1 */
173 	obj_node1 = nb_objs - obj_node0;
174 	next = 1;
175 	next_stream = rte_node_next_stream_get(graph, node, next, obj_node1);
176 	for (i = 0; i < obj_node1; i++) {
177 		data = &mbuf[0][obj_node0 + i];
178 		data->udata64 = ((uint64_t)tm->test_node[1].idx << 32) | i;
179 		if ((i + 1) == obj_node1)
180 			data->udata64 |= (1 << 16);
181 		next_stream[i] = &mbuf[0][obj_node0 + i];
182 	}
183 
184 	rte_node_next_stream_put(graph, node, next, obj_node1);
185 	obj_stats[0] += nb_objs;
186 	fn_calls[0] += 1;
187 	return nb_objs;
188 }
189 
190 uint16_t
191 test_node0_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
192 		  uint16_t nb_objs)
193 {
194 	test_main_t *tm = &test_main;
195 
196 	if (*(uint32_t *)node->ctx == test_node0.id) {
197 		uint32_t obj_node0 = rte_rand() % 100, obj_node1;
198 		struct rte_mbuf *data;
199 		uint8_t second_pass = 0;
200 		uint32_t count = 0;
201 		uint32_t i;
202 
203 		obj_stats[1] += nb_objs;
204 		fn_calls[1] += 1;
205 
206 		for (i = 0; i < nb_objs; i++) {
207 			data = (struct rte_mbuf *)objs[i];
208 			if ((data->udata64 >> 32) != tm->test_node[0].idx) {
209 				printf("Data idx miss match at node 0, expected"
210 				       " = %u got = %u\n",
211 				       tm->test_node[0].idx,
212 				       (uint32_t)(data->udata64 >> 32));
213 				goto end;
214 			}
215 
216 			if ((data->udata64 & 0xffff) != (i - count)) {
217 				printf("Expected buff count miss match at "
218 				       "node 0\n");
219 				goto end;
220 			}
221 
222 			if (data->udata64 & (0x1 << 16))
223 				count = i + 1;
224 			if (data->udata64 & (0x1 << 17))
225 				second_pass = 1;
226 		}
227 
228 		if (count != i) {
229 			printf("Count mismatch at node 0\n");
230 			goto end;
231 		}
232 
233 		obj_node0 = nb_objs * obj_node0 * 0.01;
234 		for (i = 0; i < obj_node0; i++) {
235 			data = &mbuf[1][i];
236 			data->udata64 =
237 				((uint64_t)tm->test_node[1].idx << 32) | i;
238 			if ((i + 1) == obj_node0)
239 				data->udata64 |= (1 << 16);
240 			if (second_pass)
241 				data->udata64 |= (1 << 17);
242 		}
243 		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[1][0],
244 				 obj_node0);
245 
246 		obj_node1 = nb_objs - obj_node0;
247 		for (i = 0; i < obj_node1; i++) {
248 			data = &mbuf[1][obj_node0 + i];
249 			data->udata64 =
250 				((uint64_t)tm->test_node[2].idx << 32) | i;
251 			if ((i + 1) == obj_node1)
252 				data->udata64 |= (1 << 16);
253 			if (second_pass)
254 				data->udata64 |= (1 << 17);
255 		}
256 		rte_node_enqueue(graph, node, 1, (void **)&mbuf_p[1][obj_node0],
257 				 obj_node1);
258 
259 	} else if (*(uint32_t *)node->ctx == tm->test_node[1].idx) {
260 		test_node1_worker(graph, node, objs, nb_objs);
261 	} else if (*(uint32_t *)node->ctx == tm->test_node[2].idx) {
262 		test_node2_worker(graph, node, objs, nb_objs);
263 	} else if (*(uint32_t *)node->ctx == tm->test_node[3].idx) {
264 		test_node3_worker(graph, node, objs, nb_objs);
265 	} else {
266 		printf("Unexpected node context\n");
267 	}
268 
269 end:
270 	return nb_objs;
271 }
272 
273 uint16_t
274 test_node1_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
275 		  uint16_t nb_objs)
276 {
277 	test_main_t *tm = &test_main;
278 	uint8_t second_pass = 0;
279 	uint32_t obj_node0 = 0;
280 	struct rte_mbuf *data;
281 	uint32_t count = 0;
282 	uint32_t i;
283 
284 	obj_stats[2] += nb_objs;
285 	fn_calls[2] += 1;
286 	for (i = 0; i < nb_objs; i++) {
287 		data = (struct rte_mbuf *)objs[i];
288 		if ((data->udata64 >> 32) != tm->test_node[1].idx) {
289 			printf("Data idx miss match at node 1, expected = %u"
290 			       " got = %u\n",
291 			       tm->test_node[1].idx,
292 			       (uint32_t)(data->udata64 >> 32));
293 			goto end;
294 		}
295 
296 		if ((data->udata64 & 0xffff) != (i - count)) {
297 			printf("Expected buff count miss match at node 1\n");
298 			goto end;
299 		}
300 
301 		if (data->udata64 & (0x1 << 16))
302 			count = i + 1;
303 		if (data->udata64 & (0x1 << 17))
304 			second_pass = 1;
305 	}
306 
307 	if (count != i) {
308 		printf("Count mismatch at node 1\n");
309 		goto end;
310 	}
311 
312 	obj_node0 = nb_objs;
313 	for (i = 0; i < obj_node0; i++) {
314 		data = &mbuf[2][i];
315 		data->udata64 = ((uint64_t)tm->test_node[2].idx << 32) | i;
316 		if ((i + 1) == obj_node0)
317 			data->udata64 |= (1 << 16);
318 		if (second_pass)
319 			data->udata64 |= (1 << 17);
320 	}
321 	rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[2][0], obj_node0);
322 
323 end:
324 	return nb_objs;
325 }
326 
327 uint16_t
328 test_node2_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
329 		  uint16_t nb_objs)
330 {
331 	test_main_t *tm = &test_main;
332 	uint8_t second_pass = 0;
333 	struct rte_mbuf *data;
334 	uint32_t count = 0;
335 	uint32_t obj_node0;
336 	uint32_t i;
337 
338 	obj_stats[3] += nb_objs;
339 	fn_calls[3] += 1;
340 	for (i = 0; i < nb_objs; i++) {
341 		data = (struct rte_mbuf *)objs[i];
342 		if ((data->udata64 >> 32) != tm->test_node[2].idx) {
343 			printf("Data idx miss match at node 2, expected = %u"
344 			       " got = %u\n",
345 			       tm->test_node[2].idx,
346 			       (uint32_t)(data->udata64 >> 32));
347 			goto end;
348 		}
349 
350 		if ((data->udata64 & 0xffff) != (i - count)) {
351 			printf("Expected buff count miss match at node 2\n");
352 			goto end;
353 		}
354 
355 		if (data->udata64 & (0x1 << 16))
356 			count = i + 1;
357 		if (data->udata64 & (0x1 << 17))
358 			second_pass = 1;
359 	}
360 
361 	if (count != i) {
362 		printf("Count mismatch at node 2\n");
363 		goto end;
364 	}
365 
366 	if (!second_pass) {
367 		obj_node0 = nb_objs;
368 		for (i = 0; i < obj_node0; i++) {
369 			data = &mbuf[3][i];
370 			data->udata64 =
371 				((uint64_t)tm->test_node[3].idx << 32) | i;
372 			if ((i + 1) == obj_node0)
373 				data->udata64 |= (1 << 16);
374 		}
375 		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[3][0],
376 				 obj_node0);
377 	}
378 
379 end:
380 	return nb_objs;
381 }
382 
383 uint16_t
384 test_node3_worker(struct rte_graph *graph, struct rte_node *node, void **objs,
385 		  uint16_t nb_objs)
386 {
387 	test_main_t *tm = &test_main;
388 	uint8_t second_pass = 0;
389 	struct rte_mbuf *data;
390 	uint32_t count = 0;
391 	uint32_t obj_node0;
392 	uint32_t i;
393 
394 	obj_stats[4] += nb_objs;
395 	fn_calls[4] += 1;
396 	for (i = 0; i < nb_objs; i++) {
397 		data = (struct rte_mbuf *)objs[i];
398 		if ((data->udata64 >> 32) != tm->test_node[3].idx) {
399 			printf("Data idx miss match at node 3, expected = %u"
400 			       " got = %u\n",
401 			       tm->test_node[3].idx,
402 			       (uint32_t)(data->udata64 >> 32));
403 			goto end;
404 		}
405 
406 		if ((data->udata64 & 0xffff) != (i - count)) {
407 			printf("Expected buff count miss match at node 3\n");
408 			goto end;
409 		}
410 
411 		if (data->udata64 & (0x1 << 16))
412 			count = i + 1;
413 		if (data->udata64 & (0x1 << 17))
414 			second_pass = 1;
415 	}
416 
417 	if (count != i) {
418 		printf("Count mismatch at node 3\n");
419 		goto end;
420 	}
421 
422 	if (second_pass) {
423 		printf("Unexpected buffers are at node 3\n");
424 		goto end;
425 	} else {
426 		obj_node0 = nb_objs * 2;
427 		for (i = 0; i < obj_node0; i++) {
428 			data = &mbuf[4][i];
429 			data->udata64 =
430 				((uint64_t)tm->test_node[0].idx << 32) | i;
431 			data->udata64 |= (1 << 17);
432 			if ((i + 1) == obj_node0)
433 				data->udata64 |= (1 << 16);
434 		}
435 		rte_node_enqueue(graph, node, 0, (void **)&mbuf_p[4][0],
436 				 obj_node0);
437 	}
438 
439 end:
440 	return nb_objs;
441 }
442 
443 static int
444 test_lookup_functions(void)
445 {
446 	test_main_t *tm = &test_main;
447 	int i;
448 
449 	/* Verify the name with ID */
450 	for (i = 1; i < MAX_NODES; i++) {
451 		char *name = rte_node_id_to_name(tm->test_node[i].idx);
452 		if (strcmp(name, node_names[i]) != 0) {
453 			printf("Test node name verify by ID = %d failed "
454 			       "Expected = %s, got %s\n",
455 			       i, node_names[i], name);
456 			return -1;
457 		}
458 	}
459 
460 	/* Verify by name */
461 	for (i = 1; i < MAX_NODES; i++) {
462 		uint32_t idx = rte_node_from_name(node_names[i]);
463 		if (idx != tm->test_node[i].idx) {
464 			printf("Test node ID verify by name = %s failed "
465 			       "Expected = %d, got %d\n",
466 			       node_names[i], tm->test_node[i].idx, idx);
467 			return -1;
468 		}
469 	}
470 
471 	/* Verify edge count */
472 	for (i = 1; i < MAX_NODES; i++) {
473 		uint32_t count = rte_node_edge_count(tm->test_node[i].idx);
474 		if (count != tm->test_node[i].node.nb_edges) {
475 			printf("Test number of edges for node = %s failed Expected = %d, got = %d\n",
476 			       tm->test_node[i].node.name,
477 			       tm->test_node[i].node.nb_edges, count);
478 			return -1;
479 		}
480 	}
481 
482 	/* Verify edge names */
483 	for (i = 1; i < MAX_NODES; i++) {
484 		uint32_t j, count;
485 		char **next_edges;
486 
487 		count = rte_node_edge_get(tm->test_node[i].idx, NULL);
488 		if (count != tm->test_node[i].node.nb_edges * sizeof(char *)) {
489 			printf("Test number of edge count for node = %s failed Expected = %d, got = %d\n",
490 			       tm->test_node[i].node.name,
491 			       tm->test_node[i].node.nb_edges, count);
492 			return -1;
493 		}
494 		next_edges = malloc(count);
495 		count = rte_node_edge_get(tm->test_node[i].idx, next_edges);
496 		if (count != tm->test_node[i].node.nb_edges) {
497 			printf("Test number of edges for node = %s failed Expected = %d, got %d\n",
498 			       tm->test_node[i].node.name,
499 			       tm->test_node[i].node.nb_edges, count);
500 			free(next_edges);
501 			return -1;
502 		}
503 
504 		for (j = 0; j < count; j++) {
505 			if (strcmp(next_edges[j],
506 				   tm->test_node[i].node.next_nodes[j]) != 0) {
507 				printf("Edge name miss match, expected = %s got = %s\n",
508 				       tm->test_node[i].node.next_nodes[j],
509 				       next_edges[j]);
510 				free(next_edges);
511 				return -1;
512 			}
513 		}
514 		free(next_edges);
515 	}
516 
517 	return 0;
518 }
519 
520 static int
521 test_node_clone(void)
522 {
523 	test_main_t *tm = &test_main;
524 	uint32_t node_id, dummy_id;
525 	int i;
526 
527 	node_id = rte_node_from_name("test_node00");
528 	tm->test_node[0].idx = node_id;
529 
530 	/* Clone with same name, should fail */
531 	dummy_id = rte_node_clone(node_id, "test_node00");
532 	if (!rte_node_is_invalid(dummy_id)) {
533 		printf("Got valid id when clone with same name, Expecting fail\n");
534 		return -1;
535 	}
536 
537 	for (i = 1; i < MAX_NODES; i++) {
538 		tm->test_node[i].idx =
539 			rte_node_clone(node_id, tm->test_node[i].node.name);
540 		if (rte_node_is_invalid(tm->test_node[i].idx)) {
541 			printf("Got invalid node id\n");
542 			return -1;
543 		}
544 	}
545 
546 	/* Clone from cloned node should fail */
547 	dummy_id = rte_node_clone(tm->test_node[1].idx, "dummy_node");
548 	if (!rte_node_is_invalid(dummy_id)) {
549 		printf("Got valid node id when cloning from cloned node, expected fail\n");
550 		return -1;
551 	}
552 
553 	return 0;
554 }
555 
556 static int
557 test_update_edges(void)
558 {
559 	test_main_t *tm = &test_main;
560 	uint32_t node_id;
561 	uint16_t count;
562 	int i;
563 
564 	node_id = rte_node_from_name("test_node00");
565 	count = rte_node_edge_update(node_id, 0,
566 				     tm->test_node[0].node.next_nodes,
567 				     tm->test_node[0].node.nb_edges);
568 	if (count != tm->test_node[0].node.nb_edges) {
569 		printf("Update edges failed expected: %d got = %d\n",
570 		       tm->test_node[0].node.nb_edges, count);
571 		return -1;
572 	}
573 
574 	for (i = 1; i < MAX_NODES; i++) {
575 		count = rte_node_edge_update(tm->test_node[i].idx, 0,
576 					     tm->test_node[i].node.next_nodes,
577 					     tm->test_node[i].node.nb_edges);
578 		if (count != tm->test_node[i].node.nb_edges) {
579 			printf("Update edges failed expected: %d got = %d\n",
580 			       tm->test_node[i].node.nb_edges, count);
581 			return -1;
582 		}
583 
584 		count = rte_node_edge_shrink(tm->test_node[i].idx,
585 					     tm->test_node[i].node.nb_edges);
586 		if (count != tm->test_node[i].node.nb_edges) {
587 			printf("Shrink edges failed\n");
588 			return -1;
589 		}
590 	}
591 
592 	return 0;
593 }
594 
595 static int
596 test_create_graph(void)
597 {
598 	static const char *node_patterns_dummy[] = {
599 		"test_node_source1",	   "test_node00",
600 		"test_node00-test_node11", "test_node00-test_node22",
601 		"test_node00-test_node33", "test_node00-dummy_node",
602 	};
603 	struct rte_graph_param gconf = {
604 		.socket_id = SOCKET_ID_ANY,
605 		.nb_node_patterns = 6,
606 		.node_patterns = node_patterns_dummy,
607 	};
608 	uint32_t dummy_node_id;
609 	uint32_t node_id;
610 
611 	node_id = rte_node_from_name("test_node00");
612 	dummy_node_id = rte_node_clone(node_id, "dummy_node");
613 	if (rte_node_is_invalid(dummy_node_id)) {
614 		printf("Got invalid node id\n");
615 		return -1;
616 	}
617 
618 	graph_id = rte_graph_create("worker0", &gconf);
619 	if (graph_id != RTE_GRAPH_ID_INVALID) {
620 		printf("Graph creation success with isolated node, expected graph creation fail\n");
621 		return -1;
622 	}
623 
624 	gconf.nb_node_patterns = 5;
625 	gconf.node_patterns = node_patterns;
626 	graph_id = rte_graph_create("worker0", &gconf);
627 	if (graph_id == RTE_GRAPH_ID_INVALID) {
628 		printf("Graph creation failed with error = %d\n", rte_errno);
629 		return -1;
630 	}
631 	return 0;
632 }
633 
634 static int
635 test_graph_walk(void)
636 {
637 	struct rte_graph *graph = rte_graph_lookup("worker0");
638 	int i;
639 
640 	if (!graph) {
641 		printf("Graph lookup failed\n");
642 		return -1;
643 	}
644 
645 	for (i = 0; i < 5; i++)
646 		rte_graph_walk(graph);
647 	return 0;
648 }
649 
650 static int
651 test_graph_lookup_functions(void)
652 {
653 	test_main_t *tm = &test_main;
654 	struct rte_node *node;
655 	int i;
656 
657 	for (i = 0; i < MAX_NODES; i++) {
658 		node = rte_graph_node_get(graph_id, tm->test_node[i].idx);
659 		if (!node) {
660 			printf("rte_graph_node_get, failed for node = %d\n",
661 			       tm->test_node[i].idx);
662 			return -1;
663 		}
664 
665 		if (tm->test_node[i].idx != node->id) {
666 			printf("Node id didn't match, expected = %d got = %d\n",
667 			       tm->test_node[i].idx, node->id);
668 			return 0;
669 		}
670 
671 		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
672 			printf("Node name didn't match, expected = %s got %s\n",
673 			       node_names[i], node->name);
674 			return -1;
675 		}
676 	}
677 
678 	for (i = 0; i < MAX_NODES; i++) {
679 		node = rte_graph_node_get_by_name("worker0", node_names[i]);
680 		if (!node) {
681 			printf("rte_graph_node_get, failed for node = %d\n",
682 			       tm->test_node[i].idx);
683 			return -1;
684 		}
685 
686 		if (tm->test_node[i].idx != node->id) {
687 			printf("Node id didn't match, expected = %d got = %d\n",
688 			       tm->test_node[i].idx, node->id);
689 			return 0;
690 		}
691 
692 		if (strncmp(node->name, node_names[i], RTE_NODE_NAMESIZE)) {
693 			printf("Node name didn't match, expected = %s got %s\n",
694 			       node_names[i], node->name);
695 			return -1;
696 		}
697 	}
698 
699 	return 0;
700 }
701 
702 static int
703 graph_cluster_stats_cb_t(bool is_first, bool is_last, void *cookie,
704 			 const struct rte_graph_cluster_node_stats *st)
705 {
706 	int i;
707 
708 	RTE_SET_USED(is_first);
709 	RTE_SET_USED(is_last);
710 	RTE_SET_USED(cookie);
711 
712 	for (i = 0; i < MAX_NODES + 1; i++) {
713 		rte_node_t id = rte_node_from_name(node_patterns[i]);
714 		if (id == st->id) {
715 			if (obj_stats[i] != st->objs) {
716 				printf("Obj count miss match for node = %s expected = %"PRId64", got=%"PRId64"\n",
717 				       node_patterns[i], obj_stats[i],
718 				       st->objs);
719 				return -1;
720 			}
721 
722 			if (fn_calls[i] != st->calls) {
723 				printf("Func call miss match for node = %s expected = %"PRId64", got = %"PRId64"\n",
724 				       node_patterns[i], fn_calls[i],
725 				       st->calls);
726 				return -1;
727 			}
728 		}
729 	}
730 	return 0;
731 }
732 
733 static int
734 test_print_stats(void)
735 {
736 	struct rte_graph_cluster_stats_param s_param;
737 	struct rte_graph_cluster_stats *stats;
738 	const char *pattern = "worker0";
739 
740 	if (!rte_graph_has_stats_feature())
741 		return 0;
742 
743 	/* Prepare stats object */
744 	memset(&s_param, 0, sizeof(s_param));
745 	s_param.f = stdout;
746 	s_param.socket_id = SOCKET_ID_ANY;
747 	s_param.graph_patterns = &pattern;
748 	s_param.nb_graph_patterns = 1;
749 	s_param.fn = graph_cluster_stats_cb_t;
750 
751 	stats = rte_graph_cluster_stats_create(&s_param);
752 	if (stats == NULL) {
753 		printf("Unable to get stats\n");
754 		return -1;
755 	}
756 	/* Clear screen and move to top left */
757 	rte_graph_cluster_stats_get(stats, 0);
758 	rte_graph_cluster_stats_destroy(stats);
759 
760 	return 0;
761 }
762 
763 static int
764 graph_setup(void)
765 {
766 	int i, j;
767 
768 	for (i = 0; i <= MAX_NODES; i++) {
769 		for (j = 0; j < MBUFF_SIZE; j++)
770 			mbuf_p[i][j] = &mbuf[i][j];
771 	}
772 	if (test_node_clone()) {
773 		printf("test_node_clone: fail\n");
774 		return -1;
775 	}
776 	printf("test_node_clone: pass\n");
777 
778 	return 0;
779 }
780 
781 static void
782 graph_teardown(void)
783 {
784 	int id;
785 
786 	id = rte_graph_destroy(rte_graph_from_name("worker0"));
787 	if (id)
788 		printf("Graph Destroy failed\n");
789 }
790 
791 static struct unit_test_suite graph_testsuite = {
792 	.suite_name = "Graph library test suite",
793 	.setup = graph_setup,
794 	.teardown = graph_teardown,
795 	.unit_test_cases = {
796 		TEST_CASE(test_update_edges),
797 		TEST_CASE(test_lookup_functions),
798 		TEST_CASE(test_create_graph),
799 		TEST_CASE(test_graph_lookup_functions),
800 		TEST_CASE(test_graph_walk),
801 		TEST_CASE(test_print_stats),
802 		TEST_CASES_END(), /**< NULL terminate unit test array */
803 	},
804 };
805 
806 static int
807 graph_autotest_fn(void)
808 {
809 	return unit_test_suite_runner(&graph_testsuite);
810 }
811 
812 REGISTER_TEST_COMMAND(graph_autotest, graph_autotest_fn);
813 
814 static int
815 test_node_list_dump(void)
816 {
817 	rte_node_list_dump(stdout);
818 
819 	return TEST_SUCCESS;
820 }
821 REGISTER_TEST_COMMAND(node_list_dump, test_node_list_dump);
822