#include "foundation/event.h" #include "test_harness.h" using pp::foundation::Event; using pp::foundation::EventDispatcher; using pp::foundation::StatusCode; using pp::foundation::max_event_subscriptions; namespace { struct Receiver { int count = 0; std::uint64_t payload_sum = 0; std::uint64_t last_source = 0; }; void receive_event(const Event& event, void* user_data) noexcept { auto* receiver = static_cast(user_data); ++receiver->count; receiver->payload_sum += event.payload_u64; receiver->last_source = event.source_id; } void subscribe_and_publish_matching_events(pp::tests::Harness& h) { EventDispatcher dispatcher(4); Receiver receiver; const auto subscription = dispatcher.subscribe(7, receive_event, &receiver); PP_EXPECT(h, subscription.ok()); PP_EXPECT(h, subscription.value() == 1U); PP_EXPECT(h, dispatcher.size() == 1U); PP_EXPECT(h, dispatcher.max_subscriptions() == 4U); const auto delivered = dispatcher.publish(Event { .type = 7, .source_id = 42, .frame_id = 3, .payload_u64 = 11, }); PP_EXPECT(h, delivered == 1U); PP_EXPECT(h, receiver.count == 1); PP_EXPECT(h, receiver.payload_sum == 11U); PP_EXPECT(h, receiver.last_source == 42U); } void ignores_non_matching_or_zero_events(pp::tests::Harness& h) { EventDispatcher dispatcher(4); Receiver receiver; PP_EXPECT(h, dispatcher.subscribe(2, receive_event, &receiver).ok()); PP_EXPECT(h, dispatcher.publish(Event { .type = 3, .payload_u64 = 1 }) == 0U); PP_EXPECT(h, dispatcher.publish(Event { .type = 0, .payload_u64 = 1 }) == 0U); PP_EXPECT(h, receiver.count == 0); } void preserves_subscription_order_and_unsubscribes(pp::tests::Harness& h) { EventDispatcher dispatcher(4); Receiver first; Receiver second; const auto first_subscription = dispatcher.subscribe(9, receive_event, &first); const auto second_subscription = dispatcher.subscribe(9, receive_event, &second); PP_EXPECT(h, first_subscription.ok()); PP_EXPECT(h, second_subscription.ok()); PP_EXPECT(h, dispatcher.publish(Event { .type = 9, .payload_u64 = 5 }) == 2U); PP_EXPECT(h, first.payload_sum == 5U); PP_EXPECT(h, second.payload_sum == 5U); PP_EXPECT(h, dispatcher.unsubscribe(first_subscription.value()).ok()); PP_EXPECT(h, dispatcher.publish(Event { .type = 9, .payload_u64 = 7 }) == 1U); PP_EXPECT(h, first.payload_sum == 5U); PP_EXPECT(h, second.payload_sum == 12U); const auto missing = dispatcher.unsubscribe(first_subscription.value()); PP_EXPECT(h, !missing.ok()); PP_EXPECT(h, missing.code == StatusCode::out_of_range); } void rejects_invalid_subscriptions_and_capacity(pp::tests::Harness& h) { EventDispatcher dispatcher(1); EventDispatcher zero_capacity(0); EventDispatcher excessive_capacity(max_event_subscriptions + 1U); Receiver receiver; const auto zero_type = dispatcher.subscribe(0, receive_event, &receiver); const auto null_callback = dispatcher.subscribe(1, nullptr, &receiver); PP_EXPECT(h, !zero_type.ok()); PP_EXPECT(h, zero_type.status().code == StatusCode::invalid_argument); PP_EXPECT(h, !null_callback.ok()); PP_EXPECT(h, null_callback.status().code == StatusCode::invalid_argument); PP_EXPECT(h, dispatcher.subscribe(1, receive_event, &receiver).ok()); const auto full = dispatcher.subscribe(2, receive_event, &receiver); PP_EXPECT(h, !full.ok()); PP_EXPECT(h, full.status().code == StatusCode::out_of_range); const auto zero_capacity_result = zero_capacity.subscribe(1, receive_event, &receiver); const auto excessive_capacity_result = excessive_capacity.subscribe(1, receive_event, &receiver); PP_EXPECT(h, !zero_capacity_result.ok()); PP_EXPECT(h, zero_capacity_result.status().code == StatusCode::out_of_range); PP_EXPECT(h, !excessive_capacity_result.ok()); PP_EXPECT(h, excessive_capacity_result.status().code == StatusCode::out_of_range); } void clear_removes_all_subscriptions(pp::tests::Harness& h) { EventDispatcher dispatcher(4); Receiver receiver; PP_EXPECT(h, dispatcher.subscribe(1, receive_event, &receiver).ok()); PP_EXPECT(h, dispatcher.subscribe(2, receive_event, &receiver).ok()); dispatcher.clear(); PP_EXPECT(h, dispatcher.empty()); PP_EXPECT(h, dispatcher.publish(Event { .type = 1, .payload_u64 = 5 }) == 0U); PP_EXPECT(h, receiver.count == 0); } } int main() { pp::tests::Harness harness; harness.run("subscribe_and_publish_matching_events", subscribe_and_publish_matching_events); harness.run("ignores_non_matching_or_zero_events", ignores_non_matching_or_zero_events); harness.run("preserves_subscription_order_and_unsubscribes", preserves_subscription_order_and_unsubscribes); harness.run("rejects_invalid_subscriptions_and_capacity", rejects_invalid_subscriptions_and_capacity); harness.run("clear_removes_all_subscriptions", clear_removes_all_subscriptions); return harness.finish(); }