Skip to content

Commit

Permalink
introduce limestone test-double for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
ban-nobuhiro committed Jan 30, 2025
1 parent 95a9fc4 commit 90de28e
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 1 deletion.
27 changes: 26 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2019 tsurugi project.
# Copyright 2019-2025 tsurugi project.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -132,9 +132,20 @@ else ()
"datastore/two_recovery/*.cpp"
"datastore/three_recovery/*.cpp"
)
file (GLOB TEST_WITH_LIMESTONE_DOUBLE_SOURCES
"test_double/*.cpp"
)
endif()
endif()

file (GLOB LIMESTONE_DOUBLE_SOURCES "test_double/doubles/limestone_double.cpp")
add_library(limestone-double ${LIMESTONE_DOUBLE_SOURCES})
target_link_libraries(limestone-double PRIVATE limestone)
target_include_directories(limestone-double
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
)

register_tests(
TARGET shirakami
DEPENDS
Expand Down Expand Up @@ -179,5 +190,19 @@ else ()
SOURCES ${LOGGING_TEST_SOURCES}
TEST_LABELS "LOGGING"
)
register_tests(
TARGET shirakami
DEPENDS
shirakami-impl
PRIVATE glog::glog
PRIVATE ${tbb_prefix}tbb
PRIVATE ${tbb_prefix}tbbmalloc
PRIVATE ${tbb_prefix}tbbmalloc_proxy
PRIVATE yakushima
PRIVATE atomic
PRIVATE limestone-double
SOURCES ${TEST_WITH_LIMESTONE_DOUBLE_SOURCES}
TEST_LABELS "LOGGING"
)
endif()
endif ()
85 changes: 85 additions & 0 deletions test/test_double/blob_mock_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

#include "concurrency_control/include/session.h"

#include "gtest/gtest.h"
#include "glog/logging.h"

#include "test_tool.h"
#include "doubles/limestone_double.h"

namespace shirakami::testing {

using namespace shirakami;

class blob_mock_test : public ::testing::Test {
public:
static void call_once_f() {
google::InitGoogleLogging("shirakami-test-test_double-blob_mock_test");
// FLAGS_stderrthreshold = 0; // output more than INFO
}
void SetUp() override {
std::call_once(init_google_, call_once_f);
init(); // NOLINT
}

void TearDown() override {
fin();
test_double::log_channel_add_entry1::hook_func = nullptr;
test_double::log_channel_add_entry2::hook_func = nullptr;
test_double::datastore_switch_available_boundary_version::hook_func = nullptr;
}

private:
static inline std::once_flag init_google_;
};

TEST_F(blob_mock_test, insert_update_blob) {
std::vector<std::pair<std::string, std::vector<limestone::api::blob_id_type>>> added;
// prepare

// test double
test_double::log_channel_add_entry1::hook_func = [&added] (
[[maybe_unused]] test_double::log_channel_add_entry1::orig_type orig_func,
[[maybe_unused]] limestone::api::log_channel* this_ptr,
[[maybe_unused]] limestone::api::storage_id_type storage_id,
[[maybe_unused]] std::string_view key, [[maybe_unused]] std::string_view value,
[[maybe_unused]] limestone::api::write_version_type write_version) -> void {
VLOG(40) << "add_entry 1 storage_id:" << storage_id << " key:" << key;
added.emplace_back(key, std::vector<limestone::api::blob_id_type>{});
};
test_double::log_channel_add_entry2::hook_func = [&added] (
[[maybe_unused]] test_double::log_channel_add_entry2::orig_type orig_func,
[[maybe_unused]] limestone::api::log_channel* this_ptr,
[[maybe_unused]] limestone::api::storage_id_type storage_id,
[[maybe_unused]] std::string_view key, [[maybe_unused]] std::string_view value,
[[maybe_unused]] limestone::api::write_version_type write_version,
[[maybe_unused]] const std::vector<limestone::api::blob_id_type>& large_objects) -> void {
VLOG(40) << "add_entry 2 storage_id:" << storage_id << " key:" << key << shirakami_vecstring(large_objects);
added.emplace_back(key, large_objects);
};

Storage st{};
create_storage("", st); // N.B. create_storage may call add_entry
Token s{};
ASSERT_OK(enter(s));
added.clear();

// test and verify
ASSERT_EQ(added.size(), 0);
ASSERT_OK(tx_begin({s, transaction_options::transaction_type::SHORT}));
const blob_id_type b1[2] = {11, 22};
ASSERT_OK(insert(s, st, "k", "v", b1, 2));
const blob_id_type b2[3] = {99, 88, 77};
ASSERT_OK(update(s, st, "k", "v1", b2, 3));
ASSERT_OK(commit(s));
{
ASSERT_EQ(added.size(), 1);
auto lobs = std::get<1>(added.at(0));
EXPECT_EQ(std::vector(std::begin(b2), std::end(b2)), lobs);
}

// cleanup
ASSERT_OK(leave(s));
}

} // namespace shirakami::testing
111 changes: 111 additions & 0 deletions test/test_double/doubles/limestone_double.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#define _GNU_SOURCE 1
#include <dlfcn.h>

