Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions include/libbase/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,24 @@ const char *bs_test_temp_path(
const char *fname_fmt_ptr,
...) __ARG_PRINTF__(2, 3);

/**
* Sets an environment variable in the process.
*
* This will first backup the current value of the environment variable, then
* call getenv(3) to change it. Upon test teardown, the original value of the
* environment variable is kept.
* Repeated calls to @ref bs_test_setenv will not overwrite the backup stored
* during the first call.
*
* @param test_ptr
* @param name_ptr
* @param value_fmt_ptr
*/
void bs_test_setenv(
bs_test_t *test_ptr,
const char *name_ptr,
const char *value_fmt_ptr, ...) __ARG_PRINTF__(3, 4);

/* == Verification macros ================================================== */

/**
Expand Down
123 changes: 123 additions & 0 deletions src/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <fnmatch.h>
#include <libbase/arg.h>
#include <libbase/assert.h>
#include <libbase/avltree.h>
#include <libbase/def.h>
#include <libbase/dllist.h>
#include <libbase/file.h>
Expand Down Expand Up @@ -53,6 +54,8 @@ struct _bs_test_t {
const bs_test_set_t *set_ptr;
/** Test environment. */
const struct bs_test_env *env_ptr;
/** Tree holding environment variables and original values. */
bs_avltree_t *env_tree_ptr;

/** Return value of @ref bs_test_set_t::setup. */
void *setup_context_ptr;
Expand Down Expand Up @@ -122,6 +125,16 @@ struct bs_test_env {
char *data_dir_ptr;
};

/** Node holding environment variable. */
struct bs_test_env_node {
/** Tree node. */
bs_avltree_node_t avlnode;
/** Name of the environment variable. */
char *name_ptr;
/** Value originally held by this environment variable. */
char *value_ptr;
};

/* Other helpers */
static void bs_test_tcode_init(void);
static int bs_test_putc(int c);
Expand All @@ -135,6 +148,11 @@ static int bs_test_set(const bs_test_set_t *set_ptr,
const struct bs_test_env *env_ptr,
bs_dllist_t *failed_tests_ptr);

static int _bs_test_env_node_cmp(
const bs_avltree_node_t *node_ptr,
const void *key_ptr);
static void _bs_test_env_node_destroy(bs_avltree_node_t *node_ptr);

