Skip to content
7 changes: 1 addition & 6 deletions src/include/daos_srv/dlck.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define __DAOS_DLCK_H__

#include "d_vector.h"
#include "dlck_debug.h"

/**
* Trace a single DTX record.
Expand Down Expand Up @@ -70,12 +71,6 @@ dlck_dtx_act_recs_remove(daos_handle_t coh);
int
dlck_dtx_act_recs_set(daos_handle_t coh, d_vector_t *dv);

#ifdef DLCK_UT_BUILD
#define DLCK_STATIC
#else
#define DLCK_STATIC static
#endif

/**
* \struct dlck_iter_bundle
*
Expand Down
16 changes: 16 additions & 0 deletions src/include/daos_srv/dlck_debug.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* (C) Copyright 2025 Hewlett Packard Enterprise Development LP
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*/

#ifndef __DLCK_DEBUG_H__
#define __DLCK_DEBUG_H__

#ifdef DLCK_UT_BUILD
#define DLCK_STATIC
#else
#define DLCK_STATIC static
#endif

#endif /** __DLCK_DEBUG_H__ */
5 changes: 3 additions & 2 deletions src/utils/dlck/dlck_args_files.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@
#include <gurt/list.h>

#include "dlck_args.h"
#include "daos_srv/dlck_debug.h"

static struct argp_option args_files_options[] = {
{"file", KEY_FILES, "UUID,TARGET", 0,
"Pool UUID and set of targets. Can be used more than once.", GROUP_OPTIONS},
{0}};

static void
DLCK_STATIC void
args_files_init(struct dlck_args_files *args)
{
memset(args, 0, sizeof(*args));
/** set defaults */
D_INIT_LIST_HEAD(&args->list);
}

