/*++
/* NAME
/* nbdb_util_test 1t
/* SUMMARY
/* nbdb_util unit test
/* SYNOPSIS
/* ./nbdb_util_test
/* DESCRIPTION
/* nbdb_util_test runs and logs each configured test, reports if
/* a test is a PASS or FAIL, and returns an exit status of zero if
/* all tests are a PASS.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library .
*/
#include <sys_defs.h>
#include <stdlib.h>
#include <string.h>
/*
* Utility library .
*/
#include <msg.h>
#include <stringops.h>
#include <msg_vstream.h>
#include <vstring.h>
#include <vstream.h>
/*
* Global library .
*/
#include <mail_params.h>
#include <nbdb_util.h>
#include <nbdb_redirect.h>
/*
* Testing library .
*/
#include <mock_dict.h>
#include <msg_capture.h>
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
#define PASS (0 )
#define FAIL (1 )
#define BOOL_TO_TEXT(b) ((b) ? "true" : "false" )
/*
* Scaffolding .
*/
char *var_nbdb_cust_map = DEF_NBDB_CUST_MAP;
/* dummy nbdb_rdr_init() support */
void nbdb_rdr_init(void )
{
}
/*
* tests for nbdb_util_init ( ) ,
*/
struct nbdb_util_init_test {
const char *label;
int (*action) (const struct nbdb_util_init_test *);
const char *in_level;
bool in_reindexing_lockout;
int want_level;
};
#define STR_OR_NULL(s) ((s) ? (s) : "(null)" )
static int test_nbdb_util_init(const struct nbdb_util_init_test * tp)
{
nbdb_util_init(tp->in_level);
if (nbdb_level != tp->want_level) {
msg_warn("unexpected nbdb_level, got: %d, want: %d" ,
nbdb_level, tp->want_level);
return (FAIL);
}
return (PASS);
}
static const struct nbdb_util_init_test nbdb_util_init_tests[] = {
{.label = "nbdb_util_init_level_none" ,
.action = test_nbdb_util_init,
.in_level = "disable" ,
.want_level = NBDB_LEV_CODE_NONE,
},
{.label = "nbdb_util_init_level_redirect" ,
.action = test_nbdb_util_init,
.in_level = "enable-redirect" ,
.want_level = NBDB_LEV_CODE_REDIRECT,
},
{.label = "nbdb_util_init_level_reindex" ,
.action = test_nbdb_util_init,
.in_level = "enable-reindex" ,
.want_level = NBDB_LEV_CODE_REINDEX,
},
{.label = "nbdb_util_init_level_reindexing_with_lockout" ,
.action = test_nbdb_util_init,
.in_level = "enable-reindex" ,
.in_reindexing_lockout = true ,
.want_level = NBDB_LEV_CODE_REINDEX,
},
/* TODO: trap msg_fatal() call after bad level name. */
{0 },
};
/*
* Tests for nbdb_is_legacy_type ( ) .
*/
struct nbdb_is_legacy_type_test {
const char *label;
int (*action) (const struct nbdb_is_legacy_type_test *);
const char *in_type;
bool want_result;
};
static int test_nbdb_is_legacy_type(const struct nbdb_is_legacy_type_test * tp)
{
bool got_result;
got_result = nbdb_is_legacy_type(tp->in_type);
if (got_result != tp->want_result) {
msg_warn("unexpected result; got '%s', want: ''%s'" ,
BOOL_TO_TEXT(got_result), BOOL_TO_TEXT(tp->want_result));
return (FAIL);
}
return (PASS);
}
static const struct nbdb_is_legacy_type_test nbdb_is_legacy_type_tests[] = {
{.label = "nbdb_is_legacy_type_hash_true" ,
.action = test_nbdb_is_legacy_type,
.in_type = "hash" ,
.want_result = true ,
},
{.label = "nbdb_is_legacy_type_btree_true" ,
.action = test_nbdb_is_legacy_type,
.in_type = "btree" ,
.want_result = true ,
},
{.label = "nbdb_is_legacy_type_lmdb_false" ,
.action = test_nbdb_is_legacy_type,
.in_type = "lmdb" ,
.want_result = false ,
},
{.label = "nbdb_is_legacy_type_cdb_false" ,
.action = test_nbdb_is_legacy_type,
.in_type = "cdb" ,
.want_result = false ,
},
{0 },
};
/*
* Tests for find_non_leg_suffix ( ) .
*/
struct find_non_leg_suffix_test {
const char *label;
int (*action) (const struct find_non_leg_suffix_test *);
const char *in_type;
const char *want_suffix;
};
#define STR_OR_NULL(s) ((s) ? (s) : "(null)" )
static int test_find_non_leg_suffix(const struct find_non_leg_suffix_test * tp)
{
const char *got_suffix;
got_suffix = nbdb_find_non_leg_suffix(tp->in_type);
if (!got_suffix != !tp->want_suffix) {
msg_warn("unexpected suffix; got: '%s', want: '%s'" ,
STR_OR_NULL(got_suffix), STR_OR_NULL(tp->want_suffix));
return (FAIL);
}
if (got_suffix && strcmp(got_suffix, tp->want_suffix) != 0 ) {
msg_warn("unexpected suffix; got: '%s', want: '%s'" ,
got_suffix, tp->want_suffix);
return (FAIL);
}
return (PASS);
}
static const struct find_non_leg_suffix_test find_non_leg_suffix_tests[] = {
{.label = "find_non_leg_suffix_lmdb" ,
.action = test_find_non_leg_suffix,
.in_type = "lmdb" ,
.want_suffix = ".lmdb" ,
},
{.label = "find_non_leg_suffix_cdb" ,
.action = test_find_non_leg_suffix,
.in_type = "cdb" ,
.want_suffix = ".cdb" ,
},
{.label = "find_non_leg_suffix_hash" ,
.action = test_find_non_leg_suffix,
.in_type = "hash" ,
.want_suffix = 0 ,
},
{.label = "find_non_leg_suffix_btree" ,
.action = test_find_non_leg_suffix,
.in_type = "btree" ,
.want_suffix = 0 ,
},
{0 },
};
/*
* Tests for map_leg_type ( ) .
*/
struct map_leg_type_test {
const char *label;
int (*action) (const struct map_leg_type_test *);
const char *in_db_type;
const char *in_cache_db_type;
const char *in_custom_map;
const char *in_type;
const char *in_name;
const char *want_type;
const char *want_why;
const char *want_warning;
};
static int test_map_leg_type(const struct map_leg_type_test * tp)
{
VSTRING *got_why = vstring_alloc(100 );
MSG_CAPTURE *capture = msg_capt_create(100 );
char *saved_db_type = var_db_type;
char *saved_cache_db_type = var_cache_db_type;
char *saved_cust_map = var_nbdb_cust_map;
const char *got_type;
const char *got_warning;
int status = PASS;
/* Setup. */
setup_mock_cdb("{{x = x}}" );
setup_mock_lmdb("{{x = x}}" );
dict_allow_surrogate = 1 ;
var_nbdb_cust_map = (char *) tp->in_custom_map;
var_db_type = (char *) tp->in_db_type;
var_cache_db_type = (char *) tp->in_cache_db_type;
msg_capt_start(capture);
nbdb_util_init("enable-redirect" );
got_type = nbdb_map_leg_type(tp->in_type, tp->in_name,
(const DICT_OPEN_INFO **) 0 , got_why);
msg_capt_stop(capture);
got_warning = msg_capt_expose(capture);
/* Verify results. */
if (!LEN(got_why) != !tp->want_why) {
msg_warn("got error text: `%s', want: `%s'" ,
STR(got_why), STR_OR_NULL(tp->want_why));
status = FAIL;
} else if (tp->want_why && strstr(STR(got_why), tp->want_why) == 0 ) {
msg_warn("got error text: `%s', want: `%s'" ,
STR(got_why), tp->want_why);
status = FAIL;
} else if (!tp->want_warning != !got_warning[0 ]) {
msg_warn("got warning: '%s', want: '%s'" ,
got_warning, STR_OR_NULL(tp->want_warning));
status = FAIL;
} else if (tp->want_warning && strstr(got_warning, tp->want_warning) == 0 ) {
msg_warn("got warning: '%s', want: '%s'" ,
got_warning, tp->want_warning);
status = FAIL;
} else if (!got_type != !tp->want_type) {
msg_warn("got unexpected type: '%s', want: '%s'" ,
STR_OR_NULL(got_type), STR_OR_NULL(tp->want_type));
status = FAIL;
} else if (got_type && strcmp(got_type, tp->want_type) != 0 ) {
msg_warn("got unexpected type: '%s', want: '%s'" ,
got_type, tp->want_type);
status = FAIL;
}
var_nbdb_cust_map = saved_cust_map;
var_db_type = saved_db_type;
var_cache_db_type = saved_cache_db_type;
vstring_free(got_why);
msg_capt_free(capture);
return (status);
}
static const struct map_leg_type_test map_leg_type_tests[] = {
{"map_leg_type:default_hash_to_cdb" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = "cdb" ,
},
{"map_leg_type:default_btree_to_lmdb" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "" ,
.in_type = "btree" ,
.in_name = "/some/where/blah" ,
.want_type = "lmdb" ,
},
{"map_leg_type:custom_hash_with_path_to_lmdb" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "inline:{{hash:/some/where/blah = lmdb}}" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = "lmdb" ,
},
{"map_leg_type:custom_btree_with_path_to_cdb" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "inline:{{btree:/some/where/blah = cdb}}" ,
.in_type = "btree" ,
.in_name = "/some/where/blah" ,
.want_type = "cdb" ,
},
{"map_leg_type:custom_hash_to_lmdb" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "inline:{{hash = lmdb}}" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = "lmdb" ,
},
{"map_leg_type:custom_mapping_type_must_not_use_hash" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "hash:/whatever" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = 0 ,
.want_why = "lookup error for 'hash:/some/where/blah'" ,
.want_warning = "mapping must not use legacy type: 'hash'"
},
{"map_leg_type:custom_mapping_type_must_not_use_btree" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "btree:/whatever" ,
.in_type = "btree" ,
.in_name = "/some/where/blah" ,
.want_type = 0 ,
.want_why = "lookup error for 'btree:/some/where/blah'" ,
.want_warning = "mapping must not use legacy type: 'btree'" ,
},
{"map_leg_type:custom_hash_to_composite" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "inline:{{hash = proxy:lmdb}}" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = 0 ,
.want_why = "bad non-legacy database type syntax: 'proxy:lmdb'" ,
},
{"map_leg_type:custom_hash_to_legacy" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "inline:{{hash = btree}}" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = 0 ,
.want_why = "mapping from 'hash' to legacy type 'btree'" ,
},
{"map_leg_type:custom_hash_to_unsupported" ,
.action = test_map_leg_type,
.in_db_type = "cdb" ,
.in_cache_db_type = "lmdb" ,
.in_custom_map = "inline:{{hash = foo}}" ,
.in_type = "hash" ,
.in_name = "/some/where/blah" ,
.want_type = 0 ,
.want_why = "unsupported database type: 'foo'" ,
},
{0 },
};
int main(int argc, char **argv)
{
int pass = 0 ;
int fail = 0 ;
#define RUN_TEST(tp) do { \
int test_failed; \
msg_info("RUN %s" , tp->label); \
test_failed = tp->action(tp); \
if (test_failed) { \
msg_info("FAIL %s" , tp->label); \
fail++; \
} else { \
msg_info("PASS %s" , tp->label); \
pass++; \
} \
} while (0 )
msg_vstream_init(sane_basename((VSTRING *) 0 , argv[0 ]), VSTREAM_ERR);
/* nbdb_util_init tests. */
{
const struct nbdb_util_init_test *tp;
for (tp = nbdb_util_init_tests; tp->label != 0 ; tp++)
RUN_TEST(tp);
}
/* nbdb_is_legacy_type() tests. */
{
const struct nbdb_is_legacy_type_test *tp;
for (tp = nbdb_is_legacy_type_tests; tp->label != 0 ; tp++)
RUN_TEST(tp);
}
/* find_non_leg_suffix() tests. */
{
const struct find_non_leg_suffix_test *tp;
for (tp = find_non_leg_suffix_tests; tp->label != 0 ; tp++)
RUN_TEST(tp);
}
/* map_leg_type() tests. */
{
const struct map_leg_type_test *tp;
for (tp = map_leg_type_tests; tp->label != 0 ; tp++)
RUN_TEST(tp);
}
msg_info("PASS=%d FAIL=%d" , pass, fail);
exit (fail != 0 );
}
Messung V0.5 in Prozent C=74 H=96 G=85
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland