diff options
Diffstat (limited to 'plugins/Dbx_mdbx/src/libmdbx/test/main.cc')
-rw-r--r-- | plugins/Dbx_mdbx/src/libmdbx/test/main.cc | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/plugins/Dbx_mdbx/src/libmdbx/test/main.cc b/plugins/Dbx_mdbx/src/libmdbx/test/main.cc new file mode 100644 index 0000000000..2c4f9b09fd --- /dev/null +++ b/plugins/Dbx_mdbx/src/libmdbx/test/main.cc @@ -0,0 +1,397 @@ +/* + * Copyright 2017 Leonid Yuriev <leo@yuriev.ru> + * and other libmdbx authors: please see AUTHORS file. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include "test.h" + +void __noreturn usage(void) { + printf("usage:\n" + "\tFIXME\n"); + exit(EXIT_FAILURE); +} + +//----------------------------------------------------------------------------- + +void actor_params::set_defaults(void) { + pathname_log = ""; + loglevel = +#ifdef NDEBUG + logging::notice; +#else + logging::trace; +#endif + + pathname_db = +#ifdef __linux__ + "/dev/shm/test_tmpdb.mdbx"; +#else + "test_tmpdb.mdbx"; +#endif + mode_flags = MDBX_NOSUBDIR | MDBX_WRITEMAP | MDBX_MAPASYNC | MDBX_NORDAHEAD | + MDBX_NOMEMINIT | MDBX_COALESCE | MDBX_LIFORECLAIM; + table_flags = MDBX_DUPSORT; + size = 1024 * 1024 * 4; + + keygen.seed = 1; + keygen.keycase = kc_random; + keygen.width = 32; + keygen.mesh = 32; + keygen.split = keygen.width / 2; + keygen.rotate = 0; + keygen.offset = 0; + + test_duration = 0; + test_nops = 1000; + nrepeat = 1; + nthreads = 1; + + keylen_min = 0; + keylen_max = 42; + datalen_min = 0; + datalen_max = 256; + + batch_read = 4; + batch_write = 4; + + delaystart = 0; + waitfor_nops = 0; + + drop_table = false; + + max_readers = 42; + max_tables = 42; + + global::config::timeout_duration_seconds = 0 /* infinite */; + global::config::dump_config = true; + global::config::cleanup_before = true; + global::config::cleanup_after = true; + global::config::failfast = true; + global::config::progress_indicator = osal_istty(STDERR_FILENO); +} + +namespace global { + +std::vector<actor_config> actors; +std::unordered_map<unsigned, actor_config *> events; +std::unordered_map<mdbx_pid_t, actor_config *> pid2actor; +std::set<std::string> databases; +unsigned nactors; +chrono::time start_motonic; +chrono::time deadline_motonic; +bool singlemode; + +namespace config { +unsigned timeout_duration_seconds; +bool dump_config; +bool cleanup_before; +bool cleanup_after; +bool failfast; +bool progress_indicator; +} /* namespace config */ + +} /* namespace global */ + +//----------------------------------------------------------------------------- + +const char global::thunk_param_prefix[] = "--execute="; + +std::string thunk_param(const actor_config &config) { + return config.serialize(global::thunk_param_prefix); +} + +void cleanup() { + log_trace(">> cleanup"); + /* TODO: remove each database */ + log_trace("<< cleanup"); +} + +int main(int argc, char *const argv[]) { + +#ifdef _DEBUG + log_trace("#argc = %d", argc); + for (int i = 0; i < argc; ++i) + log_trace("#argv[%d] = %s", i, argv[i]); +#endif /* _DEBUG */ + + if (argc < 2) + failure("No parameters given\n"); + + if (argc == 2 && + strncmp(argv[1], global::thunk_param_prefix, + strlen(global::thunk_param_prefix)) == 0) + return test_execute( + actor_config(argv[1] + strlen(global::thunk_param_prefix))) + ? EXIT_SUCCESS + : EXIT_FAILURE; + + actor_params params; + params.set_defaults(); + global::config::dump_config = true; + logging::setup((logging::loglevel)params.loglevel, "main"); + unsigned last_space_id = 0; + + for (int narg = 1; narg < argc; ++narg) { + const char *value = nullptr; + + if (config::parse_option(argc, argv, narg, "case", &value)) { + testcase_setup(value, params, last_space_id); + continue; + } + if (config::parse_option(argc, argv, narg, "pathname", params.pathname_db)) + continue; + if (config::parse_option(argc, argv, narg, "mode", params.mode_flags, + config::mode_bits)) + continue; + if (config::parse_option(argc, argv, narg, "table", params.table_flags, + config::table_bits)) + continue; + if (config::parse_option(argc, argv, narg, "size", params.size, + config::binary, 4096 * 4)) + continue; + + if (config::parse_option(argc, argv, narg, "keygen.width", + params.keygen.width, 1, 64)) + continue; + if (config::parse_option(argc, argv, narg, "keygen.mesh", + params.keygen.mesh, 1, 64)) + continue; + if (config::parse_option(argc, argv, narg, "keygen.seed", + params.keygen.seed, config::no_scale)) + continue; + if (config::parse_option(argc, argv, narg, "keygen.split", + params.keygen.split, 1, 64)) + continue; + if (config::parse_option(argc, argv, narg, "keygen.rotate", + params.keygen.rotate, 1, 64)) + continue; + if (config::parse_option(argc, argv, narg, "keygen.offset", + params.keygen.offset, config::binary)) + continue; + if (config::parse_option(argc, argv, narg, "keygen.case", &value)) { + keycase_setup(value, params); + continue; + } + + if (config::parse_option(argc, argv, narg, "repeat", params.nrepeat, + config::no_scale)) + continue; + if (config::parse_option(argc, argv, narg, "threads", params.nthreads, + config::no_scale, 1, 64)) + continue; + if (config::parse_option(argc, argv, narg, "timeout", + global::config::timeout_duration_seconds, + config::duration, 1)) + continue; + if (config::parse_option(argc, argv, narg, "keylen.min", params.keylen_min, + config::no_scale, 0, params.keylen_max)) + continue; + if (config::parse_option(argc, argv, narg, "keylen.max", params.keylen_max, + config::no_scale, params.keylen_min, + mdbx_get_maxkeysize(0))) + continue; + if (config::parse_option(argc, argv, narg, "datalen.min", + params.datalen_min, config::no_scale, 0, + params.datalen_max)) + continue; + if (config::parse_option(argc, argv, narg, "datalen.max", + params.datalen_max, config::no_scale, + params.datalen_min, MDBX_MAXDATASIZE)) + continue; + if (config::parse_option(argc, argv, narg, "batch.read", params.batch_read, + config::no_scale, 1)) + continue; + if (config::parse_option(argc, argv, narg, "batch.write", + params.batch_write, config::no_scale, 1)) + continue; + if (config::parse_option(argc, argv, narg, "delay", params.delaystart, + config::duration)) + continue; + if (config::parse_option(argc, argv, narg, "wait4ops", params.waitfor_nops, + config::decimal)) + continue; + if (config::parse_option(argc, argv, narg, "drop", params.drop_table)) + continue; + if (config::parse_option(argc, argv, narg, "dump-config", + global::config::dump_config)) + continue; + if (config::parse_option(argc, argv, narg, "cleanup-before", + global::config::cleanup_before)) + continue; + if (config::parse_option(argc, argv, narg, "cleanup-after", + global::config::cleanup_after)) + continue; + if (config::parse_option(argc, argv, narg, "max-readers", + params.max_readers, config::no_scale, 1, 255)) + continue; + if (config::parse_option(argc, argv, narg, "max-tables", params.max_tables, + config::no_scale, 1, INT16_MAX)) + continue; + + if (config::parse_option(argc, argv, narg, "no-delay", nullptr)) { + params.delaystart = 0; + continue; + } + if (config::parse_option(argc, argv, narg, "no-wait", nullptr)) { + params.waitfor_nops = 0; + continue; + } + if (config::parse_option(argc, argv, narg, "duration", params.test_duration, + config::duration, 1)) { + params.test_nops = 0; + continue; + } + if (config::parse_option(argc, argv, narg, "nops", params.test_nops, + config::decimal, 1)) { + params.test_duration = 0; + continue; + } + if (config::parse_option(argc, argv, narg, "hill", &value, "auto")) { + configure_actor(last_space_id, ac_hill, value, params); + continue; + } + if (config::parse_option(argc, argv, narg, "jitter", nullptr)) { + configure_actor(last_space_id, ac_jitter, value, params); + continue; + } + if (config::parse_option(argc, argv, narg, "dead.reader", nullptr)) { + configure_actor(last_space_id, ac_deadread, value, params); + continue; + } + if (config::parse_option(argc, argv, narg, "dead.writer", nullptr)) { + configure_actor(last_space_id, ac_deadwrite, value, params); + continue; + } + if (config::parse_option(argc, argv, narg, "failfast", + global::config::failfast)) + continue; + if (config::parse_option(argc, argv, narg, "progress", + global::config::progress_indicator)) + continue; + + if (*argv[narg] != '-') + testcase_setup(argv[narg], params, last_space_id); + else + failure("Unknown option '%s'\n", argv[narg]); + } + + if (global::config::dump_config) + config::dump(); + + //-------------------------------------------------------------------------- + + if (global::actors.empty()) { + log_notice("no testcase(s) configured, exiting"); + return EXIT_SUCCESS; + } + + bool failed = false; + global::start_motonic = chrono::now_motonic(); + global::deadline_motonic.fixedpoint = + (global::config::timeout_duration_seconds == 0) + ? chrono::infinite().fixedpoint + : global::start_motonic.fixedpoint + + chrono::from_seconds(global::config::timeout_duration_seconds) + .fixedpoint; + + if (global::config::cleanup_before) + cleanup(); + + if (global::actors.size() == 1) { + logging::setup("main"); + global::singlemode = true; + if (!test_execute(global::actors.front())) + failed = true; + } else { + logging::setup("overlord"); + + log_trace("=== preparing..."); + log_trace(">> osal_setup"); + osal_setup(global::actors); + log_trace("<< osal_setup"); + + for (auto &a : global::actors) { + mdbx_pid_t pid; + log_trace(">> actor_start"); + int rc = osal_actor_start(a, pid); + log_trace("<< actor_start"); + if (rc) { + log_trace(">> killall_actors: (%s)", "start failed"); + osal_killall_actors(); + log_trace("<< killall_actors"); + failure("Failed to start actor #%u (%s)\n", a.actor_id, + test_strerror(rc)); + } + global::pid2actor[pid] = &a; + } + + log_trace("=== ready to start..."); + atexit(osal_killall_actors); + log_trace(">> wait4barrier"); + osal_wait4barrier(); + log_trace("<< wait4barrier"); + + size_t left = global::actors.size(); + log_trace("=== polling..."); + while (left > 0) { + unsigned timeout_seconds_left = INT_MAX; + chrono::time now_motonic = chrono::now_motonic(); + if (now_motonic.fixedpoint >= global::deadline_motonic.fixedpoint) + timeout_seconds_left = 0; + else { + chrono::time left_motonic; + left_motonic.fixedpoint = + global::deadline_motonic.fixedpoint - now_motonic.fixedpoint; + timeout_seconds_left = left_motonic.seconds(); + } + + mdbx_pid_t pid; + int rc = osal_actor_poll(pid, timeout_seconds_left); + if (rc) + failure("Poll error: %s (%d)\n", test_strerror(rc), rc); + + if (pid) { + actor_status status = osal_actor_info(pid); + actor_config *actor = global::pid2actor.at(pid); + if (!actor) + continue; + + log_info("actor #%u, id %d, pid %u: %s\n", actor->actor_id, + actor->space_id, pid, status2str(status)); + if (status > as_running) { + left -= 1; + if (status != as_successful) { + if (global::config::failfast && !failed) { + log_trace(">> killall_actors: (%s)", "failfast"); + osal_killall_actors(); + log_trace("<< killall_actors"); + } + failed = true; + } + } + } else { + if (timeout_seconds_left == 0) + failure("Timeout\n"); + } + } + log_trace("=== done..."); + } + + log_notice("RESULT: %s\n", failed ? "Failed" : "Successful"); + if (global::config::cleanup_before) { + if (failed) + log_info("skip cleanup"); + else + cleanup(); + } + return failed ? EXIT_FAILURE : EXIT_SUCCESS; +} |