Browse Source

test: refactor test suites of klotski cases

master
Dnomd343 3 months ago
parent
commit
afe8c595a1
  1. 1
      src/core_test/CMakeLists.txt
  2. 200
      src/core_test/cases/all_cases.cc
  3. 53
      src/core_test/cases/basic_ranges.cc
  4. 29
      src/core_test/cases/helper.h
  5. 45
      src/core_test/utility/concurrent.h

1
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})

200
src/core_test/cases/all_cases.cc

@ -1,19 +1,5 @@
#include <vector>
#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<uint64_t, 16> ALL_CASES_XXH3 = {
0x71c8ff7a71c93da0, 0x2a5247ee8bfed666, 0xf4efc8fc692d58e2, 0x2d06800538d394c2,
@ -22,44 +8,25 @@ static constexpr std::array<uint64_t, 16> 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<int> 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();
}

53
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();
}

29
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<test_suite_name##Test>())

45
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 <memory>
#include <functional>
@ -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<void()> &&item) : pool_(Times), item_(item) {
pool_.detach_sequence(0, Times, [this](const int) {
item_(); // execute race function
});
std::function<void(std::function<void()> &&)> Entry() {
return [this](auto &&func) {
pool_.detach_task(func);
};
}
~Racer() { Join(); }
void Join() { pool_.wait(); }
private:
BS::thread_pool pool_;
std::function<void()> 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<void(std::function<void()> &&)> Entry() {
return [this](auto &&func) {
pool_.detach_task(func);
};
void Begin(std::function<void()> &&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<void()> item_;
BS::thread_pool pool_ { Num };
};
} // namespace co

Loading…
Cancel
Save