Browse Source

test: adjust ShortCode test suites

master
Dnomd343 2 months ago
parent
commit
9a0cb15d4b
  1. 18
      src/core_test/codec/helper/codec.cc
  2. 179
      src/core_test/codec/short_code.cc
  3. 5
      src/core_test/utility/concurrent.h

18
src/core_test/codec/helper/codec.cc

@ -68,3 +68,21 @@ void raw_code_parallel(std::function<void(std::span<RawCode>)> &&func) {
pool.wait(); pool.wait();
} }
void short_code_parallel(std::function<void(std::span<ShortCode>)> &&func) {
static auto codes = []() {
std::vector<uint32_t> v (klotski::codec::SHORT_CODE_LIMIT);
std::iota(v.begin(), v.end(), 0);
return v;
}();
BS::thread_pool pool;
pool.detach_blocks((uint64_t)0, codes.size(), [func = std::move(func)](auto start, auto end) {
auto span = std::span<uint32_t> {codes.data() + start, end - start};
func(std::bit_cast<std::span<ShortCode>>(span));
}, 16);
pool.wait();
}

179
src/core_test/codec/short_code.cc

@ -5,12 +5,15 @@
#include "helper/codec.h" #include "helper/codec.h"
#include "helper/sample.h" #include "helper/sample.h"
#include "utility/exposer.h" #include "utility/exposer.h"
#include "utility/concurrent.h"
#include "all_cases/all_cases.h" #include "all_cases/all_cases.h"
#include "short_code/short_code.h" #include "short_code/short_code.h"
#include "common_code/common_code.h" #include "common_code/common_code.h"
using klotski::cases::Ranges;
using klotski::cases::AllCases; using klotski::cases::AllCases;
using klotski::cases::BasicRanges; using klotski::cases::BasicRanges;
using klotski::cases::RangesUnion;
using klotski::codec::ShortCode; using klotski::codec::ShortCode;
using klotski::codec::CommonCode; using klotski::codec::CommonCode;
@ -19,11 +22,14 @@ using klotski::cases::ALL_CASES_NUM;
using klotski::cases::ALL_CASES_NUM_; using klotski::cases::ALL_CASES_NUM_;
using klotski::codec::SHORT_CODE_LIMIT; using klotski::codec::SHORT_CODE_LIMIT;
static const auto TEST_THREAD_NUM = 256; constexpr auto TEST_THREAD_NUM = 256;
/// Forcibly modify private variables to reset state. /// Forcibly modify private variables to reset state.
EXPOSE_VAR(AllCases, bool, available_) EXPOSE_VAR(AllCases, bool, available_)
EXPOSE_VAR(BasicRanges, bool, available_) EXPOSE_VAR(BasicRanges, bool, available_)
EXPOSE_STATIC_VAR(ShortCode, bool, fast_)
EXPOSE_STATIC_VAR(ShortCode, const RangesUnion*, cases_)
EXPOSE_STATIC_VAR(ShortCode, std::atomic<const Ranges*>, ranges_)
/// Reset basic ranges build state, note it is thread-unsafe. /// Reset basic ranges build state, note it is thread-unsafe.
void basic_ranges_reset() { void basic_ranges_reset() {
@ -35,20 +41,25 @@ void all_cases_reset() {
exposer::AllCases_available_(AllCases::instance()) = false; exposer::AllCases_available_(AllCases::instance()) = false;
} }
TEST(ShortCode, limit) { void speed_up_reset() {
auto all_cases_num = std::accumulate(ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0); exposer::ShortCode_fast_() = false;
EXPECT_EQ(all_cases_num, SHORT_CODE_LIMIT); exposer::ShortCode_cases_() = nullptr;
exposer::ShortCode_ranges_() = nullptr;
exposer::AllCases_available_(AllCases::instance()) = false;
exposer::BasicRanges_available_(BasicRanges::instance()) = false;
} }
TEST(ShortCode, validity) { TEST(ShortCode, basic) {
EXPECT_FALSE(ShortCode::check(-1)); // out of short code range EXPECT_FALSE(ShortCode::check(-1)); // out of short code range
EXPECT_FALSE(ShortCode::check(29670987)); // out of short code range EXPECT_FALSE(ShortCode::check(29670987)); // out of short code range
EXPECT_FALSE(ShortCode::create(SHORT_CODE_LIMIT).has_value()); // invalid code
EXPECT_FALSE(ShortCode::from_string("R50EH").has_value()); // with invalid `0` EXPECT_FALSE(ShortCode::from_string("R50EH").has_value()); // with invalid `0`
EXPECT_FALSE(ShortCode::from_string("123456").has_value()); // length != 5 EXPECT_FALSE(ShortCode::from_string("123456").has_value()); // length != 5
EXPECT_FALSE(ShortCode::from_string("Z9EFV").has_value()); // out of short code range EXPECT_FALSE(ShortCode::from_string("Z9EFV").has_value()); // out of short code range
const auto sum = std::accumulate(ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0);
EXPECT_EQ(sum, SHORT_CODE_LIMIT);
#ifndef KLSK_NDEBUG #ifndef KLSK_NDEBUG
std::ostringstream out; std::ostringstream out;
out << ShortCode::unsafe_create(TEST_S_CODE); // ostream capture out << ShortCode::unsafe_create(TEST_S_CODE); // ostream capture
@ -97,14 +108,14 @@ TEST(ShortCode, exporter) {
auto short_code = ShortCode::unsafe_create(TEST_S_CODE); auto short_code = ShortCode::unsafe_create(TEST_S_CODE);
EXPECT_EQ(short_code.unwrap(), TEST_S_CODE); EXPECT_EQ(short_code.unwrap(), TEST_S_CODE);
EXPECT_EQ(short_code.to_string(), TEST_S_CODE_STR); EXPECT_EQ(short_code.to_string(), TEST_S_CODE_STR);
EXPECT_EQ(short_code.to_common_code(), TEST_C_CODE);
// TODO: test fast mode of `to_common_code` speed_up_reset();
EXPECT_EQ(short_code.to_common_code(), TEST_C_CODE);
ShortCode::speed_up(true);
EXPECT_EQ(short_code.to_common_code(), TEST_C_CODE);
} }
// TODO: maybe add `speed_up` test suite TEST(ShortCode, initialize) {
TEST(ShortCode, initializate) {
auto short_code = ShortCode::unsafe_create(TEST_S_CODE); auto short_code = ShortCode::unsafe_create(TEST_S_CODE);
auto common_code = CommonCode::unsafe_create(TEST_C_CODE); auto common_code = CommonCode::unsafe_create(TEST_C_CODE);
@ -114,14 +125,17 @@ TEST(ShortCode, initializate) {
EXPECT_EQ(s1, TEST_S_CODE); // l-value EXPECT_EQ(s1, TEST_S_CODE); // l-value
EXPECT_EQ(s2, TEST_S_CODE); // r-value EXPECT_EQ(s2, TEST_S_CODE); // r-value
// TODO: test fast mode of `ShortCode(CommonCode)`
// ShortCode(...) // ShortCode(...)
EXPECT_EQ(ShortCode(common_code), TEST_S_CODE); EXPECT_EQ(ShortCode(common_code), TEST_S_CODE);
EXPECT_EQ(ShortCode(short_code), TEST_S_CODE); // l-value EXPECT_EQ(ShortCode(short_code), TEST_S_CODE); // l-value
EXPECT_EQ(ShortCode(ShortCode(short_code)), TEST_S_CODE); // r-value EXPECT_EQ(ShortCode(ShortCode(short_code)), TEST_S_CODE); // r-value
// ShortCode::create(uint32_t) // ShortCode::create(uint32_t)
speed_up_reset();
EXPECT_TRUE(ShortCode::create(TEST_S_CODE).has_value());
EXPECT_FALSE(ShortCode::create(TEST_S_CODE_ERR).has_value());
EXPECT_EQ(ShortCode::create(TEST_S_CODE), TEST_S_CODE);
ShortCode::speed_up(true);
EXPECT_TRUE(ShortCode::create(TEST_S_CODE).has_value()); EXPECT_TRUE(ShortCode::create(TEST_S_CODE).has_value());
EXPECT_FALSE(ShortCode::create(TEST_S_CODE_ERR).has_value()); EXPECT_FALSE(ShortCode::create(TEST_S_CODE_ERR).has_value());
EXPECT_EQ(ShortCode::create(TEST_S_CODE), TEST_S_CODE); EXPECT_EQ(ShortCode::create(TEST_S_CODE), TEST_S_CODE);
@ -148,95 +162,89 @@ TEST(ShortCode, initializate) {
EXPECT_EQ(ShortCode::from_common_code(TEST_C_CODE_STR), TEST_S_CODE); EXPECT_EQ(ShortCode::from_common_code(TEST_C_CODE_STR), TEST_S_CODE);
} }
// TODO: global verify function
// -> check
// -> fast_decode / fast_encode
// -> tiny_decode / tiny_encode
// -> string_encode / string_decode
TEST(ShortCode, speed_up) { TEST(ShortCode, speed_up) {
all_cases_reset(); co::Racer racer {TEST_THREAD_NUM};
basic_ranges_reset();
BS::thread_pool pool(TEST_THREAD_NUM); static auto EXPECT_STAGE_0 = +[]() {
EXPECT_FALSE(exposer::ShortCode_fast_());
for (auto i = 0; i < TEST_THREAD_NUM; ++i) { EXPECT_EQ(exposer::ShortCode_cases_(), nullptr);
pool.detach_task([]() { EXPECT_EQ(exposer::ShortCode_ranges_(), nullptr);
ShortCode::speed_up(false); EXPECT_FALSE(BasicRanges::instance().is_available());
}); EXPECT_FALSE(AllCases::instance().is_available());
} };
EXPECT_FALSE(BasicRanges::instance().is_available());
EXPECT_FALSE(AllCases::instance().is_available()); static auto EXPECT_STAGE_1 = +[]() {
pool.wait(); EXPECT_FALSE(exposer::ShortCode_fast_());
EXPECT_TRUE(BasicRanges::instance().is_available()); EXPECT_EQ(exposer::ShortCode_cases_(), nullptr);
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_EQ(exposer::ShortCode_ranges_(), &BasicRanges::instance().fetch());
EXPECT_TRUE(BasicRanges::instance().is_available());
for (auto i = 0; i < TEST_THREAD_NUM; ++i) { EXPECT_FALSE(AllCases::instance().is_available());
pool.detach_task([]() { };
ShortCode::speed_up(true);
}); static auto EXPECT_STAGE_2 = +[]() {
} EXPECT_TRUE(exposer::ShortCode_fast_());
EXPECT_TRUE(BasicRanges::instance().is_available()); EXPECT_EQ(exposer::ShortCode_cases_(), &AllCases::instance().fetch());
EXPECT_FALSE(AllCases::instance().is_available()); EXPECT_EQ(exposer::ShortCode_ranges_(), &BasicRanges::instance().fetch());
pool.wait(); EXPECT_TRUE(BasicRanges::instance().is_available());
EXPECT_TRUE(BasicRanges::instance().is_available()); EXPECT_TRUE(AllCases::instance().is_available());
EXPECT_TRUE(AllCases::instance().is_available()); };
speed_up_reset();
EXPECT_STAGE_0();
racer.Race([] { ShortCode::speed_up(false); });
EXPECT_STAGE_1();
racer.Race([] { ShortCode::speed_up(true); });
EXPECT_STAGE_2();
racer.Race([] { ShortCode::speed_up(true); });
EXPECT_STAGE_2();
racer.Race([] { ShortCode::speed_up(false); });
EXPECT_STAGE_2();
speed_up_reset();
EXPECT_STAGE_0();
racer.Race([] { ShortCode::speed_up(true); });
EXPECT_STAGE_2();
} }
TEST(ShortCode, code_verify) { TEST(ShortCode, code_verify) {
BS::thread_pool pool; ShortCode::speed_up(true); // enter fast mode
ShortCode::speed_up(true); short_code_parallel([](std::span<ShortCode> codes) {
pool.detach_sequence(0, 16, [](const uint64_t head) { for (auto code : codes) {
std::vector<uint32_t> archive; EXPECT_TRUE(ShortCode::check(code.unwrap()));
for (auto range : AllCases::instance().fetch()[head]) { auto common_code = code.to_common_code(); // ShortCode::fast_decode
auto code = ShortCode::from_common_code(head << 32 | range); EXPECT_EQ(ShortCode::from_common_code(common_code), code); // ShortCode::fast_encode
EXPECT_TRUE(code.has_value());
EXPECT_TRUE(ShortCode::check(code->unwrap()));
EXPECT_EQ(code->to_common_code(), head << 32 | range);
archive.emplace_back(code->unwrap());
}
if (!archive.empty()) {
EXPECT_TRUE(std::is_sorted(archive.begin(), archive.end())); // increasingly one by one
EXPECT_EQ(archive[archive.size() - 1] - archive[0], archive.size() - 1);
EXPECT_EQ(std::accumulate(ALL_CASES_NUM.begin(), ALL_CASES_NUM.begin() + head, 0), archive[0]);
} }
}); });
pool.wait();
} }
TEST(ShortCode, code_string) { TEST(ShortCode, code_string) {
auto test_func = [](ShortCode code) { short_code_parallel([](std::span<ShortCode> codes) {
auto code_str = code.to_string(); for (auto code : codes) {
EXPECT_EQ(code_str.size(), 5); // length = 5 auto code_str = code.to_string();
for (auto c : code_str) { EXPECT_EQ(code_str.size(), 5); // length = 5
EXPECT_TRUE((c >= '1' && c <= '9') || (c >= 'A' && c <= 'Z')); for (auto c : code_str) {
EXPECT_TRUE(c != 'I' && c != 'L' && c != 'O'); EXPECT_TRUE((c >= '1' && c <= '9') || (c >= 'A' && c <= 'Z'));
} EXPECT_TRUE(c != 'I' && c != 'L' && c != 'O');
EXPECT_EQ(ShortCode::from_string(code_str), code); // test upper cases }
std::transform(code_str.begin(), code_str.end(), code_str.begin(), ::tolower); EXPECT_EQ(ShortCode::from_string(code_str), code); // test upper cases
EXPECT_EQ(ShortCode::from_string(code_str), code); // test lower cases std::transform(code_str.begin(), code_str.end(), code_str.begin(), ::tolower);
}; EXPECT_EQ(ShortCode::from_string(code_str), code); // test lower cases
BS::thread_pool pool;
pool.detach_blocks((uint32_t)0, SHORT_CODE_LIMIT, [&test_func](uint32_t start, uint32_t end) {
for (uint32_t short_code = start; short_code < end; ++short_code) {
test_func(ShortCode::unsafe_create(short_code));
} }
}); });
pool.wait();
} }
TEST(ShortCode, DISABLED_global_verify) { TEST(ShortCode, DISABLED_global_verify) {
all_cases_reset(); speed_up_reset();
BS::thread_pool pool; BS::thread_pool pool;
auto futures = pool.submit_blocks((uint32_t)0, SHORT_CODE_LIMIT, [](uint32_t start, uint32_t end) { auto futures = pool.submit_blocks(0U, SHORT_CODE_LIMIT, [](auto start, auto end) {
std::vector<uint64_t> archive; std::vector<uint64_t> codes;
archive.reserve(end - start); codes.reserve(end - start);
for (uint32_t short_code = start; short_code < end; ++short_code) { for (uint32_t short_code = start; short_code < end; ++short_code) {
auto common_code = CommonCode::from_short_code(short_code).value(); auto common_code = CommonCode::from_short_code(short_code).value(); // ShortCode::tiny_decode
EXPECT_EQ(common_code.to_short_code(), short_code); EXPECT_EQ(common_code.to_short_code(), short_code); // ShortCode::tiny_encode
archive.emplace_back(common_code.unwrap()); codes.emplace_back(common_code.unwrap());
} }
return archive; return codes;
}, 0x1000); // split as 4096 pieces }, 0x1000); // split as 4096 pieces
std::vector<uint64_t> result; std::vector<uint64_t> result;
@ -245,6 +253,5 @@ TEST(ShortCode, DISABLED_global_verify) {
const auto data = future.get(); const auto data = future.get();
result.insert(result.end(), data.begin(), data.end()); // combine sections result.insert(result.end(), data.begin(), data.end()); // combine sections
} }
pool.wait();
EXPECT_EQ(result, all_common_codes()); EXPECT_EQ(result, all_common_codes());
} }

5
src/core_test/utility/concurrent.h

@ -37,6 +37,11 @@ public:
return race_num_; // number of racing threads return race_num_; // number of racing threads
} }
void Race(std::function<void()> &&item) {
Start(std::move(item));
Join();
}
void Start(std::function<void()> &&item) { void Start(std::function<void()> &&item) {
auto wrapper = [item = std::move(item)](const int) { auto wrapper = [item = std::move(item)](const int) {
item(); // execute racing function item(); // execute racing function

Loading…
Cancel
Save