#include "glog/logging.h"

#include "limestone_double.h"

#define EXTERN_C 1

namespace test_double::log_channel_add_entry1 { hook_type hook_func = nullptr; }
namespace test_double::log_channel_add_entry2 { hook_type hook_func = nullptr; }
namespace test_double::datastore_switch_available_boundary_version { hook_type hook_func = nullptr; }

#if EXTERN_C == 0

namespace limestone::api {

void log_channel::add_entry(storage_id_type storage_id, std::string_view key, std::string_view value, write_version_type write_version) {
using namespace test_double::log_channel_add_entry1;
static std::atomic<orig_type> orig_ptr{nullptr};
if (!orig_ptr) {
orig_ptr = reinterpret_cast<orig_type>(dlsym(RTLD_NEXT, sym_name));
}
LOG_IF(FATAL, !orig_ptr) << "dlsym " << sym_name << " failed";
if (hook_func) {
return hook_func(orig_ptr.load(), this, storage_id, key, value, write_version);
} else {
return orig_ptr.load()(this, storage_id, key, value, write_version);
}
}

void log_channel::add_entry(storage_id_type storage_id, std::string_view key, std::string_view value, write_version_type write_version, const std::vector<blob_id_type>& large_objects) {
using namespace test_double::log_channel_add_entry2;
static std::atomic<orig_type> orig_ptr{nullptr};
if (!orig_ptr) {
orig_ptr = reinterpret_cast<orig_type>(dlsym(RTLD_NEXT, sym_name));
}
LOG_IF(FATAL, !orig_ptr) << "dlsym " << sym_name << " failed";
if (hook_func) {
return hook_func(orig_ptr.load(), this, storage_id, key, value, write_version, large_objects);
} else {
return orig_ptr.load()(this, storage_id, key, value, write_version, large_objects);
}
}

void datastore::switch_available_boundary_version(write_version_type version) {
using namespace test_double::datastore_switch_available_boundary_version;
static std::atomic<orig_type> orig_ptr{nullptr};
if (!orig_ptr) {
orig_ptr = reinterpret_cast<orig_type>(dlsym(RTLD_NEXT, sym_name));
}
LOG_IF(FATAL, !orig_ptr) << "dlsym " << sym_name << " failed";
if (hook_func) {
return hook_func(orig_ptr.load(), this, version);
} else {
return orig_ptr.load()(this, version);
}
}

}

#else

