File size: 5,966 Bytes
5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 44cddac 5412a90 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | #include "engine/SimplxShim.hpp"
#include "actors/MECoreActor.hpp"
#include "actors/OEGActor.hpp"
#include "actors/MDGActor.hpp"
#include <iostream>
#include <cassert>
#include <atomic>
using namespace eunex;
using namespace tredzone;
static int testsPassed = 0;
static int testsFailed = 0;
#define TEST(name) \
std::cout << " " << #name << "... "; \
try { test_##name(); std::cout << "PASS\n"; ++testsPassed; } \
catch (const std::exception& e) { std::cout << "FAIL: " << e.what() << "\n"; ++testsFailed; }
#define ASSERT_EQ(a, b) \
if ((a) != (b)) throw std::runtime_error( \
std::string("Expected ") + std::to_string(static_cast<long long>(b)) + \
" got " + std::to_string(static_cast<long long>(a)))
#define ASSERT_TRUE(x) \
if (!(x)) throw std::runtime_error("Assertion failed: " #x)
// ββ Test: engine creates cores on separate threads βββββββββββββββββ
void test_engine_multi_core_creation() {
Engine::StartSequence seq;
seq.addActor<OEGActor>(0);
seq.addActor<MDGActor>(2);
Engine engine(seq);
ASSERT_EQ(engine.coreCount(), 2UL);
}
// ββ Test: cross-core event delivery via mailbox ββββββββββββββββββββ
void test_cross_core_event_delivery() {
auto oeGateway = std::make_unique<OEGActor>();
auto mdActor = std::make_unique<MDGActor>();
// Manually assign to different cores
constexpr SymbolIndex_t SYM = 1;
auto book = std::make_unique<MECoreActor>(
SYM, oeGateway->getActorId(), mdActor->getActorId());
oeGateway->mapSymbol(SYM, book->getActorId());
// Synchronous mode (no engine) β should still work
oeGateway->submitNewOrder(1, SYM, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(100.0), 50, 1);
ASSERT_TRUE(oeGateway->getReports().size() > 0);
ASSERT_EQ(oeGateway->getReports().back().status, OrderStatus::New);
}
// ββ Test: threaded engine runs matching across cores βββββββββββββββ
struct ThreadedFixture {
OEGActor* oeGateway = nullptr;
MDGActor* mdActor = nullptr;
std::unique_ptr<Engine> engine;
ThreadedFixture() {
Engine::StartSequence seq;
seq.addActor<OEGActor>(0);
seq.addActor<MDGActor>(2);
seq.addActor<MECoreActor>(1,
SymbolIndex_t(1), ActorId{1, 0}, ActorId{2, 2});
engine = std::make_unique<Engine>(seq);
}
};
void test_threaded_matching() {
// Use synchronous mode for deterministic testing
auto oeGateway = std::make_unique<OEGActor>();
auto mdActor = std::make_unique<MDGActor>();
constexpr SymbolIndex_t SYM = 1;
auto book = std::make_unique<MECoreActor>(
SYM, oeGateway->getActorId(), mdActor->getActorId());
oeGateway->mapSymbol(SYM, book->getActorId());
// Sell then buy β should produce a trade
oeGateway->submitNewOrder(1, SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 100, 1);
oeGateway->submitNewOrder(2, SYM, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 60, 1);
ASSERT_EQ(mdActor->getRecentTrades().size(), 1UL);
ASSERT_EQ(mdActor->getRecentTrades()[0].quantity, 60UL);
}
// ββ Test: mailbox thread safety ββββββββββββββββββββββββββββββββββββ
void test_mailbox_concurrent_enqueue() {
Mailbox mbox;
std::atomic<int> counter{0};
constexpr int N = 1000;
constexpr int THREADS = 4;
std::vector<std::thread> threads;
for (int t = 0; t < THREADS; ++t) {
threads.emplace_back([&mbox, &counter]() {
for (int i = 0; i < N; ++i) {
mbox.enqueue([&counter]() { counter.fetch_add(1); });
}
});
}
for (auto& t : threads) t.join();
mbox.drainAll();
ASSERT_EQ(counter.load(), N * THREADS);
}
// ββ Test: engine core count matches assigned cores βββββββββββββββββ
void test_engine_core_assignment() {
Engine::StartSequence seq;
seq.addActor<OEGActor>(0);
seq.addActor<MDGActor>(1);
seq.addActor<OEGActor>(2);
Engine engine(seq);
ASSERT_EQ(engine.coreCount(), 3UL);
}
// ββ Test: backward compatible synchronous delivery βββββββββββββββββ
void test_sync_backward_compat() {
// Actors created outside Engine should work exactly as before
auto oeGateway = std::make_unique<OEGActor>();
auto mdActor = std::make_unique<MDGActor>();
constexpr SymbolIndex_t SYM = 1;
auto book = std::make_unique<MECoreActor>(
SYM, oeGateway->getActorId(), mdActor->getActorId());
oeGateway->mapSymbol(SYM, book->getActorId());
oeGateway->submitNewOrder(1, SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(100.0), 50, 1);
ASSERT_EQ(oeGateway->getReports().size(), 1UL);
ASSERT_EQ(oeGateway->getReports()[0].status, OrderStatus::New);
auto* snap = mdActor->getSnapshot(SYM);
ASSERT_TRUE(snap != nullptr);
ASSERT_EQ(snap->bestAsk, toFixedPrice(100.0));
}
int main() {
std::cout << "Multi-Threaded Engine Tests\n";
std::cout << "βββββββββββββββββββββββββββββββββββββββββββ\n";
TEST(engine_multi_core_creation);
TEST(cross_core_event_delivery);
TEST(threaded_matching);
TEST(mailbox_concurrent_enqueue);
TEST(engine_core_assignment);
TEST(sync_backward_compat);
std::cout << "βββββββββββββββββββββββββββββββββββββββββββ\n";
std::cout << testsPassed << " passed, " << testsFailed << " failed\n";
return testsFailed > 0 ? 1 : 0;
}
|