/* Testcase helpers. */
static bool bs_test_case_prepare(bs_test_t *test_ptr,
const bs_test_set_t *set_ptr,
Expand Down Expand Up @@ -546,6 +564,36 @@ const char *bs_test_temp_path(
return NULL;
}

/* ------------------------------------------------------------------------- */
void bs_test_setenv(
bs_test_t *test_ptr,
const char *name_ptr,
const char *value_fmt_ptr, ...)
{
if (NULL == bs_avltree_lookup(test_ptr->env_tree_ptr, name_ptr)) {
struct bs_test_env_node *node_ptr = BS_ASSERT_NOTNULL(
logged_calloc(1, sizeof(struct bs_test_env_node)));
node_ptr->name_ptr = BS_ASSERT_NOTNULL(logged_strdup(name_ptr));
char *old_value_ptr = getenv(name_ptr);
if (NULL != old_value_ptr) {
node_ptr->value_ptr = BS_ASSERT_NOTNULL(
logged_strdup(old_value_ptr));
}
BS_ASSERT(bs_avltree_insert(
test_ptr->env_tree_ptr,
node_ptr->name_ptr,
&node_ptr->avlnode,
false));
}

va_list ap;
va_start(ap, value_fmt_ptr);
char *value_ptr = BS_ASSERT_NOTNULL(bs_vstrdupf(value_fmt_ptr, ap));
va_end(ap);
setenv(name_ptr, value_ptr, 1);
free(value_ptr);
}

/* == Static (Local) Functions ============================================= */

/* ------------------------------------------------------------------------- */
Expand Down Expand Up @@ -749,6 +797,33 @@ int bs_test_set(const bs_test_set_t *set_ptr,
return set_report.failed;
}

/* ------------------------------------------------------------------------- */
int _bs_test_env_node_cmp(
const bs_avltree_node_t *avlnode_ptr,
const void *key_ptr)
{
struct bs_test_env_node *node_ptr = BS_CONTAINER_OF(
avlnode_ptr, struct bs_test_env_node, avlnode);
return strcmp(node_ptr->name_ptr, key_ptr);
}

/* ------------------------------------------------------------------------- */
void _bs_test_env_node_destroy(bs_avltree_node_t *avlnode_ptr)
{
struct bs_test_env_node *node_ptr = BS_CONTAINER_OF(
avlnode_ptr, struct bs_test_env_node, avlnode);
if (NULL != node_ptr->name_ptr) {
if (NULL != node_ptr->value_ptr) {
setenv(node_ptr->name_ptr, node_ptr->value_ptr, 1);
} else {
unsetenv(node_ptr->name_ptr);
}
free(node_ptr->name_ptr);
}
if (NULL != node_ptr->value_ptr) free(node_ptr->value_ptr);
free(node_ptr);
}

/* ------------------------------------------------------------------------- */
/**
* Prepares test_ptr for this test case.
Expand All @@ -767,8 +842,14 @@ bool bs_test_case_prepare(bs_test_t *test_ptr,
.case_idx = case_idx,
.case_ptr = set_ptr->cases_ptr + case_idx,
.failed = false,
.env_tree_ptr = bs_avltree_create(
_bs_test_env_node_cmp, _bs_test_env_node_destroy)
};

if (NULL == test_ptr->env_tree_ptr) {
BS_TEST_FAIL(test_ptr, "Failed bs_avltree_create(...)");
}

if (set_ptr->setup) {
test_ptr->setup_context_ptr = set_ptr->setup();
if (NULL == test_ptr->setup_context_ptr) {
Expand Down Expand Up @@ -807,6 +888,11 @@ void bs_test_case_report(bs_test_t *test_ptr,
free(test_ptr->test_path_ptr);
}

if (NULL != test_ptr->env_tree_ptr) {
bs_avltree_destroy(test_ptr->env_tree_ptr);
test_ptr->env_tree_ptr = NULL;
}

void *p;
while (NULL != (p = bs_ptr_stack_pop(&test_ptr->allocated_ptrs))) {
free(p);
Expand Down Expand Up @@ -930,6 +1016,7 @@ static void _bs_test_eq_neq_tests(bs_test_t *test_ptr);
static void _bs_test_context_setup_ok(bs_test_t *test_ptr);
static void _bs_test_context_setup_fail(bs_test_t *test_ptr);
static void _bs_test_temp_path(bs_test_t *test_ptr);
static void _bs_test_set_env(bs_test_t *test_ptr);

/** Unit test cases. */
static const bs_test_case_t bs_test_test_cases[] = {
Expand All @@ -938,6 +1025,7 @@ static const bs_test_case_t bs_test_test_cases[] = {
{ true, "context_setup_ok", _bs_test_context_setup_ok },
{ true, "context_setup_fail", _bs_test_context_setup_fail },
{ true, "temp_path", _bs_test_temp_path },
{ true, "set_env", _bs_test_set_env },
BS_TEST_CASE_SENTINEL()
};

Expand Down Expand Up @@ -1067,5 +1155,40 @@ void _bs_test_temp_path(bs_test_t *test_ptr)
BS_TEST_VERIFY_TRUE(test_ptr, bs_file_realpath_is(tmp_path, S_IFDIR));
}

/* ------------------------------------------------------------------------- */
/** Set and verify an env variable. Used in @ref _bs_test_set_env. */
void _bs_test_set_and_verify_env(bs_test_t *test_ptr)
{
bs_test_setenv(test_ptr, "VAR", "value");
char *value_ptr = getenv("VAR");
BS_TEST_VERIFY_NEQ_OR_RETURN(test_ptr, NULL, value_ptr);
BS_TEST_VERIFY_STREQ(test_ptr, "value", value_ptr);
}

/** Use environment variables, and ensure they're cleared on teardown. */
void _bs_test_set_env(bs_test_t *test_ptr)
{
static const bs_test_case_t cases [] = {
{ true, "set_and_verify_env", _bs_test_set_and_verify_env },
BS_TEST_CASE_SENTINEL()
};
static const bs_test_set_t set = BS_TEST_SET(true, "env", cases);
static const bs_test_set_t *sets[] = { &set, NULL };

char *old_value_ptr = getenv("VAR");
if (NULL != old_value_ptr) {
old_value_ptr = BS_ASSERT_NOTNULL(strdup(old_value_ptr));
}
BS_TEST_VERIFY_EQ(test_ptr, 0, bs_test_sets(sets, 0, NULL, NULL));

char *new_value_ptr = getenv("VAR");
if (NULL != old_value_ptr) {
BS_TEST_VERIFY_STREQ(test_ptr, old_value_ptr, new_value_ptr);
free(old_value_ptr);
} else {
BS_TEST_VERIFY_EQ(test_ptr, NULL, new_value_ptr);
}
}

/** @endcond */
/* == End of test.c ======================================================== */