extern "C" {

void _ZN9limestone3api11log_channel9add_entryEmSt17basic_string_viewIcSt11char_traitsIcEES5_NS0_18write_version_typeE(limestone::api::log_channel* this_ptr, limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version) {
using namespace test_double::log_channel_add_entry1;
static std::atomic<orig_type> orig_ptr{nullptr};
if (!orig_ptr) {
orig_ptr = reinterpret_cast<orig_type>(dlsym(RTLD_NEXT, sym_name));
}
LOG_IF(FATAL, !orig_ptr) << "dlsym " << sym_name << " failed";
if (hook_func) {
return hook_func(orig_ptr.load(), this_ptr, storage_id, key, value, write_version);
} else {
return orig_ptr.load()(this_ptr, storage_id, key, value, write_version);
}
}

void _ZN9limestone3api11log_channel9add_entryEmSt17basic_string_viewIcSt11char_traitsIcEES5_NS0_18write_version_typeERKSt6vectorImSaImEE(limestone::api::log_channel* this_ptr, limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version, const std::vector<limestone::api::blob_id_type>& large_objects) {
using namespace test_double::log_channel_add_entry2;
static std::atomic<orig_type> orig_ptr{nullptr};
if (!orig_ptr) {
orig_ptr = reinterpret_cast<orig_type>(dlsym(RTLD_NEXT, sym_name));
}
LOG_IF(FATAL, !orig_ptr) << "dlsym " << sym_name << " failed";
if (hook_func) {
return hook_func(orig_ptr.load(), this_ptr, storage_id, key, value, write_version, large_objects);
} else {
return orig_ptr.load()(this_ptr, storage_id, key, value, write_version, large_objects);
}
}

void _ZN9limestone3api9datastore33switch_available_boundary_versionENS0_18write_version_typeE(limestone::api::datastore* this_ptr, limestone::api::write_version_type version) {
using namespace test_double::datastore_switch_available_boundary_version;
static std::atomic<orig_type> orig_ptr{nullptr};
if (!orig_ptr) {
orig_ptr = reinterpret_cast<orig_type>(dlsym(RTLD_NEXT, sym_name));
}
LOG_IF(FATAL, !orig_ptr) << "dlsym " << sym_name << " failed";
if (hook_func) {
return hook_func(orig_ptr.load(), this_ptr, version);
} else {
return orig_ptr.load()(this_ptr, version);
}
}

}
#endif

#undef EXTERN_C
41 changes: 41 additions & 0 deletions test/test_double/doubles/limestone_double.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include "limestone/api/write_version_type.h"
#include "limestone/api/datastore.h"

namespace test_double {

// MEMBER_FUNC_DEF
// nickname log_channel_add_entry1
// cppname void limestone::api::log_channel::add_entry(limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version)
// cname _ZN9limestone3api11log_channel9add_entryEmSt17basic_string_viewIcSt11char_traitsIcEES5_NS0_18write_version_typeE
namespace log_channel_add_entry1 {
using orig_type = void(*)(limestone::api::log_channel*, limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version);
using hook_type = std::function<void(orig_type orig_func, limestone::api::log_channel*, limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version)>;
extern hook_type hook_func;
constexpr const char sym_name[] = "_ZN9limestone3api11log_channel9add_entryEmSt17basic_string_viewIcSt11char_traitsIcEES5_NS0_18write_version_typeE";
}

// MEMBER_FUNC_DEF
// nickname log_channel_add_entry2
// cppname void limestone::api::log_channel::add_entry(limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version, const std::vector<limestone::api::blob_id_type>& large_objects)
// cname _ZN9limestone3api11log_channel9add_entryEmSt17basic_string_viewIcSt11char_traitsIcEES5_NS0_18write_version_typeERKSt6vectorImSaImEE
namespace log_channel_add_entry2 {
using orig_type = void(*)(limestone::api::log_channel*, limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version, const std::vector<limestone::api::blob_id_type>& large_objects);
using hook_type = std::function<void(orig_type orig_func, limestone::api::log_channel*, limestone::api::storage_id_type storage_id, std::string_view key, std::string_view value, limestone::api::write_version_type write_version, const std::vector<limestone::api::blob_id_type>& large_objects)>;
extern hook_type hook_func;
constexpr const char sym_name[] = "_ZN9limestone3api11log_channel9add_entryEmSt17basic_string_viewIcSt11char_traitsIcEES5_NS0_18write_version_typeERKSt6vectorImSaImEE";
}

// MEMBER_FUNC_DEF
// nickname datastore_switch_available_boundary_version
// cppname void limestone::api::datastore::switch_available_boundary_version(limestone::api::write_version_type version)
// cname _ZN9limestone3api9datastore33switch_available_boundary_versionENS0_18write_version_typeE
namespace datastore_switch_available_boundary_version {
using orig_type = void(*)(limestone::api::datastore*, limestone::api::write_version_type write_version);
using hook_type = std::function<void(orig_type orig_func, limestone::api::datastore*, limestone::api::write_version_type write_version)>;
extern hook_type hook_func;
constexpr const char sym_name[] = "_ZN9limestone3api9datastore33switch_available_boundary_versionENS0_18write_version_typeE";
}

}

0 comments on commit 90de28e

Please sign in to comment.