| #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) |
|
|
| |
|
|
| 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); |
| } |
|
|
| |
|
|
| void test_cross_core_event_delivery() { |
| 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::Buy, OrderType::Limit, |
| TimeInForce::Day, toFixedPrice(100.0), 50, 1); |
|
|
| ASSERT_TRUE(oeGateway->getReports().size() > 0); |
| ASSERT_EQ(oeGateway->getReports().back().status, OrderStatus::New); |
| } |
|
|
| |
|
|
| 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() { |
| |
| 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(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); |
| } |
|
|
| |
|
|
| 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); |
| } |
|
|
| |
|
|
| 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); |
| } |
|
|
| |
|
|
| void test_sync_backward_compat() { |
| |
| 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; |
| } |
|
|