mirror of
https://github.com/latentPrion/libspinscale.git
synced 2026-06-23 19:48:32 +00:00
Adversarial review on test porting plan
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
#ifndef SPINSCALE_TEST_SUPPORT_COROUTINE_DRIVER_H
|
||||
#define SPINSCALE_TEST_SUPPORT_COROUTINE_DRIVER_H
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <support/threadHarness.h>
|
||||
|
||||
namespace sscl::tests {
|
||||
|
||||
class CoroutineDriver
|
||||
{
|
||||
public:
|
||||
template <typename Invoker>
|
||||
static auto completedReturnValue(Invoker &invoker)
|
||||
{
|
||||
if (invoker.completedReturnValues().myExceptionPtr) {
|
||||
std::rethrow_exception(
|
||||
invoker.completedReturnValues().myExceptionPtr);
|
||||
}
|
||||
|
||||
return invoker.completedReturnValues().myReturnValue;
|
||||
}
|
||||
|
||||
template <typename Invoker>
|
||||
static auto pumpUntilIdleAndReturnValue(
|
||||
boost::asio::io_context &ioContext,
|
||||
Invoker &invoker)
|
||||
{
|
||||
IoContextPump::pumpUntilIdle(ioContext);
|
||||
return completedReturnValue(invoker);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sscl::tests
|
||||
|
||||
#endif // SPINSCALE_TEST_SUPPORT_COROUTINE_DRIVER_H
|
||||
+108
-35
@@ -2,6 +2,7 @@
|
||||
#define SPINSCALE_TEST_SUPPORT_GROUP_ASSERTIONS_H
|
||||
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
@@ -21,12 +22,31 @@ int completedIntValue(Invoker &invoker)
|
||||
return invoker.completedReturnValues().myReturnValue;
|
||||
}
|
||||
|
||||
inline void expectCompletedSettlement(
|
||||
inline void requireCompletedSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
descriptor.type,
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::COMPLETED);
|
||||
if (descriptor.type !=
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::COMPLETED)
|
||||
{
|
||||
throw std::runtime_error("Expected completed settlement");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Invoker>
|
||||
void requireCompletedIntSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor,
|
||||
int expectedValue)
|
||||
{
|
||||
requireCompletedSettlement(descriptor);
|
||||
|
||||
const int actualValue = completedIntValue(descriptor.invokerAs<Invoker>());
|
||||
if (actualValue != expectedValue) {
|
||||
throw std::runtime_error(
|
||||
"Expected completed settlement value "
|
||||
+ std::to_string(expectedValue)
|
||||
+ ", got "
|
||||
+ std::to_string(actualValue));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Invoker>
|
||||
@@ -34,39 +54,85 @@ void expectCompletedIntSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor,
|
||||
int expectedValue)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
descriptor.type,
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::COMPLETED);
|
||||
EXPECT_EQ(completedIntValue(descriptor.invokerAs<Invoker>()), expectedValue);
|
||||
EXPECT_NO_THROW(
|
||||
requireCompletedIntSettlement<Invoker>(
|
||||
descriptor,
|
||||
expectedValue));
|
||||
}
|
||||
|
||||
inline void expectCompletedSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor)
|
||||
{
|
||||
EXPECT_NO_THROW(requireCompletedSettlement(descriptor));
|
||||
}
|
||||
|
||||
inline void requireExceptionSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor)
|
||||
{
|
||||
if (descriptor.type !=
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::EXCEPTION_THROWN)
|
||||
{
|
||||
throw std::runtime_error("Expected exception settlement");
|
||||
}
|
||||
|
||||
if (!descriptor.calleeException) {
|
||||
throw std::runtime_error("Expected exception pointer in settlement");
|
||||
}
|
||||
}
|
||||
|
||||
inline void expectExceptionSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
descriptor.type,
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::EXCEPTION_THROWN);
|
||||
EXPECT_TRUE(descriptor.calleeException != nullptr);
|
||||
EXPECT_NO_THROW(requireExceptionSettlement(descriptor));
|
||||
}
|
||||
|
||||
inline void expectRuntimeErrorSettlement(
|
||||
inline void requireRuntimeErrorSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor,
|
||||
const std::string &expectedMessage)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
descriptor.type,
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::EXCEPTION_THROWN);
|
||||
ASSERT_TRUE(descriptor.calleeException != nullptr);
|
||||
requireExceptionSettlement(descriptor);
|
||||
|
||||
try {
|
||||
std::rethrow_exception(descriptor.calleeException);
|
||||
}
|
||||
catch (const std::runtime_error &runtimeError) {
|
||||
EXPECT_EQ(std::string(runtimeError.what()), expectedMessage);
|
||||
const std::string actualMessage = runtimeError.what();
|
||||
if (actualMessage != expectedMessage) {
|
||||
throw std::runtime_error(
|
||||
"Expected runtime_error settlement message \""
|
||||
+ expectedMessage
|
||||
+ "\", got \""
|
||||
+ actualMessage
|
||||
+ "\"");
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (...) {
|
||||
FAIL() << "Expected std::runtime_error settlement.";
|
||||
throw std::runtime_error("Expected std::runtime_error settlement");
|
||||
}
|
||||
}
|
||||
|
||||
inline void requireIntExceptionSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor,
|
||||
int expectedValue)
|
||||
{
|
||||
requireExceptionSettlement(descriptor);
|
||||
|
||||
try {
|
||||
std::rethrow_exception(descriptor.calleeException);
|
||||
}
|
||||
catch (int caughtValue) {
|
||||
if (caughtValue != expectedValue) {
|
||||
throw std::runtime_error(
|
||||
"Expected int exception settlement value "
|
||||
+ std::to_string(expectedValue)
|
||||
+ ", got "
|
||||
+ std::to_string(caughtValue));
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch (...) {
|
||||
throw std::runtime_error("Expected int exception settlement");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,29 +140,36 @@ inline void expectIntExceptionSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor,
|
||||
int expectedValue)
|
||||
{
|
||||
ASSERT_EQ(
|
||||
descriptor.type,
|
||||
sscl::co::Group::SettlementDescriptor::TypeE::EXCEPTION_THROWN);
|
||||
ASSERT_TRUE(descriptor.calleeException != nullptr);
|
||||
EXPECT_NO_THROW(
|
||||
requireIntExceptionSettlement(
|
||||
descriptor,
|
||||
expectedValue));
|
||||
}
|
||||
|
||||
try {
|
||||
std::rethrow_exception(descriptor.calleeException);
|
||||
}
|
||||
catch (int caughtValue) {
|
||||
EXPECT_EQ(caughtValue, expectedValue);
|
||||
return;
|
||||
}
|
||||
catch (...) {
|
||||
FAIL() << "Expected int exception settlement.";
|
||||
inline void expectRuntimeErrorSettlement(
|
||||
const sscl::co::Group::SettlementDescriptor &descriptor,
|
||||
const std::string &expectedMessage)
|
||||
{
|
||||
EXPECT_NO_THROW(
|
||||
requireRuntimeErrorSettlement(
|
||||
descriptor,
|
||||
expectedMessage));
|
||||
}
|
||||
|
||||
inline void requireEmptyGroupError(
|
||||
const std::runtime_error &runtimeError)
|
||||
{
|
||||
constexpr const char *expectedMessage =
|
||||
"co_await: Group has no member invokers; call add() before awaiting";
|
||||
if (std::string(runtimeError.what()) != expectedMessage) {
|
||||
throw std::runtime_error("Unexpected empty group error message");
|
||||
}
|
||||
}
|
||||
|
||||
inline void expectEmptyGroupError(
|
||||
const std::runtime_error &runtimeError)
|
||||
{
|
||||
constexpr const char *expectedMessage =
|
||||
"co_await: Group has no member invokers; call add() before awaiting";
|
||||
EXPECT_EQ(std::string(runtimeError.what()), expectedMessage);
|
||||
EXPECT_NO_THROW(requireEmptyGroupError(runtimeError));
|
||||
}
|
||||
|
||||
} // namespace sscl::tests
|
||||
|
||||
@@ -217,13 +217,32 @@ void ThreadRegistry::registerThread(
|
||||
DedicatedIoThread &thread)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(registryMutex());
|
||||
threadsByRole()[role] = &thread;
|
||||
auto [iterator, inserted] = threadsByRole().emplace(role, &thread);
|
||||
|
||||
if (!inserted) {
|
||||
throw std::runtime_error(
|
||||
"Test thread role already registered for " + threadRoleName(role));
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadRegistry::unregisterThread(PostingThreadRole role)
|
||||
void ThreadRegistry::unregisterThread(
|
||||
PostingThreadRole role,
|
||||
DedicatedIoThread &expectedThread)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(registryMutex());
|
||||
threadsByRole().erase(role);
|
||||
auto iterator = threadsByRole().find(role);
|
||||
|
||||
if (iterator == threadsByRole().end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (iterator->second != &expectedThread) {
|
||||
throw std::runtime_error(
|
||||
"Test thread role registered to a different thread for "
|
||||
+ threadRoleName(role));
|
||||
}
|
||||
|
||||
threadsByRole().erase(iterator);
|
||||
}
|
||||
|
||||
boost::asio::io_context &ThreadRegistry::ioContext(PostingThreadRole role)
|
||||
@@ -272,6 +291,20 @@ PostingThreadSet::PostingThreadSet()
|
||||
bodyThread(PostingThreadRole::BODY),
|
||||
worldThread(PostingThreadRole::WORLD),
|
||||
legThread(PostingThreadRole::LEG)
|
||||
{
|
||||
previousPuppeteerThread = sscl::ComponentThread::getPptr();
|
||||
previousPuppeteerThreadId = sscl::pptr::puppeteerThreadId;
|
||||
registerAllThreads();
|
||||
installCallerAsPuppeteer();
|
||||
}
|
||||
|
||||
PostingThreadSet::~PostingThreadSet()
|
||||
{
|
||||
restorePreviousPuppeteer();
|
||||
unregisterAllThreads();
|
||||
}
|
||||
|
||||
void PostingThreadSet::registerAllThreads()
|
||||
{
|
||||
ThreadRegistry::registerThread(PostingThreadRole::CALLER, callerThread);
|
||||
ThreadRegistry::registerThread(PostingThreadRole::CALLEE, calleeThread);
|
||||
@@ -279,22 +312,31 @@ PostingThreadSet::PostingThreadSet()
|
||||
ThreadRegistry::registerThread(PostingThreadRole::BODY, bodyThread);
|
||||
ThreadRegistry::registerThread(PostingThreadRole::WORLD, worldThread);
|
||||
ThreadRegistry::registerThread(PostingThreadRole::LEG, legThread);
|
||||
}
|
||||
|
||||
void PostingThreadSet::unregisterAllThreads()
|
||||
{
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::CALLER, callerThread);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::CALLEE, calleeThread);
|
||||
ThreadRegistry::unregisterThread(
|
||||
PostingThreadRole::ALTERNATE,
|
||||
alternateThread);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::BODY, bodyThread);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::WORLD, worldThread);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::LEG, legThread);
|
||||
}
|
||||
|
||||
void PostingThreadSet::installCallerAsPuppeteer()
|
||||
{
|
||||
sscl::ComponentThread::setPuppeteerThreadId(
|
||||
static_cast<sscl::ThreadId>(PostingThreadRole::CALLER));
|
||||
sscl::ComponentThread::setPuppeteerThread(callerThread.componentThread());
|
||||
}
|
||||
|
||||
PostingThreadSet::~PostingThreadSet()
|
||||
void PostingThreadSet::restorePreviousPuppeteer()
|
||||
{
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::CALLER);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::CALLEE);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::ALTERNATE);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::BODY);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::WORLD);
|
||||
ThreadRegistry::unregisterThread(PostingThreadRole::LEG);
|
||||
|
||||
sscl::ComponentThread::setPuppeteerThread(nullptr);
|
||||
sscl::ComponentThread::setPuppeteerThreadId(previousPuppeteerThreadId);
|
||||
sscl::ComponentThread::setPuppeteerThread(previousPuppeteerThread);
|
||||
}
|
||||
|
||||
DedicatedIoThread &PostingThreadSet::thread(PostingThreadRole role)
|
||||
|
||||
@@ -180,7 +180,9 @@ public:
|
||||
static void registerThread(
|
||||
PostingThreadRole role,
|
||||
DedicatedIoThread &thread);
|
||||
static void unregisterThread(PostingThreadRole role);
|
||||
static void unregisterThread(
|
||||
PostingThreadRole role,
|
||||
DedicatedIoThread &expectedThread);
|
||||
static boost::asio::io_context &ioContext(PostingThreadRole role);
|
||||
static std::thread::id osThreadId(PostingThreadRole role);
|
||||
|
||||
@@ -240,14 +242,28 @@ public:
|
||||
DedicatedIoThread &leg();
|
||||
|
||||
private:
|
||||
void registerAllThreads();
|
||||
void unregisterAllThreads();
|
||||
void installCallerAsPuppeteer();
|
||||
void restorePreviousPuppeteer();
|
||||
|
||||
DedicatedIoThread callerThread;
|
||||
DedicatedIoThread calleeThread;
|
||||
DedicatedIoThread alternateThread;
|
||||
DedicatedIoThread bodyThread;
|
||||
DedicatedIoThread worldThread;
|
||||
DedicatedIoThread legThread;
|
||||
std::shared_ptr<sscl::PuppeteerThread> previousPuppeteerThread;
|
||||
sscl::ThreadId previousPuppeteerThreadId = 0;
|
||||
};
|
||||
|
||||
template <typename Function>
|
||||
auto RunOnThread(DedicatedIoThread &thread, Function &&function)
|
||||
-> std::invoke_result_t<Function &>
|
||||
{
|
||||
return thread.runSync(std::forward<Function>(function));
|
||||
}
|
||||
|
||||
class CrossThreadTrace
|
||||
{
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user