feat: initial libcli implementation
This commit is contained in:
parent
04e067875a
commit
77a5904105
15 changed files with 564 additions and 0 deletions
116
example/main.c
Normal file
116
example/main.c
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "cli.h"
|
||||||
|
#include "cli_parse_utils.h"
|
||||||
|
#include "compiler.h"
|
||||||
|
|
||||||
|
/* Config */
|
||||||
|
|
||||||
|
#define FLAG_VERBOSE (1 << 0)
|
||||||
|
|
||||||
|
struct greet_config {
|
||||||
|
const char *name;
|
||||||
|
uint64_t count;
|
||||||
|
uint8_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Handlers */
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_help(__unused const char *arg, __unused void *cfg)
|
||||||
|
{
|
||||||
|
printf("Usage: greet [OPTIONS] ...\n\n");
|
||||||
|
printf(" -h, --help Display this help\n");
|
||||||
|
printf(" -V, --version Display version\n");
|
||||||
|
printf(" -v, --verbose Verbose output\n");
|
||||||
|
printf(" -n, --name NAME Name to greet (default: world)\n");
|
||||||
|
printf(" -c, --count N Number of greetings (default: 1)\n");
|
||||||
|
return CLI_EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_version(__unused const char *arg, __unused void *cfg)
|
||||||
|
{
|
||||||
|
printf("greet 0.1.0 (libcli example)\n");
|
||||||
|
return CLI_EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_verbose(__unused const char *arg, void *cfg)
|
||||||
|
{
|
||||||
|
struct greet_config *config = (struct greet_config *)cfg;
|
||||||
|
SET_FLAG(config->flags, FLAG_VERBOSE);
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_name(const char *arg, void *cfg)
|
||||||
|
{
|
||||||
|
struct greet_config *config = (struct greet_config *)cfg;
|
||||||
|
config->name = arg;
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_count(const char *arg, void *cfg)
|
||||||
|
{
|
||||||
|
struct greet_config *config = (struct greet_config *)cfg;
|
||||||
|
if (0 != cli_parse_uint64(arg, &config->count))
|
||||||
|
return CLI_ERROR;
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Option table */
|
||||||
|
|
||||||
|
#define GREET_OPTIONS_LIST \
|
||||||
|
X('h', "help", no_argument, handle_help, OPT_ARG_NONE, "Display this help") \
|
||||||
|
X('V', "version", no_argument, handle_version, OPT_ARG_NONE, "Display version") \
|
||||||
|
X('v', "verbose", no_argument, handle_verbose, OPT_ARG_NONE, "Verbose output") \
|
||||||
|
X('n', "name", required_argument, handle_name, OPT_ARG_STRING, "Name to greet") \
|
||||||
|
X('c', "count", required_argument, handle_count, OPT_ARG_UINT, "Number of greetings")
|
||||||
|
|
||||||
|
#define X(s, l, a, h, t, d) + 1
|
||||||
|
enum { GREET_NB_OPTS = 0 GREET_OPTIONS_LIST };
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
#define X(s, l, a, h, t, d) + (1 + (a != no_argument ? 1 : 0))
|
||||||
|
enum { GREET_OPTSTR_LEN = 1 GREET_OPTIONS_LIST };
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
#define X(s, l, a, h, t, d) { s, l, a, h, t, d },
|
||||||
|
static const struct option_descriptor g_opts[] = { GREET_OPTIONS_LIST };
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
/* Main */
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct greet_config config = { .name = "world", .count = 1 };
|
||||||
|
char opt_str[GREET_OPTSTR_LEN + 1];
|
||||||
|
struct option long_opts[GREET_NB_OPTS + 1];
|
||||||
|
enum cli_code ret;
|
||||||
|
uint64_t i;
|
||||||
|
|
||||||
|
cli_set_prog_name(argv[0]);
|
||||||
|
|
||||||
|
ret = cli_parse(argc, argv, &config, g_opts, GREET_NB_OPTS,
|
||||||
|
opt_str, long_opts);
|
||||||
|
|
||||||
|
if (CLI_EXIT_SUCCESS == ret)
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
if (CLI_ERROR == ret)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
for (i = 0; i < config.count; i++)
|
||||||
|
{
|
||||||
|
if (HAS_FLAG(config.flags, FLAG_VERBOSE))
|
||||||
|
printf("[%llu] Hello, %s!\n", (unsigned long long)(i + 1),
|
||||||
|
config.name);
|
||||||
|
else
|
||||||
|
printf("Hello, %s!\n", config.name);
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
42
include/cli.h
Normal file
42
include/cli.h
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef CLI_H
|
||||||
|
#define CLI_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
enum cli_code {
|
||||||
|
CLI_EXIT_SUCCESS = -1,
|
||||||
|
CLI_SUCCESS = 0,
|
||||||
|
CLI_ERROR = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*t_option_handler)(const char *arg, void *config);
|
||||||
|
|
||||||
|
enum option_arg_type
|
||||||
|
{
|
||||||
|
OPT_ARG_NONE = 0,
|
||||||
|
OPT_ARG_INT,
|
||||||
|
OPT_ARG_UINT,
|
||||||
|
OPT_ARG_SECONDS,
|
||||||
|
OPT_ARG_BYTES,
|
||||||
|
OPT_ARG_TTL,
|
||||||
|
OPT_ARG_STRING
|
||||||
|
};
|
||||||
|
|
||||||
|
struct option_descriptor
|
||||||
|
{
|
||||||
|
char short_opt;
|
||||||
|
const char *long_opt;
|
||||||
|
int has_arg;
|
||||||
|
t_option_handler handler;
|
||||||
|
enum option_arg_type arg_type;
|
||||||
|
const char *description;
|
||||||
|
};
|
||||||
|
|
||||||
|
void cli_set_prog_name(const char *name);
|
||||||
|
|
||||||
|
enum cli_code cli_parse(int argc, char **argv, void *config,
|
||||||
|
const struct option_descriptor *opts, size_t nb_opts,
|
||||||
|
char *opt_str, struct option *long_opts);
|
||||||
|
|
||||||
|
#endif
|
||||||
9
include/cli_parse_utils.h
Normal file
9
include/cli_parse_utils.h
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef CLI_PARSE_UTILS_H
|
||||||
|
#define CLI_PARSE_UTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int cli_parse_uint64(const char *s, uint64_t *out);
|
||||||
|
int cli_parse_float(const char *s, float *out);
|
||||||
|
|
||||||
|
#endif
|
||||||
16
include/compiler.h
Normal file
16
include/compiler.h
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef COMPILER_H
|
||||||
|
#define COMPILER_H
|
||||||
|
|
||||||
|
#define __unused __attribute__((unused))
|
||||||
|
|
||||||
|
#define COUNT_OF(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
|
#define STATIC_ARRAY_FOREACH(arr, ptr) \
|
||||||
|
for ((ptr) = (arr); (ptr) < (arr) + COUNT_OF(arr); (ptr)++)
|
||||||
|
|
||||||
|
#define HAS_FLAG(flags, flag) ((flags) & (flag))
|
||||||
|
#define SET_FLAG(flags, flag) ((flags) |= (flag))
|
||||||
|
#define CLEAR_FLAG(flags, flag) ((flags) &= ~(flag))
|
||||||
|
#define TOGGLE_FLAG(flags, flag) ((flags) ^= (flag))
|
||||||
|
|
||||||
|
#endif
|
||||||
166
src/parse.c
Normal file
166
src/parse.c
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "cli.h"
|
||||||
|
|
||||||
|
/* Internal bitmask helpers */
|
||||||
|
#define HAS_FLAG(flags, flag) ((flags) & (flag))
|
||||||
|
#define SET_FLAG(flags, flag) ((flags) |= (flag))
|
||||||
|
|
||||||
|
/* Map -? to -h to support help shortcut */
|
||||||
|
#define HANDLE_QUESTION_MARK(opt) \
|
||||||
|
do { \
|
||||||
|
if (opt == '?' && (optopt) == '?') \
|
||||||
|
opt = 'h'; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static void build_long_options(struct option *long_opts, size_t nb_opts,
|
||||||
|
const struct option_descriptor *src);
|
||||||
|
static void build_optstr(char *dest, const struct option_descriptor *opts,
|
||||||
|
size_t nb_opts);
|
||||||
|
static const struct option_descriptor *find_option_handler(int opt,
|
||||||
|
const struct option_descriptor *opts, size_t nb_opts);
|
||||||
|
static int handle_one_option(int opt, char **argv, void *config,
|
||||||
|
uint64_t *opt_tracker,
|
||||||
|
const struct option_descriptor *opts, size_t nb_opts);
|
||||||
|
static enum cli_code error_unknown_opt(const char *current_opt);
|
||||||
|
static enum cli_code error_invalid_opt(const char *current_opt);
|
||||||
|
static enum cli_code error_duplicate_opt(const char *current_opt);
|
||||||
|
static void print_suggest_help(void);
|
||||||
|
/* ------------------- */
|
||||||
|
|
||||||
|
static const char *s_prog_name = "";
|
||||||
|
|
||||||
|
void
|
||||||
|
cli_set_prog_name(const char *name)
|
||||||
|
{
|
||||||
|
if (NULL != name)
|
||||||
|
s_prog_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum cli_code
|
||||||
|
cli_parse(int argc, char **argv, void *config,
|
||||||
|
const struct option_descriptor *opts, size_t nb_opts,
|
||||||
|
char *opt_str, struct option *long_opts)
|
||||||
|
{
|
||||||
|
uint64_t tracker = 0;
|
||||||
|
int opt;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
build_optstr(opt_str, opts, nb_opts);
|
||||||
|
build_long_options(long_opts, nb_opts, opts);
|
||||||
|
while (-1 != (opt = getopt_long(argc, argv, opt_str, long_opts, NULL)))
|
||||||
|
{
|
||||||
|
HANDLE_QUESTION_MARK(opt);
|
||||||
|
res = handle_one_option(opt, argv, config, &tracker, opts, nb_opts);
|
||||||
|
if (CLI_SUCCESS != res)
|
||||||
|
return (enum cli_code)res;
|
||||||
|
}
|
||||||
|
return CLI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
build_optstr(char *dest, const struct option_descriptor *opts, size_t nb_opts)
|
||||||
|
{
|
||||||
|
size_t str_i = 1;
|
||||||
|
|
||||||
|
/* Mute default error messages */
|
||||||
|
dest[0] = ':';
|
||||||
|
|
||||||
|
for (size_t opt_i = 0; opt_i < nb_opts; ++opt_i)
|
||||||
|
{
|
||||||
|
dest[str_i++] = opts[opt_i].short_opt;
|
||||||
|
switch (opts[opt_i].has_arg)
|
||||||
|
{
|
||||||
|
case optional_argument: dest[str_i++] = ':'; /* fall through */
|
||||||
|
case required_argument: dest[str_i++] = ':'; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest[str_i] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
handle_one_option(int opt, char **argv, void *config,
|
||||||
|
uint64_t *opt_tracker,
|
||||||
|
const struct option_descriptor *opts, size_t nb_opts)
|
||||||
|
{
|
||||||
|
const char *current_opt = argv[optind - 1];
|
||||||
|
const struct option_descriptor *desc;
|
||||||
|
size_t bitmask_index;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if ('?' == opt)
|
||||||
|
return error_unknown_opt(current_opt);
|
||||||
|
else if (':' == opt)
|
||||||
|
return error_invalid_opt(current_opt);
|
||||||
|
|
||||||
|
desc = find_option_handler(opt, opts, nb_opts);
|
||||||
|
bitmask_index = (size_t)(desc - opts);
|
||||||
|
if (HAS_FLAG(*opt_tracker, (1ULL << bitmask_index)))
|
||||||
|
return error_duplicate_opt(current_opt);
|
||||||
|
|
||||||
|
SET_FLAG(*opt_tracker, (1ULL << bitmask_index));
|
||||||
|
res = desc->handler(optarg, config);
|
||||||
|
if (CLI_ERROR == res)
|
||||||
|
return error_invalid_opt(current_opt);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
build_long_options(struct option *long_opts, size_t nb_opts,
|
||||||
|
const struct option_descriptor *src)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < nb_opts; ++i)
|
||||||
|
{
|
||||||
|
long_opts[i].name = src[i].long_opt;
|
||||||
|
long_opts[i].has_arg = src[i].has_arg;
|
||||||
|
long_opts[i].flag = NULL;
|
||||||
|
long_opts[i].val = src[i].short_opt;
|
||||||
|
}
|
||||||
|
long_opts[nb_opts] = (struct option){0};
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct option_descriptor *
|
||||||
|
find_option_handler(int opt, const struct option_descriptor *opts,
|
||||||
|
size_t nb_opts)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < nb_opts; ++i)
|
||||||
|
{
|
||||||
|
if (opts[i].short_opt == opt)
|
||||||
|
return &opts[i];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cli_code
|
||||||
|
error_unknown_opt(const char *current_opt)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: unknown option -- '%s'\n", s_prog_name, current_opt);
|
||||||
|
print_suggest_help();
|
||||||
|
return CLI_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cli_code
|
||||||
|
error_invalid_opt(const char *current_opt)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: invalid option '%s'\n", s_prog_name, current_opt);
|
||||||
|
print_suggest_help();
|
||||||
|
return CLI_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cli_code
|
||||||
|
error_duplicate_opt(const char *current_opt)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: duplicate option '%s'\n", s_prog_name, current_opt);
|
||||||
|
print_suggest_help();
|
||||||
|
return CLI_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_suggest_help(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Try '%s --help' for more information.\n", s_prog_name);
|
||||||
|
}
|
||||||
0
src/parse_utils/.dirstamp
Normal file
0
src/parse_utils/.dirstamp
Normal file
21
src/parse_utils/parse_float.c
Normal file
21
src/parse_utils/parse_float.c
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
cli_parse_float(const char *s, float *out)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
if (NULL == s || '\0' == *s || '-' == *s)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
float v = strtof(s, &end);
|
||||||
|
|
||||||
|
if (s == end || ERANGE == errno || '\0' != *end || 0 == isfinite(v))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*out = v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
21
src/parse_utils/parse_int.c
Normal file
21
src/parse_utils/parse_int.c
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
cli_parse_uint64(const char *s, uint64_t *out)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
if ( NULL == s || '\0' == *s || '-' == *s)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
const uintmax_t v = (uintmax_t)strtoumax(s, &end, 10);
|
||||||
|
|
||||||
|
if (s == end || ERANGE == errno || '\0' != *end || v > UINT64_MAX)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*out = (uint64_t)v;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
0
tests/parse_utils/.dirstamp
Normal file
0
tests/parse_utils/.dirstamp
Normal file
68
tests/parse_utils/test_parse_float.c
Normal file
68
tests/parse_utils/test_parse_float.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include "cli_parse_utils.h"
|
||||||
|
|
||||||
|
/* Test 1: Valid simple number */
|
||||||
|
Test(parse_float, valid_number)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float("42.42", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 0);
|
||||||
|
cr_assert_float_eq(result, 42.42, 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 2: Zero */
|
||||||
|
Test(parse_float, zero)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float("0", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 0);
|
||||||
|
cr_assert_float_eq(result, 0, 0.0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 3: NULL pointer */
|
||||||
|
Test(parse_float, null_pointer)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float(NULL, &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 4: Empty string */
|
||||||
|
Test(parse_float, empty_string)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float("", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 5: Negative number */
|
||||||
|
Test(parse_float, negative_number)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float("-42", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 6: Invalid characters */
|
||||||
|
Test(parse_float, invalid_characters)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float("42abc", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 7: Dot + number */
|
||||||
|
Test(parse_float, dot)
|
||||||
|
{
|
||||||
|
float result;
|
||||||
|
int ret = cli_parse_float(".42", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 0);
|
||||||
|
cr_assert_float_eq(result, 0.42, 0.0001);
|
||||||
|
}
|
||||||
68
tests/parse_utils/test_parse_int.c
Normal file
68
tests/parse_utils/test_parse_int.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "cli_parse_utils.h"
|
||||||
|
|
||||||
|
/* Test 1: Valid simple number */
|
||||||
|
Test(parse_uint64, valid_number)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64("42", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 0);
|
||||||
|
cr_assert_eq(result, 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 2: Zero */
|
||||||
|
Test(parse_uint64, zero)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64("0", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 0);
|
||||||
|
cr_assert_eq(result, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 3: NULL pointer */
|
||||||
|
Test(parse_uint64, null_pointer)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64(NULL, &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 4: Empty string */
|
||||||
|
Test(parse_uint64, empty_string)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64("", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 5: Negative number */
|
||||||
|
Test(parse_uint64, negative_number)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64("-42", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 6: Invalid characters */
|
||||||
|
Test(parse_uint64, invalid_characters)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64("42abc", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 7: Overflow */
|
||||||
|
Test(parse_uint64, overflow)
|
||||||
|
{
|
||||||
|
uint64_t result;
|
||||||
|
int ret = cli_parse_uint64("18446744073709551616", &result);
|
||||||
|
|
||||||
|
cr_assert_eq(ret, 1);
|
||||||
|
}
|
||||||
25
tests/test-suite.log
Normal file
25
tests/test-suite.log
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
========================================
|
||||||
|
libcli 0.1.0: tests/test-suite.log
|
||||||
|
========================================
|
||||||
|
|
||||||
|
# TOTAL: 1
|
||||||
|
# PASS: 1
|
||||||
|
# SKIP: 0
|
||||||
|
# XFAIL: 0
|
||||||
|
# FAIL: 0
|
||||||
|
# XPASS: 0
|
||||||
|
# ERROR: 0
|
||||||
|
|
||||||
|
System information (uname -a): Linux 6.12.58-gentoo-v2 #3 SMP PREEMPT_DYNAMIC Wed Jan 14 19:57:01 CET 2026 x86_64 AMD Ryzen 7 2700X Eight-Core Processor AuthenticAMD
|
||||||
|
Distribution information (/etc/os-release):
|
||||||
|
NAME=Gentoo
|
||||||
|
ID=gentoo
|
||||||
|
PRETTY_NAME="Gentoo Linux"
|
||||||
|
ANSI_COLOR="1;32"
|
||||||
|
HOME_URL="https://www.gentoo.org/"
|
||||||
|
SUPPORT_URL="https://www.gentoo.org/support/"
|
||||||
|
BUG_REPORT_URL="https://bugs.gentoo.org/"
|
||||||
|
VERSION_ID="2.18"
|
||||||
|
|
||||||
|
.. contents:: :depth: 2
|
||||||
|
|
||||||
2
tests/test_cli.log
Normal file
2
tests/test_cli.log
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
[====] Synthesis: Tested: 15 | Passing: 15 | Failing: 0 | Crashing: 0
|
||||||
|
PASS test_cli (exit status: 0)
|
||||||
4
tests/test_cli.trs
Normal file
4
tests/test_cli.trs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
:test-result: PASS
|
||||||
|
:global-test-result: PASS
|
||||||
|
:recheck: no
|
||||||
|
:copy-in-global-log: no
|
||||||
6
tests/test_main.c
Normal file
6
tests/test_main.c
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <criterion/criterion.h>
|
||||||
|
|
||||||
|
Test(dummy, always_pass)
|
||||||
|
{
|
||||||
|
cr_assert(1, "Hello, world!");
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue