Files
coopos/unittests/partitioned_block_log_tests.cpp
2023-08-01 18:55:03 -04:00

521 lines
24 KiB
C++

#include <sstream>
#include <eosio/chain/block_log.hpp>
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/snapshot.hpp>
#include <eosio/testing/tester.hpp>
// #include <boost/mpl/list.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <contracts.hpp>
#include <fc/io/cfile.hpp>
#include <snapshots.hpp>
BOOST_AUTO_TEST_SUITE(partitioned_block_log_tests)
void remove_existing_states(eosio::chain::controller::config& config) {
auto state_path = config.state_dir;
remove_all(state_path);
std::filesystem::create_directories(state_path);
}
std::filesystem::path get_retained_dir(const eosio::chain::controller::config& cfg) {
std::filesystem::path retained_dir;
auto paritioned_config = std::get_if<eosio::chain::partitioned_blocklog_config>(&cfg.blog);
if (paritioned_config) {
retained_dir = paritioned_config->retained_dir;
if (retained_dir.is_relative())
retained_dir = cfg.blocks_dir / retained_dir;
}
return retained_dir;
}
struct restart_from_block_log_test_fixture {
eosio::testing::tester chain;
uint32_t cutoff_block_num;
restart_from_block_log_test_fixture() {
using namespace eosio::chain;
chain.create_account("replay1"_n);
chain.produce_blocks(1);
chain.create_account("replay2"_n);
chain.produce_blocks(1);
chain.create_account("replay3"_n);
chain.produce_blocks(1);
auto cutoff_block = chain.produce_block();
cutoff_block_num = cutoff_block->block_num();
chain.produce_blocks(10);
BOOST_REQUIRE_NO_THROW(chain.control->get_account("replay1"_n));
BOOST_REQUIRE_NO_THROW(chain.control->get_account("replay2"_n));
BOOST_REQUIRE_NO_THROW(chain.control->get_account("replay3"_n));
chain.close();
}
void restart_chain() {
eosio::chain::controller::config copied_config = chain.get_config();
auto genesis = eosio::chain::block_log::extract_genesis_state(copied_config.blocks_dir,
get_retained_dir(copied_config));
BOOST_REQUIRE(genesis);
copied_config.blog =
eosio::chain::basic_blocklog_config{};
// remove the state files to make sure we are starting from block log
remove_existing_states(copied_config);
eosio::testing::tester from_block_log_chain(copied_config, *genesis);
using namespace eosio::chain;
BOOST_REQUIRE_NO_THROW(from_block_log_chain.control->get_account("replay1"_n));
BOOST_REQUIRE_NO_THROW(from_block_log_chain.control->get_account("replay2"_n));
BOOST_REQUIRE_NO_THROW(from_block_log_chain.control->get_account("replay3"_n));
}
};
BOOST_AUTO_TEST_CASE(test_split_log) {
fc::temp_directory temp_dir;
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog = eosio::chain::partitioned_blocklog_config{ .archive_dir = "archive",
.stride = 20,
.max_retained_files = 5 };
},
true);
chain.produce_blocks(150);
auto blocks_dir = chain.get_config().blocks_dir;
auto blocks_archive_dir = blocks_dir / "archive";
BOOST_CHECK(std::filesystem::exists(blocks_archive_dir / "blocks-1-20.log"));
BOOST_CHECK(std::filesystem::exists(blocks_archive_dir / "blocks-1-20.index"));
BOOST_CHECK(std::filesystem::exists(blocks_archive_dir / "blocks-21-40.log"));
BOOST_CHECK(std::filesystem::exists(blocks_archive_dir / "blocks-21-40.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-41-60.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-41-60.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-61-80.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-61-80.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-81-100.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-81-100.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-101-120.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-101-120.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-121-140.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-121-140.index"));
BOOST_CHECK(!chain.control->fetch_block_by_number(40));
BOOST_CHECK(chain.control->fetch_block_by_number(81)->block_num() == 81u);
BOOST_CHECK(chain.control->fetch_block_by_number(90)->block_num() == 90u);
BOOST_CHECK(chain.control->fetch_block_by_number(100)->block_num() == 100u);
BOOST_CHECK(chain.control->fetch_block_by_number(41)->block_num() == 41u);
BOOST_CHECK(chain.control->fetch_block_by_number(50)->block_num() == 50u);
BOOST_CHECK(chain.control->fetch_block_by_number(60)->block_num() == 60u);
BOOST_CHECK(chain.control->fetch_block_by_number(121)->block_num() == 121u);
BOOST_CHECK(chain.control->fetch_block_by_number(130)->block_num() == 130u);
BOOST_CHECK(chain.control->fetch_block_by_number(140)->block_num() == 140u);
BOOST_CHECK(chain.control->fetch_block_by_number(145)->block_num() == 145u);
BOOST_CHECK(!chain.control->fetch_block_by_number(160));
}
BOOST_AUTO_TEST_CASE(test_split_log_zero_retained_file) {
fc::temp_directory temp_dir;
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog = eosio::chain::partitioned_blocklog_config{
.retained_dir = "retained", .archive_dir = "archive", .stride = 50, .max_retained_files = 0
};
},
true);
chain.produce_blocks(150);
auto blocks_dir = chain.get_config().blocks_dir;
auto retained_dir = blocks_dir / "retained";
auto archive_dir = blocks_dir / "archive";
BOOST_CHECK(std::filesystem::is_empty(retained_dir));
BOOST_CHECK(std::filesystem::exists(archive_dir / "blocks-1-50.log"));
BOOST_CHECK(std::filesystem::exists(archive_dir / "blocks-1-50.index"));
BOOST_CHECK(std::filesystem::exists(archive_dir / "blocks-51-100.log"));
BOOST_CHECK(std::filesystem::exists(archive_dir / "blocks-51-100.index"));
BOOST_CHECK(std::filesystem::exists(archive_dir / "blocks-101-150.log"));
BOOST_CHECK(std::filesystem::exists(archive_dir / "blocks-101-150.index"));
}
BOOST_AUTO_TEST_CASE(test_split_log_all_in_retained_new_default) {
fc::temp_directory temp_dir;
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog = eosio::chain::partitioned_blocklog_config{ .retained_dir = "retained",
.archive_dir = "archive",
.stride = 50 };
},
true);
chain.produce_blocks(150);
auto blocks_dir = chain.get_config().blocks_dir;
auto retained_dir = blocks_dir / "retained";
auto archive_dir = blocks_dir / "archive";
BOOST_CHECK(std::filesystem::is_empty(archive_dir));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-1-50.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-1-50.index"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-51-100.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-51-100.index"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-101-150.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-101-150.index"));
}
BOOST_AUTO_TEST_CASE(test_split_log_util1) {
fc::temp_directory temp_dir;
eosio::testing::tester chain;
chain.produce_blocks(160);
uint32_t head_block_num = chain.control->head_block_num();
eosio::chain::controller::config copied_config = chain.get_config();
auto genesis = eosio::chain::block_log::extract_genesis_state(chain.get_config().blocks_dir,
get_retained_dir(chain.get_config()));
BOOST_REQUIRE(genesis);
chain.close();
auto blocks_dir = chain.get_config().blocks_dir;
auto retained_dir = blocks_dir / "retained";
eosio::chain::block_log::split_blocklog(blocks_dir, retained_dir, 50);
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-1-50.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-1-50.index"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-51-100.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-51-100.index"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-101-150.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-101-150.index"));
char buf[64];
snprintf(buf, 64, "blocks-151-%u.log", head_block_num - 1);
std::filesystem::path last_block_file = retained_dir / buf;
snprintf(buf, 64, "blocks-151-%u.index", head_block_num - 1);
std::filesystem::path last_index_file = retained_dir / buf;
BOOST_CHECK(std::filesystem::exists(last_block_file));
BOOST_CHECK(std::filesystem::exists(last_index_file));
std::filesystem::rename(last_block_file, blocks_dir / "blocks.log");
std::filesystem::rename(last_index_file, blocks_dir / "blocks.index");
// remove the state files to make sure we are starting from block log
remove_existing_states(copied_config);
// we need to remove the reversible blocks so that new blocks can be produced from the new chain
std::filesystem::remove_all(copied_config.blocks_dir / "reversible");
copied_config.blog = eosio::chain::partitioned_blocklog_config{ .retained_dir = retained_dir,
.stride = 50,
.max_retained_files = 5 };
eosio::testing::tester from_block_log_chain(copied_config, *genesis);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(1)->block_num() == 1u);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(75)->block_num() == 75u);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(100)->block_num() == 100u);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(150)->block_num() == 150u);
}
BOOST_AUTO_TEST_CASE(test_split_log_no_archive) {
fc::temp_directory temp_dir;
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog =
eosio::chain::partitioned_blocklog_config{ .archive_dir = "", .stride = 10, .max_retained_files = 5 };
},
true);
chain.produce_blocks(75);
auto blocks_dir = chain.get_config().blocks_dir;
auto blocks_archive_dir = blocks_dir;
BOOST_CHECK(!std::filesystem::exists(blocks_archive_dir / "blocks-1-10.log"));
BOOST_CHECK(!std::filesystem::exists(blocks_archive_dir / "blocks-1-10.index"));
BOOST_CHECK(!std::filesystem::exists(blocks_archive_dir / "blocks-11-20.log"));
BOOST_CHECK(!std::filesystem::exists(blocks_archive_dir / "blocks-11-20.index"));
BOOST_CHECK(!std::filesystem::exists(blocks_dir / "blocks-1-10.log"));
BOOST_CHECK(!std::filesystem::exists(blocks_dir / "blocks-1-10.index"));
BOOST_CHECK(!std::filesystem::exists(blocks_dir / "blocks-11-20.log"));
BOOST_CHECK(!std::filesystem::exists(blocks_dir / "blocks-11-20.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-21-30.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-21-30.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-31-40.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-31-40.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-41-50.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-41-50.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-51-60.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-51-60.index"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-61-70.log"));
BOOST_CHECK(std::filesystem::exists(blocks_dir / "blocks-61-70.index"));
BOOST_CHECK(!chain.control->fetch_block_by_number(10));
BOOST_CHECK(chain.control->fetch_block_by_number(70));
BOOST_CHECK(!chain.control->fetch_block_by_number(80));
}
void split_log_replay(uint32_t replay_max_retained_block_files) {
fc::temp_directory temp_dir;
const uint32_t stride = 20;
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog = eosio::chain::partitioned_blocklog_config{ .stride = stride, .max_retained_files = 10 };
},
true);
chain.produce_blocks(150);
auto copied_config = chain.get_config();
auto genesis =
eosio::chain::block_log::extract_genesis_state(copied_config.blocks_dir, get_retained_dir(copied_config));
BOOST_REQUIRE(genesis);
chain.close();
// remove the state files to make sure we are starting from block log
remove_existing_states(copied_config);
// we need to remove the reversible blocks so that new blocks can be produced from the new chain
std::filesystem::remove_all(copied_config.blocks_dir / "reversible");
copied_config.blog =
eosio::chain::partitioned_blocklog_config{ .stride = stride,
.max_retained_files = replay_max_retained_block_files };
eosio::testing::tester from_block_log_chain(copied_config, *genesis);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(1)->block_num() == 1);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(75)->block_num() == 75);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(100)->block_num() == 100);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(150)->block_num() == 150);
// produce new blocks to cross the blocks_log_stride boundary
from_block_log_chain.produce_blocks(stride);
const auto previous_chunk_end_block_num = (from_block_log_chain.control->head_block_num() / stride) * stride;
const auto num_removed_blocks = std::min(stride * replay_max_retained_block_files, previous_chunk_end_block_num);
const auto min_retained_block_number = previous_chunk_end_block_num - num_removed_blocks + 1;
if (min_retained_block_number > 1) {
// old blocks beyond the max_retained_block_files will no longer be available
BOOST_CHECK(!from_block_log_chain.control->fetch_block_by_number(min_retained_block_number - 1));
}
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(min_retained_block_number)->block_num() ==
min_retained_block_number);
}
BOOST_AUTO_TEST_CASE(test_split_log_replay_retained_block_files_10) { split_log_replay(10); }
BOOST_AUTO_TEST_CASE(test_split_log_replay_retained_block_files_5) { split_log_replay(5); }
BOOST_AUTO_TEST_CASE(test_split_log_replay_retained_block_files_1) { split_log_replay(1); }
BOOST_AUTO_TEST_CASE(test_split_log_replay_retained_block_files_0) { split_log_replay(0); }
BOOST_AUTO_TEST_CASE(test_restart_without_blocks_log_file) {
fc::temp_directory temp_dir;
const uint32_t stride = 20;
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog = eosio::chain::partitioned_blocklog_config{ .stride = stride, .max_retained_files = 10 };
},
true);
chain.produce_blocks(160);
eosio::chain::controller::config copied_config = chain.get_config();
auto genesis = eosio::chain::block_log::extract_genesis_state(chain.get_config().blocks_dir, get_retained_dir(copied_config));
BOOST_REQUIRE(genesis);
chain.close();
// remove the state files to make sure we are starting from block log
remove_existing_states(copied_config);
// we need to remove the reversible blocks so that new blocks can be produced from the new chain
std::filesystem::remove_all(copied_config.blocks_dir / "reversible");
std::filesystem::remove(copied_config.blocks_dir / "blocks.log");
std::filesystem::remove(copied_config.blocks_dir / "blocks.index");
copied_config.blog = eosio::chain::partitioned_blocklog_config{ .stride = stride, .max_retained_files = 10 };
eosio::testing::tester from_block_log_chain(copied_config, *genesis);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(1)->block_num() == 1u);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(75)->block_num() == 75u);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(100)->block_num() == 100u);
BOOST_CHECK(from_block_log_chain.control->fetch_block_by_number(160)->block_num() == 160u);
from_block_log_chain.produce_blocks(10);
}
BOOST_FIXTURE_TEST_CASE(start_with_incomplete_head, restart_from_block_log_test_fixture) {
auto& config = chain.get_config();
auto blocks_path = config.blocks_dir;
// write a few random bytes to block log indicating the last block entry is incomplete
fc::cfile logfile;
logfile.set_file_path(config.blocks_dir / "blocks.log");
logfile.open("ab");
const char random_data[] = "12345678901231876983271649837";
logfile.write(random_data, sizeof(random_data));
logfile.close();
BOOST_CHECK_THROW(restart_chain(), eosio::chain::block_log_exception);
}
BOOST_FIXTURE_TEST_CASE(start_with_corrupted_index, restart_from_block_log_test_fixture) {
auto& config = chain.get_config();
auto blocks_path = config.blocks_dir;
// write a few random index to block log indicating the index is corrupted
fc::cfile indexfile;
indexfile.set_file_path(config.blocks_dir / "blocks.index");
indexfile.open("ab");
uint64_t data = UINT64_MAX;
indexfile.write(reinterpret_cast<const char*>(&data), sizeof(data));
indexfile.close();
BOOST_CHECK_THROW(restart_chain(), eosio::chain::block_log_exception);
}
BOOST_FIXTURE_TEST_CASE(start_with_corrupted_log_and_index, restart_from_block_log_test_fixture) {
auto& config = chain.get_config();
auto blocks_path = config.blocks_dir;
// write a few random bytes to block log and index
fc::cfile indexfile;
indexfile.set_file_path(config.blocks_dir / "blocks.index");
indexfile.open("ab");
const char random_index[] = "1234";
indexfile.write(reinterpret_cast<const char*>(&random_index), sizeof(random_index));
fc::cfile logfile;
logfile.set_file_path(config.blocks_dir / "blocks.log");
logfile.open("ab");
const char random_data[] = "12345678901231876983271649837";
logfile.write(random_data, sizeof(random_data));
indexfile.close();
BOOST_CHECK_THROW(restart_chain(), eosio::chain::block_log_exception);
}
struct blocklog_version_setter {
blocklog_version_setter(uint32_t ver) { eosio::chain::block_log::set_initial_version(ver); };
~blocklog_version_setter() { eosio::chain::block_log::set_initial_version(eosio::chain::block_log::max_supported_version); };
};
BOOST_AUTO_TEST_CASE(test_split_from_v1_log) {
fc::temp_directory temp_dir;
blocklog_version_setter set_version(1);
eosio::testing::tester chain(
temp_dir,
[](eosio::chain::controller::config& config) {
config.blog = eosio::chain::partitioned_blocklog_config{ .stride = 20, .max_retained_files = 5 };
},
true);
chain.produce_blocks(75);
BOOST_CHECK(chain.control->fetch_block_by_number(1)->block_num() == 1u);
BOOST_CHECK(chain.control->fetch_block_by_number(21)->block_num() == 21u);
BOOST_CHECK(chain.control->fetch_block_by_number(41)->block_num() == 41u);
BOOST_CHECK(chain.control->fetch_block_by_number(75)->block_num() == 75u);
}
void trim_blocklog_front(uint32_t version) {
blocklog_version_setter set_version(version);
eosio::testing::tester chain;
chain.produce_blocks(10);
chain.produce_blocks(20);
chain.close();
auto blocks_dir = chain.get_config().blocks_dir;
auto old_index_size = std::filesystem::file_size(blocks_dir / "blocks.index");
fc::temp_directory temp1, temp2;
std::filesystem::copy(blocks_dir / "blocks.log", temp1.path() / "blocks.log");
std::filesystem::copy(blocks_dir / "blocks.index", temp1.path() / "blocks.index");
BOOST_REQUIRE_NO_THROW(eosio::chain::block_log::trim_blocklog_front(temp1.path(), temp2.path(), 10));
BOOST_REQUIRE_NO_THROW(eosio::chain::block_log::smoke_test(temp1.path(), 1));
eosio::chain::block_log old_log(blocks_dir, chain.get_config().blog);
eosio::chain::block_log new_log(temp1.path());
// double check if the version has been set to the desired version
BOOST_CHECK(old_log.version() == version);
BOOST_CHECK(new_log.first_block_num() == 10u);
BOOST_CHECK(new_log.head()->block_num() == old_log.head()->block_num());
int num_blocks_trimmed = 10 - 1;
BOOST_CHECK(std::filesystem::file_size(temp1.path() / "blocks.index") == old_index_size - sizeof(uint64_t) * num_blocks_trimmed);
}
BOOST_AUTO_TEST_CASE(test_trim_blocklog_front) { trim_blocklog_front(eosio::chain::block_log::max_supported_version); }
BOOST_AUTO_TEST_CASE(test_trim_blocklog_front_v1) { trim_blocklog_front(1); }
BOOST_AUTO_TEST_CASE(test_trim_blocklog_front_v2) { trim_blocklog_front(2); }
BOOST_AUTO_TEST_CASE(test_blocklog_split_then_merge) {
eosio::testing::tester chain;
chain.produce_blocks(160);
chain.close();
auto blocks_dir = chain.get_config().blocks_dir;
auto retained_dir = blocks_dir / "retained";
fc::temp_directory temp_dir;
BOOST_REQUIRE_NO_THROW(eosio::chain::block_log::trim_blocklog_front(blocks_dir, temp_dir.path(), 50));
BOOST_REQUIRE_NO_THROW(eosio::chain::block_log::trim_blocklog_end(blocks_dir, 150));
BOOST_CHECK_NO_THROW(eosio::chain::block_log::split_blocklog(blocks_dir, retained_dir, 50));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-50-50.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-50-50.index"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-51-100.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-51-100.index"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-101-150.log"));
BOOST_CHECK(std::filesystem::exists(retained_dir / "blocks-101-150.index"));
std::filesystem::remove(blocks_dir / "blocks.log");
std::filesystem::remove(blocks_dir / "blocks.index");
eosio::chain::block_log blog(blocks_dir, eosio::chain::partitioned_blocklog_config{ .retained_dir = retained_dir });
BOOST_CHECK(blog.version() != 0);
BOOST_CHECK_EQUAL(blog.head()->block_num(), 150u);
// test blocklog merge
fc::temp_directory dest_dir;
BOOST_CHECK_NO_THROW(eosio::chain::block_log::merge_blocklogs(retained_dir, dest_dir.path()));
BOOST_CHECK(std::filesystem::exists(dest_dir.path() / "blocks-50-150.log"));
if (std::filesystem::exists(dest_dir.path() / "blocks-50-150.log")) {
std::filesystem::rename(dest_dir.path() / "blocks-50-150.log", dest_dir.path() / "blocks.log");
std::filesystem::rename(dest_dir.path() / "blocks-50-150.index", dest_dir.path() / "blocks.index");
BOOST_CHECK_NO_THROW(eosio::chain::block_log::smoke_test(dest_dir.path(), 1));
}
std::filesystem::remove(dest_dir.path() / "blocks.log");
// test blocklog merge with gap
std::filesystem::remove(retained_dir / "blocks-51-100.log");
std::filesystem::remove(retained_dir / "blocks-51-100.index");
BOOST_CHECK_NO_THROW(eosio::chain::block_log::merge_blocklogs(retained_dir, dest_dir.path()));
BOOST_CHECK(std::filesystem::exists(dest_dir.path() / "blocks-50-50.log"));
BOOST_CHECK(std::filesystem::exists(dest_dir.path() / "blocks-50-50.index"));
BOOST_CHECK(std::filesystem::exists(dest_dir.path() / "blocks-101-150.log"));
BOOST_CHECK(std::filesystem::exists(dest_dir.path() / "blocks-101-150.index"));
}
BOOST_AUTO_TEST_SUITE_END()