static int
DLCK_STATIC int
args_files_check(struct argp_state *state, struct dlck_args_files *args)
{
if (d_list_empty(&args->list)) {
Expand Down
15 changes: 15 additions & 0 deletions src/utils/dlck/tests/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ def build_dlck_vos_cont_recs_get_active(venv):
venv.Install('$PREFIX/bin/', program)


def build_dlck_args_files_ut(venv):
"""Build dlck_args_files.c test"""
venv.require('cmocka')
venv.AppendUnique(LINKFLAGS=['-Wl,--wrap=argp_failure'])
venv.AppendUnique(LINKFLAGS=['-Wl,--wrap=d_calloc'])
venv.AppendUnique(LINKFLAGS=['-Wl,--wrap=d_free'])
libs = ['cmocka', 'gurt', 'daos_common_pmem', 'uuid']
srcs = ['dlck_args_files_ut.c', '../dlck_args_files.c']
cppdefines = ['DLCK_UT_BUILD']

program = venv.d_test_program('dlck_args_files_ut', srcs, LIBS=libs, CPPDEFINES=cppdefines)
venv.Install('$PREFIX/bin/', program)


def build_dlck_test_helper(henv):
"""Build dlck_test_helper"""
henv.Append(OBJPREFIX="dlck_ut_helper_")
Expand Down Expand Up @@ -94,6 +108,7 @@ def scons():
build_dlck_dtx_act_recs_recover_ut(env.Clone())
build_dlck_vos_cont_recs_get_active(env.Clone())
build_dlck_test_helper(env.Clone())
build_dlck_args_files_ut(env.Clone())


if __name__ == "SCons.Script":
Expand Down
241 changes: 241 additions & 0 deletions src/utils/dlck/tests/dlck_args_files_ut.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/**
* (C) Copyright 2025 Hewlett Packard Enterprise Development LP
*
* SPDX-License-Identifier: BSD-2-Clause-Patent
*/

#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <uuid/uuid.h>

#include <argp.h>
#include <daos/debug.h>

#include "../dlck_args.h"

void
args_files_init(struct dlck_args_files *args);
int
args_files_check(struct argp_state *state, struct dlck_args_files *args);
extern struct argp argp_file;

/** mocks & globals */

struct argp_state Argp_state;
#define MOCK_ARGP_STATE (&Argp_state)
struct dlck_file File;
#define MOCK_ARG ((void *)0xDEADBEEF)
struct dlck_args_files Args;

#define MOCK_KEY_UKNOWN 9999

void
__wrap_argp_failure(struct argp_state *state, int status, int errnum, const char *fmt, ...)
{
assert_ptr_equal(state, MOCK_ARGP_STATE);
check_expected(status);
check_expected(errnum);
assert_non_null(fmt);
}

int
parse_file(const char *arg, struct argp_state *state, struct dlck_file **file_ptr)
{
assert_ptr_equal(arg, MOCK_ARG);
assert_ptr_equal(state, MOCK_ARGP_STATE);
assert_non_null(file_ptr);
int rc = mock_type(int);
if (rc == 0) {
memset(&File, 0, sizeof(File));
*file_ptr = &File;
}
return rc;
}

void *
__wrap_d_calloc(size_t count, size_t eltsize)
{
return test_calloc(count, eltsize);
}

void
__wrap_d_free(void *ptr)
{
test_free(ptr);
}

/** setup (no teardown necessary) */
static int
setup(void **unused)
{
args_files_init(&Args);
Argp_state.input = &Args;
return 0;
}

/** tests */

static void
test_check_no_files_fail(void **unused)
{
expect_value(__wrap_argp_failure, status, EINVAL);
expect_value(__wrap_argp_failure, errnum, EINVAL);

int rc = args_files_check(MOCK_ARGP_STATE, &Args);
assert_int_equal(rc, EINVAL);
}

static void
test_check_success(void **unused)
{
d_list_add_tail(&File.link, &Args.list);

int rc = args_files_check(MOCK_ARGP_STATE, &Args);
assert_int_equal(rc, 0);
}

static void
test_parser_KEY_INIT(void **unused)
{
memset(&Args, 0xf, sizeof(Args));

int rc = argp_file.parser(ARGP_KEY_INIT, NULL, &Argp_state);
assert_int_equal(rc, 0);

assert_true(d_list_empty(&Args.list));
}

static void
test_parser_KEY_END_without_files_fails(void **unused)
{
expect_value(__wrap_argp_failure, status, EINVAL);
expect_value(__wrap_argp_failure, errnum, EINVAL);

Argp_state.input = &Args;
int rc = argp_file.parser(ARGP_KEY_END, NULL, &Argp_state);
assert_int_equal(rc, EINVAL);
}

static void
test_parser_KEY_END_with_files_success(void **unused)
{
d_list_add_tail(&File.link, &Args.list);

int rc = argp_file.parser(ARGP_KEY_END, NULL, &Argp_state);

assert_int_equal(rc, 0);
}

static void
helper_KEY_is_noop(int key)
{
d_list_add_tail(&File.link, &Args.list);
int rc = argp_file.parser(key, NULL, &Argp_state);
assert_int_equal(rc, 0);
assert_false(d_list_empty(&Args.list));
}

static void
test_parser_KEY_SUCCESS_is_noop(void **unused)
{
helper_KEY_is_noop(ARGP_KEY_SUCCESS);
}

static void
test_parser_KEY_FINI_is_noop(void **unused)
{
helper_KEY_is_noop(ARGP_KEY_FINI);
}

static void
test_KEY_FILE_success(void **unused)
{
will_return(parse_file, 0);

int rc = argp_file.parser(KEY_FILES, MOCK_ARG, &Argp_state);
assert_int_equal(rc, 0);

assert_false(d_list_empty(&Args.list));

struct dlck_file *file = d_list_entry(Args.list.next, struct dlck_file, link);
assert_ptr_equal(file, &File);
}

static void
test_KEY_FILE_fail(void **unused)
{
will_return(parse_file, EINVAL);

int rc = argp_file.parser(KEY_FILES, MOCK_ARG, &Argp_state);
assert_int_equal(rc, EINVAL);
assert_true(d_list_empty(&Args.list));
}

static void
test_KEY_UNKNOWN_fail(void **unused)
{
int rc = argp_file.parser(MOCK_KEY_UKNOWN, NULL, &Argp_state);
assert_int_equal(rc, ARGP_ERR_UNKNOWN);
}

static void
test_files_free_empty_list(void **unused)
{
assert_true(d_list_empty(&Args.list));
dlck_args_files_free(&Args);
assert_true(d_list_empty(&Args.list));
}

static void
helper_files_free(int file_num)
{
for (int i = 0; i < file_num; i++) {
struct dlck_file *file;
D_ALLOC_PTR(file);
d_list_add_tail(&file->link, &Args.list);
}

dlck_args_files_free(&Args);
assert_true(d_list_empty(&Args.list));
}

static void
test_files_free_one_file(void **unused)
{
helper_files_free(1);
}

static void
test_files_free_three_files(void **state)
{
helper_files_free(3);
}

static const struct CMUnitTest tests_all[] = {
{"ARGFILES100: args_files_check() no files", test_check_no_files_fail, setup, NULL},
{"ARGFILES101: args_files_check() success", test_check_success, setup, NULL},
{"ARGFILES102: parser ARGP_KEY_INIT", test_parser_KEY_INIT, setup, NULL},
{"ARGFILES103: parser ARGP_KEY_END no files", test_parser_KEY_END_without_files_fails, setup,
NULL},
{"ARGFILES104: parser ARGP_KEY_END success", test_parser_KEY_END_with_files_success, setup,
NULL},
{"ARGFILES105: parser ARGP_KEY_SUCCESS noop", test_parser_KEY_SUCCESS_is_noop, setup, NULL},
{"ARGFILES106: parser ARGP_KEY_FINI noop", test_parser_KEY_FINI_is_noop, setup, NULL},
{"ARGFILES107: KEY_FILES success", test_KEY_FILE_success, setup, NULL},
{"ARGFILES108: KEY_FILES fail", test_KEY_FILE_fail, setup, NULL},
{"ARGFILES109: unknown key", test_KEY_UNKNOWN_fail, setup, NULL},
{"ARGFILES110: dlck_args_files_free() no files", test_files_free_empty_list, setup, NULL},
{"ARGFILES111: dlck_args_files_free() one file", test_files_free_one_file, setup, NULL},
{"ARGFILES112: dlck_args_files_free() multiple files", test_files_free_three_files, setup,
NULL},
};

int
main(void)
{
const char *test_name = "dlck_args_files.c tests";

return cmocka_run_group_tests_name(test_name, tests_all, NULL, NULL);
}
Loading