From afe8c595a1ef130849e204232da9e4df251e5903 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sun, 31 Mar 2024 19:05:01 +0800 Subject: [PATCH] test: refactor test suites of klotski cases --- src/core_test/CMakeLists.txt | 1 + src/core_test/cases/all_cases.cc | 200 ++++++++++------------------ src/core_test/cases/basic_ranges.cc | 53 ++++++++ src/core_test/cases/helper.h | 29 ++++ src/core_test/utility/concurrent.h | 45 ++++--- 5 files changed, 180 insertions(+), 148 deletions(-) create mode 100644 src/core_test/cases/basic_ranges.cc create mode 100644 src/core_test/cases/helper.h diff --git a/src/core_test/CMakeLists.txt b/src/core_test/CMakeLists.txt index 6cc19c5..42fc855 100644 --- a/src/core_test/CMakeLists.txt +++ b/src/core_test/CMakeLists.txt @@ -16,6 +16,7 @@ include_directories(${KLOTSKI_ROOT_DIR}/src/core/ffi) set(KLOTSKI_TEST_CASES_SRC cases/all_cases.cc + cases/basic_ranges.cc ) add_executable(test_klotski_cases ${KLOTSKI_TEST_CASES_SRC}) diff --git a/src/core_test/cases/all_cases.cc b/src/core_test/cases/all_cases.cc index 001c2ae..da950f8 100644 --- a/src/core_test/cases/all_cases.cc +++ b/src/core_test/cases/all_cases.cc @@ -1,19 +1,5 @@ -#include - #include "hash.h" -#include "exposer.h" -#include "all_cases.h" -#include "concurrent.h" -#include "gtest/gtest.h" - -using klotski::cases::AllCases; -using klotski::cases::BasicRanges; - -using klotski::cases::ALL_CASES_NUM; -using klotski::cases::ALL_CASES_NUM_; -using klotski::cases::BASIC_RANGES_NUM; - -static constexpr uint64_t BASIC_RANGES_XXH3 = 0x82b040060044e336; +#include "helper.h" static constexpr std::array ALL_CASES_XXH3 = { 0x71c8ff7a71c93da0, 0x2a5247ee8bfed666, 0xf4efc8fc692d58e2, 0x2d06800538d394c2, @@ -22,44 +8,25 @@ static constexpr std::array ALL_CASES_XXH3 = { 0x2cdf6c14a7ce3e9a, 0xb9dd04a315583f5c, 0x19046e49c44ae90d, 0x2d06800538d394c2, }; -/// Forcibly modify private variables to reset state. -FORCIBLY_ACCESS(AllCases, available_, bool) -FORCIBLY_ACCESS(BasicRanges, available_, bool) - -class BR_Test final { -public: - [[nodiscard]] static bool available() { - return BasicRanges::instance().is_available(); +class AllCasesTest : public testing::Test, public Concurrent { +protected: + void SetUp() override { + Reset(); + EXPECT_FALSE(Available()); } - // TODO: expect member function - - /// Reset basic ranges build state, note it is thread-unsafe. - static void reset() { - exposer::BasicRanges_available_(BasicRanges::instance()) = false; - } - - /// Verify that whether basic ranges data is correct. - static void verify() { - const auto &basic_ranges = BasicRanges::instance().fetch(); - EXPECT_EQ(basic_ranges.size(), BASIC_RANGES_NUM); // verify basic ranges size - EXPECT_EQ(hash::xxh3(basic_ranges), BASIC_RANGES_XXH3); // verify basic ranges checksum - } -}; - -class AC_Test final { -public: - [[nodiscard]] static bool available() { + /// Whether all cases are ready. + [[nodiscard]] static bool Available() { return AllCases::instance().is_available(); } /// Reset all cases build state, note it is thread-unsafe. - static void reset() { + static void Reset() { exposer::AllCases_available_(AllCases::instance()) = false; } /// Verify that whether all cases data is correct. - static void verify() { + static void Verify() { const auto &all_cases = AllCases::instance().fetch(); for (int head = 0; head < 16; ++head) { EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size @@ -71,137 +38,112 @@ public: }); EXPECT_EQ(all_cases_num, ALL_CASES_NUM_); // verify all cases global size - for (uint64_t head = 0; head < 16; ++head) { + for (int head = 0; head < 16; ++head) { EXPECT_EQ(hash::xxh3(all_cases[head]), ALL_CASES_XXH3[head]); // verify all cases checksum } } }; -TEST(AllCases, basic_ranges) { - BR_Test::reset(); - EXPECT_FALSE(BR_Test::available()); +TEST_FF(AllCases, constant) { + EXPECT_EQ(ALL_CASES_NUM[0], 2942906); + EXPECT_EQ(ALL_CASES_NUM[1], 2260392); + EXPECT_EQ(ALL_CASES_NUM[2], 2942906); + EXPECT_EQ(ALL_CASES_NUM[3], 0); - BasicRanges::instance().build(); - EXPECT_TRUE(BR_Test::available()); + EXPECT_EQ(ALL_CASES_NUM[4], 2322050); + EXPECT_EQ(ALL_CASES_NUM[5], 1876945); + EXPECT_EQ(ALL_CASES_NUM[6], 2322050); + EXPECT_EQ(ALL_CASES_NUM[7], 0); - BasicRanges::instance().build(); - EXPECT_TRUE(BR_Test::available()); - BR_Test::verify(); -} + EXPECT_EQ(ALL_CASES_NUM[8], 2322050); + EXPECT_EQ(ALL_CASES_NUM[9], 1876945); + EXPECT_EQ(ALL_CASES_NUM[10], 2322050); + EXPECT_EQ(ALL_CASES_NUM[11], 0); -TEST(AllCases, basic_ranges_race) { - BR_Test::reset(); - EXPECT_FALSE(BR_Test::available()); + EXPECT_EQ(ALL_CASES_NUM[12], 2942906); + EXPECT_EQ(ALL_CASES_NUM[13], 2260392); + EXPECT_EQ(ALL_CASES_NUM[14], 2942906); + EXPECT_EQ(ALL_CASES_NUM[15], 0); - auto racer = co::Racer([] { - BasicRanges::instance().build(); - }); - EXPECT_FALSE(BR_Test::available()); - - racer.Join(); - EXPECT_TRUE(BR_Test::available()); - BR_Test::verify(); + EXPECT_EQ(ALL_CASES_NUM_, 29334498); } -TEST(AllCases, all_cases) { - AC_Test::reset(); - EXPECT_FALSE(AC_Test::available()); - +TEST_FF(AllCases, all_cases) { AllCases::instance().build(); - EXPECT_TRUE(AC_Test::available()); + EXPECT_TRUE(Available()); + Verify(); AllCases::instance().build(); - EXPECT_TRUE(AC_Test::available()); - AC_Test::verify(); + EXPECT_TRUE(Available()); + Verify(); } -TEST(AllCases, all_cases_race) { - AC_Test::reset(); - EXPECT_FALSE(AC_Test::available()); - - auto racer = co::Racer([] { +TEST_FF(AllCases, all_cases_race) { + racer_.Begin([] { AllCases::instance().build(); }); - EXPECT_FALSE(AC_Test::available()); - - racer.Join(); - EXPECT_TRUE(AC_Test::available()); - AC_Test::verify(); + EXPECT_FALSE(Available()); + racer_.Join(); + EXPECT_TRUE(Available()); + Verify(); } -TEST(AllCases, all_cases_parallel) { - AC_Test::reset(); - EXPECT_FALSE(AC_Test::available()); - - co::Executor executor; - AllCases::instance().build_parallel(executor.Entry()); - EXPECT_TRUE(AC_Test::available()); +TEST_FF(AllCases, all_cases_parallel) { + AllCases::instance().build_parallel(executor_.Entry()); + EXPECT_TRUE(Available()); + Verify(); - AllCases::instance().build_parallel(executor.Entry()); - EXPECT_TRUE(AC_Test::available()); - AC_Test::verify(); + AllCases::instance().build_parallel(executor_.Entry()); + EXPECT_TRUE(Available()); + Verify(); } -TEST(AllCases, all_cases_parallel_race) { - AC_Test::reset(); - EXPECT_FALSE(AC_Test::available()); - - co::Executor executor; - auto racer = co::Racer([&executor] { - AllCases::instance().build_parallel(executor.Entry()); +TEST_FF(AllCases, all_cases_parallel_race) { + racer_.Begin([this] { + AllCases::instance().build_parallel(executor_.Entry()); }); - EXPECT_FALSE(AC_Test::available()); - - racer.Join(); - EXPECT_TRUE(AC_Test::available()); - AC_Test::verify(); + EXPECT_FALSE(Available()); + racer_.Join(); + EXPECT_TRUE(Available()); + Verify(); } -TEST(AllCases, all_cases_async) { - AC_Test::reset(); - EXPECT_FALSE(AC_Test::available()); - - co::Executor executor; +TEST_FF(AllCases, all_cases_async) { std::atomic_flag flag; flag.clear(); - AllCases::instance().build_parallel_async(executor.Entry(), [&flag]() { + AllCases::instance().build_parallel_async(executor_.Entry(), [&flag]() { flag.test_and_set(); flag.notify_all(); }); - EXPECT_FALSE(AC_Test::available()); - + EXPECT_FALSE(Available()); flag.wait(false); - EXPECT_TRUE(AC_Test::available()); + EXPECT_TRUE(Available()); + Verify(); flag.clear(); - AllCases::instance().build_parallel_async(executor.Entry(), [&flag]() { + AllCases::instance().build_parallel_async(executor_.Entry(), [&flag]() { flag.test_and_set(); flag.notify_all(); }); - EXPECT_TRUE(AC_Test::available()); - + EXPECT_TRUE(Available()); flag.wait(false); - EXPECT_TRUE(AC_Test::available()); - AC_Test::verify(); + EXPECT_TRUE(Available()); + Verify(); } -TEST(AllCases, all_cases_async_race) { - AC_Test::reset(); - EXPECT_FALSE(AC_Test::available()); - - co::Executor executor; +TEST_FF(AllCases, all_cases_async_race) { std::atomic callback_num(0); - auto racer = co::Racer([&executor, &callback_num] { - AllCases::instance().build_parallel_async(executor.Entry(), [&callback_num]() { + racer_.Begin([this, &callback_num] { + AllCases::instance().build_parallel_async(executor_.Entry(), [&callback_num]() { callback_num.fetch_add(1); }); }); - EXPECT_FALSE(AC_Test::available()); + EXPECT_FALSE(Available()); - racer.Join(); - EXPECT_TRUE(AC_Test::available()); - EXPECT_EQ(callback_num.load(), co::Racer::Times); - AC_Test::verify(); + racer_.Join(); + EXPECT_TRUE(Available()); + EXPECT_EQ(callback_num.load(), co::Racer::Num); + Verify(); } diff --git a/src/core_test/cases/basic_ranges.cc b/src/core_test/cases/basic_ranges.cc new file mode 100644 index 0000000..88f1200 --- /dev/null +++ b/src/core_test/cases/basic_ranges.cc @@ -0,0 +1,53 @@ +#include "hash.h" +#include "helper.h" + +static constexpr uint64_t BASIC_RANGES_XXH3 = 0x82b040060044e336; + +class BasicRangesTest : public testing::Test, public Concurrent { +protected: + void SetUp() override { + Reset(); + EXPECT_FALSE(Available()); + } + + /// Whether basic ranges are ready. + [[nodiscard]] static bool Available() { + return BasicRanges::instance().is_available(); + } + + /// Reset basic ranges build state, note it is thread-unsafe. + static void Reset() { + exposer::BasicRanges_available_(BasicRanges::instance()) = false; + } + + /// Verify that whether basic ranges data is correct. + static void Verify() { + const auto &basic_ranges = BasicRanges::instance().fetch(); + EXPECT_EQ(basic_ranges.size(), BASIC_RANGES_NUM); // verify basic ranges size + EXPECT_EQ(hash::xxh3(basic_ranges), BASIC_RANGES_XXH3); // verify basic ranges checksum + } +}; + +TEST_FF(BasicRanges, constant) { + EXPECT_EQ(BASIC_RANGES_NUM, 7311921); +} + +TEST_FF(BasicRanges, basic_ranges) { + BasicRanges::instance().build(); + EXPECT_TRUE(Available()); + Verify(); + + BasicRanges::instance().build(); + EXPECT_TRUE(Available()); + Verify(); +} + +TEST_FF(BasicRanges, basic_ranges_race) { + racer_.Begin([] { + BasicRanges::instance().build(); + }); + EXPECT_FALSE(Available()); + racer_.Join(); + EXPECT_TRUE(Available()); + Verify(); +} diff --git a/src/core_test/cases/helper.h b/src/core_test/cases/helper.h new file mode 100644 index 0000000..29776ab --- /dev/null +++ b/src/core_test/cases/helper.h @@ -0,0 +1,29 @@ +#pragma once + +#include "exposer.h" +#include "all_cases.h" +#include "concurrent.h" +#include "gtest/gtest.h" + +using klotski::cases::AllCases; +using klotski::cases::BasicRanges; + +using klotski::cases::ALL_CASES_NUM; +using klotski::cases::ALL_CASES_NUM_; +using klotski::cases::BASIC_RANGES_NUM; + +/// Test fixture wrapper with Racer and Executor. +class Concurrent { +protected: + co::Racer racer_; + co::Executor executor_; +}; + +/// Forcibly modify private variables to reset state. +FORCIBLY_ACCESS(AllCases, available_, bool) +FORCIBLY_ACCESS(BasicRanges, available_, bool) + +/// Test fixture macro with custom test suite name. +#define TEST_FF(test_suite_name, test_name) \ + GTEST_TEST_(test_suite_name, test_name, test_suite_name##Test, \ + ::testing::internal::GetTypeId()) diff --git a/src/core_test/utility/concurrent.h b/src/core_test/utility/concurrent.h index 91a7821..2a13418 100644 --- a/src/core_test/utility/concurrent.h +++ b/src/core_test/utility/concurrent.h @@ -1,5 +1,10 @@ #pragma once +/// The concurrency test helper class implements the Executor and Racer based +/// on the BS::thread_pool. The Executor allows you to submit multiple tasks +/// and dispatch them to different threads. The Racer schedules the specified +/// function to a certain number of threads at one time. + #include #include @@ -7,39 +12,41 @@ namespace co { -class Racer final { +class Executor final { public: - static constexpr int Times = 256; + Executor() = default; + ~Executor() { pool_.wait(); } - explicit Racer(std::function &&item) : pool_(Times), item_(item) { - pool_.detach_sequence(0, Times, [this](const int) { - item_(); // execute race function - }); + std::function &&)> Entry() { + return [this](auto &&func) { + pool_.detach_task(func); + }; } - ~Racer() { Join(); } - - void Join() { pool_.wait(); } - private: BS::thread_pool pool_; - std::function item_; }; -class Executor final { +class Racer final { public: - Executor() = default; + Racer() = default; + ~Racer() { Join(); } - ~Executor() { pool_.wait(); } + static constexpr int Num = 256; // number of racing threads - std::function &&)> Entry() { - return [this](auto &&func) { - pool_.detach_task(func); - }; + void Begin(std::function &&item) { + item_ = std::move(item); + pool_.detach_sequence(0, Num, [this](const int) { + item_(); // execute racing function + }); } + void Join() { pool_.wait(); } + private: - BS::thread_pool pool_; + std::function item_; + BS::thread_pool pool_ { Num }; }; + } // namespace co