/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2024 HiSilicon Limited
 */

#include <stdio.h>
#include <string.h>

#include <rte_argparse.h>

#include "test.h"

static int default_argc;
static char *default_argv[1];

#define MAX_STRDUP_STORE_NUM	512
static char *strdup_store_array[MAX_STRDUP_STORE_NUM];
static uint32_t strdup_store_index;

/*
 * Define strdup wrapper.
 * 1. Mainly to fix compile error "warning: assignment discards 'const'
 *    qualifier from pointer target type [-Wdiscarded-qualifiers]" for
 *    following code:
 *      argv[x] = "100";
 * 2. The strdup result will store in the strdup_store_array, and then
 *    freed in the teardown function, prevent ASAN errors from being
 *    triggered.
 */
static char *
test_strdup(const char *str)
{
	char *s = strdup(str);
	if (s == NULL) {
		printf("strdup failed! exiting...\n");
		exit(-ENOMEM);
	}
	if (strdup_store_index >= MAX_STRDUP_STORE_NUM) {
		printf("too much strdup calls! exiting...\n");
		exit(-ERANGE);
	}
	strdup_store_array[strdup_store_index++] = s;
	return s;
}

static int
test_argparse_setup(void)
{
	strdup_store_index = 0;
	default_argc = 1;
	default_argv[0] = test_strdup("test_argparse");
	return 0;
}

static void
test_argparse_teardown(void)
{
	uint32_t i;
	printf("total used strdup_store_index = %u\n", strdup_store_index);
	for (i = 0; i < strdup_store_index; i++)
		free(strdup_store_array[i]);
	strdup_store_index = 0;
}

static int
test_argparse_callback(uint32_t index, const char *value, void *opaque)
{
	RTE_SET_USED(index);
	RTE_SET_USED(value);
	RTE_SET_USED(opaque);
	return 0;
}

/* valid templater, must contain at least two args. */
#define argparse_templater() { \
	.prog_name = "test_argparse", \
	.usage = "-a xx -b yy", \
	.descriptor = NULL, \
	.epilog = NULL, \
	.exit_on_error = false, \
	.callback = test_argparse_callback, \
	.args = { \
		{ "--abc", "-a", "abc argument", (void *)1, (void *)1, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
		{ "--xyz", "-x", "xyz argument", (void *)1, (void *)2, RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT }, \
		ARGPARSE_ARG_END(), \
	}, \
}

static void
test_argparse_copy(struct rte_argparse *dst, struct rte_argparse *src)
{
	uint32_t i;
	memcpy(dst, src, sizeof(*src));
	for (i = 0; /* NULL */; i++) {
		memcpy(&dst->args[i], &src->args[i], sizeof(src->args[i]));
		if (src->args[i].name_long == NULL)
			break;
	}
}

static struct rte_argparse *
test_argparse_init_obj(void)
{
	static struct rte_argparse backup = argparse_templater();
	static struct rte_argparse obj = argparse_templater();
	/* Because obj may be overwritten, do a deep copy. */
	test_argparse_copy(&obj, &backup);
	return &obj;
}

static int
test_argparse_invalid_basic_param(void)
{
	struct rte_argparse *obj;
	int ret;

	obj = test_argparse_init_obj();
	obj->prog_name = NULL;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->usage = NULL;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return TEST_SUCCESS;
}

