blockchain/tests/db_tests/db_tests.cpp
2018-12-27 18:50:45 +03:00

434 lines
13 KiB
C++

// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/multiprecision/cpp_int.hpp>
#include <sstream>
#include "common/db_abstract_accessor.h"
#include "common/db_backend_lmdb.h"
#include "common/util.h"
#include "cache_helper.h"
#include "serialization/binary_archive.h"
#include "serialization/serialization.h"
#include "serialization/multiprecision.h"
#define DB_TEST_SUB_DIR "db_test"
#define DB_TEST_SIMPLE_KV_ACCESSOR_NAME "accessor1"
struct simple_get_set_test
{
typedef tools::db::cached_key_value_accessor<uint64_t, std::string, true, true> accessor_type;
static bool run_test(accessor_type& acc)
{
acc.begin_transaction();
uint64_t key = 2342342342;
std::string data1;
data1 = "lkel;s;lwedck";
acc.set(key, data1);
auto ptr = acc.get(key);
CHECK_AND_ASSERT_MES(ptr.get(), false, "unable to get");
CHECK_AND_ASSERT_MES(*ptr == data1, false, "missmatch");
acc.commit_transaction();
LOG_PRINT_GREEN("Test successful", LOG_LEVEL_0);
return true;
}
};
struct simple_check_count
{
typedef tools::db::cached_key_value_accessor<uint64_t, std::string, true, true> accessor_type;
static bool run_test(accessor_type& acc)
{
acc.begin_transaction();
uint64_t key1 = 2342342342;
std::string data1 = "lkel;s;lwedck";
uint64_t key2 = 2341342232;
std::string data2 = "lkel;s;lffwedck";
acc.set(key1, data1);
acc.set(key2, data2);
uint64_t cz = acc.size();
CHECK_AND_ASSERT_MES(cz == 2, false, "");
acc.erase(key1);
cz = acc.size();
CHECK_AND_ASSERT_MES(cz == 1, false, "");
auto r = acc.find(key1);
CHECK_AND_ASSERT_MES(r == acc.end(), false, "");
auto r2 = acc.find(key2);
CHECK_AND_ASSERT_MES(r2 != acc.end(), false, "");
acc.clear();
cz = acc.size();
CHECK_AND_ASSERT_MES(cz == 0, false, "");
acc.commit_transaction();
LOG_PRINT_GREEN("Test successful", LOG_LEVEL_0);
return true;
}
};
struct check_array_container
{
typedef tools::db::array_accessor<std::string, true> accessor_type;
static bool run_test(accessor_type& acc)
{
acc.begin_transaction();
std::string data1 = "lkel;s;lwedck";
std::string data2 = "lkel;s;lswedck";
acc.push_back(data1);
acc.push_back(data2);
uint64_t cz = acc.size();
CHECK_AND_ASSERT_MES(cz == 2, false, "");
auto vptr = acc.get(0);
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data1, false, "");
vptr = acc.get(1);
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data2, false, "");
std::vector<std::pair<uint64_t, std::string >> enum_res;
acc.enumerate_items([&](uint64_t i, uint64_t i_, const std::string& val){
enum_res.push_back(std::make_pair(i_, val));
return true;
});
CHECK_AND_ASSERT_MES(enum_res.size() == 2, false, "");
CHECK_AND_ASSERT_MES(enum_res[0].first == 0 && enum_res[0].second == data1, false, "");
CHECK_AND_ASSERT_MES(enum_res[1].first == 1 && enum_res[1].second == data2, false, "");
acc.pop_back();
cz = acc.size();
CHECK_AND_ASSERT_MES(cz == 1, false, "");
vptr = acc.get(1);
CHECK_AND_ASSERT_MES(!vptr.get(), false, "");
vptr = acc.back();
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data1, false, "");
acc.commit_transaction();
LOG_PRINT_GREEN("Test successful", LOG_LEVEL_0);
return true;
}
};
struct check_subcontainer_container
{
typedef tools::db::basic_key_to_array_accessor<uint64_t, std::string, true> accessor_type;
static bool run_test(accessor_type& acc)
{
acc.begin_transaction();
uint64_t subcontainer_1 = 22;
uint64_t subcontainer_2 = 0;
std::string data1 = "lkel;s;lwedck";
std::string data2 = "lkel;s;lwedck";
acc.push_back_item(subcontainer_1, data1);
acc.push_back_item(subcontainer_1, data2);
uint64_t cz = acc.get_item_size(subcontainer_1);
CHECK_AND_ASSERT_MES(cz == 2, false, "");
cz = acc.get_item_size(subcontainer_2);
CHECK_AND_ASSERT_MES(cz == 0, false, "");
//----------
acc.push_back_item(subcontainer_2, data1);
acc.push_back_item(subcontainer_2, data2);
cz = acc.get_item_size(subcontainer_2);
CHECK_AND_ASSERT_MES(cz == 2, false, "");
auto vptr = acc.get_subitem(subcontainer_1, 0);
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data1, false, "");
vptr = acc.get_subitem(subcontainer_1, 1);
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data2, false, "");
vptr = acc.get_subitem(subcontainer_2, 0);
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data1, false, "");
vptr = acc.get_subitem(subcontainer_2, 1);
CHECK_AND_ASSERT_MES(vptr.get(), false, "");
CHECK_AND_ASSERT_MES(*vptr == data2, false, "");
//===========
acc.pop_back_item(subcontainer_1);
cz = acc.get_item_size(subcontainer_1);
CHECK_AND_ASSERT_MES(cz == 1, false, "");
acc.pop_back_item(subcontainer_2);
cz = acc.get_item_size(subcontainer_2);
CHECK_AND_ASSERT_MES(cz == 1, false, "");
acc.commit_transaction();
LOG_PRINT_GREEN("Test successful", LOG_LEVEL_0);
return true;
}
};
template<class t_test>
bool prepare_db_and_run_test()
{
epee::log_space::log_singletone::set_thread_log_prefix(std::string("[") + typeid(t_test).name() + "]");
LOG_PRINT_MAGENTA("[" << typeid(t_test).name() << "]", LOG_LEVEL_0);
epee::shared_recursive_mutex rw_lock;
tools::db::basic_db_accessor m_db(std::shared_ptr<tools::db::i_db_backend>(new tools::db::lmdb_db_backend()), rw_lock);
typename t_test::accessor_type simple_k_v_accessor(m_db);
std::string test_folder = epee::string_tools::get_current_module_folder() + "/" + DB_TEST_SUB_DIR;
boost::system::error_code ec;
boost::filesystem::remove_all(test_folder, ec);
if (ec)
{
LOG_ERROR("Failed to empty db subdir: " << ec.message());
return false;
}
tools::create_directories_if_necessary(test_folder);
bool r = m_db.open(test_folder);
CHECK_AND_ASSERT_MES(r, 0, "Failed to open DB from " << test_folder);
r = simple_k_v_accessor.init(DB_TEST_SIMPLE_KV_ACCESSOR_NAME);
CHECK_AND_ASSERT_MES(r, 0, "Failed to open DB_TEST_SIMPLE_KV_ACCESSOR_NAME");
return t_test::run_test(simple_k_v_accessor);
}
bool db_cache_test()
{
#define CACHE_TEST_MAX_ALOWED_RECORDS 1000
#define CACHE_TEST_TOTAL_RECORDS 10000
epee::misc_utils::cache_base<true, uint64_t, std::string, CACHE_TEST_MAX_ALOWED_RECORDS> cache;
//std::map<uint64_t, std::string> source;
for (uint64_t i = 0; i != CACHE_TEST_TOTAL_RECORDS; i++)
{
cache.set(i, std::to_string(i));
}
CHECK_AND_ASSERT_MES(cache.size() == CACHE_TEST_MAX_ALOWED_RECORDS, 0, "Wrong cache size");
for (uint64_t i = CACHE_TEST_TOTAL_RECORDS-1; i != 0; i--)
{
std::string str_res;
bool r = cache.get(i, str_res);
if (i < CACHE_TEST_TOTAL_RECORDS - CACHE_TEST_MAX_ALOWED_RECORDS)
{
CHECK_AND_ASSERT_MES(!r, false, "wrong result");
}
else
{
CHECK_AND_ASSERT_MES(r, false, "wrong result");
CHECK_AND_ASSERT_MES(str_res == std::to_string(i), false, "wrong result");
}
}
for (uint64_t i = 0; i != 100; i++)
{
cache.set(i, std::to_string(i));
}
#define VALIDATE_GET(i, expected_val) \
{std::string str_res; bool r = cache.get(i, str_res); if (expected_val){ CHECK_AND_ASSERT_MES(r && str_res == std::to_string(i), false, "wrong condition"); } else { CHECK_AND_ASSERT_MES(!r, false, "wrong condition"); } }
VALIDATE_GET(0, true);
VALIDATE_GET(10, true);
VALIDATE_GET(50, true);
VALIDATE_GET(99, true);
VALIDATE_GET(100, false);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 1, false );
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 2, false);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 10, false);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 50, false);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 99, false);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 100, false);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 101, true);
VALIDATE_GET(CACHE_TEST_TOTAL_RECORDS - 110, true);
return true;
}
struct check_boost_multiprecision_container
{
struct test_mp_struct
{
DEFINE_SERIALIZATION_VERSION(1)
boost::multiprecision::uint128_t some_mp_1;
boost::multiprecision::uint128_t some_mp_2;
std::string some_str;
uint32_t version;
BEGIN_SERIALIZE_OBJECT()
VERSION_ENTRY(version)
FIELD(some_mp_1)
FIELD(some_mp_2)
FIELD(some_str)
END_SERIALIZE()
};
typedef tools::db::basic_key_value_accessor<uint64_t, test_mp_struct, true> accessor_type;
static bool run_test(accessor_type& acc)
{
acc.begin_transaction();
test_mp_struct ts = AUTO_VAL_INIT(ts);
uint64_t a = 0xfddfdfeffddfdfefll;
uint64_t key = 0;
ts.some_mp_1 = a;
ts.some_mp_1 = ts.some_mp_1 * a;
ts.some_mp_2 = ts.some_mp_1 * a;
ts.some_str = ts.some_mp_2.convert_to<std::string>();
acc.set(key, ts);
auto ts2_ptr = acc.get(key);
CHECK_AND_ASSERT_MES(ts2_ptr, false, "");
CHECK_AND_ASSERT_MES(ts2_ptr->some_mp_1 == ts.some_mp_1, false, "");
CHECK_AND_ASSERT_MES(ts2_ptr->some_mp_2 == ts.some_mp_2, false, "");
CHECK_AND_ASSERT_MES(ts2_ptr->some_str == ts.some_str, false, "");
acc.commit_transaction();
LOG_PRINT_GREEN("Test successful", LOG_LEVEL_0);
return true;
}
};
bool solo_container_save_load_test_iteration(bool clear_data, uint64_t& read_value, uint64_t value_to_set)
{
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPABILITY_VERSION 3
#define BLOCKCHAIN_STORAGE_CONTAINER_SOLO_OPTIONS "solo"
typedef tools::db::cached_key_value_accessor<uint64_t, uint64_t, false, true> solo_options_container;
epee::shared_recursive_mutex m_rwlock;
tools::db::basic_db_accessor m_db(std::shared_ptr<tools::db::i_db_backend>(new tools::db::lmdb_db_backend), m_rwlock);
solo_options_container m_db_solo_options(m_db);
tools::db::solo_db_value<uint64_t, uint64_t, solo_options_container> m_db_storage_major_compability_version(BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPABILITY_VERSION, m_db_solo_options);
std::string test_folder = epee::string_tools::get_current_module_folder() + "/" + DB_TEST_SUB_DIR;
bool r = false;
if (clear_data)
{
boost::system::error_code ec;
boost::filesystem::remove_all(test_folder, ec);
CHECK_AND_ASSERT_MES(!ec, false, "Failed to empty db subdir: " << ec.message());
r = tools::create_directories_if_necessary(test_folder);
CHECK_AND_ASSERT_MES(r, false, "create_directories_if_necessary failed");
}
r = m_db.open(test_folder);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize database in folder: " << test_folder);
r = m_db_solo_options.init(BLOCKCHAIN_STORAGE_CONTAINER_SOLO_OPTIONS);
CHECK_AND_ASSERT_MES(r, false, "Unable to init db container");
read_value = m_db_storage_major_compability_version;
m_db.begin_transaction();
m_db_storage_major_compability_version = value_to_set;
m_db.commit_transaction();
m_db.close();
return true;
}
bool solo_container_save_load_test()
{
const char * const test_name = "solo container save/load test";
epee::log_space::log_singletone::set_thread_log_prefix(std::string("[") + test_name + "]");
LOG_PRINT_MAGENTA("[" << test_name << "]", LOG_LEVEL_0);
uint64_t v = 100, expected_v = 0;
bool r = solo_container_save_load_test_iteration(true, v, 1);
CHECK_AND_ASSERT_MES(r, false, "solo_container_test_load_save_iteration failed");
expected_v = 0;
CHECK_AND_ASSERT_MES(v == expected_v, false, "solo_container_save_load_test_iteration returned invalid value from solo container: " << v << ", expected: " << expected_v);
r = solo_container_save_load_test_iteration(false, v, 2);
CHECK_AND_ASSERT_MES(r, false, "solo_container_test_load_save_iteration failed");
expected_v = 1;
CHECK_AND_ASSERT_MES(v == expected_v, false, "solo_container_save_load_test_iteration returned invalid value from solo container: " << v << ", expected: " << expected_v);
r = solo_container_save_load_test_iteration(false, v, 3);
CHECK_AND_ASSERT_MES(r, false, "solo_container_test_load_save_iteration failed");
expected_v = 2;
CHECK_AND_ASSERT_MES(v == expected_v, false, "solo_container_save_load_test_iteration returned invalid value from solo container: " << v << ", expected: " << expected_v);
LOG_PRINT_GREEN("Test successful", LOG_LEVEL_0);
return true;
}
int main(int argc, char* argv[])
{
epee::string_tools::set_module_name_and_folder(argv[0]);
epee::log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
if (!prepare_db_and_run_test<check_boost_multiprecision_container>())
return 0;
if (!db_cache_test())
return 0;
if (!prepare_db_and_run_test<simple_get_set_test>())
return 0;
if (!prepare_db_and_run_test<simple_check_count>())
return 0;
if (!prepare_db_and_run_test<check_array_container>())
return 0;
if (!prepare_db_and_run_test<check_subcontainer_container>())
return 0;
if (!solo_container_save_load_test())
return 0;
return 1;
}