From b48958c17720a2f6d4936d9d3b8cb3bf873ae1b0 Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Mon, 15 Mar 2021 20:52:18 +0300 Subject: [PATCH] mdbx-test: add `registry` for test cases. Change-Id: Ie9f069dbe6846af170628945db9897ec690fc3da --- test/append.cc | 8 +++ test/cases.cc | 32 ++++++++++++ test/copy.cc | 12 +++++ test/dead.cc | 17 +++++++ test/hill.cc | 48 ++++++++++-------- test/jitter.cc | 11 ++++ test/nested.cc | 31 ++++++++++++ test/test.cc | 35 +------------ test/test.h | 133 +++++++++++++++++-------------------------------- test/try.cc | 8 +++ test/ttl.cc | 1 + 11 files changed, 194 insertions(+), 142 deletions(-) diff --git a/test/append.cc b/test/append.cc index 837db7bc..f26c0345 100644 --- a/test/append.cc +++ b/test/append.cc @@ -14,6 +14,14 @@ #include "test.h" +class testcase_append : public testcase { +public: + testcase_append(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run() override; +}; +REGISTER_TESTCASE(append); + bool testcase_append::run() { int err = db_open__begin__table_create_open_clean(dbi); if (unlikely(err != MDBX_SUCCESS)) { diff --git a/test/cases.cc b/test/cases.cc index 72da9899..cd5b6af2 100644 --- a/test/cases.cc +++ b/test/cases.cc @@ -14,6 +14,38 @@ #include "test.h" +registry *registry::instance() { + static registry *singleton; + if (!singleton) + singleton = new registry(); + return singleton; +} + +bool registry::add(const record *item) { + auto const singleton = instance(); + assert(singleton->name2id.count(std::string(item->name)) == 0); + assert(singleton->id2record.count(item->id) == 0); + if (singleton->name2id.count(std::string(item->name)) + + singleton->id2record.count(item->id) == + 0) { + singleton->name2id[std::string(item->name)] = item; + singleton->id2record[item->id] = item; + return true; + } + return false; +} + +testcase *registry::create_actor(const actor_config &config, + const mdbx_pid_t pid) { + return instance()->id2record.at(config.testcase)->constructor(config, pid); +} + +bool registry::review_actor_config(actor_config &config) { + return instance()->id2record.at(config.testcase)->review_config(config); +} + +//----------------------------------------------------------------------------- + void configure_actor(unsigned &last_space_id, const actor_testcase testcase, const char *space_id_cstr, actor_params params) { // silently fix key/data length for fixed-length modes diff --git a/test/copy.cc b/test/copy.cc index 9e07af7b..37c58a24 100644 --- a/test/copy.cc +++ b/test/copy.cc @@ -1,5 +1,17 @@ #include "test.h" +class testcase_copy : public testcase { + const std::string copy_pathname; + void copy_db(const bool with_compaction); + +public: + testcase_copy(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid), + copy_pathname(config.params.pathname_db + "-copy") {} + bool run() override; +}; +REGISTER_TESTCASE(copy); + void testcase_copy::copy_db(const bool with_compaction) { int err = mdbx_env_delete(copy_pathname.c_str(), MDBX_ENV_JUST_DELETE); if (err != MDBX_SUCCESS && err != MDBX_RESULT_TRUE) diff --git a/test/dead.cc b/test/dead.cc index a4acea77..7f388506 100644 --- a/test/dead.cc +++ b/test/dead.cc @@ -14,6 +14,14 @@ #include "test.h" +class testcase_deadread : public testcase { +public: + testcase_deadread(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run() override; +}; +REGISTER_TESTCASE(deadread); + bool testcase_deadread::run() { db_open(); txn_begin(true); @@ -25,6 +33,15 @@ bool testcase_deadread::run() { //----------------------------------------------------------------------------- +class testcase_deadwrite : public testcase { +public: + testcase_deadwrite(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run() override; +}; + +REGISTER_TESTCASE(deadwrite); + bool testcase_deadwrite::run() { db_open(); txn_begin(false); diff --git a/test/hill.cc b/test/hill.cc index 16da22e4..58b2271b 100644 --- a/test/hill.cc +++ b/test/hill.cc @@ -14,6 +14,34 @@ #include "test.h" +/* LY: тест "холмиком": + * - сначала наполняем таблицу циклическими CRUD-манипуляциями, + * которые в каждом цикле делают несколько операций, включая удаление, + * но в результате добавляют записи. + * - затем очищаем таблицу также CRUD-манипуляциями, но уже с другой + * пропорцией удалений. + * + * При этом очень многое зависит от порядка перебора ключей: + * - (псевдо)случайное распределение требуется лишь для полноты картины, + * но в целом не покрывает важных кейсов. + * - кроме (псевдо)случайного перебора требуется последовательное + * итерирование ключей интервалами различной ширины, с тем чтобы + * проверить различные варианты как разделения, так и слияния страниц + * внутри движка. + * - при не-уникальных ключах (MDBX_DUPSORT с подвариантами), для каждого + * повтора внутри движка формируется вложенное btree-дерево, + * соответственно требуется соблюдение аналогичных принципов + * итерирования для значений. + */ + +class testcase_hill : public testcase { +public: + testcase_hill(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run() override; +}; +REGISTER_TESTCASE(hill); + bool testcase_hill::run() { int err = db_open__begin__table_create_open_clean(dbi); if (unlikely(err != MDBX_SUCCESS)) { @@ -23,26 +51,6 @@ bool testcase_hill::run() { speculum.clear(); speculum_committed.clear(); - /* LY: тест "холмиком": - * - сначала наполняем таблицу циклическими CRUD-манипуляциями, - * которые в каждом цикле делают несколько операций, включая удаление, - * но в результате добавляют записи. - * - затем очищаем таблицу также CRUD-манипуляциями, но уже с другой - * пропорцией удалений. - * - * При этом очень многое зависит от порядка перебора ключей: - * - (псевдо)случайное распределение требуется лишь для полноты картины, - * но в целом не покрывает важных кейсов. - * - кроме (псевдо)случайного перебора требуется последовательное - * итерирование ключей интервалами различной ширины, с тем чтобы - * проверить различные варианты как разделения, так и слияния страниц - * внутри движка. - * - при не-уникальных ключах (MDBX_DUPSORT с подвариантами), для каждого - * повтора внутри движка формируется вложенное btree-дерево, - * соответственно требуется соблюдение аналогичных принципов - * итерирования для значений. - */ - /* TODO: работа в несколько потоков */ keyvalue_maker.setup(config.params, config.actor_id, 0 /* thread_number */); diff --git a/test/jitter.cc b/test/jitter.cc index da8fb87a..e1a8ff51 100644 --- a/test/jitter.cc +++ b/test/jitter.cc @@ -14,6 +14,17 @@ #include "test.h" +class testcase_jitter : public testcase { +protected: + void check_dbi_error(int expect, const char *stage); + +public: + testcase_jitter(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run() override; +}; +REGISTER_TESTCASE(jitter); + void testcase_jitter::check_dbi_error(int expect, const char *stage) { MDBX_stat stat; int err = mdbx_dbi_stat(txn_guard.get(), dbi, &stat, sizeof(stat)); diff --git a/test/nested.cc b/test/nested.cc index fc30f3f3..9fab5657 100644 --- a/test/nested.cc +++ b/test/nested.cc @@ -34,6 +34,37 @@ * Таким образом имитируется поведение таблицы с TTL: записи стохастически * добавляются и удаляются, и изредка происходят массивные удаления. */ +class testcase_nested : public testcase_ttl { + using inherited = testcase_ttl; + using FIFO = std::deque>; + + uint64_t serial{0}; + unsigned clear_wholetable_passed{0}; + unsigned clear_stepbystep_passed{0}; + unsigned dbfull_passed{0}; + bool keyspace_overflow{false}; + FIFO fifo; + std::stack> stack; + + bool trim_tail(unsigned window_width); + bool grow_head(unsigned head_count); + bool pop_txn(bool abort); + bool pop_txn() { + return pop_txn(inherited::is_nested_txn_available() ? flipcoin_x3() + : flipcoin_x2()); + } + void push_txn(); + bool stochastic_breakable_restart_with_nested(bool force_restart = false); + +public: + testcase_nested(const actor_config &config, const mdbx_pid_t pid) + : inherited(config, pid) {} + bool setup() override; + bool run() override; + bool teardown() override; +}; +REGISTER_TESTCASE(nested); + bool testcase_nested::setup() { if (!inherited::setup()) return false; diff --git a/test/test.cc b/test/test.cc index beac5912..31032bc9 100644 --- a/test/test.cc +++ b/test/test.cc @@ -573,40 +573,7 @@ bool test_execute(const actor_config &config_const) { } try { - std::unique_ptr test; - switch (config.testcase) { - case ac_hill: - test.reset(new testcase_hill(config, pid)); - break; - case ac_deadread: - test.reset(new testcase_deadread(config, pid)); - break; - case ac_deadwrite: - test.reset(new testcase_deadwrite(config, pid)); - break; - case ac_jitter: - test.reset(new testcase_jitter(config, pid)); - break; - case ac_try: - test.reset(new testcase_try(config, pid)); - break; - case ac_copy: - test.reset(new testcase_copy(config, pid)); - break; - case ac_append: - test.reset(new testcase_append(config, pid)); - break; - case ac_ttl: - test.reset(new testcase_ttl(config, pid)); - break; - case ac_nested: - test.reset(new testcase_nested(config, pid)); - break; - default: - test.reset(new testcase(config, pid)); - break; - } - + std::unique_ptr test(registry::create_actor(config, pid)); size_t iter = 0; do { iter++; diff --git a/test/test.h b/test/test.h index 4c00ba79..1ee74758 100644 --- a/test/test.h +++ b/test/test.h @@ -90,12 +90,48 @@ struct cursor_deleter /* : public std::unary_function */ { void operator()(MDBX_cursor *cursor) const { mdbx_cursor_close(cursor); } }; -typedef std::unique_ptr scoped_db_guard; -typedef std::unique_ptr scoped_txn_guard; -typedef std::unique_ptr scoped_cursor_guard; +using scoped_db_guard = std::unique_ptr; +using scoped_txn_guard = std::unique_ptr; +using scoped_cursor_guard = std::unique_ptr; //----------------------------------------------------------------------------- +class testcase; + +class registry { + struct record { + actor_testcase id; + std::string name; + bool (*review_config)(actor_config &); + testcase *(*constructor)(const actor_config &, const mdbx_pid_t); + }; + std::unordered_map name2id; + std::unordered_map id2record; + static bool add(const record *item); + static registry *instance(); + +public: + template struct factory : public record { + factory(const actor_testcase id, const char *name) { + this->id = id; + this->name = name; + review_config = TESTCASE::review; + constructor = [](const actor_config &config, + const mdbx_pid_t pid) -> testcase * { + return new TESTCASE(config, pid); + }; + add(this); + } + }; + static bool review_actor_config(actor_config &config); + static testcase *create_actor(const actor_config &config, + const mdbx_pid_t pid); +}; + +#define REGISTER_TESTCASE(NAME) \ + static registry::factory gRegister_##NAME(ac_##NAME, \ + STRINGIFY(NAME)) + class testcase { protected: #if HAVE_cxx17_std_string_view @@ -252,67 +288,18 @@ public: memset(&last, 0, sizeof(last)); } + static bool review(actor_config &config) { + (void)config; + return true; + } + virtual bool setup(); virtual bool run() { return true; } virtual bool teardown(); virtual ~testcase() {} }; -class testcase_hill : public testcase { -public: - testcase_hill(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - -class testcase_append : public testcase { -public: - testcase_append(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - -class testcase_deadread : public testcase { -public: - testcase_deadread(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - -class testcase_deadwrite : public testcase { -public: - testcase_deadwrite(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - -class testcase_jitter : public testcase { -protected: - void check_dbi_error(int expect, const char *stage); - -public: - testcase_jitter(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - -class testcase_try : public testcase { -public: - testcase_try(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid) {} - bool run() override; -}; - -class testcase_copy : public testcase { - const std::string copy_pathname; - void copy_db(const bool with_compaction); - -public: - testcase_copy(const actor_config &config, const mdbx_pid_t pid) - : testcase(config, pid), - copy_pathname(config.params.pathname_db + "-copy") {} - bool run() override; -}; +//----------------------------------------------------------------------------- class testcase_ttl : public testcase { using inherited = testcase; @@ -331,33 +318,3 @@ public: bool setup() override; bool run() override; }; - -class testcase_nested : public testcase_ttl { - using inherited = testcase_ttl; - using FIFO = std::deque>; - - uint64_t serial{0}; - unsigned clear_wholetable_passed{0}; - unsigned clear_stepbystep_passed{0}; - unsigned dbfull_passed{0}; - bool keyspace_overflow{false}; - FIFO fifo; - std::stack> stack; - - bool trim_tail(unsigned window_width); - bool grow_head(unsigned head_count); - bool pop_txn(bool abort); - bool pop_txn() { - return pop_txn(inherited::is_nested_txn_available() ? flipcoin_x3() - : flipcoin_x2()); - } - void push_txn(); - bool stochastic_breakable_restart_with_nested(bool force_restart = false); - -public: - testcase_nested(const actor_config &config, const mdbx_pid_t pid) - : inherited(config, pid) {} - bool setup() override; - bool run() override; - bool teardown() override; -}; diff --git a/test/try.cc b/test/try.cc index d2aae281..da81e631 100644 --- a/test/try.cc +++ b/test/try.cc @@ -1,5 +1,13 @@ #include "test.h" +class testcase_try : public testcase { +public: + testcase_try(const actor_config &config, const mdbx_pid_t pid) + : testcase(config, pid) {} + bool run() override; +}; +REGISTER_TESTCASE(try); + bool testcase_try::run() { db_open(); assert(!txn_guard); diff --git a/test/ttl.cc b/test/ttl.cc index 2493276f..c4ac6c0f 100644 --- a/test/ttl.cc +++ b/test/ttl.cc @@ -31,6 +31,7 @@ * Таким образом имитируется поведение таблицы с TTL: записи стохастически * добавляются и удаляются, но изредка происходит массивное удаление. */ +REGISTER_TESTCASE(ttl); unsigned testcase_ttl::edge2count(uint64_t edge) { const double rnd = u64_to_double1(prng64_map1_white(edge));