static int
test_argparse_invalid_arg_name(void)
{
	struct rte_argparse *obj;
	int ret;

	obj = test_argparse_init_obj();
	obj->args[0].name_long = "-ab";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->args[0].name_long = "-abc";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->args[0].name_long = "---c";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->args[0].name_long = "abc";
	obj->args[0].name_short = "-a";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->args[0].name_short = "a";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->args[0].name_short = "abc";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	obj->args[0].name_short = "ab";
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_invalid_arg_help(void)
{
	struct rte_argparse *obj;
	int ret;

	obj = test_argparse_init_obj();
	obj->args[0].help = NULL;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_invalid_has_val(void)
{
	uint64_t set_mask[] = { 0,
				RTE_ARGPARSE_ARG_NO_VALUE,
				RTE_ARGPARSE_ARG_OPTIONAL_VALUE
			      };
	struct rte_argparse *obj;
	uint32_t index;
	int ret;

	/* test optional arg don't config has-value. */
	obj = test_argparse_init_obj();
	obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test positional arg don't config required-value. */
	for (index = 0; index < RTE_DIM(set_mask); index++) {
		obj = test_argparse_init_obj();
		obj->args[0].name_long = "abc";
		obj->args[0].name_short = NULL;
		obj->args[0].flags &= ~RTE_ARGPARSE_HAS_VAL_BITMASK;
		obj->args[0].flags |= set_mask[index];
		ret = rte_argparse_parse(obj, default_argc, default_argv);
		TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
	}

	return 0;
}

static int
test_argparse_invalid_arg_saver(void)
{
	struct rte_argparse *obj;
	int ret;

	/* test saver == NULL with val-type != 0. */
	obj = test_argparse_init_obj();
	obj->args[0].val_saver = NULL;
	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test saver == NULL with callback is NULL. */
	obj = test_argparse_init_obj();
	obj->args[0].val_saver = NULL;
	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
	obj->callback = NULL;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test saver != NULL with val-type is zero! */
	obj = test_argparse_init_obj();
	obj->args[0].val_saver = (void *)1;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test saver != NULL with val-type is max. */
	obj = test_argparse_init_obj();
	obj->args[0].val_saver = (void *)1;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_MAX;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test saver != NULL with required value, but val-set is not NULL. */
	obj = test_argparse_init_obj();
	obj->args[0].val_saver = (void *)1;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_invalid_arg_flags(void)
{
	struct rte_argparse *obj;
	int ret;

	/* test set unused bits. */
	obj = test_argparse_init_obj();
	obj->args[0].flags |= ~(RTE_ARGPARSE_HAS_VAL_BITMASK |
				RTE_ARGPARSE_VAL_TYPE_BITMASK |
				RTE_ARGPARSE_ARG_SUPPORT_MULTI);
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test positional arg should not config multiple.  */
	obj = test_argparse_init_obj();
	obj->args[0].name_long = "positional";
	obj->args[0].name_short = NULL;
	obj->args[0].val_saver = (void *)1;
	obj->args[0].val_set = NULL;
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT |
			     RTE_ARGPARSE_ARG_SUPPORT_MULTI;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test optional arg enabled multiple but prased by autosave. */
	obj = test_argparse_init_obj();
	obj->args[0].flags |= RTE_ARGPARSE_ARG_SUPPORT_MULTI;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_invalid_arg_repeat(void)
{
	struct rte_argparse *obj;
	int ret;

	/* test for long name repeat! */
	obj = test_argparse_init_obj();
	obj->args[1].name_long = obj->args[0].name_long;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test for short name repeat! */
	obj = test_argparse_init_obj();
	obj->args[1].name_short = obj->args[0].name_short;
	ret = rte_argparse_parse(obj, default_argc, default_argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_invalid_option(void)
{
	struct rte_argparse *obj;
	char *argv[2];
	int ret;

	obj = test_argparse_init_obj();
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--invalid");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	obj = test_argparse_init_obj();
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("invalid");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_opt_autosave_parse_int_of_no_val(void)
{
	uint64_t flags = RTE_ARGPARSE_ARG_NO_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[2];
	int ret;

	obj = test_argparse_init_obj();
	obj->args[0].name_long = "--test-long";
	obj->args[0].name_short = "-t";
	obj->args[0].val_saver = (void *)&val_saver;
	obj->args[0].val_set = (void *)100;
	obj->args[0].flags = flags;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--test-long");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	return 0;
}

static int
test_argparse_opt_autosave_parse_int_of_required_val(void)
{
	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[3];
	int ret;

	obj = test_argparse_init_obj();
	obj->args[0].name_long = "--test-long";
	obj->args[0].name_short = "-t";
	obj->args[0].val_saver = (void *)&val_saver;
	obj->args[0].val_set = NULL;
	obj->args[0].flags = flags;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--test-long");
	argv[2] = test_strdup("100");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	/* test invalid value. */
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	argv[2] = test_strdup("100a");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_opt_autosave_parse_int_of_optional_val(void)
{
	uint64_t flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[2];
	int ret;

	/* test without value. */
	obj = test_argparse_init_obj();
	obj->args[0].name_long = "--test-long";
	obj->args[0].name_short = "-t";
	obj->args[0].val_saver = (void *)&val_saver;
	obj->args[0].val_set = (void *)100;
	obj->args[0].flags = flags;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--test-long");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	/* test with value. */
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("--test-long=200");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("-t=200");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 200, "Argparse parse expect success!");

	/* test with option value, but with wrong value. */
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("--test-long=200a");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("-t=200a");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
opt_callback_parse_int_of_no_val(uint32_t index, const char *value, void *opaque)
{
	if (index != 1)
		return -EINVAL;
	if (value != NULL)
		return -EINVAL;
	*(int *)opaque = 100;
	return 0;
}

static int
test_argparse_opt_callback_parse_int_of_no_val(void)
{
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[2];
	int ret;

	obj = test_argparse_init_obj();
	obj->callback = opt_callback_parse_int_of_no_val;
	obj->opaque = (void *)&val_saver;
	obj->args[0].name_long = "--test-long";
	obj->args[0].name_short = "-t";
	obj->args[0].val_saver = NULL;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--test-long");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	obj->args[0].flags = RTE_ARGPARSE_ARG_NO_VALUE;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	return 0;
}

static int
opt_callback_parse_int_of_required_val(uint32_t index, const char *value, void *opaque)
{
	char *s = NULL;

	if (index != 1)
		return -EINVAL;

	if (value == NULL)
		return -EINVAL;
	*(int *)opaque = strtol(value, &s, 0);

	if (s[0] != '\0')
		return -EINVAL;

	return 0;
}

static int
test_argparse_opt_callback_parse_int_of_required_val(void)
{
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[3];
	int ret;

	obj = test_argparse_init_obj();
	obj->callback = opt_callback_parse_int_of_required_val;
	obj->opaque = (void *)&val_saver;
	obj->args[0].name_long = "--test-long";
	obj->args[0].name_short = "-t";
	obj->args[0].val_saver = NULL;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--test-long");
	argv[2] = test_strdup("100");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	/* test no more parameters. */
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test callback return failed. */
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	argv[2] = test_strdup("100a");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
opt_callback_parse_int_of_optional_val(uint32_t index, const char *value, void *opaque)
{
	char *s = NULL;

	if (index != 1)
		return -EINVAL;

	if (value == NULL) {
		*(int *)opaque = 10;
	} else {
		*(int *)opaque = strtol(value, &s, 0);
		if (s[0] != '\0')
			return -EINVAL;
	}

	return 0;
}

static int
test_argparse_opt_callback_parse_int_of_optional_val(void)
{
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[2];
	int ret;

	obj = test_argparse_init_obj();
	obj->callback = opt_callback_parse_int_of_optional_val;
	obj->opaque = (void *)&val_saver;
	obj->args[0].name_long = "--test-long";
	obj->args[0].name_short = "-t";
	obj->args[0].val_saver = NULL;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("--test-long");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");

	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
	val_saver = 0;
	argv[1] = test_strdup("-t");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 10, "Argparse parse expect success!");

	/* test with value. */
	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
	val_saver = 0;
	argv[1] = test_strdup("--test-long=100");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");
	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
	val_saver = 0;
	argv[1] = test_strdup("-t=100");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	/* test callback return failed. */
	obj->args[0].flags = RTE_ARGPARSE_ARG_OPTIONAL_VALUE;
	argv[1] = test_strdup("-t=100a");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_pos_autosave_parse_int(void)
{
	uint64_t flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE | RTE_ARGPARSE_ARG_VALUE_INT;
	struct rte_argparse *obj;
	int val_saver = 0;
	char *argv[3];
	int ret;

	/* test positional autosave parse successful. */
	obj = test_argparse_init_obj();
	obj->args[0].name_long = "test-long";
	obj->args[0].name_short = NULL;
	obj->args[0].val_saver = (void *)&val_saver;
	obj->args[0].val_set = NULL;
	obj->args[0].flags = flags;
	obj->args[1].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("100");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver == 100, "Argparse parse expect success!");

	/* test positional autosave parse failed. */
	obj->args[0].flags = flags;
	val_saver = 0;
	argv[1] = test_strdup("100a");
	ret = rte_argparse_parse(obj, 2, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	/* test too much position parameters. */
	obj->args[0].flags = flags;
	argv[1] = test_strdup("100");
	argv[2] = test_strdup("200");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
pos_callback_parse_int(uint32_t index, const char *value, void *opaque)
{
	uint32_t int_val;
	char *s = NULL;

	if (index != 1 && index != 2)
		return -EINVAL;
	if (value == NULL)
		return -EINVAL;

	int_val = strtol(value, &s, 0);
	if (s[0] != '\0')
		return -EINVAL;

	*((int *)opaque	+ index) = int_val;

	return 0;
}

static int
test_argparse_pos_callback_parse_int(void)
{
	int val_saver[3] = { 0, 0, 0 };
	struct rte_argparse *obj;
	char *argv[3];
	int ret;

	/* test positional callback parse successful. */
	obj = test_argparse_init_obj();
	obj->callback = pos_callback_parse_int;
	obj->opaque = (void *)val_saver;
	obj->args[0].name_long = "test-long1";
	obj->args[0].name_short = NULL;
	obj->args[0].val_saver = NULL;
	obj->args[0].val_set = (void *)1;
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	obj->args[1].name_long = "test-long2";
	obj->args[1].name_short = NULL;
	obj->args[1].val_saver = NULL;
	obj->args[1].val_set = (void *)2;
	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	obj->args[2].name_long = NULL;
	argv[0] = test_strdup(obj->prog_name);
	argv[1] = test_strdup("100");
	argv[2] = test_strdup("200");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == 0, "Argparse parse expect success!");
	TEST_ASSERT(val_saver[1] == 100, "Argparse parse expect success!");
	TEST_ASSERT(val_saver[2] == 200, "Argparse parse expect success!");

	/* test positional callback parse failed. */
	obj->args[0].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	obj->args[1].flags = RTE_ARGPARSE_ARG_REQUIRED_VALUE;
	argv[2] = test_strdup("200a");
	ret = rte_argparse_parse(obj, 3, argv);
	TEST_ASSERT(ret == -EINVAL, "Argparse parse expect failed!");

	return 0;
}

static int
test_argparse_parse_type(void)
{
	char *str_erange = test_strdup("9999999999999999999999999999999999");
	char *str_erange_u32 = test_strdup("4294967296");
	char *str_erange_u16 = test_strdup("65536");
	char *str_erange_u8 = test_strdup("256");
	char *str_invalid = test_strdup("1a");
	char *str_ok = test_strdup("123");
	uint16_t val_u16;
	uint32_t val_u32;
	uint64_t val_u64;
	uint8_t val_u8;
	int val_int;
	int ret;

	/* test for int parsing */
	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_INT, &val_int);
	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
	TEST_ASSERT(val_int == 123, "Argparse parse type expect failed!");

	/* test for u8 parsing */
	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_erange_u8, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	val_u8 = 0;
	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U8, &val_u8);
	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
	TEST_ASSERT(val_u8 == 123, "Argparse parse type expect failed!");

	/* test for u16 parsing */
	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_erange_u16, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	val_u16 = 0;
	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U16, &val_u16);
	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
	TEST_ASSERT(val_u16 == 123, "Argparse parse type expect failed!");

	/* test for u32 parsing */
	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_erange_u32, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	val_u32 = 0;
	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U32, &val_u32);
	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
	TEST_ASSERT(val_u32 == 123, "Argparse parse type expect failed!");

	/* test for u64 parsing */
	ret = rte_argparse_parse_type(str_erange, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	ret = rte_argparse_parse_type(str_invalid, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
	TEST_ASSERT(ret != 0, "Argparse parse type expect failed!");
	val_u64 = 0;
	ret = rte_argparse_parse_type(str_ok, RTE_ARGPARSE_ARG_VALUE_U64, &val_u64);
	TEST_ASSERT(ret == 0, "Argparse parse type expect failed!");
	TEST_ASSERT(val_u64 == 123, "Argparse parse type expect failed!");

	return 0;
}

static struct unit_test_suite argparse_test_suite = {
	.suite_name = "Argparse Unit Test Suite",
	.setup = test_argparse_setup,
	.teardown = test_argparse_teardown,
	.unit_test_cases = {
		TEST_CASE(test_argparse_invalid_basic_param),
		TEST_CASE(test_argparse_invalid_arg_name),
		TEST_CASE(test_argparse_invalid_arg_help),
		TEST_CASE(test_argparse_invalid_has_val),
		TEST_CASE(test_argparse_invalid_arg_saver),
		TEST_CASE(test_argparse_invalid_arg_flags),
		TEST_CASE(test_argparse_invalid_arg_repeat),
		TEST_CASE(test_argparse_invalid_option),
		TEST_CASE(test_argparse_opt_autosave_parse_int_of_no_val),
		TEST_CASE(test_argparse_opt_autosave_parse_int_of_required_val),
		TEST_CASE(test_argparse_opt_autosave_parse_int_of_optional_val),
		TEST_CASE(test_argparse_opt_callback_parse_int_of_no_val),
		TEST_CASE(test_argparse_opt_callback_parse_int_of_required_val),
		TEST_CASE(test_argparse_opt_callback_parse_int_of_optional_val),
		TEST_CASE(test_argparse_pos_autosave_parse_int),
		TEST_CASE(test_argparse_pos_callback_parse_int),
		TEST_CASE(test_argparse_parse_type),

		TEST_CASES_END() /**< NULL terminate unit test array */
	}
};

static int
test_argparse(void)
{
	return unit_test_suite_runner(&argparse_test_suite);
}

REGISTER_FAST_TEST(argparse_autotest, true, true, test_argparse);
